Skip to content

Commit

Permalink
Remove internal deapexer module
Browse files Browse the repository at this point in the history
The build ations will be created by the top-level apex.

Details
1. In GenerateAndroidBuildActions, do a graph walk to determine if the
   apex has exported deps.
2. If there are exported deps, call the newly introduced `deapex`
   function. This registers the build rules and returns a DeapexerInfo
object. This was previously provided by the internal deapexer
dependency.
3. Update `dexpreoptSystemServerJars and `provideApexExportsInfo` to use
   the DeapexerInfo object from (2).

A lot of unit tests that relied on the legacy mechanism of deapexing
have been updated.

Test: go test ./apex
Test: lunch cf_x86_64_phone-next-userdebug (uses mainline prebuilts)
Test: verified no diff in file_list.txt
Bug: 368337090
Change-Id: I0edb681beccac4d2a9ceb73f9a506c081a8a96e0
  • Loading branch information
dasspandan committed Sep 20, 2024
1 parent a326b32 commit 52c01a1
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 233 deletions.
44 changes: 0 additions & 44 deletions android/deapexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
package android

import (
"fmt"
"strings"

"github.com/google/blueprint"
Expand Down Expand Up @@ -109,10 +108,6 @@ func (i DeapexerInfo) GetExportedModuleNames() []string {
return i.exportedModuleNames
}

// Provider that can be used from within the `GenerateAndroidBuildActions` of a module that depends
// on a `deapexer` module to retrieve its `DeapexerInfo`.
var DeapexerProvider = blueprint.NewProvider[DeapexerInfo]()

// NewDeapexerInfo creates and initializes a DeapexerInfo that is suitable
// for use with a prebuilt_apex module.
//
Expand Down Expand Up @@ -169,45 +164,6 @@ type RequiresFilesFromPrebuiltApexTag interface {
RequiresFilesFromPrebuiltApex()
}

// FindDeapexerProviderForModule searches through the direct dependencies of the current context
// module for a DeapexerTag dependency and returns its DeapexerInfo. If a single nonambiguous
// deapexer module isn't found then it returns it an error
// clients should check the value of error and call ctx.ModuleErrof if a non nil error is received
func FindDeapexerProviderForModule(ctx ModuleContext) (*DeapexerInfo, error) {
var di *DeapexerInfo
var err error
ctx.VisitDirectDepsWithTag(DeapexerTag, func(m Module) {
if err != nil {
// An err has been found. Do not visit further.
return
}
c, ok := OtherModuleProvider(ctx, m, DeapexerProvider)
if !ok {
ctx.ModuleErrorf("Expected all deps with DeapexerTag to have a DeapexerProvider, but module %q did not", m.Name())
return
}
p := &c
if di != nil {
// If two DeapexerInfo providers have been found then check if they are
// equivalent. If they are then use the selected one, otherwise fail.
if selected := equivalentDeapexerInfoProviders(di, p); selected != nil {
di = selected
return
}
err = fmt.Errorf("Multiple installable prebuilt APEXes provide ambiguous deapexers: %s and %s", di.ApexModuleName(), p.ApexModuleName())
}
di = p
})
if err != nil {
return nil, err
}
if di != nil {
return di, nil
}
ai, _ := ModuleProvider(ctx, ApexInfoProvider)
return nil, fmt.Errorf("No prebuilt APEX provides a deapexer module for APEX variant %s", ai.ApexVariationName)
}

// removeCompressedApexSuffix removes the _compressed suffix from the name if present.
func removeCompressedApexSuffix(name string) string {
return strings.TrimSuffix(name, "_compressed")
Expand Down
48 changes: 30 additions & 18 deletions apex/apex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3685,7 +3685,7 @@ func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName, var
}

func ensureExactDeapexedContents(t *testing.T, ctx *android.TestContext, moduleName string, variant string, files []string) {
deapexer := ctx.ModuleForTests(moduleName+".deapexer", variant).Description("deapex")
deapexer := ctx.ModuleForTests(moduleName, variant).Description("deapex")
outputs := make([]string, 0, len(deapexer.ImplicitOutputs)+1)
if deapexer.Output != nil {
outputs = append(outputs, deapexer.Output.String())
Expand Down Expand Up @@ -4959,12 +4959,14 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) {
},
}
java_import {
java_sdk_library_import {
name: "libfoo",
jars: ["libfoo.jar"],
public: {
jars: ["libfoo.jar"],
},
apex_available: ["myapex"],
shared_library: false,
permitted_packages: ["foo"],
sdk_version: "core_current",
}
java_sdk_library_import {
Expand Down Expand Up @@ -5018,13 +5020,17 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) {
apex_available: ["myapex"],
}
java_import {
java_sdk_library_import {
name: "libfoo",
jars: ["libfoo.jar"],
public: {
jars: ["libfoo.jar"],
},
apex_available: ["myapex"],
permitted_packages: ["foo"],
shared_library: false,
permitted_packages: ["libfoo"],
}
java_sdk_library_import {
name: "libbar",
public: {
Expand Down Expand Up @@ -5200,13 +5206,15 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) {
},
}
java_import {
java_sdk_library_import {
name: "libfoo",
prefer: true,
jars: ["libfoo.jar"],
public: {
jars: ["libfoo.jar"],
},
apex_available: ["myapex"],
permitted_packages: ["foo"],
sdk_version: "core_current",
shared_library: false,
permitted_packages: ["libfoo"],
}
java_library {
Expand Down Expand Up @@ -8057,12 +8065,16 @@ func TestDexpreoptAccessDexFilesFromPrebuiltApex(t *testing.T) {
},
}
java_import {
name: "libfoo",
java_sdk_library_import {
name: "libfoo",
prefer: true,
public: {
jars: ["libfoo.jar"],
apex_available: ["myapex"],
permitted_packages: ["libfoo"],
}
},
apex_available: ["myapex"],
shared_library: false,
permitted_packages: ["libfoo"],
}
`, "", preparer, fragment)
})
}
Expand Down Expand Up @@ -10749,12 +10761,12 @@ func TestBootDexJarsMultipleApexPrebuilts(t *testing.T) {
{
desc: "Prebuilt apex prebuilt_com.android.foo is selected, profile should come from .prof deapexed from the prebuilt",
selectedApexContributions: "foo.prebuilt.contributions",
expectedBootJar: "out/soong/.intermediates/prebuilt_com.android.foo.deapexer/android_common/deapexer/javalib/framework-foo.jar",
expectedBootJar: "out/soong/.intermediates/prebuilt_com.android.foo/android_common_com.android.foo/deapexer/javalib/framework-foo.jar",
},
{
desc: "Prebuilt apex prebuilt_com.android.foo.v2 is selected, profile should come from .prof deapexed from the prebuilt",
selectedApexContributions: "foo.prebuilt.v2.contributions",
expectedBootJar: "out/soong/.intermediates/prebuilt_com.android.foo.v2.deapexer/android_common/deapexer/javalib/framework-foo.jar",
expectedBootJar: "out/soong/.intermediates/com.android.foo.v2/android_common_com.android.foo/deapexer/javalib/framework-foo.jar",
},
}

Expand Down
26 changes: 17 additions & 9 deletions apex/bootclasspath_fragment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,11 +398,20 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) {

// Make sure that a preferred prebuilt with consistent contents doesn't affect the apex.
addPrebuilt(true, "foo", "bar"),
android.FixtureMergeMockFs(android.MockFS{
"apex_contributions/Android.bp": []byte(`
apex_contributions {
name: "prebuilt_art_contributions",
contents: ["prebuilt_com.android.art"],
api_domain: "com.android.art",
}
`)}),
android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"),

java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
).RunTest(t)

ensureExactDeapexedContents(t, result.TestContext, "prebuilt_com.android.art", "android_common", []string{
ensureExactDeapexedContents(t, result.TestContext, "prebuilt_com.android.art", "android_common_com.android.art", []string{
"etc/boot-image.prof",
"javalib/bar.jar",
"javalib/foo.jar",
Expand Down Expand Up @@ -495,6 +504,7 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) {
java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:foo", "com.android.art:bar"),
java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"),
)

bp := `
Expand Down Expand Up @@ -552,6 +562,12 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) {
src: "com.mycompany.android.art.apex",
exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
}
apex_contributions {
name: "prebuilt_art_contributions",
contents: ["prebuilt_com.android.art"],
api_domain: "com.android.art",
}
`

t.Run("disabled alternative APEX", func(t *testing.T) {
Expand All @@ -562,26 +578,18 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) {
`dex2oatd`,
`prebuilt_art-bootclasspath-fragment`,
`prebuilt_com.android.art.apex.selector`,
`prebuilt_com.android.art.deapexer`,
})

java.CheckModuleDependencies(t, result.TestContext, "art-bootclasspath-fragment", "android_common_com.android.art", []string{
`all_apex_contributions`,
`dex2oatd`,
`prebuilt_bar`,
`prebuilt_com.android.art.deapexer`,
`prebuilt_foo`,
})

module := result.ModuleForTests("dex_bootjars", "android_common")
checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
})

t.Run("enabled alternative APEX", func(t *testing.T) {
preparers.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
"Multiple installable prebuilt APEXes provide ambiguous deapexers: prebuilt_com.android.art and prebuilt_com.mycompany.android.art")).
RunTestWithBp(t, fmt.Sprintf(bp, ""))
})
}

// checkCopiesToPredefinedLocationForArt checks that the supplied modules are copied to the
Expand Down
84 changes: 10 additions & 74 deletions apex/deapexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,9 @@
package apex

import (
"strings"

"android/soong/android"
)

// Contains 'deapexer' a private module type used by 'prebuilt_apex' to make dex files contained
// within a .apex file referenced by `prebuilt_apex` available for use by their associated
// `java_import` modules.
//
// An 'apex' module references `java_library` modules from which .dex files are obtained that are
// stored in the resulting `.apex` file. The resulting `.apex` file is then made available as a
// prebuilt by referencing it from a `prebuilt_apex`. For each such `java_library` that is used by
// modules outside the `.apex` file a `java_import` prebuilt is made available referencing a jar
// that contains the Java classes.
//
// When building a Java module type, e.g. `java_module` or `android_app` against such prebuilts the
// `java_import` provides the classes jar (jar containing `.class` files) against which the
// module's `.java` files are compiled. That classes jar usually contains only stub classes. The
// resulting classes jar is converted into a dex jar (jar containing `.dex` files). Then if
// necessary the dex jar is further processed by `dexpreopt` to produce an optimized form of the
// library specific to the current Android version. This process requires access to implementation
// dex jars for each `java_import`. The `java_import` will obtain the implementation dex jar from
// the `.apex` file in the associated `prebuilt_apex`.
//
// This is intentionally not registered by name as it is not intended to be used from within an
// `Android.bp` file.

// DeapexerProperties specifies the properties supported by the deapexer module.
//
// As these are never intended to be supplied in a .bp file they use a different naming convention
// to make it clear that they are different.
type DeapexerProperties struct {
// List of common modules that may need access to files exported by this module.
//
Expand All @@ -72,54 +44,17 @@ type SelectedApexProperties struct {
Selected_apex *string `android:"path" blueprint:"mutated"`
}

type Deapexer struct {
android.ModuleBase

properties DeapexerProperties
selectedApexProperties SelectedApexProperties

inputApex android.Path
}

// Returns the name of the deapexer module corresponding to an APEX module with the given name.
func deapexerModuleName(apexModuleName string) string {
return apexModuleName + ".deapexer"
}

// Returns the name of the APEX module corresponding to an deapexer module with
// the given name. This reverses deapexerModuleName.
func apexModuleName(deapexerModuleName string) string {
return strings.TrimSuffix(deapexerModuleName, ".deapexer")
}

func privateDeapexerFactory() android.Module {
module := &Deapexer{}
module.AddProperties(&module.properties, &module.selectedApexProperties)
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
return module
}

func (p *Deapexer) DepsMutator(ctx android.BottomUpMutatorContext) {
// Add dependencies from the java modules to which this exports files from the `.apex` file onto
// this module so that they can access the `DeapexerInfo` object that this provides.
// TODO: b/308174306 - Once all the mainline modules have been flagged, drop this dependency edge
for _, lib := range p.properties.CommonModules {
dep := prebuiltApexExportedModuleName(ctx, lib)
ctx.AddReverseDependency(ctx.Module(), android.DeapexerTag, dep)
}
}

func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
p.inputApex = android.OptionalPathForModuleSrc(ctx, p.selectedApexProperties.Selected_apex).Path()

// deapex creates the build rules to deapex a prebuilt .apex file
// it returns a pointer to a DeapexerInfo object
func deapex(ctx android.ModuleContext, apexFile android.Path, deapexerProps DeapexerProperties) *android.DeapexerInfo {
// Create and remember the directory into which the .apex file's contents will be unpacked.
deapexerOutput := android.PathForModuleOut(ctx, "deapexer")

exports := make(map[string]android.WritablePath)

// Create mappings from apex relative path to the extracted file's path.
exportedPaths := make(android.Paths, 0, len(exports))
for _, path := range p.properties.ExportedFiles {
for _, path := range deapexerProps.ExportedFiles {
// Populate the exports that this makes available.
extractedPath := deapexerOutput.Join(ctx, path)
exports[path] = extractedPath
Expand All @@ -131,9 +66,8 @@ func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// apex relative path to extracted file path available for other modules.
if len(exports) > 0 {
// Make the information available for other modules.
di := android.NewDeapexerInfo(apexModuleName(ctx.ModuleName()), exports, p.properties.CommonModules)
di.AddDexpreoptProfileGuidedExportedModuleNames(p.properties.DexpreoptProfileGuidedModules...)
android.SetProvider(ctx, android.DeapexerProvider, di)
di := android.NewDeapexerInfo(ctx.ModuleName(), exports, deapexerProps.CommonModules)
di.AddDexpreoptProfileGuidedExportedModuleNames(deapexerProps.DexpreoptProfileGuidedModules...)

// Create a sorted list of the files that this exports.
exportedPaths = android.SortedUniquePaths(exportedPaths)
Expand All @@ -147,11 +81,13 @@ func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
BuiltTool("deapexer").
BuiltTool("debugfs").
BuiltTool("fsck.erofs").
Input(p.inputApex).
Input(apexFile).
Text(deapexerOutput.String())
for _, p := range exportedPaths {
command.Output(p.(android.WritablePath))
}
builder.Build("deapexer", "deapex "+apexModuleName(ctx.ModuleName()))
builder.Build("deapexer", "deapex "+ctx.ModuleName())
return &di
}
return nil
}
Loading

0 comments on commit 52c01a1

Please sign in to comment.