blob: 4af2770122c66eadcfb75ae1df28bee4369b5633 [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"
Paul Duffine245b612021-06-10 08:59:41 +010024)
25
26// Supports constructing a list of ClasspathElement from a set of fragments and modules.
27
28// ClasspathElement represents a component that contributes to a classpath. That can be
29// either a java module or a classpath fragment module.
30type ClasspathElement interface {
31 Module() android.Module
32 String() string
33}
34
35type ClasspathElements []ClasspathElement
36
37// ClasspathFragmentElement is a ClasspathElement that encapsulates a classpath fragment module.
38type ClasspathFragmentElement struct {
39 Fragment android.Module
40 Contents []android.Module
41}
42
43func (b *ClasspathFragmentElement) Module() android.Module {
44 return b.Fragment
45}
46
47func (b *ClasspathFragmentElement) String() string {
48 contents := []string{}
49 for _, module := range b.Contents {
50 contents = append(contents, module.String())
51 }
52 return fmt.Sprintf("fragment(%s, %s)", b.Fragment, strings.Join(contents, ", "))
53}
54
55var _ ClasspathElement = (*ClasspathFragmentElement)(nil)
56
57// ClasspathLibraryElement is a ClasspathElement that encapsulates a java library.
58type ClasspathLibraryElement struct {
59 Library android.Module
60}
61
62func (b *ClasspathLibraryElement) Module() android.Module {
63 return b.Library
64}
65
66func (b *ClasspathLibraryElement) String() string {
67 return fmt.Sprintf("library{%s}", b.Library)
68}
69
70var _ ClasspathElement = (*ClasspathLibraryElement)(nil)
71
72// ClasspathElementContext defines the context methods needed by CreateClasspathElements
73type ClasspathElementContext interface {
Colin Cross313aa542023-12-13 13:47:44 -080074 android.OtherModuleProviderContext
Paul Duffine245b612021-06-10 08:59:41 +010075 ModuleErrorf(fmt string, args ...interface{})
76}
77
78// CreateClasspathElements creates a list of ClasspathElement objects from a list of libraries and
79// a list of fragments.
80//
81// The libraries parameter contains the set of libraries from which the classpath is constructed.
82// The fragments parameter contains the classpath fragment modules whose contents are libraries that
83// are part of the classpath. Each library in the libraries parameter may be part of a fragment. The
84// determination as to which libraries belong to fragments and which do not is based on the apex to
85// which they belong, if any.
86//
87// Every fragment in the fragments list must be part of one or more apexes and each apex is assumed
88// to contain only a single fragment from the fragments list. A library in the libraries parameter
89// that is part of an apex must be provided by a classpath fragment in the corresponding apex.
90//
91// This will return a ClasspathElements list that contains a ClasspathElement for each standalone
92// library and each fragment. The order of the elements in the list is such that if the list was
93// flattened into a list of library modules that it would result in the same list or modules as the
94// input libraries. Flattening the list can be done by replacing each ClasspathFragmentElement in
95// the list with its Contents field.
96//
97// Requirements/Assumptions:
Colin Crossd079e0b2022-08-16 10:27:33 -070098// - A fragment can be associated with more than one apex but each apex must only be associated with
99// a single fragment from the fragments list.
100// - All of a fragment's contents must appear as a contiguous block in the same order in the
101// libraries list.
102// - Each library must only appear in a single fragment.
Paul Duffine245b612021-06-10 08:59:41 +0100103//
104// The apex is used to identify which libraries belong to which fragment. First a mapping is created
105// from apex to fragment. Then the libraries are iterated over and any library in an apex is
106// associated with an element for the fragment to which it belongs. Otherwise, the libraries are
107// standalone and have their own element.
108//
109// e.g. Given the following input:
Colin Crossd079e0b2022-08-16 10:27:33 -0700110//
Colin Cross92b0eb12025-02-06 11:49:52 -0800111// libraries: core-oj, core-libart, framework, ext
112// fragments: art-bootclasspath-fragment
113// libraryToApex: core-oj: com.android.art, core-libart: com.android.art
114// apexNameToFragment: 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),
Colin Cross92b0eb12025-02-06 11:49:52 -0800121func CreateClasspathElements(ctx ClasspathElementContext, libraries []android.Module, fragments []android.Module,
122 libraryToApex map[android.Module]string, apexNameToFragment map[string]android.Module) ClasspathElements {
Paul Duffine245b612021-06-10 08:59:41 +0100123
124 fragmentToElement := map[android.Module]*ClasspathFragmentElement{}
125 elements := []ClasspathElement{}
126 var currentElement ClasspathElement
127
128skipLibrary:
129 // Iterate over the libraries to construct the ClasspathElements list.
130 for _, library := range libraries {
131 var element ClasspathElement
Colin Cross92b0eb12025-02-06 11:49:52 -0800132 if libraryApex, ok := libraryToApex[library]; ok {
Paul Duffine245b612021-06-10 08:59:41 +0100133 var fragment android.Module
134
135 // Make sure that the library is in only one fragment of the classpath.
Colin Cross92b0eb12025-02-06 11:49:52 -0800136 if f, ok := apexNameToFragment[libraryApex]; ok {
137 if fragment == nil {
138 // This is the first fragment so just save it away.
139 fragment = f
140 } else if f != fragment {
141 // This apex variant of the library is in a different fragment.
142 ctx.ModuleErrorf("library %s is in two separate fragments, %s and %s", library, fragment, f)
143 // Skip over this library entirely as otherwise the resulting classpath elements would
144 // be invalid.
145 continue skipLibrary
Paul Duffine245b612021-06-10 08:59:41 +0100146 }
Colin Cross92b0eb12025-02-06 11:49:52 -0800147 } else {
148 // There is no fragment associated with the library's apex.
Paul Duffine245b612021-06-10 08:59:41 +0100149 }
150
151 if fragment == nil {
152 ctx.ModuleErrorf("library %s is from apexes %s which have no corresponding fragment in %s",
Colin Cross92b0eb12025-02-06 11:49:52 -0800153 library, []string{libraryApex}, fragments)
Paul Duffine245b612021-06-10 08:59:41 +0100154 // Skip over this library entirely as otherwise the resulting classpath elements would
155 // be invalid.
156 continue skipLibrary
157 } else if existingFragmentElement, ok := fragmentToElement[fragment]; ok {
158 // This library is in a fragment element that has already been added.
159
160 // If the existing fragment element is still the current element then this library is
161 // contiguous with other libraries in that fragment so there is nothing more to do.
162 // Otherwise this library is not contiguous with other libraries in the same fragment which
163 // is an error.
164 if existingFragmentElement != currentElement {
165 separator := ""
166 if fragmentElement, ok := currentElement.(*ClasspathFragmentElement); ok {
167 separator = fmt.Sprintf("libraries from fragment %s like %s", fragmentElement.Fragment, fragmentElement.Contents[0])
168 } else {
169 libraryElement := currentElement.(*ClasspathLibraryElement)
170 separator = fmt.Sprintf("library %s", libraryElement.Library)
171 }
172
173 // Get the library that precedes this library in the fragment. That is the last library as
174 // this library has not yet been added.
175 precedingLibraryInFragment := existingFragmentElement.Contents[len(existingFragmentElement.Contents)-1]
176 ctx.ModuleErrorf("libraries from the same fragment must be contiguous, however %s and %s from fragment %s are separated by %s",
177 precedingLibraryInFragment, library, fragment, separator)
178 }
179
180 // Add this library to the fragment element's contents.
181 existingFragmentElement.Contents = append(existingFragmentElement.Contents, library)
182 } else {
183 // This is the first library in this fragment so add a new element for the fragment,
184 // including the library.
185 fragmentElement := &ClasspathFragmentElement{
186 Fragment: fragment,
187 Contents: []android.Module{library},
188 }
189
190 // Store it away so we can detect when attempting to create another element for the same
191 // fragment.
192 fragmentToElement[fragment] = fragmentElement
193 element = fragmentElement
194 }
195 } else {
196 // The library is from the platform so just add an element for it.
197 element = &ClasspathLibraryElement{Library: library}
198 }
199
200 // If no element was created then it means that the library has been added to an existing
201 // fragment element so the list of elements and current element are unaffected.
202 if element != nil {
203 // Add the element to the list and make it the current element for the next iteration.
204 elements = append(elements, element)
205 currentElement = element
206 }
207 }
208
209 return elements
210}