mirror of
https://github.com/protocolbuffers/protobuf
synced 2026-08-26 02:23:14 -04:00
235 lines
8 KiB
Python
235 lines
8 KiB
Python
"""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_os_name(ctx):
|
|
for name, label in ctx.attr._os_constraints.items():
|
|
if ctx.target_platform_has_constraint(label[platform_common.ConstraintValueInfo]):
|
|
return name
|
|
fail("Protobuf: Unknown OS for target platform")
|
|
|
|
def _get_cpu_name(ctx):
|
|
for name, label in ctx.attr._cpu_constraints.items():
|
|
if ctx.target_platform_has_constraint(label[platform_common.ConstraintValueInfo]):
|
|
return name
|
|
fail("Protobuf: Unknown CPU for target platform")
|
|
|
|
def _get_suffix(ctx, limited_api, python_version):
|
|
"""Computes an ABI version tag for an extension module per PEP 3149."""
|
|
os = _get_os_name(ctx)
|
|
cpu = _get_cpu_name(ctx)
|
|
if os == "windows":
|
|
if limited_api:
|
|
return ".pyd"
|
|
if cpu == "x86_32":
|
|
abi = "win32"
|
|
elif cpu == "x86_64":
|
|
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"
|
|
|
|
if os == "osx":
|
|
abi = "darwin"
|
|
elif os == "linux":
|
|
if cpu == "x86_64":
|
|
abi = "x86_64-linux-gnu"
|
|
elif cpu == "aarch64":
|
|
abi = "aarch64-linux-gnu"
|
|
elif cpu == "s390x":
|
|
abi = "s390x-linux-gnu"
|
|
else:
|
|
fail("Unsupported CPU: " + cpu)
|
|
else:
|
|
fail("Unsupported OS: " + os)
|
|
|
|
return ".cpython-{}-{}.{}".format(
|
|
python_version,
|
|
abi,
|
|
"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(
|
|
ctx = ctx,
|
|
python_version = python_version,
|
|
limited_api = limited_api,
|
|
)
|
|
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 len(settings["//command_line_option:platforms"]) == 1 and settings["//command_line_option:platforms"][0] == Label("//build_defs:osx-universal2"):
|
|
ret = [
|
|
{"//command_line_option:platforms": platform}
|
|
for platform in [
|
|
"//build_defs:osx-aarch_64",
|
|
"//build_defs:osx-x86_64",
|
|
]
|
|
]
|
|
return ret
|
|
else:
|
|
return settings
|
|
|
|
_py_multiarch_transition = transition(
|
|
implementation = _py_multiarch_transition_impl,
|
|
inputs = ["//command_line_option:platforms"],
|
|
outputs = ["//command_line_option:platforms"],
|
|
)
|
|
|
|
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(
|
|
mnemonic = "PyDistModule",
|
|
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(
|
|
mnemonic = "PyDistModule",
|
|
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",
|
|
),
|
|
"_os_constraints": attr.string_keyed_label_dict(
|
|
default = {
|
|
"osx": "@platforms//os:osx",
|
|
"windows": "@platforms//os:windows",
|
|
"linux": "@platforms//os:linux",
|
|
},
|
|
),
|
|
"_cpu_constraints": attr.string_keyed_label_dict(
|
|
default = {
|
|
"aarch64": "@platforms//cpu:aarch64",
|
|
"x86_64": "@platforms//cpu:x86_64",
|
|
"x86_32": "@platforms//cpu:x86_32",
|
|
"s390x": "@platforms//cpu:s390x",
|
|
"ppc64le": "@platforms//cpu:ppc64le",
|
|
},
|
|
),
|
|
},
|
|
)
|
|
|
|
# --------------------------------------------------------------------------------------------------
|
|
# 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 platform, version in attr.limited_api_platforms.items():
|
|
transitions.append({
|
|
"//command_line_option:platforms": platform,
|
|
"//python:python_version": version,
|
|
"//python:limited_api": True,
|
|
})
|
|
|
|
for version in attr.full_api_versions:
|
|
for platform in attr.full_api_platforms:
|
|
transitions.append({
|
|
"//command_line_option:platforms": platform,
|
|
"//python:python_version": version,
|
|
"//python:limited_api": False,
|
|
})
|
|
|
|
return transitions
|
|
|
|
_py_dist_transition = transition(
|
|
implementation = _py_dist_transition_impl,
|
|
inputs = [],
|
|
outputs = [
|
|
"//command_line_option:platforms",
|
|
"//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_platforms": attr.string_dict(),
|
|
"full_api_versions": attr.string_list(),
|
|
"full_api_platforms": attr.string_list(),
|
|
"_allowlist_function_transition": attr.label(
|
|
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
|
|
),
|
|
},
|
|
)
|