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