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 17package java 18 19import ( 20 "fmt" 21 "strings" 22 23 "android/soong/android" 24) 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 { 74 android.OtherModuleProviderContext 75 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: 98// - 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. 103// 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: 110// 111// libraries: com.android.art:core-oj, com.android.art:core-libart, framework, ext 112// fragments: com.android.art:art-bootclasspath-fragment 113// 114// Then this will return: 115// 116// ClasspathFragmentElement(art-bootclasspath-fragment, [core-oj, core-libart]), 117// ClasspathLibraryElement(framework), 118// ClasspathLibraryElement(ext), 119func CreateClasspathElements(ctx ClasspathElementContext, libraries []android.Module, fragments []android.Module) ClasspathElements { 120 // Create a map from apex name to the fragment module. This makes it easy to find the fragment 121 // associated with a particular apex. 122 apexToFragment := map[string]android.Module{} 123 for _, fragment := range fragments { 124 apexInfo, ok := android.OtherModuleProvider(ctx, fragment, android.ApexInfoProvider) 125 if !ok { 126 ctx.ModuleErrorf("fragment %s is not part of an apex", fragment) 127 continue 128 } 129 130 for _, apex := range apexInfo.InApexVariants { 131 if existing, ok := apexToFragment[apex]; ok { 132 ctx.ModuleErrorf("apex %s has multiple fragments, %s and %s", apex, fragment, existing) 133 continue 134 } 135 apexToFragment[apex] = fragment 136 } 137 } 138 139 fragmentToElement := map[android.Module]*ClasspathFragmentElement{} 140 elements := []ClasspathElement{} 141 var currentElement ClasspathElement 142 143skipLibrary: 144 // Iterate over the libraries to construct the ClasspathElements list. 145 for _, library := range libraries { 146 var element ClasspathElement 147 if apexInfo, ok := android.OtherModuleProvider(ctx, library, android.ApexInfoProvider); ok { 148 149 var fragment android.Module 150 151 // Make sure that the library is in only one fragment of the classpath. 152 for _, apex := range apexInfo.InApexVariants { 153 if f, ok := apexToFragment[apex]; ok { 154 if fragment == nil { 155 // This is the first fragment so just save it away. 156 fragment = f 157 } else if f != fragment { 158 // This apex variant of the library is in a different fragment. 159 ctx.ModuleErrorf("library %s is in two separate fragments, %s and %s", library, fragment, f) 160 // Skip over this library entirely as otherwise the resulting classpath elements would 161 // be invalid. 162 continue skipLibrary 163 } 164 } else { 165 // There is no fragment associated with the library's apex. 166 } 167 } 168 169 if fragment == nil { 170 ctx.ModuleErrorf("library %s is from apexes %s which have no corresponding fragment in %s", 171 library, apexInfo.InApexVariants, fragments) 172 // Skip over this library entirely as otherwise the resulting classpath elements would 173 // be invalid. 174 continue skipLibrary 175 } else if existingFragmentElement, ok := fragmentToElement[fragment]; ok { 176 // This library is in a fragment element that has already been added. 177 178 // If the existing fragment element is still the current element then this library is 179 // contiguous with other libraries in that fragment so there is nothing more to do. 180 // Otherwise this library is not contiguous with other libraries in the same fragment which 181 // is an error. 182 if existingFragmentElement != currentElement { 183 separator := "" 184 if fragmentElement, ok := currentElement.(*ClasspathFragmentElement); ok { 185 separator = fmt.Sprintf("libraries from fragment %s like %s", fragmentElement.Fragment, fragmentElement.Contents[0]) 186 } else { 187 libraryElement := currentElement.(*ClasspathLibraryElement) 188 separator = fmt.Sprintf("library %s", libraryElement.Library) 189 } 190 191 // Get the library that precedes this library in the fragment. That is the last library as 192 // this library has not yet been added. 193 precedingLibraryInFragment := existingFragmentElement.Contents[len(existingFragmentElement.Contents)-1] 194 ctx.ModuleErrorf("libraries from the same fragment must be contiguous, however %s and %s from fragment %s are separated by %s", 195 precedingLibraryInFragment, library, fragment, separator) 196 } 197 198 // Add this library to the fragment element's contents. 199 existingFragmentElement.Contents = append(existingFragmentElement.Contents, library) 200 } else { 201 // This is the first library in this fragment so add a new element for the fragment, 202 // including the library. 203 fragmentElement := &ClasspathFragmentElement{ 204 Fragment: fragment, 205 Contents: []android.Module{library}, 206 } 207 208 // Store it away so we can detect when attempting to create another element for the same 209 // fragment. 210 fragmentToElement[fragment] = fragmentElement 211 element = fragmentElement 212 } 213 } else { 214 // The library is from the platform so just add an element for it. 215 element = &ClasspathLibraryElement{Library: library} 216 } 217 218 // If no element was created then it means that the library has been added to an existing 219 // fragment element so the list of elements and current element are unaffected. 220 if element != nil { 221 // Add the element to the list and make it the current element for the next iteration. 222 elements = append(elements, element) 223 currentElement = element 224 } 225 } 226 227 return elements 228} 229