1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
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
}
|