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