blob: c7b808f3d6bc172dd7cd56328534c0fab8f63f2f [file] [log] [blame]
Liz Kammer7ec40cc2022-07-29 10:44:23 -04001// Copyright 2022 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package android
16
17import (
18 "encoding/json"
19 "errors"
20 "fmt"
21 "path/filepath"
22 "strings"
23)
24
25// The ConfiguredJarList struct provides methods for handling a list of (apex, jar) pairs.
26// Such lists are used in the build system for things like bootclasspath jars or system server jars.
27// The apex part is either an apex name, or a special names "platform" or "system_ext". Jar is a
28// module name. The pairs come from Make product variables as a list of colon-separated strings.
29//
30// Examples:
31// - "com.android.art:core-oj"
32// - "platform:framework"
33// - "system_ext:foo"
34type ConfiguredJarList struct {
35 // A list of apex components, which can be an apex name,
36 // or special names like "platform" or "system_ext".
37 apexes []string
38
39 // A list of jar module name components.
40 jars []string
41}
42
43// Len returns the length of the list of jars.
44func (l *ConfiguredJarList) Len() int {
45 return len(l.jars)
46}
47
48// Jar returns the idx-th jar component of (apex, jar) pairs.
49func (l *ConfiguredJarList) Jar(idx int) string {
50 return l.jars[idx]
51}
52
53// Apex returns the idx-th apex component of (apex, jar) pairs.
54func (l *ConfiguredJarList) Apex(idx int) string {
55 return l.apexes[idx]
56}
57
58// ContainsJar returns true if the (apex, jar) pairs contains a pair with the
59// given jar module name.
60func (l *ConfiguredJarList) ContainsJar(jar string) bool {
61 return InList(jar, l.jars)
62}
63
64// If the list contains the given (apex, jar) pair.
65func (l *ConfiguredJarList) containsApexJarPair(apex, jar string) bool {
66 for i := 0; i < l.Len(); i++ {
67 if apex == l.apexes[i] && jar == l.jars[i] {
68 return true
69 }
70 }
71 return false
72}
73
74// ApexOfJar returns the apex component of the first pair with the given jar name on the list, or
75// an empty string if not found.
76func (l *ConfiguredJarList) ApexOfJar(jar string) string {
77 if idx := IndexList(jar, l.jars); idx != -1 {
78 return l.Apex(IndexList(jar, l.jars))
79 }
80 return ""
81}
82
83// IndexOfJar returns the first pair with the given jar name on the list, or -1
84// if not found.
85func (l *ConfiguredJarList) IndexOfJar(jar string) int {
86 return IndexList(jar, l.jars)
87}
88
89func copyAndAppend(list []string, item string) []string {
90 // Create the result list to be 1 longer than the input.
91 result := make([]string, len(list)+1)
92
93 // Copy the whole input list into the result.
94 count := copy(result, list)
95
96 // Insert the extra item at the end.
97 result[count] = item
98
99 return result
100}
101
102// Append an (apex, jar) pair to the list.
103func (l *ConfiguredJarList) Append(apex string, jar string) ConfiguredJarList {
104 // Create a copy of the backing arrays before appending to avoid sharing backing
105 // arrays that are mutated across instances.
106 apexes := copyAndAppend(l.apexes, apex)
107 jars := copyAndAppend(l.jars, jar)
108
109 return ConfiguredJarList{apexes, jars}
110}
111
112// Append a list of (apex, jar) pairs to the list.
113func (l *ConfiguredJarList) AppendList(other *ConfiguredJarList) ConfiguredJarList {
114 apexes := make([]string, 0, l.Len()+other.Len())
115 jars := make([]string, 0, l.Len()+other.Len())
116
117 apexes = append(apexes, l.apexes...)
118 jars = append(jars, l.jars...)
119
120 apexes = append(apexes, other.apexes...)
121 jars = append(jars, other.jars...)
122
123 return ConfiguredJarList{apexes, jars}
124}
125
126// RemoveList filters out a list of (apex, jar) pairs from the receiving list of pairs.
127func (l *ConfiguredJarList) RemoveList(list ConfiguredJarList) ConfiguredJarList {
128 apexes := make([]string, 0, l.Len())
129 jars := make([]string, 0, l.Len())
130
131 for i, jar := range l.jars {
132 apex := l.apexes[i]
133 if !list.containsApexJarPair(apex, jar) {
134 apexes = append(apexes, apex)
135 jars = append(jars, jar)
136 }
137 }
138
139 return ConfiguredJarList{apexes, jars}
140}
141
142// Filter keeps the entries if a jar appears in the given list of jars to keep. Returns a new list
143// and any remaining jars that are not on this list.
144func (l *ConfiguredJarList) Filter(jarsToKeep []string) (ConfiguredJarList, []string) {
145 var apexes []string
146 var jars []string
147
148 for i, jar := range l.jars {
149 if InList(jar, jarsToKeep) {
150 apexes = append(apexes, l.apexes[i])
151 jars = append(jars, jar)
152 }
153 }
154
155 return ConfiguredJarList{apexes, jars}, RemoveListFromList(jarsToKeep, jars)
156}
157
158// CopyOfJars returns a copy of the list of strings containing jar module name
159// components.
160func (l *ConfiguredJarList) CopyOfJars() []string {
161 return CopyOf(l.jars)
162}
163
164// CopyOfApexJarPairs returns a copy of the list of strings with colon-separated
165// (apex, jar) pairs.
166func (l *ConfiguredJarList) CopyOfApexJarPairs() []string {
167 pairs := make([]string, 0, l.Len())
168
169 for i, jar := range l.jars {
170 apex := l.apexes[i]
171 pairs = append(pairs, apex+":"+jar)
172 }
173
174 return pairs
175}
176
177// BuildPaths returns a list of build paths based on the given directory prefix.
178func (l *ConfiguredJarList) BuildPaths(ctx PathContext, dir OutputPath) WritablePaths {
179 paths := make(WritablePaths, l.Len())
180 for i, jar := range l.jars {
Jiakai Zhang4d90da22023-07-12 16:51:48 +0100181 paths[i] = dir.Join(ctx, ModuleStem(ctx.Config(), l.Apex(i), jar)+".jar")
Liz Kammer7ec40cc2022-07-29 10:44:23 -0400182 }
183 return paths
184}
185
186// BuildPathsByModule returns a map from module name to build paths based on the given directory
187// prefix.
188func (l *ConfiguredJarList) BuildPathsByModule(ctx PathContext, dir OutputPath) map[string]WritablePath {
189 paths := map[string]WritablePath{}
Jiakai Zhang4d90da22023-07-12 16:51:48 +0100190 for i, jar := range l.jars {
191 paths[jar] = dir.Join(ctx, ModuleStem(ctx.Config(), l.Apex(i), jar)+".jar")
Liz Kammer7ec40cc2022-07-29 10:44:23 -0400192 }
193 return paths
194}
195
196// UnmarshalJSON converts JSON configuration from raw bytes into a
197// ConfiguredJarList structure.
198func (l *ConfiguredJarList) UnmarshalJSON(b []byte) error {
199 // Try and unmarshal into a []string each item of which contains a pair
200 // <apex>:<jar>.
201 var list []string
202 err := json.Unmarshal(b, &list)
203 if err != nil {
204 // Did not work so return
205 return err
206 }
207
208 apexes, jars, err := splitListOfPairsIntoPairOfLists(list)
209 if err != nil {
210 return err
211 }
212 l.apexes = apexes
213 l.jars = jars
214 return nil
215}
216
217func (l *ConfiguredJarList) MarshalJSON() ([]byte, error) {
218 if len(l.apexes) != len(l.jars) {
219 return nil, errors.New(fmt.Sprintf("Inconsistent ConfiguredJarList: apexes: %q, jars: %q", l.apexes, l.jars))
220 }
221
222 list := make([]string, 0, len(l.apexes))
223
224 for i := 0; i < len(l.apexes); i++ {
225 list = append(list, l.apexes[i]+":"+l.jars[i])
226 }
227
228 return json.Marshal(list)
229}
230
Jiakai Zhang4d90da22023-07-12 16:51:48 +0100231func OverrideConfiguredJarLocationFor(cfg Config, apex string, jar string) (newApex string, newJar string) {
232 for _, entry := range cfg.productVariables.ConfiguredJarLocationOverrides {
233 tuple := strings.Split(entry, ":")
234 if len(tuple) != 4 {
235 panic("malformed configured jar location override '%s', expected format: <old_apex>:<old_jar>:<new_apex>:<new_jar>")
236 }
237 if apex == tuple[0] && jar == tuple[1] {
238 return tuple[2], tuple[3]
239 }
Liz Kammer7ec40cc2022-07-29 10:44:23 -0400240 }
Jiakai Zhang4d90da22023-07-12 16:51:48 +0100241 return apex, jar
242}
243
244// ModuleStem returns the overridden jar name.
245func ModuleStem(cfg Config, apex string, jar string) string {
246 _, newJar := OverrideConfiguredJarLocationFor(cfg, apex, jar)
247 return newJar
Liz Kammer7ec40cc2022-07-29 10:44:23 -0400248}
249
250// DevicePaths computes the on-device paths for the list of (apex, jar) pairs,
251// based on the operating system.
252func (l *ConfiguredJarList) DevicePaths(cfg Config, ostype OsType) []string {
253 paths := make([]string, l.Len())
Jiakai Zhang4d90da22023-07-12 16:51:48 +0100254 for i := 0; i < l.Len(); i++ {
255 apex, jar := OverrideConfiguredJarLocationFor(cfg, l.Apex(i), l.Jar(i))
256 name := jar + ".jar"
Liz Kammer7ec40cc2022-07-29 10:44:23 -0400257
258 var subdir string
259 if apex == "platform" {
260 subdir = "system/framework"
261 } else if apex == "system_ext" {
262 subdir = "system_ext/framework"
263 } else {
264 subdir = filepath.Join("apex", apex, "javalib")
265 }
266
267 if ostype.Class == Host {
268 paths[i] = filepath.Join(cfg.Getenv("OUT_DIR"), "host", cfg.PrebuiltOS(), subdir, name)
269 } else {
270 paths[i] = filepath.Join("/", subdir, name)
271 }
272 }
273 return paths
274}
275
276func (l *ConfiguredJarList) String() string {
277 var pairs []string
278 for i := 0; i < l.Len(); i++ {
279 pairs = append(pairs, l.apexes[i]+":"+l.jars[i])
280 }
281 return strings.Join(pairs, ",")
282}
283
284func splitListOfPairsIntoPairOfLists(list []string) ([]string, []string, error) {
285 // Now we need to populate this list by splitting each item in the slice of
286 // pairs and appending them to the appropriate list of apexes or jars.
287 apexes := make([]string, len(list))
288 jars := make([]string, len(list))
289
290 for i, apexjar := range list {
291 apex, jar, err := splitConfiguredJarPair(apexjar)
292 if err != nil {
293 return nil, nil, err
294 }
295 apexes[i] = apex
296 jars[i] = jar
297 }
298
299 return apexes, jars, nil
300}
301
302// Expected format for apexJarValue = <apex name>:<jar name>
303func splitConfiguredJarPair(str string) (string, string, error) {
304 pair := strings.SplitN(str, ":", 2)
305 if len(pair) == 2 {
306 apex := pair[0]
307 jar := pair[1]
308 if apex == "" {
309 return apex, jar, fmt.Errorf("invalid apex '%s' in <apex>:<jar> pair '%s', expected format: <apex>:<jar>", apex, str)
310 }
311 return apex, jar, nil
312 } else {
313 return "error-apex", "error-jar", fmt.Errorf("malformed (apex, jar) pair: '%s', expected format: <apex>:<jar>", str)
314 }
315}
316
317// EmptyConfiguredJarList returns an empty jar list.
318func EmptyConfiguredJarList() ConfiguredJarList {
319 return ConfiguredJarList{}
320}
321
Jiakai Zhangb1639db2023-07-11 15:03:13 +0100322// IsConfiguredJarForPlatform returns true if the given apex name is a special name for the platform.
323func IsConfiguredJarForPlatform(apex string) bool {
324 return apex == "platform" || apex == "system_ext"
325}
326
Liz Kammer7ec40cc2022-07-29 10:44:23 -0400327var earlyBootJarsKey = NewOnceKey("earlyBootJars")