Add apex_set module.
apex_set takes an .apks file that contains a set of prebuilt apexes with
different configurations. It uses extract_apks to select and install the
best matching one for the current target.
Bug: 153456259
Test: apex_test.go
Test: com.android.media.apks
Change-Id: I1da8bbcf1611b7c580a0cb225856cbd7029cc0a7
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index d089c28..03266c5 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -16,13 +16,29 @@
import (
"fmt"
+ "strconv"
"strings"
"android/soong/android"
+ "android/soong/java"
+ "github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
+var (
+ extractMatchingApex = pctx.StaticRule(
+ "extractMatchingApex",
+ blueprint.RuleParams{
+ Command: `rm -rf "$out" && ` +
+ `${extract_apks} -o "${out}" -allow-prereleased=${allow-prereleased} ` +
+ `-sdk-version=${sdk-version} -abis=${abis} -screen-densities=all -extract-single ` +
+ `${in}`,
+ CommandDeps: []string{"${extract_apks}"},
+ },
+ "abis", "allow-prereleased", "sdk-version")
+)
+
type Prebuilt struct {
android.ModuleBase
prebuilt android.Prebuilt
@@ -208,3 +224,117 @@
},
}}
}
+
+type ApexSet struct {
+ android.ModuleBase
+ prebuilt android.Prebuilt
+
+ properties ApexSetProperties
+
+ installDir android.InstallPath
+ installFilename string
+ outputApex android.WritablePath
+
+ // list of commands to create symlinks for backward compatibility.
+ // these commands will be attached as LOCAL_POST_INSTALL_CMD
+ compatSymlinks []string
+}
+
+type ApexSetProperties struct {
+ // the .apks file path that contains prebuilt apex files to be extracted.
+ Set *string
+
+ // whether the extracted apex file installable.
+ Installable *bool
+
+ // optional name for the installed apex. If unspecified, name of the
+ // module is used as the file name
+ Filename *string
+
+ // names of modules to be overridden. Listed modules can only be other binaries
+ // (in Make or Soong).
+ // This does not completely prevent installation of the overridden binaries, but if both
+ // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
+ // from PRODUCT_PACKAGES.
+ Overrides []string
+
+ // apexes in this set use prerelease SDK version
+ Prerelease *bool
+}
+
+func (a *ApexSet) installable() bool {
+ return a.properties.Installable == nil || proptools.Bool(a.properties.Installable)
+}
+
+func (a *ApexSet) InstallFilename() string {
+ return proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+imageApexSuffix)
+}
+
+func (a *ApexSet) Prebuilt() *android.Prebuilt {
+ return &a.prebuilt
+}
+
+func (a *ApexSet) Name() string {
+ return a.prebuilt.Name(a.ModuleBase.Name())
+}
+
+// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
+func apexSetFactory() android.Module {
+ module := &ApexSet{}
+ module.AddProperties(&module.properties)
+ android.InitSingleSourcePrebuiltModule(module, &module.properties, "Set")
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ return module
+}
+
+func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ a.installFilename = a.InstallFilename()
+ if !strings.HasSuffix(a.installFilename, imageApexSuffix) {
+ ctx.ModuleErrorf("filename should end in %s for apex_set", imageApexSuffix)
+ }
+
+ apexSet := a.prebuilt.SingleSourcePath(ctx)
+ a.outputApex = android.PathForModuleOut(ctx, a.installFilename)
+ ctx.Build(pctx,
+ android.BuildParams{
+ Rule: extractMatchingApex,
+ Description: "Extract an apex from an apex set",
+ Inputs: android.Paths{apexSet},
+ Output: a.outputApex,
+ Args: map[string]string{
+ "abis": strings.Join(java.SupportedAbis(ctx), ","),
+ "allow-prereleased": strconv.FormatBool(proptools.Bool(a.properties.Prerelease)),
+ "sdk-version": ctx.Config().PlatformSdkVersion(),
+ },
+ })
+ a.installDir = android.PathForModuleInstall(ctx, "apex")
+ if a.installable() {
+ ctx.InstallFile(a.installDir, a.installFilename, a.outputApex)
+ }
+
+ // in case that apex_set replaces source apex (using prefer: prop)
+ a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
+ // or that apex_set overrides other apexes (using overrides: prop)
+ for _, overridden := range a.properties.Overrides {
+ a.compatSymlinks = append(a.compatSymlinks, makeCompatSymlinks(overridden, ctx)...)
+ }
+}
+
+func (a *ApexSet) AndroidMkEntries() []android.AndroidMkEntries {
+ return []android.AndroidMkEntries{android.AndroidMkEntries{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(a.outputApex),
+ Include: "$(BUILD_PREBUILT)",
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", a.installDir.ToMakePath().String())
+ entries.SetString("LOCAL_MODULE_STEM", a.installFilename)
+ entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !a.installable())
+ entries.AddStrings("LOCAL_OVERRIDES_MODULES", a.properties.Overrides...)
+ if len(a.compatSymlinks) > 0 {
+ entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(a.compatSymlinks, " && "))
+ }
+ },
+ },
+ }}
+}