blob: 53fef052a65cc0bd9cc45f379cb624c3783f821f [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 {
181 paths[i] = dir.Join(ctx, ModuleStem(jar)+".jar")
182 }
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{}
190 for _, jar := range l.jars {
191 paths[jar] = dir.Join(ctx, ModuleStem(jar)+".jar")
192 }
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
231// ModuleStem hardcodes the stem of framework-minus-apex to return "framework".
232//
233// TODO(b/139391334): hard coded until we find a good way to query the stem of a
234// module before any other mutators are run.
235func ModuleStem(module string) string {
236 if module == "framework-minus-apex" {
237 return "framework"
238 }
239 return module
240}
241
242// DevicePaths computes the on-device paths for the list of (apex, jar) pairs,
243// based on the operating system.
244func (l *ConfiguredJarList) DevicePaths(cfg Config, ostype OsType) []string {
245 paths := make([]string, l.Len())
246 for i, jar := range l.jars {
247 apex := l.apexes[i]
248 name := ModuleStem(jar) + ".jar"
249
250 var subdir string
251 if apex == "platform" {
252 subdir = "system/framework"
253 } else if apex == "system_ext" {
254 subdir = "system_ext/framework"
255 } else {
256 subdir = filepath.Join("apex", apex, "javalib")
257 }
258
259 if ostype.Class == Host {
260 paths[i] = filepath.Join(cfg.Getenv("OUT_DIR"), "host", cfg.PrebuiltOS(), subdir, name)
261 } else {
262 paths[i] = filepath.Join("/", subdir, name)
263 }
264 }
265 return paths
266}
267
268func (l *ConfiguredJarList) String() string {
269 var pairs []string
270 for i := 0; i < l.Len(); i++ {
271 pairs = append(pairs, l.apexes[i]+":"+l.jars[i])
272 }
273 return strings.Join(pairs, ",")
274}
275
276func splitListOfPairsIntoPairOfLists(list []string) ([]string, []string, error) {
277 // Now we need to populate this list by splitting each item in the slice of
278 // pairs and appending them to the appropriate list of apexes or jars.
279 apexes := make([]string, len(list))
280 jars := make([]string, len(list))
281
282 for i, apexjar := range list {
283 apex, jar, err := splitConfiguredJarPair(apexjar)
284 if err != nil {
285 return nil, nil, err
286 }
287 apexes[i] = apex
288 jars[i] = jar
289 }
290
291 return apexes, jars, nil
292}
293
294// Expected format for apexJarValue = <apex name>:<jar name>
295func splitConfiguredJarPair(str string) (string, string, error) {
296 pair := strings.SplitN(str, ":", 2)
297 if len(pair) == 2 {
298 apex := pair[0]
299 jar := pair[1]
300 if apex == "" {
301 return apex, jar, fmt.Errorf("invalid apex '%s' in <apex>:<jar> pair '%s', expected format: <apex>:<jar>", apex, str)
302 }
303 return apex, jar, nil
304 } else {
305 return "error-apex", "error-jar", fmt.Errorf("malformed (apex, jar) pair: '%s', expected format: <apex>:<jar>", str)
306 }
307}
308
309// EmptyConfiguredJarList returns an empty jar list.
310func EmptyConfiguredJarList() ConfiguredJarList {
311 return ConfiguredJarList{}
312}
313
314var earlyBootJarsKey = NewOnceKey("earlyBootJars")