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