230 lines
9.3 KiB
Go
230 lines
9.3 KiB
Go
|
/*
|
||
|
* 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
|
||
|
}
|