classpath_fragment.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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. "github.com/google/blueprint"
  20. "github.com/google/blueprint/proptools"
  21. "strings"
  22. "android/soong/android"
  23. )
  24. // Build rules and utilities to generate individual packages/modules/common/proto/classpaths.proto
  25. // config files based on build configuration to embed into /system and /apex on a device.
  26. //
  27. // See `derive_classpath` service that reads the configs at runtime and defines *CLASSPATH variables
  28. // on the device.
  29. type classpathType int
  30. const (
  31. // Matches definition in packages/modules/common/proto/classpaths.proto
  32. BOOTCLASSPATH classpathType = iota
  33. DEX2OATBOOTCLASSPATH
  34. SYSTEMSERVERCLASSPATH
  35. STANDALONE_SYSTEMSERVER_JARS
  36. )
  37. func (c classpathType) String() string {
  38. return [...]string{"BOOTCLASSPATH", "DEX2OATBOOTCLASSPATH", "SYSTEMSERVERCLASSPATH", "STANDALONE_SYSTEMSERVER_JARS"}[c]
  39. }
  40. type classpathFragmentProperties struct {
  41. // Whether to generated classpaths.proto config instance for the fragment. If the config is not
  42. // generated, then relevant boot jars are added to platform classpath, i.e. platform_bootclasspath
  43. // or platform_systemserverclasspath. This is useful for non-updatable APEX boot jars, to keep
  44. // them as part of dexopt on device. Defaults to true.
  45. Generate_classpaths_proto *bool
  46. }
  47. // classpathFragment interface is implemented by a module that contributes jars to a *CLASSPATH
  48. // variables at runtime.
  49. type classpathFragment interface {
  50. android.Module
  51. classpathFragmentBase() *ClasspathFragmentBase
  52. }
  53. // ClasspathFragmentBase is meant to be embedded in any module types that implement classpathFragment;
  54. // such modules are expected to call initClasspathFragment().
  55. type ClasspathFragmentBase struct {
  56. properties classpathFragmentProperties
  57. classpathType classpathType
  58. outputFilepath android.OutputPath
  59. installDirPath android.InstallPath
  60. }
  61. func (c *ClasspathFragmentBase) classpathFragmentBase() *ClasspathFragmentBase {
  62. return c
  63. }
  64. // Initializes ClasspathFragmentBase struct. Must be called by all modules that include ClasspathFragmentBase.
  65. func initClasspathFragment(c classpathFragment, classpathType classpathType) {
  66. base := c.classpathFragmentBase()
  67. base.classpathType = classpathType
  68. c.AddProperties(&base.properties)
  69. }
  70. // Matches definition of Jar in packages/modules/SdkExtensions/proto/classpaths.proto
  71. type classpathJar struct {
  72. path string
  73. classpath classpathType
  74. minSdkVersion string
  75. maxSdkVersion string
  76. }
  77. // gatherPossibleApexModuleNamesAndStems returns a set of module and stem names from the
  78. // supplied contents that may be in the apex boot jars.
  79. //
  80. // The module names are included because sometimes the stem is set to just change the name of
  81. // the installed file and it expects the configuration to still use the actual module name.
  82. //
  83. // The stem names are included because sometimes the stem is set to change the effective name of the
  84. // module that is used in the configuration as well,e .g. when a test library is overriding an
  85. // actual boot jar
  86. func gatherPossibleApexModuleNamesAndStems(ctx android.ModuleContext, contents []string, tag blueprint.DependencyTag) []string {
  87. set := map[string]struct{}{}
  88. for _, name := range contents {
  89. dep := ctx.GetDirectDepWithTag(name, tag)
  90. set[name] = struct{}{}
  91. if m, ok := dep.(ModuleWithStem); ok {
  92. set[m.Stem()] = struct{}{}
  93. } else {
  94. ctx.PropertyErrorf("contents", "%v is not a ModuleWithStem", name)
  95. }
  96. }
  97. return android.SortedKeys(set)
  98. }
  99. // Converts android.ConfiguredJarList into a list of classpathJars for each given classpathType.
  100. func configuredJarListToClasspathJars(ctx android.ModuleContext, configuredJars android.ConfiguredJarList, classpaths ...classpathType) []classpathJar {
  101. paths := configuredJars.DevicePaths(ctx.Config(), android.Android)
  102. jars := make([]classpathJar, 0, len(paths)*len(classpaths))
  103. for i := 0; i < len(paths); i++ {
  104. for _, classpathType := range classpaths {
  105. jar := classpathJar{
  106. classpath: classpathType,
  107. path: paths[i],
  108. }
  109. ctx.VisitDirectDepsIf(func(m android.Module) bool {
  110. return m.Name() == configuredJars.Jar(i)
  111. }, func(m android.Module) {
  112. if s, ok := m.(*SdkLibrary); ok {
  113. // TODO(208456999): instead of mapping "current" to latest, min_sdk_version should never be set to "current"
  114. if s.minSdkVersion.Specified() {
  115. if s.minSdkVersion.IsCurrent() {
  116. jar.minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
  117. } else {
  118. jar.minSdkVersion = s.minSdkVersion.String()
  119. }
  120. }
  121. if s.maxSdkVersion.Specified() {
  122. if s.maxSdkVersion.IsCurrent() {
  123. jar.maxSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
  124. } else {
  125. jar.maxSdkVersion = s.maxSdkVersion.String()
  126. }
  127. }
  128. }
  129. })
  130. jars = append(jars, jar)
  131. }
  132. }
  133. return jars
  134. }
  135. func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, configuredJars android.ConfiguredJarList, jars []classpathJar) {
  136. generateProto := proptools.BoolDefault(c.properties.Generate_classpaths_proto, true)
  137. if generateProto {
  138. outputFilename := strings.ToLower(c.classpathType.String()) + ".pb"
  139. c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
  140. c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
  141. generatedTextproto := android.PathForModuleOut(ctx, outputFilename+".textproto")
  142. writeClasspathsTextproto(ctx, generatedTextproto, jars)
  143. rule := android.NewRuleBuilder(pctx, ctx)
  144. rule.Command().
  145. BuiltTool("conv_classpaths_proto").
  146. Flag("encode").
  147. Flag("--format=textproto").
  148. FlagWithInput("--input=", generatedTextproto).
  149. FlagWithOutput("--output=", c.outputFilepath)
  150. rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String())
  151. }
  152. classpathProtoInfo := ClasspathFragmentProtoContentInfo{
  153. ClasspathFragmentProtoGenerated: generateProto,
  154. ClasspathFragmentProtoContents: configuredJars,
  155. ClasspathFragmentProtoInstallDir: c.installDirPath,
  156. ClasspathFragmentProtoOutput: c.outputFilepath,
  157. }
  158. ctx.SetProvider(ClasspathFragmentProtoContentInfoProvider, classpathProtoInfo)
  159. }
  160. func writeClasspathsTextproto(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) {
  161. var content strings.Builder
  162. for _, jar := range jars {
  163. fmt.Fprintf(&content, "jars {\n")
  164. fmt.Fprintf(&content, "path: \"%s\"\n", jar.path)
  165. fmt.Fprintf(&content, "classpath: %s\n", jar.classpath)
  166. fmt.Fprintf(&content, "min_sdk_version: \"%s\"\n", jar.minSdkVersion)
  167. fmt.Fprintf(&content, "max_sdk_version: \"%s\"\n", jar.maxSdkVersion)
  168. fmt.Fprintf(&content, "}\n")
  169. }
  170. android.WriteFileRule(ctx, output, content.String())
  171. }
  172. // Returns AndroidMkEntries objects to install generated classpath.proto.
  173. // Do not use this to install into APEXes as the injection of the generated files happen separately for APEXes.
  174. func (c *ClasspathFragmentBase) androidMkEntries() []android.AndroidMkEntries {
  175. return []android.AndroidMkEntries{{
  176. Class: "ETC",
  177. OutputFile: android.OptionalPathForPath(c.outputFilepath),
  178. ExtraEntries: []android.AndroidMkExtraEntriesFunc{
  179. func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
  180. entries.SetString("LOCAL_MODULE_PATH", c.installDirPath.String())
  181. entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.outputFilepath.Base())
  182. },
  183. },
  184. }}
  185. }
  186. var ClasspathFragmentProtoContentInfoProvider = blueprint.NewProvider(ClasspathFragmentProtoContentInfo{})
  187. type ClasspathFragmentProtoContentInfo struct {
  188. // Whether the classpaths.proto config is generated for the fragment.
  189. ClasspathFragmentProtoGenerated bool
  190. // ClasspathFragmentProtoContents contains a list of jars that are part of this classpath fragment.
  191. ClasspathFragmentProtoContents android.ConfiguredJarList
  192. // ClasspathFragmentProtoOutput is an output path for the generated classpaths.proto config of this module.
  193. //
  194. // The file should be copied to a relevant place on device, see ClasspathFragmentProtoInstallDir
  195. // for more details.
  196. ClasspathFragmentProtoOutput android.OutputPath
  197. // ClasspathFragmentProtoInstallDir contains information about on device location for the generated classpaths.proto file.
  198. //
  199. // The path encodes expected sub-location within partitions, i.e. etc/classpaths/<proto-file>,
  200. // for ClasspathFragmentProtoOutput. To get sub-location, instead of the full output / make path
  201. // use android.InstallPath#Rel().
  202. //
  203. // This is only relevant for APEX modules as they perform their own installation; while regular
  204. // system files are installed via ClasspathFragmentBase#androidMkEntries().
  205. ClasspathFragmentProtoInstallDir android.InstallPath
  206. }