Skip to content

Commit 5391c6d

Browse files
julianadrianheineFrancisco Rossi
andauthored
Improve ros_launch support for launch files (#419)
This patch enables using normal `ros_launch` methods for running nodes and loading files as if you were running colcon, by building a package directory with a `lib` directory for executables and `share` for other files. --------- Co-authored-by: Francisco Rossi <frossi@ekumenlabs.com>
1 parent debaf76 commit 5391c6d

4 files changed

Lines changed: 80 additions & 47 deletions

File tree

bazel_ros2_rules/lib/ament_index.bzl

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,15 @@ Recursively aggregates AmentIndex prefixes from dependencies into one provider.
4747
"""
4848

4949
def _ament_index_share_files_impl(ctx):
50-
# declare that a "package" with the given name exists
5150
package_marker_path = paths.join(
5251
ctx.attr.prefix,
5352
"share/ament_index/resource_index/packages/",
5453
ctx.attr.package_name,
5554
)
5655
package_marker_out = ctx.actions.declare_file(package_marker_path)
57-
ctx.actions.write(
58-
output = package_marker_out,
59-
content = "",
60-
)
56+
ctx.actions.write(output = package_marker_out, content = "")
6157

62-
# Symlink sources into the share directory
63-
runfiles_symlinks = {
64-
package_marker_path: package_marker_out,
65-
}
58+
root_symlinks = {package_marker_path: package_marker_out}
6659

6760
for src in ctx.attr.srcs:
6861
for file in depset(transitive = [
@@ -78,23 +71,37 @@ def _ament_index_share_files_impl(ctx):
7871
ctx.attr.package_name,
7972
sp,
8073
)
81-
runfiles_symlinks[symlink_path] = file
74+
root_symlinks[symlink_path] = file
75+
76+
for executable in ctx.attr.executables:
77+
if executable.files_to_run.executable == None:
78+
fail("{} is not an executable target".format(executable.label))
79+
symlink_path = paths.join(
80+
ctx.attr.prefix,
81+
"lib",
82+
ctx.attr.package_name,
83+
executable.files_to_run.executable.basename,
84+
)
85+
root_symlinks[symlink_path] = executable.files_to_run.executable
8286

8387
return [
8488
AmentIndex(prefix = ctx.attr.prefix),
8589
DefaultInfo(
86-
runfiles = ctx.runfiles(root_symlinks = runfiles_symlinks),
90+
runfiles = ctx.runfiles(root_symlinks = root_symlinks),
8791
),
8892
]
8993

9094
ament_index_share_files = rule(
9195
attrs = dict(
9296
package_name = attr.string(mandatory = True),
9397
srcs = attr.label_list(
94-
mandatory = True,
95-
allow_empty = False,
98+
allow_empty = True,
9699
allow_files = True,
97100
),
101+
executables = attr.label_list(
102+
allow_empty = True,
103+
allow_files = False,
104+
),
98105
# A prefix is required because the shim can't prepend the runfiles
99106
# root to AMENT_PREFIX_PATH
100107
prefix = attr.string(default = "ament_index_share_files"),
@@ -105,14 +112,17 @@ ament_index_share_files = rule(
105112
provides = [AmentIndex],
106113
)
107114
"""
108-
Creates an ament resource index and share/ directory for a single package.
115+
Creates an ament resource index for a single ROS 2 package.
116+
117+
Files in `srcs` are symlinked under `<prefix>/share/<package_name>/`,
118+
enabling ament_index lookups such as `get_package_share_directory()`.
119+
120+
Executable targets in `executables` are symlinked under
121+
`<prefix>/lib/<package_name>/`, enabling
122+
`launch_ros.actions.Node(package=..., executable=...)` to find Bazel-built
123+
binaries without a colcon install space.
109124
110-
This creates both a package marker for a package, and a share directory
111-
for that package.
112-
Files in `srcs` will be added to `share/package_name`.
113-
A target depending on this one will be able to access the files in the
114-
share directory using the package's share directory joined with the
115-
files they expect.
125+
Both can be used together in a single rule invocation.
116126
117127
d = ament_index_cpp::get_package_share_directory("package_name")
118128
file = join(d, "short_path of file")
@@ -132,11 +142,12 @@ TODO(sloretz) detect and error when a target is given two ament indexes
132142
with the same package.
133143
134144
Args:
135-
package_name: name of a ROS 2 package to which these share files belong
145+
package_name: name of a ROS 2 package
136146
srcs: targets whose files are placed into the share directory. Both
137147
direct files and transitive runfiles are included.
148+
executables: executable targets to expose under lib/.
138149
prefix: optional prefix to give to the generated runfiles.
139-
strip_prefix: optional prefix to strip from the short_path of the files
150+
strip_prefix: optional prefix to strip from the short_path of srcs files.
140151
141152
Provides:
142153
AmentIndex: a prefix path where the ament resource index was generated.

bazel_ros2_rules/lib/private/ros_py.bzl

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# -*- python -*-
22

3+
load(
4+
"@bazel_ros2_rules//lib:ament_index.bzl",
5+
"ament_index_share_files",
6+
)
37
load(
48
"@bazel_ros2_rules//lib:kwargs.bzl",
59
"filter_to_only_common_kwargs",
@@ -174,12 +178,7 @@ os.execv(ros2_bin, args)
174178
def _make_respath(relpath, workspace_name):
175179
repo = native.repository_name()
176180
if repo == "@":
177-
if workspace_name == None:
178-
fail(
179-
"Please provide `ros_launch(*, workspace_name)` so that " +
180-
"paths can be resolved properly",
181-
)
182-
repo = workspace_name
181+
repo = workspace_name if workspace_name != None else native.module_name()
183182
pkg = native.package_name()
184183
if pkg != "":
185184
pieces = [repo, pkg, relpath]
@@ -194,10 +193,22 @@ def ros_launch(
194193
data = [],
195194
deps = [],
196195
visibility = None,
197-
# TODO(eric.cousineau): Remove this once Bazel provides a way to tell
198-
# runfiles.py to use "this repository" in a way that doesn't require
199-
# bespoke information.
196+
# Optional: overrides the Bzlmod module name as the ament package name.
197+
# Only needed when the desired package name differs from the module name.
200198
workspace_name = None,
199+
# Executable targets to expose via the ament resource index under
200+
# lib/<package_name>/, enabling
201+
# launch_ros.actions.Node(package=<package_name>, executable=...) to
202+
# find Bazel-built binaries without a colcon install space.
203+
# Example:
204+
# executables = [":talker", ":listener"],
205+
executables = [],
206+
# Data file targets to expose via the ament resource index under
207+
# share/<package_name>/, enabling FindPackageShare(<package_name>)
208+
# and get_package_share_directory(<package_name>) to locate them.
209+
# Example:
210+
# share = ["//my_pkg:config_files"],
211+
share = [],
201212
**kwargs):
202213
main = "{}_roslaunch_main.py".format(name)
203214
launch_respath = _make_respath(launch_file, workspace_name)
@@ -218,6 +229,19 @@ def ros_launch(
218229
REPOSITORY_ROOT + ":ros2",
219230
],
220231
)
232+
233+
if executables or share:
234+
package_name = workspace_name if workspace_name != None else native.module_name()
235+
index_target = "_{}_ament_index".format(name)
236+
ament_index_share_files(
237+
name = index_target,
238+
package_name = package_name,
239+
executables = executables,
240+
srcs = share,
241+
visibility = ["//visibility:private"],
242+
)
243+
data = data + executables + share + [":" + index_target]
244+
221245
data = data + [launch_file]
222246

223247
if "tags" not in kwargs:

ros2_example_bazel_installed/ros2_example_apps/BUILD.bazel

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,12 +233,14 @@ ros_cc_binary(
233233
# See bazel_ros2_rules/ros2/README.md, Launch Files, for notes on
234234
# features and limitations.
235235

236-
# Uses a python launch file to spawn the talker and listener.
237236
workspace_name = "ros2_example_bazel_installed"
238237

238+
# Uses a python launch file to spawn the talker and listener.
239+
# Note: it also uses launch_ros.actions.Node, the natural ROS 2 pattern,
240+
# by registering the Bazel-built binaries in a fake ament prefix.
239241
ros_launch(
240242
name = "roslaunch_eg_py",
241-
data = [
243+
executables = [
242244
":eg_listener",
243245
":eg_talker",
244246
],
@@ -249,7 +251,7 @@ ros_launch(
249251
# Uses an xml launch file to spawn the talker and listener.
250252
ros_launch(
251253
name = "roslaunch_eg_xml",
252-
data = [
254+
executables = [
253255
":eg_listener",
254256
":eg_talker",
255257
],
Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
11
from launch import LaunchDescription
2-
from launch.actions import ExecuteProcess
3-
from python.runfiles import runfiles
2+
import launch_ros.actions
43

54

65
def generate_launch_description():
7-
# See bazel_ros2_rules/ros2/README.md, Launch Files, for notes on
8-
# features and limitations.
9-
r = runfiles.Create()
10-
prefix = "ros2_example_bazel_installed/ros2_example_apps"
11-
talker_bin = r.Rlocation(f"{prefix}/eg_talker")
12-
listener_bin = r.Rlocation(f"{prefix}/eg_listener")
13-
146
return LaunchDescription(
157
[
16-
# Running a talker written in python.
17-
ExecuteProcess(cmd=[talker_bin]),
18-
# Running a listener written in cpp.
19-
ExecuteProcess(cmd=[listener_bin]),
8+
launch_ros.actions.Node(
9+
package="ros2_example_bazel_installed",
10+
executable="eg_talker",
11+
),
12+
launch_ros.actions.Node(
13+
package="ros2_example_bazel_installed",
14+
executable="eg_listener",
15+
),
2016
]
2117
)

0 commit comments

Comments
 (0)