blob: 496291678a8eb1d126858a61bf1c3c59c1978781 [file] [log] [blame]
Paul Duffine245b612021-06-10 08:59:41 +01001/*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package java
18
19import (
20 "fmt"
21 "strings"
22
23 "android/soong/android"
24 "github.com/google/blueprint"
25)
26
27// Supports constructing a list of ClasspathElement from a set of fragments and modules.
28
29// ClasspathElement represents a component that contributes to a classpath. That can be
30// either a java module or a classpath fragment module.
31type ClasspathElement interface {
32 Module() android.Module
33 String() string
34}
35
36type ClasspathElements []ClasspathElement
37
38// ClasspathFragmentElement is a ClasspathElement that encapsulates a classpath fragment module.
39type ClasspathFragmentElement struct {
40 Fragment android.Module
41 Contents []android.Module
42}
43
44func (b *ClasspathFragmentElement) Module() android.Module {
45 return b.Fragment
46}
47
48func (b *ClasspathFragmentElement) String() string {
49 contents := []string{}
50 for _, module := range b.Contents {
51 contents = append(contents, module.String())
52 }
53 return fmt.Sprintf("fragment(%s, %s)", b.Fragment, strings.Join(contents, ", "))
54}
55
56var _ ClasspathElement = (*ClasspathFragmentElement)(nil)
57
58// ClasspathLibraryElement is a ClasspathElement that encapsulates a java library.
59type ClasspathLibraryElement struct {
60 Library android.Module
61}
62
63func (b *ClasspathLibraryElement) Module() android.Module {
64 return b.Library
65}
66
67func (b *ClasspathLibraryElement) String() string {
68 return fmt.Sprintf("library{%s}", b.Library)
69}
70
71var _ ClasspathElement = (*ClasspathLibraryElement)(nil)
72
73// ClasspathElementContext defines the context methods needed by CreateClasspathElements
74type ClasspathElementContext interface {
75 OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool
76 OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{}
77 ModuleErrorf(fmt string, args ...interface{})
78}
79
80// CreateClasspathElements creates a list of ClasspathElement objects from a list of libraries and
81// a list of fragments.
82//
83// The libraries parameter contains the set of libraries from which the classpath is constructed.
84// The fragments parameter contains the classpath fragment modules whose contents are libraries that
85// are part of the classpath. Each library in the libraries parameter may be part of a fragment. The
86// determination as to which libraries belong to fragments and which do not is based on the apex to
87// which they belong, if any.
88//
89// Every fragment in the fragments list must be part of one or more apexes and each apex is assumed
90// to contain only a single fragment from the fragments list. A library in the libraries parameter
91// that is part of an apex must be provided by a classpath fragment in the corresponding apex.
92//
93// This will return a ClasspathElements list that contains a ClasspathElement for each standalone
94// library and each fragment. The order of the elements in the list is such that if the list was
95// flattened into a list of library modules that it would result in the same list or modules as the
96// input libraries. Flattening the list can be done by replacing each ClasspathFragmentElement in
97// the list with its Contents field.
98//
99// Requirements/Assumptions:
Colin Crossd079e0b2022-08-16 10:27:33 -0700100// - A fragment can be associated with more than one apex but each apex must only be associated with
101// a single fragment from the fragments list.
102// - All of a fragment's contents must appear as a contiguous block in the same order in the
103// libraries list.
104// - Each library must only appear in a single fragment.
Paul Duffine245b612021-06-10 08:59:41 +0100105//
106// The apex is used to identify which libraries belong to which fragment. First a mapping is created
107// from apex to fragment. Then the libraries are iterated over and any library in an apex is
108// associated with an element for the fragment to which it belongs. Otherwise, the libraries are
109// standalone and have their own element.
110//
111// e.g. Given the following input:
Colin Crossd079e0b2022-08-16 10:27:33 -0700112//
113// libraries: com.android.art:core-oj, com.android.art:core-libart, framework, ext
114// fragments: com.android.art:art-bootclasspath-fragment
Paul Duffine245b612021-06-10 08:59:41 +0100115//
116// Then this will return:
Colin Crossd079e0b2022-08-16 10:27:33 -0700117//
118// ClasspathFragmentElement(art-bootclasspath-fragment, [core-oj, core-libart]),
119// ClasspathLibraryElement(framework),
120// ClasspathLibraryElement(ext),
Paul Duffine245b612021-06-10 08:59:41 +0100121func CreateClasspathElements(ctx ClasspathElementContext, libraries []android.Module, fragments []android.Module) ClasspathElements {
122 // Create a map from apex name to the fragment module. This makes it easy to find the fragment
123 // associated with a particular apex.
124 apexToFragment := map[string]android.Module{}
125 for _, fragment := range fragments {
126 if !ctx.OtherModuleHasProvider(fragment, android.ApexInfoProvider) {
127 ctx.ModuleErrorf("fragment %s is not part of an apex", fragment)
128 continue
129 }
130
131 apexInfo := ctx.OtherModuleProvider(fragment, android.ApexInfoProvider).(android.ApexInfo)
132 for _, apex := range apexInfo.InApexVariants {
133 if existing, ok := apexToFragment[apex]; ok {
134 ctx.ModuleErrorf("apex %s has multiple fragments, %s and %s", apex, fragment, existing)
135 continue
136 }
137 apexToFragment[apex] = fragment
138 }
139 }
140
141 fragmentToElement := map[android.Module]*ClasspathFragmentElement{}
142 elements := []ClasspathElement{}
143 var currentElement ClasspathElement
144
145skipLibrary:
146 // Iterate over the libraries to construct the ClasspathElements list.
147 for _, library := range libraries {
148 var element ClasspathElement
149 if ctx.OtherModuleHasProvider(library, android.ApexInfoProvider) {
150 apexInfo := ctx.OtherModuleProvider(library, android.ApexInfoProvider).(android.ApexInfo)
151
152 var fragment android.Module
153
154 // Make sure that the library is in only one fragment of the classpath.
155 for _, apex := range apexInfo.InApexVariants {
156 if f, ok := apexToFragment[apex]; ok {
157 if fragment == nil {
158 // This is the first fragment so just save it away.
159 fragment = f
160 } else if f != fragment {
161 // This apex variant of the library is in a different fragment.
162 ctx.ModuleErrorf("library %s is in two separate fragments, %s and %s", library, fragment, f)
163 // Skip over this library entirely as otherwise the resulting classpath elements would
164 // be invalid.
165 continue skipLibrary
166 }
167 } else {
168 // There is no fragment associated with the library's apex.
169 }
170 }
171
172 if fragment == nil {
173 ctx.ModuleErrorf("library %s is from apexes %s which have no corresponding fragment in %s",
174 library, apexInfo.InApexVariants, fragments)
175 // Skip over this library entirely as otherwise the resulting classpath elements would
176 // be invalid.
177 continue skipLibrary
178 } else if existingFragmentElement, ok := fragmentToElement[fragment]; ok {
179 // This library is in a fragment element that has already been added.
180
181 // If the existing fragment element is still the current element then this library is
182 // contiguous with other libraries in that fragment so there is nothing more to do.
183 // Otherwise this library is not contiguous with other libraries in the same fragment which
184 // is an error.
185 if existingFragmentElement != currentElement {
186 separator := ""
187 if fragmentElement, ok := currentElement.(*ClasspathFragmentElement); ok {
188 separator = fmt.Sprintf("libraries from fragment %s like %s", fragmentElement.Fragment, fragmentElement.Contents[0])
189 } else {
190 libraryElement := currentElement.(*ClasspathLibraryElement)
191 separator = fmt.Sprintf("library %s", libraryElement.Library)
192 }
193
194 // Get the library that precedes this library in the fragment. That is the last library as
195 // this library has not yet been added.
196 precedingLibraryInFragment := existingFragmentElement.Contents[len(existingFragmentElement.Contents)-1]
197 ctx.ModuleErrorf("libraries from the same fragment must be contiguous, however %s and %s from fragment %s are separated by %s",
198 precedingLibraryInFragment, library, fragment, separator)
199 }
200
201 // Add this library to the fragment element's contents.
202 existingFragmentElement.Contents = append(existingFragmentElement.Contents, library)
203 } else {
204 // This is the first library in this fragment so add a new element for the fragment,
205 // including the library.
206 fragmentElement := &ClasspathFragmentElement{
207 Fragment: fragment,
208 Contents: []android.Module{library},
209 }
210
211 // Store it away so we can detect when attempting to create another element for the same
212 // fragment.
213 fragmentToElement[fragment] = fragmentElement
214 element = fragmentElement
215 }
216 } else {
217 // The library is from the platform so just add an element for it.
218 element = &ClasspathLibraryElement{Library: library}
219 }
220
221 // If no element was created then it means that the library has been added to an existing
222 // fragment element so the list of elements and current element are unaffected.
223 if element != nil {
224 // Add the element to the list and make it the current element for the next iteration.
225 elements = append(elements, element)
226 currentElement = element
227 }
228 }
229
230 return elements
231}