Merge "Export dex implementation jars from prebuilt_apex" am: fe6147eb26
am: c022da2b2a
am: bf4b8488e9
Original change: https://android-review.googlesource.com/c/platform/build/soong/+/1523984 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: I1ec430725444022e43e9532964fdf4134031158c
This commit is contained in:
commit
d8733e056d
13 changed files with 719 additions and 56 deletions
|
@ -21,6 +21,7 @@ bootstrap_go_package {
|
||||||
"bazel_handler.go",
|
"bazel_handler.go",
|
||||||
"config.go",
|
"config.go",
|
||||||
"csuite_config.go",
|
"csuite_config.go",
|
||||||
|
"deapexer.go",
|
||||||
"defaults.go",
|
"defaults.go",
|
||||||
"defs.go",
|
"defs.go",
|
||||||
"depset_generic.go",
|
"depset_generic.go",
|
||||||
|
|
|
@ -64,6 +64,14 @@ type ApexInfo struct {
|
||||||
// module is part of. The ApexContents gives information about which modules the apexBundle
|
// module is part of. The ApexContents gives information about which modules the apexBundle
|
||||||
// has and whether a module became part of the apexBundle via a direct dependency or not.
|
// has and whether a module became part of the apexBundle via a direct dependency or not.
|
||||||
ApexContents []*ApexContents
|
ApexContents []*ApexContents
|
||||||
|
|
||||||
|
// True if this is for a prebuilt_apex.
|
||||||
|
//
|
||||||
|
// If true then this will customize the apex processing to make it suitable for handling
|
||||||
|
// prebuilt_apex, e.g. it will prevent ApexInfos from being merged together.
|
||||||
|
//
|
||||||
|
// See Prebuilt.ApexInfoMutator for more information.
|
||||||
|
ForPrebuiltApex bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex")
|
var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex")
|
||||||
|
@ -412,6 +420,16 @@ func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexIn
|
||||||
sort.Sort(byApexName(apexInfos))
|
sort.Sort(byApexName(apexInfos))
|
||||||
seen := make(map[string]int)
|
seen := make(map[string]int)
|
||||||
for _, apexInfo := range apexInfos {
|
for _, apexInfo := range apexInfos {
|
||||||
|
// If this is for a prebuilt apex then use the actual name of the apex variation to prevent this
|
||||||
|
// from being merged with other ApexInfo. See Prebuilt.ApexInfoMutator for more information.
|
||||||
|
if apexInfo.ForPrebuiltApex {
|
||||||
|
merged = append(merged, apexInfo)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge the ApexInfo together. If a compatible ApexInfo exists then merge the information from
|
||||||
|
// this one into it, otherwise create a new merged ApexInfo from this one and save it away so
|
||||||
|
// other ApexInfo instances can be merged into it.
|
||||||
apexName := apexInfo.ApexVariationName
|
apexName := apexInfo.ApexVariationName
|
||||||
mergedName := apexInfo.mergedName(ctx)
|
mergedName := apexInfo.mergedName(ctx)
|
||||||
if index, exists := seen[mergedName]; exists {
|
if index, exists := seen[mergedName]; exists {
|
||||||
|
@ -582,10 +600,14 @@ const (
|
||||||
// apexContents, and modules in that apex have a provider containing the apexContents of each
|
// apexContents, and modules in that apex have a provider containing the apexContents of each
|
||||||
// apexBundle they are part of.
|
// apexBundle they are part of.
|
||||||
type ApexContents struct {
|
type ApexContents struct {
|
||||||
// map from a module name to its membership to this apexBUndle
|
// map from a module name to its membership in this apexBundle
|
||||||
contents map[string]ApexMembership
|
contents map[string]ApexMembership
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewApexContents creates and initializes an ApexContents that is suitable
|
||||||
|
// for use with an apex module.
|
||||||
|
// * contents is a map from a module name to information about its membership within
|
||||||
|
// the apex.
|
||||||
func NewApexContents(contents map[string]ApexMembership) *ApexContents {
|
func NewApexContents(contents map[string]ApexMembership) *ApexContents {
|
||||||
return &ApexContents{
|
return &ApexContents{
|
||||||
contents: contents,
|
contents: contents,
|
||||||
|
|
|
@ -20,6 +20,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_mergeApexVariations(t *testing.T) {
|
func Test_mergeApexVariations(t *testing.T) {
|
||||||
|
const (
|
||||||
|
ForPrebuiltApex = true
|
||||||
|
NotForPrebuiltApex = false
|
||||||
|
)
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
in []ApexInfo
|
in []ApexInfo
|
||||||
|
@ -29,10 +33,10 @@ func Test_mergeApexVariations(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "single",
|
name: "single",
|
||||||
in: []ApexInfo{
|
in: []ApexInfo{
|
||||||
{"foo", "current", false, nil, []string{"foo"}, nil},
|
{"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||||
},
|
},
|
||||||
wantMerged: []ApexInfo{
|
wantMerged: []ApexInfo{
|
||||||
{"apex10000", "current", false, nil, []string{"foo"}, nil},
|
{"apex10000", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||||
},
|
},
|
||||||
wantAliases: [][2]string{
|
wantAliases: [][2]string{
|
||||||
{"foo", "apex10000"},
|
{"foo", "apex10000"},
|
||||||
|
@ -41,11 +45,11 @@ func Test_mergeApexVariations(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "merge",
|
name: "merge",
|
||||||
in: []ApexInfo{
|
in: []ApexInfo{
|
||||||
{"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil},
|
{"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||||
{"bar", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar"}, nil},
|
{"bar", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar"}, nil, NotForPrebuiltApex},
|
||||||
},
|
},
|
||||||
wantMerged: []ApexInfo{
|
wantMerged: []ApexInfo{
|
||||||
{"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, nil}},
|
{"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, nil, false}},
|
||||||
wantAliases: [][2]string{
|
wantAliases: [][2]string{
|
||||||
{"bar", "apex10000_baz_1"},
|
{"bar", "apex10000_baz_1"},
|
||||||
{"foo", "apex10000_baz_1"},
|
{"foo", "apex10000_baz_1"},
|
||||||
|
@ -54,12 +58,12 @@ func Test_mergeApexVariations(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "don't merge version",
|
name: "don't merge version",
|
||||||
in: []ApexInfo{
|
in: []ApexInfo{
|
||||||
{"foo", "current", false, nil, []string{"foo"}, nil},
|
{"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||||
{"bar", "30", false, nil, []string{"bar"}, nil},
|
{"bar", "30", false, nil, []string{"bar"}, nil, NotForPrebuiltApex},
|
||||||
},
|
},
|
||||||
wantMerged: []ApexInfo{
|
wantMerged: []ApexInfo{
|
||||||
{"apex30", "30", false, nil, []string{"bar"}, nil},
|
{"apex30", "30", false, nil, []string{"bar"}, nil, NotForPrebuiltApex},
|
||||||
{"apex10000", "current", false, nil, []string{"foo"}, nil},
|
{"apex10000", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||||
},
|
},
|
||||||
wantAliases: [][2]string{
|
wantAliases: [][2]string{
|
||||||
{"bar", "apex30"},
|
{"bar", "apex30"},
|
||||||
|
@ -69,11 +73,11 @@ func Test_mergeApexVariations(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "merge updatable",
|
name: "merge updatable",
|
||||||
in: []ApexInfo{
|
in: []ApexInfo{
|
||||||
{"foo", "current", false, nil, []string{"foo"}, nil},
|
{"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||||
{"bar", "current", true, nil, []string{"bar"}, nil},
|
{"bar", "current", true, nil, []string{"bar"}, nil, NotForPrebuiltApex},
|
||||||
},
|
},
|
||||||
wantMerged: []ApexInfo{
|
wantMerged: []ApexInfo{
|
||||||
{"apex10000", "current", true, nil, []string{"bar", "foo"}, nil},
|
{"apex10000", "current", true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
|
||||||
},
|
},
|
||||||
wantAliases: [][2]string{
|
wantAliases: [][2]string{
|
||||||
{"bar", "apex10000"},
|
{"bar", "apex10000"},
|
||||||
|
@ -83,19 +87,38 @@ func Test_mergeApexVariations(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "don't merge sdks",
|
name: "don't merge sdks",
|
||||||
in: []ApexInfo{
|
in: []ApexInfo{
|
||||||
{"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil},
|
{"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||||
{"bar", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil},
|
{"bar", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex},
|
||||||
},
|
},
|
||||||
wantMerged: []ApexInfo{
|
wantMerged: []ApexInfo{
|
||||||
{"apex10000_baz_2", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil},
|
{"apex10000_baz_2", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex},
|
||||||
{"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil},
|
{"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||||
},
|
},
|
||||||
wantAliases: [][2]string{
|
wantAliases: [][2]string{
|
||||||
{"bar", "apex10000_baz_2"},
|
{"bar", "apex10000_baz_2"},
|
||||||
{"foo", "apex10000_baz_1"},
|
{"foo", "apex10000_baz_1"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "don't merge when for prebuilt_apex",
|
||||||
|
in: []ApexInfo{
|
||||||
|
{"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||||
|
{"bar", "current", true, nil, []string{"bar"}, nil, NotForPrebuiltApex},
|
||||||
|
// This one should not be merged in with the others because it is for
|
||||||
|
// a prebuilt_apex.
|
||||||
|
{"baz", "current", true, nil, []string{"baz"}, nil, ForPrebuiltApex},
|
||||||
|
},
|
||||||
|
wantMerged: []ApexInfo{
|
||||||
|
{"apex10000", "current", true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
|
||||||
|
{"baz", "current", true, nil, []string{"baz"}, nil, ForPrebuiltApex},
|
||||||
|
},
|
||||||
|
wantAliases: [][2]string{
|
||||||
|
{"bar", "apex10000"},
|
||||||
|
{"foo", "apex10000"},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
config := TestConfig(buildDir, nil, "", nil)
|
config := TestConfig(buildDir, nil, "", nil)
|
||||||
|
|
83
android/deapexer.go
Normal file
83
android/deapexer.go
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
// Copyright (C) 2021 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package android
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/blueprint"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Provides support for interacting with the `deapexer` module to which a `prebuilt_apex` module
|
||||||
|
// will delegate the work to export files from a prebuilt '.apex` file.
|
||||||
|
|
||||||
|
// The information exported by the `deapexer` module, access it using `DeapxerInfoProvider`.
|
||||||
|
type DeapexerInfo struct {
|
||||||
|
// map from the name of an exported file from a prebuilt_apex to the path to that file. The
|
||||||
|
// exported file name is of the form <module>{<tag>} where <tag> is currently only allowed to be
|
||||||
|
// ".dexjar".
|
||||||
|
//
|
||||||
|
// See Prebuilt.ApexInfoMutator for more information.
|
||||||
|
exports map[string]Path
|
||||||
|
}
|
||||||
|
|
||||||
|
// The set of supported prebuilt export tags. Used to verify the tag parameter for
|
||||||
|
// `PrebuiltExportPath`.
|
||||||
|
var supportedPrebuiltExportTags = map[string]struct{}{
|
||||||
|
".dexjar": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrebuiltExportPath provides the path, or nil if not available, of a file exported from the
|
||||||
|
// prebuilt_apex that created this ApexInfo.
|
||||||
|
//
|
||||||
|
// The exported file is identified by the module name and the tag:
|
||||||
|
// * The module name is the name of the module that contributed the file when the .apex file
|
||||||
|
// referenced by the prebuilt_apex was built. It must be specified in one of the exported_...
|
||||||
|
// properties on the prebuilt_apex module.
|
||||||
|
// * The tag identifies the type of file and is dependent on the module type.
|
||||||
|
//
|
||||||
|
// See apex/deapexer.go for more information.
|
||||||
|
func (i DeapexerInfo) PrebuiltExportPath(name, tag string) Path {
|
||||||
|
|
||||||
|
if _, ok := supportedPrebuiltExportTags[tag]; !ok {
|
||||||
|
panic(fmt.Errorf("unsupported prebuilt export tag %q, expected one of %s",
|
||||||
|
tag, strings.Join(SortedStringKeys(supportedPrebuiltExportTags), ", ")))
|
||||||
|
}
|
||||||
|
|
||||||
|
path := i.exports[name+"{"+tag+"}"]
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// See apex/deapexer.go for more information.
|
||||||
|
func NewDeapexerInfo(exports map[string]Path) DeapexerInfo {
|
||||||
|
return DeapexerInfo{
|
||||||
|
exports: exports,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type deapexerTagStruct struct {
|
||||||
|
blueprint.BaseDependencyTag
|
||||||
|
}
|
||||||
|
|
||||||
|
// A tag that is used for dependencies on the `deapexer` module.
|
||||||
|
var DeapexerTag = deapexerTagStruct{}
|
|
@ -469,6 +469,9 @@ func AndroidMkDataForTest(t *testing.T, config Config, bpPath string, mod bluepr
|
||||||
//
|
//
|
||||||
// The build and source paths should be distinguishable based on their contents.
|
// The build and source paths should be distinguishable based on their contents.
|
||||||
func NormalizePathForTesting(path Path) string {
|
func NormalizePathForTesting(path Path) string {
|
||||||
|
if path == nil {
|
||||||
|
return "<nil path>"
|
||||||
|
}
|
||||||
p := path.String()
|
p := path.String()
|
||||||
// Allow absolute paths to /dev/
|
// Allow absolute paths to /dev/
|
||||||
if strings.HasPrefix(p, "/dev/") {
|
if strings.HasPrefix(p, "/dev/") {
|
||||||
|
|
|
@ -18,6 +18,7 @@ bootstrap_go_package {
|
||||||
"apex.go",
|
"apex.go",
|
||||||
"apex_singleton.go",
|
"apex_singleton.go",
|
||||||
"builder.go",
|
"builder.go",
|
||||||
|
"deapexer.go",
|
||||||
"key.go",
|
"key.go",
|
||||||
"prebuilt.go",
|
"prebuilt.go",
|
||||||
"vndk.go",
|
"vndk.go",
|
||||||
|
|
|
@ -1501,7 +1501,7 @@ func apexFileForFilesystem(ctx android.BaseModuleContext, buildFile android.Path
|
||||||
return newApexFile(ctx, buildFile, buildFile.Base(), dirInApex, etc, fs)
|
return newApexFile(ctx, buildFile, buildFile.Base(), dirInApex, etc, fs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WalyPayloadDeps visits dependencies that contributes to the payload of this APEX. For each of the
|
// WalkPayloadDeps visits dependencies that contributes to the payload of this APEX. For each of the
|
||||||
// visited module, the `do` callback is executed. Returning true in the callback continues the visit
|
// visited module, the `do` callback is executed. Returning true in the callback continues the visit
|
||||||
// to the child modules. Returning false makes the visit to continue in the sibling or the parent
|
// to the child modules. Returning false makes the visit to continue in the sibling or the parent
|
||||||
// modules. This is used in check* functions below.
|
// modules. This is used in check* functions below.
|
||||||
|
|
|
@ -190,6 +190,7 @@ func testApexContext(_ *testing.T, bp string, handlers ...testCustomizer) (*andr
|
||||||
"testdata/baz": nil,
|
"testdata/baz": nil,
|
||||||
"AppSet.apks": nil,
|
"AppSet.apks": nil,
|
||||||
"foo.rs": nil,
|
"foo.rs": nil,
|
||||||
|
"libfoo.jar": nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
cc.GatherRequiredFilesForTest(fs)
|
cc.GatherRequiredFilesForTest(fs)
|
||||||
|
@ -4219,6 +4220,121 @@ func TestPrebuiltOverrides(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrebuiltExportDexImplementationJars(t *testing.T) {
|
||||||
|
transform := func(config *dexpreopt.GlobalConfig) {
|
||||||
|
config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"})
|
||||||
|
}
|
||||||
|
|
||||||
|
checkDexJarBuildPath := func(ctx *android.TestContext, name string) {
|
||||||
|
// Make sure the import has been given the correct path to the dex jar.
|
||||||
|
p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.Dependency)
|
||||||
|
dexJarBuildPath := p.DexJarBuildPath()
|
||||||
|
if expected, actual := ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar", android.NormalizePathForTesting(dexJarBuildPath); actual != expected {
|
||||||
|
t.Errorf("Incorrect DexJarBuildPath value '%s', expected '%s'", actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureNoSourceVariant := func(ctx *android.TestContext) {
|
||||||
|
// Make sure that an apex variant is not created for the source module.
|
||||||
|
if expected, actual := []string{"android_common"}, ctx.ModuleVariantsForTests("libfoo"); !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Errorf("invalid set of variants for %q: expected %q, found %q", "libfoo", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("prebuilt only", func(t *testing.T) {
|
||||||
|
bp := `
|
||||||
|
prebuilt_apex {
|
||||||
|
name: "myapex",
|
||||||
|
arch: {
|
||||||
|
arm64: {
|
||||||
|
src: "myapex-arm64.apex",
|
||||||
|
},
|
||||||
|
arm: {
|
||||||
|
src: "myapex-arm.apex",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exported_java_libs: ["libfoo"],
|
||||||
|
}
|
||||||
|
|
||||||
|
java_import {
|
||||||
|
name: "libfoo",
|
||||||
|
jars: ["libfoo.jar"],
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// Make sure that dexpreopt can access dex implementation files from the prebuilt.
|
||||||
|
ctx := testDexpreoptWithApexes(t, bp, "", transform)
|
||||||
|
|
||||||
|
checkDexJarBuildPath(ctx, "libfoo")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("prebuilt with source preferred", func(t *testing.T) {
|
||||||
|
|
||||||
|
bp := `
|
||||||
|
prebuilt_apex {
|
||||||
|
name: "myapex",
|
||||||
|
arch: {
|
||||||
|
arm64: {
|
||||||
|
src: "myapex-arm64.apex",
|
||||||
|
},
|
||||||
|
arm: {
|
||||||
|
src: "myapex-arm.apex",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exported_java_libs: ["libfoo"],
|
||||||
|
}
|
||||||
|
|
||||||
|
java_import {
|
||||||
|
name: "libfoo",
|
||||||
|
jars: ["libfoo.jar"],
|
||||||
|
}
|
||||||
|
|
||||||
|
java_library {
|
||||||
|
name: "libfoo",
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// Make sure that dexpreopt can access dex implementation files from the prebuilt.
|
||||||
|
ctx := testDexpreoptWithApexes(t, bp, "", transform)
|
||||||
|
|
||||||
|
checkDexJarBuildPath(ctx, "prebuilt_libfoo")
|
||||||
|
ensureNoSourceVariant(ctx)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("prebuilt preferred with source", func(t *testing.T) {
|
||||||
|
bp := `
|
||||||
|
prebuilt_apex {
|
||||||
|
name: "myapex",
|
||||||
|
prefer: true,
|
||||||
|
arch: {
|
||||||
|
arm64: {
|
||||||
|
src: "myapex-arm64.apex",
|
||||||
|
},
|
||||||
|
arm: {
|
||||||
|
src: "myapex-arm.apex",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exported_java_libs: ["libfoo"],
|
||||||
|
}
|
||||||
|
|
||||||
|
java_import {
|
||||||
|
name: "libfoo",
|
||||||
|
jars: ["libfoo.jar"],
|
||||||
|
}
|
||||||
|
|
||||||
|
java_library {
|
||||||
|
name: "libfoo",
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// Make sure that dexpreopt can access dex implementation files from the prebuilt.
|
||||||
|
ctx := testDexpreoptWithApexes(t, bp, "", transform)
|
||||||
|
|
||||||
|
checkDexJarBuildPath(ctx, "prebuilt_libfoo")
|
||||||
|
ensureNoSourceVariant(ctx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestApexWithTests(t *testing.T) {
|
func TestApexWithTests(t *testing.T) {
|
||||||
ctx, config := testApex(t, `
|
ctx, config := testApex(t, `
|
||||||
apex_test {
|
apex_test {
|
||||||
|
@ -5783,7 +5899,7 @@ func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, transformDexpre
|
||||||
testDexpreoptWithApexes(t, bp, errmsg, transformDexpreoptConfig)
|
testDexpreoptWithApexes(t, bp, errmsg, transformDexpreoptConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) {
|
func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) *android.TestContext {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
bp += cc.GatherRequiredDepsForTest(android.Android)
|
bp += cc.GatherRequiredDepsForTest(android.Android)
|
||||||
|
@ -5808,6 +5924,7 @@ func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreopt
|
||||||
ctx := android.NewTestArchContext(config)
|
ctx := android.NewTestArchContext(config)
|
||||||
ctx.RegisterModuleType("apex", BundleFactory)
|
ctx.RegisterModuleType("apex", BundleFactory)
|
||||||
ctx.RegisterModuleType("apex_key", ApexKeyFactory)
|
ctx.RegisterModuleType("apex_key", ApexKeyFactory)
|
||||||
|
ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
|
||||||
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
|
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
|
||||||
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
|
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
|
||||||
android.RegisterPrebuiltMutators(ctx)
|
android.RegisterPrebuiltMutators(ctx)
|
||||||
|
@ -5837,10 +5954,11 @@ func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreopt
|
||||||
android.FailIfErrored(t, errs)
|
android.FailIfErrored(t, errs)
|
||||||
} else if len(errs) > 0 {
|
} else if len(errs) > 0 {
|
||||||
android.FailIfNoMatchingErrors(t, errmsg, errs)
|
android.FailIfNoMatchingErrors(t, errmsg, errs)
|
||||||
return
|
|
||||||
} else {
|
} else {
|
||||||
t.Fatalf("missing expected error %q (0 errors are returned)", errmsg)
|
t.Fatalf("missing expected error %q (0 errors are returned)", errmsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
|
func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
|
||||||
|
@ -5939,6 +6057,56 @@ func TestNoUpdatableJarsInBootImage(t *testing.T) {
|
||||||
}
|
}
|
||||||
testNoUpdatableJarsInBootImage(t, "", transform)
|
testNoUpdatableJarsInBootImage(t, "", transform)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDexpreoptAccessDexFilesFromPrebuiltApex(t *testing.T) {
|
||||||
|
transform := func(config *dexpreopt.GlobalConfig) {
|
||||||
|
config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"})
|
||||||
|
}
|
||||||
|
t.Run("prebuilt no source", func(t *testing.T) {
|
||||||
|
testDexpreoptWithApexes(t, `
|
||||||
|
prebuilt_apex {
|
||||||
|
name: "myapex" ,
|
||||||
|
arch: {
|
||||||
|
arm64: {
|
||||||
|
src: "myapex-arm64.apex",
|
||||||
|
},
|
||||||
|
arm: {
|
||||||
|
src: "myapex-arm.apex",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exported_java_libs: ["libfoo"],
|
||||||
|
}
|
||||||
|
|
||||||
|
java_import {
|
||||||
|
name: "libfoo",
|
||||||
|
jars: ["libfoo.jar"],
|
||||||
|
}
|
||||||
|
`, "", transform)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("prebuilt no source", func(t *testing.T) {
|
||||||
|
testDexpreoptWithApexes(t, `
|
||||||
|
prebuilt_apex {
|
||||||
|
name: "myapex" ,
|
||||||
|
arch: {
|
||||||
|
arm64: {
|
||||||
|
src: "myapex-arm64.apex",
|
||||||
|
},
|
||||||
|
arm: {
|
||||||
|
src: "myapex-arm.apex",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exported_java_libs: ["libfoo"],
|
||||||
|
}
|
||||||
|
|
||||||
|
java_import {
|
||||||
|
name: "libfoo",
|
||||||
|
jars: ["libfoo.jar"],
|
||||||
|
}
|
||||||
|
`, "", transform)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testApexPermittedPackagesRules(t *testing.T, errmsg, bp string, apexBootJars []string, rules []android.Rule) {
|
func testApexPermittedPackagesRules(t *testing.T, errmsg, bp string, apexBootJars []string, rules []android.Rule) {
|
||||||
|
|
139
apex/deapexer.go
Normal file
139
apex/deapexer.go
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
// Copyright (C) 2021 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package apex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"android/soong/android"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Properties that are specific to `deapexer` but which need to be provided on the `prebuilt_apex`
|
||||||
|
// module.`
|
||||||
|
type DeapexerProperties struct {
|
||||||
|
// List of java libraries that are embedded inside this prebuilt APEX bundle and for which this
|
||||||
|
// APEX bundle will provide dex implementation jars for use by dexpreopt and boot jars package
|
||||||
|
// check.
|
||||||
|
Exported_java_libs []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Deapexer struct {
|
||||||
|
android.ModuleBase
|
||||||
|
prebuilt android.Prebuilt
|
||||||
|
|
||||||
|
properties DeapexerProperties
|
||||||
|
apexFileProperties ApexFileProperties
|
||||||
|
|
||||||
|
inputApex android.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
func privateDeapexerFactory() android.Module {
|
||||||
|
module := &Deapexer{}
|
||||||
|
module.AddProperties(
|
||||||
|
&module.properties,
|
||||||
|
&module.apexFileProperties,
|
||||||
|
)
|
||||||
|
android.InitSingleSourcePrebuiltModule(module, &module.apexFileProperties, "Source")
|
||||||
|
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
|
||||||
|
return module
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Deapexer) Prebuilt() *android.Prebuilt {
|
||||||
|
return &p.prebuilt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Deapexer) Name() string {
|
||||||
|
return p.prebuilt.Name(p.ModuleBase.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Deapexer) DepsMutator(ctx android.BottomUpMutatorContext) {
|
||||||
|
if err := p.apexFileProperties.selectSource(ctx); err != nil {
|
||||||
|
ctx.ModuleErrorf("%s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
for _, lib := range p.properties.Exported_java_libs {
|
||||||
|
dep := prebuiltApexExportedModuleName(ctx, lib)
|
||||||
|
ctx.AddReverseDependency(ctx.Module(), android.DeapexerTag, dep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||||
|
p.inputApex = p.Prebuilt().SingleSourcePath(ctx)
|
||||||
|
|
||||||
|
// 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.Path)
|
||||||
|
|
||||||
|
// Create mappings from name+tag to all the required exported paths.
|
||||||
|
for _, l := range p.properties.Exported_java_libs {
|
||||||
|
// Populate the exports that this makes available. The path here must match the path of the
|
||||||
|
// file in the APEX created by apexFileForJavaModule(...).
|
||||||
|
exports[l+"{.dexjar}"] = deapexerOutput.Join(ctx, "javalib", l+".jar")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the prebuilt_apex exports any files then create a build rule that unpacks the apex using
|
||||||
|
// deapexer and verifies that all the required files were created. Also, make the mapping from
|
||||||
|
// name+tag to path available for other modules.
|
||||||
|
if len(exports) > 0 {
|
||||||
|
// Make the information available for other modules.
|
||||||
|
ctx.SetProvider(android.DeapexerProvider, android.NewDeapexerInfo(exports))
|
||||||
|
|
||||||
|
// Create a sorted list of the files that this exports.
|
||||||
|
exportedPaths := make(android.Paths, 0, len(exports))
|
||||||
|
for _, p := range exports {
|
||||||
|
exportedPaths = append(exportedPaths, p)
|
||||||
|
}
|
||||||
|
exportedPaths = android.SortedUniquePaths(exportedPaths)
|
||||||
|
|
||||||
|
// The apex needs to export some files so create a ninja rule to unpack the apex and check that
|
||||||
|
// the required files are present.
|
||||||
|
builder := android.NewRuleBuilder(pctx, ctx)
|
||||||
|
command := builder.Command()
|
||||||
|
command.
|
||||||
|
Tool(android.PathForSource(ctx, "build/soong/scripts/unpack-prebuilt-apex.sh")).
|
||||||
|
BuiltTool("deapexer").
|
||||||
|
BuiltTool("debugfs").
|
||||||
|
Input(p.inputApex).
|
||||||
|
Text(deapexerOutput.String())
|
||||||
|
for _, p := range exportedPaths {
|
||||||
|
command.Output(p.(android.WritablePath))
|
||||||
|
}
|
||||||
|
builder.Build("deapexer", "deapex "+ctx.ModuleName())
|
||||||
|
}
|
||||||
|
}
|
134
apex/prebuilt.go
134
apex/prebuilt.go
|
@ -157,6 +157,7 @@ func (p *ApexFileProperties) selectSource(ctx android.BottomUpMutatorContext) er
|
||||||
|
|
||||||
type PrebuiltProperties struct {
|
type PrebuiltProperties struct {
|
||||||
ApexFileProperties
|
ApexFileProperties
|
||||||
|
DeapexerProperties
|
||||||
|
|
||||||
Installable *bool
|
Installable *bool
|
||||||
// Optional name for the installed apex. If unspecified, name of the
|
// Optional name for the installed apex. If unspecified, name of the
|
||||||
|
@ -197,19 +198,152 @@ func (p *Prebuilt) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
|
// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
|
||||||
|
//
|
||||||
|
// If this needs to make files from within a `.apex` file available for use by other Soong modules,
|
||||||
|
// e.g. make dex implementation jars available for java_import modules isted in exported_java_libs,
|
||||||
|
// it does so as follows:
|
||||||
|
//
|
||||||
|
// 1. It creates a `deapexer` module that actually extracts the files from the `.apex` file and
|
||||||
|
// makes them available for use by other modules, at both Soong and ninja levels.
|
||||||
|
//
|
||||||
|
// 2. It adds a dependency onto those modules and creates an apex specific variant similar to what
|
||||||
|
// an `apex` module does. That ensures that code which looks for specific apex variant, e.g.
|
||||||
|
// dexpreopt, will work the same way from source and prebuilt.
|
||||||
|
//
|
||||||
|
// 3. The `deapexer` module adds a dependency from the modules that require the exported files onto
|
||||||
|
// itself so that they can retrieve the file paths to those files.
|
||||||
|
//
|
||||||
func PrebuiltFactory() android.Module {
|
func PrebuiltFactory() android.Module {
|
||||||
module := &Prebuilt{}
|
module := &Prebuilt{}
|
||||||
module.AddProperties(&module.properties)
|
module.AddProperties(&module.properties)
|
||||||
android.InitSingleSourcePrebuiltModule(module, &module.properties, "Source")
|
android.InitSingleSourcePrebuiltModule(module, &module.properties, "Source")
|
||||||
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
|
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
|
||||||
|
|
||||||
|
android.AddLoadHook(module, func(ctx android.LoadHookContext) {
|
||||||
|
props := struct {
|
||||||
|
Name *string
|
||||||
|
}{
|
||||||
|
Name: proptools.StringPtr(module.BaseModuleName() + ".deapexer"),
|
||||||
|
}
|
||||||
|
ctx.CreateModule(privateDeapexerFactory,
|
||||||
|
&props,
|
||||||
|
&module.properties.ApexFileProperties,
|
||||||
|
&module.properties.DeapexerProperties,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
return module
|
return module
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prebuiltApexExportedModuleName(ctx android.BottomUpMutatorContext, name string) string {
|
||||||
|
// The prebuilt_apex should be depending on prebuilt modules but as this runs after
|
||||||
|
// prebuilt_rename the prebuilt module may or may not be using the prebuilt_ prefixed named. So,
|
||||||
|
// check to see if the prefixed name is in use first, if it is then use that, otherwise assume
|
||||||
|
// the unprefixed name is the one to use. If the unprefixed one turns out to be a source module
|
||||||
|
// and not a renamed prebuilt module then that will be detected and reported as an error when
|
||||||
|
// processing the dependency in ApexInfoMutator().
|
||||||
|
prebuiltName := "prebuilt_" + name
|
||||||
|
if ctx.OtherModuleExists(prebuiltName) {
|
||||||
|
name = prebuiltName
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
|
func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
|
||||||
if err := p.properties.selectSource(ctx); err != nil {
|
if err := p.properties.selectSource(ctx); err != nil {
|
||||||
ctx.ModuleErrorf("%s", err)
|
ctx.ModuleErrorf("%s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add dependencies onto the java modules that represent the java libraries that are provided by
|
||||||
|
// and exported from this prebuilt apex.
|
||||||
|
for _, lib := range p.properties.Exported_java_libs {
|
||||||
|
dep := prebuiltApexExportedModuleName(ctx, lib)
|
||||||
|
ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), javaLibTag, dep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ApexInfoMutator = (*Prebuilt)(nil)
|
||||||
|
|
||||||
|
// ApexInfoMutator marks any modules for which this apex exports a file as requiring an apex
|
||||||
|
// specific variant and checks that they are supported.
|
||||||
|
//
|
||||||
|
// The apexMutator will ensure that the ApexInfo objects passed to BuildForApex(ApexInfo) are
|
||||||
|
// associated with the apex specific variant using the ApexInfoProvider for later retrieval.
|
||||||
|
//
|
||||||
|
// Unlike the source apex module type the prebuilt_apex module type cannot share compatible variants
|
||||||
|
// across prebuilt_apex modules. That is because there is no way to determine whether two
|
||||||
|
// prebuilt_apex modules that export files for the same module are compatible. e.g. they could have
|
||||||
|
// been built from different source at different times or they could have been built with different
|
||||||
|
// build options that affect the libraries.
|
||||||
|
//
|
||||||
|
// While it may be possible to provide sufficient information to determine whether two prebuilt_apex
|
||||||
|
// modules were compatible it would be a lot of work and would not provide much benefit for a couple
|
||||||
|
// of reasons:
|
||||||
|
// * The number of prebuilt_apex modules that will be exporting files for the same module will be
|
||||||
|
// low as the prebuilt_apex only exports files for the direct dependencies that require it and
|
||||||
|
// very few modules are direct dependencies of multiple prebuilt_apex modules, e.g. there are a
|
||||||
|
// few com.android.art* apex files that contain the same contents and could export files for the
|
||||||
|
// same modules but only one of them needs to do so. Contrast that with source apex modules which
|
||||||
|
// need apex specific variants for every module that contributes code to the apex, whether direct
|
||||||
|
// or indirect.
|
||||||
|
// * The build cost of a prebuilt_apex variant is generally low as at worst it will involve some
|
||||||
|
// extra copying of files. Contrast that with source apex modules that has to build each variant
|
||||||
|
// from source.
|
||||||
|
func (p *Prebuilt) ApexInfoMutator(mctx android.TopDownMutatorContext) {
|
||||||
|
|
||||||
|
// Collect direct dependencies into contents.
|
||||||
|
contents := make(map[string]android.ApexMembership)
|
||||||
|
|
||||||
|
// Collect the list of dependencies.
|
||||||
|
var dependencies []android.ApexModule
|
||||||
|
mctx.VisitDirectDeps(func(m android.Module) {
|
||||||
|
tag := mctx.OtherModuleDependencyTag(m)
|
||||||
|
if tag == javaLibTag {
|
||||||
|
depName := mctx.OtherModuleName(m)
|
||||||
|
|
||||||
|
// It is an error if the other module is not a prebuilt.
|
||||||
|
if _, ok := m.(android.PrebuiltInterface); !ok {
|
||||||
|
mctx.PropertyErrorf("exported_java_libs", "%q is not a prebuilt module", depName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// It is an error if the other module is not an ApexModule.
|
||||||
|
if _, ok := m.(android.ApexModule); !ok {
|
||||||
|
mctx.PropertyErrorf("exported_java_libs", "%q is not usable within an apex", depName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip off the prebuilt_ prefix if present before storing content to ensure consistent
|
||||||
|
// behavior whether there is a corresponding source module present or not.
|
||||||
|
depName = android.RemoveOptionalPrebuiltPrefix(depName)
|
||||||
|
|
||||||
|
// Remember that this module was added as a direct dependency.
|
||||||
|
contents[depName] = contents[depName].Add(true)
|
||||||
|
|
||||||
|
// Add the module to the list of dependencies that need to have an APEX variant.
|
||||||
|
dependencies = append(dependencies, m.(android.ApexModule))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create contents for the prebuilt_apex and store it away for later use.
|
||||||
|
apexContents := android.NewApexContents(contents)
|
||||||
|
mctx.SetProvider(ApexBundleInfoProvider, ApexBundleInfo{
|
||||||
|
Contents: apexContents,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create an ApexInfo for the prebuilt_apex.
|
||||||
|
apexInfo := android.ApexInfo{
|
||||||
|
ApexVariationName: mctx.ModuleName(),
|
||||||
|
InApexes: []string{mctx.ModuleName()},
|
||||||
|
ApexContents: []*android.ApexContents{apexContents},
|
||||||
|
ForPrebuiltApex: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the dependencies of this module as requiring a variant for this module.
|
||||||
|
for _, am := range dependencies {
|
||||||
|
am.BuildForApex(apexInfo)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||||
|
|
|
@ -422,16 +422,19 @@ func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) {
|
||||||
// Note that the same jar may occur in multiple modules.
|
// Note that the same jar may occur in multiple modules.
|
||||||
// This logic is tested in the apex package to avoid import cycle apex <-> java.
|
// This logic is tested in the apex package to avoid import cycle apex <-> java.
|
||||||
func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, module android.Module) (int, android.Path) {
|
func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, module android.Module) (int, android.Path) {
|
||||||
// Ignore any module that is not listed in the boot image configuration.
|
|
||||||
name := ctx.ModuleName(module)
|
name := ctx.ModuleName(module)
|
||||||
|
|
||||||
|
// Strip a prebuilt_ prefix so that this can access the dex jar from a prebuilt module.
|
||||||
|
name = android.RemoveOptionalPrebuiltPrefix(name)
|
||||||
|
|
||||||
|
// Ignore any module that is not listed in the boot image configuration.
|
||||||
index := image.modules.IndexOfJar(name)
|
index := image.modules.IndexOfJar(name)
|
||||||
if index == -1 {
|
if index == -1 {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// It is an error if a module configured in the boot image does not support
|
// It is an error if a module configured in the boot image does not support accessing the dex jar.
|
||||||
// accessing the dex jar. This is safe because every module that has the same
|
// This is safe because every module that has the same name has to have the same module type.
|
||||||
// name has to have the same module type.
|
|
||||||
jar, hasJar := module.(interface{ DexJarBuildPath() android.Path })
|
jar, hasJar := module.(interface{ DexJarBuildPath() android.Path })
|
||||||
if !hasJar {
|
if !hasJar {
|
||||||
ctx.Errorf("module %q configured in boot image %q does not support accessing dex jar", module, image.name)
|
ctx.Errorf("module %q configured in boot image %q does not support accessing dex jar", module, image.name)
|
||||||
|
|
91
java/java.go
91
java/java.go
|
@ -2859,6 +2859,7 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||||
j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
|
j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
|
||||||
|
|
||||||
var flags javaBuilderFlags
|
var flags javaBuilderFlags
|
||||||
|
var deapexerModule android.Module
|
||||||
|
|
||||||
ctx.VisitDirectDeps(func(module android.Module) {
|
ctx.VisitDirectDeps(func(module android.Module) {
|
||||||
tag := ctx.OtherModuleDependencyTag(module)
|
tag := ctx.OtherModuleDependencyTag(module)
|
||||||
|
@ -2879,6 +2880,11 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
addCLCFromDep(ctx, module, j.classLoaderContexts)
|
addCLCFromDep(ctx, module, j.classLoaderContexts)
|
||||||
|
|
||||||
|
// Save away the `deapexer` module on which this depends, if any.
|
||||||
|
if tag == android.DeapexerTag {
|
||||||
|
deapexerModule = module
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if Bool(j.properties.Installable) {
|
if Bool(j.properties.Installable) {
|
||||||
|
@ -2888,39 +2894,60 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||||
|
|
||||||
j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
|
j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
|
||||||
|
|
||||||
if ctx.Device() && Bool(j.dexProperties.Compile_dex) {
|
if ctx.Device() {
|
||||||
sdkDep := decodeSdkDep(ctx, sdkContext(j))
|
// If this is a variant created for a prebuilt_apex then use the dex implementation jar
|
||||||
if sdkDep.invalidVersion {
|
// obtained from the associated deapexer module.
|
||||||
ctx.AddMissingDependencies(sdkDep.bootclasspath)
|
ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
|
||||||
ctx.AddMissingDependencies(sdkDep.java9Classpath)
|
if ai.ForPrebuiltApex {
|
||||||
} else if sdkDep.useFiles {
|
if deapexerModule == nil {
|
||||||
// sdkDep.jar is actually equivalent to turbine header.jar.
|
// This should never happen as a variant for a prebuilt_apex is only created if the
|
||||||
flags.classpath = append(flags.classpath, sdkDep.jars...)
|
// deapxer module has been configured to export the dex implementation jar for this module.
|
||||||
|
ctx.ModuleErrorf("internal error: module %q does not depend on a `deapexer` module for prebuilt_apex %q",
|
||||||
|
j.Name(), ai.ApexVariationName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the path of the dex implementation jar from the `deapexer` module.
|
||||||
|
di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
|
||||||
|
j.dexJarFile = di.PrebuiltExportPath(j.BaseModuleName(), ".dexjar")
|
||||||
|
if j.dexJarFile == nil {
|
||||||
|
// This should never happen as a variant for a prebuilt_apex is only created if the
|
||||||
|
// prebuilt_apex has been configured to export the java library dex file.
|
||||||
|
ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt_apex %q", deapexerModule.Name())
|
||||||
|
}
|
||||||
|
} else if Bool(j.dexProperties.Compile_dex) {
|
||||||
|
sdkDep := decodeSdkDep(ctx, sdkContext(j))
|
||||||
|
if sdkDep.invalidVersion {
|
||||||
|
ctx.AddMissingDependencies(sdkDep.bootclasspath)
|
||||||
|
ctx.AddMissingDependencies(sdkDep.java9Classpath)
|
||||||
|
} else if sdkDep.useFiles {
|
||||||
|
// sdkDep.jar is actually equivalent to turbine header.jar.
|
||||||
|
flags.classpath = append(flags.classpath, sdkDep.jars...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dex compilation
|
||||||
|
|
||||||
|
j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", jarName)
|
||||||
|
if j.dexProperties.Uncompress_dex == nil {
|
||||||
|
// If the value was not force-set by the user, use reasonable default based on the module.
|
||||||
|
j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
|
||||||
|
}
|
||||||
|
j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
|
||||||
|
|
||||||
|
var dexOutputFile android.ModuleOutPath
|
||||||
|
dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
|
||||||
|
if ctx.Failed() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
configurationName := j.BaseModuleName()
|
||||||
|
primary := j.Prebuilt().UsePrebuilt()
|
||||||
|
|
||||||
|
// Hidden API CSV generation and dex encoding
|
||||||
|
dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, outputFile,
|
||||||
|
proptools.Bool(j.dexProperties.Uncompress_dex))
|
||||||
|
|
||||||
|
j.dexJarFile = dexOutputFile
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dex compilation
|
|
||||||
|
|
||||||
j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", jarName)
|
|
||||||
if j.dexProperties.Uncompress_dex == nil {
|
|
||||||
// If the value was not force-set by the user, use reasonable default based on the module.
|
|
||||||
j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
|
|
||||||
}
|
|
||||||
j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
|
|
||||||
|
|
||||||
var dexOutputFile android.ModuleOutPath
|
|
||||||
dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
|
|
||||||
if ctx.Failed() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
configurationName := j.BaseModuleName()
|
|
||||||
primary := j.Prebuilt().UsePrebuilt()
|
|
||||||
|
|
||||||
// Hidden API CSV generation and dex encoding
|
|
||||||
dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, outputFile,
|
|
||||||
proptools.Bool(j.dexProperties.Uncompress_dex))
|
|
||||||
|
|
||||||
j.dexJarFile = dexOutputFile
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
59
scripts/unpack-prebuilt-apex.sh
Executable file
59
scripts/unpack-prebuilt-apex.sh
Executable file
|
@ -0,0 +1,59 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
# Copyright 2020 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.
|
||||||
|
|
||||||
|
# Tool to unpack an apex file and verify that the required files were extracted.
|
||||||
|
if [ $# -lt 5 ]; then
|
||||||
|
echo "usage: $0 <deapaxer_path> <debugfs_path> <apex file> <output_dir> <required_files>+" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEAPEXER_PATH=$1
|
||||||
|
DEBUGFS_PATH=$2
|
||||||
|
APEX_FILE=$3
|
||||||
|
OUTPUT_DIR=$4
|
||||||
|
shift 4
|
||||||
|
REQUIRED_PATHS=$@
|
||||||
|
|
||||||
|
set -x 1
|
||||||
|
|
||||||
|
rm -fr $OUTPUT_DIR
|
||||||
|
mkdir -p $OUTPUT_DIR
|
||||||
|
|
||||||
|
# Unpack the apex file contents.
|
||||||
|
$DEAPEXER_PATH --debugfs_path $DEBUGFS_PATH extract $APEX_FILE $OUTPUT_DIR
|
||||||
|
|
||||||
|
# Verify that the files that the build expects to be in the .apex file actually
|
||||||
|
# exist, and make sure they have a fresh mtime to not confuse ninja.
|
||||||
|
typeset -i FAILED=0
|
||||||
|
for r in $REQUIRED_PATHS; do
|
||||||
|
if [ ! -f $r ]; then
|
||||||
|
echo "Required file $r not present in apex $APEX_FILE" >&2
|
||||||
|
FAILED=$FAILED+1
|
||||||
|
else
|
||||||
|
# TODO(http:/b/177646343) - deapexer extracts the files with a timestamp of 1 Jan 1970.
|
||||||
|
# touch the file so that ninja knows it has changed.
|
||||||
|
touch $r
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $FAILED -gt 0 ]; then
|
||||||
|
echo "$FAILED required files were missing from $APEX_FILE" >&2
|
||||||
|
echo "Available files are:" >&2
|
||||||
|
find $OUTPUT_DIR -type f | sed "s|^| |" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
Loading…
Reference in a new issue