1// Copyright 2017 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 "fmt" 20 "strconv" 21 22 "android/soong/bazel" 23 "android/soong/starlark_fmt" 24) 25 26func init() { 27 RegisterSingletonType("api_levels", ApiLevelsSingleton) 28} 29 30const previewAPILevelBase = 9000 31 32// An API level, which may be a finalized (numbered) API, a preview (codenamed) 33// API, or the future API level (10000). Can be parsed from a string with 34// ApiLevelFromUser or ApiLevelOrPanic. 35// 36// The different *types* of API levels are handled separately. Currently only 37// Java has these, and they're managed with the SdkKind enum of the SdkSpec. A 38// future cleanup should be to migrate SdkSpec to using ApiLevel instead of its 39// SdkVersion int, and to move SdkSpec into this package. 40type ApiLevel struct { 41 // The string representation of the API level. 42 value string 43 44 // A number associated with the API level. The exact value depends on 45 // whether this API level is a preview or final API. 46 // 47 // For final API levels, this is the assigned version number. 48 // 49 // For preview API levels, this value has no meaning except to index known 50 // previews to determine ordering. 51 number int 52 53 // Identifies this API level as either a preview or final API level. 54 isPreview bool 55} 56 57func (this ApiLevel) FinalOrFutureInt() int { 58 if this.IsPreview() { 59 return FutureApiLevelInt 60 } else { 61 return this.number 62 } 63} 64 65// FinalOrPreviewInt distinguishes preview versions from "current" (future). 66// This is for "native" stubs and should be in sync with ndkstubgen/getApiLevelsMap(). 67// - "current" -> future (10000) 68// - preview codenames -> preview base (9000) + index 69// - otherwise -> cast to int 70func (this ApiLevel) FinalOrPreviewInt() int { 71 if this.IsCurrent() { 72 return this.number 73 } 74 if this.IsPreview() { 75 return previewAPILevelBase + this.number 76 } 77 return this.number 78} 79 80// Returns the canonical name for this API level. For a finalized API level 81// this will be the API number as a string. For a preview API level this 82// will be the codename, or "current". 83func (this ApiLevel) String() string { 84 return this.value 85} 86 87// Returns true if this is a non-final API level. 88func (this ApiLevel) IsPreview() bool { 89 return this.isPreview 90} 91 92// Returns true if this is the unfinalized "current" API level. This means 93// different things across Java and native. Java APIs do not use explicit 94// codenames, so all non-final codenames are grouped into "current". For native 95// explicit codenames are typically used, and current is the union of all 96// non-final APIs, including those that may not yet be in any codename. 97// 98// Note that in a build where the platform is final, "current" will not be a 99// preview API level but will instead be canonicalized to the final API level. 100func (this ApiLevel) IsCurrent() bool { 101 return this.value == "current" 102} 103 104func (this ApiLevel) IsNone() bool { 105 return this.number == -1 106} 107 108// Returns -1 if the current API level is less than the argument, 0 if they 109// are equal, and 1 if it is greater than the argument. 110func (this ApiLevel) CompareTo(other ApiLevel) int { 111 if this.IsPreview() && !other.IsPreview() { 112 return 1 113 } else if !this.IsPreview() && other.IsPreview() { 114 return -1 115 } 116 117 if this.number < other.number { 118 return -1 119 } else if this.number == other.number { 120 return 0 121 } else { 122 return 1 123 } 124} 125 126func (this ApiLevel) EqualTo(other ApiLevel) bool { 127 return this.CompareTo(other) == 0 128} 129 130func (this ApiLevel) GreaterThan(other ApiLevel) bool { 131 return this.CompareTo(other) > 0 132} 133 134func (this ApiLevel) GreaterThanOrEqualTo(other ApiLevel) bool { 135 return this.CompareTo(other) >= 0 136} 137 138func (this ApiLevel) LessThan(other ApiLevel) bool { 139 return this.CompareTo(other) < 0 140} 141 142func (this ApiLevel) LessThanOrEqualTo(other ApiLevel) bool { 143 return this.CompareTo(other) <= 0 144} 145 146func uncheckedFinalApiLevel(num int) ApiLevel { 147 return ApiLevel{ 148 value: strconv.Itoa(num), 149 number: num, 150 isPreview: false, 151 } 152} 153 154var NoneApiLevel = ApiLevel{ 155 value: "(no version)", 156 // Not 0 because we don't want this to compare equal with the first preview. 157 number: -1, 158 isPreview: true, 159} 160 161// The first version that introduced 64-bit ABIs. 162var FirstLp64Version = uncheckedFinalApiLevel(21) 163 164// Android has had various kinds of packed relocations over the years 165// (http://b/187907243). 166// 167// API level 30 is where the now-standard SHT_RELR is available. 168var FirstShtRelrVersion = uncheckedFinalApiLevel(30) 169 170// API level 28 introduced SHT_RELR when it was still Android-only, and used an 171// Android-specific relocation. 172var FirstAndroidRelrVersion = uncheckedFinalApiLevel(28) 173 174// API level 23 was when we first had the Chrome relocation packer, which is 175// obsolete and has been removed, but lld can now generate compatible packed 176// relocations itself. 177var FirstPackedRelocationsVersion = uncheckedFinalApiLevel(23) 178 179// The first API level that does not require NDK code to link 180// libandroid_support. 181var FirstNonLibAndroidSupportVersion = uncheckedFinalApiLevel(21) 182 183// LastWithoutModuleLibCoreSystemModules is the last API level where prebuilts/sdk does not contain 184// a core-for-system-modules.jar for the module-lib API scope. 185var LastWithoutModuleLibCoreSystemModules = uncheckedFinalApiLevel(31) 186 187// If the `raw` input is the codename of an API level has been finalized, this 188// function returns the API level number associated with that API level. If the 189// input is *not* a finalized codename, the input is returned unmodified. 190// 191// For example, at the time of writing, R has been finalized as API level 30, 192// but S is in development so it has no number assigned. For the following 193// inputs: 194// 195// * "30" -> "30" 196// * "R" -> "30" 197// * "S" -> "S" 198func ReplaceFinalizedCodenames(config Config, raw string) string { 199 num, ok := getFinalCodenamesMap(config)[raw] 200 if !ok { 201 return raw 202 } 203 204 return strconv.Itoa(num) 205} 206 207// ApiLevelFromUser converts the given string `raw` to an ApiLevel, possibly returning an error. 208// 209// `raw` must be non-empty. Passing an empty string results in a panic. 210// 211// "current" will return CurrentApiLevel, which is the ApiLevel associated with 212// an arbitrary future release (often referred to as API level 10000). 213// 214// Finalized codenames will be interpreted as their final API levels, not the 215// preview of the associated releases. R is now API 30, not the R preview. 216// 217// Future codenames return a preview API level that has no associated integer. 218// 219// Inputs that are not "current", known previews, or convertible to an integer 220// will return an error. 221func ApiLevelFromUser(ctx PathContext, raw string) (ApiLevel, error) { 222 return ApiLevelFromUserWithConfig(ctx.Config(), raw) 223} 224 225// ApiLevelFromUserWithConfig implements ApiLevelFromUser, see comments for 226// ApiLevelFromUser for more details. 227func ApiLevelFromUserWithConfig(config Config, raw string) (ApiLevel, error) { 228 if raw == "" { 229 panic("API level string must be non-empty") 230 } 231 232 if raw == "current" { 233 return FutureApiLevel, nil 234 } 235 236 for _, preview := range config.PreviewApiLevels() { 237 if raw == preview.String() { 238 return preview, nil 239 } 240 } 241 242 canonical := ReplaceFinalizedCodenames(config, raw) 243 asInt, err := strconv.Atoi(canonical) 244 if err != nil { 245 return NoneApiLevel, fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", canonical) 246 } 247 248 apiLevel := uncheckedFinalApiLevel(asInt) 249 return apiLevel, nil 250} 251 252// ApiLevelForTest returns an ApiLevel constructed from the supplied raw string. 253// 254// This only supports "current" and numeric levels, code names are not supported. 255func ApiLevelForTest(raw string) ApiLevel { 256 if raw == "" { 257 panic("API level string must be non-empty") 258 } 259 260 if raw == "current" { 261 return FutureApiLevel 262 } 263 264 asInt, err := strconv.Atoi(raw) 265 if err != nil { 266 panic(fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", raw)) 267 } 268 269 apiLevel := uncheckedFinalApiLevel(asInt) 270 return apiLevel 271} 272 273// Converts an API level string `raw` into an ApiLevel in the same method as 274// `ApiLevelFromUser`, but the input is assumed to have no errors and any errors 275// will panic instead of returning an error. 276func ApiLevelOrPanic(ctx PathContext, raw string) ApiLevel { 277 value, err := ApiLevelFromUser(ctx, raw) 278 if err != nil { 279 panic(err.Error()) 280 } 281 return value 282} 283 284func ApiLevelsSingleton() Singleton { 285 return &apiLevelsSingleton{} 286} 287 288type apiLevelsSingleton struct{} 289 290func createApiLevelsJson(ctx SingletonContext, file WritablePath, 291 apiLevelsMap map[string]int) { 292 293 jsonStr, err := json.Marshal(apiLevelsMap) 294 if err != nil { 295 ctx.Errorf(err.Error()) 296 } 297 298 WriteFileRule(ctx, file, string(jsonStr)) 299} 300 301func GetApiLevelsJson(ctx PathContext) WritablePath { 302 return PathForOutput(ctx, "api_levels.json") 303} 304 305var finalCodenamesMapKey = NewOnceKey("FinalCodenamesMap") 306 307func getFinalCodenamesMap(config Config) map[string]int { 308 return config.Once(finalCodenamesMapKey, func() interface{} { 309 apiLevelsMap := map[string]int{ 310 "G": 9, 311 "I": 14, 312 "J": 16, 313 "J-MR1": 17, 314 "J-MR2": 18, 315 "K": 19, 316 "L": 21, 317 "L-MR1": 22, 318 "M": 23, 319 "N": 24, 320 "N-MR1": 25, 321 "O": 26, 322 "O-MR1": 27, 323 "P": 28, 324 "Q": 29, 325 "R": 30, 326 "S": 31, 327 "S-V2": 32, 328 "Tiramisu": 33, 329 } 330 331 // TODO: Differentiate "current" and "future". 332 // The code base calls it FutureApiLevel, but the spelling is "current", 333 // and these are really two different things. When defining APIs it 334 // means the API has not yet been added to a specific release. When 335 // choosing an API level to build for it means that the future API level 336 // should be used, except in the case where the build is finalized in 337 // which case the platform version should be used. This is *weird*, 338 // because in the circumstance where API foo was added in R and bar was 339 // added in S, both of these are usable when building for "current" when 340 // neither R nor S are final, but the S APIs stop being available in a 341 // final R build. 342 if Bool(config.productVariables.Platform_sdk_final) { 343 apiLevelsMap["current"] = config.PlatformSdkVersion().FinalOrFutureInt() 344 } 345 346 return apiLevelsMap 347 }).(map[string]int) 348} 349 350var apiLevelsMapKey = NewOnceKey("ApiLevelsMap") 351 352func GetApiLevelsMap(config Config) map[string]int { 353 return config.Once(apiLevelsMapKey, func() interface{} { 354 apiLevelsMap := map[string]int{ 355 "G": 9, 356 "I": 14, 357 "J": 16, 358 "J-MR1": 17, 359 "J-MR2": 18, 360 "K": 19, 361 "L": 21, 362 "L-MR1": 22, 363 "M": 23, 364 "N": 24, 365 "N-MR1": 25, 366 "O": 26, 367 "O-MR1": 27, 368 "P": 28, 369 "Q": 29, 370 "R": 30, 371 "S": 31, 372 "S-V2": 32, 373 "Tiramisu": 33, 374 } 375 for i, codename := range config.PlatformVersionActiveCodenames() { 376 apiLevelsMap[codename] = previewAPILevelBase + i 377 } 378 379 return apiLevelsMap 380 }).(map[string]int) 381} 382 383func (a *apiLevelsSingleton) GenerateBuildActions(ctx SingletonContext) { 384 apiLevelsMap := GetApiLevelsMap(ctx.Config()) 385 apiLevelsJson := GetApiLevelsJson(ctx) 386 createApiLevelsJson(ctx, apiLevelsJson, apiLevelsMap) 387} 388 389func printApiLevelsStarlarkDict(config Config) string { 390 apiLevelsMap := GetApiLevelsMap(config) 391 valDict := make(map[string]string, len(apiLevelsMap)) 392 for k, v := range apiLevelsMap { 393 valDict[k] = strconv.Itoa(v) 394 } 395 return starlark_fmt.PrintDict(valDict, 0) 396} 397 398func StarlarkApiLevelConfigs(config Config) string { 399 return fmt.Sprintf(bazel.GeneratedBazelFileWarning+` 400_api_levels = %s 401 402api_levels = _api_levels 403`, printApiLevelsStarlarkDict(config), 404 ) 405} 406