[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.
This commit is contained in:
PJ Reiniger
2026-03-06 02:18:37 -05:00
committed by GitHub
parent 26b2b08c8d
commit 51a3876330
37 changed files with 498 additions and 25 deletions

View File

@@ -102,6 +102,14 @@ def define_native_wrapper(name, pyproject_toml = None):
headers = "{}.copy_headers".format(name),
native_shared_library = "shared/{{shared_library_name}}",
install_path = "native/{{nativelib_config.pcfile[0].name}}/",
strip_path_prefixes = ["{{root_package}}"],
requires = {{raw_project_config.dependencies | double_quotes}},
summary = "{{raw_project_config.description}}",
entry_points = {
"pkg_config": [
"{{nativelib_config.pcfile[0].name}} = native.{{nativelib_config.pcfile[0].name}}",
],
},
)
"""

View File

@@ -417,6 +417,19 @@ def generate_pybind_build_file(
except:
version_file = None
# The entry points defined above are implicit to how the project is broken down in the toml files.
# This addes potentially extra explicitly declared entry points
if "entry-points" in raw_config["project"]:
explicit_entry_points = raw_config["project"]["entry-points"]
for entry_point_type in explicit_entry_points:
for ep_key, ep_value in explicit_entry_points[entry_point_type].items():
entry_points[entry_point_type].append(f"{ep_key} = {ep_value}")
strip_path_prefixes = [
f"{fixup_root_package_name(top_level_name)}/{stripped_include_prefix}",
f"{fixup_root_package_name(top_level_name)}",
]
with open(output_file, "w") as f:
f.write(
template.render(
@@ -426,6 +439,7 @@ def generate_pybind_build_file(
python_deps=sorted(python_deps),
all_local_native_deps=all_local_native_deps,
stripped_include_prefix=stripped_include_prefix,
strip_path_prefixes=strip_path_prefixes,
yml_prefix=yml_prefix,
package_root_file=package_root_file,
raw_project_config=raw_config["project"],

View File

@@ -13,6 +13,10 @@ def fixup_root_package_name(name):
return "ntcore"
if name == "halsim-ws":
return "simulation/halsim_ws_core"
if name == "wpimath_test":
return "wpimath"
if name == "robotpy_apriltag":
return "apriltag"
return name

View File

@@ -12,7 +12,7 @@ load("//shared/bazel/rules/robotpy:pybind_rules.bzl", "create_pybind_library", "
load("//shared/bazel/rules/robotpy:semiwrap_helpers.bzl", "gen_libinit", "gen_modinit_hpp", "gen_pkgconf", {% if publish_casters_targets %}"publish_casters", {% endif %}"resolve_casters", "run_header_gen")
load("//shared/bazel/rules/robotpy:semiwrap_tool_helpers.bzl", "scan_headers", "update_yaml_files")
{% for extension_module in extension_modules%}
def {{extension_module.name}}_extension(srcs = [], header_to_dat_deps = [], extra_hdrs = [], includes = [], extra_pyi_deps = []):
def {{extension_module.name}}_extension(srcs = [], header_to_dat_deps = [], extra_hdrs = [], includes = []):
{{extension_module.name|upper}}_HEADER_GEN = [
{%- for header_cfg in extension_module.generation_data.values() %}
struct(
@@ -217,6 +217,16 @@ def define_pybind_library(name, pkgcfgs = []):
{% if "requirement" in d %}{{d}}{% else %}"{{d}}"{% endif %},
{%- endfor %}
],
strip_path_prefixes = [{% for spp in strip_path_prefixes %}"{{ spp }}"{% if not loop.last%}, {% endif %}{% endfor %}],
summary = "{{raw_project_config.description}}",
project_urls = {{raw_project_config.urls | jsonify}},
author_email = "RobotPy Development Team <robotpy@googlegroups.com>",
requires = {{raw_project_config.dependencies | jsonify}},
entry_points = {
{%- for group, entries in entry_points.items() %}
"{{ group }}": {{entries | jsonify}},
{%- endfor %}
},
visibility = ["//visibility:public"],
)

View File

@@ -1,6 +1,8 @@
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(
@@ -82,21 +84,54 @@ def create_pybind_library(
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,
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
@@ -153,6 +188,12 @@ def native_wrappery_library(
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
@@ -187,11 +228,34 @@ def native_wrappery_library(
)
py_library(
name = name,
name = name + "-lib",
srcs = [libinit_file],
data = [pc_file, ":{}.copy_lib".format(libname), headers],
deps = deps,
imports = ["."],
visibility = ["//visibility:public"],
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,
)