Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create xacro Bazel module #350

Open
wants to merge 29 commits into
base: ros2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c4740c1
Initial try at bazelizing xacro
mjcarroll Dec 18, 2024
40ad447
Add bazel test support
mjcarroll Dec 19, 2024
2bc9bc2
Fix test
mjcarroll Dec 19, 2024
f6ee8d1
Add bazel CI workflow
mjcarroll Dec 19, 2024
3aea25b
Run on all PRs
mjcarroll Dec 19, 2024
0b95e2a
Cleanup
rhaschke Dec 20, 2024
71dbbaf
Move most bazel accessory files into bazel/
mjcarroll Dec 20, 2024
f150b54
Merge remote-tracking branch 'origin/mjcarroll/bazel' into mjcarroll/…
mjcarroll Dec 20, 2024
9388e1b
Update test location and drop CI specific bazelrc
mjcarroll Dec 20, 2024
d48c5c6
Remove root bazelrc
mjcarroll Dec 20, 2024
ad38049
Add bazel genrule test
mjcarroll Dec 20, 2024
d9188bc
Add copyright header
mjcarroll Dec 20, 2024
5d04b37
Undo substitution_args change, as these are skipped in bazel now
mjcarroll Dec 20, 2024
56426d1
Move test inputs to subdirectory
mjcarroll Dec 20, 2024
1457734
Buildifier Lint
mjcarroll Dec 20, 2024
6602ced
Trim trailing whitespace
mjcarroll Dec 20, 2024
2348098
Add filegroup rule
mjcarroll Dec 20, 2024
7531667
Address reviewer feedback:
mjcarroll Dec 20, 2024
b3af566
Make python dependency repo name unique.
mjcarroll Dec 20, 2024
a21a02e
Change build dependency to reflect repo rename
mjcarroll Dec 20, 2024
a6d30be
Add support for multiple python versions
mjcarroll Dec 20, 2024
c739c42
Remove concurrency constraint and add comment
mjcarroll Dec 20, 2024
d2e0934
Merge remote-tracking branch 'origin/ros2' into mjcarroll/bazel
mjcarroll Jan 8, 2025
747b833
Remove bazel specific find semantics
mjcarroll Jan 8, 2025
5ae92bb
Remove bazel-specific semantics
mjcarroll Jan 14, 2025
662dfdc
Correctly resolve root_dir
mjcarroll Jan 14, 2025
6495fd4
Update test paths
mjcarroll Jan 14, 2025
b4aa47d
Minimize diff
mjcarroll Jan 14, 2025
4399f2b
Add test for --root-dir argument
mjcarroll Jan 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .bazelignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bazel/integration_test
1 change: 1 addition & 0 deletions .bazelversion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
7.4.1
21 changes: 21 additions & 0 deletions .github/workflows/bazel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Bazel CI

on: [push, pull_request, workflow_dispatch]

jobs:
test:
uses: bazel-contrib/.github/.github/workflows/bazel.yaml@v7
with:
folders: |
[
".",
"bazel/integration_test",
]
# Explicitly exclude build/test configurations where bzlmod is disabled.
# Since xacro only supports bzlmod, these will always fail.
# Remove these exclusions when workspace support is dropped.
exclude: |
[
{"folder": ".", "bzlmodEnabled": false},
{"folder": "bazel/integration_test", "bzlmodEnabled": false},
mjcarroll marked this conversation as resolved.
Show resolved Hide resolved
]
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
*.pyc
build
bazel-*
MODULE.bazel.lock
81 changes: 81 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
load("@bazel_skylib//rules:write_file.bzl", "write_file")
load("@rules_license//rules:license.bzl", "license")
load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test")

package(
default_applicable_licenses = [":license"],
)

licenses(["notice"])

license(
name = "license",
license_kinds = [
"@rules_license//licenses/spdx:BSD-3-Clause",
],
license_text = "LICENSE",
)

write_file(
name = "write_xacro_main",
out = "xacro_main.py",
# This is the same as scripts/xacro from upstream, except that we lose the
# unused shebang line and we use a filename that is not subject to import
# path conflicts.
content = ["import xacro; xacro.main()"],
)

py_library(
name = "xacro_lib",
srcs = [
"xacro/__init__.py",
"xacro/cli.py",
"xacro/color.py",
"xacro/substitution_args.py",
"xacro/xmlutils.py",
],
imports = ["."],
visibility = ["//visibility:public"],
deps = [
"@rules_python//python/runfiles",
"@xacro_python_dependencies//pyyaml:pkg",
],
)

py_binary(
name = "xacro_main",
srcs = ["xacro_main.py"],
main = "xacro_main.py",
deps = [":xacro_lib"],
)

alias(
name = "xacro",
actual = ":xacro_main",
visibility = ["//visibility:public"],
)

TEST_RESOURCES = glob([
"test/*.xacro",
"test/*.xml",
"test/*.yaml",
"test/subdir/**",
"test/robots/**",
])

filegroup(
name = "test_data",
srcs = TEST_RESOURCES,
data = TEST_RESOURCES,
)

py_test(
name = "test_xacro",
srcs = ["test/test_xacro.py"],
data = [":test_data"],
main = "test/test_xacro.py",
deps = [
":xacro_main",
"@rules_python//python/runfiles",
],
)
39 changes: 39 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module(
name = "xacro",
version = "2.0.11",
)

bazel_dep(name = "rules_license", version = "1.0.0")
bazel_dep(name = "bazel_skylib", version = "1.7.1")
bazel_dep(name = "rules_python", version = "0.40.0")

PYTHON_VERSIONS = [
"3.8",
"3.9",
"3.10",
"3.11",
"3.12",
]

python = use_extension("@rules_python//python/extensions:python.bzl", "python")

[
python.toolchain(
is_default = python_version == PYTHON_VERSIONS[-1],
python_version = python_version,
)
for python_version in PYTHON_VERSIONS
]

pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")

[
pip.parse(
hub_name = "xacro_python_dependencies",
python_version = python_version,
requirements_lock = "//bazel:requirements_lock.txt",
)
for python_version in PYTHON_VERSIONS
]

use_repo(pip, "xacro_python_dependencies")
20 changes: 20 additions & 0 deletions bazel/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("@rules_python//python:pip.bzl", "compile_pip_requirements")

# This rule adds a convenient way to update the requirements file.
compile_pip_requirements(
name = "requirements",
src = "requirements.in",
requirements_txt = "requirements_lock.txt",
)

bzl_library(
name = "defs",
srcs = ["defs.bzl"],
visibility = ["//visibility:public"],
)

exports_files([
"requirements.in",
"requirements_lock.txt",
])
66 changes: 66 additions & 0 deletions bazel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Bazel

This directory contains support files and tests for the bazel build system.

In addition to supporting building xacro with bazel, this also introduces two rules for build-time generation of xacro files.

## xacro_file

Allows you to transform a single xacro file into a generated output

A simple example:

```
load("@xacro//bazel:defs.bzl", "xacro_file")

# By default, will transform input filename with .xacro removed
xacro_file(
name = "sample1",
src = "sample1.xml.xacro",
)
```

A more complex example:

```
load("@xacro//bazel:defs.bzl", "xacro_file")

# By default, will transform input filename with .xacro removed
xacro_file(
name = "complex_example",
src = "complex.xml.xacro",
# Override the default output file name
out = "my_complex_model.xml",
# Depend on the XML file that we generated in the previous step
deps = [":sample1"],
# Set extra substitution args via the command line
extra_args = ["special_argument:=foo"]
)
```

Note in the case of the more complex example, you can use bazel-specified filenames if they are specified in the `deps` field:

```
<?xml version="1.0"?>
<root xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- include a file from a bazel path -->
<xacro:include filename="//sample1.xml"/>
</root>
```

## xacro_filegroup

Allows you to transform multiple xacro files into a generated filegroup

```
xacro_filegroup(
name = "samples",
srcs = [
"sample1.xml.xacro",
"sample2.xml.xacro",
],
data = [
"box.xml",
],
)
```
116 changes: 116 additions & 0 deletions bazel/defs.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""Provider and rules for generating xacro output at build time."""

XacroInfo = provider(
"Provider holding the result of a xacro generation step.",
fields = ["result"],
)

XACRO_EXTENSION = ".xacro"

def _xacro_impl(ctx):
# Use declared output or derive from source name
out = ctx.outputs.out or ctx.actions.declare_file(ctx.file.src.basename[:-len(XACRO_EXTENSION)])

# Gather inputs for the xacro command
direct_inputs = [ctx.file.src] + ctx.files.data
dep_inputs = [dep[XacroInfo].result for dep in ctx.attr.deps]
all_inputs = direct_inputs + dep_inputs

# Create a temporary directory
temp_dir = "TMP_XACRO/" + ctx.label.name

symlink_paths = []
for input in all_inputs:
symlink_path = ctx.actions.declare_file(temp_dir + "/" + input.basename)
ctx.actions.symlink(
output = symlink_path,
target_file = input,
)
symlink_paths.append(symlink_path)

arguments = [
"-o",
out.path,
"--root-dir",
ctx.bin_dir.path + "/" + temp_dir,
ctx.file.src.basename,
]
arguments += ["{}:={}".format(arg, val) for arg, val in ctx.attr.arguments.items()]

ctx.actions.run(
inputs = symlink_paths,
outputs = [out],
arguments = arguments,
executable = ctx.executable._xacro,
progress_message = "Running xacro: %s -> %s" % (ctx.file.src.short_path, out.short_path),
mnemonic = "Xacro",
)

return [
XacroInfo(result = out),
DefaultInfo(
files = depset([out]),
data_runfiles = ctx.runfiles(files = [out]),
),
]

xacro_file = rule(
attrs = {
"src": attr.label(
mandatory = True,
allow_single_file = True,
),
"out": attr.output(),
"data": attr.label_list(
allow_files = True,
),
"arguments": attr.string_dict(),
"deps": attr.label_list(providers = [XacroInfo]),
"_xacro": attr.label(
default = "@xacro//:xacro",
cfg = "host",
executable = True,
),
},
implementation = _xacro_impl,
provides = [XacroInfo, DefaultInfo],
)

def xacro_filegroup(
name,
srcs = [],
data = [],
tags = [],
visibility = None):
"""Runs xacro on several input files, creating a filegroup of the output.

The output filenames will match the input filenames but with the ".xacro"
suffix removed.

Xacro is the ROS XML macro tool; http://wiki.ros.org/xacro.

Args:
name: The name of the filegroup label.
srcs: The xacro input files of this rule.
data: Optional supplemental files required by the srcs.
"""
outs = []
for src in srcs:
if not src.endswith(XACRO_EXTENSION):
fail("xacro_filegroup srcs should be named *.xacro not {}".format(
src,
))
out = src[:-len(XACRO_EXTENSION)]
outs.append(out)
xacro_file(
name = out,
src = src,
data = data,
tags = tags,
visibility = ["//visibility:private"],
)
native.filegroup(
name = name,
srcs = outs,
visibility = visibility,
)
1 change: 1 addition & 0 deletions bazel/integration_test/.bazelversion
Loading
Loading