| Paul Duffin | e245b61 | 2021-06-10 08:59:41 +0100 | [diff] [blame] | 1 | /* | 
 | 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 |  | 
 | 17 | package java | 
 | 18 |  | 
 | 19 | import ( | 
 | 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. | 
 | 31 | type ClasspathElement interface { | 
 | 32 | 	Module() android.Module | 
 | 33 | 	String() string | 
 | 34 | } | 
 | 35 |  | 
 | 36 | type ClasspathElements []ClasspathElement | 
 | 37 |  | 
 | 38 | // ClasspathFragmentElement is a ClasspathElement that encapsulates a classpath fragment module. | 
 | 39 | type ClasspathFragmentElement struct { | 
 | 40 | 	Fragment android.Module | 
 | 41 | 	Contents []android.Module | 
 | 42 | } | 
 | 43 |  | 
 | 44 | func (b *ClasspathFragmentElement) Module() android.Module { | 
 | 45 | 	return b.Fragment | 
 | 46 | } | 
 | 47 |  | 
 | 48 | func (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 |  | 
 | 56 | var _ ClasspathElement = (*ClasspathFragmentElement)(nil) | 
 | 57 |  | 
 | 58 | // ClasspathLibraryElement is a ClasspathElement that encapsulates a java library. | 
 | 59 | type ClasspathLibraryElement struct { | 
 | 60 | 	Library android.Module | 
 | 61 | } | 
 | 62 |  | 
 | 63 | func (b *ClasspathLibraryElement) Module() android.Module { | 
 | 64 | 	return b.Library | 
 | 65 | } | 
 | 66 |  | 
 | 67 | func (b *ClasspathLibraryElement) String() string { | 
 | 68 | 	return fmt.Sprintf("library{%s}", b.Library) | 
 | 69 | } | 
 | 70 |  | 
 | 71 | var _ ClasspathElement = (*ClasspathLibraryElement)(nil) | 
 | 72 |  | 
 | 73 | // ClasspathElementContext defines the context methods needed by CreateClasspathElements | 
 | 74 | type 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 Cross | d079e0b | 2022-08-16 10:27:33 -0700 | [diff] [blame] | 100 | //   - 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 Duffin | e245b61 | 2021-06-10 08:59:41 +0100 | [diff] [blame] | 105 | // | 
 | 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 Cross | d079e0b | 2022-08-16 10:27:33 -0700 | [diff] [blame] | 112 | // | 
 | 113 | //	libraries: com.android.art:core-oj, com.android.art:core-libart, framework, ext | 
 | 114 | //	fragments: com.android.art:art-bootclasspath-fragment | 
| Paul Duffin | e245b61 | 2021-06-10 08:59:41 +0100 | [diff] [blame] | 115 | // | 
 | 116 | // Then this will return: | 
| Colin Cross | d079e0b | 2022-08-16 10:27:33 -0700 | [diff] [blame] | 117 | // | 
 | 118 | //	ClasspathFragmentElement(art-bootclasspath-fragment, [core-oj, core-libart]), | 
 | 119 | //	ClasspathLibraryElement(framework), | 
 | 120 | //	ClasspathLibraryElement(ext), | 
| Paul Duffin | e245b61 | 2021-06-10 08:59:41 +0100 | [diff] [blame] | 121 | func 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 |  | 
 | 145 | skipLibrary: | 
 | 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 | } |