Add ClasspathElement support
The configuration of the bootclasspath, e.g. PRODUCT_BOOT_JARS,
includes libraries that are standalone and libraries that are part of
an APEX. All libraries that are part of an APEX must also be part of a
bootclasspath_fragment. Currently, the libraries and fragments are
handled separately but that is limiting as it does not make it easy to
process all the libraries while treating those from fragments
differently to standalone libraries. That is needed for hidden API
processing as it needs to use classesJars from standalone libraries
but pre-generated flag files from fragments.
This change adds support for ClasspathElements which is represents the
classpath as a whole while differentiating between standalone libraries
and fragments.
Bug: 177892522
Test: m nothing
Merged-In: I7a4adc67164a46079eb8ec0a17fc755044b4949d
Change-Id: I7a4adc67164a46079eb8ec0a17fc755044b4949d
(cherry picked from commit e245b61aa2
)
This commit is contained in:
parent
9dc8c54fc4
commit
f23512fa58
4 changed files with 548 additions and 0 deletions
|
@ -31,6 +31,7 @@ bootstrap_go_package {
|
||||||
testSrcs: [
|
testSrcs: [
|
||||||
"apex_test.go",
|
"apex_test.go",
|
||||||
"bootclasspath_fragment_test.go",
|
"bootclasspath_fragment_test.go",
|
||||||
|
"classpath_element_test.go",
|
||||||
"platform_bootclasspath_test.go",
|
"platform_bootclasspath_test.go",
|
||||||
"systemserver_classpath_fragment_test.go",
|
"systemserver_classpath_fragment_test.go",
|
||||||
"vndk_test.go",
|
"vndk_test.go",
|
||||||
|
|
317
apex/classpath_element_test.go
Normal file
317
apex/classpath_element_test.go
Normal file
|
@ -0,0 +1,317 @@
|
||||||
|
// 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 (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"android/soong/android"
|
||||||
|
"android/soong/java"
|
||||||
|
"github.com/google/blueprint"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Contains tests for java.CreateClasspathElements logic from java/classpath_element.go that
|
||||||
|
// requires apexes.
|
||||||
|
|
||||||
|
// testClasspathElementContext is a ClasspathElementContext suitable for use in tests.
|
||||||
|
type testClasspathElementContext struct {
|
||||||
|
testContext *android.TestContext
|
||||||
|
module android.Module
|
||||||
|
errs []error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testClasspathElementContext) OtherModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool {
|
||||||
|
return t.testContext.ModuleHasProvider(module, provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testClasspathElementContext) OtherModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{} {
|
||||||
|
return t.testContext.ModuleProvider(module, provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testClasspathElementContext) ModuleErrorf(fmt string, args ...interface{}) {
|
||||||
|
t.errs = append(t.errs, t.testContext.ModuleErrorf(t.module, fmt, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ java.ClasspathElementContext = (*testClasspathElementContext)(nil)
|
||||||
|
|
||||||
|
func TestCreateClasspathElements(t *testing.T) {
|
||||||
|
preparer := android.GroupFixturePreparers(
|
||||||
|
prepareForTestWithPlatformBootclasspath,
|
||||||
|
prepareForTestWithArtApex,
|
||||||
|
prepareForTestWithMyapex,
|
||||||
|
// For otherapex.
|
||||||
|
android.FixtureMergeMockFs(android.MockFS{
|
||||||
|
"system/sepolicy/apex/otherapex-file_contexts": nil,
|
||||||
|
}),
|
||||||
|
java.PrepareForTestWithJavaSdkLibraryFiles,
|
||||||
|
java.FixtureWithLastReleaseApis("foo", "othersdklibrary"),
|
||||||
|
android.FixtureWithRootAndroidBp(`
|
||||||
|
apex {
|
||||||
|
name: "com.android.art",
|
||||||
|
key: "com.android.art.key",
|
||||||
|
bootclasspath_fragments: [
|
||||||
|
"art-bootclasspath-fragment",
|
||||||
|
],
|
||||||
|
java_libs: [
|
||||||
|
"othersdklibrary",
|
||||||
|
],
|
||||||
|
updatable: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
apex_key {
|
||||||
|
name: "com.android.art.key",
|
||||||
|
public_key: "com.android.art.avbpubkey",
|
||||||
|
private_key: "com.android.art.pem",
|
||||||
|
}
|
||||||
|
|
||||||
|
bootclasspath_fragment {
|
||||||
|
name: "art-bootclasspath-fragment",
|
||||||
|
apex_available: [
|
||||||
|
"com.android.art",
|
||||||
|
],
|
||||||
|
contents: [
|
||||||
|
"baz",
|
||||||
|
"quuz",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
java_library {
|
||||||
|
name: "baz",
|
||||||
|
apex_available: [
|
||||||
|
"com.android.art",
|
||||||
|
],
|
||||||
|
srcs: ["b.java"],
|
||||||
|
installable: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
java_library {
|
||||||
|
name: "quuz",
|
||||||
|
apex_available: [
|
||||||
|
"com.android.art",
|
||||||
|
],
|
||||||
|
srcs: ["b.java"],
|
||||||
|
installable: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
apex {
|
||||||
|
name: "myapex",
|
||||||
|
key: "myapex.key",
|
||||||
|
bootclasspath_fragments: [
|
||||||
|
"mybootclasspath-fragment",
|
||||||
|
],
|
||||||
|
java_libs: [
|
||||||
|
"othersdklibrary",
|
||||||
|
],
|
||||||
|
updatable: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
apex_key {
|
||||||
|
name: "myapex.key",
|
||||||
|
public_key: "testkey.avbpubkey",
|
||||||
|
private_key: "testkey.pem",
|
||||||
|
}
|
||||||
|
|
||||||
|
bootclasspath_fragment {
|
||||||
|
name: "mybootclasspath-fragment",
|
||||||
|
apex_available: [
|
||||||
|
"myapex",
|
||||||
|
],
|
||||||
|
contents: [
|
||||||
|
"bar",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
java_library {
|
||||||
|
name: "bar",
|
||||||
|
srcs: ["b.java"],
|
||||||
|
installable: true,
|
||||||
|
apex_available: ["myapex"],
|
||||||
|
permitted_packages: ["bar"],
|
||||||
|
}
|
||||||
|
|
||||||
|
java_sdk_library {
|
||||||
|
name: "foo",
|
||||||
|
srcs: ["b.java"],
|
||||||
|
}
|
||||||
|
|
||||||
|
java_sdk_library {
|
||||||
|
name: "othersdklibrary",
|
||||||
|
srcs: ["b.java"],
|
||||||
|
shared_library: false,
|
||||||
|
apex_available: [
|
||||||
|
"com.android.art",
|
||||||
|
"myapex",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
bootclasspath_fragment {
|
||||||
|
name: "non-apex-fragment",
|
||||||
|
contents: ["othersdklibrary"],
|
||||||
|
}
|
||||||
|
|
||||||
|
apex {
|
||||||
|
name: "otherapex",
|
||||||
|
key: "otherapex.key",
|
||||||
|
java_libs: [
|
||||||
|
"otherapexlibrary",
|
||||||
|
],
|
||||||
|
updatable: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
apex_key {
|
||||||
|
name: "otherapex.key",
|
||||||
|
public_key: "testkey.avbpubkey",
|
||||||
|
private_key: "testkey.pem",
|
||||||
|
}
|
||||||
|
|
||||||
|
java_library {
|
||||||
|
name: "otherapexlibrary",
|
||||||
|
srcs: ["b.java"],
|
||||||
|
installable: true,
|
||||||
|
apex_available: ["otherapex"],
|
||||||
|
permitted_packages: ["otherapexlibrary"],
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_bootclasspath {
|
||||||
|
name: "myplatform-bootclasspath",
|
||||||
|
|
||||||
|
fragments: [
|
||||||
|
{
|
||||||
|
apex: "com.android.art",
|
||||||
|
module: "art-bootclasspath-fragment",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`),
|
||||||
|
)
|
||||||
|
|
||||||
|
result := preparer.RunTest(t)
|
||||||
|
|
||||||
|
artFragment := result.Module("art-bootclasspath-fragment", "android_common_apex10000")
|
||||||
|
artBaz := result.Module("baz", "android_common_apex10000")
|
||||||
|
artQuuz := result.Module("quuz", "android_common_apex10000")
|
||||||
|
|
||||||
|
myFragment := result.Module("mybootclasspath-fragment", "android_common_apex10000")
|
||||||
|
myBar := result.Module("bar", "android_common_apex10000")
|
||||||
|
|
||||||
|
nonApexFragment := result.Module("non-apex-fragment", "android_common")
|
||||||
|
other := result.Module("othersdklibrary", "android_common_apex10000")
|
||||||
|
|
||||||
|
otherApexLibrary := result.Module("otherapexlibrary", "android_common_apex10000")
|
||||||
|
|
||||||
|
platformFoo := result.Module("quuz", "android_common")
|
||||||
|
|
||||||
|
bootclasspath := result.Module("myplatform-bootclasspath", "android_common")
|
||||||
|
|
||||||
|
// Use a custom assertion method instead of AssertDeepEquals as the latter formats the output
|
||||||
|
// using %#v which results in meaningless output as ClasspathElements are pointers.
|
||||||
|
assertElementsEquals := func(t *testing.T, message string, expected, actual java.ClasspathElements) {
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Errorf("%s: expected:\n %s\n got:\n %s", message, expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expectFragmentElement := func(module android.Module, contents ...android.Module) java.ClasspathElement {
|
||||||
|
return &java.ClasspathFragmentElement{module, contents}
|
||||||
|
}
|
||||||
|
expectLibraryElement := func(module android.Module) java.ClasspathElement {
|
||||||
|
return &java.ClasspathLibraryElement{module}
|
||||||
|
}
|
||||||
|
|
||||||
|
newCtx := func() *testClasspathElementContext {
|
||||||
|
return &testClasspathElementContext{testContext: result.TestContext, module: bootclasspath}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that CreateClasspathElements works when given valid input.
|
||||||
|
t.Run("art:baz, art:quuz, my:bar, foo", func(t *testing.T) {
|
||||||
|
ctx := newCtx()
|
||||||
|
elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, artQuuz, myBar, platformFoo}, []android.Module{artFragment, myFragment})
|
||||||
|
expectedElements := java.ClasspathElements{
|
||||||
|
expectFragmentElement(artFragment, artBaz, artQuuz),
|
||||||
|
expectFragmentElement(myFragment, myBar),
|
||||||
|
expectLibraryElement(platformFoo),
|
||||||
|
}
|
||||||
|
assertElementsEquals(t, "elements", expectedElements, elements)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Verify that CreateClasspathElements detects when a fragment does not have an associated apex.
|
||||||
|
t.Run("non apex fragment", func(t *testing.T) {
|
||||||
|
ctx := newCtx()
|
||||||
|
elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{nonApexFragment})
|
||||||
|
android.FailIfNoMatchingErrors(t, "fragment non-apex-fragment{.*} is not part of an apex", ctx.errs)
|
||||||
|
expectedElements := java.ClasspathElements{}
|
||||||
|
assertElementsEquals(t, "elements", expectedElements, elements)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Verify that CreateClasspathElements detects when an apex has multiple fragments.
|
||||||
|
t.Run("multiple fragments for same apex", func(t *testing.T) {
|
||||||
|
ctx := newCtx()
|
||||||
|
elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{artFragment, artFragment})
|
||||||
|
android.FailIfNoMatchingErrors(t, "apex com.android.art has multiple fragments, art-bootclasspath-fragment{.*} and art-bootclasspath-fragment{.*}", ctx.errs)
|
||||||
|
expectedElements := java.ClasspathElements{}
|
||||||
|
assertElementsEquals(t, "elements", expectedElements, elements)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Verify that CreateClasspathElements detects when a library is in multiple fragments.
|
||||||
|
t.Run("library from multiple fragments", func(t *testing.T) {
|
||||||
|
ctx := newCtx()
|
||||||
|
elements := java.CreateClasspathElements(ctx, []android.Module{other}, []android.Module{artFragment, myFragment})
|
||||||
|
android.FailIfNoMatchingErrors(t, "library othersdklibrary{.*} is in two separate fragments, art-bootclasspath-fragment{.*} and mybootclasspath-fragment{.*}", ctx.errs)
|
||||||
|
expectedElements := java.ClasspathElements{}
|
||||||
|
assertElementsEquals(t, "elements", expectedElements, elements)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and
|
||||||
|
// are separated by a library from another fragment.
|
||||||
|
t.Run("discontiguous separated by fragment", func(t *testing.T) {
|
||||||
|
ctx := newCtx()
|
||||||
|
elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, myBar, artQuuz, platformFoo}, []android.Module{artFragment, myFragment})
|
||||||
|
expectedElements := java.ClasspathElements{
|
||||||
|
expectFragmentElement(artFragment, artBaz, artQuuz),
|
||||||
|
expectFragmentElement(myFragment, myBar),
|
||||||
|
expectLibraryElement(platformFoo),
|
||||||
|
}
|
||||||
|
assertElementsEquals(t, "elements", expectedElements, elements)
|
||||||
|
android.FailIfNoMatchingErrors(t, "libraries from the same fragment must be contiguous, however baz{.*} and quuz{os:android,arch:common,apex:apex10000} from fragment art-bootclasspath-fragment{.*} are separated by libraries from fragment mybootclasspath-fragment{.*} like bar{.*}", ctx.errs)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and
|
||||||
|
// are separated by a standalone library.
|
||||||
|
t.Run("discontiguous separated by library", func(t *testing.T) {
|
||||||
|
ctx := newCtx()
|
||||||
|
elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, platformFoo, artQuuz, myBar}, []android.Module{artFragment, myFragment})
|
||||||
|
expectedElements := java.ClasspathElements{
|
||||||
|
expectFragmentElement(artFragment, artBaz, artQuuz),
|
||||||
|
expectLibraryElement(platformFoo),
|
||||||
|
expectFragmentElement(myFragment, myBar),
|
||||||
|
}
|
||||||
|
assertElementsEquals(t, "elements", expectedElements, elements)
|
||||||
|
android.FailIfNoMatchingErrors(t, "libraries from the same fragment must be contiguous, however baz{.*} and quuz{os:android,arch:common,apex:apex10000} from fragment art-bootclasspath-fragment{.*} are separated by library quuz{.*}", ctx.errs)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Verify that CreateClasspathElements detects when there a library on the classpath that
|
||||||
|
// indicates it is from an apex the supplied fragments list does not contain a fragment for that
|
||||||
|
// apex.
|
||||||
|
t.Run("no fragment for apex", func(t *testing.T) {
|
||||||
|
ctx := newCtx()
|
||||||
|
elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, otherApexLibrary}, []android.Module{artFragment})
|
||||||
|
expectedElements := java.ClasspathElements{
|
||||||
|
expectFragmentElement(artFragment, artBaz),
|
||||||
|
}
|
||||||
|
assertElementsEquals(t, "elements", expectedElements, elements)
|
||||||
|
android.FailIfNoMatchingErrors(t, `library otherapexlibrary{.*} is from apexes \[otherapex\] which have no corresponding fragment in \[art-bootclasspath-fragment{.*}\]`, ctx.errs)
|
||||||
|
})
|
||||||
|
}
|
|
@ -33,6 +33,7 @@ bootstrap_go_package {
|
||||||
"bootclasspath.go",
|
"bootclasspath.go",
|
||||||
"bootclasspath_fragment.go",
|
"bootclasspath_fragment.go",
|
||||||
"builder.go",
|
"builder.go",
|
||||||
|
"classpath_element.go",
|
||||||
"classpath_fragment.go",
|
"classpath_fragment.go",
|
||||||
"device_host_converter.go",
|
"device_host_converter.go",
|
||||||
"dex.go",
|
"dex.go",
|
||||||
|
|
229
java/classpath_element.go
Normal file
229
java/classpath_element.go
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
/*
|
||||||
|
* 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 java
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"android/soong/android"
|
||||||
|
"github.com/google/blueprint"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Supports constructing a list of ClasspathElement from a set of fragments and modules.
|
||||||
|
|
||||||
|
// ClasspathElement represents a component that contributes to a classpath. That can be
|
||||||
|
// either a java module or a classpath fragment module.
|
||||||
|
type ClasspathElement interface {
|
||||||
|
Module() android.Module
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClasspathElements []ClasspathElement
|
||||||
|
|
||||||
|
// ClasspathFragmentElement is a ClasspathElement that encapsulates a classpath fragment module.
|
||||||
|
type ClasspathFragmentElement struct {
|
||||||
|
Fragment android.Module
|
||||||
|
Contents []android.Module
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ClasspathFragmentElement) Module() android.Module {
|
||||||
|
return b.Fragment
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ClasspathFragmentElement) String() string {
|
||||||
|
contents := []string{}
|
||||||
|
for _, module := range b.Contents {
|
||||||
|
contents = append(contents, module.String())
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("fragment(%s, %s)", b.Fragment, strings.Join(contents, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ClasspathElement = (*ClasspathFragmentElement)(nil)
|
||||||
|
|
||||||
|
// ClasspathLibraryElement is a ClasspathElement that encapsulates a java library.
|
||||||
|
type ClasspathLibraryElement struct {
|
||||||
|
Library android.Module
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ClasspathLibraryElement) Module() android.Module {
|
||||||
|
return b.Library
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ClasspathLibraryElement) String() string {
|
||||||
|
return fmt.Sprintf("library{%s}", b.Library)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ClasspathElement = (*ClasspathLibraryElement)(nil)
|
||||||
|
|
||||||
|
// ClasspathElementContext defines the context methods needed by CreateClasspathElements
|
||||||
|
type ClasspathElementContext interface {
|
||||||
|
OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool
|
||||||
|
OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{}
|
||||||
|
ModuleErrorf(fmt string, args ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateClasspathElements creates a list of ClasspathElement objects from a list of libraries and
|
||||||
|
// a list of fragments.
|
||||||
|
//
|
||||||
|
// The libraries parameter contains the set of libraries from which the classpath is constructed.
|
||||||
|
// The fragments parameter contains the classpath fragment modules whose contents are libraries that
|
||||||
|
// are part of the classpath. Each library in the libraries parameter may be part of a fragment. The
|
||||||
|
// determination as to which libraries belong to fragments and which do not is based on the apex to
|
||||||
|
// which they belong, if any.
|
||||||
|
//
|
||||||
|
// Every fragment in the fragments list must be part of one or more apexes and each apex is assumed
|
||||||
|
// to contain only a single fragment from the fragments list. A library in the libraries parameter
|
||||||
|
// that is part of an apex must be provided by a classpath fragment in the corresponding apex.
|
||||||
|
//
|
||||||
|
// This will return a ClasspathElements list that contains a ClasspathElement for each standalone
|
||||||
|
// library and each fragment. The order of the elements in the list is such that if the list was
|
||||||
|
// flattened into a list of library modules that it would result in the same list or modules as the
|
||||||
|
// input libraries. Flattening the list can be done by replacing each ClasspathFragmentElement in
|
||||||
|
// the list with its Contents field.
|
||||||
|
//
|
||||||
|
// Requirements/Assumptions:
|
||||||
|
// * A fragment can be associated with more than one apex but each apex must only be associated with
|
||||||
|
// a single fragment from the fragments list.
|
||||||
|
// * All of a fragment's contents must appear as a contiguous block in the same order in the
|
||||||
|
// libraries list.
|
||||||
|
// * Each library must only appear in a single fragment.
|
||||||
|
//
|
||||||
|
// The apex is used to identify which libraries belong to which fragment. First a mapping is created
|
||||||
|
// from apex to fragment. Then the libraries are iterated over and any library in an apex is
|
||||||
|
// associated with an element for the fragment to which it belongs. Otherwise, the libraries are
|
||||||
|
// standalone and have their own element.
|
||||||
|
//
|
||||||
|
// e.g. Given the following input:
|
||||||
|
// libraries: com.android.art:core-oj, com.android.art:core-libart, framework, ext
|
||||||
|
// fragments: com.android.art:art-bootclasspath-fragment
|
||||||
|
//
|
||||||
|
// Then this will return:
|
||||||
|
// ClasspathFragmentElement(art-bootclasspath-fragment, [core-oj, core-libart]),
|
||||||
|
// ClasspathLibraryElement(framework),
|
||||||
|
// ClasspathLibraryElement(ext),
|
||||||
|
func CreateClasspathElements(ctx ClasspathElementContext, libraries []android.Module, fragments []android.Module) ClasspathElements {
|
||||||
|
// Create a map from apex name to the fragment module. This makes it easy to find the fragment
|
||||||
|
// associated with a particular apex.
|
||||||
|
apexToFragment := map[string]android.Module{}
|
||||||
|
for _, fragment := range fragments {
|
||||||
|
if !ctx.OtherModuleHasProvider(fragment, android.ApexInfoProvider) {
|
||||||
|
ctx.ModuleErrorf("fragment %s is not part of an apex", fragment)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
apexInfo := ctx.OtherModuleProvider(fragment, android.ApexInfoProvider).(android.ApexInfo)
|
||||||
|
for _, apex := range apexInfo.InApexVariants {
|
||||||
|
if existing, ok := apexToFragment[apex]; ok {
|
||||||
|
ctx.ModuleErrorf("apex %s has multiple fragments, %s and %s", apex, fragment, existing)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
apexToFragment[apex] = fragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragmentToElement := map[android.Module]*ClasspathFragmentElement{}
|
||||||
|
elements := []ClasspathElement{}
|
||||||
|
var currentElement ClasspathElement
|
||||||
|
|
||||||
|
skipLibrary:
|
||||||
|
// Iterate over the libraries to construct the ClasspathElements list.
|
||||||
|
for _, library := range libraries {
|
||||||
|
var element ClasspathElement
|
||||||
|
if ctx.OtherModuleHasProvider(library, android.ApexInfoProvider) {
|
||||||
|
apexInfo := ctx.OtherModuleProvider(library, android.ApexInfoProvider).(android.ApexInfo)
|
||||||
|
|
||||||
|
var fragment android.Module
|
||||||
|
|
||||||
|
// Make sure that the library is in only one fragment of the classpath.
|
||||||
|
for _, apex := range apexInfo.InApexVariants {
|
||||||
|
if f, ok := apexToFragment[apex]; ok {
|
||||||
|
if fragment == nil {
|
||||||
|
// This is the first fragment so just save it away.
|
||||||
|
fragment = f
|
||||||
|
} else if f != fragment {
|
||||||
|
// This apex variant of the library is in a different fragment.
|
||||||
|
ctx.ModuleErrorf("library %s is in two separate fragments, %s and %s", library, fragment, f)
|
||||||
|
// Skip over this library entirely as otherwise the resulting classpath elements would
|
||||||
|
// be invalid.
|
||||||
|
continue skipLibrary
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// There is no fragment associated with the library's apex.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fragment == nil {
|
||||||
|
ctx.ModuleErrorf("library %s is from apexes %s which have no corresponding fragment in %s",
|
||||||
|
library, apexInfo.InApexVariants, fragments)
|
||||||
|
// Skip over this library entirely as otherwise the resulting classpath elements would
|
||||||
|
// be invalid.
|
||||||
|
continue skipLibrary
|
||||||
|
} else if existingFragmentElement, ok := fragmentToElement[fragment]; ok {
|
||||||
|
// This library is in a fragment element that has already been added.
|
||||||
|
|
||||||
|
// If the existing fragment element is still the current element then this library is
|
||||||
|
// contiguous with other libraries in that fragment so there is nothing more to do.
|
||||||
|
// Otherwise this library is not contiguous with other libraries in the same fragment which
|
||||||
|
// is an error.
|
||||||
|
if existingFragmentElement != currentElement {
|
||||||
|
separator := ""
|
||||||
|
if fragmentElement, ok := currentElement.(*ClasspathFragmentElement); ok {
|
||||||
|
separator = fmt.Sprintf("libraries from fragment %s like %s", fragmentElement.Fragment, fragmentElement.Contents[0])
|
||||||
|
} else {
|
||||||
|
libraryElement := currentElement.(*ClasspathLibraryElement)
|
||||||
|
separator = fmt.Sprintf("library %s", libraryElement.Library)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the library that precedes this library in the fragment. That is the last library as
|
||||||
|
// this library has not yet been added.
|
||||||
|
precedingLibraryInFragment := existingFragmentElement.Contents[len(existingFragmentElement.Contents)-1]
|
||||||
|
ctx.ModuleErrorf("libraries from the same fragment must be contiguous, however %s and %s from fragment %s are separated by %s",
|
||||||
|
precedingLibraryInFragment, library, fragment, separator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this library to the fragment element's contents.
|
||||||
|
existingFragmentElement.Contents = append(existingFragmentElement.Contents, library)
|
||||||
|
} else {
|
||||||
|
// This is the first library in this fragment so add a new element for the fragment,
|
||||||
|
// including the library.
|
||||||
|
fragmentElement := &ClasspathFragmentElement{
|
||||||
|
Fragment: fragment,
|
||||||
|
Contents: []android.Module{library},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store it away so we can detect when attempting to create another element for the same
|
||||||
|
// fragment.
|
||||||
|
fragmentToElement[fragment] = fragmentElement
|
||||||
|
element = fragmentElement
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The library is from the platform so just add an element for it.
|
||||||
|
element = &ClasspathLibraryElement{Library: library}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no element was created then it means that the library has been added to an existing
|
||||||
|
// fragment element so the list of elements and current element are unaffected.
|
||||||
|
if element != nil {
|
||||||
|
// Add the element to the list and make it the current element for the next iteration.
|
||||||
|
elements = append(elements, element)
|
||||||
|
currentElement = element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements
|
||||||
|
}
|
Loading…
Reference in a new issue