blob: 92e01a2dfa3832383d75949ab78a296cc6478a61 [file] [log] [blame]
Artur Satayeveabf2c12021-04-07 15:45:02 +01001/*
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 (
Artur Satayev97259dc2021-04-07 15:17:14 +010020 "fmt"
satayev14e49132021-05-17 21:03:07 +010021 "github.com/google/blueprint"
satayevb98371c2021-06-15 16:49:50 +010022 "github.com/google/blueprint/proptools"
Artur Satayev97259dc2021-04-07 15:17:14 +010023 "strings"
24
Artur Satayeveabf2c12021-04-07 15:45:02 +010025 "android/soong/android"
26)
27
28// Build rules and utilities to generate individual packages/modules/SdkExtensions/proto/classpaths.proto
29// config files based on build configuration to embed into /system and /apex on a device.
30//
31// See `derive_classpath` service that reads the configs at runtime and defines *CLASSPATH variables
32// on the device.
33
34type classpathType int
35
36const (
37 // Matches definition in packages/modules/SdkExtensions/proto/classpaths.proto
38 BOOTCLASSPATH classpathType = iota
39 DEX2OATBOOTCLASSPATH
40 SYSTEMSERVERCLASSPATH
41)
42
43func (c classpathType) String() string {
44 return [...]string{"BOOTCLASSPATH", "DEX2OATBOOTCLASSPATH", "SYSTEMSERVERCLASSPATH"}[c]
45}
46
47type classpathFragmentProperties struct {
satayevb98371c2021-06-15 16:49:50 +010048 // Whether to generated classpaths.proto config instance for the fragment. If the config is not
49 // generated, then relevant boot jars are added to platform classpath, i.e. platform_bootclasspath
50 // or platform_systemserverclasspath. This is useful for non-updatable APEX boot jars, to keep
51 // them as part of dexopt on device. Defaults to true.
52 Generate_classpaths_proto *bool
Artur Satayeveabf2c12021-04-07 15:45:02 +010053}
54
55// classpathFragment interface is implemented by a module that contributes jars to a *CLASSPATH
56// variables at runtime.
57type classpathFragment interface {
58 android.Module
59
Artur Satayev97259dc2021-04-07 15:17:14 +010060 classpathFragmentBase() *ClasspathFragmentBase
Artur Satayeveabf2c12021-04-07 15:45:02 +010061}
62
Artur Satayev97259dc2021-04-07 15:17:14 +010063// ClasspathFragmentBase is meant to be embedded in any module types that implement classpathFragment;
Artur Satayeveabf2c12021-04-07 15:45:02 +010064// such modules are expected to call initClasspathFragment().
Artur Satayev97259dc2021-04-07 15:17:14 +010065type ClasspathFragmentBase struct {
Artur Satayeveabf2c12021-04-07 15:45:02 +010066 properties classpathFragmentProperties
67
satayev95e9c5b2021-04-29 11:50:26 +010068 classpathType classpathType
69
Artur Satayeveabf2c12021-04-07 15:45:02 +010070 outputFilepath android.OutputPath
Artur Satayev97259dc2021-04-07 15:17:14 +010071 installDirPath android.InstallPath
Artur Satayeveabf2c12021-04-07 15:45:02 +010072}
73
Artur Satayev97259dc2021-04-07 15:17:14 +010074func (c *ClasspathFragmentBase) classpathFragmentBase() *ClasspathFragmentBase {
75 return c
76}
77
78// Initializes ClasspathFragmentBase struct. Must be called by all modules that include ClasspathFragmentBase.
satayev95e9c5b2021-04-29 11:50:26 +010079func initClasspathFragment(c classpathFragment, classpathType classpathType) {
Artur Satayeveabf2c12021-04-07 15:45:02 +010080 base := c.classpathFragmentBase()
satayev95e9c5b2021-04-29 11:50:26 +010081 base.classpathType = classpathType
Artur Satayeveabf2c12021-04-07 15:45:02 +010082 c.AddProperties(&base.properties)
83}
Artur Satayev97259dc2021-04-07 15:17:14 +010084
85// Matches definition of Jar in packages/modules/SdkExtensions/proto/classpaths.proto
86type classpathJar struct {
satayevcca4ab72021-11-30 12:33:55 +000087 path string
88 classpath classpathType
89 minSdkVersion string
90 maxSdkVersion string
Artur Satayev97259dc2021-04-07 15:17:14 +010091}
92
satayevd604b212021-07-21 14:23:52 +010093// gatherPossibleApexModuleNamesAndStems returns a set of module and stem names from the
94// supplied contents that may be in the apex boot jars.
Paul Duffin56c93e82021-06-29 20:04:45 +010095//
96// The module names are included because sometimes the stem is set to just change the name of
97// the installed file and it expects the configuration to still use the actual module name.
98//
99// The stem names are included because sometimes the stem is set to change the effective name of the
100// module that is used in the configuration as well,e .g. when a test library is overriding an
101// actual boot jar
satayevd604b212021-07-21 14:23:52 +0100102func gatherPossibleApexModuleNamesAndStems(ctx android.ModuleContext, contents []string, tag blueprint.DependencyTag) []string {
Paul Duffin56c93e82021-06-29 20:04:45 +0100103 set := map[string]struct{}{}
104 for _, name := range contents {
105 dep := ctx.GetDirectDepWithTag(name, tag)
106 set[name] = struct{}{}
107 if m, ok := dep.(ModuleWithStem); ok {
108 set[m.Stem()] = struct{}{}
109 } else {
110 ctx.PropertyErrorf("contents", "%v is not a ModuleWithStem", name)
111 }
112 }
113 return android.SortedStringKeys(set)
114}
115
satayev013485b2021-05-06 23:38:10 +0100116// Converts android.ConfiguredJarList into a list of classpathJars for each given classpathType.
117func configuredJarListToClasspathJars(ctx android.ModuleContext, configuredJars android.ConfiguredJarList, classpaths ...classpathType) []classpathJar {
118 paths := configuredJars.DevicePaths(ctx.Config(), android.Android)
119 jars := make([]classpathJar, 0, len(paths)*len(classpaths))
120 for i := 0; i < len(paths); i++ {
121 for _, classpathType := range classpaths {
satayevcca4ab72021-11-30 12:33:55 +0000122 jar := classpathJar{
satayev013485b2021-05-06 23:38:10 +0100123 classpath: classpathType,
124 path: paths[i],
satayevcca4ab72021-11-30 12:33:55 +0000125 }
126 ctx.VisitDirectDepsIf(func(m android.Module) bool {
127 return m.Name() == configuredJars.Jar(i)
128 }, func(m android.Module) {
129 if s, ok := m.(*SdkLibrary); ok {
130 // TODO(208456999): instead of mapping "current" to latest, min_sdk_version should never be set to "current"
131 if s.minSdkVersion.Specified() {
132 if s.minSdkVersion.ApiLevel.IsCurrent() {
133 jar.minSdkVersion = ctx.Config().LatestPreviewApiLevel().String()
134 } else {
135 jar.minSdkVersion = s.minSdkVersion.ApiLevel.String()
136 }
137 }
138 if s.maxSdkVersion.Specified() {
139 if s.maxSdkVersion.ApiLevel.IsCurrent() {
140 jar.maxSdkVersion = ctx.Config().LatestPreviewApiLevel().String()
141 } else {
142 jar.maxSdkVersion = s.maxSdkVersion.ApiLevel.String()
143 }
144 }
145 }
satayev013485b2021-05-06 23:38:10 +0100146 })
satayevcca4ab72021-11-30 12:33:55 +0000147 jars = append(jars, jar)
satayev013485b2021-05-06 23:38:10 +0100148 }
149 }
150 return jars
151}
152
satayevb3090502021-06-15 17:49:10 +0100153func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, configuredJars android.ConfiguredJarList, jars []classpathJar) {
satayevb98371c2021-06-15 16:49:50 +0100154 generateProto := proptools.BoolDefault(c.properties.Generate_classpaths_proto, true)
155 if generateProto {
156 outputFilename := strings.ToLower(c.classpathType.String()) + ".pb"
157 c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
158 c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
Artur Satayev97259dc2021-04-07 15:17:14 +0100159
satayevb98371c2021-06-15 16:49:50 +0100160 generatedJson := android.PathForModuleOut(ctx, outputFilename+".json")
161 writeClasspathsJson(ctx, generatedJson, jars)
Artur Satayev97259dc2021-04-07 15:17:14 +0100162
satayevb98371c2021-06-15 16:49:50 +0100163 rule := android.NewRuleBuilder(pctx, ctx)
164 rule.Command().
165 BuiltTool("conv_classpaths_proto").
166 Flag("encode").
167 Flag("--format=json").
168 FlagWithInput("--input=", generatedJson).
169 FlagWithOutput("--output=", c.outputFilepath)
Artur Satayev97259dc2021-04-07 15:17:14 +0100170
satayevb98371c2021-06-15 16:49:50 +0100171 rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String())
172 }
satayev14e49132021-05-17 21:03:07 +0100173
174 classpathProtoInfo := ClasspathFragmentProtoContentInfo{
satayevb98371c2021-06-15 16:49:50 +0100175 ClasspathFragmentProtoGenerated: generateProto,
satayevb3090502021-06-15 17:49:10 +0100176 ClasspathFragmentProtoContents: configuredJars,
satayev14e49132021-05-17 21:03:07 +0100177 ClasspathFragmentProtoInstallDir: c.installDirPath,
178 ClasspathFragmentProtoOutput: c.outputFilepath,
179 }
180 ctx.SetProvider(ClasspathFragmentProtoContentInfoProvider, classpathProtoInfo)
Artur Satayev97259dc2021-04-07 15:17:14 +0100181}
182
183func writeClasspathsJson(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) {
184 var content strings.Builder
satayevcca4ab72021-11-30 12:33:55 +0000185
Artur Satayev97259dc2021-04-07 15:17:14 +0100186 fmt.Fprintf(&content, "{\n")
187 fmt.Fprintf(&content, "\"jars\": [\n")
188 for idx, jar := range jars {
189 fmt.Fprintf(&content, "{\n")
190
satayev7d226572021-05-18 19:37:32 +0100191 fmt.Fprintf(&content, "\"path\": \"%s\",\n", jar.path)
Artur Satayev97259dc2021-04-07 15:17:14 +0100192 fmt.Fprintf(&content, "\"classpath\": \"%s\"\n", jar.classpath)
193
satayevcca4ab72021-11-30 12:33:55 +0000194 if jar.minSdkVersion != "" {
195 fmt.Fprintf(&content, ",\n")
196 fmt.Fprintf(&content, "\"minSdkVersion\": \"%s\"\n", jar.minSdkVersion)
197 } else {
198 fmt.Fprintf(&content, "\n")
199 }
200
201 if jar.maxSdkVersion != "" {
202 fmt.Fprintf(&content, ",\n")
203 fmt.Fprintf(&content, "\"maxSdkVersion\": \"%s\"\n", jar.maxSdkVersion)
204 } else {
205 fmt.Fprintf(&content, "\n")
206 }
207
Artur Satayev97259dc2021-04-07 15:17:14 +0100208 if idx < len(jars)-1 {
209 fmt.Fprintf(&content, "},\n")
210 } else {
211 fmt.Fprintf(&content, "}\n")
212 }
213 }
214 fmt.Fprintf(&content, "]\n")
215 fmt.Fprintf(&content, "}\n")
satayevcca4ab72021-11-30 12:33:55 +0000216
Artur Satayev97259dc2021-04-07 15:17:14 +0100217 android.WriteFileRule(ctx, output, content.String())
218}
219
satayev3db35472021-05-06 23:59:58 +0100220// Returns AndroidMkEntries objects to install generated classpath.proto.
221// Do not use this to install into APEXes as the injection of the generated files happen separately for APEXes.
satayev128ce2f2021-05-06 13:21:15 +0100222func (c *ClasspathFragmentBase) androidMkEntries() []android.AndroidMkEntries {
satayev013485b2021-05-06 23:38:10 +0100223 return []android.AndroidMkEntries{{
Artur Satayev97259dc2021-04-07 15:17:14 +0100224 Class: "ETC",
225 OutputFile: android.OptionalPathForPath(c.outputFilepath),
226 ExtraEntries: []android.AndroidMkExtraEntriesFunc{
227 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
228 entries.SetString("LOCAL_MODULE_PATH", c.installDirPath.ToMakePath().String())
229 entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.outputFilepath.Base())
230 },
231 },
232 }}
233}
satayev14e49132021-05-17 21:03:07 +0100234
235var ClasspathFragmentProtoContentInfoProvider = blueprint.NewProvider(ClasspathFragmentProtoContentInfo{})
236
237type ClasspathFragmentProtoContentInfo struct {
satayevb98371c2021-06-15 16:49:50 +0100238 // Whether the classpaths.proto config is generated for the fragment.
239 ClasspathFragmentProtoGenerated bool
240
satayevb3090502021-06-15 17:49:10 +0100241 // ClasspathFragmentProtoContents contains a list of jars that are part of this classpath fragment.
242 ClasspathFragmentProtoContents android.ConfiguredJarList
243
satayev14e49132021-05-17 21:03:07 +0100244 // ClasspathFragmentProtoOutput is an output path for the generated classpaths.proto config of this module.
245 //
246 // The file should be copied to a relevant place on device, see ClasspathFragmentProtoInstallDir
247 // for more details.
248 ClasspathFragmentProtoOutput android.OutputPath
249
250 // ClasspathFragmentProtoInstallDir contains information about on device location for the generated classpaths.proto file.
251 //
252 // The path encodes expected sub-location within partitions, i.e. etc/classpaths/<proto-file>,
253 // for ClasspathFragmentProtoOutput. To get sub-location, instead of the full output / make path
254 // use android.InstallPath#Rel().
255 //
256 // This is only relevant for APEX modules as they perform their own installation; while regular
257 // system files are installed via ClasspathFragmentBase#androidMkEntries().
258 ClasspathFragmentProtoInstallDir android.InstallPath
259}