• 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(ctx.Config(), l.Apex(i), 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 i, jar := range l.jars {
191		paths[jar] = dir.Join(ctx, ModuleStem(ctx.Config(), l.Apex(i), 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
231func 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		}
240	}
241	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
248}
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())
254	for i := 0; i < l.Len(); i++ {
255		apex, jar := OverrideConfiguredJarLocationFor(cfg, l.Apex(i), l.Jar(i))
256		name := jar + ".jar"
257
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
322// 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
327var earlyBootJarsKey = NewOnceKey("earlyBootJars")
328