1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
"""Rules to create python distribution files and properly name them"""
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
load("@system_python//:version.bzl", "SYSTEM_PYTHON_VERSION")
def _get_suffix(limited_api, python_version, cpu):
"""Computes an ABI version tag for an extension module per PEP 3149."""
if "win32" in cpu or "win64" in cpu:
if limited_api:
return ".pyd"
if "win32" in cpu:
abi = "win32"
elif "win64" in cpu:
abi = "win_amd64"
else:
fail("Unsupported CPU: " + cpu)
return ".cp{}-{}.{}".format(python_version, abi, "pyd")
if python_version == "system":
python_version = SYSTEM_PYTHON_VERSION
if int(python_version) < 38:
python_version += "m"
abis = {
"darwin_arm64": "darwin",
"darwin_x86_64": "darwin",
"darwin": "darwin",
"osx-x86_64": "darwin",
"osx-aarch_64": "darwin",
"linux-aarch_64": "aarch64-linux-gnu",
"linux-x86_64": "x86_64-linux-gnu",
"k8": "x86_64-linux-gnu",
"s390x": "s390x-linux-gnu",
}
return ".cpython-{}-{}.{}".format(
python_version,
abis[cpu],
"so" if limited_api else "abi3.so",
)
elif limited_api:
return ".abi3.so"
fail("Unsupported combination of flags")
def _declare_module_file(ctx, module_name, python_version, limited_api):
"""Declares an output file for a Python module with this name, version, and limited api."""
base_filename = module_name.replace(".", "/")
suffix = _get_suffix(
python_version = python_version,
limited_api = limited_api,
cpu = ctx.var["TARGET_CPU"],
)
filename = base_filename + suffix
return ctx.actions.declare_file(filename)
# --------------------------------------------------------------------------------------------------
# py_dist_module()
#
# Creates a Python binary extension module that is ready for distribution.
#
# py_dist_module(
# name = "message_mod",
# extension = "//python:_message_binary",
# module_name = "google._upb._message",
# )
#
# In the simple case, this simply involves copying the input file to the proper filename for
# our current configuration (module_name, cpu, python_version, limited_abi).
#
# For multiarch platforms (osx-universal2), we must combine binaries for multiple architectures
# into a single output binary using the "llvm-lipo" tool. A config transition depends on multiple
# architectures to get us the input files we need.
def _py_multiarch_transition_impl(settings, attr):
if settings["//command_line_option:cpu"] == "osx-universal2":
return [{"//command_line_option:cpu": cpu} for cpu in ["osx-aarch_64", "osx-x86_64"]]
else:
return settings
_py_multiarch_transition = transition(
implementation = _py_multiarch_transition_impl,
inputs = ["//command_line_option:cpu"],
outputs = ["//command_line_option:cpu"],
)
def _py_dist_module_impl(ctx):
output_file = _declare_module_file(
ctx = ctx,
module_name = ctx.attr.module_name,
python_version = ctx.attr._python_version[BuildSettingInfo].value,
limited_api = ctx.attr._limited_api[BuildSettingInfo].value,
)
if len(ctx.attr.extension) == 1:
src = ctx.attr.extension[0][DefaultInfo].files.to_list()[0]
ctx.actions.run(
executable = "cp",
arguments = [src.path, output_file.path],
inputs = [src],
outputs = [output_file],
)
return [
DefaultInfo(files = depset([output_file])),
]
else:
srcs = [mod[DefaultInfo].files.to_list()[0] for mod in ctx.attr.extension]
ctx.actions.run(
executable = "/usr/local/bin/llvm-lipo",
arguments = ["-create", "-output", output_file.path] + [src.path for src in srcs],
inputs = srcs,
outputs = [output_file],
)
return [
DefaultInfo(files = depset([output_file])),
]
py_dist_module = rule(
implementation = _py_dist_module_impl,
attrs = {
"module_name": attr.string(mandatory = True),
"extension": attr.label(
mandatory = True,
cfg = _py_multiarch_transition,
),
"_limited_api": attr.label(default = "//python:limited_api"),
"_python_version": attr.label(default = "//python:python_version"),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
),
},
)
# --------------------------------------------------------------------------------------------------
# py_dist()
#
# A rule that builds a collection of binary wheels, using transitions to depend on many different
# python versions and cpus.
def _py_dist_transition_impl(settings, attr):
_ignore = (settings) # @unused
transitions = []
for cpu, version in attr.limited_api_wheels.items():
transitions.append({
"//command_line_option:cpu": cpu,
"//python:python_version": version,
"//python:limited_api": True,
})
for version in attr.full_api_versions:
for cpu in attr.full_api_cpus:
transitions.append({
"//command_line_option:cpu": cpu,
"//python:python_version": version,
"//python:limited_api": False,
})
return transitions
_py_dist_transition = transition(
implementation = _py_dist_transition_impl,
inputs = [],
outputs = [
"//command_line_option:cpu",
"//python:python_version",
"//python:limited_api",
],
)
def _py_dist_impl(ctx):
binary_files = [dep[DefaultInfo].files for dep in ctx.attr.binary_wheel]
pure_python_files = [ctx.attr.pure_python_wheel[DefaultInfo].files]
return [
DefaultInfo(files = depset(
transitive = binary_files + pure_python_files,
)),
]
py_dist = rule(
implementation = _py_dist_impl,
attrs = {
"binary_wheel": attr.label(
mandatory = True,
cfg = _py_dist_transition,
),
"pure_python_wheel": attr.label(mandatory = True),
"limited_api_wheels": attr.string_dict(),
"full_api_versions": attr.string_list(),
"full_api_cpus": attr.string_list(),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
),
},
)
|