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",
|
||||
"config.go",
|
||||
"csuite_config.go",
|
||||
"deapexer.go",
|
||||
"defaults.go",
|
||||
"defs.go",
|
||||
"depset_generic.go",
|
||||
|
|
|
@ -64,6 +64,14 @@ type ApexInfo struct {
|
|||
// 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.
|
||||
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")
|
||||
|
@ -412,6 +420,16 @@ func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexIn
|
|||
sort.Sort(byApexName(apexInfos))
|
||||
seen := make(map[string]int)
|
||||
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
|
||||
mergedName := apexInfo.mergedName(ctx)
|
||||
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
|
||||
// apexBundle they are part of.
|
||||
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
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return &ApexContents{
|
||||
contents: contents,
|
||||
|
|
|
@ -20,6 +20,10 @@ import (
|
|||
)
|
||||
|
||||
func Test_mergeApexVariations(t *testing.T) {
|
||||
const (
|
||||
ForPrebuiltApex = true
|
||||
NotForPrebuiltApex = false
|
||||
)
|
||||
tests := []struct {
|
||||
name string
|
||||
in []ApexInfo
|
||||
|
@ -29,10 +33,10 @@ func Test_mergeApexVariations(t *testing.T) {
|
|||
{
|
||||
name: "single",
|
||||
in: []ApexInfo{
|
||||
{"foo", "current", false, nil, []string{"foo"}, nil},
|
||||
{"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||
},
|
||||
wantMerged: []ApexInfo{
|
||||
{"apex10000", "current", false, nil, []string{"foo"}, nil},
|
||||
{"apex10000", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||
},
|
||||
wantAliases: [][2]string{
|
||||
{"foo", "apex10000"},
|
||||
|
@ -41,11 +45,11 @@ func Test_mergeApexVariations(t *testing.T) {
|
|||
{
|
||||
name: "merge",
|
||||
in: []ApexInfo{
|
||||
{"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil},
|
||||
{"bar", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar"}, nil},
|
||||
{"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||
{"bar", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar"}, nil, NotForPrebuiltApex},
|
||||
},
|
||||
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{
|
||||
{"bar", "apex10000_baz_1"},
|
||||
{"foo", "apex10000_baz_1"},
|
||||
|
@ -54,12 +58,12 @@ func Test_mergeApexVariations(t *testing.T) {
|
|||
{
|
||||
name: "don't merge version",
|
||||
in: []ApexInfo{
|
||||
{"foo", "current", false, nil, []string{"foo"}, nil},
|
||||
{"bar", "30", false, nil, []string{"bar"}, nil},
|
||||
{"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||
{"bar", "30", false, nil, []string{"bar"}, nil, NotForPrebuiltApex},
|
||||
},
|
||||
wantMerged: []ApexInfo{
|
||||
{"apex30", "30", false, nil, []string{"bar"}, nil},
|
||||
{"apex10000", "current", false, nil, []string{"foo"}, nil},
|
||||
{"apex30", "30", false, nil, []string{"bar"}, nil, NotForPrebuiltApex},
|
||||
{"apex10000", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||
},
|
||||
wantAliases: [][2]string{
|
||||
{"bar", "apex30"},
|
||||
|
@ -69,11 +73,11 @@ func Test_mergeApexVariations(t *testing.T) {
|
|||
{
|
||||
name: "merge updatable",
|
||||
in: []ApexInfo{
|
||||
{"foo", "current", false, nil, []string{"foo"}, nil},
|
||||
{"bar", "current", true, nil, []string{"bar"}, nil},
|
||||
{"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||
{"bar", "current", true, nil, []string{"bar"}, nil, NotForPrebuiltApex},
|
||||
},
|
||||
wantMerged: []ApexInfo{
|
||||
{"apex10000", "current", true, nil, []string{"bar", "foo"}, nil},
|
||||
{"apex10000", "current", true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
|
||||
},
|
||||
wantAliases: [][2]string{
|
||||
{"bar", "apex10000"},
|
||||
|
@ -83,19 +87,38 @@ func Test_mergeApexVariations(t *testing.T) {
|
|||
{
|
||||
name: "don't merge sdks",
|
||||
in: []ApexInfo{
|
||||
{"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil},
|
||||
{"bar", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil},
|
||||
{"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||
{"bar", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex},
|
||||
},
|
||||
wantMerged: []ApexInfo{
|
||||
{"apex10000_baz_2", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil},
|
||||
{"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil},
|
||||
{"apex10000_baz_2", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex},
|
||||
{"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
|
||||
},
|
||||
wantAliases: [][2]string{
|
||||
{"bar", "apex10000_baz_2"},
|
||||
{"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 {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
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.
|
||||
func NormalizePathForTesting(path Path) string {
|
||||
if path == nil {
|
||||
return "<nil path>"
|
||||
}
|
||||
p := path.String()
|
||||
// Allow absolute paths to /dev/
|
||||
if strings.HasPrefix(p, "/dev/") {
|
||||
|
|
|
@ -18,6 +18,7 @@ bootstrap_go_package {
|
|||
"apex.go",
|
||||
"apex_singleton.go",
|
||||
"builder.go",
|
||||
"deapexer.go",
|
||||
"key.go",
|
||||
"prebuilt.go",
|
||||
"vndk.go",
|
||||
|
|
|
@ -1501,7 +1501,7 @@ func apexFileForFilesystem(ctx android.BaseModuleContext, buildFile android.Path
|
|||
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
|
||||
// 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.
|
||||
|
|
|
@ -190,6 +190,7 @@ func testApexContext(_ *testing.T, bp string, handlers ...testCustomizer) (*andr
|
|||
"testdata/baz": nil,
|
||||
"AppSet.apks": nil,
|
||||
"foo.rs": nil,
|
||||
"libfoo.jar": nil,
|
||||
}
|
||||
|
||||
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) {
|
||||
ctx, config := testApex(t, `
|
||||
apex_test {
|
||||
|
@ -5783,7 +5899,7 @@ func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, transformDexpre
|
|||
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()
|
||||
|
||||
bp += cc.GatherRequiredDepsForTest(android.Android)
|
||||
|
@ -5808,6 +5924,7 @@ func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreopt
|
|||
ctx := android.NewTestArchContext(config)
|
||||
ctx.RegisterModuleType("apex", BundleFactory)
|
||||
ctx.RegisterModuleType("apex_key", ApexKeyFactory)
|
||||
ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
|
||||
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
|
||||
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
|
||||
android.RegisterPrebuiltMutators(ctx)
|
||||
|
@ -5837,10 +5954,11 @@ func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreopt
|
|||
android.FailIfErrored(t, errs)
|
||||
} else if len(errs) > 0 {
|
||||
android.FailIfNoMatchingErrors(t, errmsg, errs)
|
||||
return
|
||||
} else {
|
||||
t.Fatalf("missing expected error %q (0 errors are returned)", errmsg)
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
|
||||
|
@ -5939,6 +6057,56 @@ func TestNoUpdatableJarsInBootImage(t *testing.T) {
|
|||
}
|
||||
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) {
|
||||
|
|
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 {
|
||||
ApexFileProperties
|
||||
DeapexerProperties
|
||||
|
||||
Installable *bool
|
||||
// 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.
|
||||
//
|
||||
// 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 {
|
||||
module := &Prebuilt{}
|
||||
module.AddProperties(&module.properties)
|
||||
android.InitSingleSourcePrebuiltModule(module, &module.properties, "Source")
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
if err := p.properties.selectSource(ctx); err != nil {
|
||||
ctx.ModuleErrorf("%s", err)
|
||||
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) {
|
||||
|
|
|
@ -422,16 +422,19 @@ func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) {
|
|||
// Note that the same jar may occur in multiple modules.
|
||||
// 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) {
|
||||
// Ignore any module that is not listed in the boot image configuration.
|
||||
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)
|
||||
if index == -1 {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
// It is an error if a module configured in the boot image does not support
|
||||
// accessing the dex jar. This is safe because every module that has the same
|
||||
// name has to have the same module type.
|
||||
// It is an error if a module configured in the boot image does not support accessing the dex jar.
|
||||
// This is safe because every module that has the same name has to have the same module type.
|
||||
jar, hasJar := module.(interface{ DexJarBuildPath() android.Path })
|
||||
if !hasJar {
|
||||
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)
|
||||
|
||||
var flags javaBuilderFlags
|
||||
var deapexerModule android.Module
|
||||
|
||||
ctx.VisitDirectDeps(func(module android.Module) {
|
||||
tag := ctx.OtherModuleDependencyTag(module)
|
||||
|
@ -2879,6 +2880,11 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
@ -2888,39 +2894,60 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
|||
|
||||
j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
|
||||
|
||||
if ctx.Device() && 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...)
|
||||
if ctx.Device() {
|
||||
// If this is a variant created for a prebuilt_apex then use the dex implementation jar
|
||||
// obtained from the associated deapexer module.
|
||||
ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
|
||||
if ai.ForPrebuiltApex {
|
||||
if deapexerModule == nil {
|
||||
// This should never happen as a variant for a prebuilt_apex is only created if the
|
||||
// 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