• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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")
315