123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- /*
- * 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
- }
|