Files
allwpilib/shared/bazel/rules/robotpy/pybind_rules.bzl
PJ Reiniger 51a3876330 [robotpy] Build examples (#8629)
This hooks up the bazel build to the robotpyExamples. It can use the
(formly pyfrc or whatever) automatic unit tests for an example, as well
as exposing the ability to run the example in simulation, with or
without `halsim_gui` with a command such as `bazel run
//robotpyExamples:AddressableLED-sim`

This required building and using wheels instead of just a normal
`py_library`, so that things like `ENTRY_POINTS` can be used. I took a
bare bones approach to building and naming the wheels (for example the
native ones don't have the OS info or python version in them, so they
wouldn't be suitable publish to pypi, but that can always be updated
later.
2026-03-05 23:18:37 -08:00

262 lines
8.5 KiB
Python

load("@aspect_bazel_lib//lib:copy_file.bzl", "copy_file")
load("@pybind11_bazel//:build_defs.bzl", "pybind_extension", "pybind_library")
load("@rules_pycross//pycross:defs.bzl", "pycross_wheel_library")
load("@rules_python//python:defs.bzl", "py_library")
load("@rules_python//python:packaging.bzl", "py_wheel")
load("//shared/bazel/rules/robotpy:compatibility_select.bzl", "robotpy_compatibility_select")
def create_pybind_library(
name,
extension_name,
install_path,
generated_srcs = [],
extra_hdrs = [],
extra_srcs = [],
deps = [],
dynamic_deps = [],
semiwrap_header = [],
includes = [],
local_defines = []):
"""
Function to create a pybind C++ extension library that has been defined by a semiwrap config
Outputs:
<name>_pybind_library - A pybind_library that functions like a header-only cc_library. It will include all
of the extra_hdrs, resolve the include paths, and add a dependency on the semiwrap headrs
<install_path + extension_name> - A pybind_extension that wraps the pybind_library and compiles all the source files.
Params:
extension_name - The name of the pybind extension. Should be sourced from pyproject
install_path - The subpath where the library will be installed. Should be source from pyproject
generated_srcs - List of auto-generated sources to be compiled into the extension.
extra_hdrs - Any non-autogenerated headers
extra_srcs - Any non-autogenerated sources
deps - cc_library deps used to create the pybind_library
dynamic_deps - cc_shared_library deps used to filter objects from the pybind_extension
semiwrap_header - Auto-generated file used to initialize the extension
includes - see cc_library#includes. Used during the creating of the pybind_library
local_defines - see cc_library#local_defines. Used during the compilation of the extension
"""
pybind_library(
name = "{}_pybind_library".format(name),
hdrs = extra_hdrs,
target_compatible_with = robotpy_compatibility_select(),
deps = deps + [
"//shared/bazel/rules/robotpy:semiwrap_headers",
],
includes = includes,
visibility = ["//visibility:public"],
tags = ["robotpy"],
)
extension_name = extension_name or "_{}".format(name)
pybind_extension(
name = install_path + extension_name,
srcs = extra_srcs + generated_srcs,
deps = [":{}_pybind_library".format(name)] + semiwrap_header,
dynamic_deps = dynamic_deps,
copts = select({
"@bazel_tools//src/conditions:darwin": [
"-Wno-deprecated-declarations",
"-Wno-overloaded-virtual",
"-Wno-pessimizing-move",
"-Wno-unused-value",
],
"@bazel_tools//src/conditions:linux_x86_64": [
"-Wno-attributes",
"-Wno-unused-value",
"-Wno-deprecated",
"-Wno-deprecated-declarations",
"-Wno-unused-parameter",
"-Wno-redundant-move",
"-Wno-unused-but-set-variable",
"-Wno-unused-variable",
"-Wno-pessimizing-move",
"-Wno-overloaded-virtual",
],
"@bazel_tools//src/conditions:windows": [
],
}),
target_compatible_with = robotpy_compatibility_select(),
local_defines = local_defines,
tags = ["robotpy"],
)
def robotpy_library(
name,
deps = [],
data = [],
strip_path_prefixes = None,
summary = None,
project_urls = None,
author_email = None,
entry_points = None,
requires = None,
**kwargs):
"""
Defines a python library that is wrapping a series of pybind extensions.
Outputs:
<name> - The python library
<name>-wheel - A wheel for the library
"""
py_library(
name = name + "-lib",
data = data,
deps = deps,
tags = ["robotpy"],
**kwargs
)
py_wheel(
name = "{}-wheel".format(name),
distribution = name,
stamp = 1,
version = "$(ROBOTPY_VERSION)",
summary = summary,
requires = requires,
project_urls = project_urls,
author_email = author_email,
deps = data + [":{}-lib".format(name)],
strip_path_prefixes = strip_path_prefixes,
entry_points = entry_points,
license = "BSD-3-Clause",
tags = ["robotpy"],
)
pycross_wheel_library(
name = "{}".format(name),
wheel = "{}-wheel".format(name),
deps = deps,
visibility = ["//visibility:public"],
tags = ["manual"],
)
def copy_native_file(name, library, base_path):
"""
Copies a compiled shared library into a naming format that can be used by robotpy rules. The libraries are named
differently on OSX / Linux / Windows and this creates a handy alias to for easier use downstream
"""
copy_file(
name = name + ".win_copy_lib",
src = library,
out = "{}lib/{}.dll".format(base_path, name),
tags = ["manual"],
visibility = ["//visibility:public"],
)
copy_file(
name = name + ".osx_copy_lib",
src = library,
out = "{}lib/lib{}.dylib".format(base_path, name),
tags = ["manual"],
visibility = ["//visibility:public"],
)
copy_file(
name = name + ".linux_copy_lib",
src = library,
out = "{}lib/lib{}.so".format(base_path, name),
tags = ["manual"],
visibility = ["//visibility:public"],
)
native.alias(
name = "{}.copy_lib".format(name),
actual = select({
"@bazel_tools//src/conditions:darwin": name + ".osx_copy_lib",
"@bazel_tools//src/conditions:windows": name + ".win_copy_lib",
"//conditions:default": name + ".linux_copy_lib",
}),
visibility = ["//visibility:public"],
tags = ["robotpy"],
)
def _folder_prefix(name):
if "/" in name:
last_slash = name.rfind("/")
return (name[0:last_slash], name[last_slash + 1:])
else:
return ("", name)
def native_wrappery_library(
name,
pyproject_toml,
libinit_file,
pc_file,
pc_deps,
native_shared_library,
install_path,
headers,
strip_path_prefixes = [],
summary = None,
project_urls = None,
author_email = None,
entry_points = None,
requires = None,
deps = []):
"""
This function provides a sugar wrapper for defining a python library that wraps an allwpilib native library
"""
cmd = "$(locations //shared/bazel/rules/robotpy/hatchlib_native_port:generate_native_lib_files) "
cmd += " $(location " + pyproject_toml + ")"
cmd += " $(OUTS) "
for pc_dep in pc_deps:
cmd += " $(location " + pc_dep + ")"
native.genrule(
name = name + ".gen",
srcs = [pyproject_toml],
outs = [libinit_file, pc_file],
cmd = cmd,
tools = ["//shared/bazel/rules/robotpy/hatchlib_native_port:generate_native_lib_files"] + pc_deps,
visibility = ["//visibility:public"],
tags = ["robotpy"],
)
prefix, libname = _folder_prefix(native_shared_library)
copy_native_file(
name = libname,
library = native_shared_library,
base_path = install_path,
)
native.filegroup(
name = name + ".pc_wrapper",
srcs = [pc_file],
)
py_library(
name = name + "-lib",
srcs = [libinit_file],
data = [pc_file, ":{}.copy_lib".format(libname), headers],
deps = deps,
imports = ["."],
tags = ["robotpy"],
)
py_wheel(
name = "{}-wheel".format(name),
distribution = name,
stamp = 1,
version = "$(ROBOTPY_VERSION)",
summary = summary,
requires = requires,
project_urls = project_urls,
author_email = author_email,
deps = [name + "-lib", ":{}.copy_lib".format(libname), headers, name + ".pc_wrapper"],
strip_path_prefixes = strip_path_prefixes,
entry_points = entry_points,
tags = ["robotpy"],
license = "BSD-3-Clause",
)
pycross_wheel_library(
name = "{}".format(name),
wheel = "{}-wheel".format(name),
visibility = ["//visibility:public"],
tags = ["manual"],
deps = deps,
)