Merge "Symbol files for Rust binaries in APEXes are exported"
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 0c1be6e..1353293 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -400,6 +400,9 @@
// not recursive due to conflicting workspace paths in tools/atest/bazel/rules
"tools/asuite/atest":/* recursive = */ false,
"tools/asuite/atest/bazel/reporter":/* recursive = */ true,
+
+ // TODO(b/266459895): remove this and the placeholder BUILD file after re-enabling libunwindstack
+ "external/rust/crates/rustc-demangle-capi":/* recursive = */ false,
}
Bp2buildModuleAlwaysConvertList = []string{
@@ -1367,6 +1370,30 @@
"prebuilt_kotlin-test",
// TODO(b/217750501) exclude_files property not supported
"prebuilt_currysrc_org.eclipse",
+
+ // TODO(b/266459895): re-enable libunwindstack
+ "libunwindstack",
+ "libunwindstack_stdout_log",
+ "libunwindstack_no_dex",
+ "libunwindstack_utils",
+ "unwind_reg_info",
+ "libunwindstack_local",
+ "unwind_for_offline",
+ "unwind",
+ "unwind_info",
+ "unwind_symbols",
+ "libc_malloc_debug",
+ "libfdtrack",
+ "mediaswcodec",
+ "libcodec2_hidl@1.0",
+ "libEGL",
+ "libstagefright_bufferqueue_helper_novndk",
+ "libGLESv2",
+ "libcodec2_hidl@1.1",
+ "libmedia_codecserviceregistrant",
+ "libcodec2_hidl@1.2",
+ "libutils_test",
+ "libutilscallstack",
}
// Bazel prod-mode allowlist. Modules in this list are built by Bazel
diff --git a/android/api_domain.go b/android/api_domain.go
index bdd4e6f..587ceae 100644
--- a/android/api_domain.go
+++ b/android/api_domain.go
@@ -32,17 +32,18 @@
// TODO(b/246656800): Reconcile with android.SdkKind
const (
- PublicApi ApiSurface = iota
- SystemApi
- VendorApi
+ // API surface provided by platform and mainline modules to other mainline modules
+ ModuleLibApi ApiSurface = iota
+ PublicApi // Aka NDK
+ VendorApi // Aka LLNDK
)
func (a ApiSurface) String() string {
switch a {
+ case ModuleLibApi:
+ return "module-libapi"
case PublicApi:
return "publicapi"
- case SystemApi:
- return "systemapi"
case VendorApi:
return "vendorapi"
default:
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 4a495f0..123cc60 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -183,9 +183,11 @@
// and their results after the requests have been made.
type mixedBuildBazelContext struct {
bazelRunner
- paths *bazelPaths
- requests map[cqueryKey]bool // cquery requests that have not yet been issued to Bazel
- requestMutex sync.Mutex // requests can be written in parallel
+ paths *bazelPaths
+ // cquery requests that have not yet been issued to Bazel. This list is maintained
+ // in a sorted state, and is guaranteed to have no duplicates.
+ requests []cqueryKey
+ requestMutex sync.Mutex // requests can be written in parallel
results map[cqueryKey]string // Results of cquery requests after Bazel invocations
@@ -296,7 +298,29 @@
key := makeCqueryKey(label, requestType, cfgKey)
bazelCtx.requestMutex.Lock()
defer bazelCtx.requestMutex.Unlock()
- bazelCtx.requests[key] = true
+
+ // Insert key into requests, maintaining the sort, and only if it's not duplicate.
+ keyString := key.String()
+ foundEqual := false
+ notLessThanKeyString := func(i int) bool {
+ s := bazelCtx.requests[i].String()
+ v := strings.Compare(s, keyString)
+ if v == 0 {
+ foundEqual = true
+ }
+ return v >= 0
+ }
+ targetIndex := sort.Search(len(bazelCtx.requests), notLessThanKeyString)
+ if foundEqual {
+ return
+ }
+
+ if targetIndex == len(bazelCtx.requests) {
+ bazelCtx.requests = append(bazelCtx.requests, key)
+ } else {
+ bazelCtx.requests = append(bazelCtx.requests[:targetIndex+1], bazelCtx.requests[targetIndex:]...)
+ bazelCtx.requests[targetIndex] = key
+ }
}
func (bazelCtx *mixedBuildBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
@@ -487,7 +511,6 @@
return &mixedBuildBazelContext{
bazelRunner: &builtinBazelRunner{},
paths: &paths,
- requests: make(map[cqueryKey]bool),
modulesDefaultToBazel: c.BuildMode == BazelDevMode,
bazelEnabledModules: enabledModules,
bazelDisabledModules: disabledModules,
@@ -727,14 +750,23 @@
configNodesSection := ""
labelsByConfig := map[string][]string{}
- for val := range context.requests {
+
+ for _, val := range context.requests {
labelString := fmt.Sprintf("\"@%s\"", val.label)
configString := getConfigString(val)
labelsByConfig[configString] = append(labelsByConfig[configString], labelString)
}
+ // Configs need to be sorted to maintain determinism of the BUILD file.
+ sortedConfigs := make([]string, 0, len(labelsByConfig))
+ for val := range labelsByConfig {
+ sortedConfigs = append(sortedConfigs, val)
+ }
+ sort.Slice(sortedConfigs, func(i, j int) bool { return sortedConfigs[i] < sortedConfigs[j] })
+
allLabels := []string{}
- for configString, labels := range labelsByConfig {
+ for _, configString := range sortedConfigs {
+ labels := labelsByConfig[configString]
configTokens := strings.Split(configString, "|")
if len(configTokens) != 2 {
panic(fmt.Errorf("Unexpected config string format: %s", configString))
@@ -765,7 +797,7 @@
// request type.
func (context *mixedBuildBazelContext) cqueryStarlarkFileContents() []byte {
requestTypeToCqueryIdEntries := map[cqueryRequest][]string{}
- for val := range context.requests {
+ for _, val := range context.requests {
cqueryId := getCqueryId(val)
mapEntryString := fmt.Sprintf("%q : True", cqueryId)
requestTypeToCqueryIdEntries[val.requestType] =
@@ -944,7 +976,7 @@
}
// Clear requests.
- context.requests = map[cqueryKey]bool{}
+ context.requests = []cqueryKey{}
return nil
}
@@ -961,17 +993,17 @@
return err
}
}
- if err := os.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil {
+ if err := writeFileBytesIfChanged(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil {
return err
}
- if err := os.WriteFile(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil {
+ if err := writeFileBytesIfChanged(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil {
return err
}
- if err := os.WriteFile(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil {
+ if err := writeFileBytesIfChanged(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil {
return err
}
cqueryFileRelpath := filepath.Join(context.paths.injectedFilesDir(), "buildroot.cquery")
- if err := os.WriteFile(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil {
+ if err := writeFileBytesIfChanged(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil {
return err
}
@@ -992,7 +1024,7 @@
cqueryResults[splitLine[0]] = splitLine[1]
}
}
- for val := range context.requests {
+ for _, val := range context.requests {
if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok {
context.results[val] = cqueryResult
} else {
@@ -1003,6 +1035,14 @@
return nil
}
+func writeFileBytesIfChanged(path string, contents []byte, perm os.FileMode) error {
+ oldContents, err := os.ReadFile(path)
+ if err != nil || !bytes.Equal(contents, oldContents) {
+ err = os.WriteFile(path, contents, perm)
+ }
+ return nil
+}
+
func (context *mixedBuildBazelContext) runAquery(config Config, ctx *Context) error {
if ctx != nil {
ctx.EventHandler.Begin("aquery")
@@ -1135,10 +1175,11 @@
// the 'bazel' package, which cannot depend on 'android' package where ctx is defined,
// because this would cause circular dependency. So, until we move aquery processing
// to the 'android' package, we need to handle special cases here.
- if buildStatement.Mnemonic == "FileWrite" || buildStatement.Mnemonic == "SourceSymlinkManifest" {
+ switch buildStatement.Mnemonic {
+ case "FileWrite", "SourceSymlinkManifest":
out := PathForBazelOut(ctx, buildStatement.OutputPaths[0])
WriteFileRuleVerbatim(ctx, out, buildStatement.FileContents)
- } else if buildStatement.Mnemonic == "SymlinkTree" {
+ case "SymlinkTree":
// build-runfiles arguments are the manifest file and the target directory
// where it creates the symlink tree according to this manifest (and then
// writes the MANIFEST file to it).
@@ -1157,7 +1198,7 @@
"outDir": outDir,
},
})
- } else {
+ default:
panic(fmt.Sprintf("unhandled build statement: %v", buildStatement))
}
}
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index 013e19c..d971802 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -168,6 +168,32 @@
}
}
+func TestBazelRequestsSorted(t *testing.T) {
+ bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
+
+ bazelContext.QueueBazelRequest("zzz", cquery.GetOutputFiles, configKey{"arm64_armv8-a", Android})
+ bazelContext.QueueBazelRequest("ccc", cquery.GetApexInfo, configKey{"arm64_armv8-a", Android})
+ bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, configKey{"arm64_armv8-a", Android})
+ bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, configKey{"arm64_armv8-a", Android})
+ bazelContext.QueueBazelRequest("xxx", cquery.GetOutputFiles, configKey{"arm64_armv8-a", Linux})
+ bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, configKey{"arm64_armv8-a", Android})
+ bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, configKey{"otherarch", Android})
+ bazelContext.QueueBazelRequest("bbb", cquery.GetOutputFiles, configKey{"otherarch", Android})
+
+ if len(bazelContext.requests) != 7 {
+ t.Error("Expected 7 request elements, but got", len(bazelContext.requests))
+ }
+
+ lastString := ""
+ for _, val := range bazelContext.requests {
+ thisString := val.String()
+ if thisString <= lastString {
+ t.Errorf("Requests are not ordered correctly. '%s' came before '%s'", lastString, thisString)
+ }
+ lastString = thisString
+ }
+}
+
func verifyExtraFlags(t *testing.T, config Config, expected string) string {
bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
@@ -204,7 +230,6 @@
return &mixedBuildBazelContext{
bazelRunner: runner,
paths: &p,
- requests: map[cqueryKey]bool{},
}, p.soongOutDir
}
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index e151521..bad7baf 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -461,11 +461,6 @@
return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
}
-// ModuleFromBazelLabel reverses the logic in bp2buildModuleLabel
-func ModuleFromBazelLabel(label string) string {
- return strings.Split(label, ":")[1]
-}
-
// BazelOutPath is a Bazel output path compatible to be used for mixed builds within Soong/Ninja.
type BazelOutPath struct {
OutputPath
diff --git a/android/config.go b/android/config.go
index a8b0a40..c305114 100644
--- a/android/config.go
+++ b/android/config.go
@@ -521,27 +521,33 @@
config.AndroidFirstDeviceTarget = FirstTarget(config.Targets[Android], "lib64", "lib32")[0]
}
- if cmdArgs.SymlinkForestMarker != "" {
- config.BuildMode = SymlinkForest
- } else if cmdArgs.Bp2buildMarker != "" {
- config.BuildMode = Bp2build
- } else if cmdArgs.BazelQueryViewDir != "" {
- config.BuildMode = GenerateQueryView
- } else if cmdArgs.BazelApiBp2buildDir != "" {
- config.BuildMode = ApiBp2build
- } else if cmdArgs.ModuleGraphFile != "" {
- config.BuildMode = GenerateModuleGraph
- } else if cmdArgs.DocFile != "" {
- config.BuildMode = GenerateDocFile
- } else if cmdArgs.BazelModeDev {
- config.BuildMode = BazelDevMode
- } else if cmdArgs.BazelMode {
- config.BuildMode = BazelProdMode
- } else if cmdArgs.BazelModeStaging {
- config.BuildMode = BazelStagingMode
- } else {
- config.BuildMode = AnalysisNoBazel
+ setBuildMode := func(arg string, mode SoongBuildMode) {
+ if arg != "" {
+ if config.BuildMode != AnalysisNoBazel {
+ fmt.Fprintf(os.Stderr, "buildMode is already set, illegal argument: %s", arg)
+ os.Exit(1)
+ }
+ config.BuildMode = mode
+ }
}
+ setBazelMode := func(arg bool, argName string, mode SoongBuildMode) {
+ if arg {
+ if config.BuildMode != AnalysisNoBazel {
+ fmt.Fprintf(os.Stderr, "buildMode is already set, illegal argument: %s", argName)
+ os.Exit(1)
+ }
+ config.BuildMode = mode
+ }
+ }
+ setBuildMode(cmdArgs.SymlinkForestMarker, SymlinkForest)
+ setBuildMode(cmdArgs.Bp2buildMarker, Bp2build)
+ setBuildMode(cmdArgs.BazelQueryViewDir, GenerateQueryView)
+ setBuildMode(cmdArgs.BazelApiBp2buildDir, ApiBp2build)
+ setBuildMode(cmdArgs.ModuleGraphFile, GenerateModuleGraph)
+ setBuildMode(cmdArgs.DocFile, GenerateDocFile)
+ setBazelMode(cmdArgs.BazelModeDev, "--bazel-mode-dev", BazelDevMode)
+ setBazelMode(cmdArgs.BazelMode, "--bazel-mode", BazelProdMode)
+ setBazelMode(cmdArgs.BazelModeStaging, "--bazel-mode-staging", BazelStagingMode)
config.BazelContext, err = NewBazelContext(config)
config.Bp2buildPackageConfig = GetBp2BuildAllowList()
@@ -1494,6 +1500,10 @@
return Bool(c.productVariables.CompressedApex) && !c.UnbundledBuildApps()
}
+func (c *config) ApexTrimEnabled() bool {
+ return Bool(c.productVariables.TrimmedApex)
+}
+
func (c *config) EnforceSystemCertificate() bool {
return Bool(c.productVariables.EnforceSystemCertificate)
}
diff --git a/android/testing.go b/android/testing.go
index 29af71f..fc39a9c 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -1125,6 +1125,11 @@
config.katiEnabled = true
}
+func SetTrimmedApexEnabledForTests(config Config) {
+ config.productVariables.TrimmedApex = new(bool)
+ *config.productVariables.TrimmedApex = true
+}
+
func AndroidMkEntriesForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) []AndroidMkEntries {
t.Helper()
var p AndroidMkEntriesProvider
@@ -1145,7 +1150,7 @@
var p AndroidMkDataProvider
var ok bool
if p, ok = mod.(AndroidMkDataProvider); !ok {
- t.Errorf("module does not implement AndroidMkDataProvider: " + mod.Name())
+ t.Fatalf("module does not implement AndroidMkDataProvider: " + mod.Name())
}
data := p.AndroidMk()
data.fillInData(ctx, mod)
diff --git a/android/util.go b/android/util.go
index a0f7160..947af69 100644
--- a/android/util.go
+++ b/android/util.go
@@ -136,6 +136,38 @@
return IndexList(s, list) != -1
}
+func setFromList[T comparable](l []T) map[T]bool {
+ m := make(map[T]bool, len(l))
+ for _, t := range l {
+ m[t] = true
+ }
+ return m
+}
+
+// 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.
+func ListSetDifference[T comparable](l1, l2 []T) (bool, []T, []T) {
+ listsDiffer := false
+ diff1 := []T{}
+ diff2 := []T{}
+ m1 := setFromList(l1)
+ m2 := setFromList(l2)
+ for t := range m1 {
+ if _, ok := m2[t]; !ok {
+ diff1 = append(diff1, t)
+ listsDiffer = true
+ }
+ }
+ for t := range m2 {
+ if _, ok := m1[t]; !ok {
+ diff2 = append(diff2, t)
+ listsDiffer = true
+ }
+ }
+ return listsDiffer, diff1, diff2
+}
+
// 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 {
diff --git a/android/variable.go b/android/variable.go
index e838b7c..e714fc4 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -380,6 +380,7 @@
Ndk_abis *bool `json:",omitempty"`
+ TrimmedApex *bool `json:",omitempty"`
Flatten_apex *bool `json:",omitempty"`
ForceApexSymlinkOptimization *bool `json:",omitempty"`
CompressedApex *bool `json:",omitempty"`
@@ -502,6 +503,7 @@
Malloc_zero_contents: boolPtr(true),
Malloc_pattern_fill_contents: boolPtr(false),
Safestack: boolPtr(false),
+ TrimmedApex: boolPtr(false),
BootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}},
ApexBootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}},
diff --git a/apex/androidmk.go b/apex/androidmk.go
index d94711b..12faf22 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -314,7 +314,7 @@
targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
}
- android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", moduleNames, a.requiredDeps, required)
+ android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", moduleNames, a.makeModulesToInstall, required)
android.AndroidMkEmitAssignList(w, "LOCAL_TARGET_REQUIRED_MODULES", targetRequired)
android.AndroidMkEmitAssignList(w, "LOCAL_HOST_REQUIRED_MODULES", hostRequired)
}
@@ -322,14 +322,14 @@
func (a *apexBundle) androidMkForType() android.AndroidMkData {
return android.AndroidMkData{
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+ moduleNames := []string{}
apexType := a.properties.ApexType
+ if a.installable() {
+ apexName := proptools.StringDefault(a.properties.Apex_name, name)
+ moduleNames = a.androidMkForFiles(w, name, apexName, moduleDir, data)
+ }
if apexType == flattenedApex {
- var moduleNames []string = nil
- if a.installable() {
- apexName := proptools.StringDefault(a.properties.Apex_name, name)
- moduleNames = a.androidMkForFiles(w, name, apexName, moduleDir, data)
- }
// Only image APEXes can be flattened.
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS) # apex.apexBundle.flat")
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
@@ -371,7 +371,7 @@
}
android.AndroidMkEmitAssignList(w, "LOCAL_OVERRIDES_MODULES", a.overridableProperties.Overrides)
- a.writeRequiredModules(w, nil)
+ a.writeRequiredModules(w, moduleNames)
fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
diff --git a/apex/apex.go b/apex/apex.go
index 9485a4b..a9c8afc 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -80,6 +80,7 @@
ctx.BottomUp("apex", apexMutator).Parallel()
ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel()
ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
+ ctx.BottomUp("apex_dcla_deps", apexDCLADepsMutator).Parallel()
// Register after apex_info mutator so that it can use ApexVariationName
ctx.TopDown("apex_strict_updatability_lint", apexStrictUpdatibilityLintMutator).Parallel()
}
@@ -123,10 +124,6 @@
// List of filesystem images that are embedded inside this APEX bundle.
Filesystems []string
- // The minimum SDK version that this APEX must support at minimum. This is usually set to
- // the SDK version that the APEX was first introduced.
- Min_sdk_version *string
-
// Whether this APEX is considered updatable or not. When set to true, this will enforce
// additional rules for making sure that the APEX is truly updatable. To be updatable,
// min_sdk_version should be set as well. This will also disable the size optimizations like
@@ -389,6 +386,13 @@
// conditions, e.g., target device needs to support APEX compression, are also fulfilled.
// Default: false.
Compressible *bool
+
+ // Trim against a specific Dynamic Common Lib APEX
+ Trim_against *string
+
+ // The minimum SDK version that this APEX must support at minimum. This is usually set to
+ // the SDK version that the APEX was first introduced.
+ Min_sdk_version *string
}
type apexBundle struct {
@@ -439,8 +443,8 @@
// GenerateAndroidBuildActions.
filesInfo []apexFile
- // List of other module names that should be installed when this APEX gets installed.
- requiredDeps []string
+ // List of other module names that should be installed when this APEX gets installed (LOCAL_REQUIRED_MODULES).
+ makeModulesToInstall []string
///////////////////////////////////////////////////////////////////////////////////////////
// Outputs (final and intermediates)
@@ -488,8 +492,6 @@
// Optional list of lint report zip files for apexes that contain java or app modules
lintReports android.Paths
- prebuiltFileToDelete string
-
isCompressed bool
// Path of API coverage generate file
@@ -675,6 +677,7 @@
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}
@@ -908,6 +911,33 @@
}
}
+func apexDCLADepsMutator(mctx android.BottomUpMutatorContext) {
+ if !mctx.Config().ApexTrimEnabled() {
+ return
+ }
+ if a, ok := mctx.Module().(*apexBundle); ok && a.overridableProperties.Trim_against != nil {
+ commonVariation := mctx.Config().AndroidCommonTarget.Variations()
+ mctx.AddFarVariationDependencies(commonVariation, dclaTag, String(a.overridableProperties.Trim_against))
+ } else if o, ok := mctx.Module().(*OverrideApex); ok {
+ for _, p := range o.GetProperties() {
+ properties, ok := p.(*overridableProperties)
+ if !ok {
+ continue
+ }
+ if properties.Trim_against != nil {
+ commonVariation := mctx.Config().AndroidCommonTarget.Variations()
+ mctx.AddFarVariationDependencies(commonVariation, dclaTag, String(properties.Trim_against))
+ }
+ }
+ }
+}
+
+type DCLAInfo struct {
+ ProvidedLibs []string
+}
+
+var DCLAInfoProvider = blueprint.NewMutatorProvider(DCLAInfo{}, "apex_info")
+
type ApexBundleInfo struct {
Contents *android.ApexContents
}
@@ -1035,6 +1065,12 @@
child.(android.ApexModule).BuildForApex(apexInfo) // leave a mark!
return true
})
+
+ if a.dynamic_common_lib_apex() {
+ mctx.SetProvider(DCLAInfoProvider, DCLAInfo{
+ ProvidedLibs: a.properties.Native_shared_libs,
+ })
+ }
}
type ApexInfoMutator interface {
@@ -1531,6 +1567,19 @@
return proptools.BoolDefault(a.properties.Dynamic_common_lib_apex, false)
}
+// See the list of libs to trim
+func (a *apexBundle) libs_to_trim(ctx android.ModuleContext) []string {
+ dclaModules := ctx.GetDirectDepsWithTag(dclaTag)
+ if len(dclaModules) > 1 {
+ panic(fmt.Errorf("expected exactly at most one dcla dependency, got %d", len(dclaModules)))
+ }
+ if len(dclaModules) > 0 {
+ DCLAInfo := ctx.OtherModuleProvider(dclaModules[0], DCLAInfoProvider).(DCLAInfo)
+ return DCLAInfo.ProvidedLibs
+ }
+ return []string{}
+}
+
// These functions are interfacing with cc/sanitizer.go. The entire APEX (along with all of its
// members) can be sanitized, either forcibly, or by the global configuration. For some of the
// sanitizers, extra dependencies can be forcibly added as well.
@@ -1657,7 +1706,7 @@
return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, rustm)
}
-func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.Module) apexFile {
+func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.PythonBinaryModule) apexFile {
dirInApex := "bin"
fileToCopy := py.HostToolPath().Path()
return newApexFile(ctx, fileToCopy, py.BaseModuleName(), dirInApex, pyBinary, py)
@@ -1922,11 +1971,9 @@
a.containerCertificateFile = android.PathForBazelOut(ctx, outputs.ContainerKeyInfo[0])
a.containerPrivateKeyFile = android.PathForBazelOut(ctx, outputs.ContainerKeyInfo[1])
- // Ensure ApexInfo.RequiresLibs are installed as part of a bundle build
- for _, bazelLabel := range outputs.RequiresLibs {
- // convert Bazel label back to Soong module name
- a.requiredDeps = append(a.requiredDeps, android.ModuleFromBazelLabel(bazelLabel))
- }
+ // Ensure ApexMkInfo.install_to_system make module names are installed as
+ // part of a bundled build.
+ a.makeModulesToInstall = append(a.makeModulesToInstall, outputs.MakeModulesToInstall...)
apexType := a.properties.ApexType
switch apexType {
@@ -2025,7 +2072,7 @@
a.primaryApexType = true
if ctx.Config().InstallExtraFlattenedApexes() {
- a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix)
+ a.makeModulesToInstall = append(a.makeModulesToInstall, a.Name()+flattenedSuffix)
}
}
case zipApex:
@@ -2147,7 +2194,7 @@
case *cc.Module:
vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, ch))
return true // track transitive dependencies
- case *python.Module:
+ case *python.PythonBinaryModule:
if ch.HostToolPath().Valid() {
vctx.filesInfo = append(vctx.filesInfo, apexFileForPyBinary(ctx, ch))
}
@@ -2177,7 +2224,7 @@
vctx.filesInfo = append(vctx.filesInfo, apexBootclasspathFragmentFiles(ctx, child)...)
for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
- a.requiredDeps = append(a.requiredDeps, makeModuleName)
+ a.makeModulesToInstall = append(a.makeModulesToInstall, makeModuleName)
}
return true
case sscpfTag:
@@ -2295,12 +2342,6 @@
} else {
ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
}
- case android.PrebuiltDepTag:
- // If the prebuilt is force disabled, remember to delete the prebuilt file
- // that might have been installed in the previous builds
- if prebuilt, ok := child.(prebuilt); ok && prebuilt.isForceDisabled() {
- a.prebuiltFileToDelete = prebuilt.InstallFilename()
- }
}
return false
}
@@ -2340,11 +2381,14 @@
//
// Always include if we are a host-apex however since those won't have any
// system libraries.
- if ch.IsStubsImplementationRequired() && !am.DirectlyInAnyApex() {
+ //
+ // Skip the dependency in unbundled builds where the device image is not
+ // being built.
+ if ch.IsStubsImplementationRequired() && !am.DirectlyInAnyApex() && !ctx.Config().UnbundledBuild() {
// we need a module name for Make
name := ch.ImplementationModuleNameForMake(ctx) + ch.Properties.SubName
- if !android.InList(name, a.requiredDeps) {
- a.requiredDeps = append(a.requiredDeps, name)
+ if !android.InList(name, a.makeModulesToInstall) {
+ a.makeModulesToInstall = append(a.makeModulesToInstall, name)
}
}
vctx.requireNativeLibs = append(vctx.requireNativeLibs, af.stem())
@@ -2479,7 +2523,6 @@
}
////////////////////////////////////////////////////////////////////////////////////////////
// 2) traverse the dependency tree to collect apexFile structs from them.
-
// Collect the module directory for IDE info in java/jdeps.go.
a.modulePaths = append(a.modulePaths, ctx.ModuleDir())
@@ -2837,7 +2880,7 @@
// Only override the minSdkVersion value on Apexes which already specify
// a min_sdk_version (it's optional for non-updatable apexes), and that its
// min_sdk_version value is lower than the one to override with.
- minApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version))
+ minApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.overridableProperties.Min_sdk_version))
if minApiLevel.IsNone() {
return ""
}
@@ -3491,8 +3534,8 @@
// TODO(b/219503907) this would need to be set to a.MinSdkVersionValue(ctx) but
// given it's coming via config, we probably don't want to put it in here.
var minSdkVersion bazel.StringAttribute
- if a.properties.Min_sdk_version != nil {
- minSdkVersion.SetValue(*a.properties.Min_sdk_version)
+ if a.overridableProperties.Min_sdk_version != nil {
+ minSdkVersion.SetValue(*a.overridableProperties.Min_sdk_version)
}
if props, ok := productVariableProps[minSdkVersionPropName]; ok {
for c, p := range props {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 395da95..4f300e7 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -526,6 +526,7 @@
data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
androidMk := builder.String()
+ ensureContains(t, androidMk, "LOCAL_MODULE := mylib.myapex\n")
ensureNotContains(t, androidMk, "LOCAL_MODULE := mylib.com.android.myapex\n")
optFlags := apexRule.Args["opt_flags"]
@@ -2995,7 +2996,7 @@
var builder strings.Builder
data.Custom(&builder, name, prefix, "", data)
androidMk := builder.String()
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc.vendor libm.vendor libdl.vendor\n")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc++.vendor.myapex:64 mylib.vendor.myapex:64 apex_manifest.pb.myapex apex_pubkey.myapex libc.vendor libm.vendor libdl.vendor\n")
}
func TestAndroidMkWritesCommonProperties(t *testing.T) {
@@ -4137,8 +4138,6 @@
module := ctx.ModuleForTests("myapex", "android_common_com.android.myapex_image")
apexManifestRule := module.Rule("apexManifestRule")
ensureContains(t, apexManifestRule.Args["opt"], "-v name com.android.myapex")
- apexRule := module.Rule("apexRule")
- ensureContains(t, apexRule.Args["opt_flags"], "--do_not_check_keyname")
apexBundle := module.Module().(*apexBundle)
data := android.AndroidMkDataForTest(t, ctx, apexBundle)
@@ -4147,6 +4146,7 @@
var builder strings.Builder
data.Custom(&builder, name, prefix, "", data)
androidMk := builder.String()
+ ensureContains(t, androidMk, "LOCAL_MODULE := mylib.myapex\n")
ensureNotContains(t, androidMk, "LOCAL_MODULE := mylib.com.android.myapex\n")
}
@@ -5680,6 +5680,12 @@
var builder strings.Builder
data.Custom(&builder, name, prefix, "", data)
androidMk := builder.String()
+ ensureContains(t, androidMk, "LOCAL_MODULE := mytest.myapex\n")
+ ensureContains(t, androidMk, "LOCAL_MODULE := mytest1.myapex\n")
+ ensureContains(t, androidMk, "LOCAL_MODULE := mytest2.myapex\n")
+ ensureContains(t, androidMk, "LOCAL_MODULE := mytest3.myapex\n")
+ ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex\n")
+ ensureContains(t, androidMk, "LOCAL_MODULE := apex_pubkey.myapex\n")
ensureContains(t, androidMk, "LOCAL_MODULE := myapex\n")
flatBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
@@ -5708,12 +5714,12 @@
}),
)
ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- ensureListContains(t, ab.requiredDeps, "myapex.flattened")
+ ensureListContains(t, ab.makeModulesToInstall, "myapex.flattened")
mk := android.AndroidMkDataForTest(t, ctx, ab)
var builder strings.Builder
mk.Custom(&builder, ab.Name(), "TARGET_", "", mk)
androidMk := builder.String()
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := myapex.flattened\n")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := apex_manifest.pb.myapex apex_pubkey.myapex myapex.flattened\n")
}
func TestErrorsIfDepsAreNotEnabled(t *testing.T) {
@@ -6506,6 +6512,12 @@
var builder strings.Builder
data.Custom(&builder, name, "TARGET_", "", data)
androidMk := builder.String()
+ ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex")
+ ensureContains(t, androidMk, "LOCAL_MODULE := overrideBpf.o.override_myapex")
+ ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.override_myapex")
+ ensureContains(t, androidMk, "LOCAL_MODULE := override_bcplib.override_myapex")
+ ensureContains(t, androidMk, "LOCAL_MODULE := override_systemserverlib.override_myapex")
+ ensureContains(t, androidMk, "LOCAL_MODULE := override_java_library.override_myapex")
ensureContains(t, androidMk, "LOCAL_MODULE_STEM := override_myapex.apex")
ensureContains(t, androidMk, "LOCAL_OVERRIDES_MODULES := unknownapex myapex")
ensureNotContains(t, androidMk, "LOCAL_MODULE := app.myapex")
@@ -7097,7 +7109,7 @@
var builder strings.Builder
data.Custom(&builder, name, prefix, "", data)
androidMk := builder.String()
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := a b\n")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := mylib.myapex:64 apex_manifest.pb.myapex apex_pubkey.myapex a b\n")
ensureContains(t, androidMk, "LOCAL_HOST_REQUIRED_MODULES := c d\n")
ensureContains(t, androidMk, "LOCAL_TARGET_REQUIRED_MODULES := e f\n")
}
@@ -7268,9 +7280,6 @@
"myapex",
"//apex_available:platform",
],
- stubs: {
- versions: ["current"],
- },
}
`)
@@ -7280,10 +7289,11 @@
data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
androidMk := builder.String()
// `myotherlib` is added to `myapex` as symlink
+ ensureContains(t, androidMk, "LOCAL_MODULE := mylib.myapex\n")
ensureNotContains(t, androidMk, "LOCAL_MODULE := prebuilt_myotherlib.myapex\n")
ensureNotContains(t, androidMk, "LOCAL_MODULE := myotherlib.myapex\n")
// `myapex` should have `myotherlib` in its required line, not `prebuilt_myotherlib`
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := myotherlib\n")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := mylib.myapex:64 myotherlib:64 apex_manifest.pb.myapex apex_pubkey.myapex\n")
}
func TestApexWithJniLibs(t *testing.T) {
@@ -8796,7 +8806,7 @@
// The make level dependency needs to be on otherlib - prebuilt_otherlib isn't
// a thing there.
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := otherlib\n")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc++:64 mylib.myapex:64 apex_manifest.pb.myapex apex_pubkey.myapex otherlib\n")
}
func TestExcludeDependency(t *testing.T) {
@@ -9190,7 +9200,7 @@
var builder strings.Builder
data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
androidMk := builder.String()
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex\n")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex apex_manifest.pb.myapex apex_pubkey.myapex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex\n")
}
func TestAndroidMk_DexpreoptBuiltInstalledForApex_Prebuilt(t *testing.T) {
@@ -9266,7 +9276,7 @@
var builder strings.Builder
data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
androidMk := builder.String()
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := otherapex")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex apex_manifest.pb.myapex apex_pubkey.myapex otherapex")
}
func TestAndroidMk_RequiredDeps(t *testing.T) {
@@ -9285,15 +9295,15 @@
`)
bundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- bundle.requiredDeps = append(bundle.requiredDeps, "foo")
+ bundle.makeModulesToInstall = append(bundle.makeModulesToInstall, "foo")
data := android.AndroidMkDataForTest(t, ctx, bundle)
var builder strings.Builder
data.Custom(&builder, bundle.BaseModuleName(), "TARGET_", "", data)
androidMk := builder.String()
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo\n")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := apex_manifest.pb.myapex apex_pubkey.myapex foo\n")
flattenedBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
- flattenedBundle.requiredDeps = append(flattenedBundle.requiredDeps, "foo")
+ flattenedBundle.makeModulesToInstall = append(flattenedBundle.makeModulesToInstall, "foo")
flattenedData := android.AndroidMkDataForTest(t, ctx, flattenedBundle)
var flattenedBuilder strings.Builder
flattenedData.Custom(&flattenedBuilder, flattenedBundle.BaseModuleName(), "TARGET_", "", flattenedData)
@@ -9537,7 +9547,7 @@
func ensureContainsRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
for _, dep := range deps {
- android.AssertStringListContains(t, "", a.requiredDeps, dep)
+ android.AssertStringListContains(t, "", a.makeModulesToInstall, dep)
}
}
@@ -9545,7 +9555,7 @@
func ensureDoesNotContainRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
for _, dep := range deps {
- android.AssertStringListDoesNotContain(t, "", a.requiredDeps, dep)
+ android.AssertStringListDoesNotContain(t, "", a.makeModulesToInstall, dep)
}
}
@@ -9850,3 +9860,70 @@
libcCoreVariant := result.ModuleForTests("libc.apiimport", "android_arm64_armv8-a_shared").Module()
android.AssertBoolEquals(t, "core variant should link against source libc", true, hasDep(libfooCoreVariant, libcCoreVariant))
}
+
+func TestTrimmedApex(t *testing.T) {
+ bp := `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["libfoo","libbaz"],
+ min_sdk_version: "29",
+ trim_against: "mydcla",
+ }
+ apex {
+ name: "mydcla",
+ key: "myapex.key",
+ native_shared_libs: ["libfoo","libbar"],
+ min_sdk_version: "29",
+ file_contexts: ":myapex-file_contexts",
+ dynamic_common_lib_apex: true,
+ }
+ apex_key {
+ name: "myapex.key",
+ }
+ cc_library {
+ name: "libfoo",
+ shared_libs: ["libc"],
+ apex_available: ["myapex","mydcla"],
+ min_sdk_version: "29",
+ }
+ cc_library {
+ name: "libbar",
+ shared_libs: ["libc"],
+ apex_available: ["myapex","mydcla"],
+ min_sdk_version: "29",
+ }
+ cc_library {
+ name: "libbaz",
+ shared_libs: ["libc"],
+ apex_available: ["myapex","mydcla"],
+ min_sdk_version: "29",
+ }
+ cc_api_library {
+ name: "libc",
+ src: "libc.so",
+ min_sdk_version: "29",
+ recovery_available: true,
+ }
+ api_imports {
+ name: "api_imports",
+ shared_libs: [
+ "libc",
+ ],
+ header_libs: [],
+ }
+ `
+ ctx := testApex(t, bp)
+ module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+ apexRule := module.MaybeRule("apexRule")
+ if apexRule.Rule == nil {
+ t.Errorf("Expecting regular apex rule but a non regular apex rule found")
+ }
+
+ ctx = testApex(t, bp, android.FixtureModifyConfig(android.SetTrimmedApexEnabledForTests))
+ trimmedApexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("TrimmedApexRule")
+ libs_to_trim := trimmedApexRule.Args["libs_to_trim"]
+ android.AssertStringDoesContain(t, "missing lib to trim", libs_to_trim, "libfoo")
+ android.AssertStringDoesContain(t, "missing lib to trim", libs_to_trim, "libbar")
+ android.AssertStringDoesNotContain(t, "unexpected libs in the libs to trim", libs_to_trim, "libbaz")
+}
diff --git a/apex/bp2build_test.go b/apex/bp2build_test.go
index 01afa52..2f2b61e 100644
--- a/apex/bp2build_test.go
+++ b/apex/bp2build_test.go
@@ -42,6 +42,7 @@
OutputBaseDir: outputBaseDir,
LabelToApexInfo: map[string]cquery.ApexInfo{
"//:foo": cquery.ApexInfo{
+ // ApexInfo Starlark provider.
SignedOutput: "signed_out.apex",
SignedCompressedOutput: "signed_out.capex",
UnsignedOutput: "unsigned_out.apex",
@@ -56,6 +57,9 @@
// unused
PackageName: "pkg_name",
ProvidesLibs: []string{"a", "b"},
+
+ // ApexMkInfo Starlark provider
+ MakeModulesToInstall: []string{"c"}, // d deliberately omitted
},
},
}
@@ -111,7 +115,12 @@
if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/installed-files.txt:foo-installed-files.txt)"; !strings.Contains(data, w) {
t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
}
- if w := "LOCAL_REQUIRED_MODULES := c d"; !strings.Contains(data, w) {
+
+ // make modules to be installed to system
+ if len(ab.makeModulesToInstall) != 1 && ab.makeModulesToInstall[0] != "c" {
+ t.Errorf("Expected makeModulesToInstall slice to only contain 'c', got %q", ab.makeModulesToInstall)
+ }
+ if w := "LOCAL_REQUIRED_MODULES := c"; !strings.Contains(data, w) {
t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
}
}
@@ -212,6 +221,7 @@
OutputBaseDir: outputBaseDir,
LabelToApexInfo: map[string]cquery.ApexInfo{
"//:foo": cquery.ApexInfo{
+ // ApexInfo Starlark provider
SignedOutput: "signed_out.apex",
UnsignedOutput: "unsigned_out.apex",
BundleKeyInfo: []string{"public_key", "private_key"},
@@ -225,8 +235,12 @@
// unused
PackageName: "pkg_name",
ProvidesLibs: []string{"a", "b"},
+
+ // ApexMkInfo Starlark provider
+ MakeModulesToInstall: []string{"c"}, // d deliberately omitted
},
"//:override_foo": cquery.ApexInfo{
+ // ApexInfo Starlark provider
SignedOutput: "override_signed_out.apex",
UnsignedOutput: "override_unsigned_out.apex",
BundleKeyInfo: []string{"override_public_key", "override_private_key"},
@@ -240,6 +254,9 @@
// unused
PackageName: "override_pkg_name",
ProvidesLibs: []string{"a", "b"},
+
+ // ApexMkInfo Starlark provider
+ MakeModulesToInstall: []string{"c"}, // d deliberately omitted
},
},
}
@@ -295,7 +312,12 @@
if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/override_installed-files.txt:override_foo-installed-files.txt)"; !strings.Contains(data, w) {
t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
}
- if w := "LOCAL_REQUIRED_MODULES := c d"; !strings.Contains(data, w) {
+
+ // make modules to be installed to system
+ if len(ab.makeModulesToInstall) != 1 && ab.makeModulesToInstall[0] != "c" {
+ t.Errorf("Expected makeModulestoInstall slice to only contain 'c', got %q", ab.makeModulesToInstall)
+ }
+ if w := "LOCAL_REQUIRED_MODULES := c"; !strings.Contains(data, w) {
t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
}
}
diff --git a/apex/builder.go b/apex/builder.go
index 18d0836..4aef3c1 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -40,6 +40,8 @@
pctx.Import("android/soong/java")
pctx.HostBinToolVariable("apexer", "apexer")
pctx.HostBinToolVariable("apexer_with_DCLA_preprocessing", "apexer_with_DCLA_preprocessing")
+ pctx.HostBinToolVariable("apexer_with_trim_preprocessing", "apexer_with_trim_preprocessing")
+
// ART minimal builds (using the master-art manifest) do not have the "frameworks/base"
// projects, and hence cannot build 'aapt2'. Use the SDK prebuilt instead.
hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) {
@@ -146,6 +148,34 @@
}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
"opt_flags", "manifest", "is_DCLA")
+ TrimmedApexRule = pctx.StaticRule("TrimmedApexRule", blueprint.RuleParams{
+ Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
+ `(. ${out}.copy_commands) && ` +
+ `APEXER_TOOL_PATH=${tool_path} ` +
+ `${apexer_with_trim_preprocessing} ` +
+ `--apexer ${apexer} ` +
+ `--canned_fs_config ${canned_fs_config} ` +
+ `--manifest ${manifest} ` +
+ `--libs_to_trim ${libs_to_trim} ` +
+ `${image_dir} ` +
+ `${out} ` +
+ `-- ` +
+ `--include_build_info ` +
+ `--force ` +
+ `--payload_type image ` +
+ `--key ${key} ` +
+ `--file_contexts ${file_contexts} ` +
+ `${opt_flags} `,
+ CommandDeps: []string{"${apexer_with_trim_preprocessing}", "${apexer}", "${avbtool}", "${e2fsdroid}",
+ "${merge_zips}", "${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}",
+ "${sload_f2fs}", "${make_erofs}", "${soong_zip}", "${zipalign}", "${aapt2}",
+ "prebuilts/sdk/current/public/android.jar"},
+ Rspfile: "${out}.copy_commands",
+ RspfileContent: "${copy_commands}",
+ Description: "APEX ${image_dir} => ${out}",
+ }, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
+ "opt_flags", "manifest", "libs_to_trim")
+
zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
`(. ${out}.copy_commands) && ` +
@@ -676,12 +706,6 @@
optFlags = append(optFlags, "--unsigned_payload")
}
- if a.properties.Apex_name != nil {
- // If apex_name is set, apexer can skip checking if key name matches with
- // apex name. Note that apex_manifest is also mended.
- optFlags = append(optFlags, "--do_not_check_keyname")
- }
-
if moduleMinSdkVersion == android.SdkVersion_Android10 {
implicitInputs = append(implicitInputs, a.manifestJsonOut)
optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String())
@@ -706,6 +730,24 @@
"opt_flags": strings.Join(optFlags, " "),
},
})
+ } else if ctx.Config().ApexTrimEnabled() && len(a.libs_to_trim(ctx)) > 0 {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: TrimmedApexRule,
+ Implicits: implicitInputs,
+ Output: unsignedOutputFile,
+ Description: "apex (" + apexType.name() + ")",
+ Args: map[string]string{
+ "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+ "image_dir": imageDir.String(),
+ "copy_commands": strings.Join(copyCommands, " && "),
+ "manifest": a.manifestPbOut.String(),
+ "file_contexts": fileContexts.String(),
+ "canned_fs_config": cannedFsConfig.String(),
+ "key": a.privateKeyFile.String(),
+ "opt_flags": strings.Join(optFlags, " "),
+ "libs_to_trim": strings.Join(a.libs_to_trim(ctx), ","),
+ },
+ })
} else {
ctx.Build(pctx, android.BuildParams{
Rule: apexRule,
diff --git a/apex/vndk.go b/apex/vndk.go
index ef3e5e1..80560cf 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -65,8 +65,23 @@
}
vndkVersion := ab.vndkVersion(mctx.DeviceConfig())
+
// Ensure VNDK APEX mount point is formatted as com.android.vndk.v###
ab.properties.Apex_name = proptools.StringPtr(vndkApexNamePrefix + vndkVersion)
+
+ apiLevel, err := android.ApiLevelFromUser(mctx, vndkVersion)
+ if err != nil {
+ mctx.PropertyErrorf("vndk_version", "%s", err.Error())
+ return
+ }
+
+ targets := mctx.MultiTargets()
+ if len(targets) > 0 && apiLevel.LessThan(cc.MinApiForArch(mctx, targets[0].Arch.ArchType)) {
+ // Disable VNDK apexes for VNDK versions less than the minimum supported API level for the primary
+ // architecture.
+ ab.Disable()
+ }
+
}
}
diff --git a/bazel/aquery.go b/bazel/aquery.go
index bc823b3..80cf70a 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -118,12 +118,11 @@
// A helper type for aquery processing which facilitates retrieval of path IDs from their
// less readable Bazel structures (depset and path fragment).
type aqueryArtifactHandler struct {
- // Switches to true if any depset contains only `bazelToolsDependencySentinel`
- bazelToolsDependencySentinelNeeded bool
// Maps depset id to AqueryDepset, a representation of depset which is
// post-processed for middleman artifact handling, unhandled artifact
// dropping, content hashing, etc.
depsetIdToAqueryDepset map[depsetId]AqueryDepset
+ emptyDepsetIds map[depsetId]struct{}
// Maps content hash to AqueryDepset.
depsetHashToAqueryDepset map[string]AqueryDepset
@@ -145,9 +144,6 @@
// The file name of py3wrapper.sh, which is used by py_binary targets.
const py3wrapperFileName = "/py3wrapper.sh"
-// A file to be put into depsets that are otherwise empty
-const bazelToolsDependencySentinel = "BAZEL_TOOLS_DEPENDENCY_SENTINEL"
-
func indexBy[K comparable, V any](values []V, keyFn func(v V) K) map[K]V {
m := map[K]V{}
for _, v := range values {
@@ -192,6 +188,7 @@
depsetIdToAqueryDepset: map[depsetId]AqueryDepset{},
depsetHashToAqueryDepset: map[string]AqueryDepset{},
depsetHashToArtifactPathsCache: map[string][]string{},
+ emptyDepsetIds: make(map[depsetId]struct{}, 0),
artifactIdToPath: artifactIdToPath,
}
@@ -208,16 +205,16 @@
// Ensures that the handler's depsetIdToAqueryDepset map contains an entry for the given
// depset.
-func (a *aqueryArtifactHandler) populateDepsetMaps(depset depSetOfFiles, middlemanIdToDepsetIds map[artifactId][]depsetId, depsetIdToDepset map[depsetId]depSetOfFiles) (AqueryDepset, error) {
+func (a *aqueryArtifactHandler) populateDepsetMaps(depset depSetOfFiles, middlemanIdToDepsetIds map[artifactId][]depsetId, depsetIdToDepset map[depsetId]depSetOfFiles) (*AqueryDepset, error) {
if aqueryDepset, containsDepset := a.depsetIdToAqueryDepset[depset.Id]; containsDepset {
- return aqueryDepset, nil
+ return &aqueryDepset, nil
}
transitiveDepsetIds := depset.TransitiveDepSetIds
var directArtifactPaths []string
for _, artifactId := range depset.DirectArtifactIds {
path, pathExists := a.artifactIdToPath[artifactId]
if !pathExists {
- return AqueryDepset{}, fmt.Errorf("undefined input artifactId %d", artifactId)
+ return nil, fmt.Errorf("undefined input artifactId %d", artifactId)
}
// Filter out any inputs which are universally dropped, and swap middleman
// artifacts with their corresponding depsets.
@@ -226,6 +223,7 @@
transitiveDepsetIds = append(transitiveDepsetIds, depsetsToUse...)
} else if strings.HasSuffix(path, py3wrapperFileName) ||
strings.HasPrefix(path, "../bazel_tools") {
+ continue
// Drop these artifacts.
// See go/python-binary-host-mixed-build for more details.
// 1) Drop py3wrapper.sh, just use python binary, the launcher script generated by the
@@ -241,20 +239,23 @@
for _, childDepsetId := range transitiveDepsetIds {
childDepset, exists := depsetIdToDepset[childDepsetId]
if !exists {
- return AqueryDepset{}, fmt.Errorf("undefined input depsetId %d (referenced by depsetId %d)", childDepsetId, depset.Id)
+ if _, empty := a.emptyDepsetIds[childDepsetId]; empty {
+ continue
+ } else {
+ return nil, fmt.Errorf("undefined input depsetId %d (referenced by depsetId %d)", childDepsetId, depset.Id)
+ }
}
- childAqueryDepset, err := a.populateDepsetMaps(childDepset, middlemanIdToDepsetIds, depsetIdToDepset)
- if err != nil {
- return AqueryDepset{}, err
+ if childAqueryDepset, err := a.populateDepsetMaps(childDepset, middlemanIdToDepsetIds, depsetIdToDepset); err != nil {
+ return nil, err
+ } else if childAqueryDepset == nil {
+ continue
+ } else {
+ childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
}
- childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
}
if len(directArtifactPaths) == 0 && len(childDepsetHashes) == 0 {
- // We could omit this depset altogether but that requires cleanup on
- // transitive dependents.
- // As a simpler alternative, we use this sentinel file as a dependency.
- directArtifactPaths = append(directArtifactPaths, bazelToolsDependencySentinel)
- a.bazelToolsDependencySentinelNeeded = true
+ a.emptyDepsetIds[depset.Id] = struct{}{}
+ return nil, nil
}
aqueryDepset := AqueryDepset{
ContentHash: depsetContentHash(directArtifactPaths, childDepsetHashes),
@@ -263,7 +264,7 @@
}
a.depsetIdToAqueryDepset[depset.Id] = aqueryDepset
a.depsetHashToAqueryDepset[aqueryDepset.ContentHash] = aqueryDepset
- return aqueryDepset, nil
+ return &aqueryDepset, nil
}
// getInputPaths flattens the depsets of the given IDs and returns all transitive
@@ -392,14 +393,6 @@
}
var buildStatements []BuildStatement
- if aqueryHandler.bazelToolsDependencySentinelNeeded {
- buildStatements = append(buildStatements, BuildStatement{
- Command: fmt.Sprintf("touch '%s'", bazelToolsDependencySentinel),
- OutputPaths: []string{bazelToolsDependencySentinel},
- Mnemonic: bazelToolsDependencySentinel,
- })
- }
-
for _, actionEntry := range aqueryResult.Actions {
if shouldSkipAction(actionEntry) {
continue
@@ -484,7 +477,9 @@
var hashes []string
for _, depsetId := range inputDepsetIds {
if aqueryDepset, exists := a.depsetIdToAqueryDepset[depsetId]; !exists {
- return nil, fmt.Errorf("undefined input depsetId %d", depsetId)
+ if _, empty := a.emptyDepsetIds[depsetId]; !empty {
+ return nil, fmt.Errorf("undefined (not even empty) input depsetId %d", depsetId)
+ }
} else {
hashes = append(hashes, aqueryDepset.ContentHash)
}
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 2eacafa..4d1503e 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -227,7 +227,7 @@
return
}
_, _, err = AqueryBuildStatements(data)
- assertError(t, err, "undefined input depsetId 2")
+ assertError(t, err, "undefined (not even empty) input depsetId 2")
}
func TestInvalidInputDepsetIdFromDepset(t *testing.T) {
@@ -584,13 +584,18 @@
{ "id": 60, "label": ".."}
]
}`
+ /* depsets
+ 1111 2222
+ / \ |
+ ../dep2 ../bazel_tools/dep1
+ */
data, err := JsonToActionGraphContainer(inputString)
if err != nil {
t.Error(err)
return
}
actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
- if len(actualDepsets) != 2 {
+ if len(actualDepsets) != 1 {
t.Errorf("expected 1 depset but found %#v", actualDepsets)
return
}
@@ -624,6 +629,82 @@
}
}
+func TestBazelOutRemovalFromTransitiveInputDepsets(t *testing.T) {
+ const inputString = `{
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 10 },
+ { "id": 2, "path_fragment_id": 20 },
+ { "id": 3, "path_fragment_id": 30 }],
+ "dep_set_of_files": [{
+ "id": 1111,
+ "transitive_dep_set_ids": [2222]
+ }, {
+ "id": 2222,
+ "direct_artifact_ids": [3]
+ }, {
+ "id": 3333,
+ "direct_artifact_ids": [3]
+ }, {
+ "id": 4444,
+ "transitive_dep_set_ids": [3333]
+ }],
+ "actions": [{
+ "target_id": 100,
+ "action_key": "x",
+ "input_dep_set_ids": [1111, 4444],
+ "mnemonic": "x",
+ "arguments": ["bogus", "command"],
+ "output_ids": [2],
+ "primary_output_id": 1
+ }],
+ "path_fragments": [
+ { "id": 10, "label": "input" },
+ { "id": 20, "label": "output" },
+ { "id": 30, "label": "dep", "parent_id": 50 },
+ { "id": 50, "label": "bazel_tools", "parent_id": 60 },
+ { "id": 60, "label": ".."}
+ ]
+}`
+ /* depsets
+ 1111 4444
+ || ||
+ 2222 3333
+ | |
+ ../bazel_tools/dep
+ Note: in dep_set_of_files:
+ 1111 appears BEFORE its dependency,2222 while
+ 4444 appears AFTER its dependency 3333
+ and this test shows that that order doesn't affect empty depset pruning
+ */
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
+ if len(actualDepsets) != 0 {
+ t.Errorf("expected 0 depsets but found %#v", actualDepsets)
+ return
+ }
+
+ expectedBuildStatement := BuildStatement{
+ Command: "bogus command",
+ OutputPaths: []string{"output"},
+ Mnemonic: "x",
+ }
+ buildStatementFound := false
+ for _, actualBuildStatement := range actualBuildStatements {
+ if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
+ buildStatementFound = true
+ break
+ }
+ }
+ if !buildStatementFound {
+ t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
+ return
+ }
+}
+
func TestMiddlemenAction(t *testing.T) {
const inputString = `
{
diff --git a/bazel/configurability.go b/bazel/configurability.go
index 2b8753b..4680256 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -343,8 +343,8 @@
}
func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
- if ca.configurationType < other.configurationType {
- return true
+ if ca.configurationType == other.configurationType {
+ return ca.subType < other.subType
}
- return ca.subType < other.subType
+ return ca.configurationType < other.configurationType
}
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 81c60d9..6654191 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -14,7 +14,14 @@
GetCcUnstrippedInfo = &getCcUnstrippedInfoType{}
)
+type CcAndroidMkInfo struct {
+ LocalStaticLibs []string
+ LocalWholeStaticLibs []string
+ LocalSharedLibs []string
+}
+
type CcInfo struct {
+ CcAndroidMkInfo
OutputFiles []string
CcObjectFiles []string
CcSharedLibraryFiles []string
@@ -180,20 +187,33 @@
if abi_diff_info:
abi_diff_files = [f.path for f in abi_diff_info.diff_files.to_list()]
+local_static_libs = []
+local_whole_static_libs = []
+local_shared_libs = []
+androidmk_tag = "//build/bazel/rules/cc:cc_library_common.bzl%CcAndroidMkInfo"
+if androidmk_tag in p:
+ androidmk_info = p[androidmk_tag]
+ local_static_libs = androidmk_info.local_static_libs
+ local_whole_static_libs = androidmk_info.local_whole_static_libs
+ local_shared_libs = androidmk_info.local_shared_libs
+
return json_encode({
- "OutputFiles": outputFiles,
- "CcObjectFiles": ccObjectFiles,
- "CcSharedLibraryFiles": sharedLibraries,
- "CcStaticLibraryFiles": staticLibraries,
- "Includes": includes,
- "SystemIncludes": system_includes,
- "Headers": headers,
- "RootStaticArchives": rootStaticArchives,
- "RootDynamicLibraries": rootSharedLibraries,
- "TidyFiles": tidy_files,
- "TocFile": toc_file,
- "UnstrippedOutput": unstripped,
- "AbiDiffFiles": abi_diff_files,
+ "OutputFiles": outputFiles,
+ "CcObjectFiles": ccObjectFiles,
+ "CcSharedLibraryFiles": sharedLibraries,
+ "CcStaticLibraryFiles": staticLibraries,
+ "Includes": includes,
+ "SystemIncludes": system_includes,
+ "Headers": headers,
+ "RootStaticArchives": rootStaticArchives,
+ "RootDynamicLibraries": rootSharedLibraries,
+ "TidyFiles": tidy_files,
+ "TocFile": toc_file,
+ "UnstrippedOutput": unstripped,
+ "AbiDiffFiles": abi_diff_files,
+ "LocalStaticLibs": [l for l in local_static_libs],
+ "LocalWholeStaticLibs": [l for l in local_whole_static_libs],
+ "LocalSharedLibs": [l for l in local_shared_libs],
})`
}
@@ -237,6 +257,10 @@
if info.signed_compressed_output:
signed_compressed_output = info.signed_compressed_output.path
+mk_info = providers(target).get("//build/bazel/rules/apex:apex_info.bzl%ApexMkInfo")
+if not mk_info:
+ fail("%s did not provide ApexMkInfo" % id_string)
+
return json_encode({
"signed_output": info.signed_output.path,
"signed_compressed_output": signed_compressed_output,
@@ -251,10 +275,12 @@
"backing_libs": info.backing_libs.path,
"bundle_file": info.base_with_config_zip.path,
"installed_files": info.installed_files.path,
+ "make_modules_to_install": mk_info.make_modules_to_install,
})`
}
type ApexInfo struct {
+ // From the ApexInfo provider
SignedOutput string `json:"signed_output"`
SignedCompressedOutput string `json:"signed_compressed_output"`
UnsignedOutput string `json:"unsigned_output"`
@@ -268,6 +294,9 @@
BackingLibs string `json:"backing_libs"`
BundleFile string `json:"bundle_file"`
InstalledFiles string `json:"installed_files"`
+
+ // From the ApexMkInfo provider
+ MakeModulesToInstall []string `json:"make_modules_to_install"`
}
// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
@@ -289,15 +318,32 @@
}
func (g getCcUnstrippedInfoType) StarlarkFunctionBody() string {
- return `unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo"
+ return `
p = providers(target)
output_path = target.files.to_list()[0].path
+
unstripped = output_path
+unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo"
if unstripped_tag in p:
- unstripped = p[unstripped_tag].unstripped.files.to_list()[0].path
+ unstripped_info = p[unstripped_tag]
+ unstripped = unstripped_info.unstripped.files.to_list()[0].path
+
+local_static_libs = []
+local_whole_static_libs = []
+local_shared_libs = []
+androidmk_tag = "//build/bazel/rules/cc:cc_library_common.bzl%CcAndroidMkInfo"
+if androidmk_tag in p:
+ androidmk_info = p[androidmk_tag]
+ local_static_libs = androidmk_info.local_static_libs
+ local_whole_static_libs = androidmk_info.local_whole_static_libs
+ local_shared_libs = androidmk_info.local_shared_libs
+
return json_encode({
"OutputFile": output_path,
"UnstrippedOutput": unstripped,
+ "LocalStaticLibs": [l for l in local_static_libs],
+ "LocalWholeStaticLibs": [l for l in local_whole_static_libs],
+ "LocalSharedLibs": [l for l in local_shared_libs],
})
`
}
@@ -312,6 +358,7 @@
}
type CcUnstrippedInfo struct {
+ CcAndroidMkInfo
OutputFile string
UnstrippedOutput string
}
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 1d30535..7003ce1 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -177,9 +177,11 @@
"backing_libs":"path/to/backing.txt",
"bundle_file": "dir/bundlefile.zip",
"installed_files":"path/to/installed-files.txt",
- "provides_native_libs":[]
+ "provides_native_libs":[],
+ "make_modules_to_install": ["foo","bar"]
}`,
expectedOutput: ApexInfo{
+ // ApexInfo
SignedOutput: "my.apex",
UnsignedOutput: "my.apex.unsigned",
RequiresLibs: []string{"//bionic/libc:libc", "//bionic/libdl:libdl"},
@@ -191,6 +193,9 @@
BackingLibs: "path/to/backing.txt",
BundleFile: "dir/bundlefile.zip",
InstalledFiles: "path/to/installed-files.txt",
+
+ // ApexMkInfo
+ MakeModulesToInstall: []string{"foo", "bar"},
},
},
}
diff --git a/bazel/properties.go b/bazel/properties.go
index 0fca60b..f4acd26 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -73,6 +73,16 @@
}
}
+func SortedConfigurationAxes[T any](m map[ConfigurationAxis]T) []ConfigurationAxis {
+ keys := make([]ConfigurationAxis, 0, len(m))
+ for k := range m {
+ keys = append(keys, k)
+ }
+
+ sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+ return keys
+}
+
// MakeLabelListFromTargetNames creates a LabelList from unqualified target names
// This is a utiltity function for bp2build converters of Soong modules that have 1:many generated targets
func MakeLabelListFromTargetNames(targetNames []string) LabelList {
@@ -152,7 +162,7 @@
ll.Includes = append(ll.Includes, other.Includes...)
}
if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
- ll.Excludes = append(other.Excludes, other.Excludes...)
+ ll.Excludes = append(ll.Excludes, other.Excludes...)
}
}
@@ -412,13 +422,7 @@
// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
- keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
- for k := range la.ConfigurableValues {
- keys = append(keys, k)
- }
-
- sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
- return keys
+ return SortedConfigurationAxes(la.ConfigurableValues)
}
// MakeLabelAttribute turns a string into a LabelAttribute
@@ -608,13 +612,7 @@
// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
- keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
- for k := range ba.ConfigurableValues {
- keys = append(keys, k)
- }
-
- sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
- return keys
+ return SortedConfigurationAxes(ba.ConfigurableValues)
}
// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
@@ -761,13 +759,7 @@
// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
- keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
- for k := range lla.ConfigurableValues {
- keys = append(keys, k)
- }
-
- sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
- return keys
+ return SortedConfigurationAxes(lla.ConfigurableValues)
}
// Append all values, including os and arch specific ones, from another
@@ -888,7 +880,7 @@
// then remove all config-specific excludes
allLabels := baseLabels.deepCopy()
allLabels.Append(val)
- lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
+ lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: allLabels.Excludes})
}
// After going through all configs, delete the duplicates in the config
@@ -1145,13 +1137,7 @@
// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis {
- keys := make([]ConfigurationAxis, 0, len(sa.ConfigurableValues))
- for k := range sa.ConfigurableValues {
- keys = append(keys, k)
- }
-
- sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
- return keys
+ return SortedConfigurationAxes(sa.ConfigurableValues)
}
// Collapse reduces the configurable axes of the string attribute to a single axis.
@@ -1353,13 +1339,7 @@
// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
- keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
- for k := range sla.ConfigurableValues {
- keys = append(keys, k)
- }
-
- sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
- return keys
+ return SortedConfigurationAxes(sla.ConfigurableValues)
}
// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index 8729381..cf03eb5 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -231,6 +231,7 @@
"all_include",
"arm_exclude",
"android_exclude",
+ "product_config_exclude",
},
[]string{"all_exclude"},
),
@@ -251,10 +252,10 @@
"a": makeLabelList([]string{}, []string{"not_in_value"}),
"b": makeLabelList([]string{"b_val"}, []string{}),
"c": makeLabelList([]string{"c_val"}, []string{}),
- ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2"}, []string{}),
+ ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2", "all_exclude"}, []string{}),
},
ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): labelListSelectValues{
- "a": makeLabelList([]string{}, []string{"not_in_value"}),
+ "a": makeLabelList([]string{}, []string{"product_config_exclude"}),
},
},
}
@@ -287,6 +288,10 @@
"c": makeLabels("c_val"),
ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"),
},
+ ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): {
+ "a": nilLabels,
+ ConditionsDefaultConfigKey: makeLabels("product_config_exclude"),
+ },
}
for _, axis := range attr.SortedConfigurationAxes() {
if _, ok := expectedConfiguredIncludes[axis]; !ok {
diff --git a/bp2build/android_app_conversion_test.go b/bp2build/android_app_conversion_test.go
index 067e34f..4d18f83 100644
--- a/bp2build/android_app_conversion_test.go
+++ b/bp2build/android_app_conversion_test.go
@@ -227,3 +227,82 @@
}),
}})
}
+
+func TestAndroidAppKotlinSrcs(t *testing.T) {
+ runAndroidAppTestCase(t, Bp2buildTestCase{
+ Description: "Android app with kotlin sources and common_srcs",
+ ModuleTypeUnderTest: "android_app",
+ ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+ Filesystem: map[string]string{
+ "res/res.png": "",
+ },
+ Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+ name: "foo",
+ srcs: ["a.java", "b.kt"],
+ certificate: ":foocert",
+ manifest: "fooManifest.xml",
+ libs: ["barLib"]
+}
+java_library{
+ name: "barLib",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "barLib", AttrNameToString{}),
+ MakeNeverlinkDuplicateTarget("java_library", "barLib"),
+ MakeBazelTarget("android_library", "foo_kt", AttrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.kt",
+ ]`,
+ "manifest": `"fooManifest.xml"`,
+ "resource_files": `["res/res.png"]`,
+ "deps": `[":barLib-neverlink"]`,
+ }),
+ MakeBazelTarget("android_binary", "foo", AttrNameToString{
+ "deps": `[":foo_kt"]`,
+ "certificate": `":foocert"`,
+ "manifest": `"fooManifest.xml"`,
+ }),
+ }})
+}
+
+func TestAndroidAppCommonSrcs(t *testing.T) {
+ runAndroidAppTestCase(t, Bp2buildTestCase{
+ Description: "Android app with common_srcs",
+ ModuleTypeUnderTest: "android_app",
+ ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+ Filesystem: map[string]string{
+ "res/res.png": "",
+ },
+ Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ common_srcs: ["b.kt"],
+ certificate: "foocert",
+ manifest: "fooManifest.xml",
+ libs: ["barLib"],
+}
+java_library{
+ name: "barLib",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "barLib", AttrNameToString{}),
+ MakeNeverlinkDuplicateTarget("java_library", "barLib"),
+ MakeBazelTarget("android_library", "foo_kt", AttrNameToString{
+ "srcs": `["a.java"]`,
+ "common_srcs": `["b.kt"]`,
+ "manifest": `"fooManifest.xml"`,
+ "resource_files": `["res/res.png"]`,
+ "deps": `[":barLib-neverlink"]`,
+ }),
+ MakeBazelTarget("android_binary", "foo", AttrNameToString{
+ "deps": `[":foo_kt"]`,
+ "certificate_name": `"foocert"`,
+ "manifest": `"fooManifest.xml"`,
+ }),
+ }})
+}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 052bc32..c11a50d 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -2825,7 +2825,7 @@
expectedBazelTargets := []string{
MakeBazelTarget(
"cc_api_library_headers",
- "libfoo.systemapi.headers",
+ "libfoo.module-libapi.headers",
AttrNameToString{
"export_includes": `["dir1"]`,
}),
@@ -2842,18 +2842,18 @@
"api": `"libfoo.map.txt"`,
"library_name": `"libfoo"`,
"api_surfaces": `[
- "systemapi",
+ "module-libapi",
"vendorapi",
]`,
"hdrs": `[
- ":libfoo.systemapi.headers",
+ ":libfoo.module-libapi.headers",
":libfoo.vendorapi.headers",
]`,
}),
}
RunApiBp2BuildTestCase(t, cc.RegisterLibraryBuildComponents, Bp2buildTestCase{
Blueprint: bp,
- Description: "cc API contributions to systemapi and vendorapi",
+ Description: "cc API contributions to module-libapi and vendorapi",
ExpectedBazelTargets: expectedBazelTargets,
})
}
@@ -2872,8 +2872,8 @@
stubs: {symbol_file: "a.map.txt"},
}`,
expectedApi: `"a.map.txt"`,
- expectedApiSurfaces: `["systemapi"]`,
- description: "Library that contributes to systemapi",
+ expectedApiSurfaces: `["module-libapi"]`,
+ description: "Library that contributes to module-libapi",
},
{
bp: `
@@ -2894,10 +2894,10 @@
}`,
expectedApi: `"a.map.txt"`,
expectedApiSurfaces: `[
- "systemapi",
+ "module-libapi",
"vendorapi",
]`,
- description: "Library that contributes to systemapi and vendorapi",
+ description: "Library that contributes to module-libapi and vendorapi",
},
}
for _, testCase := range testCases {
@@ -3437,23 +3437,23 @@
ExpectedBazelTargets: []string{
MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
"implementation_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
- "//build/bazel/rules/apex:non_apex": [":buh__BP2BUILD__MISSING__DEP"],
- "//conditions:default": [],
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":buh__BP2BUILD__MISSING__DEP"],
})`,
"implementation_dynamic_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
- "//build/bazel/rules/apex:non_apex": [":bar__BP2BUILD__MISSING__DEP"],
- "//conditions:default": [],
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"],
})`,
"local_includes": `["."]`,
}),
MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
"implementation_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
- "//build/bazel/rules/apex:non_apex": [":buh__BP2BUILD__MISSING__DEP"],
- "//conditions:default": [],
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":buh__BP2BUILD__MISSING__DEP"],
})`,
"implementation_dynamic_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
- "//build/bazel/rules/apex:non_apex": [":bar__BP2BUILD__MISSING__DEP"],
- "//conditions:default": [],
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"],
})`,
"local_includes": `["."]`,
}),
@@ -3483,16 +3483,16 @@
ExpectedBazelTargets: []string{
MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
"implementation_dynamic_deps": `select({
- "//build/bazel/rules/apex:non_apex": [":bar__BP2BUILD__MISSING__DEP"],
- "//conditions:default": [],
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"],
})`,
"dynamic_deps": `select({
- "//build/bazel/rules/apex:non_apex": [":baz__BP2BUILD__MISSING__DEP"],
- "//conditions:default": [],
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":baz__BP2BUILD__MISSING__DEP"],
})`,
"deps": `select({
- "//build/bazel/rules/apex:non_apex": [":abc__BP2BUILD__MISSING__DEP"],
- "//conditions:default": [],
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":abc__BP2BUILD__MISSING__DEP"],
})`,
"local_includes": `["."]`,
}),
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 1377c6b..eab84e1 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -58,6 +58,7 @@
exclude_srcs: ["a/b/exclude.c"],
sdk_version: "current",
min_sdk_version: "29",
+ crt: true,
}
`,
ExpectedBazelTargets: []string{
@@ -76,6 +77,7 @@
"system_dynamic_deps": `[]`,
"sdk_version": `"current"`,
"min_sdk_version": `"29"`,
+ "crt": "True",
}),
},
})
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 4244956..2a0a78e 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -160,6 +160,12 @@
// select statements.
func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) {
var value reflect.Value
+ // configurableAttrs is the list of individual select statements to be
+ // concatenated together. These select statements should be along different
+ // axes. For example, one element may be
+ // `select({"//color:red": "one", "//color:green": "two"})`, and the second
+ // element may be `select({"//animal:cat": "three", "//animal:dog": "four"}).
+ // These selects should be sorted by axis identifier.
var configurableAttrs []selects
var prepend bool
var defaultSelectValue *string
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index 93a6174..0784f4b 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -715,3 +715,43 @@
},
})
}
+
+func TestJavaLibraryArchVariantLibs(t *testing.T) {
+ runJavaLibraryTestCase(t, Bp2buildTestCase{
+ Description: "java_library with arch variant libs",
+ Blueprint: `java_library {
+ name: "java-lib-1",
+ srcs: ["a.java"],
+ libs: ["java-lib-2"],
+ target: {
+ android: {
+ libs: ["java-lib-3"],
+ },
+ },
+ bazel_module: { bp2build_available: true },
+}
+
+ java_library{
+ name: "java-lib-2",
+}
+
+ java_library{
+ name: "java-lib-3",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+ "srcs": `["a.java"]`,
+ "deps": `[":java-lib-2-neverlink"] + select({
+ "//build/bazel/platforms/os:android": [":java-lib-3-neverlink"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+ MakeBazelTarget("java_library", "java-lib-2", AttrNameToString{}),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-2"),
+ MakeBazelTarget("java_library", "java-lib-3", AttrNameToString{}),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-3"),
+ },
+ })
+}
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index d6e5cf3..7e29fac 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -188,6 +188,10 @@
)
func (metrics *CodegenMetrics) AddConvertedModule(m blueprint.Module, moduleType string, dir string, conversionType ConversionType) {
+ //a package module has empty name
+ if moduleType == "package" {
+ return
+ }
// Undo prebuilt_ module name prefix modifications
moduleName := android.RemoveOptionalPrebuiltPrefix(m.Name())
metrics.serialized.ConvertedModules = append(metrics.serialized.ConvertedModules, moduleName)
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index 89be767..ba42f34 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -742,6 +742,101 @@
)`}})
}
+func TestSoongConfigModuleType_UnsetConditionsExcludeLibs(t *testing.T) {
+ bp := `
+soong_config_string_variable {
+ name: "library_linking_strategy",
+ values: [
+ "prefer_static",
+ ],
+}
+
+soong_config_module_type {
+ name: "library_linking_strategy_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "ANDROID",
+ variables: ["library_linking_strategy"],
+ properties: ["shared_libs"],
+}
+
+library_linking_strategy_cc_defaults {
+ name: "library_linking_strategy_lib_a_defaults",
+ soong_config_variables: {
+ library_linking_strategy: {
+ prefer_static: {},
+ conditions_default: {
+ shared_libs: [
+ "lib_a",
+ ],
+ },
+ },
+ },
+}
+
+library_linking_strategy_cc_defaults {
+ name: "library_linking_strategy_merged_defaults",
+ defaults: ["library_linking_strategy_lib_a_defaults"],
+ host_supported: true,
+ soong_config_variables: {
+ library_linking_strategy: {
+ prefer_static: {},
+ conditions_default: {
+ shared_libs: [
+ "lib_b",
+ "lib_c",
+ ],
+ },
+ },
+ },
+ exclude_shared_libs: ["lib_a"],
+}
+
+cc_binary {
+ name: "library_linking_strategy_sample_binary",
+ defaults: ["library_linking_strategy_merged_defaults"],
+ include_build_directory: false,
+}
+
+cc_binary {
+ name: "library_linking_strategy_sample_binary_with_excludes",
+ defaults: ["library_linking_strategy_merged_defaults"],
+ exclude_shared_libs: ["lib_c"],
+ include_build_directory: false,
+}`
+
+ otherDeps := `
+cc_library { name: "lib_a", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_b", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_c", bazel_module: { bp2build_available: false } }
+`
+
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for library_linking_strategy",
+ ModuleTypeUnderTest: "cc_binary",
+ ModuleTypeUnderTestFactory: cc.BinaryFactory,
+ Blueprint: bp,
+ Filesystem: map[string]string{
+ "foo/bar/Android.bp": otherDeps,
+ },
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("cc_binary", "library_linking_strategy_sample_binary", AttrNameToString{
+ "dynamic_deps": `select({
+ "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+ "//conditions:default": [
+ "//foo/bar:lib_b",
+ "//foo/bar:lib_c",
+ ],
+ })`,
+ }),
+ MakeBazelTargetNoRestrictions("cc_binary", "library_linking_strategy_sample_binary_with_excludes", AttrNameToString{
+ "dynamic_deps": `select({
+ "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+ "//conditions:default": ["//foo/bar:lib_b"],
+ })`,
+ }),
+ }})
+}
+
func TestSoongConfigModuleType_Defaults(t *testing.T) {
bp := `
soong_config_string_variable {
diff --git a/bp2build/testing.go b/bp2build/testing.go
index c340a8f..92a9bf1 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -258,6 +258,7 @@
}
func (b BazelTestResult) CompareBazelTargets(t *testing.T, description string, expectedContents []string, actualTargets BazelTargets) {
+ t.Helper()
if actualCount, expectedCount := len(actualTargets), len(expectedContents); actualCount != expectedCount {
t.Errorf("%s: Expected %d bazel target (%s), got %d (%s)",
description, expectedCount, expectedContents, actualCount, actualTargets)
diff --git a/cc/api_level.go b/cc/api_level.go
index fdff5cb..a5571f3 100644
--- a/cc/api_level.go
+++ b/cc/api_level.go
@@ -20,7 +20,9 @@
"android/soong/android"
)
-func minApiForArch(ctx android.EarlyModuleContext,
+// MinApiLevelForArch returns the ApiLevel for the Android version that
+// first supported the architecture.
+func MinApiForArch(ctx android.EarlyModuleContext,
arch android.ArchType) android.ApiLevel {
switch arch {
@@ -38,7 +40,7 @@
func nativeApiLevelFromUser(ctx android.BaseModuleContext,
raw string) (android.ApiLevel, error) {
- min := minApiForArch(ctx, ctx.Arch().ArchType)
+ min := MinApiForArch(ctx, ctx.Arch().ArchType)
if raw == "minimum" {
return min, nil
}
diff --git a/cc/binary.go b/cc/binary.go
index 54c1abc..2f8ee7f 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -591,6 +591,8 @@
outputFilePath := android.PathForBazelOut(ctx, info.OutputFile)
handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
handler.module.linker.(*binaryDecorator).unstrippedOutputFile = android.PathForBazelOut(ctx, info.UnstrippedOutput)
+
+ handler.module.setAndroidMkVariablesFromCquery(info.CcAndroidMkInfo)
}
func binaryBp2buildAttrs(ctx android.TopDownMutatorContext, m *Module) binaryAttributes {
diff --git a/cc/bp2build.go b/cc/bp2build.go
index aea1fa1..6c5505a 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -702,7 +702,13 @@
compilerAttrs := compilerAttributes{}
linkerAttrs := linkerAttributes{}
- for axis, configs := range axisToConfigs {
+ // Iterate through these axes in a deterministic order. This is required
+ // because processing certain dependencies may result in concatenating
+ // elements along other axes. (For example, processing NoConfig may result
+ // in elements being added to InApex). This is thus the only way to ensure
+ // that the order of entries in each list is in a predictable order.
+ for _, axis := range bazel.SortedConfigurationAxes(axisToConfigs) {
+ configs := axisToConfigs[axis]
for cfg := range configs {
var allHdrs []string
if baseCompilerProps, ok := archVariantCompilerProps[axis][cfg].(*BaseCompilerProperties); ok {
@@ -979,34 +985,22 @@
// resolveTargetApex re-adds the shared and static libs in target.apex.exclude_shared|static_libs props to non-apex variant
// since all libs are already excluded by default
-func (la *linkerAttributes) resolveTargetApexProp(ctx android.BazelConversionPathContext, isBinary bool, props *BaseLinkerProperties) {
- sharedLibsForNonApex := maybePartitionExportedAndImplementationsDeps(
- ctx,
- true,
- props.Target.Apex.Exclude_shared_libs,
- props.Export_shared_lib_headers,
- bazelLabelForSharedDeps,
- )
- dynamicDeps := la.dynamicDeps.SelectValue(bazel.InApexAxis, bazel.NonApex)
- implDynamicDeps := la.implementationDynamicDeps.SelectValue(bazel.InApexAxis, bazel.NonApex)
- (&dynamicDeps).Append(sharedLibsForNonApex.export)
- (&implDynamicDeps).Append(sharedLibsForNonApex.implementation)
- la.dynamicDeps.SetSelectValue(bazel.InApexAxis, bazel.NonApex, dynamicDeps)
- la.implementationDynamicDeps.SetSelectValue(bazel.InApexAxis, bazel.NonApex, implDynamicDeps)
+func (la *linkerAttributes) resolveTargetApexProp(ctx android.BazelConversionPathContext, props *BaseLinkerProperties) {
+ excludeSharedLibs := bazelLabelForSharedDeps(ctx, props.Target.Apex.Exclude_shared_libs)
+ sharedExcludes := bazel.LabelList{Excludes: excludeSharedLibs.Includes}
+ sharedExcludesLabelList := bazel.LabelListAttribute{}
+ sharedExcludesLabelList.SetSelectValue(bazel.InApexAxis, bazel.InApex, sharedExcludes)
- staticLibsForNonApex := maybePartitionExportedAndImplementationsDeps(
- ctx,
- !isBinary,
- props.Target.Apex.Exclude_static_libs,
- props.Export_static_lib_headers,
- bazelLabelForSharedDeps,
- )
- deps := la.deps.SelectValue(bazel.InApexAxis, bazel.NonApex)
- implDeps := la.implementationDeps.SelectValue(bazel.InApexAxis, bazel.NonApex)
- (&deps).Append(staticLibsForNonApex.export)
- (&implDeps).Append(staticLibsForNonApex.implementation)
- la.deps.SetSelectValue(bazel.InApexAxis, bazel.NonApex, deps)
- la.implementationDeps.SetSelectValue(bazel.InApexAxis, bazel.NonApex, implDeps)
+ la.dynamicDeps.Append(sharedExcludesLabelList)
+ la.implementationDynamicDeps.Append(sharedExcludesLabelList)
+
+ excludeStaticLibs := bazelLabelForStaticDeps(ctx, props.Target.Apex.Exclude_static_libs)
+ staticExcludes := bazel.LabelList{Excludes: excludeStaticLibs.Includes}
+ staticExcludesLabelList := bazel.LabelListAttribute{}
+ staticExcludesLabelList.SetSelectValue(bazel.InApexAxis, bazel.InApex, staticExcludes)
+
+ la.deps.Append(staticExcludesLabelList)
+ la.implementationDeps.Append(staticExcludesLabelList)
}
func (la *linkerAttributes) bp2buildForAxisAndConfig(ctx android.BazelConversionPathContext, isBinary bool, axis bazel.ConfigurationAxis, config string, props *BaseLinkerProperties) {
@@ -1040,8 +1034,7 @@
ctx,
!isBinary,
staticLibs,
- // Exclude static libs in Exclude_static_libs and Target.Apex.Exclude_static_libs props
- append(props.Exclude_static_libs, props.Target.Apex.Exclude_static_libs...),
+ props.Exclude_static_libs,
props.Export_static_lib_headers,
bazelLabelForStaticDepsExcludes,
)
@@ -1080,14 +1073,13 @@
ctx,
!isBinary,
sharedLibs,
- // Exclude shared libs in Exclude_shared_libs and Target.Apex.Exclude_shared_libs props
- append(props.Exclude_shared_libs, props.Target.Apex.Exclude_shared_libs...),
+ props.Exclude_shared_libs,
props.Export_shared_lib_headers,
bazelLabelForSharedDepsExcludes,
)
la.dynamicDeps.SetSelectValue(axis, config, sharedDeps.export)
la.implementationDynamicDeps.SetSelectValue(axis, config, sharedDeps.implementation)
- la.resolveTargetApexProp(ctx, isBinary, props)
+ la.resolveTargetApexProp(ctx, props)
if axis == bazel.NoConfigAxis || (axis == bazel.OsConfigurationAxis && config == bazel.OsAndroid) {
// If a dependency in la.implementationDynamicDeps has stubs, its stub variant should be
diff --git a/cc/cc.go b/cc/cc.go
index b194360..c33c5e3 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -28,6 +28,7 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/bazel/cquery"
"android/soong/cc/config"
"android/soong/fuzz"
"android/soong/genrule"
@@ -1445,6 +1446,8 @@
}
func InstallToBootstrap(name string, config android.Config) bool {
+ // NOTE: also update //build/bazel/rules/apex/cc.bzl#_installed_to_bootstrap
+ // if this list is updated.
if name == "libclang_rt.hwasan" {
return true
}
@@ -1870,7 +1873,8 @@
// in any of the --bazel-mode(s). This filters at the module level and takes
// precedence over the allowlists in allowlists/allowlists.go.
func (c *Module) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
- if c.testBinary() && !android.InList(c.Name(), mixedBuildSupportedCcTest) {
+ _, isForTesting := ctx.Config().BazelContext.(android.MockBazelContext)
+ if c.testBinary() && !android.InList(c.Name(), mixedBuildSupportedCcTest) && !isForTesting {
// Per-module rollout of mixed-builds for cc_test modules.
return false
}
@@ -2085,6 +2089,12 @@
}
}
+func (c *Module) setAndroidMkVariablesFromCquery(info cquery.CcAndroidMkInfo) {
+ c.Properties.AndroidMkSharedLibs = info.LocalSharedLibs
+ c.Properties.AndroidMkStaticLibs = info.LocalStaticLibs
+ c.Properties.AndroidMkWholeStaticLibs = info.LocalWholeStaticLibs
+}
+
func (c *Module) toolchain(ctx android.BaseModuleContext) config.Toolchain {
if c.cachedToolchain == nil {
c.cachedToolchain = config.FindToolchainWithContext(ctx)
@@ -2216,6 +2226,13 @@
if err != nil {
ctx.PropertyErrorf("min_sdk_version", err.Error())
}
+
+ // Raise the minSdkVersion to the minimum supported for the architecture.
+ minApiForArch := MinApiForArch(ctx, m.Target().Arch.ArchType)
+ if apiLevel.LessThan(minApiForArch) {
+ apiLevel = minApiForArch
+ }
+
return []blueprint.Variation{
{Mutator: "sdk", Variation: "sdk"},
{Mutator: "version", Variation: apiLevel.String()},
@@ -3697,7 +3714,7 @@
// This allows introducing new architectures in the platform that
// need to be included in apexes that normally require an older
// min_sdk_version.
- minApiForArch := minApiForArch(ctx, c.Target().Arch.ArchType)
+ minApiForArch := MinApiForArch(ctx, c.Target().Arch.ArchType)
if sdkVersion.LessThan(minApiForArch) {
sdkVersion = minApiForArch
}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 8293f2d..62adfd3 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -25,6 +25,7 @@
"testing"
"android/soong/android"
+ "android/soong/bazel/cquery"
)
func TestMain(m *testing.M) {
@@ -3054,6 +3055,253 @@
checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins"}, module)
}
+func TestLibDepAndroidMkExportInMixedBuilds(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "static_dep",
+ }
+ cc_library {
+ name: "whole_static_dep",
+ }
+ cc_library {
+ name: "shared_dep",
+ }
+ cc_library {
+ name: "lib",
+ bazel_module: { label: "//:lib" },
+ static_libs: ["static_dep"],
+ whole_static_libs: ["whole_static_dep"],
+ shared_libs: ["shared_dep"],
+ }
+ cc_test {
+ name: "test",
+ bazel_module: { label: "//:test" },
+ static_libs: ["static_dep"],
+ whole_static_libs: ["whole_static_dep"],
+ shared_libs: ["shared_dep"],
+ gtest: false,
+ }
+ cc_binary {
+ name: "binary",
+ bazel_module: { label: "//:binary" },
+ static_libs: ["static_dep"],
+ whole_static_libs: ["whole_static_dep"],
+ shared_libs: ["shared_dep"],
+ }
+ cc_library_headers {
+ name: "lib_headers",
+ bazel_module: { label: "//:lib_headers" },
+ static_libs: ["static_dep"],
+ whole_static_libs: ["whole_static_dep"],
+ shared_libs: ["shared_dep"],
+ }
+ cc_prebuilt_library {
+ name: "lib_prebuilt",
+ bazel_module: { label: "//:lib_prebuilt" },
+ static_libs: ["static_dep"],
+ whole_static_libs: ["whole_static_dep"],
+ shared_libs: ["shared_dep"],
+ }
+ `
+
+ testCases := []struct {
+ name string
+ moduleName string
+ variant string
+ androidMkInfo cquery.CcAndroidMkInfo
+ }{
+ {
+ name: "shared lib",
+ moduleName: "lib",
+ variant: "android_arm64_armv8-a_shared",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "static lib",
+ moduleName: "lib",
+ variant: "android_arm64_armv8-a_static",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "cc_test arm64",
+ moduleName: "test",
+ variant: "android_arm64_armv8-a",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "cc_test arm",
+ moduleName: "test",
+ variant: "android_arm_armv7-a-neon",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "cc_binary",
+ moduleName: "binary",
+ variant: "android_arm64_armv8-a",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "cc_library_headers",
+ moduleName: "lib_headers",
+ variant: "android_arm64_armv8-a",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "prebuilt lib static",
+ moduleName: "lib_prebuilt",
+ variant: "android_arm64_armv8-a_static",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "prebuilt lib shared",
+ moduleName: "lib_prebuilt",
+ variant: "android_arm64_armv8-a_shared",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ }
+
+ outputBaseDir := "out/bazel"
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: outputBaseDir,
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//:lib": cquery.CcInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ RootDynamicLibraries: []string{""},
+ },
+ "//:lib_bp2build_cc_library_static": cquery.CcInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ RootStaticArchives: []string{""},
+ },
+ "//:lib_headers": cquery.CcInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ OutputFiles: []string{""},
+ },
+ "//:lib_prebuilt": cquery.CcInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ },
+ "//:lib_prebuilt_bp2build_cc_library_static": cquery.CcInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ },
+ },
+ LabelToCcBinary: map[string]cquery.CcUnstrippedInfo{
+ "//:test": cquery.CcUnstrippedInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ },
+ "//:binary": cquery.CcUnstrippedInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ },
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+ ctx := result.TestContext
+
+ module := ctx.ModuleForTests(tc.moduleName, tc.variant).Module().(*Module)
+ entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
+ if !reflect.DeepEqual(module.Properties.AndroidMkStaticLibs, tc.androidMkInfo.LocalStaticLibs) {
+ t.Errorf("incorrect static_libs"+
+ "\nactual: %v"+
+ "\nexpected: %v",
+ module.Properties.AndroidMkStaticLibs,
+ tc.androidMkInfo.LocalStaticLibs,
+ )
+ }
+ staticDepsDiffer, missingStaticDeps, additionalStaticDeps := android.ListSetDifference(
+ entries.EntryMap["LOCAL_STATIC_LIBRARIES"],
+ tc.androidMkInfo.LocalStaticLibs,
+ )
+ if staticDepsDiffer {
+ t.Errorf(
+ "expected LOCAL_STATIC_LIBRARIES to be %q but was %q; missing: %q; extra %q",
+ tc.androidMkInfo.LocalStaticLibs,
+ entries.EntryMap["LOCAL_STATIC_LIBRARIES"],
+ missingStaticDeps,
+ additionalStaticDeps,
+ )
+ }
+
+ if !reflect.DeepEqual(module.Properties.AndroidMkWholeStaticLibs, tc.androidMkInfo.LocalWholeStaticLibs) {
+ t.Errorf("expected module.Properties.AndroidMkWholeStaticLibs to be %q, but was %q",
+ tc.androidMkInfo.LocalWholeStaticLibs,
+ module.Properties.AndroidMkWholeStaticLibs,
+ )
+ }
+ wholeStaticDepsDiffer, missingWholeStaticDeps, additionalWholeStaticDeps := android.ListSetDifference(
+ entries.EntryMap["LOCAL_WHOLE_STATIC_LIBRARIES"],
+ tc.androidMkInfo.LocalWholeStaticLibs,
+ )
+ if wholeStaticDepsDiffer {
+ t.Errorf(
+ "expected LOCAL_WHOLE_STATIC_LIBRARIES to be %q but was %q; missing: %q; extra %q",
+ tc.androidMkInfo.LocalWholeStaticLibs,
+ entries.EntryMap["LOCAL_WHOLE_STATIC_LIBRARIES"],
+ missingWholeStaticDeps,
+ additionalWholeStaticDeps,
+ )
+ }
+
+ if !reflect.DeepEqual(module.Properties.AndroidMkSharedLibs, tc.androidMkInfo.LocalSharedLibs) {
+ t.Errorf("incorrect shared_libs"+
+ "\nactual: %v"+
+ "\nexpected: %v",
+ module.Properties.AndroidMkSharedLibs,
+ tc.androidMkInfo.LocalSharedLibs,
+ )
+ }
+ sharedDepsDiffer, missingSharedDeps, additionalSharedDeps := android.ListSetDifference(
+ entries.EntryMap["LOCAL_SHARED_LIBRARIES"],
+ tc.androidMkInfo.LocalSharedLibs,
+ )
+ if sharedDepsDiffer {
+ t.Errorf(
+ "expected LOCAL_SHARED_LIBRARIES to be %q but was %q; missing %q; extra %q",
+ tc.androidMkInfo.LocalSharedLibs,
+ entries.EntryMap["LOCAL_SHARED_LIBRARIES"],
+ missingSharedDeps,
+ additionalSharedDeps,
+ )
+ }
+ })
+ }
+}
+
var compilerFlagsTestCases = []struct {
in string
out bool
diff --git a/cc/config/global.go b/cc/config/global.go
index 2205c9e..d557c0b 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -386,30 +386,17 @@
return strings.Join(deviceGlobalCflags, " ")
})
- // Export the static default NoOverrideGlobalCflags and NoOverride64GlobalCflags to Bazel.
+ // Export the static default NoOverrideGlobalCflags to Bazel.
exportedVars.ExportStringList("NoOverrideGlobalCflags", noOverrideGlobalCflags)
- exportedVars.ExportStringList("NoOverride64GlobalCflags", noOverride64GlobalCflags)
pctx.VariableFunc("NoOverrideGlobalCflags", func(ctx android.PackageVarContext) string {
flags := noOverrideGlobalCflags
if ctx.Config().IsEnvTrue("LLVM_NEXT") {
flags = append(noOverrideGlobalCflags, llvmNextExtraCommonGlobalCflags...)
- if ctx.Config().Android64() {
- flags = append(noOverride64GlobalCflags)
- }
}
return strings.Join(flags, " ")
})
- // Export the static default NoOverride64GlobalCflags to Bazel.
- exportedVars.ExportStringList("NoOverride64GlobalCflags", noOverride64GlobalCflags)
- pctx.VariableFunc("NoOverride64GlobalCflags", func(ctx android.PackageVarContext) string {
- flags := noOverride64GlobalCflags
- if ctx.Config().IsEnvTrue("LLVM_NEXT") && ctx.Config().Android64() {
- flags = append(noOverride64GlobalCflags, llvmNextExtraCommonGlobalCflags...)
- }
- return strings.Join(flags, " ")
- })
-
+ exportedVars.ExportStringListStaticVariable("NoOverride64GlobalCflags", noOverride64GlobalCflags)
exportedVars.ExportStringListStaticVariable("HostGlobalCflags", hostGlobalCflags)
exportedVars.ExportStringListStaticVariable("NoOverrideExternalGlobalCflags", noOverrideExternalGlobalCflags)
exportedVars.ExportStringListStaticVariable("CommonGlobalCppflags", commonGlobalCppflags)
diff --git a/cc/library.go b/cc/library.go
index 1291f5c..b644728 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -475,10 +475,10 @@
func apiContributionBp2Build(ctx android.TopDownMutatorContext, module *Module) {
apiSurfaces := make([]string, 0)
apiHeaders := make([]string, 0)
- // systemapi (non-null `stubs` property)
+ // module-libapi for apexes (non-null `stubs` property)
if module.HasStubsVariants() {
- apiSurfaces = append(apiSurfaces, android.SystemApi.String())
- apiIncludes := getSystemApiIncludes(ctx, module)
+ apiSurfaces = append(apiSurfaces, android.ModuleLibApi.String())
+ apiIncludes := getModuleLibApiIncludes(ctx, module)
if !apiIncludes.isEmpty() {
createApiHeaderTarget(ctx, apiIncludes)
apiHeaders = append(apiHeaders, apiIncludes.name)
@@ -494,8 +494,8 @@
}
}
// create a target only if this module contributes to an api surface
- // TODO: Currently this does not distinguish systemapi-only headers and vendrorapi-only headers
- // TODO: Update so that systemapi-only headers do not get exported to vendorapi (and vice-versa)
+ // TODO: Currently this does not distinguish modulelibapi-only headers and vendrorapi-only headers
+ // TODO: Update so that modulelibapi-only headers do not get exported to vendorapi (and vice-versa)
if len(apiSurfaces) > 0 {
props := bazel.BazelTargetModuleProperties{
Rule_class: "cc_api_contribution",
@@ -527,8 +527,8 @@
linker := module.linker.(*libraryDecorator)
if llndkApi := linker.Properties.Llndk.Symbol_file; llndkApi != nil {
apiFile = llndkApi
- } else if systemApi := linker.Properties.Stubs.Symbol_file; systemApi != nil {
- apiFile = systemApi
+ } else if moduleLibApi := linker.Properties.Stubs.Symbol_file; moduleLibApi != nil {
+ apiFile = moduleLibApi
} else {
ctx.ModuleErrorf("API surface library does not have any API file")
}
@@ -566,7 +566,8 @@
includes.attrs.Deps.Append(lla)
}
-func getSystemApiIncludes(ctx android.TopDownMutatorContext, c *Module) apiIncludes {
+// includes provided to the module-lib API surface. This API surface is used by apexes.
+func getModuleLibApiIncludes(ctx android.TopDownMutatorContext, c *Module) apiIncludes {
flagProps := c.library.(*libraryDecorator).flagExporter.Properties
linkProps := c.library.(*libraryDecorator).baseLinker.Properties
includes := android.FirstUniqueStrings(flagProps.Export_include_dirs)
@@ -579,7 +580,7 @@
}
return apiIncludes{
- name: c.Name() + ".systemapi.headers",
+ name: c.Name() + ".module-libapi.headers",
attrs: bazelCcApiLibraryHeadersAttributes{
bazelCcLibraryHeadersAttributes: attrs,
},
@@ -936,6 +937,8 @@
// implementation.
i.(*libraryDecorator).collectedSnapshotHeaders = android.Paths{}
}
+
+ handler.module.setAndroidMkVariablesFromCquery(ccInfo.CcAndroidMkInfo)
}
func (library *libraryDecorator) setFlagExporterInfoFromCcInfo(ctx android.ModuleContext, ccInfo cquery.CcInfo) {
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 4d38068..6440ee2 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -88,6 +88,8 @@
// validation will fail. For now, set this to an empty list.
// TODO(cparsons): More closely mirror the collectHeadersForSnapshot implementation.
h.library.collectedSnapshotHeaders = android.Paths{}
+
+ h.module.setAndroidMkVariablesFromCquery(ccInfo.CcAndroidMkInfo)
}
// cc_library_headers contains a set of c/c++ headers which are imported by
@@ -169,7 +171,7 @@
// For API export, create a top-level arch-agnostic target and list the arch-specific targets as its deps
// arch-agnostic includes
- apiIncludes := getSystemApiIncludes(ctx, module)
+ apiIncludes := getModuleLibApiIncludes(ctx, module)
// arch and os specific includes
archApiIncludes, androidOsIncludes := archOsSpecificApiIncludes(ctx, module)
for _, arch := range allArches { // sorted iteration
@@ -186,7 +188,7 @@
}
if !apiIncludes.isEmpty() {
- // override the name from <mod>.systemapi.headers --> <mod>.contribution
+ // override the name from <mod>.module-libapi.headers --> <mod>.contribution
apiIncludes.name = android.ApiContributionTargetName(module.Name())
createApiHeaderTarget(ctx, apiIncludes)
}
diff --git a/cc/object.go b/cc/object.go
index 6cb1a30..11ce793 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -142,6 +142,7 @@
Absolute_includes bazel.StringListAttribute
Stl *string
Linker_script bazel.LabelAttribute
+ Crt *bool
sdkAttributes
}
@@ -208,6 +209,7 @@
Absolute_includes: compilerAttrs.absoluteIncludes,
Stl: compilerAttrs.stl,
Linker_script: linkerScript,
+ Crt: m.linker.(*objectLinker).Properties.Crt,
sdkAttributes: bp2BuildParseSdkAttributes(m),
}
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index af83278..9e62bf8 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -462,6 +462,8 @@
}
h.module.maybeUnhideFromMake()
+
+ h.module.setAndroidMkVariablesFromCquery(ccInfo.CcAndroidMkInfo)
}
func (h *prebuiltLibraryBazelHandler) processStaticBazelQueryResponse(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool {
diff --git a/cc/sanitize.go b/cc/sanitize.go
index c61e5e4..66f459a 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -76,7 +76,7 @@
minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
"-fno-sanitize-recover=integer,undefined"}
hwasanGlobalOptions = []string{"heap_history_size=1023", "stack_history_size=512",
- "export_memory_stats=0", "max_malloc_fill_size=4096", "malloc_fill_byte=0"}
+ "export_memory_stats=0", "max_malloc_fill_size=131072", "malloc_fill_byte=0"}
memtagStackCommonFlags = []string{"-march=armv8-a+memtag"}
hostOnlySanitizeFlags = []string{"-fno-sanitize-recover=all"}
diff --git a/cc/stl.go b/cc/stl.go
index 6353a4a..f1433ef 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -38,9 +38,9 @@
func getNdkStlFamilyAndLinkType(m LinkableInterface) (string, string) {
stl := m.SelectedStl()
switch stl {
- case "ndk_libc++_shared":
+ case "ndk_libc++_shared", "libc++":
return "libc++", "shared"
- case "ndk_libc++_static":
+ case "ndk_libc++_static", "libc++_static":
return "libc++", "static"
case "ndk_system":
return "system", "shared"
@@ -80,7 +80,8 @@
return ""
}
s = deduplicateStlInput(s)
- if ctx.useSdk() && ctx.Device() {
+ archHasNDKStl := ctx.Arch().ArchType != android.Riscv64
+ if ctx.useSdk() && ctx.Device() && archHasNDKStl {
switch s {
case "", "system":
return "ndk_system"
diff --git a/cc/test.go b/cc/test.go
index 4b968dc..5c4d548 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -652,6 +652,8 @@
outputFilePath := android.PathForBazelOut(ctx, info.OutputFile)
handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
handler.module.linker.(*testBinary).unstrippedOutputFile = android.PathForBazelOut(ctx, info.UnstrippedOutput)
+
+ handler.module.setAndroidMkVariablesFromCquery(info.CcAndroidMkInfo)
}
// binaryAttributes contains Bazel attributes corresponding to a cc test
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index 712c7fc..e3d1179 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -359,7 +359,8 @@
}
for _, file := range inputZip.Entries() {
pyPkg := getPackage(file.Name)
- if filepath.Base(file.Name) == "__init__.py" {
+ baseName := filepath.Base(file.Name)
+ if baseName == "__init__.py" || baseName == "__init__.pyc" {
if _, found := initedPackages[pyPkg]; found {
panic(fmt.Errorf("found __init__.py path duplicates during pars merging: %q", file.Name))
}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 8c99988..661bd5d 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -199,11 +199,16 @@
soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
bp2buildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bp2build_metrics.pb")
+ bazelMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bazel_metrics.pb")
+
+ //the profile file generated by Bazel"
+ bazelProfileFile := filepath.Join(logsDir, c.logsPrefix+"analyzed_bazel_profile.txt")
metricsFiles := []string{
buildErrorFile, // build error strings
rbeMetricsFile, // high level metrics related to remote build execution.
bp2buildMetricsFile, // high level metrics related to bp2build.
soongMetricsFile, // high level metrics related to this build system.
+ bazelMetricsFile, // high level metrics related to bazel execution
config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
}
@@ -217,7 +222,7 @@
defer met.Dump(soongMetricsFile)
if !config.SkipMetricsUpload() {
- defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
+ defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, bazelProfileFile, bazelMetricsFile, metricsFiles...)
}
}
diff --git a/docs/map_files.md b/docs/map_files.md
index 1388059..35e8cbb 100644
--- a/docs/map_files.md
+++ b/docs/map_files.md
@@ -148,9 +148,24 @@
### systemapi
-This is a synonym of the `apex` tag. It should be used to clarify that the API
-is an API exposed by the system for an APEX, whereas `apex` should be used for
-APIs exposed by an APEX to the platform or another APEX.
+Indicates that the symbol is exposed by the platform for an apex. Whereas `apex`
+should be used for APIs exposed by an APEX to the platform or another APEX.
+
+May be used in combination with `llndk` if the symbol is exposed to both APEX
+and the LL-NDK.
+
+Since a single library can be installed ether in platform or an apex, but not
+both, a single map.txt file should not contain _both_ # apex and # systemapi symbols.
+
+The granularity between # apex and # systemapi exists to help the API review
+process (b/191371676). These two symbols have very similar lifetime "in
+practice". A #systemapi symbol can be dropped from the next release if we are
+confident that no one is using it. Similarily, #apex can be dropped if we are
+sure that the old platform which used the symbol has reached EOL and thus is no
+longer accepting new APEX updates. Unlike the APIs for apps where we have zero
+control over how APIs are used, we are in a much more controllable environment
+when talking about #systemapi and #apex symbols. So, we have some flexibility
+here when determining the lifetime of a symbol.
### var
diff --git a/java/android_manifest.go b/java/android_manifest.go
index c785310..f6457a0 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -149,13 +149,14 @@
if params.SdkContext != nil {
targetSdkVersion := targetSdkVersionForManifestFixer(ctx, params)
- args = append(args, "--targetSdkVersion ", targetSdkVersion)
if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
deps = append(deps, ApiFingerprintPath(ctx))
}
+ args = append(args, "--targetSdkVersion ", targetSdkVersion)
+
minSdkVersion, err := params.SdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
if err != nil {
ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
diff --git a/java/app.go b/java/app.go
index 4d9c407..5234808 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1508,20 +1508,48 @@
certificate, certificateName := android.BazelStringOrLabelFromProp(ctx, a.overridableAppProperties.Certificate)
- attrs := &bazelAndroidAppAttributes{
- commonAttrs,
- aapt,
- deps,
+ appAttrs := &bazelAndroidAppAttributes{
// TODO(b/209576404): handle package name override by product variable PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
- a.overridableAppProperties.Package_name,
- certificate,
- certificateName,
+ Custom_package: a.overridableAppProperties.Package_name,
+ Certificate: certificate,
+ Certificate_name: certificateName,
}
props := bazel.BazelTargetModuleProperties{
Rule_class: "android_binary",
Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
}
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs)
+
+ if !bp2BuildInfo.hasKotlinSrcs && len(a.properties.Common_srcs) == 0 {
+ appAttrs.javaCommonAttributes = commonAttrs
+ appAttrs.bazelAapt = aapt
+ appAttrs.Deps = deps
+ } else {
+ ktName := a.Name() + "_kt"
+ commonAttrs.Common_srcs = bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, a.properties.Common_srcs))
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "android_library",
+ Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
+ },
+ android.CommonAttributes{Name: ktName},
+ &bazelAndroidLibrary{
+ javaLibraryAttributes: &javaLibraryAttributes{
+ javaCommonAttributes: commonAttrs,
+ Deps: deps,
+ },
+ bazelAapt: aapt,
+ },
+ )
+
+ appAttrs.bazelAapt = &bazelAapt{Manifest: aapt.Manifest}
+ appAttrs.Deps = bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + ktName})
+ }
+
+ ctx.CreateBazelTargetModule(
+ props,
+ android.CommonAttributes{Name: a.Name()},
+ appAttrs,
+ )
}
diff --git a/java/config/config.go b/java/config/config.go
index 49d88c4..7c22076 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -96,8 +96,6 @@
}, dexerJavaVmFlagsList...))
exportedVars.ExportStringListStaticVariable("R8Flags", append([]string{
"-JXmx2048M",
- // Disable this optimization as it can impact weak reference semantics. See b/233432839.
- "-JDcom.android.tools.r8.disableEnqueuerDeferredTracing=true",
}, dexerJavaVmFlagsList...))
exportedVars.ExportStringListStaticVariable("CommonJdkFlags", []string{
diff --git a/java/dex.go b/java/dex.go
index 971da92..a8dd375 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -22,7 +22,6 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
- "android/soong/java/config"
"android/soong/remoteexec"
)
@@ -90,6 +89,7 @@
// list of extra proguard flag files
extraProguardFlagFiles android.Paths
proguardDictionary android.OptionalPath
+ proguardConfiguration android.OptionalPath
proguardUsageZip android.OptionalPath
providesTransitiveHeaderJars
@@ -133,17 +133,18 @@
var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8",
blueprint.RuleParams{
Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
- `rm -f "$outDict" && rm -rf "${outUsageDir}" && ` +
+ `rm -f "$outDict" && rm -f "$outConfig" && rm -rf "${outUsageDir}" && ` +
`mkdir -p $$(dirname ${outUsage}) && ` +
`mkdir -p $$(dirname $tmpJar) && ` +
`${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` +
`$r8Template${config.R8Cmd} ${config.R8Flags} -injars $tmpJar --output $outDir ` +
`--no-data-resources ` +
`-printmapping ${outDict} ` +
+ `--pg-conf-output ${outConfig} ` +
`-printusage ${outUsage} ` +
`--deps-file ${out}.d ` +
`$r8Flags && ` +
- `touch "${outDict}" "${outUsage}" && ` +
+ `touch "${outDict}" "${outConfig}" "${outUsage}" && ` +
`${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` +
`rm -rf ${outUsageDir} && ` +
`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
@@ -179,7 +180,7 @@
ExecStrategy: "${config.RER8ExecStrategy}",
Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
},
- }, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir",
+ }, []string{"outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir",
"r8Flags", "zipFlags", "tmpJar", "mergeZipsFlags"}, []string{"implicits"})
func (d *dexer) dexCommonFlags(ctx android.ModuleContext,
@@ -257,12 +258,6 @@
r8Deps = append(r8Deps, flags.bootClasspath...)
r8Flags = append(r8Flags, flags.dexClasspath.FormJavaClassPath("-libraryjars"))
r8Deps = append(r8Deps, flags.dexClasspath...)
- r8Flags = append(r8Flags, flags.processorPath.FormJavaClassPath("-libraryjars"))
- r8Deps = append(r8Deps, flags.processorPath...)
-
- errorProneClasspath := classpath(android.PathsForSource(ctx, config.ErrorProneClasspath))
- r8Flags = append(r8Flags, errorProneClasspath.FormJavaClassPath("-libraryjars"))
- r8Deps = append(r8Deps, errorProneClasspath...)
transitiveStaticLibsLookupMap := map[android.Path]bool{}
if d.transitiveStaticLibsHeaderJars != nil {
@@ -370,6 +365,8 @@
if useR8 {
proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
d.proguardDictionary = android.OptionalPathForPath(proguardDictionary)
+ proguardConfiguration := android.PathForModuleOut(ctx, "proguard_configuration")
+ d.proguardConfiguration = android.OptionalPathForPath(proguardConfiguration)
proguardUsageDir := android.PathForModuleOut(ctx, "proguard_usage")
proguardUsage := proguardUsageDir.Join(ctx, ctx.Namespace().Path,
android.ModuleNameWithPossibleOverride(ctx), "unused.txt")
@@ -382,6 +379,7 @@
"r8Flags": strings.Join(append(commonFlags, r8Flags...), " "),
"zipFlags": zipFlags,
"outDict": proguardDictionary.String(),
+ "outConfig": proguardConfiguration.String(),
"outUsageDir": proguardUsageDir.String(),
"outUsage": proguardUsage.String(),
"outUsageZip": proguardUsageZip.String(),
diff --git a/java/droiddoc.go b/java/droiddoc.go
index aa55f37..01a2c14 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -602,8 +602,15 @@
Flag("-J-Xmx1600m").
Flag("-J-XX:-OmitStackTraceInFastThrow").
Flag("-XDignore.symbol.file").
- FlagWithArg("-doclet ", "com.google.doclava.Doclava").
+ Flag("--ignore-source-errors").
+ // b/240421555: use a stub doclet until Doclava works with JDK 17
+ //FlagWithArg("-doclet ", "com.google.doclava.Doclava").
+ FlagWithArg("-doclet ", "com.google.stubdoclet.StubDoclet").
FlagWithInputList("-docletpath ", docletPath.Paths(), ":").
+ FlagWithArg("-Xmaxerrs ", "1").
+ FlagWithArg("-Xmaxwarns ", "1").
+ Flag("-J--add-exports=jdk.javadoc/jdk.javadoc.internal.doclets.formats.html=ALL-UNNAMED").
+ Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED").
FlagWithArg("-hdf page.build ", ctx.Config().BuildId()+"-$(cat "+buildNumberFile.String()+")").OrderOnly(buildNumberFile).
FlagWithArg("-hdf page.now ", `"$(date -d @$(cat `+ctx.Config().Getenv("BUILD_DATETIME_FILE")+`) "+%d %b %Y %k:%M")" `)
@@ -687,7 +694,7 @@
outDir, srcJarDir, srcJarList android.Path, sourcepaths android.Paths) *android.RuleBuilderCommand {
cmd := rule.Command().
- BuiltTool("soong_javac_wrapper").Tool(android.PathForSource(ctx, "prebuilts/jdk/jdk11/linux-x86/bin/javadoc")).
+ BuiltTool("soong_javac_wrapper").Tool(config.JavadocCmd(ctx)).
Flag(config.JavacVmFlags).
FlagWithArg("-encoding ", "UTF-8").
FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "javadoc.rsp"), srcs).
@@ -773,6 +780,8 @@
jsilver := ctx.Config().HostJavaToolPath(ctx, "jsilver.jar")
doclava := ctx.Config().HostJavaToolPath(ctx, "doclava.jar")
+ // b/240421555: use a stub doclet until Doclava works with JDK 17
+ stubdoclet := ctx.Config().HostJavaToolPath(ctx, "stubdoclet.jar")
outDir := android.PathForModuleOut(ctx, "out")
srcJarDir := android.PathForModuleOut(ctx, "srcjars")
@@ -800,7 +809,8 @@
if Bool(d.properties.Dokka_enabled) {
desc = "dokka"
} else {
- d.doclavaDocsFlags(ctx, cmd, classpath{jsilver, doclava})
+ // b/240421555: use a stub doclet until Doclava works with JDK 17
+ d.doclavaDocsFlags(ctx, cmd, classpath{jsilver, doclava, stubdoclet})
for _, o := range d.Javadoc.properties.Out {
cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
@@ -818,9 +828,9 @@
FlagWithArg("-C ", outDir.String()).
FlagWithArg("-D ", outDir.String())
- rule.Restat()
+ // rule.Restat()
- zipSyncCleanupCmd(rule, srcJarDir)
+ // zipSyncCleanupCmd(rule, srcJarDir)
rule.Build("javadoc", desc)
}
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 4bbe70a..8a521aa 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -148,6 +148,10 @@
// path or filegroup to file defining extension an SDK name <-> numerical ID mapping and
// what APIs exist in which SDKs; passed to metalava via --sdk-extensions-info
Extensions_info_file *string `android:"path"`
+
+ // API surface of this module. If set, the module contributes to an API surface.
+ // For the full list of available API surfaces, refer to soong/android/sdk_version.go
+ Api_surface *string
}
// Used by xsd_config
@@ -178,6 +182,10 @@
&module.Javadoc.properties)
InitDroiddocModule(module, android.HostAndDeviceSupported)
+
+ module.SetDefaultableHook(func(ctx android.DefaultableHookContext) {
+ module.createApiContribution(ctx)
+ })
return module
}
@@ -862,6 +870,25 @@
}, attrs)
}
+func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) {
+ api_file := d.properties.Check_api.Current.Api_file
+ api_surface := d.properties.Api_surface
+
+ props := struct {
+ Name *string
+ Api_surface *string
+ Api_file *string
+ Visibility []string
+ }{}
+
+ props.Name = proptools.StringPtr(d.Name() + ".api.contribution")
+ props.Api_surface = api_surface
+ props.Api_file = api_file
+ props.Visibility = []string{"//visibility:override", "//visibility:public"}
+
+ ctx.CreateModule(ApiContributionFactory, &props)
+}
+
// TODO (b/262014796): Export the API contributions of CorePlatformApi
// A map to populate the api surface of a droidstub from a substring appearing in its name
// This map assumes that droidstubs (either checked-in or created by java_sdk_library)
@@ -876,6 +903,7 @@
"system": android.SdkSystem,
"module_lib": android.SdkModule,
"module-lib": android.SdkModule,
+ "platform.api": android.SdkCorePlatform,
"test": android.SdkTest,
"toolchain": android.SdkToolchain,
}
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index ef2e6dc..7a04d73 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -346,3 +346,60 @@
android.AssertStringEquals(t, tc.desc, tc.expectedApiSurface, bazelApiSurfaceName(tc.name))
}
}
+
+func TestDroidStubsApiContributionGeneration(t *testing.T) {
+ ctx, _ := testJavaWithFS(t, `
+ droidstubs {
+ name: "foo",
+ srcs: ["A/a.java"],
+ api_surface: "public",
+ check_api: {
+ current: {
+ api_file: "A/current.txt",
+ removed_api_file: "A/removed.txt",
+ }
+ }
+ }
+ `,
+ map[string][]byte{
+ "A/a.java": nil,
+ "A/current.txt": nil,
+ "A/removed.txt": nil,
+ },
+ )
+
+ ctx.ModuleForTests("foo.api.contribution", "")
+}
+
+func TestGeneratedApiContributionVisibilityTest(t *testing.T) {
+ library_bp := `
+ java_api_library {
+ name: "bar",
+ api_surface: "public",
+ api_contributions: ["foo.api.contribution"],
+ }
+ `
+ ctx, _ := testJavaWithFS(t, `
+ droidstubs {
+ name: "foo",
+ srcs: ["A/a.java"],
+ api_surface: "public",
+ check_api: {
+ current: {
+ api_file: "A/current.txt",
+ removed_api_file: "A/removed.txt",
+ }
+ },
+ visibility: ["//a"],
+ }
+ `,
+ map[string][]byte{
+ "a/a.java": nil,
+ "a/current.txt": nil,
+ "a/removed.txt": nil,
+ "b/Android.bp": []byte(library_bp),
+ },
+ )
+
+ ctx.ModuleForTests("bar", "android_common")
+}
diff --git a/java/java.go b/java/java.go
index 7078cc3..659f98a 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1594,7 +1594,11 @@
var JavaApiImportProvider = blueprint.NewProvider(JavaApiImportInfo{})
func (ap *JavaApiContribution) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- apiFile := android.PathForModuleSrc(ctx, String(ap.properties.Api_file))
+ var apiFile android.Path = nil
+ if apiFileString := ap.properties.Api_file; apiFileString != nil {
+ apiFile = android.PathForModuleSrc(ctx, String(apiFileString))
+ }
+
ctx.SetProvider(JavaApiImportProvider, JavaApiImportInfo{
ApiFile: apiFile,
})
@@ -1725,7 +1729,11 @@
switch tag {
case javaApiContributionTag:
provider := ctx.OtherModuleProvider(dep, JavaApiImportProvider).(JavaApiImportInfo)
- srcFiles = append(srcFiles, android.PathForSource(ctx, provider.ApiFile.String()))
+ providerApiFile := provider.ApiFile
+ if providerApiFile == nil {
+ ctx.ModuleErrorf("Error: %s has an empty api file.", dep.Name())
+ }
+ srcFiles = append(srcFiles, android.PathForSource(ctx, providerApiFile.String()))
case libTag:
provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
classPaths = append(classPaths, provider.HeaderJars...)
@@ -2595,7 +2603,7 @@
// to be returned to the calling function.
func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *bp2BuildJavaInfo) {
var srcs bazel.LabelListAttribute
- var deps bazel.LabelList
+ var deps bazel.LabelListAttribute
var staticDeps bazel.LabelList
archVariantProps := m.GetArchVariantProperties(ctx, &CommonProperties{})
@@ -2701,11 +2709,17 @@
Javacopts: bazel.MakeStringListAttribute(javacopts),
}
- if m.properties.Libs != nil {
- for _, d := range m.properties.Libs {
- neverlinkLabel := android.BazelLabelForModuleDepSingle(ctx, d)
- neverlinkLabel.Label = neverlinkLabel.Label + "-neverlink"
- deps.Add(&neverlinkLabel)
+ for axis, configToProps := range archVariantProps {
+ for config, _props := range configToProps {
+ if archProps, ok := _props.(*CommonProperties); ok {
+ var libLabels []bazel.Label
+ for _, d := range archProps.Libs {
+ neverlinkLabel := android.BazelLabelForModuleDepSingle(ctx, d)
+ neverlinkLabel.Label = neverlinkLabel.Label + "-neverlink"
+ libLabels = append(libLabels, neverlinkLabel)
+ }
+ deps.SetSelectValue(axis, config, bazel.MakeLabelList(libLabels))
+ }
}
}
@@ -2723,7 +2737,7 @@
staticDeps.Add(protoDepLabel)
depLabels := &javaDependencyLabels{}
- depLabels.Deps = bazel.MakeLabelListAttribute(deps)
+ depLabels.Deps = deps
depLabels.StaticDeps = bazel.MakeLabelListAttribute(staticDeps)
bp2BuildInfo := &bp2BuildJavaInfo{
diff --git a/java/java_test.go b/java/java_test.go
index ae77842..21993ec 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -30,7 +30,6 @@
"android/soong/cc"
"android/soong/dexpreopt"
"android/soong/genrule"
- "android/soong/python"
)
// Legacy preparer used for running tests within the java package.
@@ -49,7 +48,6 @@
// Include all the default java modules.
PrepareForTestWithJavaDefaultModules,
PrepareForTestWithOverlayBuildComponents,
- python.PrepareForTestWithPythonBuildComponents,
android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
}),
@@ -1440,24 +1438,26 @@
}
func TestDataNativeBinaries(t *testing.T) {
- ctx, _ := testJava(t, `
+ ctx := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ android.PrepareForTestWithAllowMissingDependencies).RunTestWithBp(t, `
java_test_host {
name: "foo",
srcs: ["a.java"],
data_native_bins: ["bin"]
}
- python_binary_host {
+ cc_binary_host {
name: "bin",
- srcs: ["bin.py"],
+ srcs: ["bin.cpp"],
}
- `)
+ `).TestContext
buildOS := ctx.Config().BuildOS.String()
test := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
- expected := []string{"out/soong/.intermediates/bin/" + buildOS + "_x86_64_PY3/bin:bin"}
+ expected := []string{"out/soong/.intermediates/bin/" + buildOS + "_x86_64/bin:bin"}
actual := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_COMPATIBILITY_SUPPORT_FILES", ctx.Config(), expected, actual)
}
@@ -1840,6 +1840,20 @@
}`)
}
+func TestJavaApiContributionEmptyApiFile(t *testing.T) {
+ testJavaError(t,
+ "Error: foo has an empty api file.",
+ `java_api_contribution {
+ name: "foo",
+ }
+ java_api_library {
+ name: "bar",
+ api_surface: "public",
+ api_contributions: ["foo"],
+ }
+ `)
+}
+
func TestJavaApiLibraryAndProviderLink(t *testing.T) {
provider_bp_a := `
java_api_contribution {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 3b64bf7..b872365 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1599,6 +1599,7 @@
Srcs []string
Installable *bool
Sdk_version *string
+ Api_surface *string
System_modules *string
Libs []string
Output_javadoc_comments *bool
@@ -1638,6 +1639,7 @@
props.Srcs = append(props.Srcs, module.properties.Srcs...)
props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
props.Sdk_version = module.deviceProperties.Sdk_version
+ props.Api_surface = &apiScope.name
props.System_modules = module.deviceProperties.System_modules
props.Installable = proptools.BoolPtr(false)
// A droiddoc module has only one Libs property and doesn't distinguish between
diff --git a/python/Android.bp b/python/Android.bp
index e49fa6a..7578673 100644
--- a/python/Android.bp
+++ b/python/Android.bp
@@ -9,13 +9,13 @@
"blueprint",
"soong-android",
"soong-tradefed",
+ "soong-cc",
],
srcs: [
- "androidmk.go",
"binary.go",
+ "bp2build.go",
"builder.go",
"defaults.go",
- "installer.go",
"library.go",
"proto.go",
"python.go",
diff --git a/python/androidmk.go b/python/androidmk.go
deleted file mode 100644
index 7dc4713..0000000
--- a/python/androidmk.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2017 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 python
-
-import (
- "path/filepath"
- "strings"
-
- "android/soong/android"
-)
-
-type subAndroidMkProvider interface {
- AndroidMk(*Module, *android.AndroidMkEntries)
-}
-
-func (p *Module) subAndroidMk(entries *android.AndroidMkEntries, obj interface{}) {
- if p.subAndroidMkOnce == nil {
- p.subAndroidMkOnce = make(map[subAndroidMkProvider]bool)
- }
- if androidmk, ok := obj.(subAndroidMkProvider); ok {
- if !p.subAndroidMkOnce[androidmk] {
- p.subAndroidMkOnce[androidmk] = true
- androidmk.AndroidMk(p, entries)
- }
- }
-}
-
-func (p *Module) AndroidMkEntries() []android.AndroidMkEntries {
- entries := android.AndroidMkEntries{OutputFile: p.installSource}
-
- p.subAndroidMk(&entries, p.installer)
-
- return []android.AndroidMkEntries{entries}
-}
-
-func (p *binaryDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
- entries.Class = "EXECUTABLES"
-
- entries.ExtraEntries = append(entries.ExtraEntries,
- func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
- })
- base.subAndroidMk(entries, p.pythonInstaller)
-}
-
-func (p *testDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
- entries.Class = "NATIVE_TESTS"
-
- entries.ExtraEntries = append(entries.ExtraEntries,
- func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.AddCompatibilityTestSuites(p.binaryDecorator.binaryProperties.Test_suites...)
- if p.testConfig != nil {
- entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
- }
-
- entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
-
- entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
-
- p.testProperties.Test_options.SetAndroidMkEntries(entries)
- })
- base.subAndroidMk(entries, p.binaryDecorator.pythonInstaller)
-}
-
-func (installer *pythonInstaller) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
- entries.Required = append(entries.Required, "libc++")
- entries.ExtraEntries = append(entries.ExtraEntries,
- func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- path, file := filepath.Split(installer.path.String())
- stem := strings.TrimSuffix(file, filepath.Ext(file))
-
- entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
- entries.SetString("LOCAL_MODULE_PATH", path)
- entries.SetString("LOCAL_MODULE_STEM", stem)
- entries.AddStrings("LOCAL_SHARED_LIBRARIES", installer.androidMkSharedLibs...)
- entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
- })
-}
diff --git a/python/binary.go b/python/binary.go
index 670e0d3..75135f3 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -18,11 +18,10 @@
import (
"fmt"
+ "path/filepath"
+ "strings"
"android/soong/android"
- "android/soong/bazel"
-
- "github.com/google/blueprint/proptools"
)
func init() {
@@ -33,63 +32,6 @@
ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
}
-type bazelPythonBinaryAttributes struct {
- Main *bazel.Label
- Srcs bazel.LabelListAttribute
- Deps bazel.LabelListAttribute
- Python_version *string
- Imports bazel.StringListAttribute
-}
-
-func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
- // TODO(b/182306917): this doesn't fully handle all nested props versioned
- // by the python version, which would have been handled by the version split
- // mutator. This is sufficient for very simple python_binary_host modules
- // under Bionic.
- py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
- py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
- var python_version *string
- if py3Enabled && py2Enabled {
- panic(fmt.Errorf(
- "error for '%s' module: bp2build's python_binary_host converter does not support "+
- "converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name()))
- } else if py2Enabled {
- python_version = &pyVersion2
- } else {
- // do nothing, since python_version defaults to PY3.
- }
-
- baseAttrs := m.makeArchVariantBaseAttributes(ctx)
- attrs := &bazelPythonBinaryAttributes{
- Main: nil,
- Srcs: baseAttrs.Srcs,
- Deps: baseAttrs.Deps,
- Python_version: python_version,
- Imports: baseAttrs.Imports,
- }
-
- for _, propIntf := range m.GetProperties() {
- if props, ok := propIntf.(*BinaryProperties); ok {
- // main is optional.
- if props.Main != nil {
- main := android.BazelLabelForModuleSrcSingle(ctx, *props.Main)
- attrs.Main = &main
- break
- }
- }
- }
-
- props := bazel.BazelTargetModuleProperties{
- // Use the native py_binary rule.
- Rule_class: "py_binary",
- }
-
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{
- Name: m.Name(),
- Data: baseAttrs.Data,
- }, attrs)
-}
-
type BinaryProperties struct {
// the name of the source file that is the main entry point of the program.
// this file must also be listed in srcs.
@@ -118,49 +60,58 @@
Auto_gen_config *bool
}
-type binaryDecorator struct {
+type PythonBinaryModule struct {
+ PythonLibraryModule
binaryProperties BinaryProperties
- *pythonInstaller
+ // (.intermediate) module output path as installation source.
+ installSource android.Path
+
+ // Final installation path.
+ installedDest android.Path
+
+ androidMkSharedLibs []string
}
+var _ android.AndroidMkEntriesProvider = (*PythonBinaryModule)(nil)
+var _ android.Module = (*PythonBinaryModule)(nil)
+
type IntermPathProvider interface {
IntermPathForModuleOut() android.OptionalPath
}
-func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
- module := newModule(hod, android.MultilibFirst)
- decorator := &binaryDecorator{pythonInstaller: NewPythonInstaller("bin", "")}
-
- module.bootstrapper = decorator
- module.installer = decorator
-
- return module, decorator
+func NewBinary(hod android.HostOrDeviceSupported) *PythonBinaryModule {
+ return &PythonBinaryModule{
+ PythonLibraryModule: *newModule(hod, android.MultilibFirst),
+ }
}
func PythonBinaryHostFactory() android.Module {
- module, _ := NewBinary(android.HostSupported)
-
- android.InitBazelModule(module)
-
- return module.init()
+ return NewBinary(android.HostSupported).init()
}
-func (binary *binaryDecorator) autorun() bool {
- return BoolDefault(binary.binaryProperties.Autorun, true)
+func (p *PythonBinaryModule) init() android.Module {
+ p.AddProperties(&p.properties, &p.protoProperties)
+ p.AddProperties(&p.binaryProperties)
+ android.InitAndroidArchModule(p, p.hod, p.multilib)
+ android.InitDefaultableModule(p)
+ android.InitBazelModule(p)
+ return p
}
-func (binary *binaryDecorator) bootstrapperProps() []interface{} {
- return []interface{}{&binary.binaryProperties}
+func (p *PythonBinaryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ p.PythonLibraryModule.GenerateAndroidBuildActions(ctx)
+ p.buildBinary(ctx)
+ p.installedDest = ctx.InstallFile(installDir(ctx, "bin", "", ""),
+ p.installSource.Base(), p.installSource)
}
-func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actualVersion string,
- embeddedLauncher bool, srcsPathMappings []pathMapping, srcsZip android.Path,
- depsSrcsZips android.Paths) android.OptionalPath {
-
+func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) {
+ embeddedLauncher := p.isEmbeddedLauncherEnabled()
+ depsSrcsZips := p.collectPathsFromTransitiveDeps(ctx, embeddedLauncher)
main := ""
- if binary.autorun() {
- main = binary.getPyMainFile(ctx, srcsPathMappings)
+ if p.autorun() {
+ main = p.getPyMainFile(ctx, p.srcsPathMappings)
}
var launcherPath android.OptionalPath
@@ -175,15 +126,88 @@
}
})
}
- binFile := registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
- binary.getHostInterpreterName(ctx, actualVersion),
- main, binary.getStem(ctx), append(android.Paths{srcsZip}, depsSrcsZips...))
+ srcsZips := make(android.Paths, 0, len(depsSrcsZips)+1)
+ if embeddedLauncher {
+ srcsZips = append(srcsZips, p.precompiledSrcsZip)
+ } else {
+ srcsZips = append(srcsZips, p.srcsZip)
+ }
+ srcsZips = append(srcsZips, depsSrcsZips...)
+ p.installSource = registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
+ p.getHostInterpreterName(ctx, p.properties.Actual_version),
+ main, p.getStem(ctx), srcsZips)
- return android.OptionalPathForPath(binFile)
+ var sharedLibs []string
+ // if embedded launcher is enabled, we need to collect the shared library dependencies of the
+ // launcher
+ for _, dep := range ctx.GetDirectDepsWithTag(launcherSharedLibTag) {
+ sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
+ }
+ p.androidMkSharedLibs = sharedLibs
+}
+
+func (p *PythonBinaryModule) AndroidMkEntries() []android.AndroidMkEntries {
+ entries := android.AndroidMkEntries{OutputFile: android.OptionalPathForPath(p.installSource)}
+
+ entries.Class = "EXECUTABLES"
+
+ entries.ExtraEntries = append(entries.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
+ })
+
+ entries.Required = append(entries.Required, "libc++")
+ entries.ExtraEntries = append(entries.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ path, file := filepath.Split(p.installedDest.String())
+ stem := strings.TrimSuffix(file, filepath.Ext(file))
+
+ entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
+ entries.SetString("LOCAL_MODULE_PATH", path)
+ entries.SetString("LOCAL_MODULE_STEM", stem)
+ entries.AddStrings("LOCAL_SHARED_LIBRARIES", p.androidMkSharedLibs...)
+ entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
+ })
+
+ return []android.AndroidMkEntries{entries}
+}
+
+func (p *PythonBinaryModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+ p.PythonLibraryModule.DepsMutator(ctx)
+
+ if p.isEmbeddedLauncherEnabled() {
+ p.AddDepsOnPythonLauncherAndStdlib(ctx, pythonLibTag, launcherTag, launcherSharedLibTag, p.autorun(), ctx.Target())
+ }
+}
+
+// HostToolPath returns a path if appropriate such that this module can be used as a host tool,
+// fulfilling the android.HostToolProvider interface.
+func (p *PythonBinaryModule) HostToolPath() android.OptionalPath {
+ // TODO: This should only be set when building host binaries -- tests built for device would be
+ // setting this incorrectly.
+ return android.OptionalPathForPath(p.installedDest)
+}
+
+// OutputFiles returns output files based on given tag, returns an error if tag is unsupported.
+func (p *PythonBinaryModule) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case "":
+ return android.Paths{p.installSource}, nil
+ default:
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+ }
+}
+
+func (p *PythonBinaryModule) isEmbeddedLauncherEnabled() bool {
+ return Bool(p.properties.Embedded_launcher)
+}
+
+func (b *PythonBinaryModule) autorun() bool {
+ return BoolDefault(b.binaryProperties.Autorun, true)
}
// get host interpreter name.
-func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext,
+func (p *PythonBinaryModule) getHostInterpreterName(ctx android.ModuleContext,
actualVersion string) string {
var interp string
switch actualVersion {
@@ -200,13 +224,13 @@
}
// find main program path within runfiles tree.
-func (binary *binaryDecorator) getPyMainFile(ctx android.ModuleContext,
+func (p *PythonBinaryModule) getPyMainFile(ctx android.ModuleContext,
srcsPathMappings []pathMapping) string {
var main string
- if String(binary.binaryProperties.Main) == "" {
+ if String(p.binaryProperties.Main) == "" {
main = ctx.ModuleName() + pyExt
} else {
- main = String(binary.binaryProperties.Main)
+ main = String(p.binaryProperties.Main)
}
for _, path := range srcsPathMappings {
@@ -219,11 +243,21 @@
return ""
}
-func (binary *binaryDecorator) getStem(ctx android.ModuleContext) string {
+func (p *PythonBinaryModule) getStem(ctx android.ModuleContext) string {
stem := ctx.ModuleName()
- if String(binary.binaryProperties.Stem) != "" {
- stem = String(binary.binaryProperties.Stem)
+ if String(p.binaryProperties.Stem) != "" {
+ stem = String(p.binaryProperties.Stem)
}
- return stem + String(binary.binaryProperties.Suffix)
+ return stem + String(p.binaryProperties.Suffix)
+}
+
+func installDir(ctx android.ModuleContext, dir, dir64, relative string) android.InstallPath {
+ if ctx.Arch().ArchType.Multilib == "lib64" && dir64 != "" {
+ dir = dir64
+ }
+ if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
+ dir = filepath.Join(dir, ctx.Arch().ArchType.String())
+ }
+ return android.PathForModuleInstall(ctx, dir, relative)
}
diff --git a/python/bp2build.go b/python/bp2build.go
new file mode 100644
index 0000000..bdac2dc
--- /dev/null
+++ b/python/bp2build.go
@@ -0,0 +1,226 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package python
+
+import (
+ "fmt"
+ "path/filepath"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+ "android/soong/bazel"
+)
+
+type bazelPythonLibraryAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Imports bazel.StringListAttribute
+ Srcs_version *string
+}
+
+type bazelPythonProtoLibraryAttributes struct {
+ Deps bazel.LabelListAttribute
+}
+
+type baseAttributes struct {
+ // TODO(b/200311466): Probably not translate b/c Bazel has no good equiv
+ //Pkg_path bazel.StringAttribute
+ // TODO: Related to Pkg_bath and similarLy gated
+ //Is_internal bazel.BoolAttribute
+ // Combines Srcs and Exclude_srcs
+ Srcs bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ // Combines Data and Java_data (invariant)
+ Data bazel.LabelListAttribute
+ Imports bazel.StringListAttribute
+}
+
+func (m *PythonLibraryModule) makeArchVariantBaseAttributes(ctx android.TopDownMutatorContext) baseAttributes {
+ var attrs baseAttributes
+ archVariantBaseProps := m.GetArchVariantProperties(ctx, &BaseProperties{})
+ for axis, configToProps := range archVariantBaseProps {
+ for config, props := range configToProps {
+ if baseProps, ok := props.(*BaseProperties); ok {
+ attrs.Srcs.SetSelectValue(axis, config,
+ android.BazelLabelForModuleSrcExcludes(ctx, baseProps.Srcs, baseProps.Exclude_srcs))
+ attrs.Deps.SetSelectValue(axis, config,
+ android.BazelLabelForModuleDeps(ctx, baseProps.Libs))
+ data := android.BazelLabelForModuleSrc(ctx, baseProps.Data)
+ data.Append(android.BazelLabelForModuleSrc(ctx, baseProps.Java_data))
+ attrs.Data.SetSelectValue(axis, config, data)
+ }
+ }
+ }
+
+ partitionedSrcs := bazel.PartitionLabelListAttribute(ctx, &attrs.Srcs, bazel.LabelPartitions{
+ "proto": android.ProtoSrcLabelPartition,
+ "py": bazel.LabelPartition{Keep_remainder: true},
+ })
+ attrs.Srcs = partitionedSrcs["py"]
+
+ if !partitionedSrcs["proto"].IsEmpty() {
+ protoInfo, _ := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, partitionedSrcs["proto"])
+ protoLabel := bazel.Label{Label: ":" + protoInfo.Name}
+
+ pyProtoLibraryName := m.Name() + "_py_proto"
+ ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
+ Rule_class: "py_proto_library",
+ Bzl_load_location: "//build/bazel/rules/python:py_proto.bzl",
+ }, android.CommonAttributes{
+ Name: pyProtoLibraryName,
+ }, &bazelPythonProtoLibraryAttributes{
+ Deps: bazel.MakeSingleLabelListAttribute(protoLabel),
+ })
+
+ attrs.Deps.Add(bazel.MakeLabelAttribute(":" + pyProtoLibraryName))
+ }
+
+ // Bazel normally requires `import path.from.top.of.tree` statements in
+ // python code, but with soong you can directly import modules from libraries.
+ // Add "imports" attributes to the bazel library so it matches soong's behavior.
+ imports := "."
+ if m.properties.Pkg_path != nil {
+ // TODO(b/215119317) This is a hack to handle the fact that we don't convert
+ // pkg_path properly right now. If the folder structure that contains this
+ // Android.bp file matches pkg_path, we can set imports to an appropriate
+ // number of ../..s to emulate moving the files under a pkg_path folder.
+ pkg_path := filepath.Clean(*m.properties.Pkg_path)
+ if strings.HasPrefix(pkg_path, "/") {
+ ctx.ModuleErrorf("pkg_path cannot start with a /: %s", pkg_path)
+ }
+
+ if !strings.HasSuffix(ctx.ModuleDir(), "/"+pkg_path) && ctx.ModuleDir() != pkg_path {
+ ctx.ModuleErrorf("Currently, bp2build only supports pkg_paths that are the same as the folders the Android.bp file is in. pkg_path: %s, module directory: %s", pkg_path, ctx.ModuleDir())
+ }
+ numFolders := strings.Count(pkg_path, "/") + 1
+ dots := make([]string, numFolders)
+ for i := 0; i < numFolders; i++ {
+ dots[i] = ".."
+ }
+ imports = strings.Join(dots, "/")
+ }
+ attrs.Imports = bazel.MakeStringListAttribute([]string{imports})
+
+ return attrs
+}
+
+func pythonLibBp2Build(ctx android.TopDownMutatorContext, m *PythonLibraryModule) {
+ // TODO(b/182306917): this doesn't fully handle all nested props versioned
+ // by the python version, which would have been handled by the version split
+ // mutator. This is sufficient for very simple python_library modules under
+ // Bionic.
+ py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, true)
+ py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
+ var python_version *string
+ if py2Enabled && !py3Enabled {
+ python_version = &pyVersion2
+ } else if !py2Enabled && py3Enabled {
+ python_version = &pyVersion3
+ } else if !py2Enabled && !py3Enabled {
+ ctx.ModuleErrorf("bp2build converter doesn't understand having neither py2 nor py3 enabled")
+ } else {
+ // do nothing, since python_version defaults to PY2ANDPY3
+ }
+
+ baseAttrs := m.makeArchVariantBaseAttributes(ctx)
+
+ attrs := &bazelPythonLibraryAttributes{
+ Srcs: baseAttrs.Srcs,
+ Deps: baseAttrs.Deps,
+ Srcs_version: python_version,
+ Imports: baseAttrs.Imports,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ // Use the native py_library rule.
+ Rule_class: "py_library",
+ }
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+ Name: m.Name(),
+ Data: baseAttrs.Data,
+ }, attrs)
+}
+
+type bazelPythonBinaryAttributes struct {
+ Main *bazel.Label
+ Srcs bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Python_version *string
+ Imports bazel.StringListAttribute
+}
+
+func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *PythonBinaryModule) {
+ // TODO(b/182306917): this doesn't fully handle all nested props versioned
+ // by the python version, which would have been handled by the version split
+ // mutator. This is sufficient for very simple python_binary_host modules
+ // under Bionic.
+ py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
+ py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
+ var python_version *string
+ if py3Enabled && py2Enabled {
+ panic(fmt.Errorf(
+ "error for '%s' module: bp2build's python_binary_host converter does not support "+
+ "converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name()))
+ } else if py2Enabled {
+ python_version = &pyVersion2
+ } else {
+ // do nothing, since python_version defaults to PY3.
+ }
+
+ baseAttrs := m.makeArchVariantBaseAttributes(ctx)
+ attrs := &bazelPythonBinaryAttributes{
+ Main: nil,
+ Srcs: baseAttrs.Srcs,
+ Deps: baseAttrs.Deps,
+ Python_version: python_version,
+ Imports: baseAttrs.Imports,
+ }
+
+ for _, propIntf := range m.GetProperties() {
+ if props, ok := propIntf.(*BinaryProperties); ok {
+ // main is optional.
+ if props.Main != nil {
+ main := android.BazelLabelForModuleSrcSingle(ctx, *props.Main)
+ attrs.Main = &main
+ break
+ }
+ }
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ // Use the native py_binary rule.
+ Rule_class: "py_binary",
+ }
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+ Name: m.Name(),
+ Data: baseAttrs.Data,
+ }, attrs)
+}
+
+func (p *PythonLibraryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ pythonLibBp2Build(ctx, p)
+}
+
+func (p *PythonBinaryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ pythonBinaryBp2Build(ctx, p)
+}
+
+func (p *PythonTestModule) ConvertWithBp2build(_ android.TopDownMutatorContext) {
+ // Tests are currently unsupported
+}
diff --git a/python/builder.go b/python/builder.go
index b4ab206..1066493 100644
--- a/python/builder.go
+++ b/python/builder.go
@@ -70,6 +70,17 @@
CommandDeps: []string{"$mergeParCmd"},
},
"srcsZips", "launcher")
+
+ precompile = pctx.AndroidStaticRule("precompilePython", blueprint.RuleParams{
+ Command: `LD_LIBRARY_PATH="$ldLibraryPath" ` +
+ `PYTHONPATH=$stdlibZip/internal/stdlib ` +
+ `$launcher build/soong/python/scripts/precompile_python.py $in $out`,
+ CommandDeps: []string{
+ "$stdlibZip",
+ "$launcher",
+ "build/soong/python/scripts/precompile_python.py",
+ },
+ }, "stdlibZip", "launcher", "ldLibraryPath")
)
func init() {
diff --git a/python/installer.go b/python/installer.go
deleted file mode 100644
index 396f036..0000000
--- a/python/installer.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2017 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 python
-
-import (
- "path/filepath"
-
- "android/soong/android"
-)
-
-// This file handles installing python executables into their final location
-
-type installLocation int
-
-const (
- InstallInData installLocation = iota
-)
-
-type pythonInstaller struct {
- dir string
- dir64 string
- relative string
-
- path android.InstallPath
-
- androidMkSharedLibs []string
-}
-
-func NewPythonInstaller(dir, dir64 string) *pythonInstaller {
- return &pythonInstaller{
- dir: dir,
- dir64: dir64,
- }
-}
-
-var _ installer = (*pythonInstaller)(nil)
-
-func (installer *pythonInstaller) installDir(ctx android.ModuleContext) android.InstallPath {
- dir := installer.dir
- if ctx.Arch().ArchType.Multilib == "lib64" && installer.dir64 != "" {
- dir = installer.dir64
- }
- if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
- dir = filepath.Join(dir, ctx.Arch().ArchType.String())
- }
- return android.PathForModuleInstall(ctx, dir, installer.relative)
-}
-
-func (installer *pythonInstaller) install(ctx android.ModuleContext, file android.Path) {
- installer.path = ctx.InstallFile(installer.installDir(ctx), file.Base(), file)
-}
-
-func (installer *pythonInstaller) setAndroidMkSharedLibs(sharedLibs []string) {
- installer.androidMkSharedLibs = sharedLibs
-}
diff --git a/python/library.go b/python/library.go
index df92df4..7cdb80b 100644
--- a/python/library.go
+++ b/python/library.go
@@ -18,9 +18,6 @@
import (
"android/soong/android"
- "android/soong/bazel"
-
- "github.com/google/blueprint/proptools"
)
func init() {
@@ -33,66 +30,9 @@
}
func PythonLibraryHostFactory() android.Module {
- module := newModule(android.HostSupported, android.MultilibFirst)
-
- android.InitBazelModule(module)
-
- return module.init()
-}
-
-type bazelPythonLibraryAttributes struct {
- Srcs bazel.LabelListAttribute
- Deps bazel.LabelListAttribute
- Imports bazel.StringListAttribute
- Srcs_version *string
-}
-
-type bazelPythonProtoLibraryAttributes struct {
- Deps bazel.LabelListAttribute
-}
-
-func pythonLibBp2Build(ctx android.TopDownMutatorContext, m *Module) {
- // TODO(b/182306917): this doesn't fully handle all nested props versioned
- // by the python version, which would have been handled by the version split
- // mutator. This is sufficient for very simple python_library modules under
- // Bionic.
- py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, true)
- py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
- var python_version *string
- if py2Enabled && !py3Enabled {
- python_version = &pyVersion2
- } else if !py2Enabled && py3Enabled {
- python_version = &pyVersion3
- } else if !py2Enabled && !py3Enabled {
- ctx.ModuleErrorf("bp2build converter doesn't understand having neither py2 nor py3 enabled")
- } else {
- // do nothing, since python_version defaults to PY2ANDPY3
- }
-
- baseAttrs := m.makeArchVariantBaseAttributes(ctx)
-
- attrs := &bazelPythonLibraryAttributes{
- Srcs: baseAttrs.Srcs,
- Deps: baseAttrs.Deps,
- Srcs_version: python_version,
- Imports: baseAttrs.Imports,
- }
-
- props := bazel.BazelTargetModuleProperties{
- // Use the native py_library rule.
- Rule_class: "py_library",
- }
-
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{
- Name: m.Name(),
- Data: baseAttrs.Data,
- }, attrs)
+ return newModule(android.HostSupported, android.MultilibFirst).init()
}
func PythonLibraryFactory() android.Module {
- module := newModule(android.HostAndDeviceSupported, android.MultilibBoth)
-
- android.InitBazelModule(module)
-
- return module.init()
+ return newModule(android.HostAndDeviceSupported, android.MultilibBoth).init()
}
diff --git a/python/python.go b/python/python.go
index 24e1bb2..18e5b68 100644
--- a/python/python.go
+++ b/python/python.go
@@ -22,8 +22,6 @@
"regexp"
"strings"
- "android/soong/bazel"
-
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -122,26 +120,13 @@
Embedded_launcher *bool `blueprint:"mutated"`
}
-type baseAttributes struct {
- // TODO(b/200311466): Probably not translate b/c Bazel has no good equiv
- //Pkg_path bazel.StringAttribute
- // TODO: Related to Pkg_bath and similarLy gated
- //Is_internal bazel.BoolAttribute
- // Combines Srcs and Exclude_srcs
- Srcs bazel.LabelListAttribute
- Deps bazel.LabelListAttribute
- // Combines Data and Java_data (invariant)
- Data bazel.LabelListAttribute
- Imports bazel.StringListAttribute
-}
-
// Used to store files of current module after expanding dependencies
type pathMapping struct {
dest string
src android.Path
}
-type Module struct {
+type PythonLibraryModule struct {
android.ModuleBase
android.DefaultableModuleBase
android.BazelModuleBase
@@ -153,16 +138,6 @@
hod android.HostOrDeviceSupported
multilib android.Multilib
- // interface used to bootstrap .par executable when embedded_launcher is true
- // this should be set by Python modules which are runnable, e.g. binaries and tests
- // bootstrapper might be nil (e.g. Python library module).
- bootstrapper bootstrapper
-
- // interface that implements functions required for installation
- // this should be set by Python modules which are runnable, e.g. binaries and tests
- // installer might be nil (e.g. Python library module).
- installer installer
-
// the Python files of current module after expanding source dependencies.
// pathMapping: <dest: runfile_path, src: source_path>
srcsPathMappings []pathMapping
@@ -171,152 +146,62 @@
// pathMapping: <dest: runfile_path, src: source_path>
dataPathMappings []pathMapping
- // the zip filepath for zipping current module source/data files.
+ // The zip file containing the current module's source/data files.
srcsZip android.Path
- // dependency modules' zip filepath for zipping current module source/data files.
- depsSrcsZips android.Paths
-
- // (.intermediate) module output path as installation source.
- installSource android.OptionalPath
-
- // Map to ensure sub-part of the AndroidMk for this module is only added once
- subAndroidMkOnce map[subAndroidMkProvider]bool
+ // The zip file containing the current module's source/data files, with the
+ // source files precompiled.
+ precompiledSrcsZip android.Path
}
// newModule generates new Python base module
-func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
- return &Module{
+func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *PythonLibraryModule {
+ return &PythonLibraryModule{
hod: hod,
multilib: multilib,
}
}
-func (m *Module) makeArchVariantBaseAttributes(ctx android.TopDownMutatorContext) baseAttributes {
- var attrs baseAttributes
- archVariantBaseProps := m.GetArchVariantProperties(ctx, &BaseProperties{})
- for axis, configToProps := range archVariantBaseProps {
- for config, props := range configToProps {
- if baseProps, ok := props.(*BaseProperties); ok {
- attrs.Srcs.SetSelectValue(axis, config,
- android.BazelLabelForModuleSrcExcludes(ctx, baseProps.Srcs, baseProps.Exclude_srcs))
- attrs.Deps.SetSelectValue(axis, config,
- android.BazelLabelForModuleDeps(ctx, baseProps.Libs))
- data := android.BazelLabelForModuleSrc(ctx, baseProps.Data)
- data.Append(android.BazelLabelForModuleSrc(ctx, baseProps.Java_data))
- attrs.Data.SetSelectValue(axis, config, data)
- }
- }
- }
-
- partitionedSrcs := bazel.PartitionLabelListAttribute(ctx, &attrs.Srcs, bazel.LabelPartitions{
- "proto": android.ProtoSrcLabelPartition,
- "py": bazel.LabelPartition{Keep_remainder: true},
- })
- attrs.Srcs = partitionedSrcs["py"]
-
- if !partitionedSrcs["proto"].IsEmpty() {
- protoInfo, _ := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, partitionedSrcs["proto"])
- protoLabel := bazel.Label{Label: ":" + protoInfo.Name}
-
- pyProtoLibraryName := m.Name() + "_py_proto"
- ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
- Rule_class: "py_proto_library",
- Bzl_load_location: "//build/bazel/rules/python:py_proto.bzl",
- }, android.CommonAttributes{
- Name: pyProtoLibraryName,
- }, &bazelPythonProtoLibraryAttributes{
- Deps: bazel.MakeSingleLabelListAttribute(protoLabel),
- })
-
- attrs.Deps.Add(bazel.MakeLabelAttribute(":" + pyProtoLibraryName))
- }
-
- // Bazel normally requires `import path.from.top.of.tree` statements in
- // python code, but with soong you can directly import modules from libraries.
- // Add "imports" attributes to the bazel library so it matches soong's behavior.
- imports := "."
- if m.properties.Pkg_path != nil {
- // TODO(b/215119317) This is a hack to handle the fact that we don't convert
- // pkg_path properly right now. If the folder structure that contains this
- // Android.bp file matches pkg_path, we can set imports to an appropriate
- // number of ../..s to emulate moving the files under a pkg_path folder.
- pkg_path := filepath.Clean(*m.properties.Pkg_path)
- if strings.HasPrefix(pkg_path, "/") {
- ctx.ModuleErrorf("pkg_path cannot start with a /: %s", pkg_path)
- }
-
- if !strings.HasSuffix(ctx.ModuleDir(), "/"+pkg_path) && ctx.ModuleDir() != pkg_path {
- ctx.ModuleErrorf("Currently, bp2build only supports pkg_paths that are the same as the folders the Android.bp file is in. pkg_path: %s, module directory: %s", pkg_path, ctx.ModuleDir())
- }
- numFolders := strings.Count(pkg_path, "/") + 1
- dots := make([]string, numFolders)
- for i := 0; i < numFolders; i++ {
- dots[i] = ".."
- }
- imports = strings.Join(dots, "/")
- }
- attrs.Imports = bazel.MakeStringListAttribute([]string{imports})
-
- return attrs
-}
-
-// bootstrapper interface should be implemented for runnable modules, e.g. binary and test
-type bootstrapper interface {
- bootstrapperProps() []interface{}
- bootstrap(ctx android.ModuleContext, ActualVersion string, embeddedLauncher bool,
- srcsPathMappings []pathMapping, srcsZip android.Path,
- depsSrcsZips android.Paths) android.OptionalPath
-
- autorun() bool
-}
-
-// installer interface should be implemented for installable modules, e.g. binary and test
-type installer interface {
- install(ctx android.ModuleContext, path android.Path)
- setAndroidMkSharedLibs(sharedLibs []string)
-}
-
// interface implemented by Python modules to provide source and data mappings and zip to python
// modules that depend on it
type pythonDependency interface {
getSrcsPathMappings() []pathMapping
getDataPathMappings() []pathMapping
getSrcsZip() android.Path
+ getPrecompiledSrcsZip() android.Path
}
// getSrcsPathMappings gets this module's path mapping of src source path : runfiles destination
-func (p *Module) getSrcsPathMappings() []pathMapping {
+func (p *PythonLibraryModule) getSrcsPathMappings() []pathMapping {
return p.srcsPathMappings
}
// getSrcsPathMappings gets this module's path mapping of data source path : runfiles destination
-func (p *Module) getDataPathMappings() []pathMapping {
+func (p *PythonLibraryModule) getDataPathMappings() []pathMapping {
return p.dataPathMappings
}
// getSrcsZip returns the filepath where the current module's source/data files are zipped.
-func (p *Module) getSrcsZip() android.Path {
+func (p *PythonLibraryModule) getSrcsZip() android.Path {
return p.srcsZip
}
-var _ pythonDependency = (*Module)(nil)
+// getSrcsZip returns the filepath where the current module's source/data files are zipped.
+func (p *PythonLibraryModule) getPrecompiledSrcsZip() android.Path {
+ return p.precompiledSrcsZip
+}
-var _ android.AndroidMkEntriesProvider = (*Module)(nil)
+func (p *PythonLibraryModule) getBaseProperties() *BaseProperties {
+ return &p.properties
+}
-func (p *Module) init(additionalProps ...interface{}) android.Module {
+var _ pythonDependency = (*PythonLibraryModule)(nil)
+
+func (p *PythonLibraryModule) init() android.Module {
p.AddProperties(&p.properties, &p.protoProperties)
-
- // Add additional properties for bootstrapping/installation
- // This is currently tied to the bootstrapper interface;
- // however, these are a combination of properties for the installation and bootstrapping of a module
- if p.bootstrapper != nil {
- p.AddProperties(p.bootstrapper.bootstrapperProps()...)
- }
-
android.InitAndroidArchModule(p, p.hod, p.multilib)
android.InitDefaultableModule(p)
-
+ android.InitBazelModule(p)
return p
}
@@ -338,36 +223,48 @@
}
var (
- pythonLibTag = dependencyTag{name: "pythonLib"}
- javaDataTag = dependencyTag{name: "javaData"}
+ pythonLibTag = dependencyTag{name: "pythonLib"}
+ javaDataTag = dependencyTag{name: "javaData"}
+ // The python interpreter, with soong module name "py3-launcher" or "py3-launcher-autorun".
launcherTag = dependencyTag{name: "launcher"}
launcherSharedLibTag = installDependencyTag{name: "launcherSharedLib"}
- pathComponentRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
- pyExt = ".py"
- protoExt = ".proto"
- pyVersion2 = "PY2"
- pyVersion3 = "PY3"
- internalPath = "internal"
+ // The python interpreter built for host so that we can precompile python sources.
+ // This only works because the precompiled sources don't vary by architecture.
+ // The soong module name is "py3-launcher".
+ hostLauncherTag = dependencyTag{name: "hostLauncher"}
+ hostlauncherSharedLibTag = dependencyTag{name: "hostlauncherSharedLib"}
+ hostStdLibTag = dependencyTag{name: "hostStdLib"}
+ pathComponentRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
+ pyExt = ".py"
+ protoExt = ".proto"
+ pyVersion2 = "PY2"
+ pyVersion3 = "PY3"
+ internalPath = "internal"
)
+type basePropertiesProvider interface {
+ getBaseProperties() *BaseProperties
+}
+
// versionSplitMutator creates version variants for modules and appends the version-specific
// properties for a given variant to the properties in the variant module
func versionSplitMutator() func(android.BottomUpMutatorContext) {
return func(mctx android.BottomUpMutatorContext) {
- if base, ok := mctx.Module().(*Module); ok {
- versionNames := []string{}
+ if base, ok := mctx.Module().(basePropertiesProvider); ok {
+ props := base.getBaseProperties()
+ var versionNames []string
// collect version specific properties, so that we can merge version-specific properties
// into the module's overall properties
- versionProps := []VersionProperties{}
+ var versionProps []VersionProperties
// PY3 is first so that we alias the PY3 variant rather than PY2 if both
// are available
- if proptools.BoolDefault(base.properties.Version.Py3.Enabled, true) {
+ if proptools.BoolDefault(props.Version.Py3.Enabled, true) {
versionNames = append(versionNames, pyVersion3)
- versionProps = append(versionProps, base.properties.Version.Py3)
+ versionProps = append(versionProps, props.Version.Py3)
}
- if proptools.BoolDefault(base.properties.Version.Py2.Enabled, false) {
+ if proptools.BoolDefault(props.Version.Py2.Enabled, false) {
versionNames = append(versionNames, pyVersion2)
- versionProps = append(versionProps, base.properties.Version.Py2)
+ versionProps = append(versionProps, props.Version.Py2)
}
modules := mctx.CreateLocalVariations(versionNames...)
// Alias module to the first variant
@@ -376,9 +273,10 @@
}
for i, v := range versionNames {
// set the actual version for Python module.
- modules[i].(*Module).properties.Actual_version = v
+ newProps := modules[i].(basePropertiesProvider).getBaseProperties()
+ newProps.Actual_version = v
// append versioned properties for the Python module to the overall properties
- err := proptools.AppendMatchingProperties([]interface{}{&modules[i].(*Module).properties}, &versionProps[i], nil)
+ err := proptools.AppendMatchingProperties([]interface{}{newProps}, &versionProps[i], nil)
if err != nil {
panic(err)
}
@@ -387,38 +285,6 @@
}
}
-// HostToolPath returns a path if appropriate such that this module can be used as a host tool,
-// fulfilling HostToolProvider interface.
-func (p *Module) HostToolPath() android.OptionalPath {
- if p.installer != nil {
- if bin, ok := p.installer.(*binaryDecorator); ok {
- // TODO: This should only be set when building host binaries -- tests built for device would be
- // setting this incorrectly.
- return android.OptionalPathForPath(bin.path)
- }
- }
-
- return android.OptionalPath{}
-
-}
-
-// OutputFiles returns output files based on given tag, returns an error if tag is unsupported.
-func (p *Module) OutputFiles(tag string) (android.Paths, error) {
- switch tag {
- case "":
- if outputFile := p.installSource; outputFile.Valid() {
- return android.Paths{outputFile.Path()}, nil
- }
- return android.Paths{}, nil
- default:
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
- }
-}
-
-func (p *Module) isEmbeddedLauncherEnabled() bool {
- return p.installer != nil && Bool(p.properties.Embedded_launcher)
-}
-
func anyHasExt(paths []string, ext string) bool {
for _, p := range paths {
if filepath.Ext(p) == ext {
@@ -429,7 +295,7 @@
return false
}
-func (p *Module) anySrcHasExt(ctx android.BottomUpMutatorContext, ext string) bool {
+func (p *PythonLibraryModule) anySrcHasExt(ctx android.BottomUpMutatorContext, ext string) bool {
return anyHasExt(p.properties.Srcs, ext)
}
@@ -437,7 +303,7 @@
// - handles proto dependencies,
// - if required, specifies launcher and adds launcher dependencies,
// - applies python version mutations to Python dependencies
-func (p *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
+func (p *PythonLibraryModule) DepsMutator(ctx android.BottomUpMutatorContext) {
android.ProtoDeps(ctx, &p.protoProperties)
versionVariation := []blueprint.Variation{
@@ -452,111 +318,85 @@
// Add python library dependencies for this python version variation
ctx.AddVariationDependencies(versionVariation, pythonLibTag, android.LastUniqueStrings(p.properties.Libs)...)
- // If this module will be installed and has an embedded launcher, we need to add dependencies for:
- // * standard library
- // * launcher
- // * shared dependencies of the launcher
- if p.installer != nil && p.isEmbeddedLauncherEnabled() {
- var stdLib string
- var launcherModule string
- // Add launcher shared lib dependencies. Ideally, these should be
- // derived from the `shared_libs` property of the launcher. However, we
- // cannot read the property at this stage and it will be too late to add
- // dependencies later.
- launcherSharedLibDeps := []string{
- "libsqlite",
- }
- // Add launcher-specific dependencies for bionic
- if ctx.Target().Os.Bionic() {
- launcherSharedLibDeps = append(launcherSharedLibDeps, "libc", "libdl", "libm")
- }
- if ctx.Target().Os == android.LinuxMusl && !ctx.Config().HostStaticBinaries() {
- launcherSharedLibDeps = append(launcherSharedLibDeps, "libc_musl")
- }
-
- switch p.properties.Actual_version {
- case pyVersion2:
- stdLib = "py2-stdlib"
-
- launcherModule = "py2-launcher"
- if p.bootstrapper.autorun() {
- launcherModule = "py2-launcher-autorun"
- }
-
- launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++")
-
- case pyVersion3:
- stdLib = "py3-stdlib"
-
- launcherModule = "py3-launcher"
- if p.bootstrapper.autorun() {
- launcherModule = "py3-launcher-autorun"
- }
- if ctx.Config().HostStaticBinaries() && ctx.Target().Os == android.LinuxMusl {
- launcherModule += "-static"
- }
-
- if ctx.Device() {
- launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog")
- }
- default:
- panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
- p.properties.Actual_version, ctx.ModuleName()))
- }
- ctx.AddVariationDependencies(versionVariation, pythonLibTag, stdLib)
- ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule)
- ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, launcherSharedLibDeps...)
- }
-
// Emulate the data property for java_data but with the arch variation overridden to "common"
// so that it can point to java modules.
javaDataVariation := []blueprint.Variation{{"arch", android.Common.String()}}
ctx.AddVariationDependencies(javaDataVariation, javaDataTag, p.properties.Java_data...)
+
+ p.AddDepsOnPythonLauncherAndStdlib(ctx, hostStdLibTag, hostLauncherTag, hostlauncherSharedLibTag, false, ctx.Config().BuildOSTarget)
}
-func (p *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- p.generatePythonBuildActions(ctx)
-
- // Only Python binary and test modules have non-empty bootstrapper.
- if p.bootstrapper != nil {
- // if the module is being installed, we need to collect all transitive dependencies to embed in
- // the final par
- p.collectPathsFromTransitiveDeps(ctx)
- // bootstrap the module, including resolving main file, getting launcher path, and
- // registering actions to build the par file
- // bootstrap returns the binary output path
- p.installSource = p.bootstrapper.bootstrap(ctx, p.properties.Actual_version,
- p.isEmbeddedLauncherEnabled(), p.srcsPathMappings, p.srcsZip, p.depsSrcsZips)
+// AddDepsOnPythonLauncherAndStdlib will make the current module depend on the python stdlib,
+// launcher (interpreter), and the launcher's shared libraries. If autorun is true, it will use
+// the autorun launcher instead of the regular one. This function acceps a targetForDeps argument
+// as the target to use for these dependencies. For embedded launcher python binaries, the launcher
+// that will be embedded will be under the same target as the python module itself. But when
+// precompiling python code, we need to get the python launcher built for host, even if we're
+// compiling the python module for device, so we pass a different target to this function.
+func (p *PythonLibraryModule) AddDepsOnPythonLauncherAndStdlib(ctx android.BottomUpMutatorContext,
+ stdLibTag, launcherTag, launcherSharedLibTag blueprint.DependencyTag,
+ autorun bool, targetForDeps android.Target) {
+ var stdLib string
+ var launcherModule string
+ // Add launcher shared lib dependencies. Ideally, these should be
+ // derived from the `shared_libs` property of the launcher. TODO: read these from
+ // the python launcher itself using ctx.OtherModuleProvider() or similar on the result
+ // of ctx.AddFarVariationDependencies()
+ launcherSharedLibDeps := []string{
+ "libsqlite",
+ }
+ // Add launcher-specific dependencies for bionic
+ if targetForDeps.Os.Bionic() {
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "libc", "libdl", "libm")
+ }
+ if targetForDeps.Os == android.LinuxMusl && !ctx.Config().HostStaticBinaries() {
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "libc_musl")
}
- // Only Python binary and test modules have non-empty installer.
- if p.installer != nil {
- var sharedLibs []string
- // if embedded launcher is enabled, we need to collect the shared library depenendencies of the
- // launcher
- for _, dep := range ctx.GetDirectDepsWithTag(launcherSharedLibTag) {
- sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
+ switch p.properties.Actual_version {
+ case pyVersion2:
+ stdLib = "py2-stdlib"
+
+ launcherModule = "py2-launcher"
+ if autorun {
+ launcherModule = "py2-launcher-autorun"
}
- p.installer.setAndroidMkSharedLibs(sharedLibs)
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++")
+ case pyVersion3:
+ stdLib = "py3-stdlib"
- // Install the par file from installSource
- if p.installSource.Valid() {
- p.installer.install(ctx, p.installSource.Path())
+ launcherModule = "py3-launcher"
+ if autorun {
+ launcherModule = "py3-launcher-autorun"
}
+ if ctx.Config().HostStaticBinaries() && targetForDeps.Os == android.LinuxMusl {
+ launcherModule += "-static"
+ }
+ if ctx.Device() {
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog")
+ }
+ default:
+ panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
+ p.properties.Actual_version, ctx.ModuleName()))
}
+ targetVariations := targetForDeps.Variations()
+ if ctx.ModuleName() != stdLib {
+ stdLibVariations := make([]blueprint.Variation, 0, len(targetVariations)+1)
+ stdLibVariations = append(stdLibVariations, blueprint.Variation{Mutator: "python_version", Variation: p.properties.Actual_version})
+ stdLibVariations = append(stdLibVariations, targetVariations...)
+ // Using AddFarVariationDependencies for all of these because they can be for a different
+ // platform, like if the python module itself was being compiled for device, we may want
+ // the python interpreter built for host so that we can precompile python sources.
+ ctx.AddFarVariationDependencies(stdLibVariations, stdLibTag, stdLib)
+ }
+ ctx.AddFarVariationDependencies(targetVariations, launcherTag, launcherModule)
+ ctx.AddFarVariationDependencies(targetVariations, launcherSharedLibTag, launcherSharedLibDeps...)
}
-// generatePythonBuildActions performs build actions common to all Python modules
-func (p *Module) generatePythonBuildActions(ctx android.ModuleContext) {
+// GenerateAndroidBuildActions performs build actions common to all Python modules
+func (p *PythonLibraryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
expandedSrcs := android.PathsForModuleSrcExcludes(ctx, p.properties.Srcs, p.properties.Exclude_srcs)
- requiresSrcs := true
- if p.bootstrapper != nil && !p.bootstrapper.autorun() {
- requiresSrcs = false
- }
- if len(expandedSrcs) == 0 && requiresSrcs {
- ctx.ModuleErrorf("doesn't have any source files!")
- }
// expand data files from "data" property.
expandedData := android.PathsForModuleSrc(ctx, p.properties.Data)
@@ -589,6 +429,7 @@
// generate the zipfile of all source and data files
p.srcsZip = p.createSrcsZip(ctx, pkgPath)
+ p.precompiledSrcsZip = p.precompileSrcs(ctx)
}
func isValidPythonPath(path string) error {
@@ -607,7 +448,7 @@
// For this module, generate unique pathMappings: <dest: runfiles_path, src: source_path>
// for python/data files expanded from properties.
-func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkgPath string,
+func (p *PythonLibraryModule) genModulePathMappings(ctx android.ModuleContext, pkgPath string,
expandedSrcs, expandedData android.Paths) {
// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
// check current module duplicates.
@@ -642,27 +483,28 @@
}
// createSrcsZip registers build actions to zip current module's sources and data.
-func (p *Module) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path {
+func (p *PythonLibraryModule) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path {
relativeRootMap := make(map[string]android.Paths)
- pathMappings := append(p.srcsPathMappings, p.dataPathMappings...)
-
var protoSrcs android.Paths
- // "srcs" or "data" properties may contain filegroup so it might happen that
- // the root directory for each source path is different.
- for _, path := range pathMappings {
+ addPathMapping := func(path pathMapping) {
// handle proto sources separately
if path.src.Ext() == protoExt {
protoSrcs = append(protoSrcs, path.src)
} else {
- var relativeRoot string
- relativeRoot = strings.TrimSuffix(path.src.String(), path.src.Rel())
- if v, found := relativeRootMap[relativeRoot]; found {
- relativeRootMap[relativeRoot] = append(v, path.src)
- } else {
- relativeRootMap[relativeRoot] = android.Paths{path.src}
- }
+ relativeRoot := strings.TrimSuffix(path.src.String(), path.src.Rel())
+ relativeRootMap[relativeRoot] = append(relativeRootMap[relativeRoot], path.src)
}
}
+
+ // "srcs" or "data" properties may contain filegroups so it might happen that
+ // the root directory for each source path is different.
+ for _, path := range p.srcsPathMappings {
+ addPathMapping(path)
+ }
+ for _, path := range p.dataPathMappings {
+ addPathMapping(path)
+ }
+
var zips android.Paths
if len(protoSrcs) > 0 {
protoFlags := android.GetProtoFlags(ctx, &p.protoProperties)
@@ -736,30 +578,79 @@
}
}
-// isPythonLibModule returns whether the given module is a Python library Module or not
+func (p *PythonLibraryModule) precompileSrcs(ctx android.ModuleContext) android.Path {
+ // To precompile the python sources, we need a python interpreter and stdlib built
+ // for host. We then use those to compile the python sources, which may be used on either
+ // host of device. Python bytecode is architecture agnostic, so we're essentially
+ // "cross compiling" for device here purely by virtue of host and device python bytecode
+ // being the same.
+ var stdLib android.Path
+ var launcher android.Path
+ if ctx.ModuleName() == "py3-stdlib" || ctx.ModuleName() == "py2-stdlib" {
+ stdLib = p.srcsZip
+ } else {
+ ctx.VisitDirectDepsWithTag(hostStdLibTag, func(module android.Module) {
+ if dep, ok := module.(pythonDependency); ok {
+ stdLib = dep.getPrecompiledSrcsZip()
+ }
+ })
+ }
+ ctx.VisitDirectDepsWithTag(hostLauncherTag, func(module android.Module) {
+ if dep, ok := module.(IntermPathProvider); ok {
+ optionalLauncher := dep.IntermPathForModuleOut()
+ if optionalLauncher.Valid() {
+ launcher = optionalLauncher.Path()
+ }
+ }
+ })
+ var launcherSharedLibs android.Paths
+ var ldLibraryPath []string
+ ctx.VisitDirectDepsWithTag(hostlauncherSharedLibTag, func(module android.Module) {
+ if dep, ok := module.(IntermPathProvider); ok {
+ optionalPath := dep.IntermPathForModuleOut()
+ if optionalPath.Valid() {
+ launcherSharedLibs = append(launcherSharedLibs, optionalPath.Path())
+ ldLibraryPath = append(ldLibraryPath, filepath.Dir(optionalPath.Path().String()))
+ }
+ }
+ })
+
+ out := android.PathForModuleOut(ctx, ctx.ModuleName()+".srcszipprecompiled")
+ if stdLib == nil || launcher == nil {
+ // This shouldn't happen in a real build because we'll error out when adding dependencies
+ // on the stdlib and launcher if they don't exist. But some tests set
+ // AllowMissingDependencies.
+ return out
+ }
+ ctx.Build(pctx, android.BuildParams{
+ Rule: precompile,
+ Input: p.srcsZip,
+ Output: out,
+ Implicits: launcherSharedLibs,
+ Description: "Precompile the python sources of " + ctx.ModuleName(),
+ Args: map[string]string{
+ "stdlibZip": stdLib.String(),
+ "launcher": launcher.String(),
+ "ldLibraryPath": strings.Join(ldLibraryPath, ":"),
+ },
+ })
+ return out
+}
+
+// isPythonLibModule returns whether the given module is a Python library PythonLibraryModule or not
func isPythonLibModule(module blueprint.Module) bool {
- if m, ok := module.(*Module); ok {
- return m.isLibrary()
+ if _, ok := module.(*PythonLibraryModule); ok {
+ if _, ok := module.(*PythonBinaryModule); !ok {
+ return true
+ }
}
return false
}
-// This is distinguished by the fact that Python libraries are not installable, while other Python
-// modules are.
-func (p *Module) isLibrary() bool {
- // Python library has no bootstrapper or installer
- return p.bootstrapper == nil && p.installer == nil
-}
-
-func (p *Module) isBinary() bool {
- _, ok := p.bootstrapper.(*binaryDecorator)
- return ok
-}
-
// collectPathsFromTransitiveDeps checks for source/data files for duplicate paths
// for module and its transitive dependencies and collects list of data/source file
// zips for transitive dependencies.
-func (p *Module) collectPathsFromTransitiveDeps(ctx android.ModuleContext) {
+func (p *PythonLibraryModule) collectPathsFromTransitiveDeps(ctx android.ModuleContext, precompiled bool) android.Paths {
// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
// check duplicates.
destToPySrcs := make(map[string]string)
@@ -773,6 +664,8 @@
seen := make(map[android.Module]bool)
+ var result android.Paths
+
// visit all its dependencies in depth first.
ctx.WalkDeps(func(child, parent android.Module) bool {
// we only collect dependencies tagged as python library deps
@@ -801,10 +694,15 @@
checkForDuplicateOutputPath(ctx, destToPyData,
path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child))
}
- p.depsSrcsZips = append(p.depsSrcsZips, dep.getSrcsZip())
+ if precompiled {
+ result = append(result, dep.getPrecompiledSrcsZip())
+ } else {
+ result = append(result, dep.getSrcsZip())
+ }
}
return true
})
+ return result
}
// chckForDuplicateOutputPath checks whether outputPath has already been included in map m, which
@@ -825,18 +723,10 @@
}
// InstallInData returns true as Python is not supported in the system partition
-func (p *Module) InstallInData() bool {
+func (p *PythonLibraryModule) InstallInData() bool {
return true
}
-func (p *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
- if p.isLibrary() {
- pythonLibBp2Build(ctx, p)
- } else if p.isBinary() {
- pythonBinaryBp2Build(ctx, p)
- }
-}
-
var Bool = proptools.Bool
var BoolDefault = proptools.BoolDefault
var String = proptools.String
diff --git a/python/python_test.go b/python/python_test.go
index 42a1ffb..75a6a89 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -18,10 +18,10 @@
"fmt"
"os"
"path/filepath"
- "regexp"
"testing"
"android/soong/android"
+ "android/soong/cc"
)
type pyModule struct {
@@ -33,8 +33,10 @@
}
var (
- buildNamePrefix = "soong_python_test"
- moduleVariantErrTemplate = "%s: module %q variant %q: "
+ buildNamePrefix = "soong_python_test"
+ // We allow maching almost anything before the actual variant so that the os/arch variant
+ // is matched.
+ moduleVariantErrTemplate = `%s: module %q variant "[a-zA-Z0-9_]*%s": `
pkgPathErrTemplate = moduleVariantErrTemplate +
"pkg_path: %q must be a relative path contained in par file."
badIdentifierErrTemplate = moduleVariantErrTemplate +
@@ -312,10 +314,6 @@
"e/file4.py",
},
srcsZip: "out/soong/.intermediates/dir/bin/PY3/bin.py.srcszip",
- depsSrcsZips: []string{
- "out/soong/.intermediates/dir/lib5/PY3/lib5.py.srcszip",
- "out/soong/.intermediates/dir/lib6/PY3/lib6.py.srcszip",
- },
},
},
},
@@ -327,17 +325,26 @@
if d.desc != "module with duplicate runfile path" {
continue
}
- errorPatterns := make([]string, len(d.errors))
- for i, s := range d.errors {
- errorPatterns[i] = regexp.QuoteMeta(s)
- }
+ d.mockFiles[filepath.Join("common", bpFile)] = []byte(`
+python_library {
+ name: "py3-stdlib",
+ host_supported: true,
+}
+cc_binary {
+ name: "py3-launcher",
+ host_supported: true,
+}
+`)
t.Run(d.desc, func(t *testing.T) {
result := android.GroupFixturePreparers(
android.PrepareForTestWithDefaults,
+ android.PrepareForTestWithArchMutator,
+ android.PrepareForTestWithAllowMissingDependencies,
+ cc.PrepareForTestWithCcDefaultModules,
PrepareForTestWithPythonBuildComponents,
d.mockFiles.AddToFixture(),
- ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(errorPatterns)).
+ ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(d.errors)).
RunTest(t)
if len(result.Errs) > 0 {
@@ -346,17 +353,17 @@
for _, e := range d.expectedBinaries {
t.Run(e.name, func(t *testing.T) {
- expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles, e.depsSrcsZips)
+ expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles)
})
}
})
}
}
-func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles, expectedDepsSrcsZips []string) {
+func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles []string) {
module := ctx.ModuleForTests(name, variant)
- base, baseOk := module.Module().(*Module)
+ base, baseOk := module.Module().(*PythonLibraryModule)
if !baseOk {
t.Fatalf("%s is not Python module!", name)
}
@@ -369,8 +376,6 @@
android.AssertDeepEquals(t, "pyRunfiles", expectedPyRunfiles, actualPyRunfiles)
android.AssertPathRelativeToTopEquals(t, "srcsZip", expectedSrcsZip, base.srcsZip)
-
- android.AssertPathsRelativeToTopEquals(t, "depsSrcsZips", expectedDepsSrcsZips, base.depsSrcsZips)
}
func TestMain(m *testing.M) {
diff --git a/python/scripts/precompile_python.py b/python/scripts/precompile_python.py
new file mode 100644
index 0000000..e12e7d2
--- /dev/null
+++ b/python/scripts/precompile_python.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+# Copyright 2023 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import py_compile
+import os
+import shutil
+import tempfile
+import zipfile
+
+# This file needs to support both python 2 and 3.
+
+
+def process_one_file(name, inf, outzip):
+ if not name.endswith('.py'):
+ outzip.writestr(name, inf.read())
+ return
+
+ # Unfortunately py_compile requires the input/output files to be written
+ # out to disk.
+ with tempfile.NamedTemporaryFile(prefix="Soong_precompile_", delete=False) as tmp:
+ shutil.copyfileobj(inf, tmp)
+ in_name = tmp.name
+ with tempfile.NamedTemporaryFile(prefix="Soong_precompile_", delete=False) as tmp:
+ out_name = tmp.name
+ try:
+ py_compile.compile(in_name, out_name, name, doraise=True)
+ with open(out_name, 'rb') as f:
+ outzip.writestr(name + 'c', f.read())
+ finally:
+ os.remove(in_name)
+ os.remove(out_name)
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('src_zip')
+ parser.add_argument('dst_zip')
+ args = parser.parse_args()
+
+ with open(args.dst_zip, 'wb') as outf, open(args.src_zip, 'rb') as inf:
+ with zipfile.ZipFile(outf, mode='w') as outzip, zipfile.ZipFile(inf, mode='r') as inzip:
+ for name in inzip.namelist():
+ with inzip.open(name, mode='r') as inzipf:
+ process_one_file(name, inzipf, outzip)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/python/test.go b/python/test.go
index fc5c211..fb8e918 100644
--- a/python/test.go
+++ b/python/test.go
@@ -32,6 +32,20 @@
ctx.RegisterModuleType("python_test", PythonTestFactory)
}
+func NewTest(hod android.HostOrDeviceSupported) *PythonTestModule {
+ return &PythonTestModule{PythonBinaryModule: *NewBinary(hod)}
+}
+
+func PythonTestHostFactory() android.Module {
+ return NewTest(android.HostSupportedNoCross).init()
+}
+
+func PythonTestFactory() android.Module {
+ module := NewTest(android.HostAndDeviceSupported)
+ module.multilib = android.MultilibBoth
+ return module.init()
+}
+
type TestProperties struct {
// the name of the test configuration (for example "AndroidTest.xml") that should be
// installed with the module.
@@ -52,76 +66,79 @@
Test_options android.CommonTestOptions
}
-type testDecorator struct {
- *binaryDecorator
+type PythonTestModule struct {
+ PythonBinaryModule
testProperties TestProperties
-
- testConfig android.Path
-
- data []android.DataPath
+ testConfig android.Path
+ data []android.DataPath
}
-func (test *testDecorator) bootstrapperProps() []interface{} {
- return append(test.binaryDecorator.bootstrapperProps(), &test.testProperties)
+func (p *PythonTestModule) init() android.Module {
+ p.AddProperties(&p.properties, &p.protoProperties)
+ p.AddProperties(&p.binaryProperties)
+ p.AddProperties(&p.testProperties)
+ android.InitAndroidArchModule(p, p.hod, p.multilib)
+ android.InitDefaultableModule(p)
+ android.InitBazelModule(p)
+ if p.hod == android.HostSupportedNoCross && p.testProperties.Test_options.Unit_test == nil {
+ p.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
+ }
+ return p
}
-func (test *testDecorator) install(ctx android.ModuleContext, file android.Path) {
- test.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
- TestConfigProp: test.testProperties.Test_config,
- TestConfigTemplateProp: test.testProperties.Test_config_template,
- TestSuites: test.binaryDecorator.binaryProperties.Test_suites,
- AutoGenConfig: test.binaryDecorator.binaryProperties.Auto_gen_config,
+func (p *PythonTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // We inherit from only the library's GenerateAndroidBuildActions, and then
+ // just use buildBinary() so that the binary is not installed into the location
+ // it would be for regular binaries.
+ p.PythonLibraryModule.GenerateAndroidBuildActions(ctx)
+ p.buildBinary(ctx)
+
+ p.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+ TestConfigProp: p.testProperties.Test_config,
+ TestConfigTemplateProp: p.testProperties.Test_config_template,
+ TestSuites: p.binaryProperties.Test_suites,
+ AutoGenConfig: p.binaryProperties.Auto_gen_config,
DeviceTemplate: "${PythonBinaryHostTestConfigTemplate}",
HostTemplate: "${PythonBinaryHostTestConfigTemplate}",
})
- test.binaryDecorator.pythonInstaller.dir = "nativetest"
- test.binaryDecorator.pythonInstaller.dir64 = "nativetest64"
+ p.installedDest = ctx.InstallFile(installDir(ctx, "nativetest", "nativetest64", ctx.ModuleName()), p.installSource.Base(), p.installSource)
- test.binaryDecorator.pythonInstaller.relative = ctx.ModuleName()
-
- test.binaryDecorator.pythonInstaller.install(ctx, file)
-
- dataSrcPaths := android.PathsForModuleSrc(ctx, test.testProperties.Data)
-
- for _, dataSrcPath := range dataSrcPaths {
- test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
+ for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Data) {
+ p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
}
// Emulate the data property for java_data dependencies.
for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) {
for _, javaDataSrcPath := range android.OutputFilesForModule(ctx, javaData, "") {
- test.data = append(test.data, android.DataPath{SrcPath: javaDataSrcPath})
+ p.data = append(p.data, android.DataPath{SrcPath: javaDataSrcPath})
}
}
}
-func NewTest(hod android.HostOrDeviceSupported) *Module {
- module, binary := NewBinary(hod)
-
- binary.pythonInstaller = NewPythonInstaller("nativetest", "nativetest64")
-
- test := &testDecorator{binaryDecorator: binary}
- if hod == android.HostSupportedNoCross && test.testProperties.Test_options.Unit_test == nil {
- test.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
+func (p *PythonTestModule) AndroidMkEntries() []android.AndroidMkEntries {
+ entriesList := p.PythonBinaryModule.AndroidMkEntries()
+ if len(entriesList) != 1 {
+ panic("Expected 1 entry")
}
+ entries := &entriesList[0]
- module.bootstrapper = test
- module.installer = test
+ entries.Class = "NATIVE_TESTS"
- return module
-}
+ entries.ExtraEntries = append(entries.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ //entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
+ if p.testConfig != nil {
+ entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
+ }
-func PythonTestHostFactory() android.Module {
- module := NewTest(android.HostSupportedNoCross)
+ entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
- return module.init()
-}
+ entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
-func PythonTestFactory() android.Module {
- module := NewTest(android.HostAndDeviceSupported)
- module.multilib = android.MultilibBoth
+ p.testProperties.Test_options.SetAndroidMkEntries(entries)
+ })
- return module.init()
+ return entriesList
}
diff --git a/python/tests/par_test.py b/python/tests/par_test.py
index 56a5063..1e03f16 100644
--- a/python/tests/par_test.py
+++ b/python/tests/par_test.py
@@ -27,7 +27,10 @@
failed = True
assert_equal("__name__", __name__, "__main__")
-assert_equal("os.path.basename(__file__)", os.path.basename(__file__), "par_test.py")
+fileName = os.path.basename(__file__)
+if fileName.endswith('.pyc'):
+ fileName = fileName[:-1]
+assert_equal("os.path.basename(__file__)", fileName, "par_test.py")
archive = os.path.dirname(__file__)
diff --git a/python/tests/testpkg/par_test.py b/python/tests/testpkg/par_test.py
index ffad430..b513409 100644
--- a/python/tests/testpkg/par_test.py
+++ b/python/tests/testpkg/par_test.py
@@ -28,7 +28,10 @@
archive = sys.modules["__main__"].__loader__.archive
assert_equal("__name__", __name__, "testpkg.par_test")
-assert_equal("__file__", __file__, os.path.join(archive, "testpkg/par_test.py"))
+fileName = __file__
+if fileName.endswith('.pyc'):
+ fileName = fileName[:-1]
+assert_equal("__file__", fileName, os.path.join(archive, "testpkg/par_test.py"))
# Python3 is returning None here for me, and I haven't found any problems caused by this.
if sys.version_info[0] == 2:
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 1ad33a1..e81ec6b 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -29,7 +29,7 @@
defaultBindgenFlags = []string{""}
// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
- bindgenClangVersion = "clang-r468909b"
+ bindgenClangVersion = "clang-r475365b"
_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
diff --git a/rust/config/global.go b/rust/config/global.go
index 26e2d06..50ac1f7 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
var pctx = android.NewPackageContext("android/soong/rust/config")
var (
- RustDefaultVersion = "1.65.0"
+ RustDefaultVersion = "1.65.0.p1"
RustDefaultBase = "prebuilts/rust/"
DefaultEdition = "2021"
Stdlibs = []string{
@@ -51,6 +51,9 @@
// Use v0 mangling to distinguish from C++ symbols
"-C symbol-mangling-version=v0",
"--color always",
+ // TODO (b/267698452): Temporary workaround until the "no unstable
+ // features" policy is enforced.
+ "-A stable-features",
}
deviceGlobalRustFlags = []string{
diff --git a/rust/source_provider.go b/rust/source_provider.go
index 4f8d22b..3236bce 100644
--- a/rust/source_provider.go
+++ b/rust/source_provider.go
@@ -31,9 +31,8 @@
Properties SourceProviderProperties
// The first file in OutputFiles must be the library entry point.
- OutputFiles android.Paths
- subAndroidMkOnce map[SubAndroidMkProvider]bool
- subName string
+ OutputFiles android.Paths
+ subName string
}
var _ SourceProvider = (*BaseSourceProvider)(nil)
diff --git a/scripts/check_boot_jars/package_allowed_list.txt b/scripts/check_boot_jars/package_allowed_list.txt
index a02c195..08bd80c 100644
--- a/scripts/check_boot_jars/package_allowed_list.txt
+++ b/scripts/check_boot_jars/package_allowed_list.txt
@@ -75,6 +75,7 @@
jdk\.internal\.ref
jdk\.internal\.reflect
jdk\.internal\.util
+jdk\.internal\.util\.jar
jdk\.internal\.vm\.annotation
jdk\.net
org\.w3c\.dom
diff --git a/tests/apex_cc_module_arch_variant_tests.sh b/tests/apex_cc_module_arch_variant_tests.sh
index 97e6576..b0cade1 100755
--- a/tests/apex_cc_module_arch_variant_tests.sh
+++ b/tests/apex_cc_module_arch_variant_tests.sh
@@ -56,7 +56,7 @@
# Number of CppCompile actions with arch variant flag
actions_with_arch_variant_num=$(call_bazel aquery --config=bp2build --config=ci --config=android \
- 'mnemonic("CppCompile", deps(//build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal.apex))' | grep -c "\-march=$ARCH_VARIANT_CFLAG")
+ 'mnemonic("CppCompile", deps(//build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal.apex))' | grep -c \'-march=$ARCH_VARIANT_CFLAG\')
# Number of all CppCompile actions
all_cppcompile_actions_num=0
diff --git a/tests/lib.sh b/tests/lib.sh
index 26bdc97..0973beb 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -94,6 +94,8 @@
symlink_directory external/go-cmp
symlink_directory external/golang-protobuf
symlink_directory external/starlark-go
+ symlink_directory external/python
+ symlink_directory external/sqlite
touch "$MOCK_TOP/Android.bp"
}
diff --git a/ui/build/upload.go b/ui/build/upload.go
index 9f14bdd..9959e6f 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -18,16 +18,21 @@
// another.
import (
+ "bufio"
"fmt"
"io/ioutil"
"os"
"path/filepath"
+ "strconv"
+ "strings"
"time"
+ "android/soong/shared"
"android/soong/ui/metrics"
"google.golang.org/protobuf/proto"
+ bazel_metrics_proto "android/soong/ui/metrics/bazel_metrics_proto"
upload_proto "android/soong/ui/metrics/upload_proto"
)
@@ -73,12 +78,113 @@
return metricsFiles
}
+func parseTimingToNanos(str string) int64 {
+ millisString := removeDecimalPoint(str)
+ timingMillis, _ := strconv.ParseInt(millisString, 10, 64)
+ return timingMillis * 1000000
+}
+
+func parsePercentageToTenThousandths(str string) int32 {
+ percentageString := removeDecimalPoint(str)
+ //remove the % at the end of the string
+ percentage := strings.ReplaceAll(percentageString, "%", "")
+ percentagePortion, _ := strconv.ParseInt(percentage, 10, 32)
+ return int32(percentagePortion)
+}
+
+func removeDecimalPoint(numString string) string {
+ // The format is always 0.425 or 10.425
+ return strings.ReplaceAll(numString, ".", "")
+}
+
+func parseTotal(line string) int64 {
+ words := strings.Fields(line)
+ timing := words[3]
+ return parseTimingToNanos(timing)
+}
+
+func parsePhaseTiming(line string) bazel_metrics_proto.PhaseTiming {
+ words := strings.Fields(line)
+ getPhaseNameAndTimingAndPercentage := func([]string) (string, int64, int32) {
+ // Sample lines include:
+ // Total launch phase time 0.011 s 2.59%
+ // Total target pattern evaluation phase time 0.011 s 2.59%
+ var beginning int
+ var end int
+ for ind, word := range words {
+ if word == "Total" {
+ beginning = ind + 1
+ } else if beginning > 0 && word == "phase" {
+ end = ind
+ break
+ }
+ }
+ phaseName := strings.Join(words[beginning:end], " ")
+
+ // end is now "phase" - advance by 2 for timing and 4 for percentage
+ percentageString := words[end+4]
+ timingString := words[end+2]
+ timing := parseTimingToNanos(timingString)
+ percentagePortion := parsePercentageToTenThousandths(percentageString)
+ return phaseName, timing, percentagePortion
+ }
+
+ phaseName, timing, portion := getPhaseNameAndTimingAndPercentage(words)
+ phaseTiming := bazel_metrics_proto.PhaseTiming{}
+ phaseTiming.DurationNanos = &timing
+ phaseTiming.PortionOfBuildTime = &portion
+
+ phaseTiming.PhaseName = &phaseName
+ return phaseTiming
+}
+
+func processBazelMetrics(bazelProfileFile string, bazelMetricsFile string, ctx Context) {
+ if bazelProfileFile == "" {
+ return
+ }
+
+ readBazelProto := func(filepath string) bazel_metrics_proto.BazelMetrics {
+ //serialize the proto, write it
+ bazelMetrics := bazel_metrics_proto.BazelMetrics{}
+
+ file, err := os.ReadFile(filepath)
+ if err != nil {
+ ctx.Fatalln("Error reading metrics file\n", err)
+ }
+
+ scanner := bufio.NewScanner(strings.NewReader(string(file)))
+ scanner.Split(bufio.ScanLines)
+
+ var phaseTimings []*bazel_metrics_proto.PhaseTiming
+ for scanner.Scan() {
+ line := scanner.Text()
+ if strings.HasPrefix(line, "Total run time") {
+ total := parseTotal(line)
+ bazelMetrics.Total = &total
+ } else if strings.HasPrefix(line, "Total") {
+ phaseTiming := parsePhaseTiming(line)
+ phaseTimings = append(phaseTimings, &phaseTiming)
+ }
+ }
+ bazelMetrics.PhaseTimings = phaseTimings
+
+ return bazelMetrics
+ }
+
+ if _, err := os.Stat(bazelProfileFile); err != nil {
+ // We can assume bazel didn't run if the profile doesn't exist
+ return
+ }
+ bazelProto := readBazelProto(bazelProfileFile)
+ shared.Save(&bazelProto, bazelMetricsFile)
+}
+
// UploadMetrics uploads a set of metrics files to a server for analysis.
// The metrics files are first copied to a temporary directory
// and the uploader is then executed in the background to allow the user/system
// to continue working. Soong communicates to the uploader through the
// upload_proto raw protobuf file.
-func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, paths ...string) {
+func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, bazelProfileFile string, bazelMetricsFile string, paths ...string) {
ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
defer ctx.EndTrace()
@@ -88,6 +194,7 @@
return
}
+ processBazelMetrics(bazelProfileFile, bazelMetricsFile, ctx)
// Several of the files might be directories.
metricsFiles := pruneMetricsFiles(paths)
if len(metricsFiles) == 0 {
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
index 764a1e1..58d9237 100644
--- a/ui/build/upload_test.go
+++ b/ui/build/upload_test.go
@@ -29,6 +29,30 @@
"android/soong/ui/logger"
)
+func writeBazelProfileFile(dir string) error {
+ contents := `
+
+=== PHASE SUMMARY INFORMATION ===
+
+Total launch phase time 1.193 s 15.77%
+Total init phase time 1.092 s 14.44%
+Total target pattern evaluation phase time 0.580 s 7.67%
+Total interleaved loading-and-analysis phase time 3.646 s 48.21%
+Total preparation phase time 0.022 s 0.30%
+Total execution phase time 0.993 s 13.13%
+Total finish phase time 0.036 s 0.48%
+---------------------------------------------------------------------
+Total run time 7.563 s 100.00%
+
+Critical path (178 ms):
+ Time Percentage Description
+ 178 ms 100.00% action 'BazelWorkspaceStatusAction stable-status.txt'
+
+`
+ file := filepath.Join(dir, "bazel_metrics.txt")
+ return os.WriteFile(file, []byte(contents), 0666)
+}
+
func TestPruneMetricsFiles(t *testing.T) {
rootDir := t.TempDir()
@@ -84,12 +108,12 @@
}, {
description: "non-existent metrics files no upload",
uploader: "echo",
- files: []string{"metrics_file_1", "metrics_file_2", "metrics_file_3"},
+ files: []string{"metrics_file_1", "metrics_file_2", "metrics_file_3, bazel_metrics.pb"},
}, {
description: "trigger upload",
uploader: "echo",
createFiles: true,
- files: []string{"metrics_file_1", "metrics_file_2"},
+ files: []string{"metrics_file_1", "metrics_file_2, bazel_metrics.pb"},
}}
for _, tt := range tests {
@@ -130,6 +154,9 @@
}
}
}
+ if err := writeBazelProfileFile(outDir); err != nil {
+ t.Fatalf("failed to create bazel profile file in dir: %v", outDir)
+ }
config := Config{&configImpl{
environ: &Environment{
@@ -139,7 +166,7 @@
metricsUploader: tt.uploader,
}}
- UploadMetrics(ctx, config, false, time.Now(), metricsFiles...)
+ UploadMetrics(ctx, config, false, time.Now(), "out/bazel_metrics.txt", "out/bazel_metrics.pb", metricsFiles...)
})
}
}
@@ -194,8 +221,79 @@
metricsUploader: "echo",
}}
- UploadMetrics(ctx, config, true, time.Now(), metricsFile)
+ UploadMetrics(ctx, config, true, time.Now(), "", "", metricsFile)
t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
})
}
}
+
+func TestParsePercentageToTenThousandths(t *testing.T) {
+ // 2.59% should be returned as 259 - representing 259/10000 of the build
+ percentage := parsePercentageToTenThousandths("2.59%")
+ if percentage != 259 {
+ t.Errorf("Expected percentage to be returned as ten-thousandths. Expected 259, have %d\n", percentage)
+ }
+
+ // Test without a leading digit
+ percentage = parsePercentageToTenThousandths(".52%")
+ if percentage != 52 {
+ t.Errorf("Expected percentage to be returned as ten-thousandths. Expected 52, have %d\n", percentage)
+ }
+}
+
+func TestParseTimingToNanos(t *testing.T) {
+ // This parses from seconds (with millis precision) and returns nanos
+ timingNanos := parseTimingToNanos("0.111")
+ if timingNanos != 111000000 {
+ t.Errorf("Error parsing timing. Expected 111000, have %d\n", timingNanos)
+ }
+
+ // Test without a leading digit
+ timingNanos = parseTimingToNanos(".112")
+ if timingNanos != 112000000 {
+ t.Errorf("Error parsing timing. Expected 112000, have %d\n", timingNanos)
+ }
+}
+
+func TestParsePhaseTiming(t *testing.T) {
+ // Sample lines include:
+ // Total launch phase time 0.011 s 2.59%
+ // Total target pattern evaluation phase time 0.012 s 4.59%
+
+ line1 := "Total launch phase time 0.011 s 2.59%"
+ timing := parsePhaseTiming(line1)
+
+ if timing.GetPhaseName() != "launch" {
+ t.Errorf("Failed to parse phase name. Expected launch, have %s\n", timing.GetPhaseName())
+ } else if timing.GetDurationNanos() != 11000000 {
+ t.Errorf("Failed to parse duration nanos. Expected 11000000, have %d\n", timing.GetDurationNanos())
+ } else if timing.GetPortionOfBuildTime() != 259 {
+ t.Errorf("Failed to parse portion of build time. Expected 259, have %d\n", timing.GetPortionOfBuildTime())
+ }
+
+ // Test with a multiword phase name
+ line2 := "Total target pattern evaluation phase time 0.012 s 4.59%"
+
+ timing = parsePhaseTiming(line2)
+ if timing.GetPhaseName() != "target pattern evaluation" {
+ t.Errorf("Failed to parse phase name. Expected target pattern evaluation, have %s\n", timing.GetPhaseName())
+ } else if timing.GetDurationNanos() != 12000000 {
+ t.Errorf("Failed to parse duration nanos. Expected 12000000, have %d\n", timing.GetDurationNanos())
+ } else if timing.GetPortionOfBuildTime() != 459 {
+ t.Errorf("Failed to parse portion of build time. Expected 459, have %d\n", timing.GetPortionOfBuildTime())
+ }
+}
+
+func TestParseTotal(t *testing.T) {
+ // Total line is in the form of:
+ // Total run time 7.563 s 100.00%
+
+ line := "Total run time 7.563 s 100.00%"
+
+ total := parseTotal(line)
+
+ // Only the seconds field is parsed, as nanos
+ if total != 7563000000 {
+ t.Errorf("Failed to parse total build time. Expected 7563000000, have %d\n", total)
+ }
+}
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 2301c56..bd1517c 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -22,6 +22,7 @@
deps: [
"golang-protobuf-proto",
"soong-ui-bp2build_metrics_proto",
+ "soong-ui-bazel_metrics_proto",
"soong-ui-metrics_upload_proto",
"soong-ui-metrics_proto",
"soong-ui-mk_metrics_proto",
@@ -73,6 +74,18 @@
}
bootstrap_go_package {
+ name: "soong-ui-bazel_metrics_proto",
+ pkgPath: "android/soong/ui/metrics/bazel_metrics_proto",
+ deps: [
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ ],
+ srcs: [
+ "bazel_metrics_proto/bazel_metrics.pb.go",
+ ],
+}
+
+bootstrap_go_package {
name: "soong-ui-mk_metrics_proto",
pkgPath: "android/soong/ui/metrics/mk_metrics_proto",
deps: [
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index cbc73ed..def76aa 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -163,6 +163,7 @@
parallelJobs := flags.Int("parallel", runtime.NumCPU(), "number of parallel threads to use")
cpuProfile := flags.String("cpuprofile", "", "write cpu profile to file")
traceFile := flags.String("trace", "", "write trace to file")
+ sha256Checksum := flags.Bool("sha256", false, "add a zip header to each file containing its SHA256 digest")
flags.Var(&rootPrefix{}, "P", "path prefix within the zip at which to place files")
flags.Var(&listFiles{}, "l", "file containing list of files to zip")
@@ -224,6 +225,7 @@
WriteIfChanged: *writeIfChanged,
StoreSymlinks: *symlinks,
IgnoreMissingFiles: *ignoreMissingFiles,
+ Sha256Checksum: *sha256Checksum,
})
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err.Error())
diff --git a/zip/zip.go b/zip/zip.go
index 955fe68..6f1a8ad 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -17,8 +17,11 @@
import (
"bytes"
"compress/flate"
+ "crypto/sha256"
+ "encoding/binary"
"errors"
"fmt"
+ "hash"
"hash/crc32"
"io"
"io/ioutil"
@@ -38,6 +41,14 @@
"android/soong/third_party/zip"
)
+// Sha256HeaderID is a custom Header ID for the `extra` field in
+// the file header to store the SHA checksum.
+const Sha256HeaderID = 0x4967
+
+// Sha256HeaderSignature is the signature to verify that the extra
+// data block is used to store the SHA checksum.
+const Sha256HeaderSignature = 0x9514
+
// Block size used during parallel compression of a single file.
const parallelBlockSize = 1 * 1024 * 1024 // 1MB
@@ -231,6 +242,8 @@
stderr io.Writer
fs pathtools.FileSystem
+
+ sha256Checksum bool
}
type zipEntry struct {
@@ -257,6 +270,7 @@
WriteIfChanged bool
StoreSymlinks bool
IgnoreMissingFiles bool
+ Sha256Checksum bool
Stderr io.Writer
Filesystem pathtools.FileSystem
@@ -280,6 +294,7 @@
ignoreMissingFiles: args.IgnoreMissingFiles,
stderr: args.Stderr,
fs: args.Filesystem,
+ sha256Checksum: args.Sha256Checksum,
}
if z.fs == nil {
@@ -782,15 +797,17 @@
// this based on actual buffer sizes in RateLimit.
ze.futureReaders = make(chan chan io.Reader, (fileSize/parallelBlockSize)+1)
- // Calculate the CRC in the background, since reading the entire
- // file could take a while.
+ // Calculate the CRC and SHA256 in the background, since reading
+ // the entire file could take a while.
//
// We could split this up into chunks as well, but it's faster
// than the compression. Due to the Go Zip API, we also need to
// know the result before we can begin writing the compressed
// data out to the zipfile.
+ //
+ // We calculate SHA256 only if `-sha256` is set.
wg.Add(1)
- go z.crcFile(r, ze, compressChan, wg)
+ go z.checksumFileAsync(r, ze, compressChan, wg)
for start := int64(0); start < fileSize; start += parallelBlockSize {
sr := io.NewSectionReader(r, start, parallelBlockSize)
@@ -829,20 +846,53 @@
return nil
}
-func (z *ZipWriter) crcFile(r io.Reader, ze *zipEntry, resultChan chan *zipEntry, wg *sync.WaitGroup) {
+func (z *ZipWriter) checksumFileAsync(r io.ReadSeeker, ze *zipEntry, resultChan chan *zipEntry, wg *sync.WaitGroup) {
defer wg.Done()
defer z.cpuRateLimiter.Finish()
+ z.checksumFile(r, ze)
+
+ resultChan <- ze
+ close(resultChan)
+}
+
+func (z *ZipWriter) checksumFile(r io.ReadSeeker, ze *zipEntry) {
crc := crc32.NewIEEE()
- _, err := io.Copy(crc, r)
+ writers := []io.Writer{crc}
+
+ var shaHasher hash.Hash
+ if z.sha256Checksum && !ze.fh.Mode().IsDir() {
+ shaHasher = sha256.New()
+ writers = append(writers, shaHasher)
+ }
+
+ w := io.MultiWriter(writers...)
+
+ _, err := io.Copy(w, r)
if err != nil {
z.errors <- err
return
}
ze.fh.CRC32 = crc.Sum32()
- resultChan <- ze
- close(resultChan)
+ if shaHasher != nil {
+ z.appendSHAToExtra(ze, shaHasher.Sum(nil))
+ }
+}
+
+func (z *ZipWriter) appendSHAToExtra(ze *zipEntry, checksum []byte) {
+ // The block of SHA256 checksum consist of:
+ // - Header ID, equals to Sha256HeaderID (2 bytes)
+ // - Data size (2 bytes)
+ // - Data block:
+ // - Signature, equals to Sha256HeaderSignature (2 bytes)
+ // - Data, SHA checksum value
+ var buf []byte
+ buf = binary.LittleEndian.AppendUint16(buf, Sha256HeaderID)
+ buf = binary.LittleEndian.AppendUint16(buf, uint16(len(checksum)+2))
+ buf = binary.LittleEndian.AppendUint16(buf, Sha256HeaderSignature)
+ buf = append(buf, checksum...)
+ ze.fh.Extra = append(ze.fh.Extra, buf...)
}
func (z *ZipWriter) compressPartialFile(r io.Reader, dict []byte, last bool, resultChan chan io.Reader, wg *sync.WaitGroup) {
@@ -894,17 +944,9 @@
}
func (z *ZipWriter) compressWholeFile(ze *zipEntry, r io.ReadSeeker, compressChan chan *zipEntry) {
+ z.checksumFile(r, ze)
- crc := crc32.NewIEEE()
- _, err := io.Copy(crc, r)
- if err != nil {
- z.errors <- err
- return
- }
-
- ze.fh.CRC32 = crc.Sum32()
-
- _, err = r.Seek(0, 0)
+ _, err := r.Seek(0, 0)
if err != nil {
z.errors <- err
return
diff --git a/zip/zip_test.go b/zip/zip_test.go
index c4832dc..e7fdea8 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -16,6 +16,7 @@
import (
"bytes"
+ "encoding/hex"
"hash/crc32"
"io"
"os"
@@ -35,6 +36,10 @@
fileEmpty = []byte("")
fileManifest = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\n\n")
+ sha256FileA = "d53eda7a637c99cc7fb566d96e9fa109bf15c478410a3f5eb4d4c4e26cd081f6"
+ sha256FileB = "430c56c5818e62bcb6d478901ef86284e97714c138f3c86aa14fd6a84b7ce5d3"
+ sha256FileC = "31c5ab6111f1d6aa13c2c4e92bb3c0f7c76b61b42d141af1e846eb7f6586a51c"
+
fileCustomManifest = []byte("Custom manifest: true\n")
customManifestAfter = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\nCustom manifest: true\n\n")
)
@@ -67,6 +72,20 @@
}
}
+func fhWithSHA256(name string, contents []byte, method uint16, sha256 string) zip.FileHeader {
+ h := fh(name, contents, method)
+ // The extra field contains 38 bytes, including 2 bytes of header ID, 2 bytes
+ // of size, 2 bytes of signature, and 32 bytes of checksum data block.
+ var extra [38]byte
+ // The first 6 bytes contains Sha256HeaderID (0x4967), size (unit(34)) and
+ // Sha256HeaderSignature (0x9514)
+ copy(extra[0:], []byte{103, 73, 34, 0, 20, 149})
+ sha256Bytes, _ := hex.DecodeString(sha256)
+ copy(extra[6:], sha256Bytes)
+ h.Extra = append(h.Extra, extra[:]...)
+ return h
+}
+
func fhManifest(contents []byte) zip.FileHeader {
return zip.FileHeader{
Name: "META-INF/MANIFEST.MF",
@@ -87,13 +106,18 @@
}
}
-func fhDir(name string) zip.FileHeader {
+type fhDirOptions struct {
+ extra []byte
+}
+
+func fhDir(name string, opts fhDirOptions) zip.FileHeader {
return zip.FileHeader{
Name: name,
Method: zip.Store,
CRC32: crc32.ChecksumIEEE(nil),
UncompressedSize64: 0,
ExternalAttrs: (syscall.S_IFDIR|0755)<<16 | 0x10,
+ Extra: opts.extra,
}
}
@@ -114,6 +138,7 @@
manifest string
storeSymlinks bool
ignoreMissingFiles bool
+ sha256Checksum bool
files []zip.FileHeader
err error
@@ -320,10 +345,10 @@
emulateJar: true,
files: []zip.FileHeader{
- fhDir("META-INF/"),
+ fhDir("META-INF/", fhDirOptions{extra: []byte{254, 202, 0, 0}}),
fhManifest(fileManifest),
- fhDir("a/"),
- fhDir("a/a/"),
+ fhDir("a/", fhDirOptions{}),
+ fhDir("a/a/", fhDirOptions{}),
fh("a/a/a", fileA, zip.Deflate),
fh("a/a/b", fileB, zip.Deflate),
},
@@ -338,10 +363,10 @@
manifest: "manifest.txt",
files: []zip.FileHeader{
- fhDir("META-INF/"),
+ fhDir("META-INF/", fhDirOptions{extra: []byte{254, 202, 0, 0}}),
fhManifest(customManifestAfter),
- fhDir("a/"),
- fhDir("a/a/"),
+ fhDir("a/", fhDirOptions{}),
+ fhDir("a/a/", fhDirOptions{}),
fh("a/a/a", fileA, zip.Deflate),
fh("a/a/b", fileB, zip.Deflate),
},
@@ -355,8 +380,8 @@
dirEntries: true,
files: []zip.FileHeader{
- fhDir("a/"),
- fhDir("a/a/"),
+ fhDir("a/", fhDirOptions{}),
+ fhDir("a/a/", fhDirOptions{}),
fh("a/a/a", fileA, zip.Deflate),
fh("a/a/b", fileB, zip.Deflate),
},
@@ -412,6 +437,23 @@
fh("a/a/a", fileA, zip.Deflate),
},
},
+ {
+ name: "generate SHA256 checksum",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b").
+ File("a/a/c").
+ File("c"),
+ compressionLevel: 9,
+ sha256Checksum: true,
+
+ files: []zip.FileHeader{
+ fhWithSHA256("a/a/a", fileA, zip.Deflate, sha256FileA),
+ fhWithSHA256("a/a/b", fileB, zip.Deflate, sha256FileB),
+ fhWithSHA256("a/a/c", fileC, zip.Deflate, sha256FileC),
+ fhWithSHA256("c", fileC, zip.Deflate, sha256FileC),
+ },
+ },
// errors
{
@@ -465,6 +507,7 @@
args.ManifestSourcePath = test.manifest
args.StoreSymlinks = test.storeSymlinks
args.IgnoreMissingFiles = test.ignoreMissingFiles
+ args.Sha256Checksum = test.sha256Checksum
args.Filesystem = mockFs
args.Stderr = &bytes.Buffer{}
@@ -555,6 +598,11 @@
t.Errorf("incorrect file %s method want %v got %v", want.Name,
want.Method, got.Method)
}
+
+ if !bytes.Equal(want.Extra, got.Extra) {
+ t.Errorf("incorrect file %s extra want %v got %v", want.Name,
+ want.Extra, got.Extra)
+ }
}
})
}