Skip to content

Commit

Permalink
Add bazel genrule test
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Carroll <[email protected]>
  • Loading branch information
mjcarroll committed Dec 20, 2024
1 parent d48c5c6 commit ad38049
Show file tree
Hide file tree
Showing 20 changed files with 356 additions and 12 deletions.
2 changes: 1 addition & 1 deletion BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ license(
license_text = "LICENSE",
)


write_file(
name = "write_xacro_main",
# This is the same as scripts/xacro from upstream, except that we lose the
Expand All @@ -35,6 +34,7 @@ py_library(
visibility = ["//visibility:public"],
deps = [
"@pypi//pyyaml:pkg",
"@rules_python//python/runfiles",
]
)

Expand Down
4 changes: 2 additions & 2 deletions bazel/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ compile_pip_requirements(
)

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

Expand Down
52 changes: 52 additions & 0 deletions bazel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# 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
6 changes: 0 additions & 6 deletions bazel/build_defs.bzl

This file was deleted.

97 changes: 97 additions & 0 deletions bazel/defs.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""Provider and rules for generating xacro output at build time."""

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

def _xacro_impl(ctx):
if ctx.outputs.out:
out = ctx.outputs.out
else:
src = ctx.file.src.basename
if not src.endswith(".xacro"):
fail("xacro_file src should be named *.xacro not {}".format(src))
out = ctx.actions.declare_file(src[:-6])


# The list of arguments we pass to the script.
args = [ctx.file.src.path, "-o", out.path] + ctx.attr.extra_args

# Action to call the script.
all_inputs = [ctx.file.src] + ctx.files.data + [dep[XacroInfo].result for dep in ctx.attr.deps]
ctx.actions.run(
inputs = all_inputs,
outputs = [out],
arguments = args,
env = {"XACRO_INPUTS": "\n".join([file.path for file in all_inputs])},
progress_message = "Running xacro: %s -> %s" % (ctx.file.src.short_path, out.short_path),
executable = ctx.executable._xacro,
)

xacro_data = depset(
direct = [out] + ctx.files.data + [d[XacroInfo].result for d in ctx.attr.deps],
transitive = [d[XacroInfo].data for d in ctx.attr.deps],
)

runfiles = ctx.runfiles(files = xacro_data.to_list())

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

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

def xacro_file(
name,
src = None,
out = None,
data = [],
tags = [],
deps = [],
extra_args = [],
visibility = None):
"""Runs xacro on a single input file, creating a single output file.
Xacro is the ROS XML macro tool; http://wiki.ros.org/xacro.
Args:
name: The xml output file of this rule.
src: The single xacro input file of this rule.
out: Optional output file name
data: Optional supplemental files required by the src file.
extra_args: Optional arguments to be interpreted by xacro
"""
_xacro_rule(
name = name,
src = src,
out = out,
data = data,
tags = tags,
deps = deps,
extra_args = extra_args,
visibility = visibility,
)
1 change: 1 addition & 0 deletions bazel/integration_test/.bazelversion
64 changes: 64 additions & 0 deletions bazel/integration_test/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
load("@xacro//bazel:defs.bzl", "xacro_file")
load("@bazel_skylib//rules:diff_test.bzl", "diff_test")

xacro_file(
name = "sample1",
src = "sample1.xml.xacro",
)

xacro_file(
name = "sample2",
src = "sample2.xml.xacro",
data = ["box.xml"]
)

xacro_file(
name = "complex_example",
src = "complex.xml.xacro",
out = "my_complex_model.xml",
deps = [":sample1"],
extra_args = ["special_argument:=foo"]
)

xacro_file(
name = "conditional_default",
src = "conditional.xml.xacro",
out = "conditional_default.xml"
)

xacro_file(
name = "conditional_false",
src = "conditional.xml.xacro",
out = "conditional_false.xml",
extra_args = ["myarg:=false"]
)

diff_test(
name = "sample1_test",
file1 = ":sample1",
file2 = "expected/sample1.xml"
)

diff_test(
name = "sample2_test",
file1 = ":sample2",
file2 = "expected/sample2.xml"
)

diff_test(
name = "complex_example_test",
file1 = ":complex_example",
file2 = "expected/my_complex_model.xml"
)

diff_test(
name = "conditional_default_test",
file1 = ":conditional_default",
file2 = "expected/conditional_default.xml"
)

diff_test(
name = "conditional_false_test",
file1 = ":conditional_false",
file2 = "expected/conditional_false.xml"
)
7 changes: 7 additions & 0 deletions bazel/integration_test/MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
bazel_dep(name = "bazel_skylib", version = "1.7.1")

bazel_dep(name = "xacro")
local_path_override(
module_name = "xacro",
path = "../.."
)
4 changes: 4 additions & 0 deletions bazel/integration_test/box.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0"?>
<root>
<box size="1.0 1.0 1.0"/>
</root>
8 changes: 8 additions & 0 deletions bazel/integration_test/complex.xml.xacro
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?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"/>

<!-- populate an argument from command line -->
<sphere name="$(arg special_argument)" radius="1"/>
</root>
7 changes: 7 additions & 0 deletions bazel/integration_test/conditional.xml.xacro
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<root xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:arg name="myarg" default="true"/>
<xacro:if value="$(arg myarg)">
<sphere radius="1"/>
</xacro:if>
</root>
8 changes: 8 additions & 0 deletions bazel/integration_test/expected/conditional_default.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" ?>
<!-- =================================================================================== -->
<!-- | This document was autogenerated by xacro from conditional.xml.xacro | -->
<!-- | EDITING THIS FILE BY HAND IS NOT RECOMMENDED | -->
<!-- =================================================================================== -->
<root>
<sphere radius="1"/>
</root>
7 changes: 7 additions & 0 deletions bazel/integration_test/expected/conditional_false.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" ?>
<!-- =================================================================================== -->
<!-- | This document was autogenerated by xacro from conditional.xml.xacro | -->
<!-- | EDITING THIS FILE BY HAND IS NOT RECOMMENDED | -->
<!-- =================================================================================== -->
<root>
</root>
10 changes: 10 additions & 0 deletions bazel/integration_test/expected/my_complex_model.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" ?>
<!-- =================================================================================== -->
<!-- | This document was autogenerated by xacro from complex.xml.xacro | -->
<!-- | EDITING THIS FILE BY HAND IS NOT RECOMMENDED | -->
<!-- =================================================================================== -->
<root>
<sphere radius="1.0"/>
<!-- populate an argument from command line -->
<sphere name="foo" radius="1"/>
</root>
8 changes: 8 additions & 0 deletions bazel/integration_test/expected/sample1.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" ?>
<!-- =================================================================================== -->
<!-- | This document was autogenerated by xacro from sample1.xml.xacro | -->
<!-- | EDITING THIS FILE BY HAND IS NOT RECOMMENDED | -->
<!-- =================================================================================== -->
<root>
<sphere radius="1.0"/>
</root>
9 changes: 9 additions & 0 deletions bazel/integration_test/expected/sample2.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" ?>
<!-- =================================================================================== -->
<!-- | This document was autogenerated by xacro from sample2.xml.xacro | -->
<!-- | EDITING THIS FILE BY HAND IS NOT RECOMMENDED | -->
<!-- =================================================================================== -->
<root>
<sphere radius="1.0"/>
<box size="1.0 1.0 1.0"/>
</root>
5 changes: 5 additions & 0 deletions bazel/integration_test/sample1.xml.xacro
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0"?>
<root xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:property name="diameter" value="2.0"/>
<sphere radius="${diameter/2}"/>
</root>
6 changes: 6 additions & 0 deletions bazel/integration_test/sample2.xml.xacro
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0"?>
<root xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:property name="diameter" value="2.0"/>
<sphere radius="${diameter/2}"/>
<xacro:include filename="box.xml"/>
</root>
17 changes: 14 additions & 3 deletions xacro/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@
from .xmlutils import opt_attrs, reqd_attrs, first_child_element, \
next_sibling_element, replace_node

try:
# Determine if we are running under bazel,
# If so, attempt to load `//` prefixed bazel paths first

# This import will only succeed when running under bazel
from python.runfiles import runfiles
from .bazel_support import open_bazel
OPEN_IMPLEMENTATION = open_bazel
except:
# Otherwise, default to default open
OPEN_IMPLEMENTATION = open

# Dictionary of substitution args
substitution_args_context = {}
Expand Down Expand Up @@ -137,7 +148,7 @@ def load_yaml(filename):
raise XacroException("yaml support not available; install python-yaml")

filename = abs_filename_spec(filename)
f = open(filename)
f = OPEN_IMPLEMENTATION(filename)
filestack.append(filename)
try:
return YamlListWrapper.wrap(yaml.safe_load(f))
Expand Down Expand Up @@ -1018,7 +1029,7 @@ def parse(inp, filename=None):
f = None
if inp is None:
try:
inp = f = open(filename)
inp = f = OPEN_IMPLEMENTATION(filename)
except IOError as e:
# do not report currently processed file as "in file ..."
filestack.pop()
Expand Down Expand Up @@ -1076,7 +1087,7 @@ def open_output(output_filename):
pass

try:
return open(output_filename, 'w')
return OPEN_IMPLEMENTATION(output_filename, 'w')
except IOError as e:
raise XacroException("Failed to open output:", exc=e)

Expand Down
Loading

0 comments on commit ad38049

Please sign in to comment.