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"Implementation of py_wheel rule" 16*60517a1eSAndroid Build Coastguard Worker 17*60517a1eSAndroid Build Coastguard Workerload("//python/private:stamp.bzl", "is_stamping_enabled") 18*60517a1eSAndroid Build Coastguard Workerload(":py_package.bzl", "py_package_lib") 19*60517a1eSAndroid Build Coastguard Workerload(":py_wheel_normalize_pep440.bzl", "normalize_pep440") 20*60517a1eSAndroid Build Coastguard Worker 21*60517a1eSAndroid Build Coastguard WorkerPyWheelInfo = provider( 22*60517a1eSAndroid Build Coastguard Worker doc = "Information about a wheel produced by `py_wheel`", 23*60517a1eSAndroid Build Coastguard Worker fields = { 24*60517a1eSAndroid Build Coastguard Worker "name_file": ( 25*60517a1eSAndroid Build Coastguard Worker "File: A file containing the canonical name of the wheel (after " + 26*60517a1eSAndroid Build Coastguard Worker "stamping, if enabled)." 27*60517a1eSAndroid Build Coastguard Worker ), 28*60517a1eSAndroid Build Coastguard Worker "wheel": "File: The wheel file itself.", 29*60517a1eSAndroid Build Coastguard Worker }, 30*60517a1eSAndroid Build Coastguard Worker) 31*60517a1eSAndroid Build Coastguard Worker 32*60517a1eSAndroid Build Coastguard Worker_distribution_attrs = { 33*60517a1eSAndroid Build Coastguard Worker "abi": attr.string( 34*60517a1eSAndroid Build Coastguard Worker default = "none", 35*60517a1eSAndroid Build Coastguard Worker doc = "Python ABI tag. 'none' for pure-Python wheels.", 36*60517a1eSAndroid Build Coastguard Worker ), 37*60517a1eSAndroid Build Coastguard Worker "distribution": attr.string( 38*60517a1eSAndroid Build Coastguard Worker mandatory = True, 39*60517a1eSAndroid Build Coastguard Worker doc = """\ 40*60517a1eSAndroid Build Coastguard WorkerName of the distribution. 41*60517a1eSAndroid Build Coastguard Worker 42*60517a1eSAndroid Build Coastguard WorkerThis should match the project name on PyPI. It's also the name that is used to 43*60517a1eSAndroid Build Coastguard Workerrefer to the package in other packages' dependencies. 44*60517a1eSAndroid Build Coastguard Worker 45*60517a1eSAndroid Build Coastguard WorkerWorkspace status keys are expanded using `{NAME}` format, for example: 46*60517a1eSAndroid Build Coastguard Worker - `distribution = "package.{CLASSIFIER}"` 47*60517a1eSAndroid Build Coastguard Worker - `distribution = "{DISTRIBUTION}"` 48*60517a1eSAndroid Build Coastguard Worker 49*60517a1eSAndroid Build Coastguard WorkerFor the available keys, see https://bazel.build/docs/user-manual#workspace-status 50*60517a1eSAndroid Build Coastguard Worker""", 51*60517a1eSAndroid Build Coastguard Worker ), 52*60517a1eSAndroid Build Coastguard Worker "platform": attr.string( 53*60517a1eSAndroid Build Coastguard Worker default = "any", 54*60517a1eSAndroid Build Coastguard Worker doc = """\ 55*60517a1eSAndroid Build Coastguard WorkerSupported platform. Use 'any' for pure-Python wheel. 56*60517a1eSAndroid Build Coastguard Worker 57*60517a1eSAndroid Build Coastguard WorkerIf you have included platform-specific data, such as a .pyd or .so 58*60517a1eSAndroid Build Coastguard Workerextension module, you will need to specify the platform in standard 59*60517a1eSAndroid Build Coastguard Workerpip format. If you support multiple platforms, you can define 60*60517a1eSAndroid Build Coastguard Workerplatform constraints, then use a select() to specify the appropriate 61*60517a1eSAndroid Build Coastguard Workerspecifier, eg: 62*60517a1eSAndroid Build Coastguard Worker 63*60517a1eSAndroid Build Coastguard Worker` 64*60517a1eSAndroid Build Coastguard Workerplatform = select({ 65*60517a1eSAndroid Build Coastguard Worker "//platforms:windows_x86_64": "win_amd64", 66*60517a1eSAndroid Build Coastguard Worker "//platforms:macos_x86_64": "macosx_10_7_x86_64", 67*60517a1eSAndroid Build Coastguard Worker "//platforms:linux_x86_64": "manylinux2014_x86_64", 68*60517a1eSAndroid Build Coastguard Worker}) 69*60517a1eSAndroid Build Coastguard Worker` 70*60517a1eSAndroid Build Coastguard Worker""", 71*60517a1eSAndroid Build Coastguard Worker ), 72*60517a1eSAndroid Build Coastguard Worker "python_tag": attr.string( 73*60517a1eSAndroid Build Coastguard Worker default = "py3", 74*60517a1eSAndroid Build Coastguard Worker doc = "Supported Python version(s), eg `py3`, `cp35.cp36`, etc", 75*60517a1eSAndroid Build Coastguard Worker ), 76*60517a1eSAndroid Build Coastguard Worker "stamp": attr.int( 77*60517a1eSAndroid Build Coastguard Worker doc = """\ 78*60517a1eSAndroid Build Coastguard WorkerWhether to encode build information into the wheel. Possible values: 79*60517a1eSAndroid Build Coastguard Worker 80*60517a1eSAndroid Build Coastguard Worker- `stamp = 1`: Always stamp the build information into the wheel, even in \ 81*60517a1eSAndroid Build Coastguard Worker[--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. \ 82*60517a1eSAndroid Build Coastguard WorkerThis setting should be avoided, since it potentially kills remote caching for the target and \ 83*60517a1eSAndroid Build Coastguard Workerany downstream actions that depend on it. 84*60517a1eSAndroid Build Coastguard Worker 85*60517a1eSAndroid Build Coastguard Worker- `stamp = 0`: Always replace build information by constant values. This gives good build result caching. 86*60517a1eSAndroid Build Coastguard Worker 87*60517a1eSAndroid Build Coastguard Worker- `stamp = -1`: Embedding of build information is controlled by the \ 88*60517a1eSAndroid Build Coastguard Worker[--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag. 89*60517a1eSAndroid Build Coastguard Worker 90*60517a1eSAndroid Build Coastguard WorkerStamped targets are not rebuilt unless their dependencies change. 91*60517a1eSAndroid Build Coastguard Worker """, 92*60517a1eSAndroid Build Coastguard Worker default = -1, 93*60517a1eSAndroid Build Coastguard Worker values = [1, 0, -1], 94*60517a1eSAndroid Build Coastguard Worker ), 95*60517a1eSAndroid Build Coastguard Worker "version": attr.string( 96*60517a1eSAndroid Build Coastguard Worker mandatory = True, 97*60517a1eSAndroid Build Coastguard Worker doc = """\ 98*60517a1eSAndroid Build Coastguard WorkerVersion number of the package. 99*60517a1eSAndroid Build Coastguard Worker 100*60517a1eSAndroid Build Coastguard WorkerNote that this attribute supports stamp format strings as well as 'make variables'. 101*60517a1eSAndroid Build Coastguard WorkerFor example: 102*60517a1eSAndroid Build Coastguard Worker - `version = "1.2.3-{BUILD_TIMESTAMP}"` 103*60517a1eSAndroid Build Coastguard Worker - `version = "{BUILD_EMBED_LABEL}"` 104*60517a1eSAndroid Build Coastguard Worker - `version = "$(VERSION)"` 105*60517a1eSAndroid Build Coastguard Worker 106*60517a1eSAndroid Build Coastguard WorkerNote that Bazel's output filename cannot include the stamp information, as outputs must be known 107*60517a1eSAndroid Build Coastguard Workerduring the analysis phase and the stamp data is available only during the action execution. 108*60517a1eSAndroid Build Coastguard Worker 109*60517a1eSAndroid Build Coastguard WorkerThe [`py_wheel`](#py_wheel) macro produces a `.dist`-suffix target which creates a 110*60517a1eSAndroid Build Coastguard Worker`dist/` folder containing the wheel with the stamped name, suitable for publishing. 111*60517a1eSAndroid Build Coastguard Worker 112*60517a1eSAndroid Build Coastguard WorkerSee [`py_wheel_dist`](#py_wheel_dist) for more info. 113*60517a1eSAndroid Build Coastguard Worker""", 114*60517a1eSAndroid Build Coastguard Worker ), 115*60517a1eSAndroid Build Coastguard Worker "_stamp_flag": attr.label( 116*60517a1eSAndroid Build Coastguard Worker doc = "A setting used to determine whether or not the `--stamp` flag is enabled", 117*60517a1eSAndroid Build Coastguard Worker default = Label("//python/private:stamp"), 118*60517a1eSAndroid Build Coastguard Worker ), 119*60517a1eSAndroid Build Coastguard Worker} 120*60517a1eSAndroid Build Coastguard Worker 121*60517a1eSAndroid Build Coastguard Worker_feature_flags = {} 122*60517a1eSAndroid Build Coastguard Worker 123*60517a1eSAndroid Build Coastguard WorkerALLOWED_DATA_FILE_PREFIX = ("purelib", "platlib", "headers", "scripts", "data") 124*60517a1eSAndroid Build Coastguard Worker_requirement_attrs = { 125*60517a1eSAndroid Build Coastguard Worker "extra_requires": attr.string_list_dict( 126*60517a1eSAndroid Build Coastguard Worker doc = ("A mapping of [extras](https://peps.python.org/pep-0508/#extras) options to lists of requirements (similar to `requires`). This attribute " + 127*60517a1eSAndroid Build Coastguard Worker "is mutually exclusive with `extra_requires_file`."), 128*60517a1eSAndroid Build Coastguard Worker ), 129*60517a1eSAndroid Build Coastguard Worker "extra_requires_files": attr.label_keyed_string_dict( 130*60517a1eSAndroid Build Coastguard Worker doc = ("A mapping of requirements files (similar to `requires_file`) to the name of an [extras](https://peps.python.org/pep-0508/#extras) option " + 131*60517a1eSAndroid Build Coastguard Worker "This attribute is mutually exclusive with `extra_requires`."), 132*60517a1eSAndroid Build Coastguard Worker allow_files = True, 133*60517a1eSAndroid Build Coastguard Worker ), 134*60517a1eSAndroid Build Coastguard Worker "requires": attr.string_list( 135*60517a1eSAndroid Build Coastguard Worker doc = ("List of requirements for this package. See the section on " + 136*60517a1eSAndroid Build Coastguard Worker "[Declaring required dependency](https://setuptools.readthedocs.io/en/latest/userguide/dependency_management.html#declaring-dependencies) " + 137*60517a1eSAndroid Build Coastguard Worker "for details and examples of the format of this argument. This " + 138*60517a1eSAndroid Build Coastguard Worker "attribute is mutually exclusive with `requires_file`."), 139*60517a1eSAndroid Build Coastguard Worker ), 140*60517a1eSAndroid Build Coastguard Worker "requires_file": attr.label( 141*60517a1eSAndroid Build Coastguard Worker doc = ("A file containing a list of requirements for this package. See the section on " + 142*60517a1eSAndroid Build Coastguard Worker "[Declaring required dependency](https://setuptools.readthedocs.io/en/latest/userguide/dependency_management.html#declaring-dependencies) " + 143*60517a1eSAndroid Build Coastguard Worker "for details and examples of the format of this argument. This " + 144*60517a1eSAndroid Build Coastguard Worker "attribute is mutually exclusive with `requires`."), 145*60517a1eSAndroid Build Coastguard Worker allow_single_file = True, 146*60517a1eSAndroid Build Coastguard Worker ), 147*60517a1eSAndroid Build Coastguard Worker} 148*60517a1eSAndroid Build Coastguard Worker 149*60517a1eSAndroid Build Coastguard Worker_entrypoint_attrs = { 150*60517a1eSAndroid Build Coastguard Worker "console_scripts": attr.string_dict( 151*60517a1eSAndroid Build Coastguard Worker doc = """\ 152*60517a1eSAndroid Build Coastguard WorkerDeprecated console_script entry points, e.g. `{'main': 'examples.wheel.main:main'}`. 153*60517a1eSAndroid Build Coastguard Worker 154*60517a1eSAndroid Build Coastguard WorkerDeprecated: prefer the `entry_points` attribute, which supports `console_scripts` as well as other entry points. 155*60517a1eSAndroid Build Coastguard Worker""", 156*60517a1eSAndroid Build Coastguard Worker ), 157*60517a1eSAndroid Build Coastguard Worker "entry_points": attr.string_list_dict( 158*60517a1eSAndroid Build Coastguard Worker doc = """\ 159*60517a1eSAndroid Build Coastguard Workerentry_points, e.g. `{'console_scripts': ['main = examples.wheel.main:main']}`. 160*60517a1eSAndroid Build Coastguard Worker""", 161*60517a1eSAndroid Build Coastguard Worker ), 162*60517a1eSAndroid Build Coastguard Worker} 163*60517a1eSAndroid Build Coastguard Worker 164*60517a1eSAndroid Build Coastguard Worker_other_attrs = { 165*60517a1eSAndroid Build Coastguard Worker "author": attr.string( 166*60517a1eSAndroid Build Coastguard Worker doc = "A string specifying the author of the package.", 167*60517a1eSAndroid Build Coastguard Worker default = "", 168*60517a1eSAndroid Build Coastguard Worker ), 169*60517a1eSAndroid Build Coastguard Worker "author_email": attr.string( 170*60517a1eSAndroid Build Coastguard Worker doc = "A string specifying the email address of the package author.", 171*60517a1eSAndroid Build Coastguard Worker default = "", 172*60517a1eSAndroid Build Coastguard Worker ), 173*60517a1eSAndroid Build Coastguard Worker "classifiers": attr.string_list( 174*60517a1eSAndroid Build Coastguard Worker doc = "A list of strings describing the categories for the package. For valid classifiers see https://pypi.org/classifiers", 175*60517a1eSAndroid Build Coastguard Worker ), 176*60517a1eSAndroid Build Coastguard Worker "data_files": attr.label_keyed_string_dict( 177*60517a1eSAndroid Build Coastguard Worker doc = ("Any file that is not normally installed inside site-packages goes into the .data directory, named " + 178*60517a1eSAndroid Build Coastguard Worker "as the .dist-info directory but with the .data/ extension. Allowed paths: {prefixes}".format(prefixes = ALLOWED_DATA_FILE_PREFIX)), 179*60517a1eSAndroid Build Coastguard Worker allow_files = True, 180*60517a1eSAndroid Build Coastguard Worker ), 181*60517a1eSAndroid Build Coastguard Worker "description_content_type": attr.string( 182*60517a1eSAndroid Build Coastguard Worker doc = ("The type of contents in description_file. " + 183*60517a1eSAndroid Build Coastguard Worker "If not provided, the type will be inferred from the extension of description_file. " + 184*60517a1eSAndroid Build Coastguard Worker "Also see https://packaging.python.org/en/latest/specifications/core-metadata/#description-content-type"), 185*60517a1eSAndroid Build Coastguard Worker ), 186*60517a1eSAndroid Build Coastguard Worker "description_file": attr.label( 187*60517a1eSAndroid Build Coastguard Worker doc = "A file containing text describing the package.", 188*60517a1eSAndroid Build Coastguard Worker allow_single_file = True, 189*60517a1eSAndroid Build Coastguard Worker ), 190*60517a1eSAndroid Build Coastguard Worker "extra_distinfo_files": attr.label_keyed_string_dict( 191*60517a1eSAndroid Build Coastguard Worker doc = "Extra files to add to distinfo directory in the archive.", 192*60517a1eSAndroid Build Coastguard Worker allow_files = True, 193*60517a1eSAndroid Build Coastguard Worker ), 194*60517a1eSAndroid Build Coastguard Worker "homepage": attr.string( 195*60517a1eSAndroid Build Coastguard Worker doc = "A string specifying the URL for the package homepage.", 196*60517a1eSAndroid Build Coastguard Worker default = "", 197*60517a1eSAndroid Build Coastguard Worker ), 198*60517a1eSAndroid Build Coastguard Worker "license": attr.string( 199*60517a1eSAndroid Build Coastguard Worker doc = "A string specifying the license of the package.", 200*60517a1eSAndroid Build Coastguard Worker default = "", 201*60517a1eSAndroid Build Coastguard Worker ), 202*60517a1eSAndroid Build Coastguard Worker "project_urls": attr.string_dict( 203*60517a1eSAndroid Build Coastguard Worker doc = ("A string dict specifying additional browsable URLs for the project and corresponding labels, " + 204*60517a1eSAndroid Build Coastguard Worker "where label is the key and url is the value. " + 205*60517a1eSAndroid Build Coastguard Worker 'e.g `{{"Bug Tracker": "http://bitbucket.org/tarek/distribute/issues/"}}`'), 206*60517a1eSAndroid Build Coastguard Worker ), 207*60517a1eSAndroid Build Coastguard Worker "python_requires": attr.string( 208*60517a1eSAndroid Build Coastguard Worker doc = ( 209*60517a1eSAndroid Build Coastguard Worker "Python versions required by this distribution, e.g. '>=3.5,<3.7'" 210*60517a1eSAndroid Build Coastguard Worker ), 211*60517a1eSAndroid Build Coastguard Worker default = "", 212*60517a1eSAndroid Build Coastguard Worker ), 213*60517a1eSAndroid Build Coastguard Worker "strip_path_prefixes": attr.string_list( 214*60517a1eSAndroid Build Coastguard Worker default = [], 215*60517a1eSAndroid Build Coastguard Worker doc = "path prefixes to strip from files added to the generated package", 216*60517a1eSAndroid Build Coastguard Worker ), 217*60517a1eSAndroid Build Coastguard Worker "summary": attr.string( 218*60517a1eSAndroid Build Coastguard Worker doc = "A one-line summary of what the distribution does", 219*60517a1eSAndroid Build Coastguard Worker ), 220*60517a1eSAndroid Build Coastguard Worker} 221*60517a1eSAndroid Build Coastguard Worker 222*60517a1eSAndroid Build Coastguard Worker_PROJECT_URL_LABEL_LENGTH_LIMIT = 32 223*60517a1eSAndroid Build Coastguard Worker_DESCRIPTION_FILE_EXTENSION_TO_TYPE = { 224*60517a1eSAndroid Build Coastguard Worker "md": "text/markdown", 225*60517a1eSAndroid Build Coastguard Worker "rst": "text/x-rst", 226*60517a1eSAndroid Build Coastguard Worker} 227*60517a1eSAndroid Build Coastguard Worker_DEFAULT_DESCRIPTION_FILE_TYPE = "text/plain" 228*60517a1eSAndroid Build Coastguard Worker 229*60517a1eSAndroid Build Coastguard Workerdef _escape_filename_distribution_name(name): 230*60517a1eSAndroid Build Coastguard Worker """Escape the distribution name component of a filename. 231*60517a1eSAndroid Build Coastguard Worker 232*60517a1eSAndroid Build Coastguard Worker See https://packaging.python.org/en/latest/specifications/binary-distribution-format/#escaping-and-unicode 233*60517a1eSAndroid Build Coastguard Worker and https://packaging.python.org/en/latest/specifications/name-normalization/. 234*60517a1eSAndroid Build Coastguard Worker 235*60517a1eSAndroid Build Coastguard Worker Apart from the valid names according to the above, we also accept 236*60517a1eSAndroid Build Coastguard Worker '{' and '}', which may be used as placeholders for stamping. 237*60517a1eSAndroid Build Coastguard Worker """ 238*60517a1eSAndroid Build Coastguard Worker escaped = "" 239*60517a1eSAndroid Build Coastguard Worker _inside_stamp_var = False 240*60517a1eSAndroid Build Coastguard Worker for character in name.elems(): 241*60517a1eSAndroid Build Coastguard Worker if character == "{": 242*60517a1eSAndroid Build Coastguard Worker _inside_stamp_var = True 243*60517a1eSAndroid Build Coastguard Worker escaped += character 244*60517a1eSAndroid Build Coastguard Worker elif character == "}": 245*60517a1eSAndroid Build Coastguard Worker _inside_stamp_var = False 246*60517a1eSAndroid Build Coastguard Worker escaped += character 247*60517a1eSAndroid Build Coastguard Worker elif character.isalnum(): 248*60517a1eSAndroid Build Coastguard Worker escaped += character if _inside_stamp_var else character.lower() 249*60517a1eSAndroid Build Coastguard Worker elif character in ["-", "_", "."]: 250*60517a1eSAndroid Build Coastguard Worker if escaped == "": 251*60517a1eSAndroid Build Coastguard Worker fail( 252*60517a1eSAndroid Build Coastguard Worker "A valid name must start with a letter or number.", 253*60517a1eSAndroid Build Coastguard Worker "Name '%s' does not." % name, 254*60517a1eSAndroid Build Coastguard Worker ) 255*60517a1eSAndroid Build Coastguard Worker elif escaped.endswith("_"): 256*60517a1eSAndroid Build Coastguard Worker pass 257*60517a1eSAndroid Build Coastguard Worker else: 258*60517a1eSAndroid Build Coastguard Worker escaped += "_" 259*60517a1eSAndroid Build Coastguard Worker else: 260*60517a1eSAndroid Build Coastguard Worker fail( 261*60517a1eSAndroid Build Coastguard Worker "A valid name consists only of ASCII letters ", 262*60517a1eSAndroid Build Coastguard Worker "and numbers, period, underscore and hyphen.", 263*60517a1eSAndroid Build Coastguard Worker "Name '%s' has bad character '%s'." % (name, character), 264*60517a1eSAndroid Build Coastguard Worker ) 265*60517a1eSAndroid Build Coastguard Worker if escaped.endswith("_"): 266*60517a1eSAndroid Build Coastguard Worker fail( 267*60517a1eSAndroid Build Coastguard Worker "A valid name must end with a letter or number.", 268*60517a1eSAndroid Build Coastguard Worker "Name '%s' does not." % name, 269*60517a1eSAndroid Build Coastguard Worker ) 270*60517a1eSAndroid Build Coastguard Worker return escaped 271*60517a1eSAndroid Build Coastguard Worker 272*60517a1eSAndroid Build Coastguard Workerdef _escape_filename_segment(segment): 273*60517a1eSAndroid Build Coastguard Worker """Escape a segment of the wheel filename. 274*60517a1eSAndroid Build Coastguard Worker 275*60517a1eSAndroid Build Coastguard Worker See https://www.python.org/dev/peps/pep-0427/#escaping-and-unicode 276*60517a1eSAndroid Build Coastguard Worker """ 277*60517a1eSAndroid Build Coastguard Worker 278*60517a1eSAndroid Build Coastguard Worker # TODO: this is wrong, isalnum replaces non-ascii letters, while we should 279*60517a1eSAndroid Build Coastguard Worker # not replace them. 280*60517a1eSAndroid Build Coastguard Worker # TODO: replace this with a regexp once starlark supports them. 281*60517a1eSAndroid Build Coastguard Worker escaped = "" 282*60517a1eSAndroid Build Coastguard Worker for character in segment.elems(): 283*60517a1eSAndroid Build Coastguard Worker # isalnum doesn't handle unicode characters properly. 284*60517a1eSAndroid Build Coastguard Worker if character.isalnum() or character == ".": 285*60517a1eSAndroid Build Coastguard Worker escaped += character 286*60517a1eSAndroid Build Coastguard Worker elif not escaped.endswith("_"): 287*60517a1eSAndroid Build Coastguard Worker escaped += "_" 288*60517a1eSAndroid Build Coastguard Worker return escaped 289*60517a1eSAndroid Build Coastguard Worker 290*60517a1eSAndroid Build Coastguard Workerdef _replace_make_variables(flag, ctx): 291*60517a1eSAndroid Build Coastguard Worker """Replace $(VERSION) etc make variables in flag""" 292*60517a1eSAndroid Build Coastguard Worker if "$" in flag: 293*60517a1eSAndroid Build Coastguard Worker for varname, varsub in ctx.var.items(): 294*60517a1eSAndroid Build Coastguard Worker flag = flag.replace("$(%s)" % varname, varsub) 295*60517a1eSAndroid Build Coastguard Worker return flag 296*60517a1eSAndroid Build Coastguard Worker 297*60517a1eSAndroid Build Coastguard Workerdef _input_file_to_arg(input_file): 298*60517a1eSAndroid Build Coastguard Worker """Converts a File object to string for --input_file argument to wheelmaker""" 299*60517a1eSAndroid Build Coastguard Worker return "%s;%s" % (py_package_lib.path_inside_wheel(input_file), input_file.path) 300*60517a1eSAndroid Build Coastguard Worker 301*60517a1eSAndroid Build Coastguard Workerdef _py_wheel_impl(ctx): 302*60517a1eSAndroid Build Coastguard Worker abi = _replace_make_variables(ctx.attr.abi, ctx) 303*60517a1eSAndroid Build Coastguard Worker python_tag = _replace_make_variables(ctx.attr.python_tag, ctx) 304*60517a1eSAndroid Build Coastguard Worker version = _replace_make_variables(ctx.attr.version, ctx) 305*60517a1eSAndroid Build Coastguard Worker 306*60517a1eSAndroid Build Coastguard Worker filename_segments = [ 307*60517a1eSAndroid Build Coastguard Worker _escape_filename_distribution_name(ctx.attr.distribution), 308*60517a1eSAndroid Build Coastguard Worker normalize_pep440(version), 309*60517a1eSAndroid Build Coastguard Worker _escape_filename_segment(python_tag), 310*60517a1eSAndroid Build Coastguard Worker _escape_filename_segment(abi), 311*60517a1eSAndroid Build Coastguard Worker _escape_filename_segment(ctx.attr.platform), 312*60517a1eSAndroid Build Coastguard Worker ] 313*60517a1eSAndroid Build Coastguard Worker 314*60517a1eSAndroid Build Coastguard Worker outfile = ctx.actions.declare_file("-".join(filename_segments) + ".whl") 315*60517a1eSAndroid Build Coastguard Worker 316*60517a1eSAndroid Build Coastguard Worker name_file = ctx.actions.declare_file(ctx.label.name + ".name") 317*60517a1eSAndroid Build Coastguard Worker 318*60517a1eSAndroid Build Coastguard Worker inputs_to_package = depset( 319*60517a1eSAndroid Build Coastguard Worker direct = ctx.files.deps, 320*60517a1eSAndroid Build Coastguard Worker ) 321*60517a1eSAndroid Build Coastguard Worker 322*60517a1eSAndroid Build Coastguard Worker # Inputs to this rule which are not to be packaged. 323*60517a1eSAndroid Build Coastguard Worker # Currently this is only the description file (if used). 324*60517a1eSAndroid Build Coastguard Worker other_inputs = [] 325*60517a1eSAndroid Build Coastguard Worker 326*60517a1eSAndroid Build Coastguard Worker # Wrap the inputs into a file to reduce command line length. 327*60517a1eSAndroid Build Coastguard Worker packageinputfile = ctx.actions.declare_file(ctx.attr.name + "_target_wrapped_inputs.txt") 328*60517a1eSAndroid Build Coastguard Worker content = "" 329*60517a1eSAndroid Build Coastguard Worker for input_file in inputs_to_package.to_list(): 330*60517a1eSAndroid Build Coastguard Worker content += _input_file_to_arg(input_file) + "\n" 331*60517a1eSAndroid Build Coastguard Worker ctx.actions.write(output = packageinputfile, content = content) 332*60517a1eSAndroid Build Coastguard Worker other_inputs.append(packageinputfile) 333*60517a1eSAndroid Build Coastguard Worker 334*60517a1eSAndroid Build Coastguard Worker args = ctx.actions.args() 335*60517a1eSAndroid Build Coastguard Worker args.add("--name", ctx.attr.distribution) 336*60517a1eSAndroid Build Coastguard Worker args.add("--version", version) 337*60517a1eSAndroid Build Coastguard Worker args.add("--python_tag", python_tag) 338*60517a1eSAndroid Build Coastguard Worker args.add("--abi", abi) 339*60517a1eSAndroid Build Coastguard Worker args.add("--platform", ctx.attr.platform) 340*60517a1eSAndroid Build Coastguard Worker args.add("--out", outfile) 341*60517a1eSAndroid Build Coastguard Worker args.add("--name_file", name_file) 342*60517a1eSAndroid Build Coastguard Worker args.add_all(ctx.attr.strip_path_prefixes, format_each = "--strip_path_prefix=%s") 343*60517a1eSAndroid Build Coastguard Worker 344*60517a1eSAndroid Build Coastguard Worker # Pass workspace status files if stamping is enabled 345*60517a1eSAndroid Build Coastguard Worker if is_stamping_enabled(ctx.attr): 346*60517a1eSAndroid Build Coastguard Worker args.add("--volatile_status_file", ctx.version_file) 347*60517a1eSAndroid Build Coastguard Worker args.add("--stable_status_file", ctx.info_file) 348*60517a1eSAndroid Build Coastguard Worker other_inputs.extend([ctx.version_file, ctx.info_file]) 349*60517a1eSAndroid Build Coastguard Worker 350*60517a1eSAndroid Build Coastguard Worker args.add("--input_file_list", packageinputfile) 351*60517a1eSAndroid Build Coastguard Worker 352*60517a1eSAndroid Build Coastguard Worker # Note: Description file and version are not embedded into metadata.txt yet, 353*60517a1eSAndroid Build Coastguard Worker # it will be done later by wheelmaker script. 354*60517a1eSAndroid Build Coastguard Worker metadata_file = ctx.actions.declare_file(ctx.attr.name + ".metadata.txt") 355*60517a1eSAndroid Build Coastguard Worker metadata_contents = ["Metadata-Version: 2.1"] 356*60517a1eSAndroid Build Coastguard Worker metadata_contents.append("Name: %s" % ctx.attr.distribution) 357*60517a1eSAndroid Build Coastguard Worker 358*60517a1eSAndroid Build Coastguard Worker if ctx.attr.author: 359*60517a1eSAndroid Build Coastguard Worker metadata_contents.append("Author: %s" % ctx.attr.author) 360*60517a1eSAndroid Build Coastguard Worker if ctx.attr.author_email: 361*60517a1eSAndroid Build Coastguard Worker metadata_contents.append("Author-email: %s" % ctx.attr.author_email) 362*60517a1eSAndroid Build Coastguard Worker if ctx.attr.homepage: 363*60517a1eSAndroid Build Coastguard Worker metadata_contents.append("Home-page: %s" % ctx.attr.homepage) 364*60517a1eSAndroid Build Coastguard Worker if ctx.attr.license: 365*60517a1eSAndroid Build Coastguard Worker metadata_contents.append("License: %s" % ctx.attr.license) 366*60517a1eSAndroid Build Coastguard Worker if ctx.attr.description_content_type: 367*60517a1eSAndroid Build Coastguard Worker metadata_contents.append("Description-Content-Type: %s" % ctx.attr.description_content_type) 368*60517a1eSAndroid Build Coastguard Worker elif ctx.attr.description_file: 369*60517a1eSAndroid Build Coastguard Worker # infer the content type from description file extension. 370*60517a1eSAndroid Build Coastguard Worker description_file_type = _DESCRIPTION_FILE_EXTENSION_TO_TYPE.get( 371*60517a1eSAndroid Build Coastguard Worker ctx.file.description_file.extension, 372*60517a1eSAndroid Build Coastguard Worker _DEFAULT_DESCRIPTION_FILE_TYPE, 373*60517a1eSAndroid Build Coastguard Worker ) 374*60517a1eSAndroid Build Coastguard Worker metadata_contents.append("Description-Content-Type: %s" % description_file_type) 375*60517a1eSAndroid Build Coastguard Worker if ctx.attr.summary: 376*60517a1eSAndroid Build Coastguard Worker metadata_contents.append("Summary: %s" % ctx.attr.summary) 377*60517a1eSAndroid Build Coastguard Worker 378*60517a1eSAndroid Build Coastguard Worker for label, url in sorted(ctx.attr.project_urls.items()): 379*60517a1eSAndroid Build Coastguard Worker if len(label) > _PROJECT_URL_LABEL_LENGTH_LIMIT: 380*60517a1eSAndroid Build Coastguard Worker fail("`label` {} in `project_urls` is too long. It is limited to {} characters.".format(len(label), _PROJECT_URL_LABEL_LENGTH_LIMIT)) 381*60517a1eSAndroid Build Coastguard Worker metadata_contents.append("Project-URL: %s, %s" % (label, url)) 382*60517a1eSAndroid Build Coastguard Worker 383*60517a1eSAndroid Build Coastguard Worker for c in ctx.attr.classifiers: 384*60517a1eSAndroid Build Coastguard Worker metadata_contents.append("Classifier: %s" % c) 385*60517a1eSAndroid Build Coastguard Worker 386*60517a1eSAndroid Build Coastguard Worker if ctx.attr.python_requires: 387*60517a1eSAndroid Build Coastguard Worker metadata_contents.append("Requires-Python: %s" % ctx.attr.python_requires) 388*60517a1eSAndroid Build Coastguard Worker 389*60517a1eSAndroid Build Coastguard Worker if ctx.attr.requires and ctx.attr.requires_file: 390*60517a1eSAndroid Build Coastguard Worker fail("`requires` and `requires_file` are mutually exclusive. Please update {}".format(ctx.label)) 391*60517a1eSAndroid Build Coastguard Worker 392*60517a1eSAndroid Build Coastguard Worker for requires in ctx.attr.requires: 393*60517a1eSAndroid Build Coastguard Worker metadata_contents.append("Requires-Dist: %s" % requires) 394*60517a1eSAndroid Build Coastguard Worker if ctx.attr.requires_file: 395*60517a1eSAndroid Build Coastguard Worker # The @ prefixed paths will be resolved by the PyWheel action. 396*60517a1eSAndroid Build Coastguard Worker # Expanding each line containing a constraint in place of this 397*60517a1eSAndroid Build Coastguard Worker # directive. 398*60517a1eSAndroid Build Coastguard Worker metadata_contents.append("Requires-Dist: @%s" % ctx.file.requires_file.path) 399*60517a1eSAndroid Build Coastguard Worker other_inputs.append(ctx.file.requires_file) 400*60517a1eSAndroid Build Coastguard Worker 401*60517a1eSAndroid Build Coastguard Worker if ctx.attr.extra_requires and ctx.attr.extra_requires_files: 402*60517a1eSAndroid Build Coastguard Worker fail("`extra_requires` and `extra_requires_files` are mutually exclusive. Please update {}".format(ctx.label)) 403*60517a1eSAndroid Build Coastguard Worker for option, option_requirements in sorted(ctx.attr.extra_requires.items()): 404*60517a1eSAndroid Build Coastguard Worker metadata_contents.append("Provides-Extra: %s" % option) 405*60517a1eSAndroid Build Coastguard Worker for requirement in option_requirements: 406*60517a1eSAndroid Build Coastguard Worker metadata_contents.append( 407*60517a1eSAndroid Build Coastguard Worker "Requires-Dist: %s; extra == '%s'" % (requirement, option), 408*60517a1eSAndroid Build Coastguard Worker ) 409*60517a1eSAndroid Build Coastguard Worker extra_requires_files = {} 410*60517a1eSAndroid Build Coastguard Worker for option_requires_target, option in ctx.attr.extra_requires_files.items(): 411*60517a1eSAndroid Build Coastguard Worker if option in extra_requires_files: 412*60517a1eSAndroid Build Coastguard Worker fail("Duplicate `extra_requires_files` option '{}' found on target {}".format(option, ctx.label)) 413*60517a1eSAndroid Build Coastguard Worker option_requires_files = option_requires_target[DefaultInfo].files.to_list() 414*60517a1eSAndroid Build Coastguard Worker if len(option_requires_files) != 1: 415*60517a1eSAndroid Build Coastguard Worker fail("Labels in `extra_requires_files` must result in a single file, but {label} provides {files} from {owner}".format( 416*60517a1eSAndroid Build Coastguard Worker label = ctx.label, 417*60517a1eSAndroid Build Coastguard Worker files = option_requires_files, 418*60517a1eSAndroid Build Coastguard Worker owner = option_requires_target.label, 419*60517a1eSAndroid Build Coastguard Worker )) 420*60517a1eSAndroid Build Coastguard Worker extra_requires_files.update({option: option_requires_files[0]}) 421*60517a1eSAndroid Build Coastguard Worker 422*60517a1eSAndroid Build Coastguard Worker for option, option_requires_file in sorted(extra_requires_files.items()): 423*60517a1eSAndroid Build Coastguard Worker metadata_contents.append("Provides-Extra: %s" % option) 424*60517a1eSAndroid Build Coastguard Worker metadata_contents.append( 425*60517a1eSAndroid Build Coastguard Worker # The @ prefixed paths will be resolved by the PyWheel action. 426*60517a1eSAndroid Build Coastguard Worker # Expanding each line containing a constraint in place of this 427*60517a1eSAndroid Build Coastguard Worker # directive and appending the extra option. 428*60517a1eSAndroid Build Coastguard Worker "Requires-Dist: @%s; extra == '%s'" % (option_requires_file.path, option), 429*60517a1eSAndroid Build Coastguard Worker ) 430*60517a1eSAndroid Build Coastguard Worker other_inputs.append(option_requires_file) 431*60517a1eSAndroid Build Coastguard Worker 432*60517a1eSAndroid Build Coastguard Worker ctx.actions.write( 433*60517a1eSAndroid Build Coastguard Worker output = metadata_file, 434*60517a1eSAndroid Build Coastguard Worker content = "\n".join(metadata_contents) + "\n", 435*60517a1eSAndroid Build Coastguard Worker ) 436*60517a1eSAndroid Build Coastguard Worker other_inputs.append(metadata_file) 437*60517a1eSAndroid Build Coastguard Worker args.add("--metadata_file", metadata_file) 438*60517a1eSAndroid Build Coastguard Worker 439*60517a1eSAndroid Build Coastguard Worker # Merge console_scripts into entry_points. 440*60517a1eSAndroid Build Coastguard Worker entrypoints = dict(ctx.attr.entry_points) # Copy so we can mutate it 441*60517a1eSAndroid Build Coastguard Worker if ctx.attr.console_scripts: 442*60517a1eSAndroid Build Coastguard Worker # Copy a console_scripts group that may already exist, so we can mutate it. 443*60517a1eSAndroid Build Coastguard Worker console_scripts = list(entrypoints.get("console_scripts", [])) 444*60517a1eSAndroid Build Coastguard Worker entrypoints["console_scripts"] = console_scripts 445*60517a1eSAndroid Build Coastguard Worker for name, ref in ctx.attr.console_scripts.items(): 446*60517a1eSAndroid Build Coastguard Worker console_scripts.append("{name} = {ref}".format(name = name, ref = ref)) 447*60517a1eSAndroid Build Coastguard Worker 448*60517a1eSAndroid Build Coastguard Worker # If any entry_points are provided, construct the file here and add it to the files to be packaged. 449*60517a1eSAndroid Build Coastguard Worker # see: https://packaging.python.org/specifications/entry-points/ 450*60517a1eSAndroid Build Coastguard Worker if entrypoints: 451*60517a1eSAndroid Build Coastguard Worker lines = [] 452*60517a1eSAndroid Build Coastguard Worker for group, entries in sorted(entrypoints.items()): 453*60517a1eSAndroid Build Coastguard Worker if lines: 454*60517a1eSAndroid Build Coastguard Worker # Blank line between groups 455*60517a1eSAndroid Build Coastguard Worker lines.append("") 456*60517a1eSAndroid Build Coastguard Worker lines.append("[{group}]".format(group = group)) 457*60517a1eSAndroid Build Coastguard Worker lines += sorted(entries) 458*60517a1eSAndroid Build Coastguard Worker entry_points_file = ctx.actions.declare_file(ctx.attr.name + "_entry_points.txt") 459*60517a1eSAndroid Build Coastguard Worker content = "\n".join(lines) 460*60517a1eSAndroid Build Coastguard Worker ctx.actions.write(output = entry_points_file, content = content) 461*60517a1eSAndroid Build Coastguard Worker other_inputs.append(entry_points_file) 462*60517a1eSAndroid Build Coastguard Worker args.add("--entry_points_file", entry_points_file) 463*60517a1eSAndroid Build Coastguard Worker 464*60517a1eSAndroid Build Coastguard Worker if ctx.attr.description_file: 465*60517a1eSAndroid Build Coastguard Worker description_file = ctx.file.description_file 466*60517a1eSAndroid Build Coastguard Worker args.add("--description_file", description_file) 467*60517a1eSAndroid Build Coastguard Worker other_inputs.append(description_file) 468*60517a1eSAndroid Build Coastguard Worker 469*60517a1eSAndroid Build Coastguard Worker for target, filename in ctx.attr.extra_distinfo_files.items(): 470*60517a1eSAndroid Build Coastguard Worker target_files = target.files.to_list() 471*60517a1eSAndroid Build Coastguard Worker if len(target_files) != 1: 472*60517a1eSAndroid Build Coastguard Worker fail( 473*60517a1eSAndroid Build Coastguard Worker "Multi-file target listed in extra_distinfo_files %s", 474*60517a1eSAndroid Build Coastguard Worker filename, 475*60517a1eSAndroid Build Coastguard Worker ) 476*60517a1eSAndroid Build Coastguard Worker other_inputs.extend(target_files) 477*60517a1eSAndroid Build Coastguard Worker args.add( 478*60517a1eSAndroid Build Coastguard Worker "--extra_distinfo_file", 479*60517a1eSAndroid Build Coastguard Worker filename + ";" + target_files[0].path, 480*60517a1eSAndroid Build Coastguard Worker ) 481*60517a1eSAndroid Build Coastguard Worker 482*60517a1eSAndroid Build Coastguard Worker for target, filename in ctx.attr.data_files.items(): 483*60517a1eSAndroid Build Coastguard Worker target_files = target.files.to_list() 484*60517a1eSAndroid Build Coastguard Worker if len(target_files) != 1: 485*60517a1eSAndroid Build Coastguard Worker fail( 486*60517a1eSAndroid Build Coastguard Worker "Multi-file target listed in data_files %s", 487*60517a1eSAndroid Build Coastguard Worker filename, 488*60517a1eSAndroid Build Coastguard Worker ) 489*60517a1eSAndroid Build Coastguard Worker 490*60517a1eSAndroid Build Coastguard Worker if filename.partition("/")[0] not in ALLOWED_DATA_FILE_PREFIX: 491*60517a1eSAndroid Build Coastguard Worker fail( 492*60517a1eSAndroid Build Coastguard Worker "The target data file must start with one of these prefixes: '%s'. Target filepath: '%s'" % 493*60517a1eSAndroid Build Coastguard Worker ( 494*60517a1eSAndroid Build Coastguard Worker ",".join(ALLOWED_DATA_FILE_PREFIX), 495*60517a1eSAndroid Build Coastguard Worker filename, 496*60517a1eSAndroid Build Coastguard Worker ), 497*60517a1eSAndroid Build Coastguard Worker ) 498*60517a1eSAndroid Build Coastguard Worker other_inputs.extend(target_files) 499*60517a1eSAndroid Build Coastguard Worker args.add( 500*60517a1eSAndroid Build Coastguard Worker "--data_files", 501*60517a1eSAndroid Build Coastguard Worker filename + ";" + target_files[0].path, 502*60517a1eSAndroid Build Coastguard Worker ) 503*60517a1eSAndroid Build Coastguard Worker 504*60517a1eSAndroid Build Coastguard Worker ctx.actions.run( 505*60517a1eSAndroid Build Coastguard Worker mnemonic = "PyWheel", 506*60517a1eSAndroid Build Coastguard Worker inputs = depset(direct = other_inputs, transitive = [inputs_to_package]), 507*60517a1eSAndroid Build Coastguard Worker outputs = [outfile, name_file], 508*60517a1eSAndroid Build Coastguard Worker arguments = [args], 509*60517a1eSAndroid Build Coastguard Worker executable = ctx.executable._wheelmaker, 510*60517a1eSAndroid Build Coastguard Worker progress_message = "Building wheel {}".format(ctx.label), 511*60517a1eSAndroid Build Coastguard Worker ) 512*60517a1eSAndroid Build Coastguard Worker return [ 513*60517a1eSAndroid Build Coastguard Worker DefaultInfo( 514*60517a1eSAndroid Build Coastguard Worker files = depset([outfile]), 515*60517a1eSAndroid Build Coastguard Worker runfiles = ctx.runfiles(files = [outfile]), 516*60517a1eSAndroid Build Coastguard Worker ), 517*60517a1eSAndroid Build Coastguard Worker PyWheelInfo( 518*60517a1eSAndroid Build Coastguard Worker wheel = outfile, 519*60517a1eSAndroid Build Coastguard Worker name_file = name_file, 520*60517a1eSAndroid Build Coastguard Worker ), 521*60517a1eSAndroid Build Coastguard Worker ] 522*60517a1eSAndroid Build Coastguard Worker 523*60517a1eSAndroid Build Coastguard Workerdef _concat_dicts(*dicts): 524*60517a1eSAndroid Build Coastguard Worker result = {} 525*60517a1eSAndroid Build Coastguard Worker for d in dicts: 526*60517a1eSAndroid Build Coastguard Worker result.update(d) 527*60517a1eSAndroid Build Coastguard Worker return result 528*60517a1eSAndroid Build Coastguard Worker 529*60517a1eSAndroid Build Coastguard Workerpy_wheel_lib = struct( 530*60517a1eSAndroid Build Coastguard Worker implementation = _py_wheel_impl, 531*60517a1eSAndroid Build Coastguard Worker attrs = _concat_dicts( 532*60517a1eSAndroid Build Coastguard Worker { 533*60517a1eSAndroid Build Coastguard Worker "deps": attr.label_list( 534*60517a1eSAndroid Build Coastguard Worker doc = """\ 535*60517a1eSAndroid Build Coastguard WorkerTargets to be included in the distribution. 536*60517a1eSAndroid Build Coastguard Worker 537*60517a1eSAndroid Build Coastguard WorkerThe targets to package are usually `py_library` rules or filesets (for packaging data files). 538*60517a1eSAndroid Build Coastguard Worker 539*60517a1eSAndroid Build Coastguard WorkerNote it's usually better to package `py_library` targets and use 540*60517a1eSAndroid Build Coastguard Worker`entry_points` attribute to specify `console_scripts` than to package 541*60517a1eSAndroid Build Coastguard Worker`py_binary` rules. `py_binary` targets would wrap a executable script that 542*60517a1eSAndroid Build Coastguard Workertries to locate `.runfiles` directory which is not packaged in the wheel. 543*60517a1eSAndroid Build Coastguard Worker""", 544*60517a1eSAndroid Build Coastguard Worker ), 545*60517a1eSAndroid Build Coastguard Worker "_wheelmaker": attr.label( 546*60517a1eSAndroid Build Coastguard Worker executable = True, 547*60517a1eSAndroid Build Coastguard Worker cfg = "exec", 548*60517a1eSAndroid Build Coastguard Worker default = "//tools:wheelmaker", 549*60517a1eSAndroid Build Coastguard Worker ), 550*60517a1eSAndroid Build Coastguard Worker }, 551*60517a1eSAndroid Build Coastguard Worker _distribution_attrs, 552*60517a1eSAndroid Build Coastguard Worker _feature_flags, 553*60517a1eSAndroid Build Coastguard Worker _requirement_attrs, 554*60517a1eSAndroid Build Coastguard Worker _entrypoint_attrs, 555*60517a1eSAndroid Build Coastguard Worker _other_attrs, 556*60517a1eSAndroid Build Coastguard Worker ), 557*60517a1eSAndroid Build Coastguard Worker) 558*60517a1eSAndroid Build Coastguard Worker 559*60517a1eSAndroid Build Coastguard Workerpy_wheel = rule( 560*60517a1eSAndroid Build Coastguard Worker implementation = py_wheel_lib.implementation, 561*60517a1eSAndroid Build Coastguard Worker doc = """\ 562*60517a1eSAndroid Build Coastguard WorkerInternal rule used by the [py_wheel macro](#py_wheel). 563*60517a1eSAndroid Build Coastguard Worker 564*60517a1eSAndroid Build Coastguard WorkerThese intentionally have the same name to avoid sharp edges with Bazel macros. 565*60517a1eSAndroid Build Coastguard WorkerFor example, a `bazel query` for a user's `py_wheel` macro expands to `py_wheel` targets, 566*60517a1eSAndroid Build Coastguard Workerin the way they expect. 567*60517a1eSAndroid Build Coastguard Worker""", 568*60517a1eSAndroid Build Coastguard Worker attrs = py_wheel_lib.attrs, 569*60517a1eSAndroid Build Coastguard Worker) 570