1*60517a1eSAndroid Build Coastguard Worker# Copyright 2023 The Bazel Authors. All rights reserved. 2*60517a1eSAndroid Build Coastguard Worker# 3*60517a1eSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*60517a1eSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*60517a1eSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*60517a1eSAndroid Build Coastguard Worker# 7*60517a1eSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*60517a1eSAndroid Build Coastguard Worker# 9*60517a1eSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*60517a1eSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*60517a1eSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*60517a1eSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*60517a1eSAndroid Build Coastguard Worker# limitations under the License. 14*60517a1eSAndroid Build Coastguard Worker 15*60517a1eSAndroid Build Coastguard Worker"Python toolchain module extensions for use with bzlmod." 16*60517a1eSAndroid Build Coastguard Worker 17*60517a1eSAndroid Build Coastguard Workerload("@bazel_features//:features.bzl", "bazel_features") 18*60517a1eSAndroid Build Coastguard Workerload("//python:versions.bzl", "DEFAULT_RELEASE_BASE_URL", "PLATFORMS", "TOOL_VERSIONS") 19*60517a1eSAndroid Build Coastguard Workerload(":auth.bzl", "AUTH_ATTRS") 20*60517a1eSAndroid Build Coastguard Workerload(":full_version.bzl", "full_version") 21*60517a1eSAndroid Build Coastguard Workerload(":python_register_toolchains.bzl", "python_register_toolchains") 22*60517a1eSAndroid Build Coastguard Workerload(":pythons_hub.bzl", "hub_repo") 23*60517a1eSAndroid Build Coastguard Workerload(":repo_utils.bzl", "repo_utils") 24*60517a1eSAndroid Build Coastguard Workerload(":semver.bzl", "semver") 25*60517a1eSAndroid Build Coastguard Workerload(":text_util.bzl", "render") 26*60517a1eSAndroid Build Coastguard Workerload(":toolchains_repo.bzl", "multi_toolchain_aliases") 27*60517a1eSAndroid Build Coastguard Workerload(":util.bzl", "IS_BAZEL_6_4_OR_HIGHER") 28*60517a1eSAndroid Build Coastguard Worker 29*60517a1eSAndroid Build Coastguard Worker# This limit can be increased essentially arbitrarily, but doing so will cause a rebuild of all 30*60517a1eSAndroid Build Coastguard Worker# targets using any of these toolchains due to the changed repository name. 31*60517a1eSAndroid Build Coastguard Worker_MAX_NUM_TOOLCHAINS = 9999 32*60517a1eSAndroid Build Coastguard Worker_TOOLCHAIN_INDEX_PAD_LENGTH = len(str(_MAX_NUM_TOOLCHAINS)) 33*60517a1eSAndroid Build Coastguard Worker 34*60517a1eSAndroid Build Coastguard Workerdef parse_modules(*, module_ctx, _fail = fail): 35*60517a1eSAndroid Build Coastguard Worker """Parse the modules and return a struct for registrations. 36*60517a1eSAndroid Build Coastguard Worker 37*60517a1eSAndroid Build Coastguard Worker Args: 38*60517a1eSAndroid Build Coastguard Worker module_ctx: {type}`module_ctx` module context. 39*60517a1eSAndroid Build Coastguard Worker _fail: {type}`function` the failure function, mainly for testing. 40*60517a1eSAndroid Build Coastguard Worker 41*60517a1eSAndroid Build Coastguard Worker Returns: 42*60517a1eSAndroid Build Coastguard Worker A struct with the following attributes: 43*60517a1eSAndroid Build Coastguard Worker * `toolchains`: The list of toolchains to register. The last 44*60517a1eSAndroid Build Coastguard Worker element is special and is treated as the default toolchain. 45*60517a1eSAndroid Build Coastguard Worker * `defaults`: The default `kwargs` passed to 46*60517a1eSAndroid Build Coastguard Worker {bzl:obj}`python_register_toolchains`. 47*60517a1eSAndroid Build Coastguard Worker * `debug_info`: {type}`None | dict` extra information to be passed 48*60517a1eSAndroid Build Coastguard Worker to the debug repo. 49*60517a1eSAndroid Build Coastguard Worker """ 50*60517a1eSAndroid Build Coastguard Worker if module_ctx.os.environ.get("RULES_PYTHON_BZLMOD_DEBUG", "0") == "1": 51*60517a1eSAndroid Build Coastguard Worker debug_info = { 52*60517a1eSAndroid Build Coastguard Worker "toolchains_registered": [], 53*60517a1eSAndroid Build Coastguard Worker } 54*60517a1eSAndroid Build Coastguard Worker else: 55*60517a1eSAndroid Build Coastguard Worker debug_info = None 56*60517a1eSAndroid Build Coastguard Worker 57*60517a1eSAndroid Build Coastguard Worker # The toolchain_info structs to register, in the order to register them in. 58*60517a1eSAndroid Build Coastguard Worker # NOTE: The last element is special: it is treated as the default toolchain, 59*60517a1eSAndroid Build Coastguard Worker # so there is special handling to ensure the last entry is the correct one. 60*60517a1eSAndroid Build Coastguard Worker toolchains = [] 61*60517a1eSAndroid Build Coastguard Worker 62*60517a1eSAndroid Build Coastguard Worker # We store the default toolchain separately to ensure it is the last 63*60517a1eSAndroid Build Coastguard Worker # toolchain added to toolchains. 64*60517a1eSAndroid Build Coastguard Worker # This is a toolchain_info struct. 65*60517a1eSAndroid Build Coastguard Worker default_toolchain = None 66*60517a1eSAndroid Build Coastguard Worker 67*60517a1eSAndroid Build Coastguard Worker # Map of string Major.Minor or Major.Minor.Patch to the toolchain_info struct 68*60517a1eSAndroid Build Coastguard Worker global_toolchain_versions = {} 69*60517a1eSAndroid Build Coastguard Worker 70*60517a1eSAndroid Build Coastguard Worker ignore_root_user_error = None 71*60517a1eSAndroid Build Coastguard Worker 72*60517a1eSAndroid Build Coastguard Worker logger = repo_utils.logger(module_ctx, "python") 73*60517a1eSAndroid Build Coastguard Worker 74*60517a1eSAndroid Build Coastguard Worker # if the root module does not register any toolchain then the 75*60517a1eSAndroid Build Coastguard Worker # ignore_root_user_error takes its default value: False 76*60517a1eSAndroid Build Coastguard Worker if not module_ctx.modules[0].tags.toolchain: 77*60517a1eSAndroid Build Coastguard Worker ignore_root_user_error = False 78*60517a1eSAndroid Build Coastguard Worker 79*60517a1eSAndroid Build Coastguard Worker config = _get_toolchain_config(modules = module_ctx.modules, _fail = _fail) 80*60517a1eSAndroid Build Coastguard Worker 81*60517a1eSAndroid Build Coastguard Worker seen_versions = {} 82*60517a1eSAndroid Build Coastguard Worker for mod in module_ctx.modules: 83*60517a1eSAndroid Build Coastguard Worker module_toolchain_versions = [] 84*60517a1eSAndroid Build Coastguard Worker toolchain_attr_structs = _create_toolchain_attr_structs( 85*60517a1eSAndroid Build Coastguard Worker mod = mod, 86*60517a1eSAndroid Build Coastguard Worker seen_versions = seen_versions, 87*60517a1eSAndroid Build Coastguard Worker config = config, 88*60517a1eSAndroid Build Coastguard Worker ) 89*60517a1eSAndroid Build Coastguard Worker 90*60517a1eSAndroid Build Coastguard Worker for toolchain_attr in toolchain_attr_structs: 91*60517a1eSAndroid Build Coastguard Worker toolchain_version = toolchain_attr.python_version 92*60517a1eSAndroid Build Coastguard Worker toolchain_name = "python_" + toolchain_version.replace(".", "_") 93*60517a1eSAndroid Build Coastguard Worker 94*60517a1eSAndroid Build Coastguard Worker # Duplicate versions within a module indicate a misconfigured module. 95*60517a1eSAndroid Build Coastguard Worker if toolchain_version in module_toolchain_versions: 96*60517a1eSAndroid Build Coastguard Worker _fail_duplicate_module_toolchain_version(toolchain_version, mod.name) 97*60517a1eSAndroid Build Coastguard Worker module_toolchain_versions.append(toolchain_version) 98*60517a1eSAndroid Build Coastguard Worker 99*60517a1eSAndroid Build Coastguard Worker if mod.is_root: 100*60517a1eSAndroid Build Coastguard Worker # Only the root module and rules_python are allowed to specify the default 101*60517a1eSAndroid Build Coastguard Worker # toolchain for a couple reasons: 102*60517a1eSAndroid Build Coastguard Worker # * It prevents submodules from specifying different defaults and only 103*60517a1eSAndroid Build Coastguard Worker # one of them winning. 104*60517a1eSAndroid Build Coastguard Worker # * rules_python needs to set a soft default in case the root module doesn't, 105*60517a1eSAndroid Build Coastguard Worker # e.g. if the root module doesn't use Python itself. 106*60517a1eSAndroid Build Coastguard Worker # * The root module is allowed to override the rules_python default. 107*60517a1eSAndroid Build Coastguard Worker is_default = toolchain_attr.is_default 108*60517a1eSAndroid Build Coastguard Worker 109*60517a1eSAndroid Build Coastguard Worker # Also only the root module should be able to decide ignore_root_user_error. 110*60517a1eSAndroid Build Coastguard Worker # Modules being depended upon don't know the final environment, so they aren't 111*60517a1eSAndroid Build Coastguard Worker # in the right position to know or decide what the correct setting is. 112*60517a1eSAndroid Build Coastguard Worker 113*60517a1eSAndroid Build Coastguard Worker # If an inconsistency in the ignore_root_user_error among multiple toolchains is detected, fail. 114*60517a1eSAndroid Build Coastguard Worker if ignore_root_user_error != None and toolchain_attr.ignore_root_user_error != ignore_root_user_error: 115*60517a1eSAndroid Build Coastguard Worker fail("Toolchains in the root module must have consistent 'ignore_root_user_error' attributes") 116*60517a1eSAndroid Build Coastguard Worker 117*60517a1eSAndroid Build Coastguard Worker ignore_root_user_error = toolchain_attr.ignore_root_user_error 118*60517a1eSAndroid Build Coastguard Worker elif mod.name == "rules_python" and not default_toolchain: 119*60517a1eSAndroid Build Coastguard Worker # We don't do the len() check because we want the default that rules_python 120*60517a1eSAndroid Build Coastguard Worker # sets to be clearly visible. 121*60517a1eSAndroid Build Coastguard Worker is_default = toolchain_attr.is_default 122*60517a1eSAndroid Build Coastguard Worker else: 123*60517a1eSAndroid Build Coastguard Worker is_default = False 124*60517a1eSAndroid Build Coastguard Worker 125*60517a1eSAndroid Build Coastguard Worker if is_default and default_toolchain != None: 126*60517a1eSAndroid Build Coastguard Worker _fail_multiple_default_toolchains( 127*60517a1eSAndroid Build Coastguard Worker first = default_toolchain.name, 128*60517a1eSAndroid Build Coastguard Worker second = toolchain_name, 129*60517a1eSAndroid Build Coastguard Worker ) 130*60517a1eSAndroid Build Coastguard Worker 131*60517a1eSAndroid Build Coastguard Worker # Ignore version collisions in the global scope because there isn't 132*60517a1eSAndroid Build Coastguard Worker # much else that can be done. Modules don't know and can't control 133*60517a1eSAndroid Build Coastguard Worker # what other modules do, so the first in the dependency graph wins. 134*60517a1eSAndroid Build Coastguard Worker if toolchain_version in global_toolchain_versions: 135*60517a1eSAndroid Build Coastguard Worker # If the python version is explicitly provided by the root 136*60517a1eSAndroid Build Coastguard Worker # module, they should not be warned for choosing the same 137*60517a1eSAndroid Build Coastguard Worker # version that rules_python provides as default. 138*60517a1eSAndroid Build Coastguard Worker first = global_toolchain_versions[toolchain_version] 139*60517a1eSAndroid Build Coastguard Worker if mod.name != "rules_python" or not first.module.is_root: 140*60517a1eSAndroid Build Coastguard Worker # The warning can be enabled by setting the verbosity: 141*60517a1eSAndroid Build Coastguard Worker # env RULES_PYTHON_REPO_DEBUG_VERBOSITY=INFO bazel build //... 142*60517a1eSAndroid Build Coastguard Worker _warn_duplicate_global_toolchain_version( 143*60517a1eSAndroid Build Coastguard Worker toolchain_version, 144*60517a1eSAndroid Build Coastguard Worker first = first, 145*60517a1eSAndroid Build Coastguard Worker second_toolchain_name = toolchain_name, 146*60517a1eSAndroid Build Coastguard Worker second_module_name = mod.name, 147*60517a1eSAndroid Build Coastguard Worker logger = logger, 148*60517a1eSAndroid Build Coastguard Worker ) 149*60517a1eSAndroid Build Coastguard Worker toolchain_info = None 150*60517a1eSAndroid Build Coastguard Worker else: 151*60517a1eSAndroid Build Coastguard Worker toolchain_info = struct( 152*60517a1eSAndroid Build Coastguard Worker python_version = toolchain_attr.python_version, 153*60517a1eSAndroid Build Coastguard Worker name = toolchain_name, 154*60517a1eSAndroid Build Coastguard Worker register_coverage_tool = toolchain_attr.configure_coverage_tool, 155*60517a1eSAndroid Build Coastguard Worker module = struct(name = mod.name, is_root = mod.is_root), 156*60517a1eSAndroid Build Coastguard Worker ) 157*60517a1eSAndroid Build Coastguard Worker global_toolchain_versions[toolchain_version] = toolchain_info 158*60517a1eSAndroid Build Coastguard Worker if debug_info: 159*60517a1eSAndroid Build Coastguard Worker debug_info["toolchains_registered"].append({ 160*60517a1eSAndroid Build Coastguard Worker "ignore_root_user_error": ignore_root_user_error, 161*60517a1eSAndroid Build Coastguard Worker "module": {"is_root": mod.is_root, "name": mod.name}, 162*60517a1eSAndroid Build Coastguard Worker "name": toolchain_name, 163*60517a1eSAndroid Build Coastguard Worker }) 164*60517a1eSAndroid Build Coastguard Worker 165*60517a1eSAndroid Build Coastguard Worker if is_default: 166*60517a1eSAndroid Build Coastguard Worker # This toolchain is setting the default, but the actual 167*60517a1eSAndroid Build Coastguard Worker # registration was performed previously, by a different module. 168*60517a1eSAndroid Build Coastguard Worker if toolchain_info == None: 169*60517a1eSAndroid Build Coastguard Worker default_toolchain = global_toolchain_versions[toolchain_version] 170*60517a1eSAndroid Build Coastguard Worker 171*60517a1eSAndroid Build Coastguard Worker # Remove it because later code will add it at the end to 172*60517a1eSAndroid Build Coastguard Worker # ensure it is last in the list. 173*60517a1eSAndroid Build Coastguard Worker toolchains.remove(default_toolchain) 174*60517a1eSAndroid Build Coastguard Worker else: 175*60517a1eSAndroid Build Coastguard Worker default_toolchain = toolchain_info 176*60517a1eSAndroid Build Coastguard Worker elif toolchain_info: 177*60517a1eSAndroid Build Coastguard Worker toolchains.append(toolchain_info) 178*60517a1eSAndroid Build Coastguard Worker 179*60517a1eSAndroid Build Coastguard Worker config.default.setdefault("ignore_root_user_error", ignore_root_user_error) 180*60517a1eSAndroid Build Coastguard Worker 181*60517a1eSAndroid Build Coastguard Worker # A default toolchain is required so that the non-version-specific rules 182*60517a1eSAndroid Build Coastguard Worker # are able to match a toolchain. 183*60517a1eSAndroid Build Coastguard Worker if default_toolchain == None: 184*60517a1eSAndroid Build Coastguard Worker fail("No default Python toolchain configured. Is rules_python missing `is_default=True`?") 185*60517a1eSAndroid Build Coastguard Worker elif default_toolchain.python_version not in global_toolchain_versions: 186*60517a1eSAndroid Build Coastguard Worker fail('Default version "{python_version}" selected by module ' + 187*60517a1eSAndroid Build Coastguard Worker '"{module_name}", but no toolchain with that version registered'.format( 188*60517a1eSAndroid Build Coastguard Worker python_version = default_toolchain.python_version, 189*60517a1eSAndroid Build Coastguard Worker module_name = default_toolchain.module.name, 190*60517a1eSAndroid Build Coastguard Worker )) 191*60517a1eSAndroid Build Coastguard Worker 192*60517a1eSAndroid Build Coastguard Worker # The last toolchain in the BUILD file is set as the default 193*60517a1eSAndroid Build Coastguard Worker # toolchain. We need the default last. 194*60517a1eSAndroid Build Coastguard Worker toolchains.append(default_toolchain) 195*60517a1eSAndroid Build Coastguard Worker 196*60517a1eSAndroid Build Coastguard Worker if len(toolchains) > _MAX_NUM_TOOLCHAINS: 197*60517a1eSAndroid Build Coastguard Worker fail("more than {} python versions are not supported".format(_MAX_NUM_TOOLCHAINS)) 198*60517a1eSAndroid Build Coastguard Worker 199*60517a1eSAndroid Build Coastguard Worker return struct( 200*60517a1eSAndroid Build Coastguard Worker config = config, 201*60517a1eSAndroid Build Coastguard Worker debug_info = debug_info, 202*60517a1eSAndroid Build Coastguard Worker default_python_version = toolchains[-1].python_version, 203*60517a1eSAndroid Build Coastguard Worker toolchains = [ 204*60517a1eSAndroid Build Coastguard Worker struct( 205*60517a1eSAndroid Build Coastguard Worker python_version = t.python_version, 206*60517a1eSAndroid Build Coastguard Worker name = t.name, 207*60517a1eSAndroid Build Coastguard Worker register_coverage_tool = t.register_coverage_tool, 208*60517a1eSAndroid Build Coastguard Worker ) 209*60517a1eSAndroid Build Coastguard Worker for t in toolchains 210*60517a1eSAndroid Build Coastguard Worker ], 211*60517a1eSAndroid Build Coastguard Worker ) 212*60517a1eSAndroid Build Coastguard Worker 213*60517a1eSAndroid Build Coastguard Workerdef _python_impl(module_ctx): 214*60517a1eSAndroid Build Coastguard Worker py = parse_modules(module_ctx = module_ctx) 215*60517a1eSAndroid Build Coastguard Worker 216*60517a1eSAndroid Build Coastguard Worker for toolchain_info in py.toolchains: 217*60517a1eSAndroid Build Coastguard Worker # Ensure that we pass the full version here. 218*60517a1eSAndroid Build Coastguard Worker full_python_version = full_version( 219*60517a1eSAndroid Build Coastguard Worker version = toolchain_info.python_version, 220*60517a1eSAndroid Build Coastguard Worker minor_mapping = py.config.minor_mapping, 221*60517a1eSAndroid Build Coastguard Worker ) 222*60517a1eSAndroid Build Coastguard Worker kwargs = { 223*60517a1eSAndroid Build Coastguard Worker "python_version": full_python_version, 224*60517a1eSAndroid Build Coastguard Worker "register_coverage_tool": toolchain_info.register_coverage_tool, 225*60517a1eSAndroid Build Coastguard Worker } 226*60517a1eSAndroid Build Coastguard Worker 227*60517a1eSAndroid Build Coastguard Worker # Allow overrides per python version 228*60517a1eSAndroid Build Coastguard Worker kwargs.update(py.config.kwargs.get(toolchain_info.python_version, {})) 229*60517a1eSAndroid Build Coastguard Worker kwargs.update(py.config.kwargs.get(full_python_version, {})) 230*60517a1eSAndroid Build Coastguard Worker kwargs.update(py.config.default) 231*60517a1eSAndroid Build Coastguard Worker python_register_toolchains(name = toolchain_info.name, **kwargs) 232*60517a1eSAndroid Build Coastguard Worker 233*60517a1eSAndroid Build Coastguard Worker # Create the pythons_hub repo for the interpreter meta data and the 234*60517a1eSAndroid Build Coastguard Worker # the various toolchains. 235*60517a1eSAndroid Build Coastguard Worker hub_repo( 236*60517a1eSAndroid Build Coastguard Worker name = "pythons_hub", 237*60517a1eSAndroid Build Coastguard Worker # Last toolchain is default 238*60517a1eSAndroid Build Coastguard Worker default_python_version = py.default_python_version, 239*60517a1eSAndroid Build Coastguard Worker toolchain_prefixes = [ 240*60517a1eSAndroid Build Coastguard Worker render.toolchain_prefix(index, toolchain.name, _TOOLCHAIN_INDEX_PAD_LENGTH) 241*60517a1eSAndroid Build Coastguard Worker for index, toolchain in enumerate(py.toolchains) 242*60517a1eSAndroid Build Coastguard Worker ], 243*60517a1eSAndroid Build Coastguard Worker toolchain_python_versions = [ 244*60517a1eSAndroid Build Coastguard Worker full_version(version = t.python_version, minor_mapping = py.config.minor_mapping) 245*60517a1eSAndroid Build Coastguard Worker for t in py.toolchains 246*60517a1eSAndroid Build Coastguard Worker ], 247*60517a1eSAndroid Build Coastguard Worker # The last toolchain is the default; it can't have version constraints 248*60517a1eSAndroid Build Coastguard Worker # Despite the implication of the arg name, the values are strs, not bools 249*60517a1eSAndroid Build Coastguard Worker toolchain_set_python_version_constraints = [ 250*60517a1eSAndroid Build Coastguard Worker "True" if i != len(py.toolchains) - 1 else "False" 251*60517a1eSAndroid Build Coastguard Worker for i in range(len(py.toolchains)) 252*60517a1eSAndroid Build Coastguard Worker ], 253*60517a1eSAndroid Build Coastguard Worker toolchain_user_repository_names = [t.name for t in py.toolchains], 254*60517a1eSAndroid Build Coastguard Worker ) 255*60517a1eSAndroid Build Coastguard Worker 256*60517a1eSAndroid Build Coastguard Worker # This is require in order to support multiple version py_test 257*60517a1eSAndroid Build Coastguard Worker # and py_binary 258*60517a1eSAndroid Build Coastguard Worker multi_toolchain_aliases( 259*60517a1eSAndroid Build Coastguard Worker name = "python_versions", 260*60517a1eSAndroid Build Coastguard Worker python_versions = { 261*60517a1eSAndroid Build Coastguard Worker toolchain.python_version: toolchain.name 262*60517a1eSAndroid Build Coastguard Worker for toolchain in py.toolchains 263*60517a1eSAndroid Build Coastguard Worker }, 264*60517a1eSAndroid Build Coastguard Worker ) 265*60517a1eSAndroid Build Coastguard Worker 266*60517a1eSAndroid Build Coastguard Worker if py.debug_info != None: 267*60517a1eSAndroid Build Coastguard Worker _debug_repo( 268*60517a1eSAndroid Build Coastguard Worker name = "rules_python_bzlmod_debug", 269*60517a1eSAndroid Build Coastguard Worker debug_info = json.encode_indent(py.debug_info), 270*60517a1eSAndroid Build Coastguard Worker ) 271*60517a1eSAndroid Build Coastguard Worker 272*60517a1eSAndroid Build Coastguard Worker if bazel_features.external_deps.extension_metadata_has_reproducible: 273*60517a1eSAndroid Build Coastguard Worker return module_ctx.extension_metadata(reproducible = True) 274*60517a1eSAndroid Build Coastguard Worker else: 275*60517a1eSAndroid Build Coastguard Worker return None 276*60517a1eSAndroid Build Coastguard Worker 277*60517a1eSAndroid Build Coastguard Workerdef _fail_duplicate_module_toolchain_version(version, module): 278*60517a1eSAndroid Build Coastguard Worker fail(("Duplicate module toolchain version: module '{module}' attempted " + 279*60517a1eSAndroid Build Coastguard Worker "to use version '{version}' multiple times in itself").format( 280*60517a1eSAndroid Build Coastguard Worker version = version, 281*60517a1eSAndroid Build Coastguard Worker module = module, 282*60517a1eSAndroid Build Coastguard Worker )) 283*60517a1eSAndroid Build Coastguard Worker 284*60517a1eSAndroid Build Coastguard Workerdef _warn_duplicate_global_toolchain_version(version, first, second_toolchain_name, second_module_name, logger): 285*60517a1eSAndroid Build Coastguard Worker if not logger: 286*60517a1eSAndroid Build Coastguard Worker return 287*60517a1eSAndroid Build Coastguard Worker 288*60517a1eSAndroid Build Coastguard Worker logger.info(lambda: ( 289*60517a1eSAndroid Build Coastguard Worker "Ignoring toolchain '{second_toolchain}' from module '{second_module}': " + 290*60517a1eSAndroid Build Coastguard Worker "Toolchain '{first_toolchain}' from module '{first_module}' " + 291*60517a1eSAndroid Build Coastguard Worker "already registered Python version {version} and has precedence." 292*60517a1eSAndroid Build Coastguard Worker ).format( 293*60517a1eSAndroid Build Coastguard Worker first_toolchain = first.name, 294*60517a1eSAndroid Build Coastguard Worker first_module = first.module.name, 295*60517a1eSAndroid Build Coastguard Worker second_module = second_module_name, 296*60517a1eSAndroid Build Coastguard Worker second_toolchain = second_toolchain_name, 297*60517a1eSAndroid Build Coastguard Worker version = version, 298*60517a1eSAndroid Build Coastguard Worker )) 299*60517a1eSAndroid Build Coastguard Worker 300*60517a1eSAndroid Build Coastguard Workerdef _fail_multiple_default_toolchains(first, second): 301*60517a1eSAndroid Build Coastguard Worker fail(("Multiple default toolchains: only one toolchain " + 302*60517a1eSAndroid Build Coastguard Worker "can have is_default=True. First default " + 303*60517a1eSAndroid Build Coastguard Worker "was toolchain '{first}'. Second was '{second}'").format( 304*60517a1eSAndroid Build Coastguard Worker first = first, 305*60517a1eSAndroid Build Coastguard Worker second = second, 306*60517a1eSAndroid Build Coastguard Worker )) 307*60517a1eSAndroid Build Coastguard Worker 308*60517a1eSAndroid Build Coastguard Workerdef _validate_version(*, version, _fail = fail): 309*60517a1eSAndroid Build Coastguard Worker parsed = semver(version) 310*60517a1eSAndroid Build Coastguard Worker if parsed.patch == None or parsed.build or parsed.pre_release: 311*60517a1eSAndroid Build Coastguard Worker _fail("The 'python_version' attribute needs to specify an 'X.Y.Z' semver-compatible version, got: '{}'".format(version)) 312*60517a1eSAndroid Build Coastguard Worker return False 313*60517a1eSAndroid Build Coastguard Worker 314*60517a1eSAndroid Build Coastguard Worker return True 315*60517a1eSAndroid Build Coastguard Worker 316*60517a1eSAndroid Build Coastguard Workerdef _process_single_version_overrides(*, tag, _fail = fail, default): 317*60517a1eSAndroid Build Coastguard Worker if not _validate_version(version = tag.python_version, _fail = _fail): 318*60517a1eSAndroid Build Coastguard Worker return 319*60517a1eSAndroid Build Coastguard Worker 320*60517a1eSAndroid Build Coastguard Worker available_versions = default["tool_versions"] 321*60517a1eSAndroid Build Coastguard Worker kwargs = default.setdefault("kwargs", {}) 322*60517a1eSAndroid Build Coastguard Worker 323*60517a1eSAndroid Build Coastguard Worker if tag.sha256 or tag.urls: 324*60517a1eSAndroid Build Coastguard Worker if not (tag.sha256 and tag.urls): 325*60517a1eSAndroid Build Coastguard Worker _fail("Both `sha256` and `urls` overrides need to be provided together") 326*60517a1eSAndroid Build Coastguard Worker return 327*60517a1eSAndroid Build Coastguard Worker 328*60517a1eSAndroid Build Coastguard Worker for platform in (tag.sha256 or []): 329*60517a1eSAndroid Build Coastguard Worker if platform not in PLATFORMS: 330*60517a1eSAndroid Build Coastguard Worker _fail("The platform must be one of {allowed} but got '{got}'".format( 331*60517a1eSAndroid Build Coastguard Worker allowed = sorted(PLATFORMS), 332*60517a1eSAndroid Build Coastguard Worker got = platform, 333*60517a1eSAndroid Build Coastguard Worker )) 334*60517a1eSAndroid Build Coastguard Worker return 335*60517a1eSAndroid Build Coastguard Worker 336*60517a1eSAndroid Build Coastguard Worker sha256 = dict(tag.sha256) or available_versions[tag.python_version]["sha256"] 337*60517a1eSAndroid Build Coastguard Worker override = { 338*60517a1eSAndroid Build Coastguard Worker "sha256": sha256, 339*60517a1eSAndroid Build Coastguard Worker "strip_prefix": { 340*60517a1eSAndroid Build Coastguard Worker platform: tag.strip_prefix 341*60517a1eSAndroid Build Coastguard Worker for platform in sha256 342*60517a1eSAndroid Build Coastguard Worker }, 343*60517a1eSAndroid Build Coastguard Worker "url": { 344*60517a1eSAndroid Build Coastguard Worker platform: list(tag.urls) 345*60517a1eSAndroid Build Coastguard Worker for platform in tag.sha256 346*60517a1eSAndroid Build Coastguard Worker } or available_versions[tag.python_version]["url"], 347*60517a1eSAndroid Build Coastguard Worker } 348*60517a1eSAndroid Build Coastguard Worker 349*60517a1eSAndroid Build Coastguard Worker if tag.patches: 350*60517a1eSAndroid Build Coastguard Worker override["patch_strip"] = { 351*60517a1eSAndroid Build Coastguard Worker platform: tag.patch_strip 352*60517a1eSAndroid Build Coastguard Worker for platform in sha256 353*60517a1eSAndroid Build Coastguard Worker } 354*60517a1eSAndroid Build Coastguard Worker override["patches"] = { 355*60517a1eSAndroid Build Coastguard Worker platform: list(tag.patches) 356*60517a1eSAndroid Build Coastguard Worker for platform in sha256 357*60517a1eSAndroid Build Coastguard Worker } 358*60517a1eSAndroid Build Coastguard Worker 359*60517a1eSAndroid Build Coastguard Worker available_versions[tag.python_version] = {k: v for k, v in override.items() if v} 360*60517a1eSAndroid Build Coastguard Worker 361*60517a1eSAndroid Build Coastguard Worker if tag.distutils_content: 362*60517a1eSAndroid Build Coastguard Worker kwargs.setdefault(tag.python_version, {})["distutils_content"] = tag.distutils_content 363*60517a1eSAndroid Build Coastguard Worker if tag.distutils: 364*60517a1eSAndroid Build Coastguard Worker kwargs.setdefault(tag.python_version, {})["distutils"] = tag.distutils 365*60517a1eSAndroid Build Coastguard Worker 366*60517a1eSAndroid Build Coastguard Workerdef _process_single_version_platform_overrides(*, tag, _fail = fail, default): 367*60517a1eSAndroid Build Coastguard Worker if not _validate_version(version = tag.python_version, _fail = _fail): 368*60517a1eSAndroid Build Coastguard Worker return 369*60517a1eSAndroid Build Coastguard Worker 370*60517a1eSAndroid Build Coastguard Worker available_versions = default["tool_versions"] 371*60517a1eSAndroid Build Coastguard Worker 372*60517a1eSAndroid Build Coastguard Worker if tag.python_version not in available_versions: 373*60517a1eSAndroid Build Coastguard Worker if not tag.urls or not tag.sha256 or not tag.strip_prefix: 374*60517a1eSAndroid Build Coastguard Worker _fail("When introducing a new python_version '{}', 'sha256', 'strip_prefix' and 'urls' must be specified".format(tag.python_version)) 375*60517a1eSAndroid Build Coastguard Worker return 376*60517a1eSAndroid Build Coastguard Worker available_versions[tag.python_version] = {} 377*60517a1eSAndroid Build Coastguard Worker 378*60517a1eSAndroid Build Coastguard Worker if tag.coverage_tool: 379*60517a1eSAndroid Build Coastguard Worker available_versions[tag.python_version].setdefault("coverage_tool", {})[tag.platform] = tag.coverage_tool 380*60517a1eSAndroid Build Coastguard Worker if tag.patch_strip: 381*60517a1eSAndroid Build Coastguard Worker available_versions[tag.python_version].setdefault("patch_strip", {})[tag.platform] = tag.patch_strip 382*60517a1eSAndroid Build Coastguard Worker if tag.patches: 383*60517a1eSAndroid Build Coastguard Worker available_versions[tag.python_version].setdefault("patches", {})[tag.platform] = list(tag.patches) 384*60517a1eSAndroid Build Coastguard Worker if tag.sha256: 385*60517a1eSAndroid Build Coastguard Worker available_versions[tag.python_version].setdefault("sha256", {})[tag.platform] = tag.sha256 386*60517a1eSAndroid Build Coastguard Worker if tag.strip_prefix: 387*60517a1eSAndroid Build Coastguard Worker available_versions[tag.python_version].setdefault("strip_prefix", {})[tag.platform] = tag.strip_prefix 388*60517a1eSAndroid Build Coastguard Worker if tag.urls: 389*60517a1eSAndroid Build Coastguard Worker available_versions[tag.python_version].setdefault("url", {})[tag.platform] = tag.urls 390*60517a1eSAndroid Build Coastguard Worker 391*60517a1eSAndroid Build Coastguard Workerdef _process_global_overrides(*, tag, default, _fail = fail): 392*60517a1eSAndroid Build Coastguard Worker if tag.available_python_versions: 393*60517a1eSAndroid Build Coastguard Worker available_versions = default["tool_versions"] 394*60517a1eSAndroid Build Coastguard Worker all_versions = dict(available_versions) 395*60517a1eSAndroid Build Coastguard Worker available_versions.clear() 396*60517a1eSAndroid Build Coastguard Worker for v in tag.available_python_versions: 397*60517a1eSAndroid Build Coastguard Worker if v not in all_versions: 398*60517a1eSAndroid Build Coastguard Worker _fail("unknown version '{}', known versions are: {}".format( 399*60517a1eSAndroid Build Coastguard Worker v, 400*60517a1eSAndroid Build Coastguard Worker sorted(all_versions), 401*60517a1eSAndroid Build Coastguard Worker )) 402*60517a1eSAndroid Build Coastguard Worker return 403*60517a1eSAndroid Build Coastguard Worker 404*60517a1eSAndroid Build Coastguard Worker available_versions[v] = all_versions[v] 405*60517a1eSAndroid Build Coastguard Worker 406*60517a1eSAndroid Build Coastguard Worker if tag.minor_mapping: 407*60517a1eSAndroid Build Coastguard Worker for minor_version, full_version in tag.minor_mapping.items(): 408*60517a1eSAndroid Build Coastguard Worker parsed = semver(minor_version) 409*60517a1eSAndroid Build Coastguard Worker if parsed.patch != None or parsed.build or parsed.pre_release: 410*60517a1eSAndroid Build Coastguard Worker fail("Expected the key to be of `X.Y` format but got `{}`".format(minor_version)) 411*60517a1eSAndroid Build Coastguard Worker parsed = semver(full_version) 412*60517a1eSAndroid Build Coastguard Worker if parsed.patch == None: 413*60517a1eSAndroid Build Coastguard Worker fail("Expected the value to at least be of `X.Y.Z` format but got `{}`".format(minor_version)) 414*60517a1eSAndroid Build Coastguard Worker 415*60517a1eSAndroid Build Coastguard Worker default["minor_mapping"] = tag.minor_mapping 416*60517a1eSAndroid Build Coastguard Worker 417*60517a1eSAndroid Build Coastguard Worker forwarded_attrs = sorted(AUTH_ATTRS) + [ 418*60517a1eSAndroid Build Coastguard Worker "ignore_root_user_error", 419*60517a1eSAndroid Build Coastguard Worker "base_url", 420*60517a1eSAndroid Build Coastguard Worker "register_all_versions", 421*60517a1eSAndroid Build Coastguard Worker ] 422*60517a1eSAndroid Build Coastguard Worker for key in forwarded_attrs: 423*60517a1eSAndroid Build Coastguard Worker if getattr(tag, key, None): 424*60517a1eSAndroid Build Coastguard Worker default[key] = getattr(tag, key) 425*60517a1eSAndroid Build Coastguard Worker 426*60517a1eSAndroid Build Coastguard Workerdef _override_defaults(*overrides, modules, _fail = fail, default): 427*60517a1eSAndroid Build Coastguard Worker mod = modules[0] if modules else None 428*60517a1eSAndroid Build Coastguard Worker if not mod or not mod.is_root: 429*60517a1eSAndroid Build Coastguard Worker return 430*60517a1eSAndroid Build Coastguard Worker 431*60517a1eSAndroid Build Coastguard Worker overriden_keys = [] 432*60517a1eSAndroid Build Coastguard Worker 433*60517a1eSAndroid Build Coastguard Worker for override in overrides: 434*60517a1eSAndroid Build Coastguard Worker for tag in getattr(mod.tags, override.name): 435*60517a1eSAndroid Build Coastguard Worker key = override.key(tag) 436*60517a1eSAndroid Build Coastguard Worker if key not in overriden_keys: 437*60517a1eSAndroid Build Coastguard Worker overriden_keys.append(key) 438*60517a1eSAndroid Build Coastguard Worker elif key: 439*60517a1eSAndroid Build Coastguard Worker _fail("Only a single 'python.{}' can be present for '{}'".format(override.name, key)) 440*60517a1eSAndroid Build Coastguard Worker return 441*60517a1eSAndroid Build Coastguard Worker else: 442*60517a1eSAndroid Build Coastguard Worker _fail("Only a single 'python.{}' can be present".format(override.name)) 443*60517a1eSAndroid Build Coastguard Worker return 444*60517a1eSAndroid Build Coastguard Worker 445*60517a1eSAndroid Build Coastguard Worker override.fn(tag = tag, _fail = _fail, default = default) 446*60517a1eSAndroid Build Coastguard Worker 447*60517a1eSAndroid Build Coastguard Workerdef _get_toolchain_config(*, modules, _fail = fail): 448*60517a1eSAndroid Build Coastguard Worker # Items that can be overridden 449*60517a1eSAndroid Build Coastguard Worker available_versions = { 450*60517a1eSAndroid Build Coastguard Worker version: { 451*60517a1eSAndroid Build Coastguard Worker # Use a dicts straight away so that we could do URL overrides for a 452*60517a1eSAndroid Build Coastguard Worker # single version. 453*60517a1eSAndroid Build Coastguard Worker "sha256": dict(item["sha256"]), 454*60517a1eSAndroid Build Coastguard Worker "strip_prefix": { 455*60517a1eSAndroid Build Coastguard Worker platform: item["strip_prefix"] 456*60517a1eSAndroid Build Coastguard Worker for platform in item["sha256"] 457*60517a1eSAndroid Build Coastguard Worker }, 458*60517a1eSAndroid Build Coastguard Worker "url": { 459*60517a1eSAndroid Build Coastguard Worker platform: [item["url"]] 460*60517a1eSAndroid Build Coastguard Worker for platform in item["sha256"] 461*60517a1eSAndroid Build Coastguard Worker }, 462*60517a1eSAndroid Build Coastguard Worker } 463*60517a1eSAndroid Build Coastguard Worker for version, item in TOOL_VERSIONS.items() 464*60517a1eSAndroid Build Coastguard Worker } 465*60517a1eSAndroid Build Coastguard Worker default = { 466*60517a1eSAndroid Build Coastguard Worker "base_url": DEFAULT_RELEASE_BASE_URL, 467*60517a1eSAndroid Build Coastguard Worker "tool_versions": available_versions, 468*60517a1eSAndroid Build Coastguard Worker } 469*60517a1eSAndroid Build Coastguard Worker 470*60517a1eSAndroid Build Coastguard Worker _override_defaults( 471*60517a1eSAndroid Build Coastguard Worker # First override by single version, because the sha256 will replace 472*60517a1eSAndroid Build Coastguard Worker # anything that has been there before. 473*60517a1eSAndroid Build Coastguard Worker struct( 474*60517a1eSAndroid Build Coastguard Worker name = "single_version_override", 475*60517a1eSAndroid Build Coastguard Worker key = lambda t: t.python_version, 476*60517a1eSAndroid Build Coastguard Worker fn = _process_single_version_overrides, 477*60517a1eSAndroid Build Coastguard Worker ), 478*60517a1eSAndroid Build Coastguard Worker # Then override particular platform entries if they need to be overridden. 479*60517a1eSAndroid Build Coastguard Worker struct( 480*60517a1eSAndroid Build Coastguard Worker name = "single_version_platform_override", 481*60517a1eSAndroid Build Coastguard Worker key = lambda t: (t.python_version, t.platform), 482*60517a1eSAndroid Build Coastguard Worker fn = _process_single_version_platform_overrides, 483*60517a1eSAndroid Build Coastguard Worker ), 484*60517a1eSAndroid Build Coastguard Worker # Then finally add global args and remove the unnecessary toolchains. 485*60517a1eSAndroid Build Coastguard Worker # This ensures that we can do further validations when removing. 486*60517a1eSAndroid Build Coastguard Worker struct( 487*60517a1eSAndroid Build Coastguard Worker name = "override", 488*60517a1eSAndroid Build Coastguard Worker key = lambda t: None, 489*60517a1eSAndroid Build Coastguard Worker fn = _process_global_overrides, 490*60517a1eSAndroid Build Coastguard Worker ), 491*60517a1eSAndroid Build Coastguard Worker modules = modules, 492*60517a1eSAndroid Build Coastguard Worker default = default, 493*60517a1eSAndroid Build Coastguard Worker _fail = _fail, 494*60517a1eSAndroid Build Coastguard Worker ) 495*60517a1eSAndroid Build Coastguard Worker 496*60517a1eSAndroid Build Coastguard Worker minor_mapping = default.pop("minor_mapping", {}) 497*60517a1eSAndroid Build Coastguard Worker register_all_versions = default.pop("register_all_versions", False) 498*60517a1eSAndroid Build Coastguard Worker kwargs = default.pop("kwargs", {}) 499*60517a1eSAndroid Build Coastguard Worker 500*60517a1eSAndroid Build Coastguard Worker if not minor_mapping: 501*60517a1eSAndroid Build Coastguard Worker versions = {} 502*60517a1eSAndroid Build Coastguard Worker for version_string in available_versions: 503*60517a1eSAndroid Build Coastguard Worker v = semver(version_string) 504*60517a1eSAndroid Build Coastguard Worker versions.setdefault("{}.{}".format(v.major, v.minor), []).append((int(v.patch), version_string)) 505*60517a1eSAndroid Build Coastguard Worker 506*60517a1eSAndroid Build Coastguard Worker minor_mapping = { 507*60517a1eSAndroid Build Coastguard Worker major_minor: max(subset)[1] 508*60517a1eSAndroid Build Coastguard Worker for major_minor, subset in versions.items() 509*60517a1eSAndroid Build Coastguard Worker } 510*60517a1eSAndroid Build Coastguard Worker 511*60517a1eSAndroid Build Coastguard Worker return struct( 512*60517a1eSAndroid Build Coastguard Worker kwargs = kwargs, 513*60517a1eSAndroid Build Coastguard Worker minor_mapping = minor_mapping, 514*60517a1eSAndroid Build Coastguard Worker default = default, 515*60517a1eSAndroid Build Coastguard Worker register_all_versions = register_all_versions, 516*60517a1eSAndroid Build Coastguard Worker ) 517*60517a1eSAndroid Build Coastguard Worker 518*60517a1eSAndroid Build Coastguard Workerdef _create_toolchain_attr_structs(*, mod, config, seen_versions): 519*60517a1eSAndroid Build Coastguard Worker arg_structs = [] 520*60517a1eSAndroid Build Coastguard Worker 521*60517a1eSAndroid Build Coastguard Worker for tag in mod.tags.toolchain: 522*60517a1eSAndroid Build Coastguard Worker arg_structs.append(_create_toolchain_attrs_struct( 523*60517a1eSAndroid Build Coastguard Worker tag = tag, 524*60517a1eSAndroid Build Coastguard Worker toolchain_tag_count = len(mod.tags.toolchain), 525*60517a1eSAndroid Build Coastguard Worker )) 526*60517a1eSAndroid Build Coastguard Worker 527*60517a1eSAndroid Build Coastguard Worker seen_versions[tag.python_version] = True 528*60517a1eSAndroid Build Coastguard Worker 529*60517a1eSAndroid Build Coastguard Worker if config.register_all_versions: 530*60517a1eSAndroid Build Coastguard Worker arg_structs.extend([ 531*60517a1eSAndroid Build Coastguard Worker _create_toolchain_attrs_struct(python_version = v) 532*60517a1eSAndroid Build Coastguard Worker for v in config.default["tool_versions"].keys() + config.minor_mapping.keys() 533*60517a1eSAndroid Build Coastguard Worker if v not in seen_versions 534*60517a1eSAndroid Build Coastguard Worker ]) 535*60517a1eSAndroid Build Coastguard Worker 536*60517a1eSAndroid Build Coastguard Worker return arg_structs 537*60517a1eSAndroid Build Coastguard Worker 538*60517a1eSAndroid Build Coastguard Workerdef _create_toolchain_attrs_struct(*, tag = None, python_version = None, toolchain_tag_count = None): 539*60517a1eSAndroid Build Coastguard Worker if tag and python_version: 540*60517a1eSAndroid Build Coastguard Worker fail("Only one of tag and python version can be specified") 541*60517a1eSAndroid Build Coastguard Worker if tag: 542*60517a1eSAndroid Build Coastguard Worker # A single toolchain is treated as the default because it's unambiguous. 543*60517a1eSAndroid Build Coastguard Worker is_default = tag.is_default or toolchain_tag_count == 1 544*60517a1eSAndroid Build Coastguard Worker else: 545*60517a1eSAndroid Build Coastguard Worker is_default = False 546*60517a1eSAndroid Build Coastguard Worker 547*60517a1eSAndroid Build Coastguard Worker return struct( 548*60517a1eSAndroid Build Coastguard Worker is_default = is_default, 549*60517a1eSAndroid Build Coastguard Worker python_version = python_version if python_version else tag.python_version, 550*60517a1eSAndroid Build Coastguard Worker configure_coverage_tool = getattr(tag, "configure_coverage_tool", False), 551*60517a1eSAndroid Build Coastguard Worker ignore_root_user_error = getattr(tag, "ignore_root_user_error", False), 552*60517a1eSAndroid Build Coastguard Worker ) 553*60517a1eSAndroid Build Coastguard Worker 554*60517a1eSAndroid Build Coastguard Workerdef _get_bazel_version_specific_kwargs(): 555*60517a1eSAndroid Build Coastguard Worker kwargs = {} 556*60517a1eSAndroid Build Coastguard Worker 557*60517a1eSAndroid Build Coastguard Worker if IS_BAZEL_6_4_OR_HIGHER: 558*60517a1eSAndroid Build Coastguard Worker kwargs["environ"] = ["RULES_PYTHON_BZLMOD_DEBUG"] 559*60517a1eSAndroid Build Coastguard Worker 560*60517a1eSAndroid Build Coastguard Worker return kwargs 561*60517a1eSAndroid Build Coastguard Worker 562*60517a1eSAndroid Build Coastguard Worker_toolchain = tag_class( 563*60517a1eSAndroid Build Coastguard Worker doc = """Tag class used to register Python toolchains. 564*60517a1eSAndroid Build Coastguard WorkerUse this tag class to register one or more Python toolchains. This class 565*60517a1eSAndroid Build Coastguard Workeris also potentially called by sub modules. The following covers different 566*60517a1eSAndroid Build Coastguard Workerbusiness rules and use cases. 567*60517a1eSAndroid Build Coastguard Worker 568*60517a1eSAndroid Build Coastguard Worker:::{topic} Toolchains in the Root Module 569*60517a1eSAndroid Build Coastguard Worker 570*60517a1eSAndroid Build Coastguard WorkerThis class registers all toolchains in the root module. 571*60517a1eSAndroid Build Coastguard Worker::: 572*60517a1eSAndroid Build Coastguard Worker 573*60517a1eSAndroid Build Coastguard Worker:::{topic} Toolchains in Sub Modules 574*60517a1eSAndroid Build Coastguard Worker 575*60517a1eSAndroid Build Coastguard WorkerIt will create a toolchain that is in a sub module, if the toolchain 576*60517a1eSAndroid Build Coastguard Workerof the same name does not exist in the root module. The extension stops name 577*60517a1eSAndroid Build Coastguard Workerclashing between toolchains in the root module and toolchains in sub modules. 578*60517a1eSAndroid Build Coastguard WorkerYou cannot configure more than one toolchain as the default toolchain. 579*60517a1eSAndroid Build Coastguard Worker::: 580*60517a1eSAndroid Build Coastguard Worker 581*60517a1eSAndroid Build Coastguard Worker:::{topic} Toolchain set as the default version 582*60517a1eSAndroid Build Coastguard Worker 583*60517a1eSAndroid Build Coastguard WorkerThis extension will not create a toolchain that exists in a sub module, 584*60517a1eSAndroid Build Coastguard Workerif the sub module toolchain is marked as the default version. If you have 585*60517a1eSAndroid Build Coastguard Workermore than one toolchain in your root module, you need to set one of the 586*60517a1eSAndroid Build Coastguard Workertoolchains as the default version. If there is only one toolchain it 587*60517a1eSAndroid Build Coastguard Workeris set as the default toolchain. 588*60517a1eSAndroid Build Coastguard Worker::: 589*60517a1eSAndroid Build Coastguard Worker 590*60517a1eSAndroid Build Coastguard Worker:::{topic} Toolchain repository name 591*60517a1eSAndroid Build Coastguard Worker 592*60517a1eSAndroid Build Coastguard WorkerA toolchain's repository name uses the format `python_{major}_{minor}`, e.g. 593*60517a1eSAndroid Build Coastguard Worker`python_3_10`. The `major` and `minor` components are 594*60517a1eSAndroid Build Coastguard Worker`major` and `minor` are the Python version from the `python_version` attribute. 595*60517a1eSAndroid Build Coastguard Worker 596*60517a1eSAndroid Build Coastguard WorkerIf a toolchain is registered in `X.Y.Z`, then similarly the toolchain name will 597*60517a1eSAndroid Build Coastguard Workerbe `python_{major}_{minor}_{patch}`, e.g. `python_3_10_19`. 598*60517a1eSAndroid Build Coastguard Worker::: 599*60517a1eSAndroid Build Coastguard Worker 600*60517a1eSAndroid Build Coastguard Worker:::{topic} Toolchain detection 601*60517a1eSAndroid Build Coastguard WorkerThe definition of the first toolchain wins, which means that the root module 602*60517a1eSAndroid Build Coastguard Workercan override settings for any python toolchain available. This relies on the 603*60517a1eSAndroid Build Coastguard Workerdocumented module traversal from the {obj}`module_ctx.modules`. 604*60517a1eSAndroid Build Coastguard Worker::: 605*60517a1eSAndroid Build Coastguard Worker 606*60517a1eSAndroid Build Coastguard Worker:::{tip} 607*60517a1eSAndroid Build Coastguard WorkerIn order to use a different name than the above, you can use the following `MODULE.bazel` 608*60517a1eSAndroid Build Coastguard Workersyntax: 609*60517a1eSAndroid Build Coastguard Worker```starlark 610*60517a1eSAndroid Build Coastguard Workerpython = use_extension("@rules_python//python/extensions:python.bzl", "python") 611*60517a1eSAndroid Build Coastguard Workerpython.toolchain( 612*60517a1eSAndroid Build Coastguard Worker is_default = True, 613*60517a1eSAndroid Build Coastguard Worker python_version = "3.11", 614*60517a1eSAndroid Build Coastguard Worker) 615*60517a1eSAndroid Build Coastguard Worker 616*60517a1eSAndroid Build Coastguard Workeruse_repo(python, my_python_name = "python_3_11") 617*60517a1eSAndroid Build Coastguard Worker``` 618*60517a1eSAndroid Build Coastguard Worker 619*60517a1eSAndroid Build Coastguard WorkerThen the python interpreter will be available as `my_python_name`. 620*60517a1eSAndroid Build Coastguard Worker::: 621*60517a1eSAndroid Build Coastguard Worker""", 622*60517a1eSAndroid Build Coastguard Worker attrs = { 623*60517a1eSAndroid Build Coastguard Worker "configure_coverage_tool": attr.bool( 624*60517a1eSAndroid Build Coastguard Worker mandatory = False, 625*60517a1eSAndroid Build Coastguard Worker doc = "Whether or not to configure the default coverage tool provided by `rules_python` for the compatible toolchains.", 626*60517a1eSAndroid Build Coastguard Worker ), 627*60517a1eSAndroid Build Coastguard Worker "ignore_root_user_error": attr.bool( 628*60517a1eSAndroid Build Coastguard Worker default = False, 629*60517a1eSAndroid Build Coastguard Worker doc = """\ 630*60517a1eSAndroid Build Coastguard WorkerIf `False`, the Python runtime installation will be made read only. This improves 631*60517a1eSAndroid Build Coastguard Workerthe ability for Bazel to cache it, but prevents the interpreter from creating 632*60517a1eSAndroid Build Coastguard Worker`.pyc` files for the standard library dynamically at runtime as they are loaded. 633*60517a1eSAndroid Build Coastguard Worker 634*60517a1eSAndroid Build Coastguard WorkerIf `True`, the Python runtime installation is read-write. This allows the 635*60517a1eSAndroid Build Coastguard Workerinterpreter to create `.pyc` files for the standard library, but, because they are 636*60517a1eSAndroid Build Coastguard Workercreated as needed, it adversely affects Bazel's ability to cache the runtime and 637*60517a1eSAndroid Build Coastguard Workercan result in spurious build failures. 638*60517a1eSAndroid Build Coastguard Worker""", 639*60517a1eSAndroid Build Coastguard Worker mandatory = False, 640*60517a1eSAndroid Build Coastguard Worker ), 641*60517a1eSAndroid Build Coastguard Worker "is_default": attr.bool( 642*60517a1eSAndroid Build Coastguard Worker mandatory = False, 643*60517a1eSAndroid Build Coastguard Worker doc = "Whether the toolchain is the default version", 644*60517a1eSAndroid Build Coastguard Worker ), 645*60517a1eSAndroid Build Coastguard Worker "python_version": attr.string( 646*60517a1eSAndroid Build Coastguard Worker mandatory = True, 647*60517a1eSAndroid Build Coastguard Worker doc = """\ 648*60517a1eSAndroid Build Coastguard WorkerThe Python version, in `major.minor` or `major.minor.patch` format, e.g 649*60517a1eSAndroid Build Coastguard Worker`3.12` (or `3.12.3`), to create a toolchain for. 650*60517a1eSAndroid Build Coastguard Worker""", 651*60517a1eSAndroid Build Coastguard Worker ), 652*60517a1eSAndroid Build Coastguard Worker }, 653*60517a1eSAndroid Build Coastguard Worker) 654*60517a1eSAndroid Build Coastguard Worker 655*60517a1eSAndroid Build Coastguard Worker_override = tag_class( 656*60517a1eSAndroid Build Coastguard Worker doc = """Tag class used to override defaults and behaviour of the module extension. 657*60517a1eSAndroid Build Coastguard Worker 658*60517a1eSAndroid Build Coastguard Worker:::{versionadded} 0.36.0 659*60517a1eSAndroid Build Coastguard Worker::: 660*60517a1eSAndroid Build Coastguard Worker""", 661*60517a1eSAndroid Build Coastguard Worker attrs = { 662*60517a1eSAndroid Build Coastguard Worker "available_python_versions": attr.string_list( 663*60517a1eSAndroid Build Coastguard Worker mandatory = False, 664*60517a1eSAndroid Build Coastguard Worker doc = """\ 665*60517a1eSAndroid Build Coastguard WorkerThe list of available python tool versions to use. Must be in `X.Y.Z` format. 666*60517a1eSAndroid Build Coastguard WorkerIf the unknown version given the processing of the extension will fail - all of 667*60517a1eSAndroid Build Coastguard Workerthe versions in the list have to be defined with 668*60517a1eSAndroid Build Coastguard Worker{obj}`python.single_version_override` or 669*60517a1eSAndroid Build Coastguard Worker{obj}`python.single_version_platform_override` before they are used in this 670*60517a1eSAndroid Build Coastguard Workerlist. 671*60517a1eSAndroid Build Coastguard Worker 672*60517a1eSAndroid Build Coastguard WorkerThis attribute is usually used in order to ensure that no unexpected transitive 673*60517a1eSAndroid Build Coastguard Workerdependencies are introduced. 674*60517a1eSAndroid Build Coastguard Worker""", 675*60517a1eSAndroid Build Coastguard Worker ), 676*60517a1eSAndroid Build Coastguard Worker "base_url": attr.string( 677*60517a1eSAndroid Build Coastguard Worker mandatory = False, 678*60517a1eSAndroid Build Coastguard Worker doc = "The base URL to be used when downloading toolchains.", 679*60517a1eSAndroid Build Coastguard Worker default = DEFAULT_RELEASE_BASE_URL, 680*60517a1eSAndroid Build Coastguard Worker ), 681*60517a1eSAndroid Build Coastguard Worker "ignore_root_user_error": attr.bool( 682*60517a1eSAndroid Build Coastguard Worker default = False, 683*60517a1eSAndroid Build Coastguard Worker doc = """\ 684*60517a1eSAndroid Build Coastguard WorkerIf `False`, the Python runtime installation will be made read only. This improves 685*60517a1eSAndroid Build Coastguard Workerthe ability for Bazel to cache it, but prevents the interpreter from creating 686*60517a1eSAndroid Build Coastguard Worker`.pyc` files for the standard library dynamically at runtime as they are loaded. 687*60517a1eSAndroid Build Coastguard Worker 688*60517a1eSAndroid Build Coastguard WorkerIf `True`, the Python runtime installation is read-write. This allows the 689*60517a1eSAndroid Build Coastguard Workerinterpreter to create `.pyc` files for the standard library, but, because they are 690*60517a1eSAndroid Build Coastguard Workercreated as needed, it adversely affects Bazel's ability to cache the runtime and 691*60517a1eSAndroid Build Coastguard Workercan result in spurious build failures. 692*60517a1eSAndroid Build Coastguard Worker""", 693*60517a1eSAndroid Build Coastguard Worker mandatory = False, 694*60517a1eSAndroid Build Coastguard Worker ), 695*60517a1eSAndroid Build Coastguard Worker "minor_mapping": attr.string_dict( 696*60517a1eSAndroid Build Coastguard Worker mandatory = False, 697*60517a1eSAndroid Build Coastguard Worker doc = """\ 698*60517a1eSAndroid Build Coastguard WorkerThe mapping between `X.Y` to `X.Y.Z` versions to be used when setting up 699*60517a1eSAndroid Build Coastguard Workertoolchains. It defaults to the interpreter with the highest available patch 700*60517a1eSAndroid Build Coastguard Workerversion for each minor version. For example if one registers `3.10.3`, `3.10.4` 701*60517a1eSAndroid Build Coastguard Workerand `3.11.4` then the default for the `minor_mapping` dict will be: 702*60517a1eSAndroid Build Coastguard Worker```starlark 703*60517a1eSAndroid Build Coastguard Worker{ 704*60517a1eSAndroid Build Coastguard Worker"3.10": "3.10.4", 705*60517a1eSAndroid Build Coastguard Worker"3.11": "3.11.4", 706*60517a1eSAndroid Build Coastguard Worker} 707*60517a1eSAndroid Build Coastguard Worker``` 708*60517a1eSAndroid Build Coastguard Worker""", 709*60517a1eSAndroid Build Coastguard Worker default = {}, 710*60517a1eSAndroid Build Coastguard Worker ), 711*60517a1eSAndroid Build Coastguard Worker "register_all_versions": attr.bool(default = False, doc = "Add all versions"), 712*60517a1eSAndroid Build Coastguard Worker } | AUTH_ATTRS, 713*60517a1eSAndroid Build Coastguard Worker) 714*60517a1eSAndroid Build Coastguard Worker 715*60517a1eSAndroid Build Coastguard Worker_single_version_override = tag_class( 716*60517a1eSAndroid Build Coastguard Worker doc = """Override single python version URLs and patches for all platforms. 717*60517a1eSAndroid Build Coastguard Worker 718*60517a1eSAndroid Build Coastguard Worker:::{note} 719*60517a1eSAndroid Build Coastguard WorkerThis will replace any existing configuration for the given python version. 720*60517a1eSAndroid Build Coastguard Worker::: 721*60517a1eSAndroid Build Coastguard Worker 722*60517a1eSAndroid Build Coastguard Worker:::{tip} 723*60517a1eSAndroid Build Coastguard WorkerIf you would like to modify the configuration for a specific `(version, 724*60517a1eSAndroid Build Coastguard Workerplatform)`, please use the {obj}`single_version_platform_override` tag 725*60517a1eSAndroid Build Coastguard Workerclass. 726*60517a1eSAndroid Build Coastguard Worker::: 727*60517a1eSAndroid Build Coastguard Worker 728*60517a1eSAndroid Build Coastguard Worker:::{versionadded} 0.36.0 729*60517a1eSAndroid Build Coastguard Worker::: 730*60517a1eSAndroid Build Coastguard Worker""", 731*60517a1eSAndroid Build Coastguard Worker attrs = { 732*60517a1eSAndroid Build Coastguard Worker # NOTE @aignas 2024-09-01: all of the attributes except for `version` 733*60517a1eSAndroid Build Coastguard Worker # can be part of the `python.toolchain` call. That would make it more 734*60517a1eSAndroid Build Coastguard Worker # ergonomic to define new toolchains and to override values for old 735*60517a1eSAndroid Build Coastguard Worker # toolchains. The same semantics of the `first one wins` would apply, 736*60517a1eSAndroid Build Coastguard Worker # so technically there is no need for any overrides? 737*60517a1eSAndroid Build Coastguard Worker # 738*60517a1eSAndroid Build Coastguard Worker # Although these attributes would override the code that is used by the 739*60517a1eSAndroid Build Coastguard Worker # code in non-root modules, so technically this could be thought as 740*60517a1eSAndroid Build Coastguard Worker # being overridden. 741*60517a1eSAndroid Build Coastguard Worker # 742*60517a1eSAndroid Build Coastguard Worker # rules_go has a single download call: 743*60517a1eSAndroid Build Coastguard Worker # https://github.com/bazelbuild/rules_go/blob/master/go/private/extensions.bzl#L38 744*60517a1eSAndroid Build Coastguard Worker # 745*60517a1eSAndroid Build Coastguard Worker # However, we need to understand how to accommodate the fact that 746*60517a1eSAndroid Build Coastguard Worker # {attr}`single_version_override.version` only allows patch versions. 747*60517a1eSAndroid Build Coastguard Worker "distutils": attr.label( 748*60517a1eSAndroid Build Coastguard Worker allow_single_file = True, 749*60517a1eSAndroid Build Coastguard Worker doc = "A distutils.cfg file to be included in the Python installation. " + 750*60517a1eSAndroid Build Coastguard Worker "Either {attr}`distutils` or {attr}`distutils_content` can be specified, but not both.", 751*60517a1eSAndroid Build Coastguard Worker mandatory = False, 752*60517a1eSAndroid Build Coastguard Worker ), 753*60517a1eSAndroid Build Coastguard Worker "distutils_content": attr.string( 754*60517a1eSAndroid Build Coastguard Worker doc = "A distutils.cfg file content to be included in the Python installation. " + 755*60517a1eSAndroid Build Coastguard Worker "Either {attr}`distutils` or {attr}`distutils_content` can be specified, but not both.", 756*60517a1eSAndroid Build Coastguard Worker mandatory = False, 757*60517a1eSAndroid Build Coastguard Worker ), 758*60517a1eSAndroid Build Coastguard Worker "patch_strip": attr.int( 759*60517a1eSAndroid Build Coastguard Worker mandatory = False, 760*60517a1eSAndroid Build Coastguard Worker doc = "Same as the --strip argument of Unix patch.", 761*60517a1eSAndroid Build Coastguard Worker default = 0, 762*60517a1eSAndroid Build Coastguard Worker ), 763*60517a1eSAndroid Build Coastguard Worker "patches": attr.label_list( 764*60517a1eSAndroid Build Coastguard Worker mandatory = False, 765*60517a1eSAndroid Build Coastguard Worker doc = "A list of labels pointing to patch files to apply for the interpreter repository. They are applied in the list order and are applied before any platform-specific patches are applied.", 766*60517a1eSAndroid Build Coastguard Worker ), 767*60517a1eSAndroid Build Coastguard Worker "python_version": attr.string( 768*60517a1eSAndroid Build Coastguard Worker mandatory = True, 769*60517a1eSAndroid Build Coastguard Worker doc = "The python version to override URLs for. Must be in `X.Y.Z` format.", 770*60517a1eSAndroid Build Coastguard Worker ), 771*60517a1eSAndroid Build Coastguard Worker "sha256": attr.string_dict( 772*60517a1eSAndroid Build Coastguard Worker mandatory = False, 773*60517a1eSAndroid Build Coastguard Worker doc = "The python platform to sha256 dict. See {attr}`python.single_version_platform_override.platform` for allowed key values.", 774*60517a1eSAndroid Build Coastguard Worker ), 775*60517a1eSAndroid Build Coastguard Worker "strip_prefix": attr.string( 776*60517a1eSAndroid Build Coastguard Worker mandatory = False, 777*60517a1eSAndroid Build Coastguard Worker doc = "The 'strip_prefix' for the archive, defaults to 'python'.", 778*60517a1eSAndroid Build Coastguard Worker default = "python", 779*60517a1eSAndroid Build Coastguard Worker ), 780*60517a1eSAndroid Build Coastguard Worker "urls": attr.string_list( 781*60517a1eSAndroid Build Coastguard Worker mandatory = False, 782*60517a1eSAndroid Build Coastguard Worker doc = "The URL template to fetch releases for this Python version. See {attr}`python.single_version_platform_override.urls` for documentation.", 783*60517a1eSAndroid Build Coastguard Worker ), 784*60517a1eSAndroid Build Coastguard Worker }, 785*60517a1eSAndroid Build Coastguard Worker) 786*60517a1eSAndroid Build Coastguard Worker 787*60517a1eSAndroid Build Coastguard Worker_single_version_platform_override = tag_class( 788*60517a1eSAndroid Build Coastguard Worker doc = """Override single python version for a single existing platform. 789*60517a1eSAndroid Build Coastguard Worker 790*60517a1eSAndroid Build Coastguard WorkerIf the `(version, platform)` is new, we will add it to the existing versions and will 791*60517a1eSAndroid Build Coastguard Workeruse the same `url` template. 792*60517a1eSAndroid Build Coastguard Worker 793*60517a1eSAndroid Build Coastguard Worker:::{tip} 794*60517a1eSAndroid Build Coastguard WorkerIf you would like to add or remove platforms to a single python version toolchain 795*60517a1eSAndroid Build Coastguard Workerconfiguration, please use {obj}`single_version_override`. 796*60517a1eSAndroid Build Coastguard Worker::: 797*60517a1eSAndroid Build Coastguard Worker 798*60517a1eSAndroid Build Coastguard Worker:::{versionadded} 0.36.0 799*60517a1eSAndroid Build Coastguard Worker::: 800*60517a1eSAndroid Build Coastguard Worker""", 801*60517a1eSAndroid Build Coastguard Worker attrs = { 802*60517a1eSAndroid Build Coastguard Worker "coverage_tool": attr.label( 803*60517a1eSAndroid Build Coastguard Worker doc = """\ 804*60517a1eSAndroid Build Coastguard WorkerThe coverage tool to be used for a particular Python interpreter. This can override 805*60517a1eSAndroid Build Coastguard Worker`rules_python` defaults. 806*60517a1eSAndroid Build Coastguard Worker""", 807*60517a1eSAndroid Build Coastguard Worker ), 808*60517a1eSAndroid Build Coastguard Worker "patch_strip": attr.int( 809*60517a1eSAndroid Build Coastguard Worker mandatory = False, 810*60517a1eSAndroid Build Coastguard Worker doc = "Same as the --strip argument of Unix patch.", 811*60517a1eSAndroid Build Coastguard Worker default = 0, 812*60517a1eSAndroid Build Coastguard Worker ), 813*60517a1eSAndroid Build Coastguard Worker "patches": attr.label_list( 814*60517a1eSAndroid Build Coastguard Worker mandatory = False, 815*60517a1eSAndroid Build Coastguard Worker doc = "A list of labels pointing to patch files to apply for the interpreter repository. They are applied in the list order and are applied after the common patches are applied.", 816*60517a1eSAndroid Build Coastguard Worker ), 817*60517a1eSAndroid Build Coastguard Worker "platform": attr.string( 818*60517a1eSAndroid Build Coastguard Worker mandatory = True, 819*60517a1eSAndroid Build Coastguard Worker values = PLATFORMS.keys(), 820*60517a1eSAndroid Build Coastguard Worker doc = "The platform to override the values for, must be one of:\n{}.".format("\n".join(sorted(["* `{}`".format(p) for p in PLATFORMS]))), 821*60517a1eSAndroid Build Coastguard Worker ), 822*60517a1eSAndroid Build Coastguard Worker "python_version": attr.string( 823*60517a1eSAndroid Build Coastguard Worker mandatory = True, 824*60517a1eSAndroid Build Coastguard Worker doc = "The python version to override URLs for. Must be in `X.Y.Z` format.", 825*60517a1eSAndroid Build Coastguard Worker ), 826*60517a1eSAndroid Build Coastguard Worker "sha256": attr.string( 827*60517a1eSAndroid Build Coastguard Worker mandatory = False, 828*60517a1eSAndroid Build Coastguard Worker doc = "The sha256 for the archive", 829*60517a1eSAndroid Build Coastguard Worker ), 830*60517a1eSAndroid Build Coastguard Worker "strip_prefix": attr.string( 831*60517a1eSAndroid Build Coastguard Worker mandatory = False, 832*60517a1eSAndroid Build Coastguard Worker doc = "The 'strip_prefix' for the archive, defaults to 'python'.", 833*60517a1eSAndroid Build Coastguard Worker default = "python", 834*60517a1eSAndroid Build Coastguard Worker ), 835*60517a1eSAndroid Build Coastguard Worker "urls": attr.string_list( 836*60517a1eSAndroid Build Coastguard Worker mandatory = False, 837*60517a1eSAndroid Build Coastguard Worker doc = "The URL template to fetch releases for this Python version. If the URL template results in a relative fragment, default base URL is going to be used. Occurrences of `{python_version}`, `{platform}` and `{build}` will be interpolated based on the contents in the override and the known {attr}`platform` values.", 838*60517a1eSAndroid Build Coastguard Worker ), 839*60517a1eSAndroid Build Coastguard Worker }, 840*60517a1eSAndroid Build Coastguard Worker) 841*60517a1eSAndroid Build Coastguard Worker 842*60517a1eSAndroid Build Coastguard Workerpython = module_extension( 843*60517a1eSAndroid Build Coastguard Worker doc = """Bzlmod extension that is used to register Python toolchains. 844*60517a1eSAndroid Build Coastguard Worker""", 845*60517a1eSAndroid Build Coastguard Worker implementation = _python_impl, 846*60517a1eSAndroid Build Coastguard Worker tag_classes = { 847*60517a1eSAndroid Build Coastguard Worker "override": _override, 848*60517a1eSAndroid Build Coastguard Worker "single_version_override": _single_version_override, 849*60517a1eSAndroid Build Coastguard Worker "single_version_platform_override": _single_version_platform_override, 850*60517a1eSAndroid Build Coastguard Worker "toolchain": _toolchain, 851*60517a1eSAndroid Build Coastguard Worker }, 852*60517a1eSAndroid Build Coastguard Worker **_get_bazel_version_specific_kwargs() 853*60517a1eSAndroid Build Coastguard Worker) 854*60517a1eSAndroid Build Coastguard Worker 855*60517a1eSAndroid Build Coastguard Worker_DEBUG_BUILD_CONTENT = """ 856*60517a1eSAndroid Build Coastguard Workerpackage( 857*60517a1eSAndroid Build Coastguard Worker default_visibility = ["//visibility:public"], 858*60517a1eSAndroid Build Coastguard Worker) 859*60517a1eSAndroid Build Coastguard Workerexports_files(["debug_info.json"]) 860*60517a1eSAndroid Build Coastguard Worker""" 861*60517a1eSAndroid Build Coastguard Worker 862*60517a1eSAndroid Build Coastguard Workerdef _debug_repo_impl(repo_ctx): 863*60517a1eSAndroid Build Coastguard Worker repo_ctx.file("BUILD.bazel", _DEBUG_BUILD_CONTENT) 864*60517a1eSAndroid Build Coastguard Worker repo_ctx.file("debug_info.json", repo_ctx.attr.debug_info) 865*60517a1eSAndroid Build Coastguard Worker 866*60517a1eSAndroid Build Coastguard Worker_debug_repo = repository_rule( 867*60517a1eSAndroid Build Coastguard Worker implementation = _debug_repo_impl, 868*60517a1eSAndroid Build Coastguard Worker attrs = { 869*60517a1eSAndroid Build Coastguard Worker "debug_info": attr.string(), 870*60517a1eSAndroid Build Coastguard Worker }, 871*60517a1eSAndroid Build Coastguard Worker) 872