classpath_element.go 9.3 KB

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