diff options
author | Scott Lobdell <slobdell@google.com> | 2021-07-07 17:54:01 +0000 |
---|---|---|
committer | Scott Lobdell <slobdell@google.com> | 2021-07-07 17:54:01 +0000 |
commit | 99f82c6f4a8511ded6de5e2b5e00565fad7f65de (patch) | |
tree | a1ecabe68c443bf3d596fe06417e98cc54dc7470 /java/classpath_element.go | |
parent | c57cdd121a8d5a0883a689f6d9fbe6fc440e2202 (diff) | |
parent | 5abcf9267173b9e80dd14fca037b0f01cc2dbd3b (diff) |
Merge SP1A.210624.001
Change-Id: I8507f8b65c47a5b425464bbbc8d932a6ca359fd3
Diffstat (limited to 'java/classpath_element.go')
-rw-r--r-- | java/classpath_element.go | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/java/classpath_element.go b/java/classpath_element.go new file mode 100644 index 000000000..753e7f888 --- /dev/null +++ b/java/classpath_element.go @@ -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 +} |