1# Copyright (C) 2023 The Android Open Source Project 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 15# An API level, can be a finalized (numbered) API, a preview (codenamed) API, or 16# the future API level (10000). Can be parsed from a string with 17# parse_api_level_with_version. 18 19load("@bazel_skylib//lib:dicts.bzl", "dicts") 20load("@soong_injection//api_levels:platform_versions.bzl", "platform_versions") 21load(":api_constants.bzl", "api_levels_released_versions") 22 23_NONE_API_LEVEL_INT = -1 24_PREVIEW_API_LEVEL_BASE = 9000 # Base constant for preview API levels. 25_FUTURE_API_LEVEL_INT = 10000 # API Level associated with an arbitrary future release 26 27# Dict of unfinalized codenames to a placeholder preview API int. 28def _preview_codenames_to_ints(platform_sdk_variables): 29 return { 30 codename: _PREVIEW_API_LEVEL_BASE + i 31 for i, codename in enumerate(platform_sdk_variables.platform_version_active_codenames) 32 } 33 34# Returns true if a string or int version is in preview (not finalized). 35def _is_preview(version, platform_sdk_variables): 36 preview_codenames_to_ints = _preview_codenames_to_ints(platform_sdk_variables) 37 if type(version) == "string" and version.isdigit(): 38 # normalize int types internally 39 version = int(version) 40 41 # Future / current / none is considered as a preview. 42 if version in ("current", "(no version)", _FUTURE_API_LEVEL_INT, _NONE_API_LEVEL_INT): 43 return True 44 45 # api can be either the codename or the int level (9000+) 46 return version in preview_codenames_to_ints or version in preview_codenames_to_ints.values() 47 48# Return 10000 for unfinalized versions, otherwise return unchanged. 49def _final_or_future(version, platform_sdk_variables): 50 if _is_preview(version = version, platform_sdk_variables = platform_sdk_variables): 51 return _FUTURE_API_LEVEL_INT 52 else: 53 return version 54 55def _api_levels_with_previews(platform_sdk_variables): 56 return dicts.add( 57 api_levels_released_versions, 58 _preview_codenames_to_ints(platform_sdk_variables), 59 ) 60 61# @unused 62def _api_levels_with_final_codenames(platform_sdk_variables): 63 if platform_sdk_variables.platform_sdk_final and platform_sdk_variables.platform_sdk_version: 64 return api_levels_released_versions 65 return dicts.add( 66 api_levels_released_versions, 67 {"current": _final_or_future( 68 version = platform_sdk_variables.platform_sdk_version, 69 platform_sdk_variables = platform_sdk_variables, 70 )}, 71 ) 72 73# parse_api_level_from_version is a Starlark implementation of ApiLevelFromUser 74# at https://cs.android.com/android/platform/superproject/+/master:build/soong/android/api_levels.go;l=221-250;drc=5095a6c4b484f34d5c4f55a855d6174e00fb7f5e 75def _parse_api_level_from_version(version, platform_sdk_variables): 76 """converts the given string `version` to an api level 77 78 Args: 79 version: must be non-empty. Inputs that are not "current", known 80 previews, finalized codenames, or convertible to an integer will return 81 an error. 82 83 Returns: The api level as an int. 84 """ 85 if version == "": 86 fail("API level string must be non-empty") 87 88 if version == "current": 89 return _FUTURE_API_LEVEL_INT 90 91 if _is_preview(version = version, platform_sdk_variables = platform_sdk_variables): 92 return _preview_codenames_to_ints(platform_sdk_variables).get(version) or int(version) 93 94 # Not preview nor current. 95 # 96 # If the level is the codename of an API level that has been finalized, this 97 # function returns the API level number associated with that API level. If 98 # the input is *not* a finalized codename, the input is returned unmodified. 99 canonical_level = api_levels_released_versions.get(version) 100 if not canonical_level: 101 if not version.isdigit(): 102 fail("version %s could not be parsed as integer and is not a recognized codename" % version) 103 return int(version) 104 return canonical_level 105 106def _default_app_target_sdk_string(platform_sdk_variables): 107 if platform_sdk_variables.platform_sdk_final: 108 return str(platform_sdk_variables.platform_sdk_version) 109 110 if not platform_sdk_variables.platform_sdk_codename: 111 # soong returns NoneApiLevel here value: "(no version)", number: -1, isPreview: true 112 # 113 # fail fast instead of returning an arbitrary value. 114 fail("Platform_sdk_codename must be set.") 115 116 if platform_sdk_variables.platform_sdk_codename == "REL": 117 fail("Platform_sdk_codename should not be REL when Platform_sdk_final is false") 118 119 return platform_sdk_variables.platform_sdk_codename 120 121# Starlark implementation of DefaultAppTargetSDK from build/soong/android/config.go 122# https://cs.android.com/android/platform/superproject/+/master:build/soong/android/config.go;l=875-889;drc=b0dc477ef740ec959548fe5517bd92ac4ea0325c 123# check what you want returned for codename == "" case before using 124def _default_app_target_sdk(platform_sdk_variables): 125 """default_app_target_sdk returns the API level that platform apps are targeting. 126 This converts a codename to the exact ApiLevel it represents. 127 """ 128 return _parse_api_level_from_version( 129 version = _default_app_target_sdk_string(platform_sdk_variables), 130 platform_sdk_variables = platform_sdk_variables, 131 ) 132 133# Starlark implementation of EffectiveVersionString from build/soong/android/api_levels.go 134# EffectiveVersionString converts an api level string into the concrete version string that the module 135# should use. For modules targeting an unreleased SDK (meaning it does not yet have a number) 136# it returns the codename (P, Q, R, etc.) 137def _effective_version_string( 138 version, 139 platform_sdk_variables): 140 if not _is_preview(version, platform_sdk_variables): 141 return version 142 default_app_target_sdk_string = _default_app_target_sdk_string(platform_sdk_variables) 143 if not _is_preview(default_app_target_sdk_string, platform_sdk_variables): 144 return default_app_target_sdk_string 145 if version in platform_sdk_variables.platform_version_active_codenames: 146 return version 147 return default_app_target_sdk_string 148 149def api_from_product(platform_sdk_variables): 150 """Provides api level-related utility functions from platform variables. 151 152 Args: 153 platform_sdk_variables: a struct that must provides the 4 154 product variables: platform_sdk_final (boolean), 155 platform_sdk_version (int), platform_sdk_codename (string), 156 platform_version_active_codenames (string list) 157 158 Returns: A struct containing utility functions and constants 159 around api levels, e.g. for parsing them from user input and for 160 overriding them based on defaults and the input product variables. 161 """ 162 return struct( 163 NONE_API_LEVEL = _NONE_API_LEVEL_INT, 164 FUTURE_API_LEVEL = _FUTURE_API_LEVEL_INT, 165 is_preview = lambda version: _is_preview( 166 version = version, 167 platform_sdk_variables = platform_sdk_variables, 168 ), 169 final_or_future = lambda version: _final_or_future( 170 version = version, 171 platform_sdk_variables = platform_sdk_variables, 172 ), 173 default_app_target_sdk_string = lambda: _default_app_target_sdk_string(platform_sdk_variables), 174 default_app_target_sdk = lambda: _default_app_target_sdk(platform_sdk_variables), 175 parse_api_level_from_version = lambda version: _parse_api_level_from_version( 176 version = version, 177 platform_sdk_variables = platform_sdk_variables, 178 ), 179 api_levels = _api_levels_with_previews(platform_sdk_variables), 180 effective_version_string = lambda version: _effective_version_string( 181 version = version, 182 platform_sdk_variables = platform_sdk_variables, 183 ), 184 ) 185 186# TODO(b/300428335): access these variables in a transition friendly way. 187api = api_from_product(platform_versions) 188