|
|
|
"""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."""
|
|
|
|
suffix = "pyd" if ("win" in cpu) else "so"
|
|
|
|
|
|
|
|
if limited_api == True:
|
|
|
|
if "win" not in cpu:
|
|
|
|
suffix = "abi3." + suffix
|
|
|
|
return "." + suffix
|
|
|
|
|
|
|
|
if "win32" in cpu or "win64" in cpu:
|
|
|
|
if "win32" in cpu:
|
|
|
|
abi = "win32"
|
|
|
|
elif "win64" in cpu:
|
|
|
|
abi = "win_amd64"
|
|
|
|
else:
|
|
|
|
fail("Unsupported CPU: " + cpu)
|
|
|
|
return ".cp{}-{}.{}".format(python_version, abi, suffix)
|
|
|
|
|
|
|
|
if python_version == "system":
|
|
|
|
python_version = SYSTEM_PYTHON_VERSION
|
|
|
|
if int(python_version) < 38:
|
|
|
|
python_version += "m"
|
|
|
|
abis = {
|
|
|
|
"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",
|
|
|
|
}
|
|
|
|
|
|
|
|
return ".cpython-{}-{}.{}".format(python_version, abis[cpu], suffix)
|
|
|
|
|
|
|
|
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(
|
|
|
|
output_to_genfiles = True,
|
|
|
|
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",
|
|
|
|
),
|
|
|
|
},
|
|
|
|
)
|