From 67779fa5da570d8990c49c0c7c881cf3206ae4e1 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 7 May 2021 17:03:59 -0300 Subject: [PATCH 001/107] Add drake_ros_bazel_installed Bazel package. Includes build infrastructure to pull a ROS 2 binary installation into the WORKSPACE as a local repository Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/BUILD.bazel | 0 drake_ros_bazel_installed/WORKSPACE | 15 + drake_ros_bazel_installed/tools/BUILD.bazel | 0 .../tools/skylark/BUILD.bazel | 0 .../tools/skylark/dload.bzl | 297 ++++++++++++++++++ .../tools/skylark/dload_cc.bzl | 111 +++++++ .../tools/skylark/dload_py.bzl | 82 +++++ .../tools/skylark/drake_ros_cc.bzl | 169 ++++++++++ .../tools/skylark/drake_ros_py.bzl | 146 +++++++++ .../tools/skylark/ros2/BUILD.bazel | 7 + .../skylark/ros2/cmake_tools/__init__.py | 26 ++ .../skylark/ros2/cmake_tools/server_mode.py | 195 ++++++++++++ .../skylark/ros2/generate_repository_files.py | 197 ++++++++++++ .../ros2/resources/BUILD.prologue.bazel | 8 + .../resources/ament_cmake_CMakeLists.txt.in | 37 +++ .../ros2/resources/package_alias.bzl.tpl | 4 + .../package_cc_binary_import.bzl.tpl | 5 + .../ros2/resources/package_cc_library.bzl.tpl | 12 + .../resources/package_meta_py_library.bzl.tpl | 4 + .../ros2/resources/package_py_library.bzl.tpl | 11 + ...kage_py_library_with_cc_extensions.bzl.tpl | 11 + .../resources/package_share_filegroup.bzl.tpl | 4 + .../tools/skylark/ros2/resources/prologue.bzl | 8 + .../skylark/ros2/resources/rosidl.bzl.tpl | 15 + .../resources/rosidl_cmake_CMakeLists.txt.in | 19 ++ .../ros2/resources/rosidl_package.xml.in | 23 ++ .../tools/skylark/ros2/resources/setup.sh.in | 15 + .../tools/skylark/ros2/rmw.bzl | 52 +++ .../skylark/ros2/rmw_fastrtps_profile_gen.py | 104 ++++++ .../tools/skylark/ros2/ros2.bzl | 132 ++++++++ .../tools/skylark/ros2/ros2bzl/__init__.py | 0 .../tools/skylark/ros2/ros2bzl/resources.py | 60 ++++ .../tools/skylark/ros2/ros2bzl/sandboxing.py | 22 ++ .../ros2/ros2bzl/scrapping/__init__.py | 41 +++ .../ros2/ros2bzl/scrapping/ament_cmake.py | 186 +++++++++++ .../ros2/ros2bzl/scrapping/ament_python.py | 70 +++++ .../ros2/ros2bzl/scrapping/metadata.py | 94 ++++++ .../skylark/ros2/ros2bzl/scrapping/system.py | 84 +++++ .../tools/skylark/ros2/ros2bzl/templates.py | 220 +++++++++++++ .../tools/skylark/ros2/ros2bzl/utilities.py | 14 + .../ros2/rosidl_generate_interfaces.py | 232 ++++++++++++++ .../tools/workspace/BUILD.bazel | 0 .../tools/workspace/ros2/BUILD.bazel | 0 .../tools/workspace/ros2/repository.bzl | 22 ++ 44 files changed, 2754 insertions(+) create mode 100644 drake_ros_bazel_installed/BUILD.bazel create mode 100644 drake_ros_bazel_installed/WORKSPACE create mode 100644 drake_ros_bazel_installed/tools/BUILD.bazel create mode 100644 drake_ros_bazel_installed/tools/skylark/BUILD.bazel create mode 100644 drake_ros_bazel_installed/tools/skylark/dload.bzl create mode 100644 drake_ros_bazel_installed/tools/skylark/dload_cc.bzl create mode 100644 drake_ros_bazel_installed/tools/skylark/dload_py.bzl create mode 100644 drake_ros_bazel_installed/tools/skylark/drake_ros_cc.bzl create mode 100644 drake_ros_bazel_installed/tools/skylark/drake_ros_py.bzl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/__init__.py create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/server_mode.py create mode 100755 drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/BUILD.prologue.bazel create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/ament_cmake_CMakeLists.txt.in create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/package_alias.bzl.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_binary_import.bzl.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_library.bzl.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/package_meta_py_library.bzl.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library.bzl.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library_with_cc_extensions.bzl.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/package_share_filegroup.bzl.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/prologue.bzl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl_cmake_CMakeLists.txt.in create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl_package.xml.in create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/setup.sh.in create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/rmw_fastrtps_profile_gen.py create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/__init__.py create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/resources.py create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/sandboxing.py create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/metadata.py create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/system.py create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/utilities.py create mode 100755 drake_ros_bazel_installed/tools/skylark/ros2/rosidl_generate_interfaces.py create mode 100644 drake_ros_bazel_installed/tools/workspace/BUILD.bazel create mode 100644 drake_ros_bazel_installed/tools/workspace/ros2/BUILD.bazel create mode 100644 drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl diff --git a/drake_ros_bazel_installed/BUILD.bazel b/drake_ros_bazel_installed/BUILD.bazel new file mode 100644 index 000000000..e69de29bb diff --git a/drake_ros_bazel_installed/WORKSPACE b/drake_ros_bazel_installed/WORKSPACE new file mode 100644 index 000000000..c98a62b9a --- /dev/null +++ b/drake_ros_bazel_installed/WORKSPACE @@ -0,0 +1,15 @@ +workspace(name = "drake_ros") + +new_local_repository( + name = "drake_repository", + path = "/opt/drake", + build_file_content = "#", +) + +load("@drake_repository//:share/drake/repo.bzl", "drake_repository") + +drake_repository(name = "drake") + +load("//tools/workspace/ros2:repository.bzl", "ros2_repository") + +ros2_repository(name = "ros2") diff --git a/drake_ros_bazel_installed/tools/BUILD.bazel b/drake_ros_bazel_installed/tools/BUILD.bazel new file mode 100644 index 000000000..e69de29bb diff --git a/drake_ros_bazel_installed/tools/skylark/BUILD.bazel b/drake_ros_bazel_installed/tools/skylark/BUILD.bazel new file mode 100644 index 000000000..e69de29bb diff --git a/drake_ros_bazel_installed/tools/skylark/dload.bzl b/drake_ros_bazel_installed/tools/skylark/dload.bzl new file mode 100644 index 000000000..8b157c6e1 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/dload.bzl @@ -0,0 +1,297 @@ +# -*- python -*- + +""" +The purpose of these macros is to support the propagation of runtime information +that is key for proper execution from libraries to executables that depend on +them. + +The two primary macros of interest are `do_dload_shim`, which aids language +specific shim generation, and `do_dload_aware_target`, which can decorate +existing targets with runtime information. +""" + +MAGIC_VARIABLES = { + "${LOAD_PATH}": "LD_LIBRARY_PATH" # for Linux +} + +RuntimeInfo = provider(fields = ['env_changes']) +""" +This provider carries runtime information through the build graph. + +Attributes + + env_changes: runtime environment changes to be applied, as a mapping from + environment variable names to actions to be performed on them. + Actions are (action_type, action_args) tuples. Supported action types + are: 'path-prepend', 'path-replace', and 'replace'. Paths are resolved + relative to the runfiles directory of the downstream executable. + Also, see MAGIC_VARIABLES for platform-independent runtime environment + specification. +""" + +def unique(input_list): + """Extracts unique values from input list, while preserving their order.""" + output_list = [] + for item in input_list: + if item not in output_list: + output_list.append(item) + return output_list + +def merge_runtime_environment_changes(base, head): + """ + Merges runtime environment head changes into base. + + Merging runtime environment actions other than 'path-prepend' is not + allowed as results would silently vary depending on build order. + """ + for envvar, head_action in head.items(): + if envvar not in base: + base[envvar] = head_action + continue + + base_action = base[envvar] + + base_action_type = base_action[0] + head_action_type = head_action[0] + + if "replace" in base_action_type or "replace" in head_action_type: + tpl = "Got '{}' and '{}' actions, results depend on build order" + fail(msg = tpl.format(base_action_type, head_action_type)) + + if base_action_type != head_action_type or \ + base_action_type != "path-prepend": + tpl = "Expected 'path-prepend' actions, got '{}' and '{}'" + fail(msg = tpl.format(base_action_type, head_action_type)) + + merged_action_type = base_action_type + + base_action_args = base_action[1:] + head_action_args = head_action[1:] + merged_action_args = unique(base_action_args + head_action_args) + + base[envvar] = [merged_action_type] + merged_action_args + return base + + +def merge_runtime_info(base_info, head_info): + """ + Merges 'head' runtime information into 'base' runtime information, + then returns the latter. + """ + merge_runtime_environment_changes( + base_info.env_changes, head_info.env_changes + ) + return base_info + +def collect_runtime_info(targets): + """Returns all targets' runtime information merged into one.""" + runtime_info = RuntimeInfo(env_changes = {}) + for target in targets: + if RuntimeInfo not in target: + continue + merge_runtime_info(runtime_info, target[RuntimeInfo]) + return runtime_info + +def get_runtime_environment_changes(runtime_info): + """ + Returns changes to be applied to the runtime environment as + an (envvars, actions) tuple. + """ + actions = [] + for action in runtime_info.env_changes.values(): + action_type = action[0] + if action_type == "path-prepend": + action_args = action[1:] + common_action_args = [] + important_action_args = [] + for path in action_args: + if path.endswith("!"): + important_action_args.append(path[:-1]) + else: + common_action_args.append(path) + action_args = unique(important_action_args + common_action_args) + action = [action_type] + action_args + actions.append(action) + return list(runtime_info.env_changes.keys()), actions + +def normpath(path): + """ + Normalizes a path by removing redundant separators and up-level references. + + Equivalent to Python's os.path.normpath(). + """ + path_parts = path.split("/") + normalized_path_parts = [path_parts[0]] + for part in path_parts[1:]: + if part == "." or part == "": + continue + if part == "..": + normalized_path_parts.pop() + continue + normalized_path_parts.append(part) + return "/".join(normalized_path_parts) + +def get_dload_shim_attributes(): + """Yields attributes common to all dload_shim-based rules.""" + return { + "target": attr.label( + mandatory = True, + allow_files = True, + executable = True, + cfg = "target", + ), + "data": attr.label_list(allow_files = True), + "deps": attr.label_list(), + } + +def do_dload_shim(ctx, template, to_list): + """ + Implements common dload_shim rule functionality. + + All runtime information is merged, and made available to the + executable by the shim. + + Args: + ctx: context of a Bazel rule + template: string template for the shim + to_list: macro for list interpolation + + It expects the following attributes on ctx: + + target: executable target to be shimmed + data: executable data dependencies, may provide RuntimeInfo + deps: executable dependencies, may provide RuntimeInfo + + You may use get_dload_shim_attributes() on rule definition. + """ + executable_file = ctx.executable.target + + runtime_info = merge_runtime_info( + collect_runtime_info(ctx.attr.data), + collect_runtime_info(ctx.attr.deps) + ) + + envvars, actions = get_runtime_environment_changes(runtime_info) + + shim_content = template.format( + # Deal with usage in external workspaces' BUILD.bazel files + executable_path=normpath("{}/{}".format( + ctx.workspace_name, executable_file.short_path + )), + names=to_list([repr(name) for name in envvars]), + actions=to_list([ + to_list([ + repr(field) for field in action + ]) for action in actions + ]), + ) + shim = ctx.actions.declare_file(ctx.label.name) + ctx.actions.write(shim, shim_content, True) + return [DefaultInfo( + files = depset([shim]), + data_runfiles = ctx.runfiles(files = [shim]), + )] + +def resolve_runfile_path(ctx, path): + """ + Resolves a package relative path into an (expected) runfiles directory + relative path. + + All `$(rootpath...)` templates in the given path, if any, will be expanded. + """ + path = normpath(ctx.expand_location(path)) + path = path.lstrip("@") + if path.startswith("/"): + path = ctx.workspace_name + path + return path + +def parse_runtime_environment_action(ctx, action): + """ + Parses a runtime environment action, validating types and resolving paths. + """ + action_type, action_args = action[0], action[1:] + if action_type == "path-prepend": + if len(action_args) == 0: + tpl = "'{}' action requires at least one argument" + fail(msg = tpl.format(action_type)) + action_args = unique([ + resolve_runfile_path(ctx, path[:-1]) + "!" if path.endswith("!") + else resolve_runfile_path(ctx, path) for path in action_args + ]) + elif action_type in ("path-replace", "replace"): + if len(action_args) != 1: + tpl = "'{}' action requires exactly one argument" + fail(msg = tpl.format(action_type)) + if action_type.startswith("path"): + action_args = [resolve_runfile_path(ctx, action_args[0])] + else: + fail(msg = "'{}' action is unknown".format(action_type)) + return [action_type] + action_args + +def get_dload_aware_target_attributes(): + """ + Yields attributes common to all dload_aware_target rules. + + See do_dload_aware_target() documentation for further reference. + """ + return { + "base": attr.label(mandatory = True), + "data": attr.label_list(allow_files = True), + "deps": attr.label_list(), + "runenv": attr.string_list_dict(), + } + +def do_dload_aware_target(ctx): + """ + Implements common dload_aware_target rule functionality. + + Any specified runtime info is augmented with that of build and runtime + dependencies, and then propagated along with base target runfiles + i.e. its DefaultInfo provider. + + Args: + ctx: context of a Bazel rule + + It expects the following attributes on ctx: + + base: target to extend with runtime information + data: base target's data dependencies, may provide RuntimeInfo + deps: base target's dependencies, may provide RuntimeInfo + runenv: runtime environment changes for this target + + You may use get_dload_aware_target_attributes() on rule definition. + """ + runtime_info = RuntimeInfo(env_changes = { + MAGIC_VARIABLES.get(name, default=name): + parse_runtime_environment_action(ctx, action) + for name, action in ctx.attr.runenv.items() + }) + merge_runtime_info(runtime_info, collect_runtime_info(ctx.attr.data)) + merge_runtime_info(runtime_info, collect_runtime_info(ctx.attr.deps)) + return [ + # Recreate base's DefaultInfo to workaround + # https://github.com/bazelbuild/bazel/issues/9442 + DefaultInfo( + files = ctx.attr.base[DefaultInfo].files, + data_runfiles = ctx.attr.base[DefaultInfo].data_runfiles, + default_runfiles = ctx.attr.base[DefaultInfo].default_runfiles, + ), + runtime_info + ] + +def do_dload_aware_library(ctx, kind): + """ + Implements common dload_aware_library rule functionality. + + It builds on top of dload_aware_target functionality, also propagating + library specific providers e.g. CcInfo providers for cc_library base + targets. + """ + providers = do_dload_aware_target(ctx) + providers.append(ctx.attr.base[kind]) + return providers + +dload_aware_target = rule( + attrs = get_dload_aware_target_attributes(), + implementation = do_dload_aware_target, +) diff --git a/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl b/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl new file mode 100644 index 000000000..0f5182e51 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl @@ -0,0 +1,111 @@ +# -*- python -*- + +""" +The purpose of these macros is to support the propagation of runtime information +that is key for proper execution from C/C++ libraries to C/C++ binaries and +tests. +""" + +load( + "//tools/skylark:dload.bzl", + "do_dload_shim", + "do_dload_aware_library", + "get_dload_shim_attributes", + "get_dload_aware_target_attributes", +) + +DLOAD_CC_SHIM_TEMPLATE = """\ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "tools/cpp/runfiles/runfiles.h" + +using bazel::tools::cpp::runfiles::Runfiles; + +int main(int argc, const char * argv[]) {{ + std::string error; + std::unique_ptr runfiles(Runfiles::Create(argv[0], &error)); + if (!runfiles) {{ + std::cerr << "ERROR: " << error << std::endl; + return -1; + }} + + std::vector names = {names}; + std::vector> actions = {actions}; // NOLINT + for (size_t i = 0; i < names.size(); ++i) {{ + std::stringstream value; + if (actions[i][0] == "replace") {{ + assert(actions[i].size() != 2); + value << actions[i][1]; + }} else if (actions[i][0] == "path-replace") {{ + assert(actions[i].size() == 2); + value << runfiles->Rlocation(actions[i][1]); + }} else if (actions[i][0] == "path-prepend") {{ + assert(actions[i].size() >= 2); + for (size_t j = 1; j < actions[i].size(); ++j) {{ + value << runfiles->Rlocation(actions[i][j]) << ":"; + }} + + const char * raw_value = getenv(names[i].c_str()); + if (raw_value != nullptr) {{ + value << raw_value; + }} + }} else {{ + assert(false); // should never get here + }} + + if (setenv(names[i].c_str(), value.str().c_str(), 1) != 0) {{ + std::cerr << "ERROR: failed to set " << names[i] << std::endl; + }} + }} + + const std::string executable_path = + runfiles->Rlocation("{executable_path}"); // NOLINT + + char ** other_argv = new char*[argc + 1]; + other_argv[0] = strdup(executable_path.c_str()); + for (int i = 1; i < argc; ++i) {{ + other_argv[i] = strdup(argv[i]); + }} + other_argv[argc] = NULL; + int ret = execv(other_argv[0], other_argv); + std::cout << "ERROR: " << strerror(errno) << std::endl; + return ret; +}} +""" + +def to_cc_list(collection): + """Turn collection into a C++ aggregate initializer expression.""" + return "{" + ", ".join(collection) + "}" + +def _dload_cc_shim_impl(ctx): + return do_dload_shim(ctx, DLOAD_CC_SHIM_TEMPLATE, to_cc_list) + +dload_cc_shim = rule( + attrs = get_dload_shim_attributes(), + implementation = _dload_cc_shim_impl, + output_to_genfiles = True, +) +""" +This rule() generates a C++ shim that can carry runtime information. +See do_dload_shim() documentation for further reference. +""" + +def _dload_aware_cc_library_impl(ctx): + return do_dload_aware_library(ctx, CcInfo) + +dload_aware_cc_library = rule( + attrs = get_dload_aware_target_attributes(), + implementation = _dload_aware_cc_library_impl, +) +""" +This rule() decorates a cc_library() rule to carry runtime information. +See do_dload_aware_library() documentation for further reference. +""" diff --git a/drake_ros_bazel_installed/tools/skylark/dload_py.bzl b/drake_ros_bazel_installed/tools/skylark/dload_py.bzl new file mode 100644 index 000000000..bf1e875ca --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/dload_py.bzl @@ -0,0 +1,82 @@ +# -*- python -*- +# vi: set ft=python : + +""" +The purpose of these macros is to support the propagation of runtime information +that is key for proper execution from Python libraries to Python binaries and +tests. +""" + +load( + "//tools/skylark:dload.bzl", + "do_dload_shim", + "do_dload_aware_library", + "get_dload_shim_attributes", + "get_dload_aware_target_attributes", +) + +DLOAD_PY_SHIM_TEMPLATE = """\ +import os +import sys + +from bazel_tools.tools.python.runfiles import runfiles + +r = runfiles.Create() +# NOTE(hidmic): unlike its C++ equivalent, Python runfiles' +# builtin tools will only look for runfiles in the manifest +# if there is a manifest +runfiles_dir = r.EnvVars()['RUNFILES_DIR'] + +def rlocation(path): + return r.Rlocation(path) or os.path.join(runfiles_dir, path) + +for name, action in zip({names}, {actions}): # noqa + action_type, action_args = action[0], action[1:] + if action_type == 'replace': + assert len(action_args) == 1 + value = action_args[0] + elif action_type == 'path-replace': + assert len(action_args) == 1 + value = rlocation(action_args[0]) + elif action_type == 'path-prepend': + assert len(action_args) > 0 + value = ':'.join([rlocation(path) for path in action_args]) + if name in os.environ: + value += ':' + os.environ[name] + else: + assert False # should never get here + os.environ[name] = value + +executable_path = r.Rlocation('{executable_path}') # noqa +argv = [executable_path] + sys.argv[1:] +os.execv(executable_path, argv) +""" + +def to_py_list(collection): + """Turn collection into a Python list expression.""" + return "[" + ", ".join(collection) + "]" + +def _dload_py_shim_impl(ctx): + return do_dload_shim(ctx, DLOAD_PY_SHIM_TEMPLATE, to_py_list) + +dload_py_shim = rule( + attrs = get_dload_shim_attributes(), + output_to_genfiles = True, + implementation = _dload_py_shim_impl, +) +""" +This rule() generates a Python shim that can carry runtime information. +See do_dload_shim() documentation for further reference. +""" + +def _dload_aware_py_library_impl(ctx): + return do_dload_aware_library(ctx, PyInfo) + +dload_aware_py_library = rule( + attrs = get_dload_aware_target_attributes(), + implementation = _dload_aware_py_library_impl, +) +""" +This rule() decorates a py_library() rule to carry runtime information. +See do_dload_aware_library() documentation for further reference. +""" diff --git a/drake_ros_bazel_installed/tools/skylark/drake_ros_cc.bzl b/drake_ros_bazel_installed/tools/skylark/drake_ros_cc.bzl new file mode 100644 index 000000000..0ff4ee3f9 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/drake_ros_cc.bzl @@ -0,0 +1,169 @@ +# -*- python -*- + +load( + "//tools/skylark:dload_cc.bzl", + "dload_aware_cc_library", + "dload_cc_shim", +) + +def drake_ros_cc_library(name, deps = [], data = [], runenv = {}, + testonly = False, visibility = None, **kwargs): + """ + Builds a C/C++ library and carries runtime information, if any. + + Equivalent to the cc_library() rule. + """ + library = "_" + name + cc_library( + name = library, + deps = deps, + data = data, + testonly = testonly, + visibility = visibility, + **kwargs + ) + dload_aware_cc_library( + name = name, + base = ":" + library, + data = data, + deps = [":" + library] + deps, + runenv = runenv, + testonly = testonly, + visibility = visibility, + ) + +def drake_ros_cc_binary_import(name, executable, data = [], deps = [], tags = [], + testonly = 0, visibility = None, **kwargs): + """ + Imports a pre-compiled C/C++ executable, picking up runtime information + in dependencies. + + Args: + executable: executable file + + Similar to the cc_binary() rule. + """ + shim = name + "_shim.cc" + dload_cc_shim( + name = shim, + target = executable, + data = data, + deps = deps, + testonly = testonly, + visibility = visibility, + ) + cc_binary( + name = name, + srcs = [shim], + data = [executable] + data, + deps = [ + "@bazel_tools//tools/cpp/runfiles", + ] + deps, + tags = ["nolint"] + tags, + testonly = testonly, + visibility = visibility, + **kwargs + ) + +def drake_ros_cc_binary(name, srcs = [], data = [], deps = [], linkshared = None, tags = [], + testonly = False, visibility = None, **kwargs): + """ + Builds a C/C++ binary, picking up runtime information in dependencies. + + Equivalent to the cc_binary() rule. + + Propagation of runtime information is disabled if linkshared is True. + """ + if linkshared: + cc_binary( + name = name, + srcs = srcs, + data = data, + deps = deps, + linkshared = linkshared, + visibility = visibility, + testonly = testonly, + tags = tags, + **kwargs + ) + return + binary = "_" + name + cc_binary( + name = binary, + srcs = srcs, + data = data, + deps = deps, + linkshared = linkshared, + visibility = visibility, + testonly = testonly, + tags = tags, + **kwargs + ) + + shim = binary + "_shim.cc" + dload_cc_shim( + name = shim, + target = ":" + binary, + data = data, + deps = deps, + testonly = testonly, + visibility = visibility, + ) + cc_binary( + name = name, + srcs = [shim], + data = [":" + binary], + deps = [ + "@bazel_tools//tools/cpp/runfiles", + ], + tags = ["nolint"] + tags, + visibility = visibility, + testonly = testonly, + **kwargs + ) + +def drake_ros_cc_test(name, srcs = [], data = [], deps = [], + testonly = 1, visibility = [], tags = [], + **kwargs): + """ + Builds C/C++ test, picking up runtime information in dependencies. + + Equivalent to the cc_test() rule. + """ + + if not srcs: + srcs = ["test/%s.cc" % name] + + test = "_" + name + + cc_test( + name = test, + srcs = srcs, + data = data, + deps = deps, + tags = ["manual"] + tags, + testonly = testonly, + visibility = visibility, + **kwargs + ) + shim = test + "_shim.cc" + dload_cc_shim( + name = shim, + target = ":" + test, + data = data, + deps = deps, + testonly = testonly, + visibility = visibility, + ) + cc_test( + name = name, + srcs = [shim], + data = [":" + test], + deps = [ + "@bazel_tools//tools/cpp/runfiles", + ], + tags = ["nolint"] + tags, + testonly = testonly, + visibility = visibility, + **kwargs + ) diff --git a/drake_ros_bazel_installed/tools/skylark/drake_ros_py.bzl b/drake_ros_bazel_installed/tools/skylark/drake_ros_py.bzl new file mode 100644 index 000000000..f4c6b7052 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/drake_ros_py.bzl @@ -0,0 +1,146 @@ +# -*- python -*- +# vi: set ft=python : + +load( + "//tools/skylark:dload_py.bzl", + "dload_aware_py_library", + "dload_py_shim", +) + +def drake_ros_py_library(name, srcs = [], main = None, imports = [], + srcs_version = None, deps = [], data = [], + runenv = {}, testonly = 0, visibility = None, + **kwargs): + """ + Adds a Python library and carries runtime information, if any. + + Equivalent to the py_library() rule. + """ + library_name = "_" + name + py_library( + name = library_name, + srcs = srcs, + main = main, + imports = imports, + srcs_version = srcs_version, + deps = deps, + data = data, + testonly = testonly, + visibility = visibility, + **kwargs + ) + dload_aware_py_library( + name = name, + base = ":" + library_name, + data = data, + deps = [":" + library_name] + deps, + runenv = runenv, + testonly = testonly, + visibility = visibility, + ) + +def drake_ros_py_binary(name, srcs = [], main = None, imports = [], args = [], + srcs_version = None, data = [], deps = [], tags = [], + testonly = 0, visibility = None, **kwargs): + """ + Adds a Python executable, picking up runtime information in dependencies. + + Equivalent to the py_library() rule. + """ + binary = "_" + name + if not main: + main = name + ".py" + py_binary( + name = binary, + srcs = srcs, + main = main, + imports = imports, + srcs_version = srcs_version, + data = data, + deps = deps, + tags = tags, + testonly = testonly, + visibility = visibility, + **kwargs + ) + shim = binary + "_shim.py" + dload_py_shim( + name = shim, + target = ":" + binary, + data = data, + deps = deps, + testonly = testonly, + visibility = visibility, + ) + py_binary( + name = name, + srcs = [shim], + main = shim, + data = [":" + binary], + deps = [ + "@bazel_tools//tools/python/runfiles", + ":" + binary, # Support py_binary being used a dependency + ], + srcs_version = srcs_version, + tags = ["nolint"] + tags, + testonly = testonly, + visibility = visibility, + **kwargs + ) + +def drake_ros_py_test(name, srcs = [], main = None, imports = [], + srcs_version = None, data = [], args = [], + deps = [], tags = [], visibility = None, + testonly = 1, **kwargs): + """ + Adds a Python test, picking up runtime information in dependencies. + + Equivalent to the py_test() rule. + """ + if not srcs: + srcs = ["test/%s.py" % name] + + if not main: + if len(srcs) > 1: + fail("You must specify main if you have more than one source.") + main = srcs[0] + + test = "_" + name + + py_test( + name = test, + srcs = srcs, + main = main, + args = args, + imports = imports, + srcs_version = srcs_version, + data = data, + deps = deps, + tags = ["manual"] + tags, + testonly = testonly, + visibility = visibility, + **kwargs + ) + shim = test + "_shim.py" + dload_py_shim( + name = shim, + target = ":" + test, + data = data, + deps = deps, + testonly = testonly, + visibility = visibility, + ) + py_test( + name = name, + srcs = [shim], + data = [":" + test], + main = shim, + deps = [ + "@bazel_tools//tools/python/runfiles", + ], + srcs_version = srcs_version, + tags = ["nolint"] + tags, + testonly = testonly, + visibility = visibility, + **kwargs + ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel new file mode 100644 index 000000000..d2ec10fa7 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel @@ -0,0 +1,7 @@ +# -*- mode: python -*- + +py_binary( + name = "rmw_fastrtps_profile_gen", + srcs = ["rmw_fastrtps_profile_gen.py"], + visibility = ["//visibility:public"], +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/__init__.py b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/__init__.py new file mode 100644 index 000000000..e9ff12f1e --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/__init__.py @@ -0,0 +1,26 @@ +import os +import shutil +import subprocess + +from .server_mode import server_mode + + +def configure_file(src, dest, subs): + with open(src, 'r') as f: + text = f.read() + for old, new in subs.items(): + text = text.replace(old, new) + with open(dest, 'w') as f: + f.write(text) + + +def build_then_install(project_path, *args, build_path='build'): + if not os.path.isabs(build_path): + build_path = os.path.join(project_path, build_path) + if os.path.exists(build_path): + shutil.rmtree(build_path) + os.makedirs(build_path) + subprocess.run([ + 'cmake', '-G', 'Unix Makefiles', *args, project_path + ], cwd=build_path, check=True) + subprocess.run(['make', 'install'], cwd=build_path, check=True) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/server_mode.py b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/server_mode.py new file mode 100644 index 000000000..3639c0e28 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/server_mode.py @@ -0,0 +1,195 @@ +import contextlib +import functools +import json +import os +import re +import selectors +import signal +import socket +import subprocess +import time +import tempfile + + +class CMakeServer: + + START_MAGIC_STRING = '[== "CMake Server" ==[' + END_MAGIC_STRING = ']== "CMake Server" ==]' + + def __init__(self, address=None): + if not address: + address = tempfile.mktemp() + self.__address = address + cmd = ['cmake', '-E', 'server', '--experimental'] + cmd.append('--pipe=' + self.__address) + self.__process = subprocess.Popen(cmd) + while not os.path.exists(self.__address): + if self.__process.poll() is not None: + break + time.sleep(1) + + @property + def address(self): + return self.__address + + def shutdown(self, timeout=None): + self.__process.send_signal(signal.SIGINT) + try: + self.__process.wait(timeout) + except subprocess.TimeoutExpired: + self.__process.kill() + + +class CMakeClient: + + def __init__(self, address): + self.__socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + try: + self.__socket.connect(address) + self.__selector = selectors.DefaultSelector() + self.__selector.register(self.__socket, selectors.EVENT_READ) + self.__unpack_buffer = '' + + message = next(self.__receive(timeout=5), None) + assert message + assert message['type'] == 'hello' + self.__supported_protocol_versions = \ + message['supportedProtocolVersions'] + except: + self.__socket.close() + raise + + @property + def supported_protocol_versions(self): + return self.__supported_protocol_versions + + def shutdown(self): + self.__socket.close() + + def __pack(self, payload): + return '\n'.join([ + CMakeServer.START_MAGIC_STRING, + json.dumps(payload), + CMakeServer.END_MAGIC_STRING + ]) + '\n' + + def __unpack(self, partial): + self.__unpack_buffer += partial + while self.__unpack_buffer: + packet_start_index = \ + self.__unpack_buffer.find(CMakeServer.START_MAGIC_STRING) + if packet_start_index < 0: + break + payload_start_index = \ + packet_start_index + len(CMakeServer.START_MAGIC_STRING) + payload_end_index = self.__unpack_buffer.find( + CMakeServer.END_MAGIC_STRING, payload_start_index + ) + if payload_end_index < 0: + break + payload = json.loads( + self.__unpack_buffer[payload_start_index:payload_end_index] + ) + packet_end_index = \ + payload_end_index + len(CMakeServer.END_MAGIC_STRING) + self.__unpack_buffer = self.__unpack_buffer[packet_end_index:] + + assert type(payload) is dict + assert 'type' in payload + yield payload + + def __send(self, payload): + packet = self.__pack(payload).encode('utf-8') + written = self.__socket.send(packet) + return len(packet) == written + + def __receive(self, timeout=None): + if timeout is not None: + deadline = time.time() + timeout + while True: + events = self.__selector.select(timeout) + for key, mask in events: + assert key.fileobj is self.__socket + assert mask & selectors.EVENT_READ + partial = self.__socket.recv(4096) + yield from self.__unpack(partial.decode('utf-8')) + if timeout is not None: + current_time = time.time() + if current_time >= deadline: + break + timeout = deadline - current_time + + def exchange( + self, + request, + message_callback=None, + progress_callback=None, + **kwargs + ): + assert self.__send(request) + for response in self.__receive(**kwargs): + if response['type'] == 'signal': + # Ignore signal responses + continue + if response['inReplyTo'] != request['type']: + continue + if response['type'] == 'reply': + return response + if response['type'] == 'error': + raise RuntimeError(response['errorMessage']) + if response['type'] == 'message': + if message_callback: + message_callback(response['message']) + continue + if response['type'] == 'progress': + if progress_callback: + message = response['progressMessage'] + progress = ( + response['progressMinimum'], + response['progressMaximum'], + response['progressCurrent'] + ) + progress_callback(message, progress) + raise TimeoutError( + 'Timeout waiting for reply to {!r} request'.format(request) + ) + + def __request_method(self, type_, attributes=None, **kwargs): + request = {'type': type_} + if attributes: + request.update(attributes) + reply = self.exchange(request, **kwargs) + return { + k: v for k, v in reply.items() + # Drop transport specific attributes + if k not in ('inReplyTo', 'type', 'cookie') + } + + def __getattr__(self, name): + return functools.partial(self.__request_method, name) + + +@contextlib.contextmanager +def server_mode(project_path): + with tempfile.TemporaryDirectory() as tmpdir: + cmake_server = CMakeServer(address=os.path.join(tmpdir, 'pipe')) + try: + cmake = CMakeClient(cmake_server.address) + try: + supported_versions = cmake.supported_protocol_versions + assert any(v['major'] == 1 for v in supported_versions) + project_build_path = os.path.join(project_path, 'build') + cmake.handshake( + attributes={ + 'sourceDirectory': os.path.abspath(project_path), + 'buildDirectory': os.path.abspath(project_build_path), + 'generator': 'Unix Makefiles', + 'protocolVersion': {'major': 1} + }, + timeout=5 + ) + yield cmake + finally: + cmake.shutdown() + finally: + cmake_server.shutdown() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py new file mode 100755 index 000000000..b5f800400 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python3 + +import argparse +import collections +import os +import sys + +from multiprocessing.dummy import Pool +import xml.etree.ElementTree as ET + +import toposort + +sys.path.insert(0, os.path.dirname(__file__)) # noqa + +from ros2bzl.scrapping import index_all_packages +from ros2bzl.scrapping import build_dependency_graph +from ros2bzl.scrapping.ament_cmake import collect_ament_cmake_package_properties +from ros2bzl.scrapping.ament_cmake import collect_ament_cmake_package_direct_properties +from ros2bzl.scrapping.ament_python import collect_ament_python_package_direct_properties + +from ros2bzl.templates import configure_package_meta_py_library +from ros2bzl.templates import configure_package_alias +from ros2bzl.templates import configure_package_cc_library +from ros2bzl.templates import configure_package_executable_imports +from ros2bzl.templates import configure_package_py_library +from ros2bzl.templates import configure_package_share_filegroup + +from ros2bzl.resources import load_resource +from ros2bzl.resources import setup_underlay + +import ros2bzl.sandboxing as sandboxing + +from ros2bzl.utilities import interpolate + + +def parse_arguments(): + parser = argparse.ArgumentParser() + parser.add_argument( + 'repository_name', help='Bazel repository name' + ) + parser.add_argument( + '-s', '--sandbox', action='append', default=[], + help='Path mappings for sandboxing, in "outer:inner" format' + ) + parser.add_argument( + '-i', '--include-package', action='append', dest='include_packages', default=[], + help='Packages to be included (plus their dependencies)' + ) + parser.add_argument( + '-e', '--exclude-package', action='append', dest='exclude_packages', + default=[], help='Packages to be explicitly excluded' + ) + parser.add_argument( + '-x', '--extras', action='append', default=[], + help=('Additional dependencies for generated targets,' + ' in "label.attribute+=label_or_string" format') + ) + parser.add_argument( + '-j', '--jobs', type=int, default=None, help='Number of jobs to use' + ) + args = parser.parse_args() + + extras = {} + for item in args.extras: + lhs, _, rhs = item.partition('+=') + target_name, _, attribute_name = lhs.rpartition('.') + if attribute_name not in extras: + extras[attribute_name] = {} + if target_name not in extras[attribute_name]: + extras[attribute_name][target_name] = [] + extras[attribute_name][target_name].append(rhs) + args.extras = extras + + args.sandbox = sandboxing.configure( + name=args.repository_name, mapping=dict( + entry.partition(':')[0::2] for entry in args.sandbox + ) + ) + + return args + + +def generate_build_file(packages, dependency_graph, cache, extras, sandbox): + rmw_implementation_packages = { + name: metadata for name, metadata in packages.items() + if 'rmw_implementation_packages' in metadata['groups'] + } + + with open('BUILD.bazel', 'w') as fd: + fd.write(load_resource('BUILD.prologue.bazel') + '\n') + + for name in toposort.toposort_flatten(dependency_graph): + metadata = packages[name] + + dependencies = { + dependency_name: packages[dependency_name] + for dependency_name in dependency_graph[name] + } + + template, config = \ + configure_package_share_filegroup(name, metadata, sandbox) + fd.write(interpolate(template, config) + '\n') + + main_target = None + if metadata['build_type'] == 'ament_cmake': + properties = collect_ament_cmake_package_direct_properties( + name, metadata, dependencies, cache + ) + + template, config = configure_package_cc_library( + name, metadata, properties, dependencies, extras, sandbox + ) + + if any(properties.values()): + # Tentatively set as main target (for aliasing) + main_target = config['name'] + + fd.write(interpolate(template, config) + '\n') + + # No way to tell if there's Python code for this package + # but to look for it. + try: + properties = collect_ament_python_package_direct_properties( + name, metadata, dependencies, cache + ) + # Add 'py' as language if not there. + metadata['langs'].add('py') + except ValueError: + if any('py' in metadata['langs'] for metadata in dependencies.values()): + metadata['langs'].add('py (transitively)') + # Dependencies still need to be propagated. + template, config = \ + configure_package_meta_py_library(name, metadata, dependencies) + fd.write(interpolate(template, config) + '\n') + + properties = {} + + if properties: + template, config = configure_package_py_library( + name, metadata, properties, dependencies, extras, sandbox + ) + + # Set as main target if not set already, + # otherwise there's no main target. + main_target = config['name'] if not main_target else None + + fd.write(interpolate(template, config) + '\n') + + if main_target is not None and main_target != name: + template, config = configure_package_alias(name, main_target) + fd.write(interpolate(template, config) + '\n') + + dependencies.update(rmw_implementation_packages) + for template, config in configure_package_executable_imports( + name, metadata, dependencies, extras, sandbox + ): + fd.write(interpolate(template, config) + '\n') + + +def precache_ament_cmake_properties(packages, jobs=None): + ament_cmake_packages = { + name: metadata + for name, metadata in packages.items() + if metadata['build_type'] == 'ament_cmake' + } + with Pool(jobs) as pool: + return dict(zip( + ament_cmake_packages.keys(), pool.starmap( + collect_ament_cmake_package_properties, + ament_cmake_packages.items() + ) + )) + + +def main(): + args = parse_arguments() + + packages, dependency_graph = build_dependency_graph( + index_all_packages(), + set(args.include_packages), + set(args.exclude_packages) + ) + + cache = { + 'ament_cmake': precache_ament_cmake_properties(packages, jobs=args.jobs) + } + + generate_build_file(packages, dependency_graph, cache, args.extras, args.sandbox) + + for name, metadata in packages.items(): + # For downstream repositories to use + metadata['bazel_workspace'] = args.repository_name + setup_underlay(packages, dependency_graph, cache, args.sandbox) + + +if __name__ == '__main__': + main() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/BUILD.prologue.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/resources/BUILD.prologue.bazel new file mode 100644 index 000000000..28e66d43a --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/BUILD.prologue.bazel @@ -0,0 +1,8 @@ +# -*- python -*- + +package(default_visibility = ["//visibility:public"]) + +load("//tools/skylark:drake_ros_cc.bzl", "drake_ros_cc_binary_import") +load("//tools/skylark:drake_ros_cc.bzl", "drake_ros_cc_library") +load("//tools/skylark:drake_ros_py.bzl", "drake_ros_py_library") +load("//tools/skylark/ros2:ros2.bzl", "package_share_filegroup") diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ament_cmake_CMakeLists.txt.in b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ament_cmake_CMakeLists.txt.in new file mode 100644 index 000000000..28a6e0dda --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ament_cmake_CMakeLists.txt.in @@ -0,0 +1,37 @@ +# Generated by Bazel. +cmake_minimum_required(VERSION 3.5) +project(@NAME@ C CXX) + +find_package(ament_cmake REQUIRED) +# TODO(hidmic): remove when rviz_default_plugins +# export dependency on Qt5 component correctly +if("@PACKAGE@" STREQUAL "rviz_default_plugins") + find_package(Qt5 COMPONENTS Widgets REQUIRED) +endif() +find_package(@PACKAGE@ REQUIRED) + +file(WRITE empty.cc "") +add_library(${PROJECT_NAME} SHARED empty.cc) +if("@PACKAGE@" STREQUAL "rviz_ogre_vendor") + # TODO(hidmic): generalize special case handling + set(OGRE_TARGETS) + if(TARGET rviz_ogre_vendor::OgreMain) + list(APPEND OGRE_TARGETS rviz_ogre_vendor::OgreMain) + endif() + if(TARGET rviz_ogre_vendor::OgreOverlay) + list(APPEND OGRE_TARGETS rviz_ogre_vendor::OgreOverlay) + endif() + if(TARGET rviz_ogre_vendor::RenderSystem_GL) + list(APPEND OGRE_TARGETS rviz_ogre_vendor::RenderSystem_GL) + endif() + if(TARGET rviz_ogre_vendor::OgreGLSupport) + list(APPEND OGRE_TARGETS rviz_ogre_vendor::OgreGLSupport) + endif() + target_link_libraries(${PROJECT_NAME} + ${OGRE_TARGETS} ${OGRE_LIBRARIES} ${OGRE_PLUGINS} + ) + file(GLOB ogre_plugin_libraries "${OGRE_PLUGIN_DIR}/*.so*") + target_link_libraries(${PROJECT_NAME} ${ogre_plugin_libraries}) +else() + ament_target_dependencies(${PROJECT_NAME} @PACKAGE@) +endif() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_alias.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_alias.bzl.tpl new file mode 100644 index 000000000..6483cff96 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_alias.bzl.tpl @@ -0,0 +1,4 @@ +alias( + name = {name}, + actual = {actual}, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_binary_import.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_binary_import.bzl.tpl new file mode 100644 index 000000000..a16922b90 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_binary_import.bzl.tpl @@ -0,0 +1,5 @@ +drake_ros_cc_binary_import( + name = {name}, + executable = {executable}, + data = {data}, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_library.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_library.bzl.tpl new file mode 100644 index 000000000..cff474b2b --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_library.bzl.tpl @@ -0,0 +1,12 @@ +drake_ros_cc_library( + name = {name}, + srcs = {srcs}, + hdrs = glob(["{{}}/**/*.*".format(x) for x in {includes}]), + includes = {include_directories}, + copts = {copts}, + defines = {defines}, + linkopts = {linkopts}, + data = {data}, + deps = {deps}, + runenv = {runenv}, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_meta_py_library.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_meta_py_library.bzl.tpl new file mode 100644 index 000000000..5a7c89a7e --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_meta_py_library.bzl.tpl @@ -0,0 +1,4 @@ +drake_ros_py_library( + name = {name}, + deps = {deps}, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library.bzl.tpl new file mode 100644 index 000000000..b4f3c2a82 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library.bzl.tpl @@ -0,0 +1,11 @@ +drake_ros_py_library( + name = {name}, + srcs = glob(["{{}}/**/*.py".format(x) for x in {packages}]), + data = glob( + include=["{{}}/**/*.*".format(x) for x in {packages}], + exclude=["**/*.py", "**/*.so"], + ) + {data}, + imports = {imports}, + deps = {deps}, + runenv = {runenv}, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library_with_cc_extensions.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library_with_cc_extensions.bzl.tpl new file mode 100644 index 000000000..b4f3c2a82 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library_with_cc_extensions.bzl.tpl @@ -0,0 +1,11 @@ +drake_ros_py_library( + name = {name}, + srcs = glob(["{{}}/**/*.py".format(x) for x in {packages}]), + data = glob( + include=["{{}}/**/*.*".format(x) for x in {packages}], + exclude=["**/*.py", "**/*.so"], + ) + {data}, + imports = {imports}, + deps = {deps}, + runenv = {runenv}, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_share_filegroup.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_share_filegroup.bzl.tpl new file mode 100644 index 000000000..a1d29413a --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_share_filegroup.bzl.tpl @@ -0,0 +1,4 @@ +package_share_filegroup( + name = {name}, + share_directories = {share_directories}, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/prologue.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/prologue.bzl new file mode 100644 index 000000000..147b73aa7 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/prologue.bzl @@ -0,0 +1,8 @@ +# -*- python -*- + +load("//tools/skylark:anzu_ros_cc.bzl", "drake_ros_cc_binary_import") +load("//tools/skylark:anzu_ros_cc.bzl", "drake_ros_cc_library") +load("//tools/skylark:anzu_ros_py.bzl", "drake_ros_py_library") +load("//tools/skylark/ros2:ros2.bzl", "package_share_filegroup") + +glob = native.glob diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl.tpl new file mode 100644 index 000000000..77fbbf4f9 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl.tpl @@ -0,0 +1,15 @@ +# -*- python -*- + +def rosidl_generate_interfaces_command( + repository, package, interfaces, output, prefix=None, dependencies=[] +): + cmd = ["ROS2BZL_PREFIX_PATH={repository_dir}", "{repository_dir}/env.sh"] + cmd.append("{repository_dir}/generate_rosidl_file.py") + cmd.extend(["-o ", output]) + if prefix: + cmd.extend(["-t", prefix]) + cmd.append(repository) + cmd.append(package) + cmd.extend(interfaces) + cmd.extend(["-d " + dep for dep in dependencies]) + return " ".join(cmd) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl_cmake_CMakeLists.txt.in b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl_cmake_CMakeLists.txt.in new file mode 100644 index 000000000..1d54487ef --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl_cmake_CMakeLists.txt.in @@ -0,0 +1,19 @@ +# Generated by Bazel. +cmake_minimum_required(VERSION 3.5) +project(@NAME@ C CXX) + +find_package(ament_cmake REQUIRED) +find_package(rosidl_cmake REQUIRED) +find_package(rosidl_default_generators REQUIRED) +set(DEPENDENCIES @DEPENDENCIES@) +foreach(dep IN LISTS DEPENDENCIES) + find_package(${dep} REQUIRED) +endforeach() + +rosidl_generate_interfaces( + ${PROJECT_NAME} + @INTERFACES@ + DEPENDENCIES ${DEPENDENCIES} +) + +ament_package() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl_package.xml.in b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl_package.xml.in new file mode 100644 index 000000000..d7c1b27c0 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl_package.xml.in @@ -0,0 +1,23 @@ + + + + @NAME@ + 0.0.0 + Mock package to generate @NAME@ rosidl interfaces + nobody + Apache License 2.0 + + ament_cmake + rosidl_cmake + rosidl_default_generators + + @DEPENDENCIES@ + + rosidl_default_runtime + + rosidl_interface_packages + + + ament_cmake + + diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/setup.sh.in b/drake_ros_bazel_installed/tools/skylark/ros2/resources/setup.sh.in new file mode 100644 index 000000000..f0731fb37 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/setup.sh.in @@ -0,0 +1,15 @@ +#! /bin/sh + +# Run @ID@ + +# Source all workspaces +for ws in @WORKSPACES@; do + . $ws/setup.sh +done + +# Setup environment +export ROS2BZL_PREFIX_PATH="@REPOSITORY_DIR@" +export PYTHONPATH="@REPOSITORY_DIR@:$PYTHONPATH" +export PATH="@REPOSITORY_DIR@:$PATH" + +exec $@ diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl new file mode 100644 index 000000000..bddf538a6 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl @@ -0,0 +1,52 @@ +# -*- python -*- +# vi: set ft=python : + +load( + "//tools/skylark:dload.bzl", + "dload_aware_target", +) + +BASE_PORT = 16384 + +def to_bitstrings(integer, *bitsizes): + bitstrings = [] + for bitsize in reversed(bitsizes): + bitstrings.append(integer & ((1 << bitsize) - 1)) + integer = integer >> bitsize + return reversed(bitstrings) + +def rmw_fastrtps_netconf(name): + tool = "//tools/skylark/ros2:rmw_fastrtps_profile_gen" + label_name = "{}//{}:{}".format( + native.repository_name(), native.package_name(), name + ) + bitstrings = to_bitstrings(hash(label_name), 8, 8, 8, 12, 12, 12) + args = [ + name, + "--metatraffic-multicast-locator {}:{}".format( + ".".join(["239"] + [str(octet) for octet in bitstrings[:3]]), + BASE_PORT + bitstrings[3] + ), + "--metatraffic-unicast-locator {}".format(BASE_PORT + bitstrings[4]), + "--unicast-locator {}".format(BASE_PORT + bitstrings[5]), + "--use-as-default", + ] + + profile = name + "_profile" + native.genrule( + name = profile, + outs = [profile + ".xml"], + cmd = "./$(location {}) {} > $@".format(tool, " ".join(args)), + tools = [tool], + output_to_bindir = True, + ) + profile_path = "{}/{}/{}.xml".format( + native.repository_name(), native.package_name(), profile + ) + dload_aware_target( + name = name, + base = ":" + profile, + runenv = { + "FASTRTPS_DEFAULT_PROFILES_FILE": ["path-replace", profile_path], + } + ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/rmw_fastrtps_profile_gen.py b/drake_ros_bazel_installed/tools/skylark/ros2/rmw_fastrtps_profile_gen.py new file mode 100644 index 000000000..8b4aae02b --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/rmw_fastrtps_profile_gen.py @@ -0,0 +1,104 @@ +import argparse +import sys + +import xml.dom.minidom as minidom +import xml.etree.ElementTree as ET + + +def build_udpv4_locator(builder, locator): + builder.start('locator') + builder.start('udpv4') + ip, _, port = locator.rpartition(':') + if ip: + builder.start('address') + builder.data(ip) + builder.end('address') + builder.start('port') + builder.data(port) + builder.end('port') + builder.end('udpv4') + builder.end('locator') + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('profile_name') + parser.add_argument( + '--use-as-default', action='store_true', default=False + ) + parser.add_argument( + '--metatraffic-multicast-locator', + dest='metatraffic_multicast_locators', + action='append', default=[] + ) + parser.add_argument( + '--metatraffic-unicast-locator', + dest='metatraffic_unicast_locators', + action='append', default=[] + ) + parser.add_argument( + '--unicast-locator', + dest='unicast_locators', + action='append', default=[] + ) + args = parser.parse_args() + + transport_name = args.profile_name + '_transport' + + builder = ET.TreeBuilder() + xmlns = 'http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles' + builder.start('profiles', {'xmlns': xmlns}) + builder.start('transport_descriptors') + builder.start('transport_descriptor') + builder.start('transport_id') + builder.data(transport_name) + builder.end('transport_id') + builder.start('type') + builder.data('UDPv4') + builder.end('type') + builder.end('transport_descriptor') + builder.end('transport_descriptors') + builder.start('participant', { + 'profile_name': args.profile_name, + 'is_default_profile': str(args.use_as_default).lower() + }) + builder.start('rtps') + if args.metatraffic_multicast_locators or \ + args.metatraffic_unicast_locators: + builder.start('builtin') + if args.metatraffic_multicast_locators: + builder.start('metatrafficMulticastLocatorList') + for locator in args.metatraffic_multicast_locators: + build_udpv4_locator(builder, locator) + builder.end('metatrafficMulticastLocatorList') + if args.metatraffic_unicast_locators: + builder.start('metatrafficUnicastLocatorList') + for locator in args.metatraffic_unicast_locators: + build_udpv4_locator(builder, locator) + builder.end('metatrafficUnicastLocatorList') + builder.end('builtin') + if args.unicast_locators: + builder.start('defaultUnicastLocatorList') + for locator in args.unicast_locators: + build_udpv4_locator(builder, locator) + builder.end('defaultUnicastLocatorList') + builder.start('userTransports') + builder.start('transport_id') + builder.data(transport_name) + builder.end('transport_id') + builder.end('userTransports') + builder.start('useBuiltinTransports') + builder.data('false') + builder.end('useBuiltinTransports') + builder.end('rtps') + builder.end('participant') + builder.end('profiles') + tree = builder.close() + inline_xml = ET.tostring(tree, encoding='utf-8') + dom = minidom.parseString(inline_xml) + pretty_xml = dom.toprettyxml(indent=' ' * 4, encoding='utf-8') + sys.stdout.buffer.write(pretty_xml) + + +if __name__ == '__main__': + main() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl new file mode 100644 index 000000000..09fd60ee0 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -0,0 +1,132 @@ + +MANIFEST = [ + "cmake_tools/server_mode.py", + "cmake_tools/__init__.py", + "resources/package_cc_binary_import.bzl.tpl", + "resources/package_py_library_with_cc_extensions.bzl.tpl", + "resources/package_cc_library.bzl.tpl", + "resources/BUILD.prologue.bazel", + "resources/rosidl_package.xml.in", + "resources/ament_cmake_CMakeLists.txt.in", + "resources/package_meta_py_library.bzl.tpl", + "resources/package_alias.bzl.tpl", + "resources/package_py_library.bzl.tpl", + "resources/package_share_filegroup.bzl.tpl", + "resources/prologue.bzl", + "resources/rosidl_cmake_CMakeLists.txt.in", + "ros2bzl/utilities.py", + "ros2bzl/sandboxing.py", + "ros2bzl/resources.py", + "ros2bzl/templates.py", + "ros2bzl/__init__.py", + "ros2bzl/scrapping/system.py", + "ros2bzl/scrapping/metadata.py", + "ros2bzl/scrapping/ament_python.py", + "ros2bzl/scrapping/ament_cmake.py", + "ros2bzl/scrapping/__init__.py", + "rosidl_generate_interfaces.py" +] + +def _execute_or_fail(repo_ctx, cmd, **kwargs): + exec_result = repo_ctx.execute(cmd, **kwargs) + if exec_result.return_code != 0: + error_message ="'{}' exited with {}".format( + " ".join([str(token) for token in cmd]), + exec_result.return_code + ) + if exec_result.stdout: + error_message += "\n--- captured stdout ---\n" + error_message += exec_result.stdout + if exec_result.stderr: + error_message += "\n--- captured stderr ---\n" + error_message += exec_result.stderr + fail("Failed to setup @{} repository: {}".format( + repo_ctx.name, error_message + )) + return exec_result + +def _label(relpath): + return Label("//tools/skylark/ros2:" + relpath) + +def _uuid(repo_ctx): + cmd = [repo_ctx.which("python3"), "-c", "import uuid; print(uuid.uuid1())"] + return _execute_or_fail(repo_ctx, cmd, quiet=True).stdout + +def _impl(repo_ctx): + for relpath in MANIFEST: + repo_ctx.symlink(_label(relpath), relpath) + + repo_ctx.template( + "setup.sh", _label("resources/setup.sh.in"), + substitutions = { + "@ID@": _uuid(repo_ctx), + "@REPOSITORY_DIR@": str(repo_ctx.path(".")), + "@WORKSPACES@": " ".join(repo_ctx.attr.workspaces), + }, + executable = True + ) + + generate_tool = repo_ctx.path(_label("generate_repository_files.py")) + cmd = ["./setup.sh", str(generate_tool)] + for ws in repo_ctx.attr.workspaces: + ws_in_sandbox = ws.replace("/", "_") + cmd.extend(["-s", ws + ":" + ws_in_sandbox]) + for pkg in repo_ctx.attr.include_packages: + cmd.extend(["-i", pkg]) + for pkg in repo_ctx.attr.exclude_packages: + cmd.extend(["-e", pkg]) + for target, data in repo_ctx.attr.extra_data.items(): + for label in data: + cmd.extend(["-x", target + ".data+=" + label]) + if repo_ctx.attr.jobs > 0: + cmd.extend(["-j", repr(repo_ctx.attr.jobs)]) + cmd.append(repo_ctx.name) + _execute_or_fail(repo_ctx, cmd, quiet=True) + +""" +Extracts relevant properties from CMake and Python stuff for ROS 2 install +trees (see https://www.ros.org/reps/rep-0122.html). Assumes `ament_cmake` +for C++ packages and generic Python packages. +""" + +# NOTE(hidmic): `ament_cmake` is not using modern CMake targets as of Dashing, +# and as of Foxy not all packages are bound to. Stick to non-standard dependency +# recollection. + +# TODO(eric): How to handle licenses? +ros2_local_repository = repository_rule( + attrs = dict( + # Workspaces + # - FHS install trees (incl. ABI compatible overlays). + workspaces = attr.string_list(mandatory = True), + # Explicit set of packages to include, with its + # recursive dependencies. Default to all. + include_packages = attr.string_list(), + # Set of packages to exclude. Default to none. + exclude_packages = attr.string_list(), + # Extra data dependencies for targets + extra_data = attr.string_list_dict(), + # Maximum CMake jobs to use during configuration + jobs = attr.int(default=0), + ), + local = False, + configure = True, + implementation = _impl, +) + +def package_share_filegroup(name, share_directories): + native.filegroup( + name = name, + srcs = [path for path in native.glob( + include = ["{}/**".format(dirpath) for dirpath in share_directories], + exclude = [ + "*/cmake/**", + "*/environment/**", + "*/*.sh", + "*/*.bash", + "*/*.dsv", + ] + ) if " " not in path], + # NOTE(hidmic): workaround lack of support for spaces. + # See https://github.com/bazelbuild/bazel/issues/4327. + ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/__init__.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/resources.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/resources.py new file mode 100644 index 000000000..b4f09d8e0 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/resources.py @@ -0,0 +1,60 @@ +import functools +import json +import os + +import ros2bzl.sandboxing as sandboxing + + +PATH_TO_RESOURCES = os.path.realpath( + os.path.join(os.path.dirname(__file__), '..', 'resources') +) + + +@functools.lru_cache(maxsize=None) +def path_to_resource(name): + return os.path.join(PATH_TO_RESOURCES, name) + + +@functools.lru_cache(maxsize=None) +def load_resource(name): + with open(path_to_resource(name), 'r') as fd: + return fd.read() + + +ROS2BZL_PREFIX_PATH = os.environ.get('ROS2BZL_PREFIX_PATH', '.') + + +class ExtendedJSONEncoder(json.JSONEncoder): + + def default(self, o): + try: + iterable = iter(o) + except TypeError: + pass + else: + return list(iterable) + return json.JSONEncoder.default(self, o) + + +def load_underlay(): + path = os.path.join(ROS2BZL_PREFIX_PATH, 'underlay.json') + if not os.path.exists(path): + raise ValueError( + 'No underlay to load: {} not found'.format(path) + ) + with open(path, 'r') as fd: + setup = json.load(fd) + return ( + setup['packages'], setup['dependency_graph'], + setup['cache'], sandboxing.configure(**setup['sandbox']), + ) + + +def setup_underlay(packages, dependency_graph, cache, sandbox): + with open('underlay.json', 'w') as fd: + json.dump({ + 'packages': packages, + 'dependency_graph': dependency_graph, + 'cache': cache, + 'sandbox': sandbox.kwargs, + }, fd, cls=ExtendedJSONEncoder, sort_keys=True, indent=4) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/sandboxing.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/sandboxing.py new file mode 100644 index 000000000..0468c02df --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/sandboxing.py @@ -0,0 +1,22 @@ +import os + +def configure(name, mapping): + def sandbox(path, external=False): + path = os.path.normpath(path) + for outer_path, inner_path in mapping.items(): + if path.startswith(outer_path): + path = os.path.normpath( + path.replace(outer_path, inner_path) + ) + if external: + path = os.path.join(name, path) + return path + return path + sandbox.kwargs = dict(name=name, mapping=mapping) + + for outer_path, inner_path in mapping.items(): + if inner_path == '.': + continue + os.symlink(outer_path, inner_path, target_is_directory=True) + + return sandbox diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py new file mode 100644 index 000000000..7192e61de --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py @@ -0,0 +1,41 @@ +from ament_index_python import get_packages_with_prefixes + +from ros2bzl.scrapping.metadata import collect_package_metadata + + +def index_all_packages(): + return { + name: collect_package_metadata(name, prefix) + for name, prefix in get_packages_with_prefixes().items() + } + + + +def build_dependency_graph(packages, include=None, exclude=None): + package_set = set(packages) + if include: + package_set &= include + if exclude: + package_set -= exclude + + dependency_graph = {} + while package_set: + name = package_set.pop() + metadata = packages[name] + dependencies = set(metadata['build_dependencies']) + dependencies.update(metadata['run_dependencies']) + if exclude: + dependencies -= exclude + # Ignore system, non-ROS dependencies + # NOTE(hidmic): shall we sandbox those too? + dependencies = { + dependency_name + for dependency_name in dependencies + if dependency_name in packages + } + dependency_graph[name] = dependencies + package_set.update(dependencies) + + packages = {name: packages[name] for name in dependency_graph} + + return packages, dependency_graph diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py new file mode 100644 index 000000000..1b344b007 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py @@ -0,0 +1,186 @@ +import os + +from tempfile import TemporaryDirectory + +import cmake_tools + +from ros2bzl.resources import path_to_resource + +from ros2bzl.scrapping.system import find_library_path +from ros2bzl.scrapping.system import find_library_dependencies +from ros2bzl.scrapping.system import is_system_include +from ros2bzl.scrapping.system import is_system_library + + +def collect_ament_cmake_shared_library_codemodel(codemodel): + assert codemodel['type'] == 'SHARED_LIBRARY' + + link_flags = [] + if 'linkFlags' in codemodel: + for flag in codemodel['linkFlags'].split(' '): + flag = flag.strip() + if not flag: + continue + link_flags.append(flag) + + link_libraries = [] + if 'linkLibraries' in codemodel: + libraries = [] + for item in codemodel['linkLibraries'].split(' '): + item = item.strip() + if item.startswith('-') and not item.startswith('-l'): + link_flags.append(item) + continue + libraries.append(item) + for library in libraries: + if not os.path.isabs(library): + if library.startswith('-l'): + library = library[2:] + library = find_library_path( + library, + # Use linker options as well + link_flags=link_flags + ) + if not library: + # Ignore and keep going + continue + # Some packages do not fully export runtime dependencies. + # NOTE(hidmic): can the CMake registry be used instead + # of the ament index to mitigate this? + library_plus_dependencies = [library] + library_plus_dependencies += list( + find_library_dependencies(library) + ) + # Remove duplicates maintaining order. + link_libraries.extend([ + library for library in library_plus_dependencies + if library not in link_libraries and not is_system_library(library) + ]) + + file_groups = codemodel['fileGroups'] + assert len(file_groups) == 1 + file_group = file_groups[0] + + include_directories = [] + if 'includePath' in file_group: + include_directories.extend([ + entry['path'] for entry in file_group['includePath'] + if not is_system_include(entry['path']) + ]) + + defines = [] + if 'defines' in file_group: + ignored_defines = [ + codemodel['name'] + '_EXPORTS' # modern CMake specific + ] + for define in file_group['defines']: + if define in ignored_defines: + continue + defines.append(define) + + compile_flags = [] + if 'compileFlags' in file_group: + ignored_compile_flags = [ + '-fPIC' # applies to shared libraries only + ] + for flag in file_group['compileFlags'].split(' '): + flag = flag.strip() + if not flag or flag in ignored_compile_flags: + continue + compile_flags.append(flag) + + return { + 'include_directories': include_directories, + 'compile_flags': compile_flags, + 'defines': defines, + 'link_directories': [], # no directories in codemodel? + 'link_libraries': link_libraries, + 'link_flags': link_flags + } + + +def collect_ament_cmake_package_properties(name, metadata): + # NOTE(hidmic): each package properties are analyzed in isolation + # to preclude potential interactions if multiple packages were + # brought into the same CMake run. The latter could be done for + # speed + with TemporaryDirectory(dir=os.getcwd()) as project_path: + project_name = 'empty_using_' + name + cmakelists_template_path = path_to_resource('ament_cmake_CMakeLists.txt.in') + cmakelists_path = os.path.join(project_path, 'CMakeLists.txt') + cmake_tools.configure_file(cmakelists_template_path, cmakelists_path, { + '@NAME@': project_name, '@PACKAGE@': name + }) + + cmake_prefix_path = os.path.realpath(metadata['prefix']) + if 'AMENT_PREFIX_PATH' in os.environ: + ament_prefix_path = os.environ['AMENT_PREFIX_PATH'] + cmake_prefix_path += ';' + ament_prefix_path.replace(':', ';') + + with cmake_tools.server_mode(project_path) as cmake: + cmake.configure(attributes={'cacheArguments': [ + '-DCMAKE_PREFIX_PATH=' + cmake_prefix_path + ]}, timeout=20, message_callback=print) + cmake.compute(timeout=20, message_callback=print) + codemodel = cmake.codemodel(timeout=5) + + configurations = codemodel['configurations'] + assert len(configurations) == 1 + configuration = configurations[0] + + projects = configuration['projects'] + assert len(projects) == 1 + project = projects[0] + + targets = {t['name']: t for t in project['targets']} + assert project_name in targets + target = targets[project_name] + + return collect_ament_cmake_shared_library_codemodel(target) + + +def collect_ament_cmake_package_direct_properties(name, metadata, dependencies, cache): + if 'ament_cmake' not in cache: + cache['ament_cmake'] = {} + ament_cmake_cache = cache['ament_cmake'] + + if name not in ament_cmake_cache: + ament_cmake_cache[name] = \ + collect_ament_cmake_package_properties(name, metadata) + + properties = dict(ament_cmake_cache[name]) + for dependency_name, dependency_metadata in dependencies.items(): + if dependency_metadata['build_type'] != 'ament_cmake': + continue + if dependency_name not in ament_cmake_cache: + ament_cmake_cache[dependency_name] = \ + collect_ament_cmake_package_properties( + dependency_name, dependency_metadata + ) + dependency_properties = ament_cmake_cache[dependency_name] + + # Remove duplicates maintaining order. + properties['compile_flags'] = [ + flags for flags in properties['compile_flags'] + if flags not in dependency_properties['compile_flags'] + ] + properties['defines'] = [ + flags for flags in properties['defines'] + if flags not in dependency_properties['defines'] + ] + properties['link_flags'] = [ + flags for flags in properties['link_flags'] + if flags not in dependency_properties['link_flags'] + ] + properties['link_libraries'] = [ + library for library in properties['link_libraries'] + if library not in dependency_properties['link_libraries'] + ] + properties['include_directories'] = [ + directory for directory in properties['include_directories'] + if directory not in dependency_properties['include_directories'] + or os.path.exists(os.path.join(directory, name)) # leverage REP-122 + ] + # Do not deduplicate link directories in case we're dealing with merge installs. + + return properties diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py new file mode 100644 index 000000000..63a2927e6 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py @@ -0,0 +1,70 @@ +import glob + +from ros2bzl.scrapping.system import find_library_dependencies +from ros2bzl.scrapping.system import find_python_package +from ros2bzl.scrapping.system import is_system_library + + +def collect_ament_python_package_properties(name, metadata): + python_package_path = find_python_package( + name, sysroot=metadata['prefix'] + ) + properties = {'python_packages': [python_package_path]} + cc_extensions = glob.glob( + '{}/**/*.so'.format(python_package_path), recursive=True + ) + if cc_extensions: + cc_extensions_deps = set() + for ext in cc_extensions: + cc_extensions_deps.update([ + library + for library in find_library_dependencies(ext) + if not is_system_library(library) + ]) + properties['cc_extensions'] = cc_extensions + properties['cc_extensions'] += list(cc_extensions_deps) + return properties + + +def collect_ament_python_package_direct_properties(name, metadata, dependencies, cache): + if 'ament_python' not in cache: + cache['ament_python'] = {} + ament_python_cache = cache['ament_python'] + + if name not in ament_python_cache: + ament_python_cache[name] = \ + collect_ament_python_package_properties(name, metadata) + + properties = dict(ament_python_cache[name]) + + if 'cc_extensions' in properties: + ament_cmake_cache = cache['ament_cmake'] + for dependency_name, dependency_metadata in dependencies.items(): + dependency_libraries = [] + if 'cc' in dependency_metadata['langs']: + if dependency_metadata['build_type'] == 'ament_cmake': + if dependency_name not in ament_cmake_cache: + ament_cmake_cache[dependency_name] = \ + collect_ament_cmake_package_properties( + dependency_name, dependency_metadata + ) + dependency_properties = ament_cmake_cache[dependency_name] + dependency_libraries.extend( + dependency_properties['link_libraries'] + ) + if 'py' in dependency_metadata['langs']: + if dependency_name not in ament_python_cache: + ament_python_cache[dependency_name] = \ + collect_ament_python_package_properties( + dependency_name, dependency_metadata + ) + dependency_properties = ament_python_cache[dependency_name] + if 'cc_extensions' in dependency_properties: + dependency_libraries.extend( + dependency_properties['cc_extensions']) + # Remove duplicates maintaining order + properties['cc_extensions'] = [ + ext for ext in properties['cc_extensions'] + if ext not in dependency_libraries + ] + return properties diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/metadata.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/metadata.py new file mode 100644 index 000000000..3d65ed7ae --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/metadata.py @@ -0,0 +1,94 @@ +import os +import xml.etree.ElementTree as ET + + +def parse_package_xml(path_to_package_xml): + tree = ET.parse(path_to_package_xml) + + depends = set([ + tag.text for tag in tree.findall('./depend') + ]) + exec_depends = set([ + tag.text for tag in tree.findall('./exec_depend') + ]) + build_export_depends = set([ + tag.text for tag in tree.findall('./build_export_depend') + ]) + group_depends = set([ + tag.text for tag in tree.findall('./group_depend') + ]) + member_of_groups = set([ + tag.text for tag in tree.findall('./member_of_group') + ]) + build_type = tree.find('./export/build_type').text + + return dict( + build_dependencies=build_export_depends | depends, + run_dependencies=exec_depends | depends, + group_dependencies=group_depends, + groups=member_of_groups, + build_type=build_type + ) + + +def parse_plugins_description_xml(path_to_plugins_description_xml): + plugins_description_xml = ET.parse(path_to_plugins_description_xml) + root = plugins_description_xml.getroot() + assert root.tag == 'library' + return dict(plugin_libraries=[root.attrib['path']]) + + +def find_executables(base_path): + for dirpath, dirnames, filenames in os.walk(base_path): + # ignore folder starting with . + dirnames[:] = [d for d in dirnames if d[0] not in ['.']] + dirnames.sort() + # select executable files + for filename in sorted(filenames): + path = os.path.join(dirpath, filename) + if os.access(path, os.X_OK): + yield path + + +DEFAULT_LANGS_PER_BUILD_TYPE = { + 'cmake': {'cc'}, + 'ament_cmake': {'cc'}, + 'ament_python': {'py'}, +} + + +def collect_package_langs(metadata): + langs = set() + build_type = metadata['build_type'] + if build_type in DEFAULT_LANGS_PER_BUILD_TYPE: + langs.update(DEFAULT_LANGS_PER_BUILD_TYPE[build_type]) + return langs + + +def collect_package_metadata(name, prefix): + share_directory = os.path.join(prefix, 'share', name) + ament_index_directory = os.path.join(prefix, 'share', 'ament_index') + + metadata = dict( + prefix=prefix, + share_directory=share_directory, + ament_index_directory=ament_index_directory, + ) + + lib_directory = os.path.join(prefix, 'lib', name) + metadata['executables'] = list(find_executables(lib_directory)) + + path_to_package_xml = os.path.join(share_directory, 'package.xml') + metadata.update(parse_package_xml(path_to_package_xml)) + + metadata['langs'] = collect_package_langs(metadata) + + path_to_plugins_description_xml = os.path.join( + share_directory, 'plugins_description.xml' + ) + if os.path.exists(path_to_plugins_description_xml): + metadata.update(parse_plugins_description_xml( + path_to_plugins_description_xml + )) + + return metadata diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/system.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/system.py new file mode 100644 index 000000000..cafe36fb5 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/system.py @@ -0,0 +1,84 @@ +import os +import re +import subprocess +import sys + +DEFAULT_INCLUDE_DIRECTORIES = ['/usr/include', '/usr/local/include'] + +def is_system_include(include_path): + include_path = os.path.realpath(include_path) + return any(include_path.startswith(path) for path in DEFAULT_INCLUDE_DIRECTORIES) + + +DEFAULT_LINK_DIRECTORIES = ['/lib', '/usr/lib', '/usr/local/lib'] + + +def is_system_library(library_path): + library_path = os.path.realpath(library_path) + return any(library_path.startswith(path) for path in DEFAULT_LINK_DIRECTORIES) + + +LD_LIBRARY_PATHS = [] +if 'LD_LIBRARY_PATH' in os.environ: + value = os.environ['LD_LIBRARY_PATH'] + LD_LIBRARY_PATHS = [path for path in value.split(':') if path] + + +def find_library_path(library_name, link_directories=[], link_flags=[]): + # Adapted from ctypes.util.find_library_path() implementation. + pattern = r'/(?:[^\(\)\s]*/)*lib{}\.[^\(\)\s]*'.format(library_name) + + cmd = ['ld', '-t'] + for path in link_directories: + cmd.extend(['-L', path]) + cmd.extend(link_flags) + for path in LD_LIBRARY_PATHS: + cmd.extend(['-L', path]) + for path in DEFAULT_LINK_DIRECTORIES: + cmd.extend(['-L', path]) + cmd.extend(['-o', os.devnull, '-l' + library_name]) + try: + out = subprocess.run( + cmd, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + encoding='utf8' + ).stdout.strip() + match = re.search(pattern, out) + if match: + path = match.group(0) + # Remove double forward slashes, if any + return os.path.join('/', os.path.relpath(path, '/')) + except Exception as e: + pass + return None + + +LDD_LINE_PATTERN = re.compile(r' => (/(?:[^\(\)\s]*/)*lib[^\(\)\s]*)') + + +def find_library_dependencies(library_path): + try: + lines = subprocess.run( + ['ldd', library_path], + check=True, + stdout=subprocess.PIPE, + encoding='utf8' + ).stdout.strip().split('\n') + for line in lines: + match = LDD_LINE_PATTERN.search(line.strip()) + if match: + yield match.group(1) + except Exception: + return + + +def find_python_package(name, sysroot='/usr'): + v = sys.version_info + path = '{}/lib/python{}.{}/site-packages/{}'.format( + sysroot, v.major, v.minor, name + ) + if not os.path.exists(os.path.join(path, '__init__.py')): + raise ValueError(name + ' is not a Python package') + return path diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py new file mode 100644 index 000000000..858d9db0d --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py @@ -0,0 +1,220 @@ +import os + +from ros2bzl.resources import load_resource +from ros2bzl.scrapping.system import find_library_path + +def load_paths_for_libraries(libraries, sandbox): + if not libraries: + return [] + libraries_directories = sorted(set( + sandbox(os.path.dirname(lib), external=True) for lib in libraries + )) + load_paths = [libraries_directories[0]] + for directory in libraries_directories[1:]: + if directory.startswith(load_paths[-1]): + continue + load_paths.append(directory) + return load_paths + + +def label_name(name, metadata): + return metadata.get('bazel_name', name) + + +def label(name, metadata): + workspace = metadata.get('bazel_workspace', '') + if workspace: + workspace = '@' + workspace + package = metadata.get('bazel_package', '') + if workspace or package: + package = '//' + package + return workspace + package + ':' + label_name(name, metadata) + + +def labels_with(suffix): + return ( + (lambda *args, **kwargs: label_name(*args, **kwargs) + suffix), + (lambda *args, **kwargs: label(*args, **kwargs) + suffix) + ) + + +share_name, share_label = labels_with(suffix='_share') +cc_name, cc_label = labels_with(suffix='_cc') +py_name, py_label = labels_with(suffix='_py') +meta_py_name, meta_py_label = labels_with(suffix='_transitively_py') + + +def configure_package_share_filegroup(name, metadata, sandbox): + return load_resource('package_share_filegroup.bzl.tpl'), { + 'name': share_name(name, metadata), + 'share_directories': [ + sandbox(metadata['share_directory']), + sandbox(metadata['ament_index_directory']), + ] + } + + +def configure_package_executable_imports(name, metadata, dependencies, extras, sandbox): + if not metadata['executables']: + return [] + + common_data = [] + dependencies = list(dependencies.items()) + dependencies = [(name, metadata)] + dependencies + for dependency_name, dependency_metadata in dependencies: + # TODO(hidmic): use appropriate target based on executable file type + if 'cc' in dependency_metadata['langs']: + common_data.append(cc_label(dependency_name, dependency_metadata)) + if 'py' in dependency_metadata['langs']: + common_data.append(py_label(dependency_name, dependency_metadata)) + elif 'py (transitively)' in dependency_metadata['langs']: + common_data.append(meta_py_label(dependency_name, dependency_metadata)) + + target_prefix = label_name(name, metadata) + for executable in metadata['executables']: + target_name = '{}_{}'.format(target_prefix, os.path.basename(executable)) + data = common_data + if extras and 'data' in extras and target_name in extras['data']: + data = data + extras['data'][target_name] + yield load_resource('package_cc_binary_import.bzl.tpl'), { + 'name': target_name, 'executable': sandbox(executable), 'data': data + } + + +def configure_package_cc_library(name, metadata, properties, dependencies, extras, sandbox): + target_name = cc_name(name, metadata) + libraries = [sandbox(library) for library in properties['link_libraries']] + include_directories = [sandbox(include) for include in properties['include_directories']] + # Assume package abides to REP-122 FHS layout. + includes = [os.path.join(include, name) for include in include_directories] + linkopts = properties['link_flags'] + for link_directory in properties['link_directories']: + link_directory = sandbox(link_directory) + if not link_directory: + continue + linkopts += [ + '-L' + link_directory, + '-Wl,-rpath ' + link_directory + ] + copts = properties['compile_flags'] + defines = properties['defines'] + deps = [ + cc_label(dependency_name, dependency_metadata) + for dependency_name, dependency_metadata in dependencies.items() + if 'cc' in dependency_metadata['langs'] + ] + + runenv = {'AMENT_PREFIX_PATH': [ + 'path-prepend', sandbox(metadata['prefix'], external=True) + ]} + if 'rmw_implementation_packages' in metadata['groups']: + runenv['RMW_IMPLEMENTATION'] = ['replace', name] + + data = [] + data.append(share_label(name, metadata)) + # Add in plugins, if any + if 'plugin_libraries' in metadata: + data.extend( + sandbox(find_library_path(library)) + for library in metadata['plugin_libraries'] + ) + # Prepare runfiles and load paths to support dynamic loading + load_paths = load_paths_for_libraries( + properties['link_libraries'], sandbox + ) + if load_paths: + runenv['${LOAD_PATH}'] = ['path-prepend', *load_paths] + data.extend(libraries) + if extras and 'data' in extras: + data.extend(extras['data'].get(target_name, [])) + + return load_resource('package_cc_library.bzl.tpl'), { + 'name': target_name, + 'srcs': libraries, + 'includes': includes, + 'include_directories': include_directories, + 'copts': copts, + 'defines': defines, + 'linkopts': linkopts, + 'runenv': runenv, + 'data': data, + 'deps': deps, + } + + +def configure_package_meta_py_library(name, metadata, dependencies): + deps = [] + for dependency_name, dependency_metadata in dependencies.items(): + if 'py' in dependency_metadata['langs']: + deps.append(py_label(dependency_name, dependency_metadata)) + elif 'py (transitively)' in dependency_metadata['langs']: + deps.append(meta_py_label(dependency_name, dependency_metadata)) + return load_resource('package_meta_py_library.bzl.tpl'), { + 'name': meta_py_name(name, metadata), 'deps': deps + } + + +def configure_package_py_library(name, metadata, properties, dependencies, extras, sandbox): + target_name = py_name(name, metadata) + packages = [sandbox(pkg) for pkg in properties['python_packages']] + imports = [os.path.dirname(pkg) for pkg in packages] + + deps = [] + for dependency_name, dependency_metadata in dependencies.items(): + if 'py' in dependency_metadata['langs']: + deps.append(py_label(dependency_name, dependency_metadata)) + elif 'py (transitively)' in dependency_metadata['langs']: + deps.append(meta_py_label(dependency_name, dependency_metadata)) + + config = { + 'name': target_name, + 'packages': packages, + 'imports': imports, + 'deps': deps + } + + data = [share_label(name, metadata)] + if 'cc' in metadata['langs']: + data.append(cc_label(name, metadata)) + + runenv = {'AMENT_PREFIX_PATH': [ + 'path-prepend', sandbox(metadata['prefix'], external=True) + ]} + + if 'rmw_implementation_packages' in metadata['groups']: + runenv['RMW_IMPLEMENTATION'] = ['replace', name] + + if 'cc_extensions' in properties: + cc_deps = [ + cc_label(dependency_name, dependency_metadata) + for dependency_name, dependency_metadata in dependencies.items() + if 'cc' in dependency_metadata['langs'] + ] + cc_extensions = [sandbox(ext) for ext in properties['cc_extensions']] + # Prepare runfiles and load paths to support dynamic loading + load_paths = load_paths_for_libraries(properties['cc_extensions'], sandbox) + if load_paths: + runenv['${LOAD_PATH}'] = ['path-prepend', *load_paths] + data.extend(cc_extensions) + data.extend(cc_deps) + # Add in plugins, if any + if 'plugin_libraries' in metadata: + data.extend( + sandbox(find_library_path(library)) + for library in metadata['plugin_libraries'] + ) + template = load_resource('package_py_library_with_cc_extensions.bzl.tpl') + else: + template = load_resource('package_py_library.bzl.tpl') + + if extras and 'data' in extras: + data.extend(extras['data'].get(target_name, [])) + config.update({'data': data, 'runenv': runenv}) + + return template, config + + +def configure_package_alias(name, target): + return load_resource('package_alias.bzl.tpl'), { + 'name': name, 'actual': ':' + target + } diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/utilities.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/utilities.py new file mode 100644 index 000000000..4df56340a --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/utilities.py @@ -0,0 +1,14 @@ +import functools + +def to_starlark_string_dict(d): + # Replace single-quotes with double-quotes + return {k: repr(v).replace("'", '"') for k, v in d.items()} + +def interpolate(template, config): + return template.format(**to_starlark_string_dict(config)) + +def compose(f, g): + @functools.wraps(f) + def wrapped(head, *args, **kwargs): + return f(g(head, *args, **kwargs), *args, **kwargs) + return wrapped diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/rosidl_generate_interfaces.py b/drake_ros_bazel_installed/tools/skylark/ros2/rosidl_generate_interfaces.py new file mode 100755 index 000000000..655c45075 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/rosidl_generate_interfaces.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python3 + +import argparse +import glob +import os +import shutil +import sys + +from tempfile import TemporaryDirectory +from textwrap import indent +import xml.etree.ElementTree as ET + +import cmake_tools + +from ros2bzl.scrapping.metadata import collect_package_metadata +from ros2bzl.scrapping.ament_cmake import collect_ament_cmake_package_direct_properties +from ros2bzl.scrapping.ament_python import collect_ament_python_package_direct_properties +from ros2bzl.templates import configure_package_share_filegroup +from ros2bzl.templates import configure_package_cc_library +from ros2bzl.templates import configure_package_py_library + +from ros2bzl.resources import load_resource +from ros2bzl.resources import load_underlay +from ros2bzl.resources import path_to_resource + +import ros2bzl.sandboxing as sandboxing + +from ros2bzl.utilities import compose +from ros2bzl.utilities import interpolate + + +def generate_rosidl_interfaces(args): + with TemporaryDirectory(dir=args.gen_directory) as tmpdir: + local_interfaces = [] + for path in args.interfaces: + symlink = os.path.join( + tmpdir, + os.path.basename( + os.path.dirname(path) + ), + os.path.basename(path) + ) + os.makedirs(os.path.dirname(symlink), exist_ok=True) + os.symlink(path, symlink) + local_interfaces.append( + os.path.relpath(symlink, start=tmpdir) + ) + + cmakelists_template_path = path_to_resource('rosidl_cmake_CMakeLists.txt.in') + cmakelists_path = os.path.join(tmpdir, 'CMakeLists.txt') + cmake_tools.configure_file(cmakelists_template_path, cmakelists_path, { + '@NAME@': args.package_name, + '@INTERFACES@': ' '.join(local_interfaces), + '@DEPENDENCIES@': ' '.join(args.dependencies), + }) + + package_xml_template_path = path_to_resource('rosidl_package.xml.in') + package_xml_path = os.path.join(tmpdir, 'package.xml') + cmake_tools.configure_file(package_xml_template_path, package_xml_path, { + '@NAME@': args.package_name, '@DEPENDENCIES@': '\n'.join([ + '' + dep + '' for dep in args.dependencies + ]), + }) + + install_path = os.path.join(args.gen_directory, args.target_prefix) + if os.path.exists(install_path): + shutil.rmtree(install_path) + os.makedirs(install_path) + + ament_prefix_path = os.environ.get('AMENT_PREFIX_PATH', '') + cmake_prefix_path = ament_prefix_path.replace(':', ';') + cmake_tools.build_then_install( + tmpdir, + '-DCMAKE_PREFIX_PATH=' + cmake_prefix_path, + '-DCMAKE_INSTALL_PREFIX=' + install_path, + ) + + return install_path + + +def generate_rosidl_file(args): + packages, _, cache, sandbox = load_underlay() + + metadata = collect_package_metadata( + args.package_name, prefix=generate_rosidl_interfaces(args) + ) + + noextras = None + + sandbox = compose(args.sandbox, sandbox) + + dependencies = { + 'rosidl_default_generators', + 'rosidl_default_runtime' + } + dependencies.update(metadata['build_dependencies']) + dependencies.update(metadata['run_dependencies']) + + dependencies = {name: packages[name] for name in dependencies} + + args.output_file.write(load_resource('prologue.bzl') + '\n') + + metadata['bazel_name'] = args.target_prefix # Use target prefix + args.output_file.write('def {}():\n'.format(args.target_prefix)) + + # Generate share files' group + template, config = configure_package_share_filegroup( + args.package_name, metadata, args.sandbox + ) + args.output_file.write( + indent(interpolate(template, config), ' ' * 4) + '\n' + ) + + # Generate C/C++ library + properties = collect_ament_cmake_package_direct_properties( + args.package_name, metadata, dependencies, cache + ) + template, config = configure_package_cc_library( + args.package_name, metadata, properties, + dependencies, noextras, sandbox + ) + args.output_file.write( + indent(interpolate(template, config), ' ' * 4) + '\n' + ) + + # Generate Python package + properties = collect_ament_python_package_direct_properties( + args.package_name, metadata, dependencies, cache + ) + template, config = configure_package_py_library( + args.package_name, metadata, properties, + dependencies, noextras, sandbox + ) + args.output_file.write(indent(interpolate(template, config), ' ' * 4)) + + +def name_from_package_xml(): + if not os.path.exists('package.xml'): + return None + tree = ET.parse('package.xml') + return tree.find('./name').text + + +def parse_arguments(): + parser = argparse.ArgumentParser() + parser.add_argument( + '-s', '--sandbox', action='append', default=[], + help='Path mappings for sandboxing, in "outer:inner" format' + ) + parser.add_argument( + '-o', '--output-file', type=argparse.FileType('w'), + default=sys.stdout, help='Output .bzl file (defaults to stdout)' + ) + parser.add_argument( + '--gen-directory', default='gen', help=( + 'Directory for generated files (relative to output file, if one is given)' + ) + ) + parser.add_argument( + '-d', '--dep', dest='dependencies', action='append', default=[], + help='Packages from which interface types are used' + ) + parser.add_argument( + '-r', '--repository-name', + default=os.environ.get('BAZEL_REPOSITORY_NAME', None), + help='Bazel repository name' + ) + parser.add_argument( + '-p', '--package-name', default=name_from_package_xml(), + help='Interfaces\' package name (defaults to package.xml content if any)' + ) + parser.add_argument( + '-t', '--target-prefix', default=None, + help='Interface\' targets prefix (defaults to package name)' + ) + parser.add_argument( + 'interfaces', nargs='+', + help='Paths to interface files (may be glob patterns)' + ) + args = parser.parse_args() + + if not args.repository_name: + parser.error( + 'No repository name provided nor $BAZEL_REPOSITORY_NAME envvar found' + ) + + if not args.package_name: + parser.error( + 'No package name provided nor package.xml found' + ) + + if args.output_file is not sys.stdout: + output_dirpath = os.path.dirname( + os.path.realpath(args.output_file.name) + ) + else: + output_dirpath = os.getcwd() + os.makedirs(output_dirpath, exist_ok=True) + + args.gen_directory = os.path.realpath(args.gen_directory) + if not args.gen_directory.startswith(output_dirpath): + parser.error( + 'Generated files must be in subdirectories of the output file ' + 'directory. All paths in a Bazel target must be relative.' + ) + os.makedirs(args.gen_directory, exist_ok=True) + + args.sandbox.append(output_dirpath + ':.') + args.sandbox = sandboxing.configure( + name=args.repository_name, mapping=dict( + entry.partition(':')[0::2] for entry in args.sandbox + ) + ) + + if not args.target_prefix: + args.target_prefix = args.package_name + + args.interfaces = [ + os.path.realpath(path) + for pattern in args.interfaces + for path in glob.glob(pattern) + ] + + return args + + +def main(): + generate_rosidl_file(parse_arguments()) + + +if __name__ == '__main__': + main() diff --git a/drake_ros_bazel_installed/tools/workspace/BUILD.bazel b/drake_ros_bazel_installed/tools/workspace/BUILD.bazel new file mode 100644 index 000000000..e69de29bb diff --git a/drake_ros_bazel_installed/tools/workspace/ros2/BUILD.bazel b/drake_ros_bazel_installed/tools/workspace/ros2/BUILD.bazel new file mode 100644 index 000000000..e69de29bb diff --git a/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl b/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl new file mode 100644 index 000000000..c97781d2b --- /dev/null +++ b/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl @@ -0,0 +1,22 @@ +load("//tools/skylark/ros2:ros2.bzl", "ros2_local_repository") + +ROS2_DIST = "rolling" + +def ros2_repository(name, overlays = []): + ros2_local_repository( + name = name, + workspaces = ["/opt/ros/{}".format(ROS2_DIST)] + overlays, + include_packages = [ + "std_msgs", + "geometry_msgs", + "visualization_msgs", + "rosidl_default_generators", + "rcpputils", + "rcutils", + "rclcpp", + "rclpy", + "rviz2", + # RMW implementation + "rmw_fastrtps_cpp", + ] + ) From 7867e025bc5c1f61e2db20f2848dcb15b76f40e2 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 12 May 2021 15:25:22 -0300 Subject: [PATCH 002/107] Use native rules in drake_ros_* macros properly Signed-off-by: Michel Hidalgo --- .../tools/skylark/drake_ros_cc.bzl | 14 +++++++------- .../tools/skylark/drake_ros_py.bzl | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/drake_ros_cc.bzl b/drake_ros_bazel_installed/tools/skylark/drake_ros_cc.bzl index 0ff4ee3f9..847f36cbf 100644 --- a/drake_ros_bazel_installed/tools/skylark/drake_ros_cc.bzl +++ b/drake_ros_bazel_installed/tools/skylark/drake_ros_cc.bzl @@ -14,7 +14,7 @@ def drake_ros_cc_library(name, deps = [], data = [], runenv = {}, Equivalent to the cc_library() rule. """ library = "_" + name - cc_library( + native.cc_library( name = library, deps = deps, data = data, @@ -52,7 +52,7 @@ def drake_ros_cc_binary_import(name, executable, data = [], deps = [], tags = [] testonly = testonly, visibility = visibility, ) - cc_binary( + native.cc_binary( name = name, srcs = [shim], data = [executable] + data, @@ -75,7 +75,7 @@ def drake_ros_cc_binary(name, srcs = [], data = [], deps = [], linkshared = None Propagation of runtime information is disabled if linkshared is True. """ if linkshared: - cc_binary( + native.cc_binary( name = name, srcs = srcs, data = data, @@ -88,7 +88,7 @@ def drake_ros_cc_binary(name, srcs = [], data = [], deps = [], linkshared = None ) return binary = "_" + name - cc_binary( + native.cc_binary( name = binary, srcs = srcs, data = data, @@ -109,7 +109,7 @@ def drake_ros_cc_binary(name, srcs = [], data = [], deps = [], linkshared = None testonly = testonly, visibility = visibility, ) - cc_binary( + native.cc_binary( name = name, srcs = [shim], data = [":" + binary], @@ -136,7 +136,7 @@ def drake_ros_cc_test(name, srcs = [], data = [], deps = [], test = "_" + name - cc_test( + native.cc_test( name = test, srcs = srcs, data = data, @@ -155,7 +155,7 @@ def drake_ros_cc_test(name, srcs = [], data = [], deps = [], testonly = testonly, visibility = visibility, ) - cc_test( + native.cc_test( name = name, srcs = [shim], data = [":" + test], diff --git a/drake_ros_bazel_installed/tools/skylark/drake_ros_py.bzl b/drake_ros_bazel_installed/tools/skylark/drake_ros_py.bzl index f4c6b7052..76a835976 100644 --- a/drake_ros_bazel_installed/tools/skylark/drake_ros_py.bzl +++ b/drake_ros_bazel_installed/tools/skylark/drake_ros_py.bzl @@ -17,7 +17,7 @@ def drake_ros_py_library(name, srcs = [], main = None, imports = [], Equivalent to the py_library() rule. """ library_name = "_" + name - py_library( + native.py_library( name = library_name, srcs = srcs, main = main, @@ -50,7 +50,7 @@ def drake_ros_py_binary(name, srcs = [], main = None, imports = [], args = [], binary = "_" + name if not main: main = name + ".py" - py_binary( + native.py_binary( name = binary, srcs = srcs, main = main, @@ -72,7 +72,7 @@ def drake_ros_py_binary(name, srcs = [], main = None, imports = [], args = [], testonly = testonly, visibility = visibility, ) - py_binary( + native.py_binary( name = name, srcs = [shim], main = shim, @@ -107,7 +107,7 @@ def drake_ros_py_test(name, srcs = [], main = None, imports = [], test = "_" + name - py_test( + native.py_test( name = test, srcs = srcs, main = main, @@ -130,7 +130,7 @@ def drake_ros_py_test(name, srcs = [], main = None, imports = [], testonly = testonly, visibility = visibility, ) - py_test( + native.py_test( name = name, srcs = [shim], data = [":" + test], From 35f3d89aec6b5da89a46ef265425c9b8cd36839a Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 12 May 2021 15:31:32 -0300 Subject: [PATCH 003/107] Target @drake_ros repo in ros2 Starlark templates. Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/resources/BUILD.prologue.bazel | 8 ++++---- .../tools/skylark/ros2/resources/prologue.bzl | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/BUILD.prologue.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/resources/BUILD.prologue.bazel index 28e66d43a..f9874c56d 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/BUILD.prologue.bazel +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/BUILD.prologue.bazel @@ -2,7 +2,7 @@ package(default_visibility = ["//visibility:public"]) -load("//tools/skylark:drake_ros_cc.bzl", "drake_ros_cc_binary_import") -load("//tools/skylark:drake_ros_cc.bzl", "drake_ros_cc_library") -load("//tools/skylark:drake_ros_py.bzl", "drake_ros_py_library") -load("//tools/skylark/ros2:ros2.bzl", "package_share_filegroup") +load("@drake_ros//tools/skylark:drake_ros_cc.bzl", "drake_ros_cc_binary_import") +load("@drake_ros//tools/skylark:drake_ros_cc.bzl", "drake_ros_cc_library") +load("@drake_ros//tools/skylark:drake_ros_py.bzl", "drake_ros_py_library") +load("@drake_ros//tools/skylark/ros2:ros2.bzl", "package_share_filegroup") diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/prologue.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/prologue.bzl index 147b73aa7..cb13db2c2 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/prologue.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/prologue.bzl @@ -1,8 +1,8 @@ # -*- python -*- -load("//tools/skylark:anzu_ros_cc.bzl", "drake_ros_cc_binary_import") -load("//tools/skylark:anzu_ros_cc.bzl", "drake_ros_cc_library") -load("//tools/skylark:anzu_ros_py.bzl", "drake_ros_py_library") -load("//tools/skylark/ros2:ros2.bzl", "package_share_filegroup") +load("@drake_ros//tools/skylark:drake_ros_cc.bzl", "drake_ros_cc_binary_import") +load("@drake_ros//tools/skylark:drake_ros_cc.bzl", "drake_ros_cc_library") +load("@drake_ros//tools/skylark:drake_ros_py.bzl", "drake_ros_py_library") +load("@drake_ros//tools/skylark/ros2:ros2.bzl", "package_share_filegroup") glob = native.glob From e0a011f6ea48e380ecea5f9036ae45b1ee6905fb Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 12 May 2021 15:33:32 -0300 Subject: [PATCH 004/107] Drop rviz_default_plugins workaround. Signed-off-by: Michel Hidalgo --- .../skylark/ros2/resources/ament_cmake_CMakeLists.txt.in | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ament_cmake_CMakeLists.txt.in b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ament_cmake_CMakeLists.txt.in index 28a6e0dda..f5d4964ba 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ament_cmake_CMakeLists.txt.in +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ament_cmake_CMakeLists.txt.in @@ -3,11 +3,6 @@ cmake_minimum_required(VERSION 3.5) project(@NAME@ C CXX) find_package(ament_cmake REQUIRED) -# TODO(hidmic): remove when rviz_default_plugins -# export dependency on Qt5 component correctly -if("@PACKAGE@" STREQUAL "rviz_default_plugins") - find_package(Qt5 COMPONENTS Widgets REQUIRED) -endif() find_package(@PACKAGE@ REQUIRED) file(WRITE empty.cc "") From 1bc0567a186f4974bd8748dc7ec57e679f2a008c Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 12 May 2021 15:35:53 -0300 Subject: [PATCH 005/107] Use importlib.metadata to find_python_package() Signed-off-by: Michel Hidalgo --- .../skylark/ros2/generate_repository_files.py | 3 ++- .../ros2/ros2bzl/scrapping/ament_python.py | 15 +++++++++++---- .../skylark/ros2/ros2bzl/scrapping/system.py | 10 ---------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py index b5f800400..9eb025042 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -17,6 +17,7 @@ from ros2bzl.scrapping.ament_cmake import collect_ament_cmake_package_properties from ros2bzl.scrapping.ament_cmake import collect_ament_cmake_package_direct_properties from ros2bzl.scrapping.ament_python import collect_ament_python_package_direct_properties +from ros2bzl.scrapping.ament_python import PackageNotFoundError from ros2bzl.templates import configure_package_meta_py_library from ros2bzl.templates import configure_package_alias @@ -125,7 +126,7 @@ def generate_build_file(packages, dependency_graph, cache, extras, sandbox): ) # Add 'py' as language if not there. metadata['langs'].add('py') - except ValueError: + except PackageNotFoundError: if any('py' in metadata['langs'] for metadata in dependencies.values()): metadata['langs'].add('py (transitively)') # Dependencies still need to be propagated. diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py index 63a2927e6..a58c5cca6 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py @@ -1,14 +1,21 @@ import glob +from importlib.metadata import distribution +from importlib.metadata import PackageNotFoundError + from ros2bzl.scrapping.system import find_library_dependencies -from ros2bzl.scrapping.system import find_python_package from ros2bzl.scrapping.system import is_system_library +def find_python_package(name): + dist = distribution(name) + top_level = dist.read_text('top_level.txt') + top_level = top_level.rstrip('\n') + return str(dist.locate_file(top_level)) + + def collect_ament_python_package_properties(name, metadata): - python_package_path = find_python_package( - name, sysroot=metadata['prefix'] - ) + python_package_path = find_python_package(name) properties = {'python_packages': [python_package_path]} cc_extensions = glob.glob( '{}/**/*.so'.format(python_package_path), recursive=True diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/system.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/system.py index cafe36fb5..ec0129b47 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/system.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/system.py @@ -72,13 +72,3 @@ def find_library_dependencies(library_path): yield match.group(1) except Exception: return - - -def find_python_package(name, sysroot='/usr'): - v = sys.version_info - path = '{}/lib/python{}.{}/site-packages/{}'.format( - sysroot, v.major, v.minor, name - ) - if not os.path.exists(os.path.join(path, '__init__.py')): - raise ValueError(name + ' is not a Python package') - return path From cacf43f2316029b191dab0b1ab1f5d24fd67250e Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 12 May 2021 15:45:31 -0300 Subject: [PATCH 006/107] Fix cc_library imports (includes, data) Signed-off-by: Michel Hidalgo --- .../ros2/resources/package_cc_library.bzl.tpl | 1 - .../tools/skylark/ros2/ros2bzl/templates.py | 27 ++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_library.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_library.bzl.tpl index cff474b2b..99c0bd0a6 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_library.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_library.bzl.tpl @@ -2,7 +2,6 @@ drake_ros_cc_library( name = {name}, srcs = {srcs}, hdrs = glob(["{{}}/**/*.*".format(x) for x in {includes}]), - includes = {include_directories}, copts = {copts}, defines = {defines}, linkopts = {linkopts}, diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py index 858d9db0d..46fb58ab2 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py @@ -85,8 +85,16 @@ def configure_package_cc_library(name, metadata, properties, dependencies, extra target_name = cc_name(name, metadata) libraries = [sandbox(library) for library in properties['link_libraries']] include_directories = [sandbox(include) for include in properties['include_directories']] - # Assume package abides to REP-122 FHS layout. - includes = [os.path.join(include, name) for include in include_directories] + local_include_directories = [ + os.path.join(include, name) # assumes package abides to REP-122 FHS layout + for include in include_directories + if not os.path.isabs(include) + ] + # Push remaining nonlocal includes through compiler options + copts = ['-isystem ' + include for include in include_directories if os.path.isabs(include)] + copts.extend(properties['compile_flags']) + defines = properties['defines'] + linkopts = properties['link_flags'] for link_directory in properties['link_directories']: link_directory = sandbox(link_directory) @@ -96,8 +104,6 @@ def configure_package_cc_library(name, metadata, properties, dependencies, extra '-L' + link_directory, '-Wl,-rpath ' + link_directory ] - copts = properties['compile_flags'] - defines = properties['defines'] deps = [ cc_label(dependency_name, dependency_metadata) for dependency_name, dependency_metadata in dependencies.items() @@ -124,15 +130,18 @@ def configure_package_cc_library(name, metadata, properties, dependencies, extra ) if load_paths: runenv['${LOAD_PATH}'] = ['path-prepend', *load_paths] - data.extend(libraries) - if extras and 'data' in extras: - data.extend(extras['data'].get(target_name, [])) + data.extend(library for library in libraries if library not in data) + if extras and 'data' in extras and target_name in extras['data']: + data.extend( + label_or_path + for label_or_path in extras['data'][target_name] + if label_or_path not in data + ) return load_resource('package_cc_library.bzl.tpl'), { 'name': target_name, 'srcs': libraries, - 'includes': includes, - 'include_directories': include_directories, + 'includes': local_include_directories, 'copts': copts, 'defines': defines, 'linkopts': linkopts, From ab802fe2ec6f1005c3eae916c1d6b64fd0bc6dcb Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 26 May 2021 19:38:27 -0300 Subject: [PATCH 007/107] Drop CMake rosidl pipeline entrypoint Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/resources/prologue.bzl | 8 - .../skylark/ros2/resources/rosidl.bzl.tpl | 15 -- .../resources/rosidl_cmake_CMakeLists.txt.in | 19 -- .../ros2/resources/rosidl_package.xml.in | 23 -- .../tools/skylark/ros2/ros2.bzl | 3 - .../ros2/rosidl_generate_interfaces.py | 232 ------------------ 6 files changed, 300 deletions(-) delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/prologue.bzl delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl.tpl delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl_cmake_CMakeLists.txt.in delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl_package.xml.in delete mode 100755 drake_ros_bazel_installed/tools/skylark/ros2/rosidl_generate_interfaces.py diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/prologue.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/prologue.bzl deleted file mode 100644 index cb13db2c2..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/prologue.bzl +++ /dev/null @@ -1,8 +0,0 @@ -# -*- python -*- - -load("@drake_ros//tools/skylark:drake_ros_cc.bzl", "drake_ros_cc_binary_import") -load("@drake_ros//tools/skylark:drake_ros_cc.bzl", "drake_ros_cc_library") -load("@drake_ros//tools/skylark:drake_ros_py.bzl", "drake_ros_py_library") -load("@drake_ros//tools/skylark/ros2:ros2.bzl", "package_share_filegroup") - -glob = native.glob diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl.tpl deleted file mode 100644 index 77fbbf4f9..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl.tpl +++ /dev/null @@ -1,15 +0,0 @@ -# -*- python -*- - -def rosidl_generate_interfaces_command( - repository, package, interfaces, output, prefix=None, dependencies=[] -): - cmd = ["ROS2BZL_PREFIX_PATH={repository_dir}", "{repository_dir}/env.sh"] - cmd.append("{repository_dir}/generate_rosidl_file.py") - cmd.extend(["-o ", output]) - if prefix: - cmd.extend(["-t", prefix]) - cmd.append(repository) - cmd.append(package) - cmd.extend(interfaces) - cmd.extend(["-d " + dep for dep in dependencies]) - return " ".join(cmd) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl_cmake_CMakeLists.txt.in b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl_cmake_CMakeLists.txt.in deleted file mode 100644 index 1d54487ef..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl_cmake_CMakeLists.txt.in +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Bazel. -cmake_minimum_required(VERSION 3.5) -project(@NAME@ C CXX) - -find_package(ament_cmake REQUIRED) -find_package(rosidl_cmake REQUIRED) -find_package(rosidl_default_generators REQUIRED) -set(DEPENDENCIES @DEPENDENCIES@) -foreach(dep IN LISTS DEPENDENCIES) - find_package(${dep} REQUIRED) -endforeach() - -rosidl_generate_interfaces( - ${PROJECT_NAME} - @INTERFACES@ - DEPENDENCIES ${DEPENDENCIES} -) - -ament_package() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl_package.xml.in b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl_package.xml.in deleted file mode 100644 index d7c1b27c0..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl_package.xml.in +++ /dev/null @@ -1,23 +0,0 @@ - - - - @NAME@ - 0.0.0 - Mock package to generate @NAME@ rosidl interfaces - nobody - Apache License 2.0 - - ament_cmake - rosidl_cmake - rosidl_default_generators - - @DEPENDENCIES@ - - rosidl_default_runtime - - rosidl_interface_packages - - - ament_cmake - - diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl index 09fd60ee0..70aaf5239 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -6,14 +6,12 @@ MANIFEST = [ "resources/package_py_library_with_cc_extensions.bzl.tpl", "resources/package_cc_library.bzl.tpl", "resources/BUILD.prologue.bazel", - "resources/rosidl_package.xml.in", "resources/ament_cmake_CMakeLists.txt.in", "resources/package_meta_py_library.bzl.tpl", "resources/package_alias.bzl.tpl", "resources/package_py_library.bzl.tpl", "resources/package_share_filegroup.bzl.tpl", "resources/prologue.bzl", - "resources/rosidl_cmake_CMakeLists.txt.in", "ros2bzl/utilities.py", "ros2bzl/sandboxing.py", "ros2bzl/resources.py", @@ -24,7 +22,6 @@ MANIFEST = [ "ros2bzl/scrapping/ament_python.py", "ros2bzl/scrapping/ament_cmake.py", "ros2bzl/scrapping/__init__.py", - "rosidl_generate_interfaces.py" ] def _execute_or_fail(repo_ctx, cmd, **kwargs): diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/rosidl_generate_interfaces.py b/drake_ros_bazel_installed/tools/skylark/ros2/rosidl_generate_interfaces.py deleted file mode 100755 index 655c45075..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/rosidl_generate_interfaces.py +++ /dev/null @@ -1,232 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import glob -import os -import shutil -import sys - -from tempfile import TemporaryDirectory -from textwrap import indent -import xml.etree.ElementTree as ET - -import cmake_tools - -from ros2bzl.scrapping.metadata import collect_package_metadata -from ros2bzl.scrapping.ament_cmake import collect_ament_cmake_package_direct_properties -from ros2bzl.scrapping.ament_python import collect_ament_python_package_direct_properties -from ros2bzl.templates import configure_package_share_filegroup -from ros2bzl.templates import configure_package_cc_library -from ros2bzl.templates import configure_package_py_library - -from ros2bzl.resources import load_resource -from ros2bzl.resources import load_underlay -from ros2bzl.resources import path_to_resource - -import ros2bzl.sandboxing as sandboxing - -from ros2bzl.utilities import compose -from ros2bzl.utilities import interpolate - - -def generate_rosidl_interfaces(args): - with TemporaryDirectory(dir=args.gen_directory) as tmpdir: - local_interfaces = [] - for path in args.interfaces: - symlink = os.path.join( - tmpdir, - os.path.basename( - os.path.dirname(path) - ), - os.path.basename(path) - ) - os.makedirs(os.path.dirname(symlink), exist_ok=True) - os.symlink(path, symlink) - local_interfaces.append( - os.path.relpath(symlink, start=tmpdir) - ) - - cmakelists_template_path = path_to_resource('rosidl_cmake_CMakeLists.txt.in') - cmakelists_path = os.path.join(tmpdir, 'CMakeLists.txt') - cmake_tools.configure_file(cmakelists_template_path, cmakelists_path, { - '@NAME@': args.package_name, - '@INTERFACES@': ' '.join(local_interfaces), - '@DEPENDENCIES@': ' '.join(args.dependencies), - }) - - package_xml_template_path = path_to_resource('rosidl_package.xml.in') - package_xml_path = os.path.join(tmpdir, 'package.xml') - cmake_tools.configure_file(package_xml_template_path, package_xml_path, { - '@NAME@': args.package_name, '@DEPENDENCIES@': '\n'.join([ - '' + dep + '' for dep in args.dependencies - ]), - }) - - install_path = os.path.join(args.gen_directory, args.target_prefix) - if os.path.exists(install_path): - shutil.rmtree(install_path) - os.makedirs(install_path) - - ament_prefix_path = os.environ.get('AMENT_PREFIX_PATH', '') - cmake_prefix_path = ament_prefix_path.replace(':', ';') - cmake_tools.build_then_install( - tmpdir, - '-DCMAKE_PREFIX_PATH=' + cmake_prefix_path, - '-DCMAKE_INSTALL_PREFIX=' + install_path, - ) - - return install_path - - -def generate_rosidl_file(args): - packages, _, cache, sandbox = load_underlay() - - metadata = collect_package_metadata( - args.package_name, prefix=generate_rosidl_interfaces(args) - ) - - noextras = None - - sandbox = compose(args.sandbox, sandbox) - - dependencies = { - 'rosidl_default_generators', - 'rosidl_default_runtime' - } - dependencies.update(metadata['build_dependencies']) - dependencies.update(metadata['run_dependencies']) - - dependencies = {name: packages[name] for name in dependencies} - - args.output_file.write(load_resource('prologue.bzl') + '\n') - - metadata['bazel_name'] = args.target_prefix # Use target prefix - args.output_file.write('def {}():\n'.format(args.target_prefix)) - - # Generate share files' group - template, config = configure_package_share_filegroup( - args.package_name, metadata, args.sandbox - ) - args.output_file.write( - indent(interpolate(template, config), ' ' * 4) + '\n' - ) - - # Generate C/C++ library - properties = collect_ament_cmake_package_direct_properties( - args.package_name, metadata, dependencies, cache - ) - template, config = configure_package_cc_library( - args.package_name, metadata, properties, - dependencies, noextras, sandbox - ) - args.output_file.write( - indent(interpolate(template, config), ' ' * 4) + '\n' - ) - - # Generate Python package - properties = collect_ament_python_package_direct_properties( - args.package_name, metadata, dependencies, cache - ) - template, config = configure_package_py_library( - args.package_name, metadata, properties, - dependencies, noextras, sandbox - ) - args.output_file.write(indent(interpolate(template, config), ' ' * 4)) - - -def name_from_package_xml(): - if not os.path.exists('package.xml'): - return None - tree = ET.parse('package.xml') - return tree.find('./name').text - - -def parse_arguments(): - parser = argparse.ArgumentParser() - parser.add_argument( - '-s', '--sandbox', action='append', default=[], - help='Path mappings for sandboxing, in "outer:inner" format' - ) - parser.add_argument( - '-o', '--output-file', type=argparse.FileType('w'), - default=sys.stdout, help='Output .bzl file (defaults to stdout)' - ) - parser.add_argument( - '--gen-directory', default='gen', help=( - 'Directory for generated files (relative to output file, if one is given)' - ) - ) - parser.add_argument( - '-d', '--dep', dest='dependencies', action='append', default=[], - help='Packages from which interface types are used' - ) - parser.add_argument( - '-r', '--repository-name', - default=os.environ.get('BAZEL_REPOSITORY_NAME', None), - help='Bazel repository name' - ) - parser.add_argument( - '-p', '--package-name', default=name_from_package_xml(), - help='Interfaces\' package name (defaults to package.xml content if any)' - ) - parser.add_argument( - '-t', '--target-prefix', default=None, - help='Interface\' targets prefix (defaults to package name)' - ) - parser.add_argument( - 'interfaces', nargs='+', - help='Paths to interface files (may be glob patterns)' - ) - args = parser.parse_args() - - if not args.repository_name: - parser.error( - 'No repository name provided nor $BAZEL_REPOSITORY_NAME envvar found' - ) - - if not args.package_name: - parser.error( - 'No package name provided nor package.xml found' - ) - - if args.output_file is not sys.stdout: - output_dirpath = os.path.dirname( - os.path.realpath(args.output_file.name) - ) - else: - output_dirpath = os.getcwd() - os.makedirs(output_dirpath, exist_ok=True) - - args.gen_directory = os.path.realpath(args.gen_directory) - if not args.gen_directory.startswith(output_dirpath): - parser.error( - 'Generated files must be in subdirectories of the output file ' - 'directory. All paths in a Bazel target must be relative.' - ) - os.makedirs(args.gen_directory, exist_ok=True) - - args.sandbox.append(output_dirpath + ':.') - args.sandbox = sandboxing.configure( - name=args.repository_name, mapping=dict( - entry.partition(':')[0::2] for entry in args.sandbox - ) - ) - - if not args.target_prefix: - args.target_prefix = args.package_name - - args.interfaces = [ - os.path.realpath(path) - for pattern in args.interfaces - for path in glob.glob(pattern) - ] - - return args - - -def main(): - generate_rosidl_file(parse_arguments()) - - -if __name__ == '__main__': - main() From 4eda216815cbfcf5ac06cad92b3a1930b876ad29 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 4 Jun 2021 12:34:02 -0300 Subject: [PATCH 008/107] Fix dload C++ shim assert. Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/tools/skylark/dload_cc.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl b/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl index 0f5182e51..0940b246e 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl @@ -42,7 +42,7 @@ int main(int argc, const char * argv[]) {{ for (size_t i = 0; i < names.size(); ++i) {{ std::stringstream value; if (actions[i][0] == "replace") {{ - assert(actions[i].size() != 2); + assert(actions[i].size() == 2); value << actions[i][1]; }} else if (actions[i][0] == "path-replace") {{ assert(actions[i].size() == 2); From 89d3ae3982ced9a83bbf517a23ac04d31650c787 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 4 Jun 2021 12:36:45 -0300 Subject: [PATCH 009/107] Add drake_ros_py_executable_import() macro. Signed-off-by: Michel Hidalgo --- .../tools/skylark/drake_ros_py.bzl | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drake_ros_bazel_installed/tools/skylark/drake_ros_py.bzl b/drake_ros_bazel_installed/tools/skylark/drake_ros_py.bzl index 76a835976..b842f5ffd 100644 --- a/drake_ros_bazel_installed/tools/skylark/drake_ros_py.bzl +++ b/drake_ros_bazel_installed/tools/skylark/drake_ros_py.bzl @@ -39,6 +39,44 @@ def drake_ros_py_library(name, srcs = [], main = None, imports = [], visibility = visibility, ) +def drake_ros_py_executable_import( + name, executable = [], imports = [], args = [], + data = [], deps = [], tags = [], testonly = 0, + visibility = None, **kwargs +): + """ + Imports an picking up runtime information in dependencies. + + Args: + executable: executable file + + Similar to the py_binary() rule. + """ + shim = name + "_shim.py" + dload_py_shim( + name = shim, + target = executable, + data = data, + deps = deps, + testonly = testonly, + visibility = visibility, + ) + native.py_binary( + name = name, + srcs = [shim], + main = shim, + imports = imports, + data = [executable] + data, + deps = [ + "@bazel_tools//tools/python/runfiles", + ] + deps, + tags = ["nolint"] + tags, + testonly = testonly, + visibility = visibility, + python_version = "PY3", + **kwargs + ) + def drake_ros_py_binary(name, srcs = [], main = None, imports = [], args = [], srcs_version = None, data = [], deps = [], tags = [], testonly = 0, visibility = None, **kwargs): From 7bc6c620130eaec4b81ebb20ae8773283faca0c0 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 4 Jun 2021 12:37:21 -0300 Subject: [PATCH 010/107] Split execute_or_fail() macro for reuse. Signed-off-by: Michel Hidalgo --- .../tools/skylark/execute.bzl | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 drake_ros_bazel_installed/tools/skylark/execute.bzl diff --git a/drake_ros_bazel_installed/tools/skylark/execute.bzl b/drake_ros_bazel_installed/tools/skylark/execute.bzl new file mode 100644 index 000000000..95d8eea54 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/execute.bzl @@ -0,0 +1,19 @@ +# -*- python -*- + +def execute_or_fail(repo_ctx, cmd, **kwargs): + exec_result = repo_ctx.execute(cmd, **kwargs) + if exec_result.return_code != 0: + error_message ="'{}' exited with {}".format( + " ".join([str(token) for token in cmd]), + exec_result.return_code + ) + if exec_result.stdout: + error_message += "\n--- captured stdout ---\n" + error_message += exec_result.stdout + if exec_result.stderr: + error_message += "\n--- captured stderr ---\n" + error_message += exec_result.stderr + fail("Failed to setup @{} repository: {}".format( + repo_ctx.name, error_message + )) + return exec_result From a68b9cb1b1a7c088a629aed6b96810f52cc7b476 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 4 Jun 2021 12:38:47 -0300 Subject: [PATCH 011/107] Add python_dev repository to support C extension builds. Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/WORKSPACE | 4 + .../tools/workspace/python/BUILD.bazel | 0 .../tools/workspace/python/repository.bzl | 102 ++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 drake_ros_bazel_installed/tools/workspace/python/BUILD.bazel create mode 100644 drake_ros_bazel_installed/tools/workspace/python/repository.bzl diff --git a/drake_ros_bazel_installed/WORKSPACE b/drake_ros_bazel_installed/WORKSPACE index c98a62b9a..c3d1bcd0a 100644 --- a/drake_ros_bazel_installed/WORKSPACE +++ b/drake_ros_bazel_installed/WORKSPACE @@ -10,6 +10,10 @@ load("@drake_repository//:share/drake/repo.bzl", "drake_repository") drake_repository(name = "drake") +load("//tools/workspace/python:repository.bzl", "python_repository") + +python_repository(name = "python_dev") + load("//tools/workspace/ros2:repository.bzl", "ros2_repository") ros2_repository(name = "ros2") diff --git a/drake_ros_bazel_installed/tools/workspace/python/BUILD.bazel b/drake_ros_bazel_installed/tools/workspace/python/BUILD.bazel new file mode 100644 index 000000000..e69de29bb diff --git a/drake_ros_bazel_installed/tools/workspace/python/repository.bzl b/drake_ros_bazel_installed/tools/workspace/python/repository.bzl new file mode 100644 index 000000000..ab57a20dd --- /dev/null +++ b/drake_ros_bazel_installed/tools/workspace/python/repository.bzl @@ -0,0 +1,102 @@ +load("//tools/skylark:execute.bzl", "execute_or_fail") + +VERSION_FILE_TEMPLATE = \ +""" +# -*- python -*- + +PYTHON_VERSION = "{}" +PYTHON_EXTENSION_SUFFIX = "{}" +""" + +BUILD_FILE_TEMPLATE = \ +""" +# -*- python -*- + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "headers", + hdrs = glob( + include = ["include/**/*.*"], + exclude_directories = 1, + ), + includes = {}, +) + +cc_library( + name = "libs", + linkopts = {}, + deps = [":headers"], +) +""" + +def _impl(repo_ctx): + python_interpreter = repo_ctx.which("python3") + if python_interpreter == None: + fail("No python3 interpreter found in PATH") + python_config = repo_ctx.which("python3-config") + if python_config == None: + fail("No python3-config utility found in PATH") + + python_version = execute_or_fail( + repo_ctx, [ + python_interpreter, "-c" , + "import sys; v = sys.version_info;" + + "print('{}.{}'.format(v.major, v.minor))" + ] + ).stdout.strip() + + extension_suffix = execute_or_fail( + repo_ctx, [python_config, "--extension-suffix"] + ).stdout.strip() + + repo_ctx.file( + "version.bzl", + content = VERSION_FILE_TEMPLATE.format( + python_version, extension_suffix + ), + executable = False + ) + + cflags = execute_or_fail( + repo_ctx, [python_config, "--includes"] + ).stdout.strip().split(" ") + + includes = [] + for cflag in cflags: + if not cflag.startswith("-I"): + continue + include = cflag[2:] + sandboxed_include = "include/{}".format( + include.replace("/", "_")) + if sandboxed_include in includes: + continue + repo_ctx.symlink(include, sandboxed_include) + includes.append(sandboxed_include) + + linkopts = execute_or_fail( + repo_ctx, [python_config, "--ldflags"] + ).stdout.strip().split(" ") + + libpython = "python{}".format(python_version) + links_libpython = False + for opt in linkopts: + if opt.startswith("-l") and libpython in opt: + links_libpython = True + break + if not links_libpython: + linkopts.append("-l{}".format(libpython)) + + repo_ctx.file( + "BUILD.bazel", + content = BUILD_FILE_TEMPLATE.format( + includes, linkopts + ), + executable = False + ) + +python_repository = repository_rule( + implementation = _impl, + local = True, + configure = True +) From 86331861fbd0fff7e7f240d8adf391be0ece6527 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 4 Jun 2021 12:39:24 -0300 Subject: [PATCH 012/107] Implement rosidl pipeline Also tweaks ros2 tools to support it Signed-off-by: Michel Hidalgo --- .../skylark/ros2/generate_repository_files.py | 75 +- .../tools/skylark/ros2/packages.bzl | 29 + .../ros2/resources/BUILD.prologue.bazel | 5 +- .../skylark/ros2/resources/distro.bzl.tpl | 1 + ...port.bzl.tpl => executable_import.bzl.tpl} | 3 +- .../ros2/resources/package_cc_library.bzl.tpl | 3 +- .../package_interfaces_filegroup.bzl.tpl | 4 + .../ros2/resources/package_py_library.bzl.tpl | 8 +- ...kage_py_library_with_cc_extensions.bzl.tpl | 8 +- .../skylark/ros2/resources/rosidl.bzl.tpl | 686 ++++++++++++++++++ .../tools/skylark/ros2/ros2.bzl | 53 +- .../ros2/ros2bzl/scrapping/__init__.py | 16 + .../ros2/ros2bzl/scrapping/ament_cmake.py | 17 +- .../ros2/ros2bzl/scrapping/ament_python.py | 8 +- .../tools/skylark/ros2/ros2bzl/templates.py | 96 ++- 15 files changed, 903 insertions(+), 109 deletions(-) create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/packages.bzl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/distro.bzl.tpl rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{package_cc_binary_import.bzl.tpl => executable_import.bzl.tpl} (58%) create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/package_interfaces_filegroup.bzl.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py index 9eb025042..eec8e05be 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -13,18 +13,22 @@ sys.path.insert(0, os.path.dirname(__file__)) # noqa from ros2bzl.scrapping import index_all_packages +from ros2bzl.scrapping import list_all_executables from ros2bzl.scrapping import build_dependency_graph from ros2bzl.scrapping.ament_cmake import collect_ament_cmake_package_properties from ros2bzl.scrapping.ament_cmake import collect_ament_cmake_package_direct_properties from ros2bzl.scrapping.ament_python import collect_ament_python_package_direct_properties from ros2bzl.scrapping.ament_python import PackageNotFoundError +from ros2bzl.templates import configure_executable_imports from ros2bzl.templates import configure_package_meta_py_library from ros2bzl.templates import configure_package_alias +from ros2bzl.templates import configure_package_c_library_alias from ros2bzl.templates import configure_package_cc_library from ros2bzl.templates import configure_package_executable_imports from ros2bzl.templates import configure_package_py_library from ros2bzl.templates import configure_package_share_filegroup +from ros2bzl.templates import configure_package_interfaces_filegroup from ros2bzl.resources import load_resource from ros2bzl.resources import setup_underlay @@ -81,7 +85,24 @@ def parse_arguments(): return args -def generate_build_file(packages, dependency_graph, cache, extras, sandbox): +def generate_distro_file(packages): + typesupport_groups = [ + 'rosidl_typesupport_c_packages', + 'rosidl_typesupport_cpp_packages' + ] + with open('distro.bzl', 'w') as fd: + fd.write(load_resource('distro.bzl.tpl').format( + available_typesupports=[ + name for name, metadata in packages.items() if any( + group in typesupport_groups for group in metadata['groups'] + ) + ] + ) + '\n') + + +def generate_build_file( + packages, executables, dependency_graph, cache, extras, sandbox +): rmw_implementation_packages = { name: metadata for name, metadata in packages.items() if 'rmw_implementation_packages' in metadata['groups'] @@ -98,11 +119,18 @@ def generate_build_file(packages, dependency_graph, cache, extras, sandbox): for dependency_name in dependency_graph[name] } + targets = [] + template, config = \ configure_package_share_filegroup(name, metadata, sandbox) fd.write(interpolate(template, config) + '\n') - main_target = None + if 'rosidl_interface_packages' in metadata['groups']: + template, config = \ + configure_package_interfaces_filegroup(name, metadata, sandbox) + fd.write(interpolate(template, config) + '\n') + targets.append(config['name']) + if metadata['build_type'] == 'ament_cmake': properties = collect_ament_cmake_package_direct_properties( name, metadata, dependencies, cache @@ -113,11 +141,17 @@ def generate_build_file(packages, dependency_graph, cache, extras, sandbox): ) if any(properties.values()): - # Tentatively set as main target (for aliasing) - main_target = config['name'] + targets.append(config['name']) fd.write(interpolate(template, config) + '\n') + if 'rosidl_interface_packages' in metadata['groups']: + # Alias C++ library as C library for interface packages + # as their headers and artifacts cannot be discriminated. + template, config = \ + configure_package_c_library_alias(name, metadata) + fd.write(interpolate(template, config) + '\n') + # No way to tell if there's Python code for this package # but to look for it. try: @@ -140,22 +174,24 @@ def generate_build_file(packages, dependency_graph, cache, extras, sandbox): template, config = configure_package_py_library( name, metadata, properties, dependencies, extras, sandbox ) - - # Set as main target if not set already, - # otherwise there's no main target. - main_target = config['name'] if not main_target else None - fd.write(interpolate(template, config) + '\n') + targets.append(config['name']) - if main_target is not None and main_target != name: - template, config = configure_package_alias(name, main_target) + if len(targets) == 1 and targets[0] != name: + template, config = configure_package_alias(name, targets[0]) fd.write(interpolate(template, config) + '\n') - dependencies.update(rmw_implementation_packages) - for template, config in configure_package_executable_imports( - name, metadata, dependencies, extras, sandbox - ): - fd.write(interpolate(template, config) + '\n') + if metadata['executables']: + dependencies.update(rmw_implementation_packages) + for template, config in configure_package_executable_imports( + name, metadata, dependencies, sandbox, extras=extras + ): + fd.write(interpolate(template, config) + '\n') + + for template, config in configure_executable_imports( + executables, packages, sandbox, extras=extras + ): + fd.write(interpolate(template, config) + '\n') def precache_ament_cmake_properties(packages, jobs=None): @@ -176,6 +212,8 @@ def precache_ament_cmake_properties(packages, jobs=None): def main(): args = parse_arguments() + executables = list_all_executables() + packages, dependency_graph = build_dependency_graph( index_all_packages(), set(args.include_packages), @@ -186,7 +224,10 @@ def main(): 'ament_cmake': precache_ament_cmake_properties(packages, jobs=args.jobs) } - generate_build_file(packages, dependency_graph, cache, args.extras, args.sandbox) + generate_build_file( + packages, executables, dependency_graph, cache, args.extras, args.sandbox) + + generate_distro_file(packages) for name, metadata in packages.items(): # For downstream repositories to use diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/packages.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/packages.bzl new file mode 100644 index 000000000..b50bec7f0 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/packages.bzl @@ -0,0 +1,29 @@ + +def package_share_filegroup(name, share_directories): + native.filegroup( + name = name, + srcs = [path for path in native.glob( + include = ["{}/**".format(dirpath) for dirpath in share_directories], + exclude = [ + "*/cmake/**", + "*/environment/**", + "*/*.sh", + "*/*.bash", + "*/*.dsv", + ] + ) if " " not in path], + # NOTE(hidmic): workaround lack of support for spaces. + # See https://github.com/bazelbuild/bazel/issues/4327. + ) + + +def package_interfaces_filegroup(name, share_directory): + native.filegroup( + name = name + "_defs", + srcs = native.glob(include = [ + "{}/**/*.idl".format(share_directory), + "{}/**/*.msg".format(share_directory), + "{}/**/*.srv".format(share_directory), + "{}/**/*.action".format(share_directory), + ]) + ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/BUILD.prologue.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/resources/BUILD.prologue.bazel index f9874c56d..338a2e148 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/BUILD.prologue.bazel +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/BUILD.prologue.bazel @@ -2,7 +2,8 @@ package(default_visibility = ["//visibility:public"]) -load("@drake_ros//tools/skylark:drake_ros_cc.bzl", "drake_ros_cc_binary_import") load("@drake_ros//tools/skylark:drake_ros_cc.bzl", "drake_ros_cc_library") +load("@drake_ros//tools/skylark:drake_ros_py.bzl", "drake_ros_py_executable_import") load("@drake_ros//tools/skylark:drake_ros_py.bzl", "drake_ros_py_library") -load("@drake_ros//tools/skylark/ros2:ros2.bzl", "package_share_filegroup") +load("@drake_ros//tools/skylark/ros2:packages.bzl", "package_share_filegroup") +load("@drake_ros//tools/skylark/ros2:packages.bzl", "package_interfaces_filegroup") diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/distro.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/distro.bzl.tpl new file mode 100644 index 000000000..ba58e5d85 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/distro.bzl.tpl @@ -0,0 +1 @@ +AVAILABLE_TYPESUPPORTS = {available_typesupports} diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_binary_import.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/executable_import.bzl.tpl similarity index 58% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_binary_import.bzl.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/executable_import.bzl.tpl index a16922b90..5e219d10b 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_binary_import.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/executable_import.bzl.tpl @@ -1,5 +1,6 @@ -drake_ros_cc_binary_import( +drake_ros_py_executable_import( name = {name}, executable = {executable}, data = {data}, + deps = {deps} ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_library.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_library.bzl.tpl index 99c0bd0a6..fabeed62e 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_library.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_library.bzl.tpl @@ -1,7 +1,8 @@ drake_ros_cc_library( name = {name}, srcs = {srcs}, - hdrs = glob(["{{}}/**/*.*".format(x) for x in {includes}]), + hdrs = glob(["{{}}/**/*.*".format(x) for x in {headers}]), + includes = {includes}, copts = {copts}, defines = {defines}, linkopts = {linkopts}, diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_interfaces_filegroup.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_interfaces_filegroup.bzl.tpl new file mode 100644 index 000000000..7558eaf2e --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_interfaces_filegroup.bzl.tpl @@ -0,0 +1,4 @@ +package_interfaces_filegroup( + name = {name}, + share_directory = {share_directory}, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library.bzl.tpl index b4f3c2a82..37c622ebc 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library.bzl.tpl @@ -1,8 +1,12 @@ drake_ros_py_library( name = {name}, - srcs = glob(["{{}}/**/*.py".format(x) for x in {packages}]), + srcs = glob(["{{}}/**/*.py".format(x) for x in {tops}]), data = glob( - include=["{{}}/**/*.*".format(x) for x in {packages}], + include=[ + "{{}}/**/*.*".format(x) for x in {tops} + ] + [ + "{{}}/*".format(x) for x in {eggs} + ], exclude=["**/*.py", "**/*.so"], ) + {data}, imports = {imports}, diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library_with_cc_extensions.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library_with_cc_extensions.bzl.tpl index b4f3c2a82..37c622ebc 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library_with_cc_extensions.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library_with_cc_extensions.bzl.tpl @@ -1,8 +1,12 @@ drake_ros_py_library( name = {name}, - srcs = glob(["{{}}/**/*.py".format(x) for x in {packages}]), + srcs = glob(["{{}}/**/*.py".format(x) for x in {tops}]), data = glob( - include=["{{}}/**/*.*".format(x) for x in {packages}], + include=[ + "{{}}/**/*.*".format(x) for x in {tops} + ] + [ + "{{}}/*".format(x) for x in {eggs} + ], exclude=["**/*.py", "**/*.so"], ) + {data}, imports = {imports}, diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl.tpl new file mode 100644 index 000000000..ec84d8b9e --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl.tpl @@ -0,0 +1,686 @@ +# -*- python -*- + +load("@REPOSITORY_ROOT@:distro.bzl", "AVAILABLE_TYPESUPPORTS") +load("@python_dev//:version.bzl", "PYTHON_EXTENSION_SUFFIX") +load("@drake_ros//tools/skylark:drake_ros_cc.bzl", "drake_ros_cc_library") +load("@drake_ros//tools/skylark:drake_ros_py.bzl", "drake_ros_py_library") + +def _as_idl_tuple(file): + path, parent, base = file.short_path.rsplit("/", 2) + if parent not in ("msg", "srv", "action"): + fail("Interface parent folder must be one of: 'msg', 'srv', 'action'") + return "{}:{}/{}".format(path, parent, base) + +def _as_include_flag(file): + return "-I" + file.short_path.rsplit("/", 3)[0] + +def _rosidl_generate_genrule_impl(ctx): + args = ctx.actions.args() + args.add("generate") + output_path = "{}/{}".format( + ctx.var["GENDIR"], + ctx.label.package) + if ctx.attr.output_dir: + output_path = "{}/{}".format( + output_path, ctx.attr.output_dir) + args.add("--output-path", output_path) + for type in ctx.attr.types: + args.add("--type", type) + for typesupport in ctx.attr.typesupports: + args.add("--type-support", typesupport) + args.add_all(ctx.files.includes, map_each=_as_include_flag, uniquify=True) + args.add(ctx.attr.package) + args.add_all(ctx.files.interfaces, map_each=_as_idl_tuple) + inputs, input_manifests = ctx.resolve_tools(tools = [ctx.attr._tool]) + inputs = inputs.to_list() + ctx.files.interfaces + ctx.files.includes + ctx.actions.run( + executable = ctx.executable._tool, + arguments = [args], inputs = inputs, + input_manifests = input_manifests, + outputs = ctx.outputs.generated_sources, + ) + +rosidl_generate_genrule = rule( + attrs = dict( + generated_sources = attr.output_list(mandatory = True), + types = attr.string_list(mandatory = False), + typesupports = attr.string_list(mandatory = False), + package = attr.string(mandatory = True), + interfaces = attr.label_list( + mandatory = True, allow_files = True + ), + includes = attr.label_list(mandatory = False), + output_dir = attr.string(mandatory = False), + _tool = attr.label( + default = "@REPOSITORY_ROOT@:rosidl", + executable = True, cfg = "exec" + ) + ), + implementation = _rosidl_generate_genrule_impl, + output_to_genfiles = True, +) + +def _rosidl_translate_genrule_impl(ctx): + args = ctx.actions.args() + args.add("translate") + output_path = "{}/{}".format( + ctx.var["GENDIR"], + ctx.label.package) + if ctx.attr.output_dir: + output_path = "{}/{}".format( + output_path, ctx.attr.output_dir) + args.add("--output-path", output_path) + args.add("--output-format", ctx.attr.output_format) + if ctx.attr.input_format: + args.add("--input-format", ctx.attr.input_format) + args.add_all(ctx.files.includes, map_each=_as_include_flag, uniquify=True) + args.add(ctx.attr.package) + args.add_all(ctx.files.interfaces, map_each=_as_idl_tuple) + inputs, input_manifests = ctx.resolve_tools(tools = [ctx.attr._tool]) + inputs = inputs.to_list() + ctx.files.interfaces + ctx.files.includes + ctx.actions.run( + executable = ctx.executable._tool, + arguments = [args], inputs = inputs, + input_manifests = input_manifests, + outputs = ctx.outputs.translated_interfaces + ) + +rosidl_translate_genrule = rule( + attrs = dict( + translated_interfaces = attr.output_list(mandatory = True), + output_format = attr.string(mandatory = True), + input_format = attr.string(mandatory = False), + package = attr.string(mandatory = True), + interfaces = attr.label_list( + mandatory = True, allow_files = True + ), + includes = attr.label_list(mandatory = False), + output_dir = attr.string(mandatory = False), + _tool = attr.label( + default = "@REPOSITORY_ROOT@:rosidl", + executable = True, cfg = "exec" + ) + ), + implementation = _rosidl_translate_genrule_impl, + output_to_genfiles = True, +) + +def _extract_interface_parts(path): + parent, _, base = path.rpartition("/") + basename, _, ext = base.rpartition(".") + return parent, basename.lower() + +def rosidl_definitions_filegroup(name, group, interfaces, includes, **kwargs): + translated_interfaces = [] + for ifc in interfaces: + base, _, ext = ifc.rpartition(".") + translated_interfaces.append(base + ".idl") + rosidl_translate_genrule( + name = name, + output_format = "idl", + translated_interfaces = translated_interfaces, + package = group, + interfaces = interfaces, + includes = includes, + **kwargs + ) + + +def _generated_source_paths(group, kind): + base = "{}/{}".format(group, kind) + include = "{}/{}".format(native.package_name(), base) + root = "{}/{}".format(base, group) + return base, root + + +def rosidl_c_library( + name, group, interfaces, includes = [], deps = [], **kwargs +): + include, root = _generated_source_paths(group, "c") + + generated_c_sources = [] + visibility_header = "msg/rosidl_generator_c__visibility_control.h" + generated_c_headers = ["{}/{}".format(root, visibility_header)] + for ifc in interfaces: + parent, basename = _extract_interface_parts(ifc) + generated_c_headers.append("{}/{}/{}.h".format(root, parent, basename)) + generated_c_headers.append("{}/{}/detail/{}__functions.h".format(root, parent, basename)) + generated_c_headers.append("{}/{}/detail/{}__struct.h".format(root, parent, basename)) + generated_c_headers.append("{}/{}/detail/{}__type_support.h".format(root, parent, basename)) + generated_c_sources.append("{}/{}/detail/{}__functions.c".format(root, parent, basename)) + generated_sources = generated_c_sources + generated_c_headers + + rosidl_generate_genrule( + name = name + "_gen", + generated_sources = generated_sources, + types = ["c"], + package = group, + interfaces = interfaces, + includes = includes, + output_dir = root, + **kwargs + ) + + deps = deps + [ + "@REPOSITORY_ROOT@:rcutils_cc", + "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", + "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", + ] + + drake_ros_cc_library( + name = name, + srcs = generated_c_sources, + hdrs = generated_c_headers, + includes = [include], + deps = deps, + **kwargs + ) + +def rosidl_cc_library( + name, group, interfaces, includes = [], deps = [], **kwargs +): + include, root = _generated_source_paths(group, "cpp") + + generated_cc_headers = [] + for ifc in interfaces: + parent, basename = _extract_interface_parts(ifc) + generated_cc_headers.append("{}/{}/{}.hpp".format(root, parent, basename)) + generated_cc_headers.append("{}/{}/detail/{}__builder.hpp".format(root, parent, basename)) + generated_cc_headers.append("{}/{}/detail/{}__struct.hpp".format(root, parent, basename)) + generated_cc_headers.append("{}/{}/detail/{}__traits.hpp".format(root, parent, basename)) + + rosidl_generate_genrule( + name = name + "_gen", + generated_sources = generated_cc_headers, + types = ["cpp"], + package = group, + interfaces = interfaces, + includes = includes, + output_dir = root, + **kwargs + ) + + drake_ros_cc_library( + name = name, + hdrs = generated_cc_headers, + includes = [include], + deps = deps + [ + "@REPOSITORY_ROOT@:rosidl_runtime_cpp_cc" + ], + **kwargs + ) + +def _c_extension(name): + return name + "_c_extension" + PYTHON_EXTENSION_SUFFIX + +def _c_extension_label(name): + return ":" + _c_extension(name) + +def _c_typesupport_extension(package, name): + return "{}__{}".format(package, name) + PYTHON_EXTENSION_SUFFIX + +def _c_typesupport_extension_label(package, name): + return ":" + _c_typesupport_extension(package, name) + +def rosidl_py_library( + name, group, interfaces, typesupports, + includes = [], c_deps = [], py_deps = [], + **kwargs +): + include, root = _generated_source_paths(group, "py") + + generated_c_sources = [] + generated_py_sources = [] + for ifc in interfaces: + parent, basename = _extract_interface_parts(ifc) + py_source = "{}/{}/__init__.py".format(root, parent) + if py_source not in generated_py_sources: + generated_py_sources.append(py_source) + generated_py_sources.append( + "{}/{}/_{}.py".format(root, parent, basename)) + generated_c_sources.append( + "{}/{}/_{}_s.c".format(root, parent, basename)) + generated_sources = generated_c_sources + generated_py_sources + + generated_c_sources_per_typesupport = {} + for typesupport_name in typesupports: + generated_c_sources_per_typesupport[typesupport_name] = [ + "{}/_{}_s.ep.{}.c".format(root, group, typesupport_name)] + generated_sources += generated_c_sources_per_typesupport[typesupport_name] + + rosidl_generate_genrule( + name = name + "_gen", + generated_sources = generated_sources, + types = [ + "py[typesupport_implementations:[{}]]" + .format(",".join(typesupports)) + ], + package = group, + interfaces = interfaces, + includes = includes, + output_dir = root, + **kwargs + ) + + drake_ros_cc_library( + name = _c_extension(name), + srcs = generated_c_sources, + deps = c_deps + [ + "@python_dev//:libs", + ], + **kwargs + ) + + c_typesupport_extension_deps = c_deps + [ + _c_extension_label(name), + "@REPOSITORY_ROOT@:rosidl_generator_py_cc", + "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", + "@REPOSITORY_ROOT@:rosidl_typesupport_c_cc", + "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", + "@python_dev//:libs", + ] + py_data = [_c_extension_label(name)] + for typesupport_name, typesupport_library in typesupports.items(): + deps = list(c_typesupport_extension_deps) + if typesupport_library not in deps: + deps.append(typesupport_library) + drake_ros_cc_library( + name = _c_typesupport_extension(group, typesupport_name), + srcs = generated_c_sources_per_typesupport[typesupport_name], + deps = deps, **kwargs + ) + py_data.append(_c_typesupport_extension_label(group, typesupport_name)) + + drake_ros_py_library( + name = name, + srcs = generated_py_sources, + data = py_data, + deps = py_deps, + **kwargs + ) + +def rosidl_typesupport_fastrtps_cc_library( + name, group, interfaces, includes = [], deps = [], **kwargs +): + include, root = _generated_source_paths(group, "typesupport/fastrtps_cpp") + + generated_cc_sources = [] + visibility_header = "msg/rosidl_typesupport_fastrtps_cpp__visibility_control.h" + generated_cc_headers = ["{}/{}".format(root, visibility_header)] + for ifc in interfaces: + parent, basename = _extract_interface_parts(ifc) + template = "{}/{}/detail/dds_fastrtps/{}__type_support.cpp" + generated_cc_sources.append(template.format(root, parent, basename)) + template = "{}/{}/detail/{}__rosidl_typesupport_fastrtps_cpp.hpp" + generated_cc_headers.append(template.format(root, parent, basename)) + generated_sources = generated_cc_sources + generated_cc_headers + + rosidl_generate_genrule( + name = name + "_gen", + generated_sources = generated_sources, + typesupports = ["fastrtps_cpp"], + package = group, + interfaces = interfaces, + includes = includes, + output_dir = root, + **kwargs + ) + + drake_ros_cc_library( + name = name, + srcs = generated_cc_sources, + hdrs = generated_cc_headers, + includes = [include], + deps = deps + [ + # NOTE(hidmic): using rmw_fastrtps_shared_cpp as proxy + # to fastcdr as it is not a ROS package (only a CMake + # package, which can be scrapped but cannot be sorted + # topologically w/o dependents information). + "@REPOSITORY_ROOT@:rmw_fastrtps_shared_cpp_cc", + "@REPOSITORY_ROOT@:rmw_cc", + "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", + "@REPOSITORY_ROOT@:rosidl_typesupport_fastrtps_cpp_cc", + "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", + ], + **kwargs + ) + +def rosidl_typesupport_fastrtps_c_library( + name, group, interfaces, includes = [], deps = [], **kwargs +): + include, root = _generated_source_paths(group, "typesupport/fastrtps_c") + + generated_c_sources = [] + visibility_header = "msg/rosidl_typesupport_fastrtps_c__visibility_control.h" + generated_c_headers = ["{}/{}".format(root, visibility_header)] + for ifc in interfaces: + parent, basename = _extract_interface_parts(ifc) + template = "{}/{}/detail/{}__type_support_c.cpp" + generated_c_sources.append(template.format(root, parent, basename)) + template = "{}/{}/detail/{}__rosidl_typesupport_fastrtps_c.h" + generated_c_headers.append(template.format(root, parent, basename)) + generated_sources = generated_c_sources + generated_c_headers + + rosidl_generate_genrule( + name = name + "_gen", + generated_sources = generated_sources, + typesupports = ["fastrtps_c"], + package = group, + interfaces = interfaces, + includes = includes, + output_dir = root, + **kwargs + ) + + drake_ros_cc_library( + name = name, + srcs = generated_c_sources, + hdrs = generated_c_headers, + includes = [include], + deps = deps + [ + # NOTE(hidmic): using rmw_fastrtps_shared_cpp as proxy + # to fastcdr as it is not a ROS package (only a CMake + # package, which can be scrapped but cannot be sorted + # topologically w/o dependents information). + "@REPOSITORY_ROOT@:rmw_fastrtps_shared_cpp_cc", + "@REPOSITORY_ROOT@:rmw_cc", + "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", + "@REPOSITORY_ROOT@:rosidl_typesupport_fastrtps_c_cc", + "@REPOSITORY_ROOT@:rosidl_typesupport_fastrtps_cpp_cc", + "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", + ], + **kwargs + ) + +def rosidl_typesupport_introspection_c_library( + name, group, interfaces, includes = [], deps = [], **kwargs +): + include, root = _generated_source_paths(group, "typesupport/introspection_c") + + generated_c_sources = [] + visibility_header = "msg/rosidl_typesupport_introspection_c__visibility_control.h" + generated_c_headers = ["{}/{}".format(root, visibility_header)] + for ifc in interfaces: + parent, basename = _extract_interface_parts(ifc) + generated_c_sources.append( + "{}/{}/detail/{}__type_support.c".format(root, parent, basename)) + template = "{}/{}/detail/{}__rosidl_typesupport_introspection_c.h" + generated_c_headers.append(template.format(root, parent, basename)) + generated_sources = generated_c_sources + generated_c_headers + + rosidl_generate_genrule( + name = name + "_gen", + generated_sources = generated_sources, + typesupports = ["introspection_c"], + package = group, + interfaces = interfaces, + includes = includes, + output_dir = root, + **kwargs + ) + + drake_ros_cc_library( + name = name, + srcs = generated_c_sources, + hdrs = generated_c_headers, + includes = [include], + deps = deps + [ + "@REPOSITORY_ROOT@:rosidl_typesupport_introspection_c_cc", + ], + **kwargs + ) + +def rosidl_typesupport_introspection_cc_library( + name, group, interfaces, includes = [], deps = [], **kwargs +): + include, root = _generated_source_paths(group, "typesupport/introspection_cpp") + + generated_cc_sources = [] + generated_cc_headers = [] + for ifc in interfaces: + parent, basename = _extract_interface_parts(ifc) + generated_cc_sources.append( + "{}/{}/detail/{}__type_support.cpp".format(root, parent, basename)) + template = "{}/{}/detail/{}__rosidl_typesupport_introspection_cpp.hpp" + generated_cc_headers.append(template.format(root, parent, basename)) + generated_sources = generated_cc_sources + generated_cc_headers + + rosidl_generate_genrule( + name = name + "_gen", + generated_sources = generated_sources, + typesupports = ["introspection_cpp"], + package = group, + interfaces = interfaces, + includes = includes, + output_dir = root, + **kwargs + ) + + drake_ros_cc_library( + name = name, + srcs = generated_cc_sources, + hdrs = generated_cc_headers, + includes = [include], + deps = deps + [ + "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", + "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", + "@REPOSITORY_ROOT@:rosidl_typesupport_introspection_cpp_cc", + ], + **kwargs + ) + +def rosidl_typesupport_c_library( + name, group, interfaces, typesupports, includes = [], deps = [], **kwargs +): + include, root = _generated_source_paths(group, "typesupport/c") + + generated_c_sources = [] + visibility_header = "msg/rosidl_typesupport_c__visibility_control.h" + generated_c_headers = ["{}/{}".format(root, visibility_header)] + for ifc in interfaces: + parent, basename = _extract_interface_parts(ifc) + generated_c_sources.append( + "{}/{}/{}__type_support.cpp".format(root, parent, basename)) + generated_sources = generated_c_sources + generated_c_headers + + rosidl_generate_genrule( + name = name + "_gen", + generated_sources = generated_sources, + typesupports = [ + "c[typesupport_implementations:[{}]]" + .format(",".join(typesupports)) + ], + package = group, + interfaces = interfaces, + includes = includes, + output_dir = root, + **kwargs + ) + + if len(typesupports) == 1: + deps = deps + [typesupports.values()[0]] + + drake_ros_cc_library( + name = name, + srcs = generated_c_sources, + hdrs = generated_c_headers, + includes = [include], + deps = deps + [ + "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", + "@REPOSITORY_ROOT@:rosidl_typesupport_c_cc", + "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", + ], + **kwargs + ) + +def rosidl_typesupport_cc_library( + name, group, interfaces, typesupports, includes = [], deps = [], **kwargs +): + include, root = _generated_source_paths(group, "typesupport/cpp") + + generated_cc_sources = [] + for ifc in interfaces: + parent, basename = _extract_interface_parts(ifc) + generated_cc_sources.append( + "{}/{}/{}__type_support.cpp".format(root, parent, basename)) + + rosidl_generate_genrule( + name = name + "_gen", + generated_sources = generated_cc_sources, + typesupports = [ + "cpp[typesupport_implementations:[{}]]" + .format(",".join(typesupports)) + ], + package = group, + interfaces = interfaces, + includes = includes, + output_dir = root, + **kwargs + ) + + if len(typesupports) == 1: + deps = deps + [typesupports.values()[0]] + + drake_ros_cc_library( + name = name, + srcs = generated_cc_sources, + includes = [include], + deps = deps + [ + "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", + "@REPOSITORY_ROOT@:rosidl_runtime_cpp_cc", + "@REPOSITORY_ROOT@:rosidl_typesupport_cpp_cc", + "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", + ], + **kwargs + ) + +def defs(name): + return name + "_defs" + +def cc(name): + return name + "_cc" + +def cc_label(name): + return ":" + cc(name) + +def rosidl_cc_support(name, interfaces, deps, **kwargs): + rosidl_cc_library( + name = cc(name), + group = name, interfaces = interfaces, + includes = [defs(dep) for dep in deps], + deps = [cc(dep) for dep in deps], + **kwargs + ) + + typesupports = {} + + if 'rosidl_typesupport_introspection_cpp' in AVAILABLE_TYPESUPPORTS: + rosidl_typesupport_introspection_cc_library( + name = name + "_typesupport_introspection_cc", + group = name, interfaces = interfaces, + includes = [defs(dep) for dep in deps], + deps = [cc_label(name)] + [cc(dep) for dep in deps], + **kwargs + ) + typesupports['rosidl_typesupport_introspection_cpp'] = \ + ":" + name + "_typesupport_introspection_cc" + + if 'rosidl_typesupport_fastrtps_cpp' in AVAILABLE_TYPESUPPORTS: + rosidl_typesupport_fastrtps_cc_library( + name = name + "_typesupport_fastrtps_cc", + group = name, interfaces = interfaces, + includes = [defs(dep) for dep in deps], + deps = [cc_label(name)] + [cc(dep) for dep in deps], + **kwargs + ) + typesupports['rosidl_typesupport_fastrtps_cpp'] = \ + ":" + name + "_typesupport_fastrtps_cc" + + rosidl_typesupport_cc_library( + name = name + "_typesupport_cc", + typesupports = typesupports, + group = name, interfaces = interfaces, + includes = [defs(dep) for dep in deps], + deps = [cc_label(name)] + [cc(dep) for dep in deps], + **kwargs + ) + +def c(name): + return name + "_c" + +def c_label(name): + return ":" + c(name) + +def py(name): + return name + "_py" + +def py_label(name): + return ":" + py(name) + +def rosidl_py_support(name, interfaces, deps, **kwargs): + rosidl_c_library( + name = c(name), + group = name, interfaces = interfaces, + includes = [defs(dep) for dep in deps], + deps = [c(dep) for dep in deps], + **kwargs + ) + + typesupports = {} + + if "rosidl_typesupport_introspection_c" in AVAILABLE_TYPESUPPORTS: + rosidl_typesupport_introspection_c_library( + name = name + "_typesupport_introspection_c", + group = name, interfaces = interfaces, + includes = [defs(dep) for dep in deps], + deps = [c_label(name)] + [c(dep) for dep in deps], + **kwargs + ) + typesupports["rosidl_typesupport_introspection_c"] = \ + ":" + name + "_typesupport_introspection_c" + + if "rosidl_typesupport_fastrtps_c" in AVAILABLE_TYPESUPPORTS: + rosidl_typesupport_fastrtps_c_library( + name = name + "_typesupport_fastrtps_c", + group = name, interfaces = interfaces, + includes = [defs(dep) for dep in deps], + deps = [c_label(name)] + [c(dep) for dep in deps], + **kwargs + ) + typesupports["rosidl_typesupport_fastrtps_c"] = \ + ":" + name + "_typesupport_fastrtps_c" + + rosidl_typesupport_c_library( + name = name + "_typesupport_c", + typesupports = typesupports, + group = name, interfaces = interfaces, + includes = [defs(dep) for dep in deps], + deps = [c_label(name)] + [c(dep) for dep in deps], + **kwargs + ) + typesupports["rosidl_typesupport_c"] = ":" + name + "_typesupport_c" + + rosidl_py_library( + name = py(name), + typesupports = typesupports, + group = name, interfaces = interfaces, + includes = [defs(dep) for dep in deps], + py_deps = [py(dep) for dep in deps], + c_deps = [c(dep) for dep in deps] + [ + c_label(name), typesupports["rosidl_typesupport_c"] + ], + **kwargs + ) + + +def rosidl_interfaces_group(name, interfaces, deps, **kwargs): + rosidl_definitions_filegroup( + name = defs(name), + group = name, interfaces = interfaces, + includes = [defs(dep) for dep in deps], + **kwargs + ) + + rosidl_cc_support(name, interfaces, deps, **kwargs) + + rosidl_py_support(name, interfaces, deps, **kwargs) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl index 70aaf5239..ec7516269 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -1,8 +1,9 @@ +load("//tools/skylark:execute.bzl", "execute_or_fail") MANIFEST = [ "cmake_tools/server_mode.py", "cmake_tools/__init__.py", - "resources/package_cc_binary_import.bzl.tpl", + "resources/executable_import.bzl.tpl", "resources/package_py_library_with_cc_extensions.bzl.tpl", "resources/package_cc_library.bzl.tpl", "resources/BUILD.prologue.bazel", @@ -11,7 +12,8 @@ MANIFEST = [ "resources/package_alias.bzl.tpl", "resources/package_py_library.bzl.tpl", "resources/package_share_filegroup.bzl.tpl", - "resources/prologue.bzl", + "resources/package_interfaces_filegroup.bzl.tpl", + "ros2bzl/utilities.py", "ros2bzl/sandboxing.py", "ros2bzl/resources.py", @@ -24,30 +26,12 @@ MANIFEST = [ "ros2bzl/scrapping/__init__.py", ] -def _execute_or_fail(repo_ctx, cmd, **kwargs): - exec_result = repo_ctx.execute(cmd, **kwargs) - if exec_result.return_code != 0: - error_message ="'{}' exited with {}".format( - " ".join([str(token) for token in cmd]), - exec_result.return_code - ) - if exec_result.stdout: - error_message += "\n--- captured stdout ---\n" - error_message += exec_result.stdout - if exec_result.stderr: - error_message += "\n--- captured stderr ---\n" - error_message += exec_result.stderr - fail("Failed to setup @{} repository: {}".format( - repo_ctx.name, error_message - )) - return exec_result - def _label(relpath): return Label("//tools/skylark/ros2:" + relpath) def _uuid(repo_ctx): cmd = [repo_ctx.which("python3"), "-c", "import uuid; print(uuid.uuid1())"] - return _execute_or_fail(repo_ctx, cmd, quiet=True).stdout + return execute_or_fail(repo_ctx, cmd, quiet=True).stdout def _impl(repo_ctx): for relpath in MANIFEST: @@ -63,6 +47,14 @@ def _impl(repo_ctx): executable = True ) + repo_ctx.template( + "rosidl.bzl", _label("resources/rosidl.bzl.tpl"), + substitutions = { + "@REPOSITORY_ROOT@": "@{}//".format(repo_ctx.name), + }, + executable = False + ) + generate_tool = repo_ctx.path(_label("generate_repository_files.py")) cmd = ["./setup.sh", str(generate_tool)] for ws in repo_ctx.attr.workspaces: @@ -78,7 +70,7 @@ def _impl(repo_ctx): if repo_ctx.attr.jobs > 0: cmd.extend(["-j", repr(repo_ctx.attr.jobs)]) cmd.append(repo_ctx.name) - _execute_or_fail(repo_ctx, cmd, quiet=True) + execute_or_fail(repo_ctx, cmd, quiet=True) """ Extracts relevant properties from CMake and Python stuff for ROS 2 install @@ -110,20 +102,3 @@ ros2_local_repository = repository_rule( configure = True, implementation = _impl, ) - -def package_share_filegroup(name, share_directories): - native.filegroup( - name = name, - srcs = [path for path in native.glob( - include = ["{}/**".format(dirpath) for dirpath in share_directories], - exclude = [ - "*/cmake/**", - "*/environment/**", - "*/*.sh", - "*/*.bash", - "*/*.dsv", - ] - ) if " " not in path], - # NOTE(hidmic): workaround lack of support for spaces. - # See https://github.com/bazelbuild/bazel/issues/4327. - ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py index 7192e61de..86e1a09d1 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py @@ -1,8 +1,24 @@ +import os + from ament_index_python import get_packages_with_prefixes +from ament_index_python import get_search_paths from ros2bzl.scrapping.metadata import collect_package_metadata +def list_all_executables(): + executables = {} + for prefix in get_search_paths(): + root = os.path.join(prefix, 'bin') + for path in os.listdir(root): + path = os.path.join(root, path) + if os.path.isfile(path) and os.access(path, os.X_OK): + name = os.path.basename(path) + if name not in executables: + executables[name] = path + return list(executables.values()) + + def index_all_packages(): return { name: collect_package_metadata(name, prefix) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py index 1b344b007..f46dc9f45 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py @@ -117,12 +117,17 @@ def collect_ament_cmake_package_properties(name, metadata): ament_prefix_path = os.environ['AMENT_PREFIX_PATH'] cmake_prefix_path += ';' + ament_prefix_path.replace(':', ';') - with cmake_tools.server_mode(project_path) as cmake: - cmake.configure(attributes={'cacheArguments': [ - '-DCMAKE_PREFIX_PATH=' + cmake_prefix_path - ]}, timeout=20, message_callback=print) - cmake.compute(timeout=20, message_callback=print) - codemodel = cmake.codemodel(timeout=5) + try: + with cmake_tools.server_mode(project_path) as cmake: + cmake.configure(attributes={'cacheArguments': [ + '-DCMAKE_PREFIX_PATH="{}"'.format(cmake_prefix_path) + ]}, timeout=20, message_callback=print) + cmake.compute(timeout=20, message_callback=print) + codemodel = cmake.codemodel(timeout=5) + except: + import shutil + shutil.copytree(project_path, 'error_case') + raise configurations = codemodel['configurations'] assert len(configurations) == 1 diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py index a58c5cca6..b4924f7c1 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py @@ -11,14 +11,14 @@ def find_python_package(name): dist = distribution(name) top_level = dist.read_text('top_level.txt') top_level = top_level.rstrip('\n') - return str(dist.locate_file(top_level)) + return str(dist._path), str(dist.locate_file(top_level)) def collect_ament_python_package_properties(name, metadata): - python_package_path = find_python_package(name) - properties = {'python_packages': [python_package_path]} + egg_path, top_level = find_python_package(name) + properties = {'python_packages': [(egg_path, top_level)]} cc_extensions = glob.glob( - '{}/**/*.so'.format(python_package_path), recursive=True + '{}/**/*.so'.format(top_level), recursive=True ) if cc_extensions: cc_extensions_deps = set() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py index 46fb58ab2..3ee00e530 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py @@ -39,6 +39,7 @@ def labels_with(suffix): share_name, share_label = labels_with(suffix='_share') +c_name, _ = labels_with(suffix='_c') cc_name, cc_label = labels_with(suffix='_cc') py_name, py_label = labels_with(suffix='_py') meta_py_name, meta_py_label = labels_with(suffix='_transitively_py') @@ -53,43 +54,19 @@ def configure_package_share_filegroup(name, metadata, sandbox): ] } - -def configure_package_executable_imports(name, metadata, dependencies, extras, sandbox): - if not metadata['executables']: - return [] - - common_data = [] - dependencies = list(dependencies.items()) - dependencies = [(name, metadata)] + dependencies - for dependency_name, dependency_metadata in dependencies: - # TODO(hidmic): use appropriate target based on executable file type - if 'cc' in dependency_metadata['langs']: - common_data.append(cc_label(dependency_name, dependency_metadata)) - if 'py' in dependency_metadata['langs']: - common_data.append(py_label(dependency_name, dependency_metadata)) - elif 'py (transitively)' in dependency_metadata['langs']: - common_data.append(meta_py_label(dependency_name, dependency_metadata)) - - target_prefix = label_name(name, metadata) - for executable in metadata['executables']: - target_name = '{}_{}'.format(target_prefix, os.path.basename(executable)) - data = common_data - if extras and 'data' in extras and target_name in extras['data']: - data = data + extras['data'][target_name] - yield load_resource('package_cc_binary_import.bzl.tpl'), { - 'name': target_name, 'executable': sandbox(executable), 'data': data - } +def configure_package_interfaces_filegroup(name, metadata, sandbox): + return load_resource('package_interfaces_filegroup.bzl.tpl'), { + 'name': name, 'share_directory': sandbox(metadata['share_directory']) + } def configure_package_cc_library(name, metadata, properties, dependencies, extras, sandbox): target_name = cc_name(name, metadata) libraries = [sandbox(library) for library in properties['link_libraries']] include_directories = [sandbox(include) for include in properties['include_directories']] - local_include_directories = [ - os.path.join(include, name) # assumes package abides to REP-122 FHS layout - for include in include_directories - if not os.path.isabs(include) - ] + local_includes = [include for include in include_directories if not os.path.isabs(include)] + # Assume package abides to REP-122 FHS layout + headers = [os.path.join(include, name) for include in local_includes] # Push remaining nonlocal includes through compiler options copts = ['-isystem ' + include for include in include_directories if os.path.isabs(include)] copts.extend(properties['compile_flags']) @@ -141,7 +118,8 @@ def configure_package_cc_library(name, metadata, properties, dependencies, extra return load_resource('package_cc_library.bzl.tpl'), { 'name': target_name, 'srcs': libraries, - 'includes': local_include_directories, + 'headers': headers, + 'includes': local_includes, 'copts': copts, 'defines': defines, 'linkopts': linkopts, @@ -165,8 +143,9 @@ def configure_package_meta_py_library(name, metadata, dependencies): def configure_package_py_library(name, metadata, properties, dependencies, extras, sandbox): target_name = py_name(name, metadata) - packages = [sandbox(pkg) for pkg in properties['python_packages']] - imports = [os.path.dirname(pkg) for pkg in packages] + eggs = [sandbox(egg_path) for egg_path, _ in properties['python_packages']] + tops = [sandbox(top_level) for _, top_level in properties['python_packages']] + imports = [os.path.dirname(egg) for egg in eggs] deps = [] for dependency_name, dependency_metadata in dependencies.items(): @@ -177,7 +156,8 @@ def configure_package_py_library(name, metadata, properties, dependencies, extra config = { 'name': target_name, - 'packages': packages, + 'tops': tops, + 'eggs': eggs, 'imports': imports, 'deps': deps } @@ -227,3 +207,49 @@ def configure_package_alias(name, target): return load_resource('package_alias.bzl.tpl'), { 'name': name, 'actual': ':' + target } + + +def configure_package_c_library_alias(name, metadata): + return load_resource('package_alias.bzl.tpl'), { + 'name': c_name(name, metadata), + 'actual': cc_label(name, metadata) + } + + +def configure_executable_imports( + executables, dependencies, sandbox, extras=None, prefix=None +): + deps = [] + common_data = [] + for dependency_name, dependency_metadata in dependencies.items(): + # TODO(hidmic): use appropriate target based on executable file type + if 'cc' in dependency_metadata['langs']: + common_data.append(cc_label(dependency_name, dependency_metadata)) + if 'py' in dependency_metadata['langs']: + deps.append(py_label(dependency_name, dependency_metadata)) + elif 'py (transitively)' in dependency_metadata['langs']: + common_data.append(meta_py_label(dependency_name, dependency_metadata)) + + for executable in executables: + target_name = os.path.basename(executable) + if prefix: + target_name = prefix + '_' + target_name + data = common_data + if extras and 'data' in extras and target_name in extras['data']: + data = data + extras['data'][target_name] + yield load_resource('executable_import.bzl.tpl'), { + 'name': target_name, + 'executable': sandbox(executable), + 'data': data, 'deps': deps, + } + + +def configure_package_executable_imports( + name, metadata, dependencies, sandbox, extras=None +): + dependencies = dict(dependencies) + dependencies[name] = metadata + yield from configure_executable_imports( + metadata['executables'], dependencies, sandbox, + extras=extras, prefix=label_name(name, metadata) + ) From 86a14551c6b11acf6c9679539c3fa162e42f2808 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 4 Jun 2021 12:40:57 -0300 Subject: [PATCH 013/107] Add message generation example packages Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/apps/BUILD.bazel | 13 +++++++++++++ drake_ros_bazel_installed/apps/msg/Stats.msg | 4 ++++ drake_ros_bazel_installed/apps/srv/QueryStats.srv | 2 ++ drake_ros_bazel_installed/common/BUILD.bazel | 14 ++++++++++++++ drake_ros_bazel_installed/common/action/Do.action | 7 +++++++ drake_ros_bazel_installed/common/msg/Status.msg | 3 +++ drake_ros_bazel_installed/common/srv/Query.srv | 3 +++ 7 files changed, 46 insertions(+) create mode 100644 drake_ros_bazel_installed/apps/BUILD.bazel create mode 100644 drake_ros_bazel_installed/apps/msg/Stats.msg create mode 100644 drake_ros_bazel_installed/apps/srv/QueryStats.srv create mode 100644 drake_ros_bazel_installed/common/BUILD.bazel create mode 100644 drake_ros_bazel_installed/common/action/Do.action create mode 100644 drake_ros_bazel_installed/common/msg/Status.msg create mode 100644 drake_ros_bazel_installed/common/srv/Query.srv diff --git a/drake_ros_bazel_installed/apps/BUILD.bazel b/drake_ros_bazel_installed/apps/BUILD.bazel new file mode 100644 index 000000000..ce8653020 --- /dev/null +++ b/drake_ros_bazel_installed/apps/BUILD.bazel @@ -0,0 +1,13 @@ +load("@ros2//:rosidl.bzl", "rosidl_interfaces_group") + +rosidl_interfaces_group( + name = "apps_msgs", + interfaces = [ + "msg/Stats.msg", + "srv/QueryStats.srv", + ], + deps = [ + "//common:common_msgs", + "@ros2//:builtin_interfaces", + ], +) diff --git a/drake_ros_bazel_installed/apps/msg/Stats.msg b/drake_ros_bazel_installed/apps/msg/Stats.msg new file mode 100644 index 000000000..2dd905423 --- /dev/null +++ b/drake_ros_bazel_installed/apps/msg/Stats.msg @@ -0,0 +1,4 @@ +builtin_interfaces/Time stamp +common_msgs/Status status +float64[100] memory_usage +float64[100] cpu_load diff --git a/drake_ros_bazel_installed/apps/srv/QueryStats.srv b/drake_ros_bazel_installed/apps/srv/QueryStats.srv new file mode 100644 index 000000000..f21c7c9f3 --- /dev/null +++ b/drake_ros_bazel_installed/apps/srv/QueryStats.srv @@ -0,0 +1,2 @@ +--- +Stats stats diff --git a/drake_ros_bazel_installed/common/BUILD.bazel b/drake_ros_bazel_installed/common/BUILD.bazel new file mode 100644 index 000000000..a75862dec --- /dev/null +++ b/drake_ros_bazel_installed/common/BUILD.bazel @@ -0,0 +1,14 @@ +load("@ros2//:rosidl.bzl", "rosidl_interfaces_group") + +rosidl_interfaces_group( + name = "common_msgs", + interfaces = [ + "msg/Status.msg", + "srv/Query.srv", + "action/Do.action", + ], + deps = [ + "@ros2//:action_msgs", + "@ros2//:builtin_interfaces", + ], +) diff --git a/drake_ros_bazel_installed/common/action/Do.action b/drake_ros_bazel_installed/common/action/Do.action new file mode 100644 index 000000000..0fe8346c8 --- /dev/null +++ b/drake_ros_bazel_installed/common/action/Do.action @@ -0,0 +1,7 @@ +string action +builtin_interfaces/Duration timeout +--- +bool ok +string reason +--- +string feedback diff --git a/drake_ros_bazel_installed/common/msg/Status.msg b/drake_ros_bazel_installed/common/msg/Status.msg new file mode 100644 index 000000000..e2876cd80 --- /dev/null +++ b/drake_ros_bazel_installed/common/msg/Status.msg @@ -0,0 +1,3 @@ +builtin_interfaces/Time stamp +uint64 sequence_id +string message diff --git a/drake_ros_bazel_installed/common/srv/Query.srv b/drake_ros_bazel_installed/common/srv/Query.srv new file mode 100644 index 000000000..974c97c04 --- /dev/null +++ b/drake_ros_bazel_installed/common/srv/Query.srv @@ -0,0 +1,3 @@ +string query +--- +string[] results From 88cc886805a33d9fa5b1a035d3ffde55b8d4cf15 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 4 Jun 2021 12:41:39 -0300 Subject: [PATCH 014/107] Update ros2 repository configuration Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl | 1 + 1 file changed, 1 insertion(+) diff --git a/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl b/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl index c97781d2b..fd0e9d975 100644 --- a/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl +++ b/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl @@ -11,6 +11,7 @@ def ros2_repository(name, overlays = []): "geometry_msgs", "visualization_msgs", "rosidl_default_generators", + "rosidl_generator_cpp", "rcpputils", "rcutils", "rclcpp", From a9167a45b63fb44f6da60a9287279bafffb793e2 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 18 Jun 2021 15:31:19 -0300 Subject: [PATCH 015/107] Refactor ROS build infrastructure - Fix runtime environment (to not break overlays) - Scrap CMake-only packages in overlays - Fix rosidl generated filename deduction - Restructure ROS Starlark tooling Signed-off-by: Michel Hidalgo --- .../tools/skylark/dload.bzl | 76 ++----- .../tools/skylark/drake_ros_cc.bzl | 169 -------------- .../skylark/ros2/cmake_tools/__init__.py | 2 +- .../skylark/ros2/cmake_tools/packages.py | 15 ++ .../skylark/ros2/{packages.bzl => common.bzl} | 5 +- .../skylark/ros2/generate_repository_files.py | 147 ++++++------ .../ros2/resources/BUILD.prologue.bazel | 9 - .../ros2/resources/bazel/cc_tools.bzl.tpl | 113 ++++++++++ .../ros2/resources/bazel/distro.bzl.tpl | 7 + .../resources/bazel/py_tools.bzl.tpl} | 73 ++---- .../rosidl_tools.bzl.tpl} | 150 +++++++++---- .../snippets/overlay_executable.bazel.tpl | 6 + .../bazel/snippets/package_alias.bazel.tpl | 4 + .../snippets/package_cc_library.bazel.tpl | 11 + ..._cc_library_with_runtime_environ.bazel.tpl | 19 ++ .../package_interfaces_filegroup.bazel.tpl | 4 + .../package_meta_py_library.bazel.tpl | 4 + .../snippets/package_py_library.bazel.tpl | 14 ++ .../package_share_filegroup.bazel.tpl | 4 + .../bazel/snippets/prologue.bazel.tpl | 8 + .../{ => cmake}/ament_cmake_CMakeLists.txt.in | 2 + .../skylark/ros2/resources/distro.bzl.tpl | 1 - .../ros2/resources/executable_import.bzl.tpl | 6 - .../ros2/resources/package_alias.bzl.tpl | 4 - .../ros2/resources/package_cc_library.bzl.tpl | 12 - .../package_interfaces_filegroup.bzl.tpl | 4 - .../resources/package_meta_py_library.bzl.tpl | 4 - .../ros2/resources/package_py_library.bzl.tpl | 15 -- ...kage_py_library_with_cc_extensions.bzl.tpl | 15 -- .../resources/package_share_filegroup.bzl.tpl | 4 - .../ros2/resources/{ => shell}/setup.sh.in | 1 - .../tools/skylark/ros2/ros2.bzl | 36 ++- .../tools/skylark/ros2/ros2bzl/resources.py | 39 ---- .../ros2/ros2bzl/scrapping/__init__.py | 71 +++++- .../ros2/ros2bzl/scrapping/ament_cmake.py | 20 +- .../ros2/ros2bzl/scrapping/ament_python.py | 6 +- .../ros2/ros2bzl/scrapping/metadata.py | 13 +- .../tools/skylark/ros2/ros2bzl/templates.py | 211 +++++++++++------- .../tools/skylark/ros2/ros2bzl/utilities.py | 5 +- 39 files changed, 669 insertions(+), 640 deletions(-) delete mode 100644 drake_ros_bazel_installed/tools/skylark/drake_ros_cc.bzl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/packages.py rename drake_ros_bazel_installed/tools/skylark/ros2/{packages.bzl => common.bzl} (88%) delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/BUILD.prologue.bazel create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/cc_tools.bzl.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/distro.bzl.tpl rename drake_ros_bazel_installed/tools/skylark/{drake_ros_py.bzl => ros2/resources/bazel/py_tools.bzl.tpl} (62%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{rosidl.bzl.tpl => bazel/rosidl_tools.bzl.tpl} (87%) create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/overlay_executable.bazel.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_alias.bazel.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_cc_library.bazel.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_cc_library_with_runtime_environ.bazel.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_interfaces_filegroup.bazel.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_meta_py_library.bazel.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_py_library.bazel.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_share_filegroup.bazel.tpl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/prologue.bazel.tpl rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{ => cmake}/ament_cmake_CMakeLists.txt.in (90%) delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/distro.bzl.tpl delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/executable_import.bzl.tpl delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/package_alias.bzl.tpl delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_library.bzl.tpl delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/package_interfaces_filegroup.bzl.tpl delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/package_meta_py_library.bzl.tpl delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library.bzl.tpl delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library_with_cc_extensions.bzl.tpl delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/package_share_filegroup.bzl.tpl rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{ => shell}/setup.sh.in (82%) diff --git a/drake_ros_bazel_installed/tools/skylark/dload.bzl b/drake_ros_bazel_installed/tools/skylark/dload.bzl index 8b157c6e1..77d29536e 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload.bzl @@ -40,39 +40,16 @@ def unique(input_list): def merge_runtime_environment_changes(base, head): """ Merges runtime environment head changes into base. - - Merging runtime environment actions other than 'path-prepend' is not - allowed as results would silently vary depending on build order. """ for envvar, head_action in head.items(): - if envvar not in base: - base[envvar] = head_action - continue - - base_action = base[envvar] - - base_action_type = base_action[0] - head_action_type = head_action[0] - - if "replace" in base_action_type or "replace" in head_action_type: - tpl = "Got '{}' and '{}' actions, results depend on build order" - fail(msg = tpl.format(base_action_type, head_action_type)) - - if base_action_type != head_action_type or \ - base_action_type != "path-prepend": - tpl = "Expected 'path-prepend' actions, got '{}' and '{}'" - fail(msg = tpl.format(base_action_type, head_action_type)) - - merged_action_type = base_action_type - - base_action_args = base_action[1:] - head_action_args = head_action[1:] - merged_action_args = unique(base_action_args + head_action_args) - - base[envvar] = [merged_action_type] + merged_action_args + if envvar in base: + base_action = base[envvar] + tpl = "Got '{}' and '{}' actions on {}, " + tpl += "results will depend on dependency order" + fail(msg = tpl.format(base_action[0], head_action[0], envvar)) + base[envvar] = head_action return base - def merge_runtime_info(base_info, head_info): """ Merges 'head' runtime information into 'base' runtime information, @@ -92,28 +69,6 @@ def collect_runtime_info(targets): merge_runtime_info(runtime_info, target[RuntimeInfo]) return runtime_info -def get_runtime_environment_changes(runtime_info): - """ - Returns changes to be applied to the runtime environment as - an (envvars, actions) tuple. - """ - actions = [] - for action in runtime_info.env_changes.values(): - action_type = action[0] - if action_type == "path-prepend": - action_args = action[1:] - common_action_args = [] - important_action_args = [] - for path in action_args: - if path.endswith("!"): - important_action_args.append(path[:-1]) - else: - common_action_args.append(path) - action_args = unique(important_action_args + common_action_args) - action = [action_type] + action_args - actions.append(action) - return list(runtime_info.env_changes.keys()), actions - def normpath(path): """ Normalizes a path by removing redundant separators and up-level references. @@ -142,6 +97,7 @@ def get_dload_shim_attributes(): ), "data": attr.label_list(allow_files = True), "deps": attr.label_list(), + "env": attr.string_list_dict(), } def do_dload_shim(ctx, template, to_list): @@ -166,12 +122,16 @@ def do_dload_shim(ctx, template, to_list): """ executable_file = ctx.executable.target - runtime_info = merge_runtime_info( - collect_runtime_info(ctx.attr.data), - collect_runtime_info(ctx.attr.deps) - ) + runtime_info = RuntimeInfo(env_changes = { + MAGIC_VARIABLES.get(name, default=name): + parse_runtime_environment_action(ctx, action) + for name, action in ctx.attr.env.items() + }) + merge_runtime_info(runtime_info, collect_runtime_info(ctx.attr.data)) + merge_runtime_info(runtime_info, collect_runtime_info(ctx.attr.deps)) - envvars, actions = get_runtime_environment_changes(runtime_info) + envvars = runtime_info.env_changes.keys() + actions = runtime_info.env_changes.values() shim_content = template.format( # Deal with usage in external workspaces' BUILD.bazel files @@ -238,7 +198,7 @@ def get_dload_aware_target_attributes(): "base": attr.label(mandatory = True), "data": attr.label_list(allow_files = True), "deps": attr.label_list(), - "runenv": attr.string_list_dict(), + "env": attr.string_list_dict(), } def do_dload_aware_target(ctx): @@ -264,7 +224,7 @@ def do_dload_aware_target(ctx): runtime_info = RuntimeInfo(env_changes = { MAGIC_VARIABLES.get(name, default=name): parse_runtime_environment_action(ctx, action) - for name, action in ctx.attr.runenv.items() + for name, action in ctx.attr.env.items() }) merge_runtime_info(runtime_info, collect_runtime_info(ctx.attr.data)) merge_runtime_info(runtime_info, collect_runtime_info(ctx.attr.deps)) diff --git a/drake_ros_bazel_installed/tools/skylark/drake_ros_cc.bzl b/drake_ros_bazel_installed/tools/skylark/drake_ros_cc.bzl deleted file mode 100644 index 847f36cbf..000000000 --- a/drake_ros_bazel_installed/tools/skylark/drake_ros_cc.bzl +++ /dev/null @@ -1,169 +0,0 @@ -# -*- python -*- - -load( - "//tools/skylark:dload_cc.bzl", - "dload_aware_cc_library", - "dload_cc_shim", -) - -def drake_ros_cc_library(name, deps = [], data = [], runenv = {}, - testonly = False, visibility = None, **kwargs): - """ - Builds a C/C++ library and carries runtime information, if any. - - Equivalent to the cc_library() rule. - """ - library = "_" + name - native.cc_library( - name = library, - deps = deps, - data = data, - testonly = testonly, - visibility = visibility, - **kwargs - ) - dload_aware_cc_library( - name = name, - base = ":" + library, - data = data, - deps = [":" + library] + deps, - runenv = runenv, - testonly = testonly, - visibility = visibility, - ) - -def drake_ros_cc_binary_import(name, executable, data = [], deps = [], tags = [], - testonly = 0, visibility = None, **kwargs): - """ - Imports a pre-compiled C/C++ executable, picking up runtime information - in dependencies. - - Args: - executable: executable file - - Similar to the cc_binary() rule. - """ - shim = name + "_shim.cc" - dload_cc_shim( - name = shim, - target = executable, - data = data, - deps = deps, - testonly = testonly, - visibility = visibility, - ) - native.cc_binary( - name = name, - srcs = [shim], - data = [executable] + data, - deps = [ - "@bazel_tools//tools/cpp/runfiles", - ] + deps, - tags = ["nolint"] + tags, - testonly = testonly, - visibility = visibility, - **kwargs - ) - -def drake_ros_cc_binary(name, srcs = [], data = [], deps = [], linkshared = None, tags = [], - testonly = False, visibility = None, **kwargs): - """ - Builds a C/C++ binary, picking up runtime information in dependencies. - - Equivalent to the cc_binary() rule. - - Propagation of runtime information is disabled if linkshared is True. - """ - if linkshared: - native.cc_binary( - name = name, - srcs = srcs, - data = data, - deps = deps, - linkshared = linkshared, - visibility = visibility, - testonly = testonly, - tags = tags, - **kwargs - ) - return - binary = "_" + name - native.cc_binary( - name = binary, - srcs = srcs, - data = data, - deps = deps, - linkshared = linkshared, - visibility = visibility, - testonly = testonly, - tags = tags, - **kwargs - ) - - shim = binary + "_shim.cc" - dload_cc_shim( - name = shim, - target = ":" + binary, - data = data, - deps = deps, - testonly = testonly, - visibility = visibility, - ) - native.cc_binary( - name = name, - srcs = [shim], - data = [":" + binary], - deps = [ - "@bazel_tools//tools/cpp/runfiles", - ], - tags = ["nolint"] + tags, - visibility = visibility, - testonly = testonly, - **kwargs - ) - -def drake_ros_cc_test(name, srcs = [], data = [], deps = [], - testonly = 1, visibility = [], tags = [], - **kwargs): - """ - Builds C/C++ test, picking up runtime information in dependencies. - - Equivalent to the cc_test() rule. - """ - - if not srcs: - srcs = ["test/%s.cc" % name] - - test = "_" + name - - native.cc_test( - name = test, - srcs = srcs, - data = data, - deps = deps, - tags = ["manual"] + tags, - testonly = testonly, - visibility = visibility, - **kwargs - ) - shim = test + "_shim.cc" - dload_cc_shim( - name = shim, - target = ":" + test, - data = data, - deps = deps, - testonly = testonly, - visibility = visibility, - ) - native.cc_test( - name = name, - srcs = [shim], - data = [":" + test], - deps = [ - "@bazel_tools//tools/cpp/runfiles", - ], - tags = ["nolint"] + tags, - testonly = testonly, - visibility = visibility, - **kwargs - ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/__init__.py b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/__init__.py index e9ff12f1e..8cf624e5d 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/__init__.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/__init__.py @@ -3,7 +3,7 @@ import subprocess from .server_mode import server_mode - +from .packages import get_packages_with_prefixes def configure_file(src, dest, subs): with open(src, 'r') as f: diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/packages.py b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/packages.py new file mode 100644 index 000000000..c4289e4b6 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/packages.py @@ -0,0 +1,15 @@ +import glob +import os + + +def get_packages_with_prefixes(prefixes): + suffixes = 'Config.cmake', '-config.cmake' + return { + os.path.basename(path)[:-len(suffix)]: prefix + for prefix in prefixes for suffix in suffixes + for path in glob.glob( + '{}/*/**/*{}'.format( + prefix, suffix), + recursive=True + ) + } diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/packages.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/common.bzl similarity index 88% rename from drake_ros_bazel_installed/tools/skylark/ros2/packages.bzl rename to drake_ros_bazel_installed/tools/skylark/ros2/common.bzl index b50bec7f0..9bfe7bed5 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/packages.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/common.bzl @@ -1,5 +1,5 @@ -def package_share_filegroup(name, share_directories): +def share_filegroup(name, share_directories): native.filegroup( name = name, srcs = [path for path in native.glob( @@ -16,8 +16,7 @@ def package_share_filegroup(name, share_directories): # See https://github.com/bazelbuild/bazel/issues/4327. ) - -def package_interfaces_filegroup(name, share_directory): +def interfaces_filegroup(name, share_directory): native.filegroup( name = name + "_defs", srcs = native.glob(include = [ diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py index eec8e05be..9d5628a8a 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -5,21 +5,21 @@ import os import sys -from multiprocessing.dummy import Pool import xml.etree.ElementTree as ET import toposort sys.path.insert(0, os.path.dirname(__file__)) # noqa -from ros2bzl.scrapping import index_all_packages -from ros2bzl.scrapping import list_all_executables -from ros2bzl.scrapping import build_dependency_graph +from ros2bzl.scrapping import load_distribution from ros2bzl.scrapping.ament_cmake import collect_ament_cmake_package_properties from ros2bzl.scrapping.ament_cmake import collect_ament_cmake_package_direct_properties +from ros2bzl.scrapping.ament_cmake import precache_ament_cmake_properties from ros2bzl.scrapping.ament_python import collect_ament_python_package_direct_properties from ros2bzl.scrapping.ament_python import PackageNotFoundError +from ros2bzl.templates import configure_cc_tools +from ros2bzl.templates import configure_distro from ros2bzl.templates import configure_executable_imports from ros2bzl.templates import configure_package_meta_py_library from ros2bzl.templates import configure_package_alias @@ -29,9 +29,11 @@ from ros2bzl.templates import configure_package_py_library from ros2bzl.templates import configure_package_share_filegroup from ros2bzl.templates import configure_package_interfaces_filegroup +from ros2bzl.templates import configure_py_tools +from ros2bzl.templates import configure_prologue +from ros2bzl.templates import configure_rosidl_tools from ros2bzl.resources import load_resource -from ros2bzl.resources import setup_underlay import ros2bzl.sandboxing as sandboxing @@ -86,69 +88,78 @@ def parse_arguments(): def generate_distro_file(packages): - typesupport_groups = [ - 'rosidl_typesupport_c_packages', - 'rosidl_typesupport_cpp_packages' - ] with open('distro.bzl', 'w') as fd: - fd.write(load_resource('distro.bzl.tpl').format( - available_typesupports=[ - name for name, metadata in packages.items() if any( - group in typesupport_groups for group in metadata['groups'] - ) - ] - ) + '\n') + template, config = configure_distro(packages) + fd.write(interpolate(template, config) + '\n') + + +def generate_cc_tools_file(repo_name): + with open('cc_tools.bzl', 'w') as fd: + template, config = configure_cc_tools(repo_name) + fd.write(interpolate(template, config) + '\n') + + +def generate_py_tools_file(repo_name): + with open('py_tools.bzl', 'w') as fd: + template, config = configure_py_tools(repo_name) + fd.write(interpolate(template, config) + '\n') -def generate_build_file( - packages, executables, dependency_graph, cache, extras, sandbox -): +def generate_rosidl_tools_file(repo_name): + with open('rosidl_tools.bzl', 'w') as fd: + template, config = configure_rosidl_tools(repo_name) + fd.write(interpolate(template, config) + '\n') + + +def generate_build_file(repo_name, distro, cache, extras, sandbox): rmw_implementation_packages = { - name: metadata for name, metadata in packages.items() - if 'rmw_implementation_packages' in metadata['groups'] + name: metadata for name, metadata in distro['packages'].items() + if 'rmw_implementation_packages' in metadata.get('groups', []) } with open('BUILD.bazel', 'w') as fd: - fd.write(load_resource('BUILD.prologue.bazel') + '\n') + template, config = configure_prologue(repo_name) + fd.write(interpolate(template, config) + '\n') - for name in toposort.toposort_flatten(dependency_graph): - metadata = packages[name] + for name in toposort.toposort_flatten(distro['dependency_graph']): + metadata = distro['packages'][name] dependencies = { - dependency_name: packages[dependency_name] - for dependency_name in dependency_graph[name] + dependency_name: distro['packages'][dependency_name] + for dependency_name in distro['dependency_graph'][name] } targets = [] - template, config = \ - configure_package_share_filegroup(name, metadata, sandbox) - fd.write(interpolate(template, config) + '\n') + if 'share_directory' in metadata: + _, template, config = \ + configure_package_share_filegroup(name, metadata, sandbox) + fd.write(interpolate(template, config) + '\n') - if 'rosidl_interface_packages' in metadata['groups']: - template, config = \ + if 'rosidl_interface_packages' in metadata.get('groups', []): + label, template, config = \ configure_package_interfaces_filegroup(name, metadata, sandbox) fd.write(interpolate(template, config) + '\n') - targets.append(config['name']) + targets.append(label) - if metadata['build_type'] == 'ament_cmake': + if 'cmake' in metadata.get('build_type'): properties = collect_ament_cmake_package_direct_properties( name, metadata, dependencies, cache ) - template, config = configure_package_cc_library( + label, template, config = configure_package_cc_library( name, metadata, properties, dependencies, extras, sandbox ) if any(properties.values()): - targets.append(config['name']) + targets.append(label) fd.write(interpolate(template, config) + '\n') - if 'rosidl_interface_packages' in metadata['groups']: + if 'rosidl_interface_packages' in metadata.get('groups', []): # Alias C++ library as C library for interface packages # as their headers and artifacts cannot be discriminated. - template, config = \ + _, template, config = \ configure_package_c_library_alias(name, metadata) fd.write(interpolate(template, config) + '\n') @@ -159,80 +170,68 @@ def generate_build_file( name, metadata, dependencies, cache ) # Add 'py' as language if not there. + if 'langs' not in metadata: + metadata['langs'] = set() metadata['langs'].add('py') except PackageNotFoundError: - if any('py' in metadata['langs'] for metadata in dependencies.values()): + if any('py' in metadata.get('langs', []) for metadata in dependencies.values()): metadata['langs'].add('py (transitively)') # Dependencies still need to be propagated. - template, config = \ + _, template, config = \ configure_package_meta_py_library(name, metadata, dependencies) fd.write(interpolate(template, config) + '\n') properties = {} if properties: - template, config = configure_package_py_library( + label, template, config = configure_package_py_library( name, metadata, properties, dependencies, extras, sandbox ) fd.write(interpolate(template, config) + '\n') - targets.append(config['name']) + targets.append(label) if len(targets) == 1 and targets[0] != name: - template, config = configure_package_alias(name, targets[0]) + _, template, config = configure_package_alias(name, targets[0]) fd.write(interpolate(template, config) + '\n') - if metadata['executables']: + if metadata.get('executables'): dependencies.update(rmw_implementation_packages) - for template, config in configure_package_executable_imports( + for _, template, config in configure_package_executable_imports( name, metadata, dependencies, sandbox, extras=extras ): fd.write(interpolate(template, config) + '\n') - for template, config in configure_executable_imports( - executables, packages, sandbox, extras=extras + for _, template, config in configure_executable_imports( + distro['executables'], distro['packages'], sandbox, extras=extras ): fd.write(interpolate(template, config) + '\n') -def precache_ament_cmake_properties(packages, jobs=None): - ament_cmake_packages = { - name: metadata - for name, metadata in packages.items() - if metadata['build_type'] == 'ament_cmake' - } - with Pool(jobs) as pool: - return dict(zip( - ament_cmake_packages.keys(), pool.starmap( - collect_ament_cmake_package_properties, - ament_cmake_packages.items() - ) - )) - - def main(): args = parse_arguments() - executables = list_all_executables() - - packages, dependency_graph = build_dependency_graph( - index_all_packages(), + distro = load_distribution( + args.sandbox, set(args.include_packages), - set(args.exclude_packages) - ) + set(args.exclude_packages)) cache = { - 'ament_cmake': precache_ament_cmake_properties(packages, jobs=args.jobs) + 'ament_cmake': precache_ament_cmake_properties( + distro['packages'], jobs=args.jobs + ) } generate_build_file( - packages, executables, dependency_graph, cache, args.extras, args.sandbox) + args.repository_name, distro, + cache, args.extras, args.sandbox) + + generate_distro_file(distro) + + generate_cc_tools_file(args.repository_name) - generate_distro_file(packages) + generate_py_tools_file(args.repository_name) - for name, metadata in packages.items(): - # For downstream repositories to use - metadata['bazel_workspace'] = args.repository_name - setup_underlay(packages, dependency_graph, cache, args.sandbox) + generate_rosidl_tools_file(args.repository_name) if __name__ == '__main__': diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/BUILD.prologue.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/resources/BUILD.prologue.bazel deleted file mode 100644 index 338a2e148..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/BUILD.prologue.bazel +++ /dev/null @@ -1,9 +0,0 @@ -# -*- python -*- - -package(default_visibility = ["//visibility:public"]) - -load("@drake_ros//tools/skylark:drake_ros_cc.bzl", "drake_ros_cc_library") -load("@drake_ros//tools/skylark:drake_ros_py.bzl", "drake_ros_py_executable_import") -load("@drake_ros//tools/skylark:drake_ros_py.bzl", "drake_ros_py_library") -load("@drake_ros//tools/skylark/ros2:packages.bzl", "package_share_filegroup") -load("@drake_ros//tools/skylark/ros2:packages.bzl", "package_interfaces_filegroup") diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/cc_tools.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/cc_tools.bzl.tpl new file mode 100644 index 000000000..b4da4930d --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/cc_tools.bzl.tpl @@ -0,0 +1,113 @@ +# -*- python -*- + +load("@REPOSITORY_ROOT@:distro.bzl", "RUNTIME_ENVIRONMENT") +load("@drake_ros//tools/skylark:dload_cc.bzl", "dload_cc_shim") + +def ros_cc_binary( + name, srcs = [], data = [], deps = [], linkshared = None, + tags = [], testonly = False, visibility = None, **kwargs +): + """ + Builds a C/C++ binary, injecting the runtime environment + specific to this ROS overlay. + + Equivalent to the cc_binary() rule. + """ + if linkshared: + native.cc_binary( + name = name, + srcs = srcs, + data = data, + deps = deps, + linkshared = linkshared, + visibility = visibility, + testonly = testonly, + tags = tags, + **kwargs + ) + return + binary = "_" + name + native.cc_binary( + name = binary, + srcs = srcs, + data = data, + deps = deps, + linkshared = linkshared, + visibility = visibility, + testonly = testonly, + tags = tags, + **kwargs + ) + + shim = binary + "_shim.cc" + dload_cc_shim( + name = shim, + target = ":" + binary, + env = RUNTIME_ENVIRONMENT, + data = data, + deps = deps, + testonly = testonly, + visibility = visibility, + ) + native.cc_binary( + name = name, + srcs = [shim], + data = [":" + binary], + deps = [ + "@bazel_tools//tools/cpp/runfiles", + ], + tags = ["nolint"] + tags, + visibility = visibility, + testonly = testonly, + **kwargs + ) + +def ros_cc_test( + name, srcs = [], data = [], deps = [], + testonly = 1, visibility = [], tags = [], + **kwargs +): + """ + Builds C/C++ test, injecting the runtime environment + specific to this ROS overlay. + + Equivalent to the cc_test() rule. + """ + + if not srcs: + srcs = ["test/%s.cc" % name] + + test = "_" + name + + native.cc_test( + name = test, + srcs = srcs, + data = data, + deps = deps, + tags = ["manual"] + tags, + testonly = testonly, + visibility = visibility, + **kwargs + ) + shim = test + "_shim.cc" + dload_cc_shim( + name = shim, + target = ":" + test, + env = RUNTIME_ENVIRONMENT, + data = data, + deps = deps, + testonly = testonly, + visibility = visibility, + ) + native.cc_test( + name = name, + srcs = [shim], + data = [":" + test], + deps = [ + "@bazel_tools//tools/cpp/runfiles", + ], + tags = ["nolint"] + tags, + testonly = testonly, + visibility = visibility, + **kwargs + ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/distro.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/distro.bzl.tpl new file mode 100644 index 000000000..3f451535f --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/distro.bzl.tpl @@ -0,0 +1,7 @@ +AVAILABLE_TYPESUPPORTS = @AVAILABLE_TYPESUPPORTS@ + +# Prepend full paths to not break workspace overlays +RUNTIME_ENVIRONMENT = { + "AMENT_PREFIX_PATH": ["path-prepend"] + @AMENT_PREFIX_PATH@, + "${LOAD_PATH}": ["path-prepend"] + @LOAD_PATH@, +} diff --git a/drake_ros_bazel_installed/tools/skylark/drake_ros_py.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl similarity index 62% rename from drake_ros_bazel_installed/tools/skylark/drake_ros_py.bzl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl index b842f5ffd..cefb520cb 100644 --- a/drake_ros_bazel_installed/tools/skylark/drake_ros_py.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl @@ -1,61 +1,27 @@ # -*- python -*- -# vi: set ft=python : -load( - "//tools/skylark:dload_py.bzl", - "dload_aware_py_library", - "dload_py_shim", -) +load("@REPOSITORY_ROOT@:distro.bzl", "RUNTIME_ENVIRONMENT") +load("@drake_ros//tools/skylark:dload_py.bzl", "dload_py_shim") -def drake_ros_py_library(name, srcs = [], main = None, imports = [], - srcs_version = None, deps = [], data = [], - runenv = {}, testonly = 0, visibility = None, - **kwargs): - """ - Adds a Python library and carries runtime information, if any. - - Equivalent to the py_library() rule. - """ - library_name = "_" + name - native.py_library( - name = library_name, - srcs = srcs, - main = main, - imports = imports, - srcs_version = srcs_version, - deps = deps, - data = data, - testonly = testonly, - visibility = visibility, - **kwargs - ) - dload_aware_py_library( - name = name, - base = ":" + library_name, - data = data, - deps = [":" + library_name] + deps, - runenv = runenv, - testonly = testonly, - visibility = visibility, - ) - -def drake_ros_py_executable_import( +def ros_py_import( name, executable = [], imports = [], args = [], data = [], deps = [], tags = [], testonly = 0, visibility = None, **kwargs ): """ - Imports an picking up runtime information in dependencies. + Imports an executable, injecting the runtime environment + specific to this ROS overlay. Args: executable: executable file - Similar to the py_binary() rule. + Akin to the cc_import() rule. """ shim = name + "_shim.py" dload_py_shim( name = shim, target = executable, + env = RUNTIME_ENVIRONMENT, data = data, deps = deps, testonly = testonly, @@ -77,11 +43,14 @@ def drake_ros_py_executable_import( **kwargs ) -def drake_ros_py_binary(name, srcs = [], main = None, imports = [], args = [], - srcs_version = None, data = [], deps = [], tags = [], - testonly = 0, visibility = None, **kwargs): +def ros_py_binary( + name, srcs = [], main = None, imports = [], args = [], + srcs_version = None, data = [], deps = [], tags = [], + testonly = 0, visibility = None, **kwargs +): """ - Adds a Python executable, picking up runtime information in dependencies. + Adds a Python executable, injecting the runtime environment + specific to this ROS overlay. Equivalent to the py_library() rule. """ @@ -105,6 +74,7 @@ def drake_ros_py_binary(name, srcs = [], main = None, imports = [], args = [], dload_py_shim( name = shim, target = ":" + binary, + env = RUNTIME_ENVIRONMENT, data = data, deps = deps, testonly = testonly, @@ -126,12 +96,14 @@ def drake_ros_py_binary(name, srcs = [], main = None, imports = [], args = [], **kwargs ) -def drake_ros_py_test(name, srcs = [], main = None, imports = [], - srcs_version = None, data = [], args = [], - deps = [], tags = [], visibility = None, - testonly = 1, **kwargs): +def ros_py_test( + name, srcs = [], main = None, imports = [], srcs_version = None, + data = [], args = [], deps = [], tags = [], visibility = None, + testonly = 1, **kwargs +): """ - Adds a Python test, picking up runtime information in dependencies. + Adds a Python test, injecting the runtime environment + specific to this ROS overlay. Equivalent to the py_test() rule. """ @@ -163,6 +135,7 @@ def drake_ros_py_test(name, srcs = [], main = None, imports = [], dload_py_shim( name = shim, target = ":" + test, + env = RUNTIME_ENVIRONMENT, data = data, deps = deps, testonly = testonly, diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl similarity index 87% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl index ec84d8b9e..5c6092c09 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl @@ -2,8 +2,6 @@ load("@REPOSITORY_ROOT@:distro.bzl", "AVAILABLE_TYPESUPPORTS") load("@python_dev//:version.bzl", "PYTHON_EXTENSION_SUFFIX") -load("@drake_ros//tools/skylark:drake_ros_cc.bzl", "drake_ros_cc_library") -load("@drake_ros//tools/skylark:drake_ros_py.bzl", "drake_ros_py_library") def _as_idl_tuple(file): path, parent, base = file.short_path.rsplit("/", 2) @@ -108,7 +106,11 @@ rosidl_translate_genrule = rule( def _extract_interface_parts(path): parent, _, base = path.rpartition("/") basename, _, ext = base.rpartition(".") - return parent, basename.lower() + basename = basename[0].lower() + ''.join([ + '_' + char.lower() if char.isupper() else char + for char in basename[1:].elems() + ]) + return parent, basename def rosidl_definitions_filegroup(name, group, interfaces, includes, **kwargs): translated_interfaces = [] @@ -125,14 +127,12 @@ def rosidl_definitions_filegroup(name, group, interfaces, includes, **kwargs): **kwargs ) - def _generated_source_paths(group, kind): base = "{}/{}".format(group, kind) include = "{}/{}".format(native.package_name(), base) root = "{}/{}".format(base, group) return base, root - def rosidl_c_library( name, group, interfaces, includes = [], deps = [], **kwargs ): @@ -167,7 +167,7 @@ def rosidl_c_library( "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", ] - drake_ros_cc_library( + native.cc_library( name = name, srcs = generated_c_sources, hdrs = generated_c_headers, @@ -200,7 +200,7 @@ def rosidl_cc_library( **kwargs ) - drake_ros_cc_library( + native.cc_library( name = name, hdrs = generated_cc_headers, includes = [include], @@ -262,7 +262,7 @@ def rosidl_py_library( **kwargs ) - drake_ros_cc_library( + native.cc_library( name = _c_extension(name), srcs = generated_c_sources, deps = c_deps + [ @@ -284,14 +284,14 @@ def rosidl_py_library( deps = list(c_typesupport_extension_deps) if typesupport_library not in deps: deps.append(typesupport_library) - drake_ros_cc_library( + native.cc_library( name = _c_typesupport_extension(group, typesupport_name), srcs = generated_c_sources_per_typesupport[typesupport_name], deps = deps, **kwargs ) py_data.append(_c_typesupport_extension_label(group, typesupport_name)) - drake_ros_py_library( + native.py_library( name = name, srcs = generated_py_sources, data = py_data, @@ -326,17 +326,13 @@ def rosidl_typesupport_fastrtps_cc_library( **kwargs ) - drake_ros_cc_library( + native.cc_library( name = name, srcs = generated_cc_sources, hdrs = generated_cc_headers, includes = [include], deps = deps + [ - # NOTE(hidmic): using rmw_fastrtps_shared_cpp as proxy - # to fastcdr as it is not a ROS package (only a CMake - # package, which can be scrapped but cannot be sorted - # topologically w/o dependents information). - "@REPOSITORY_ROOT@:rmw_fastrtps_shared_cpp_cc", + "@REPOSITORY_ROOT@:fastcdr_cc", "@REPOSITORY_ROOT@:rmw_cc", "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", "@REPOSITORY_ROOT@:rosidl_typesupport_fastrtps_cpp_cc", @@ -372,17 +368,13 @@ def rosidl_typesupport_fastrtps_c_library( **kwargs ) - drake_ros_cc_library( + native.cc_library( name = name, srcs = generated_c_sources, hdrs = generated_c_headers, includes = [include], deps = deps + [ - # NOTE(hidmic): using rmw_fastrtps_shared_cpp as proxy - # to fastcdr as it is not a ROS package (only a CMake - # package, which can be scrapped but cannot be sorted - # topologically w/o dependents information). - "@REPOSITORY_ROOT@:rmw_fastrtps_shared_cpp_cc", + "@REPOSITORY_ROOT@:fastcdr_cc", "@REPOSITORY_ROOT@:rmw_cc", "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", "@REPOSITORY_ROOT@:rosidl_typesupport_fastrtps_c_cc", @@ -419,7 +411,7 @@ def rosidl_typesupport_introspection_c_library( **kwargs ) - drake_ros_cc_library( + native.cc_library( name = name, srcs = generated_c_sources, hdrs = generated_c_headers, @@ -456,7 +448,7 @@ def rosidl_typesupport_introspection_cc_library( **kwargs ) - drake_ros_cc_library( + native.cc_library( name = name, srcs = generated_cc_sources, hdrs = generated_cc_headers, @@ -500,7 +492,7 @@ def rosidl_typesupport_c_library( if len(typesupports) == 1: deps = deps + [typesupports.values()[0]] - drake_ros_cc_library( + native.cc_library( name = name, srcs = generated_c_sources, hdrs = generated_c_headers, @@ -541,7 +533,7 @@ def rosidl_typesupport_cc_library( if len(typesupports) == 1: deps = deps + [typesupports.values()[0]] - drake_ros_cc_library( + native.cc_library( name = name, srcs = generated_cc_sources, includes = [include], @@ -557,15 +549,41 @@ def rosidl_typesupport_cc_library( def defs(name): return name + "_defs" + + def cc(name): return name + "_cc" def cc_label(name): return ":" + cc(name) +def cc_types(name): + return name + "_cc_types" + +def cc_types_label(name): + return ":" + cc_types(name) + +def typesupport_cc(name): + return name + "_typesupport_cc" + +def typesupport_cc_label(name): + return ":" + typesupport_cc(name) + +def typesupport_introspection_cc(name): + return name + "_typesupport_introspection_cpp" + +def typesupport_introspection_cc_label(name): + return ":" + typesupport_introspection_cc(name) + +def typesupport_fastrtps_cc(name): + return name + "_typesupport_fastrtps_cpp" + +def typesupport_fastrtps_cc_label(name): + return ":" + typesupport_fastrtps_cc(name) + def rosidl_cc_support(name, interfaces, deps, **kwargs): rosidl_cc_library( - name = cc(name), + name = cc_types(name), group = name, interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [cc(dep) for dep in deps], @@ -576,32 +594,41 @@ def rosidl_cc_support(name, interfaces, deps, **kwargs): if 'rosidl_typesupport_introspection_cpp' in AVAILABLE_TYPESUPPORTS: rosidl_typesupport_introspection_cc_library( - name = name + "_typesupport_introspection_cc", + name = typesupport_introspection_cc(name), group = name, interfaces = interfaces, includes = [defs(dep) for dep in deps], - deps = [cc_label(name)] + [cc(dep) for dep in deps], + deps = [cc_types_label(name)] + [cc(dep) for dep in deps], **kwargs ) typesupports['rosidl_typesupport_introspection_cpp'] = \ - ":" + name + "_typesupport_introspection_cc" + typesupport_introspection_cc_label(name) if 'rosidl_typesupport_fastrtps_cpp' in AVAILABLE_TYPESUPPORTS: rosidl_typesupport_fastrtps_cc_library( - name = name + "_typesupport_fastrtps_cc", + name = typesupport_fastrtps_cc(name), group = name, interfaces = interfaces, includes = [defs(dep) for dep in deps], - deps = [cc_label(name)] + [cc(dep) for dep in deps], + deps = [cc_types_label(name)] + [cc(dep) for dep in deps], **kwargs ) typesupports['rosidl_typesupport_fastrtps_cpp'] = \ - ":" + name + "_typesupport_fastrtps_cc" + typesupport_fastrtps_cc_label(name) rosidl_typesupport_cc_library( - name = name + "_typesupport_cc", + name = typesupport_cc(name), typesupports = typesupports, group = name, interfaces = interfaces, includes = [defs(dep) for dep in deps], - deps = [cc_label(name)] + [cc(dep) for dep in deps], + deps = [cc_types_label(name)] + [cc(dep) for dep in deps], + **kwargs + ) + typesupports["rosidl_typesupport_c"] = typesupport_cc_label(name) + + native.cc_library( + name = cc(name), + deps = [ + cc_types_label(name), + ] + typesupports.values(), **kwargs ) @@ -611,15 +638,39 @@ def c(name): def c_label(name): return ":" + c(name) +def c_types(name): + return name + "_c_types" + +def c_types_label(name): + return ":" + c_types(name) + def py(name): return name + "_py" def py_label(name): return ":" + py(name) +def typesupport_c(name): + return name + "_typesupport_c" + +def typesupport_c_label(name): + return ":" + typesupport_c(name) + +def typesupport_introspection_c(name): + return name + "_typesupport_introspection_c" + +def typesupport_introspection_c_label(name): + return ":" + typesupport_introspection_c(name) + +def typesupport_fastrtps_c(name): + return name + "_typesupport_fastrtps_c" + +def typesupport_fastrtps_c_label(name): + return ":" + typesupport_fastrtps_c(name) + def rosidl_py_support(name, interfaces, deps, **kwargs): rosidl_c_library( - name = c(name), + name = c_types(name), group = name, interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [c(dep) for dep in deps], @@ -630,35 +681,43 @@ def rosidl_py_support(name, interfaces, deps, **kwargs): if "rosidl_typesupport_introspection_c" in AVAILABLE_TYPESUPPORTS: rosidl_typesupport_introspection_c_library( - name = name + "_typesupport_introspection_c", + name = typesupport_introspection_c(name), group = name, interfaces = interfaces, includes = [defs(dep) for dep in deps], - deps = [c_label(name)] + [c(dep) for dep in deps], + deps = [c_types_label(name)] + [c(dep) for dep in deps], **kwargs ) typesupports["rosidl_typesupport_introspection_c"] = \ - ":" + name + "_typesupport_introspection_c" + typesupport_introspection_c_label(name) if "rosidl_typesupport_fastrtps_c" in AVAILABLE_TYPESUPPORTS: rosidl_typesupport_fastrtps_c_library( name = name + "_typesupport_fastrtps_c", group = name, interfaces = interfaces, includes = [defs(dep) for dep in deps], - deps = [c_label(name)] + [c(dep) for dep in deps], + deps = [c_types_label(name)] + [c(dep) for dep in deps], **kwargs ) typesupports["rosidl_typesupport_fastrtps_c"] = \ - ":" + name + "_typesupport_fastrtps_c" + typesupport_fastrtps_c_label(name) rosidl_typesupport_c_library( - name = name + "_typesupport_c", + name = typesupport_c(name), typesupports = typesupports, group = name, interfaces = interfaces, includes = [defs(dep) for dep in deps], - deps = [c_label(name)] + [c(dep) for dep in deps], + deps = [c_types_label(name)] + [c(dep) for dep in deps], + **kwargs + ) + typesupports["rosidl_typesupport_c"] = typesupport_c_label(name) + + native.cc_library( + name = c(name), + deps = [ + c_types_label(name), + ] + typesupports.values(), **kwargs ) - typesupports["rosidl_typesupport_c"] = ":" + name + "_typesupport_c" rosidl_py_library( name = py(name), @@ -667,12 +726,11 @@ def rosidl_py_support(name, interfaces, deps, **kwargs): includes = [defs(dep) for dep in deps], py_deps = [py(dep) for dep in deps], c_deps = [c(dep) for dep in deps] + [ - c_label(name), typesupports["rosidl_typesupport_c"] + c_types_label(name), typesupports["rosidl_typesupport_c"] ], **kwargs ) - def rosidl_interfaces_group(name, interfaces, deps, **kwargs): rosidl_definitions_filegroup( name = defs(name), diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/overlay_executable.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/overlay_executable.bazel.tpl new file mode 100644 index 000000000..356a445cb --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/overlay_executable.bazel.tpl @@ -0,0 +1,6 @@ +ros_py_import( + name = @name@, + executable = @executable@, + data = @data@, + deps = @deps@, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_alias.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_alias.bazel.tpl new file mode 100644 index 000000000..8bcb25700 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_alias.bazel.tpl @@ -0,0 +1,4 @@ +alias( + name = @name@, + actual = @actual@, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_cc_library.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_cc_library.bazel.tpl new file mode 100644 index 000000000..f9e992cc9 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_cc_library.bazel.tpl @@ -0,0 +1,11 @@ +cc_library( + name = @name@, + srcs = @srcs@, + hdrs = glob(["{}/**/*.*".format(x) for x in @headers@]), + includes = @includes@, + copts = @copts@, + defines = @defines@, + linkopts = @linkopts@, + data = @data@, + deps = @deps@, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_cc_library_with_runtime_environ.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_cc_library_with_runtime_environ.bazel.tpl new file mode 100644 index 000000000..d88295bd9 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_cc_library_with_runtime_environ.bazel.tpl @@ -0,0 +1,19 @@ +cc_library( + name = "_" + @name@, + srcs = @srcs@, + hdrs = glob(["{}/**/*.*".format(x) for x in @headers@]), + includes = @includes@, + copts = @copts@, + defines = @defines@, + linkopts = @linkopts@, + data = @data@, + deps = @deps@, +) + +dload_aware_cc_library( + name = @name@, + base = ":_" + @name@, + data = @data@, + deps = [":_" + @name@] + @deps@, + env = @env@, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_interfaces_filegroup.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_interfaces_filegroup.bazel.tpl new file mode 100644 index 000000000..b02882647 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_interfaces_filegroup.bazel.tpl @@ -0,0 +1,4 @@ +interfaces_filegroup( + name = @name@, + share_directory = @share_directory@, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_meta_py_library.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_meta_py_library.bazel.tpl new file mode 100644 index 000000000..68455c674 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_meta_py_library.bazel.tpl @@ -0,0 +1,4 @@ +py_library( + name = @name@, + deps = @deps@, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_py_library.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_py_library.bazel.tpl new file mode 100644 index 000000000..764978217 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_py_library.bazel.tpl @@ -0,0 +1,14 @@ +py_library( + name = @name@, + srcs = glob(["{}/**/*.py".format(x) for x in @tops@]), + data = glob( + include=[ + "{}/**/*.*".format(x) for x in @tops@ + ] + [ + "{}/*".format(x) for x in @eggs@ + ], + exclude=["**/*.py", "**/*.so"], + ) + @data@, + imports = @imports@, + deps = @deps@, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_share_filegroup.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_share_filegroup.bazel.tpl new file mode 100644 index 000000000..1aa3e9cb9 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_share_filegroup.bazel.tpl @@ -0,0 +1,4 @@ +share_filegroup( + name = @name@, + share_directories = @share_directories@, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/prologue.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/prologue.bazel.tpl new file mode 100644 index 000000000..27ea52545 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/prologue.bazel.tpl @@ -0,0 +1,8 @@ +# -*- python -*- + +package(default_visibility = ["//visibility:public"]) + +load("@REPOSITORY_ROOT@:py_tools.bzl", "ros_py_import") +load("@drake_ros//tools/skylark:dload_cc.bzl", "dload_aware_cc_library") +load("@drake_ros//tools/skylark/ros2:common.bzl", "interfaces_filegroup") +load("@drake_ros//tools/skylark/ros2:common.bzl", "share_filegroup") diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ament_cmake_CMakeLists.txt.in b/drake_ros_bazel_installed/tools/skylark/ros2/resources/cmake/ament_cmake_CMakeLists.txt.in similarity index 90% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/ament_cmake_CMakeLists.txt.in rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/cmake/ament_cmake_CMakeLists.txt.in index f5d4964ba..c976e84b8 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ament_cmake_CMakeLists.txt.in +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/cmake/ament_cmake_CMakeLists.txt.in @@ -28,5 +28,7 @@ if("@PACKAGE@" STREQUAL "rviz_ogre_vendor") file(GLOB ogre_plugin_libraries "${OGRE_PLUGIN_DIR}/*.so*") target_link_libraries(${PROJECT_NAME} ${ogre_plugin_libraries}) else() + # TODO(hidmic): figure out why this is sometimes necessary ament_target_dependencies(${PROJECT_NAME} @PACKAGE@) + target_link_libraries(${PROJECT_NAME} @PACKAGE@) endif() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/distro.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/distro.bzl.tpl deleted file mode 100644 index ba58e5d85..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/distro.bzl.tpl +++ /dev/null @@ -1 +0,0 @@ -AVAILABLE_TYPESUPPORTS = {available_typesupports} diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/executable_import.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/executable_import.bzl.tpl deleted file mode 100644 index 5e219d10b..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/executable_import.bzl.tpl +++ /dev/null @@ -1,6 +0,0 @@ -drake_ros_py_executable_import( - name = {name}, - executable = {executable}, - data = {data}, - deps = {deps} -) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_alias.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_alias.bzl.tpl deleted file mode 100644 index 6483cff96..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_alias.bzl.tpl +++ /dev/null @@ -1,4 +0,0 @@ -alias( - name = {name}, - actual = {actual}, -) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_library.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_library.bzl.tpl deleted file mode 100644 index fabeed62e..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_cc_library.bzl.tpl +++ /dev/null @@ -1,12 +0,0 @@ -drake_ros_cc_library( - name = {name}, - srcs = {srcs}, - hdrs = glob(["{{}}/**/*.*".format(x) for x in {headers}]), - includes = {includes}, - copts = {copts}, - defines = {defines}, - linkopts = {linkopts}, - data = {data}, - deps = {deps}, - runenv = {runenv}, -) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_interfaces_filegroup.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_interfaces_filegroup.bzl.tpl deleted file mode 100644 index 7558eaf2e..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_interfaces_filegroup.bzl.tpl +++ /dev/null @@ -1,4 +0,0 @@ -package_interfaces_filegroup( - name = {name}, - share_directory = {share_directory}, -) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_meta_py_library.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_meta_py_library.bzl.tpl deleted file mode 100644 index 5a7c89a7e..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_meta_py_library.bzl.tpl +++ /dev/null @@ -1,4 +0,0 @@ -drake_ros_py_library( - name = {name}, - deps = {deps}, -) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library.bzl.tpl deleted file mode 100644 index 37c622ebc..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library.bzl.tpl +++ /dev/null @@ -1,15 +0,0 @@ -drake_ros_py_library( - name = {name}, - srcs = glob(["{{}}/**/*.py".format(x) for x in {tops}]), - data = glob( - include=[ - "{{}}/**/*.*".format(x) for x in {tops} - ] + [ - "{{}}/*".format(x) for x in {eggs} - ], - exclude=["**/*.py", "**/*.so"], - ) + {data}, - imports = {imports}, - deps = {deps}, - runenv = {runenv}, -) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library_with_cc_extensions.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library_with_cc_extensions.bzl.tpl deleted file mode 100644 index 37c622ebc..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_py_library_with_cc_extensions.bzl.tpl +++ /dev/null @@ -1,15 +0,0 @@ -drake_ros_py_library( - name = {name}, - srcs = glob(["{{}}/**/*.py".format(x) for x in {tops}]), - data = glob( - include=[ - "{{}}/**/*.*".format(x) for x in {tops} - ] + [ - "{{}}/*".format(x) for x in {eggs} - ], - exclude=["**/*.py", "**/*.so"], - ) + {data}, - imports = {imports}, - deps = {deps}, - runenv = {runenv}, -) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_share_filegroup.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_share_filegroup.bzl.tpl deleted file mode 100644 index a1d29413a..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/package_share_filegroup.bzl.tpl +++ /dev/null @@ -1,4 +0,0 @@ -package_share_filegroup( - name = {name}, - share_directories = {share_directories}, -) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/setup.sh.in b/drake_ros_bazel_installed/tools/skylark/ros2/resources/shell/setup.sh.in similarity index 82% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/setup.sh.in rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/shell/setup.sh.in index f0731fb37..3ed4fdf4b 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/setup.sh.in +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/shell/setup.sh.in @@ -8,7 +8,6 @@ for ws in @WORKSPACES@; do done # Setup environment -export ROS2BZL_PREFIX_PATH="@REPOSITORY_DIR@" export PYTHONPATH="@REPOSITORY_DIR@:$PYTHONPATH" export PATH="@REPOSITORY_DIR@:$PATH" diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl index ec7516269..9375e5ba6 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -3,16 +3,22 @@ load("//tools/skylark:execute.bzl", "execute_or_fail") MANIFEST = [ "cmake_tools/server_mode.py", "cmake_tools/__init__.py", - "resources/executable_import.bzl.tpl", - "resources/package_py_library_with_cc_extensions.bzl.tpl", - "resources/package_cc_library.bzl.tpl", - "resources/BUILD.prologue.bazel", - "resources/ament_cmake_CMakeLists.txt.in", - "resources/package_meta_py_library.bzl.tpl", - "resources/package_alias.bzl.tpl", - "resources/package_py_library.bzl.tpl", - "resources/package_share_filegroup.bzl.tpl", - "resources/package_interfaces_filegroup.bzl.tpl", + + "resources/bazel/cc_tools.bzl.tpl", + "resources/bazel/distro.bzl.tpl", + "resources/bazel/py_tools.bzl.tpl", + "resources/bazel/rosidl_tools.bzl.tpl", + "resources/bazel/snippets/overlay_executable.bazel.tpl", + "resources/bazel/snippets/package_alias.bazel.tpl", + "resources/bazel/snippets/package_interfaces_filegroup.bazel.tpl", + "resources/bazel/snippets/package_cc_library.bazel.tpl", + "resources/bazel/snippets/package_cc_library_with_runtime_environ.bazel.tpl", + "resources/bazel/snippets/package_meta_py_library.bazel.tpl", + "resources/bazel/snippets/package_py_library.bazel.tpl", + "resources/bazel/snippets/package_share_filegroup.bazel.tpl", + "resources/bazel/snippets/prologue.bazel.tpl", + + "resources/cmake/ament_cmake_CMakeLists.txt.in", "ros2bzl/utilities.py", "ros2bzl/sandboxing.py", @@ -38,7 +44,7 @@ def _impl(repo_ctx): repo_ctx.symlink(_label(relpath), relpath) repo_ctx.template( - "setup.sh", _label("resources/setup.sh.in"), + "setup.sh", _label("resources/shell/setup.sh.in"), substitutions = { "@ID@": _uuid(repo_ctx), "@REPOSITORY_DIR@": str(repo_ctx.path(".")), @@ -47,14 +53,6 @@ def _impl(repo_ctx): executable = True ) - repo_ctx.template( - "rosidl.bzl", _label("resources/rosidl.bzl.tpl"), - substitutions = { - "@REPOSITORY_ROOT@": "@{}//".format(repo_ctx.name), - }, - executable = False - ) - generate_tool = repo_ctx.path(_label("generate_repository_files.py")) cmd = ["./setup.sh", str(generate_tool)] for ws in repo_ctx.attr.workspaces: diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/resources.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/resources.py index b4f09d8e0..fe5fa0a3e 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/resources.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/resources.py @@ -19,42 +19,3 @@ def path_to_resource(name): def load_resource(name): with open(path_to_resource(name), 'r') as fd: return fd.read() - - -ROS2BZL_PREFIX_PATH = os.environ.get('ROS2BZL_PREFIX_PATH', '.') - - -class ExtendedJSONEncoder(json.JSONEncoder): - - def default(self, o): - try: - iterable = iter(o) - except TypeError: - pass - else: - return list(iterable) - return json.JSONEncoder.default(self, o) - - -def load_underlay(): - path = os.path.join(ROS2BZL_PREFIX_PATH, 'underlay.json') - if not os.path.exists(path): - raise ValueError( - 'No underlay to load: {} not found'.format(path) - ) - with open(path, 'r') as fd: - setup = json.load(fd) - return ( - setup['packages'], setup['dependency_graph'], - setup['cache'], sandboxing.configure(**setup['sandbox']), - ) - - -def setup_underlay(packages, dependency_graph, cache, sandbox): - with open('underlay.json', 'w') as fd: - json.dump({ - 'packages': packages, - 'dependency_graph': dependency_graph, - 'cache': cache, - 'sandbox': sandbox.kwargs, - }, fd, cls=ExtendedJSONEncoder, sort_keys=True, indent=4) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py index 86e1a09d1..278c119ff 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py @@ -1,17 +1,20 @@ import os -from ament_index_python import get_packages_with_prefixes -from ament_index_python import get_search_paths +import ament_index_python +import cmake_tools -from ros2bzl.scrapping.metadata import collect_package_metadata +from ros2bzl.scrapping.metadata import collect_cmake_package_metadata +from ros2bzl.scrapping.metadata import collect_ros_package_metadata def list_all_executables(): executables = {} - for prefix in get_search_paths(): - root = os.path.join(prefix, 'bin') - for path in os.listdir(root): - path = os.path.join(root, path) + for prefix in ament_index_python.get_packages_with_prefixes().values(): + bindir = os.path.join(prefix, 'bin') + if not os.path.isdir(bindir): + continue + for path in os.listdir(bindir): + path = os.path.join(bindir, path) if os.path.isfile(path) and os.access(path, os.X_OK): name = os.path.basename(path) if name not in executables: @@ -20,11 +23,22 @@ def list_all_executables(): def index_all_packages(): - return { - name: collect_package_metadata(name, prefix) - for name, prefix in get_packages_with_prefixes().items() + packages = { + name: collect_ros_package_metadata(name, prefix) + for name, prefix in + ament_index_python.get_packages_with_prefixes().items() } + for name, prefix in cmake_tools.get_packages_with_prefixes( + ament_index_python.get_search_paths() + ).items(): + if name in packages: + # Assume unique package names across package types + continue + packages[name] = collect_cmake_package_metadata(name, prefix) + return packages + +SKIPPED_GROUP_DEPENDENCIES = ('rmw_implementation_packages',) def build_dependency_graph(packages, include=None, exclude=None): @@ -34,12 +48,26 @@ def build_dependency_graph(packages, include=None, exclude=None): if exclude: package_set -= exclude + groups = {} + for name, metadata in packages.items(): + if 'groups' not in metadata: + continue + for group_name in metadata['groups']: + if group_name not in groups: + groups[group_name] = [] + groups[group_name].append(name) + dependency_graph = {} while package_set: name = package_set.pop() metadata = packages[name] - dependencies = set(metadata['build_dependencies']) - dependencies.update(metadata['run_dependencies']) + dependencies = set(metadata.get('build_dependencies', [])) + dependencies.update(metadata.get('run_dependencies', [])) + if 'group_dependencies' in metadata: + for group_name in metadata['group_dependencies']: + if group_name in SKIPPED_GROUP_DEPENDENCIES: + continue + dependencies.update(groups[group_name]) if exclude: dependencies -= exclude # Ignore system, non-ROS dependencies @@ -55,3 +83,22 @@ def build_dependency_graph(packages, include=None, exclude=None): packages = {name: packages[name] for name in dependency_graph} return packages, dependency_graph + + +def load_distribution(sandbox, include=None, exclude=None): + packages, dependency_graph = build_dependency_graph( + index_all_packages(), include, exclude) + executables = list_all_executables() + return { + 'packages': packages, + 'dependency_graph': dependency_graph, + 'executables': executables, + 'paths': { + 'ament_prefix': [ + sandbox(path, external=True) for path in + ament_index_python.get_search_paths()], + 'library_load': [ + sandbox(path, external=True) for path in + os.environ['LD_LIBRARY_PATH'].split(os.path.pathsep)], + } + } diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py index f46dc9f45..de6e1e786 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py @@ -1,5 +1,6 @@ import os +from multiprocessing.dummy import Pool from tempfile import TemporaryDirectory import cmake_tools @@ -106,7 +107,7 @@ def collect_ament_cmake_package_properties(name, metadata): # speed with TemporaryDirectory(dir=os.getcwd()) as project_path: project_name = 'empty_using_' + name - cmakelists_template_path = path_to_resource('ament_cmake_CMakeLists.txt.in') + cmakelists_template_path = path_to_resource('cmake/ament_cmake_CMakeLists.txt.in') cmakelists_path = os.path.join(project_path, 'CMakeLists.txt') cmake_tools.configure_file(cmakelists_template_path, cmakelists_path, { '@NAME@': project_name, '@PACKAGE@': name @@ -155,7 +156,7 @@ def collect_ament_cmake_package_direct_properties(name, metadata, dependencies, properties = dict(ament_cmake_cache[name]) for dependency_name, dependency_metadata in dependencies.items(): - if dependency_metadata['build_type'] != 'ament_cmake': + if dependency_metadata.get('build_type') != 'ament_cmake': continue if dependency_name not in ament_cmake_cache: ament_cmake_cache[dependency_name] = \ @@ -189,3 +190,18 @@ def collect_ament_cmake_package_direct_properties(name, metadata, dependencies, # Do not deduplicate link directories in case we're dealing with merge installs. return properties + + +def precache_ament_cmake_properties(packages, jobs=None): + ament_cmake_packages = { + name: metadata + for name, metadata in packages.items() + if metadata.get('build_type') == 'ament_cmake' + } + with Pool(jobs) as pool: + return dict(zip( + ament_cmake_packages.keys(), pool.starmap( + collect_ament_cmake_package_properties, + ament_cmake_packages.items() + ) + )) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py index b4924f7c1..19ae4845f 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py @@ -48,8 +48,8 @@ def collect_ament_python_package_direct_properties(name, metadata, dependencies, ament_cmake_cache = cache['ament_cmake'] for dependency_name, dependency_metadata in dependencies.items(): dependency_libraries = [] - if 'cc' in dependency_metadata['langs']: - if dependency_metadata['build_type'] == 'ament_cmake': + if 'cc' in dependency_metadata.get('langs', []): + if dependency_metadata.get('build_type') == 'ament_cmake': if dependency_name not in ament_cmake_cache: ament_cmake_cache[dependency_name] = \ collect_ament_cmake_package_properties( @@ -59,7 +59,7 @@ def collect_ament_python_package_direct_properties(name, metadata, dependencies, dependency_libraries.extend( dependency_properties['link_libraries'] ) - if 'py' in dependency_metadata['langs']: + if 'py' in dependency_metadata.get('langs', []): if dependency_name not in ament_python_cache: ament_python_cache[dependency_name] = \ collect_ament_python_package_properties( diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/metadata.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/metadata.py index 3d65ed7ae..0dd3015b3 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/metadata.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/metadata.py @@ -59,13 +59,22 @@ def find_executables(base_path): def collect_package_langs(metadata): langs = set() - build_type = metadata['build_type'] + build_type = metadata.get('build_type') if build_type in DEFAULT_LANGS_PER_BUILD_TYPE: langs.update(DEFAULT_LANGS_PER_BUILD_TYPE[build_type]) return langs -def collect_package_metadata(name, prefix): +def collect_cmake_package_metadata(name, prefix): + metadata = dict( + prefix=prefix, + build_type='cmake', + ) + metadata['langs'] = collect_package_langs(metadata) + return metadata + + +def collect_ros_package_metadata(name, prefix): share_directory = os.path.join(prefix, 'share', name) ament_index_directory = os.path.join(prefix, 'share', 'ament_index') diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py index 3ee00e530..355c19fe5 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py @@ -3,18 +3,7 @@ from ros2bzl.resources import load_resource from ros2bzl.scrapping.system import find_library_path -def load_paths_for_libraries(libraries, sandbox): - if not libraries: - return [] - libraries_directories = sorted(set( - sandbox(os.path.dirname(lib), external=True) for lib in libraries - )) - load_paths = [libraries_directories[0]] - for directory in libraries_directories[1:]: - if directory.startswith(load_paths[-1]): - continue - load_paths.append(directory) - return load_paths +from ros2bzl.utilities import to_starlark_string_dict def label_name(name, metadata): @@ -46,18 +35,27 @@ def labels_with(suffix): def configure_package_share_filegroup(name, metadata, sandbox): - return load_resource('package_share_filegroup.bzl.tpl'), { - 'name': share_name(name, metadata), - 'share_directories': [ - sandbox(metadata['share_directory']), - sandbox(metadata['ament_index_directory']), - ] - } + target_name = share_name(name, metadata) + shared_directories = [sandbox(metadata['share_directory'])] + if 'ament_index_directory' in metadata: + shared_directories.append(sandbox(metadata['ament_index_directory'])) + return ( + target_name, + load_resource('bazel/snippets/package_share_filegroup.bazel.tpl'), + to_starlark_string_dict({ + 'name': target_name, 'share_directories': shared_directories + }) + ) + def configure_package_interfaces_filegroup(name, metadata, sandbox): - return load_resource('package_interfaces_filegroup.bzl.tpl'), { - 'name': name, 'share_directory': sandbox(metadata['share_directory']) - } + return ( + name, + load_resource('bazel/snippets/package_interfaces_filegroup.bazel.tpl'), + to_starlark_string_dict({ + 'name': name, 'share_directory': sandbox(metadata['share_directory']) + }) + ) def configure_package_cc_library(name, metadata, properties, dependencies, extras, sandbox): @@ -84,29 +82,19 @@ def configure_package_cc_library(name, metadata, properties, dependencies, extra deps = [ cc_label(dependency_name, dependency_metadata) for dependency_name, dependency_metadata in dependencies.items() - if 'cc' in dependency_metadata['langs'] + if 'cc' in dependency_metadata.get('langs', []) ] - runenv = {'AMENT_PREFIX_PATH': [ - 'path-prepend', sandbox(metadata['prefix'], external=True) - ]} - if 'rmw_implementation_packages' in metadata['groups']: - runenv['RMW_IMPLEMENTATION'] = ['replace', name] - data = [] - data.append(share_label(name, metadata)) + if 'share_directory' in metadata: + data.append(share_label(name, metadata)) # Add in plugins, if any if 'plugin_libraries' in metadata: data.extend( sandbox(find_library_path(library)) for library in metadata['plugin_libraries'] ) - # Prepare runfiles and load paths to support dynamic loading - load_paths = load_paths_for_libraries( - properties['link_libraries'], sandbox - ) - if load_paths: - runenv['${LOAD_PATH}'] = ['path-prepend', *load_paths] + # Prepare runfiles to support dynamic loading data.extend(library for library in libraries if library not in data) if extras and 'data' in extras and target_name in extras['data']: data.extend( @@ -115,7 +103,7 @@ def configure_package_cc_library(name, metadata, properties, dependencies, extra if label_or_path not in data ) - return load_resource('package_cc_library.bzl.tpl'), { + config = { 'name': target_name, 'srcs': libraries, 'headers': headers, @@ -123,22 +111,32 @@ def configure_package_cc_library(name, metadata, properties, dependencies, extra 'copts': copts, 'defines': defines, 'linkopts': linkopts, - 'runenv': runenv, 'data': data, 'deps': deps, } + if 'rmw_implementation_packages' in metadata.get('groups', []): + template_path = 'bazel/snippets/package_cc_library_with_runtime_environ.bazel.tpl' + config['env'] = {'RMW_IMPLEMENTATION': ['replace', name]} + else: + template_path = 'bazel/snippets/package_cc_library.bazel.tpl' + + return target_name, load_resource(template_path), to_starlark_string_dict(config) + def configure_package_meta_py_library(name, metadata, dependencies): deps = [] for dependency_name, dependency_metadata in dependencies.items(): - if 'py' in dependency_metadata['langs']: + if 'py' in dependency_metadata.get('langs', []): deps.append(py_label(dependency_name, dependency_metadata)) - elif 'py (transitively)' in dependency_metadata['langs']: + elif 'py (transitively)' in dependency_metadata.get('langs', []): deps.append(meta_py_label(dependency_name, dependency_metadata)) - return load_resource('package_meta_py_library.bzl.tpl'), { - 'name': meta_py_name(name, metadata), 'deps': deps - } + target_name = meta_py_name(name, metadata) + return ( + target_name, + load_resource('bazel/snippets/package_meta_py_library.bazel.tpl'), + to_starlark_string_dict({'name': target_name, 'deps': deps}) + ) def configure_package_py_library(name, metadata, properties, dependencies, extras, sandbox): @@ -149,41 +147,23 @@ def configure_package_py_library(name, metadata, properties, dependencies, extra deps = [] for dependency_name, dependency_metadata in dependencies.items(): - if 'py' in dependency_metadata['langs']: + if 'py' in dependency_metadata.get('langs', []): deps.append(py_label(dependency_name, dependency_metadata)) - elif 'py (transitively)' in dependency_metadata['langs']: + elif 'py (transitively)' in dependency_metadata.get('langs', []): deps.append(meta_py_label(dependency_name, dependency_metadata)) - config = { - 'name': target_name, - 'tops': tops, - 'eggs': eggs, - 'imports': imports, - 'deps': deps - } - data = [share_label(name, metadata)] - if 'cc' in metadata['langs']: + if 'cc' in metadata.get('langs', []): data.append(cc_label(name, metadata)) - runenv = {'AMENT_PREFIX_PATH': [ - 'path-prepend', sandbox(metadata['prefix'], external=True) - ]} - - if 'rmw_implementation_packages' in metadata['groups']: - runenv['RMW_IMPLEMENTATION'] = ['replace', name] - if 'cc_extensions' in properties: cc_deps = [ cc_label(dependency_name, dependency_metadata) for dependency_name, dependency_metadata in dependencies.items() - if 'cc' in dependency_metadata['langs'] + if 'cc' in dependency_metadata.get('langs', []) ] cc_extensions = [sandbox(ext) for ext in properties['cc_extensions']] - # Prepare runfiles and load paths to support dynamic loading - load_paths = load_paths_for_libraries(properties['cc_extensions'], sandbox) - if load_paths: - runenv['${LOAD_PATH}'] = ['path-prepend', *load_paths] + # Prepare runfiles to support dynamic loading data.extend(cc_extensions) data.extend(cc_deps) # Add in plugins, if any @@ -192,28 +172,42 @@ def configure_package_py_library(name, metadata, properties, dependencies, extra sandbox(find_library_path(library)) for library in metadata['plugin_libraries'] ) - template = load_resource('package_py_library_with_cc_extensions.bzl.tpl') - else: - template = load_resource('package_py_library.bzl.tpl') if extras and 'data' in extras: data.extend(extras['data'].get(target_name, [])) - config.update({'data': data, 'runenv': runenv}) - return template, config + return ( + target_name, + load_resource('bazel/snippets/package_py_library.bazel.tpl'), + to_starlark_string_dict({ + 'name': target_name, + 'tops': tops, + 'eggs': eggs, + 'imports': imports, + 'data': data, + 'deps': deps + }) + ) def configure_package_alias(name, target): - return load_resource('package_alias.bzl.tpl'), { - 'name': name, 'actual': ':' + target - } + return ( + name, + load_resource('bazel/snippets/package_alias.bazel.tpl'), + to_starlark_string_dict({'name': name, 'actual': ':' + target}) + ) def configure_package_c_library_alias(name, metadata): - return load_resource('package_alias.bzl.tpl'), { - 'name': c_name(name, metadata), - 'actual': cc_label(name, metadata) - } + target_name = c_name(name, metadata) + return ( + target_name, + load_resource('bazel/snippets/package_alias.bazel.tpl'), + to_starlark_string_dict({ + 'name': target_name, + 'actual': cc_label(name, metadata) + }) + ) def configure_executable_imports( @@ -223,11 +217,11 @@ def configure_executable_imports( common_data = [] for dependency_name, dependency_metadata in dependencies.items(): # TODO(hidmic): use appropriate target based on executable file type - if 'cc' in dependency_metadata['langs']: + if 'cc' in dependency_metadata.get('langs', []): common_data.append(cc_label(dependency_name, dependency_metadata)) - if 'py' in dependency_metadata['langs']: + if 'py' in dependency_metadata.get('langs', []): deps.append(py_label(dependency_name, dependency_metadata)) - elif 'py (transitively)' in dependency_metadata['langs']: + elif 'py (transitively)' in dependency_metadata.get('langs', []): common_data.append(meta_py_label(dependency_name, dependency_metadata)) for executable in executables: @@ -237,11 +231,15 @@ def configure_executable_imports( data = common_data if extras and 'data' in extras and target_name in extras['data']: data = data + extras['data'][target_name] - yield load_resource('executable_import.bzl.tpl'), { - 'name': target_name, - 'executable': sandbox(executable), - 'data': data, 'deps': deps, - } + yield ( + target_name, + load_resource('bazel/snippets/overlay_executable.bazel.tpl'), + to_starlark_string_dict({ + 'name': target_name, + 'executable': sandbox(executable), + 'data': data, 'deps': deps, + }) + ) def configure_package_executable_imports( @@ -253,3 +251,44 @@ def configure_package_executable_imports( metadata['executables'], dependencies, sandbox, extras=extras, prefix=label_name(name, metadata) ) + + +def configure_prologue(repo_name): + return load_resource('bazel/snippets/prologue.bazel.tpl'), { + 'REPOSITORY_ROOT': '@{}//'.format(repo_name) + } + + +def configure_rosidl_tools(repo_name): + return load_resource('bazel/rosidl_tools.bzl.tpl'), { + 'REPOSITORY_ROOT': '@{}//'.format(repo_name) + } + + +def configure_cc_tools(repo_name): + return load_resource('bazel/cc_tools.bzl.tpl'), { + 'REPOSITORY_ROOT': '@{}//'.format(repo_name) + } + + +def configure_py_tools(repo_name): + return load_resource('bazel/py_tools.bzl.tpl'), { + 'REPOSITORY_ROOT': '@{}//'.format(repo_name) + } + + +def configure_distro(distro): + typesupport_groups = [ + 'rosidl_typesupport_c_packages', + 'rosidl_typesupport_cpp_packages' + ] + return load_resource('bazel/distro.bzl.tpl'), to_starlark_string_dict({ + 'AMENT_PREFIX_PATH': distro['paths']['ament_prefix'], + 'LOAD_PATH': distro['paths']['library_load'], # Linux only + 'AVAILABLE_TYPESUPPORTS': [ + name for name, metadata in distro['packages'].items() if any( + group in typesupport_groups + for group in metadata.get('groups', []) + ) + ], + }) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/utilities.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/utilities.py index 4df56340a..f8a1d8230 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/utilities.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/utilities.py @@ -5,7 +5,10 @@ def to_starlark_string_dict(d): return {k: repr(v).replace("'", '"') for k, v in d.items()} def interpolate(template, config): - return template.format(**to_starlark_string_dict(config)) + content = template + for key, value in config.items(): + content = content.replace('@{}@'.format(key), value) + return content def compose(f, g): @functools.wraps(f) From 055c0051d7fe96b19d37b06301d1cc563f8ecd94 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 18 Jun 2021 15:35:25 -0300 Subject: [PATCH 016/107] Configure workspace through .bazelrc Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/.bazelrc | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 drake_ros_bazel_installed/.bazelrc diff --git a/drake_ros_bazel_installed/.bazelrc b/drake_ros_bazel_installed/.bazelrc new file mode 100644 index 000000000..a7d45b413 --- /dev/null +++ b/drake_ros_bazel_installed/.bazelrc @@ -0,0 +1,9 @@ +# Perform optimized builds. +build -c opt + +# Use C++17. +build --cxxopt=-std=c++17 +build --host_cxxopt=-std=c++17 + +# Use Python 3. +build --python_path=/usr/bin/python3 From 668fce13d380c466fdde0ad762c92744bdc4ac4c Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 18 Jun 2021 15:36:07 -0300 Subject: [PATCH 017/107] Fix //apps and //common packages Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/apps/BUILD.bazel | 2 +- drake_ros_bazel_installed/common/BUILD.bazel | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drake_ros_bazel_installed/apps/BUILD.bazel b/drake_ros_bazel_installed/apps/BUILD.bazel index ce8653020..c80e845b1 100644 --- a/drake_ros_bazel_installed/apps/BUILD.bazel +++ b/drake_ros_bazel_installed/apps/BUILD.bazel @@ -1,4 +1,4 @@ -load("@ros2//:rosidl.bzl", "rosidl_interfaces_group") +load("@ros2//:rosidl_tools.bzl", "rosidl_interfaces_group") rosidl_interfaces_group( name = "apps_msgs", diff --git a/drake_ros_bazel_installed/common/BUILD.bazel b/drake_ros_bazel_installed/common/BUILD.bazel index a75862dec..00afe00e5 100644 --- a/drake_ros_bazel_installed/common/BUILD.bazel +++ b/drake_ros_bazel_installed/common/BUILD.bazel @@ -1,4 +1,4 @@ -load("@ros2//:rosidl.bzl", "rosidl_interfaces_group") +load("@ros2//:rosidl_tools.bzl", "rosidl_interfaces_group") rosidl_interfaces_group( name = "common_msgs", @@ -11,4 +11,5 @@ rosidl_interfaces_group( "@ros2//:action_msgs", "@ros2//:builtin_interfaces", ], + visibility = ["//visibility:public"], ) From 99993c9307aebea251b26a5cc30e96f7d8e8e70f Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 24 Jun 2021 19:50:13 -0300 Subject: [PATCH 018/107] Update ROS build infrastructure - Ensure proper rosidl dynamic libraries linkage - Use proper naming for rosidl dynamic libraries Signed-off-by: Michel Hidalgo --- .../ros2/resources/bazel/rosidl_tools.bzl.tpl | 94 +++++++++---------- .../cmake/ament_cmake_CMakeLists.txt.in | 6 +- .../tools/skylark/ros2/ros2.bzl | 1 + .../ros2/ros2bzl/scrapping/ament_cmake.py | 5 +- 4 files changed, 54 insertions(+), 52 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl index 5c6092c09..a9df344b6 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl @@ -106,8 +106,8 @@ rosidl_translate_genrule = rule( def _extract_interface_parts(path): parent, _, base = path.rpartition("/") basename, _, ext = base.rpartition(".") - basename = basename[0].lower() + ''.join([ - '_' + char.lower() if char.isupper() else char + basename = basename[0].lower() + "".join([ + "_" + char.lower() if char.isupper() else char for char in basename[1:].elems() ]) return parent, basename @@ -262,9 +262,10 @@ def rosidl_py_library( **kwargs ) - native.cc_library( + native.cc_binary( name = _c_extension(name), srcs = generated_c_sources, + linkshared = 1, deps = c_deps + [ "@python_dev//:libs", ], @@ -284,10 +285,10 @@ def rosidl_py_library( deps = list(c_typesupport_extension_deps) if typesupport_library not in deps: deps.append(typesupport_library) - native.cc_library( + native.cc_binary( name = _c_typesupport_extension(group, typesupport_name), srcs = generated_c_sources_per_typesupport[typesupport_name], - deps = deps, **kwargs + deps = deps, linkshared = 1, **kwargs ) py_data.append(_c_typesupport_extension_label(group, typesupport_name)) @@ -326,11 +327,11 @@ def rosidl_typesupport_fastrtps_cc_library( **kwargs ) - native.cc_library( + native.cc_binary( name = name, - srcs = generated_cc_sources, - hdrs = generated_cc_headers, + srcs = generated_cc_sources + generated_cc_headers, includes = [include], + linkshared = 1, deps = deps + [ "@REPOSITORY_ROOT@:fastcdr_cc", "@REPOSITORY_ROOT@:rmw_cc", @@ -368,11 +369,11 @@ def rosidl_typesupport_fastrtps_c_library( **kwargs ) - native.cc_library( + native.cc_binary( name = name, - srcs = generated_c_sources, - hdrs = generated_c_headers, + srcs = generated_c_sources + generated_c_headers, includes = [include], + linkshared = 1, deps = deps + [ "@REPOSITORY_ROOT@:fastcdr_cc", "@REPOSITORY_ROOT@:rmw_cc", @@ -411,11 +412,11 @@ def rosidl_typesupport_introspection_c_library( **kwargs ) - native.cc_library( + native.cc_binary( name = name, - srcs = generated_c_sources, - hdrs = generated_c_headers, + srcs = generated_c_sources + generated_c_headers, includes = [include], + linkshared = 1, deps = deps + [ "@REPOSITORY_ROOT@:rosidl_typesupport_introspection_c_cc", ], @@ -448,11 +449,11 @@ def rosidl_typesupport_introspection_cc_library( **kwargs ) - native.cc_library( + native.cc_binary( name = name, - srcs = generated_cc_sources, - hdrs = generated_cc_headers, + srcs = generated_cc_sources + generated_cc_headers, includes = [include], + linkshared = 1, deps = deps + [ "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", @@ -489,14 +490,12 @@ def rosidl_typesupport_c_library( **kwargs ) - if len(typesupports) == 1: - deps = deps + [typesupports.values()[0]] - - native.cc_library( + native.cc_binary( name = name, - srcs = generated_c_sources, - hdrs = generated_c_headers, + srcs = generated_c_sources + generated_c_headers + [ + ts for ts in typesupports.values()], includes = [include], + linkshared = 1, deps = deps + [ "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", "@REPOSITORY_ROOT@:rosidl_typesupport_c_cc", @@ -530,13 +529,12 @@ def rosidl_typesupport_cc_library( **kwargs ) - if len(typesupports) == 1: - deps = deps + [typesupports.values()[0]] - - native.cc_library( + native.cc_binary( name = name, - srcs = generated_cc_sources, + srcs = generated_cc_sources + [ + ts for ts in typesupports.values()], includes = [include], + linkshared = 1, deps = deps + [ "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", "@REPOSITORY_ROOT@:rosidl_runtime_cpp_cc", @@ -549,8 +547,6 @@ def rosidl_typesupport_cc_library( def defs(name): return name + "_defs" - - def cc(name): return name + "_cc" @@ -558,25 +554,25 @@ def cc_label(name): return ":" + cc(name) def cc_types(name): - return name + "_cc_types" + return name + "__rosidl_cpp" def cc_types_label(name): return ":" + cc_types(name) def typesupport_cc(name): - return name + "_typesupport_cc" + return name + "__rosidl_typesupport_cpp" def typesupport_cc_label(name): return ":" + typesupport_cc(name) def typesupport_introspection_cc(name): - return name + "_typesupport_introspection_cpp" + return name + "__rosidl_typesupport_introspection_cpp" def typesupport_introspection_cc_label(name): return ":" + typesupport_introspection_cc(name) def typesupport_fastrtps_cc(name): - return name + "_typesupport_fastrtps_cpp" + return name + "__rosidl_typesupport_fastrtps_cpp" def typesupport_fastrtps_cc_label(name): return ":" + typesupport_fastrtps_cc(name) @@ -592,7 +588,7 @@ def rosidl_cc_support(name, interfaces, deps, **kwargs): typesupports = {} - if 'rosidl_typesupport_introspection_cpp' in AVAILABLE_TYPESUPPORTS: + if "rosidl_typesupport_introspection_cpp" in AVAILABLE_TYPESUPPORTS: rosidl_typesupport_introspection_cc_library( name = typesupport_introspection_cc(name), group = name, interfaces = interfaces, @@ -600,10 +596,10 @@ def rosidl_cc_support(name, interfaces, deps, **kwargs): deps = [cc_types_label(name)] + [cc(dep) for dep in deps], **kwargs ) - typesupports['rosidl_typesupport_introspection_cpp'] = \ + typesupports["rosidl_typesupport_introspection_cpp"] = \ typesupport_introspection_cc_label(name) - if 'rosidl_typesupport_fastrtps_cpp' in AVAILABLE_TYPESUPPORTS: + if "rosidl_typesupport_fastrtps_cpp" in AVAILABLE_TYPESUPPORTS: rosidl_typesupport_fastrtps_cc_library( name = typesupport_fastrtps_cc(name), group = name, interfaces = interfaces, @@ -611,7 +607,7 @@ def rosidl_cc_support(name, interfaces, deps, **kwargs): deps = [cc_types_label(name)] + [cc(dep) for dep in deps], **kwargs ) - typesupports['rosidl_typesupport_fastrtps_cpp'] = \ + typesupports["rosidl_typesupport_fastrtps_cpp"] = \ typesupport_fastrtps_cc_label(name) rosidl_typesupport_cc_library( @@ -622,13 +618,14 @@ def rosidl_cc_support(name, interfaces, deps, **kwargs): deps = [cc_types_label(name)] + [cc(dep) for dep in deps], **kwargs ) - typesupports["rosidl_typesupport_c"] = typesupport_cc_label(name) native.cc_library( name = cc(name), - deps = [ - cc_types_label(name), + srcs = [ + typesupport_cc_label(name), ] + typesupports.values(), + deps = [cc_types_label(name)], + linkstatic = 1, **kwargs ) @@ -639,7 +636,7 @@ def c_label(name): return ":" + c(name) def c_types(name): - return name + "_c_types" + return name + "__rosidl_c" def c_types_label(name): return ":" + c_types(name) @@ -651,19 +648,19 @@ def py_label(name): return ":" + py(name) def typesupport_c(name): - return name + "_typesupport_c" + return name + "__rosidl_typesupport_c" def typesupport_c_label(name): return ":" + typesupport_c(name) def typesupport_introspection_c(name): - return name + "_typesupport_introspection_c" + return name + "__rosidl_typesupport_introspection_c" def typesupport_introspection_c_label(name): return ":" + typesupport_introspection_c(name) def typesupport_fastrtps_c(name): - return name + "_typesupport_fastrtps_c" + return name + "__rosidl_typesupport_fastrtps_c" def typesupport_fastrtps_c_label(name): return ":" + typesupport_fastrtps_c(name) @@ -711,11 +708,12 @@ def rosidl_py_support(name, interfaces, deps, **kwargs): ) typesupports["rosidl_typesupport_c"] = typesupport_c_label(name) + # Use C typesupport library as entrypoint native.cc_library( name = c(name), - deps = [ - c_types_label(name), - ] + typesupports.values(), + srcs = typesupports.values(), + deps = [c_types_label(name)], + linkstatic = 1, **kwargs ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/cmake/ament_cmake_CMakeLists.txt.in b/drake_ros_bazel_installed/tools/skylark/ros2/resources/cmake/ament_cmake_CMakeLists.txt.in index c976e84b8..6b1394ae8 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/cmake/ament_cmake_CMakeLists.txt.in +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/cmake/ament_cmake_CMakeLists.txt.in @@ -28,7 +28,9 @@ if("@PACKAGE@" STREQUAL "rviz_ogre_vendor") file(GLOB ogre_plugin_libraries "${OGRE_PLUGIN_DIR}/*.so*") target_link_libraries(${PROJECT_NAME} ${ogre_plugin_libraries}) else() - # TODO(hidmic): figure out why this is sometimes necessary ament_target_dependencies(${PROJECT_NAME} @PACKAGE@) - target_link_libraries(${PROJECT_NAME} @PACKAGE@) + # TODO(hidmic): figure out why this is sometimes necessary + if(TARGET @PACKAGE@) + target_link_libraries(${PROJECT_NAME} @PACKAGE@) + endif() endif() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl index 9375e5ba6..1604dba80 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -1,6 +1,7 @@ load("//tools/skylark:execute.bzl", "execute_or_fail") MANIFEST = [ + "cmake_tools/packages.py", "cmake_tools/server_mode.py", "cmake_tools/__init__.py", diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py index de6e1e786..806abeddd 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py @@ -122,11 +122,12 @@ def collect_ament_cmake_package_properties(name, metadata): with cmake_tools.server_mode(project_path) as cmake: cmake.configure(attributes={'cacheArguments': [ '-DCMAKE_PREFIX_PATH="{}"'.format(cmake_prefix_path) - ]}, timeout=20, message_callback=print) + ]}, timeout=30, message_callback=print) cmake.compute(timeout=20, message_callback=print) - codemodel = cmake.codemodel(timeout=5) + codemodel = cmake.codemodel(timeout=10) except: import shutil + shutil.rmtree('error_case', ignore_errors=True) shutil.copytree(project_path, 'error_case') raise From a0880033b07af17cc66aa37e6886cf068ddb47a7 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 24 Jun 2021 19:53:48 -0300 Subject: [PATCH 019/107] Functional oracle and inquirer nodes Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/apps/BUILD.bazel | 28 +++++++++++++ drake_ros_bazel_installed/apps/inquirer.cc | 40 +++++++++++++++++++ drake_ros_bazel_installed/apps/oracle.cc | 46 ++++++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 drake_ros_bazel_installed/apps/inquirer.cc create mode 100644 drake_ros_bazel_installed/apps/oracle.cc diff --git a/drake_ros_bazel_installed/apps/BUILD.bazel b/drake_ros_bazel_installed/apps/BUILD.bazel index c80e845b1..2ab3a84f2 100644 --- a/drake_ros_bazel_installed/apps/BUILD.bazel +++ b/drake_ros_bazel_installed/apps/BUILD.bazel @@ -1,4 +1,6 @@ load("@ros2//:rosidl_tools.bzl", "rosidl_interfaces_group") +load("@ros2//:cc_tools.bzl", "ros_cc_binary") + rosidl_interfaces_group( name = "apps_msgs", @@ -11,3 +13,29 @@ rosidl_interfaces_group( "@ros2//:builtin_interfaces", ], ) + +ros_cc_binary( + name = "oracle", + srcs = ["oracle.cc"], + data = [ + "@ros2//:rmw_cyclonedds_cpp", # pick RMW impl + ], + deps = [ + "//common:common_msgs_cc", + "@ros2//:rclcpp", + ], + tags = ["requires-network"], +) + +ros_cc_binary( + name = "inquirer", + srcs = ["inquirer.cc"], + data = [ + "@ros2//:rmw_cyclonedds_cpp", # pick RMW impl + ], + deps = [ + "//common:common_msgs_cc", + "@ros2//:rclcpp", + ], + tags = ["requires-network"], +) diff --git a/drake_ros_bazel_installed/apps/inquirer.cc b/drake_ros_bazel_installed/apps/inquirer.cc new file mode 100644 index 000000000..2f5ff10a3 --- /dev/null +++ b/drake_ros_bazel_installed/apps/inquirer.cc @@ -0,0 +1,40 @@ +#include +#include + +#include + +#include "common_msgs/msg/status.hpp" + +using namespace std::chrono_literals; + +namespace apps { + +class Inquirer : public rclcpp::Node { +public: + Inquirer() : Node("inquirer") { + using namespace std::placeholders; + status_sub_ = + this->create_subscription( + "status", rclcpp::QoS(rclcpp::KeepLast(1)), + std::bind(&Inquirer::on_status, this, _1)); + } + +private: + void on_status(const common_msgs::msg::Status & msg) { + RCLCPP_INFO( + get_logger(), "(%lu) %s", + msg.sequence_id, msg.message.c_str()); + } + + std::shared_ptr> status_sub_; +}; + +} // namespace apps + +int main(int argc, char ** argv) { + rclcpp::init(argc, argv); + + rclcpp::spin(std::make_shared()); + + rclcpp::shutdown(); +} diff --git a/drake_ros_bazel_installed/apps/oracle.cc b/drake_ros_bazel_installed/apps/oracle.cc new file mode 100644 index 000000000..825a8c036 --- /dev/null +++ b/drake_ros_bazel_installed/apps/oracle.cc @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +#include +#include "common_msgs/msg/status.hpp" + +using namespace std::chrono_literals; + +namespace apps { + +class Oracle : public rclcpp::Node { +public: + Oracle() : Node("oracle") { + status_pub_ = this->create_publisher( + "status", rclcpp::QoS(rclcpp::KeepLast(1))); + + status_timer_ = this->create_wall_timer( + 1s, std::bind(&Oracle::publish_status, this)); + } + +private: + void publish_status() { + common_msgs::msg::Status msg; + msg.stamp = this->get_clock()->now(); + msg.sequence_id = sequence_id_++; + msg.message = "All good!"; + status_pub_->publish(msg); + } + + uint64_t sequence_id_{0u}; + std::shared_ptr status_timer_; + std::shared_ptr> status_pub_; +}; + +} // namespace apps + +int main(int argc, char ** argv) { + rclcpp::init(argc, argv); + + rclcpp::spin(std::make_shared()); + + rclcpp::shutdown(); +} From c32ae73cdf372c83d3fbe1725f0f35bf6d22c857 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 30 Jun 2021 11:38:37 -0300 Subject: [PATCH 020/107] Fix label in ROS 2 rosidl_tools Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl index a9df344b6..b6a8f2667 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl @@ -689,7 +689,7 @@ def rosidl_py_support(name, interfaces, deps, **kwargs): if "rosidl_typesupport_fastrtps_c" in AVAILABLE_TYPESUPPORTS: rosidl_typesupport_fastrtps_c_library( - name = name + "_typesupport_fastrtps_c", + name = typesupport_fastrtps_c(name), group = name, interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [c_types_label(name)] + [c(dep) for dep in deps], From 28bc19d9440d593986cfc1eb65ad9ef45ce8bfd7 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 30 Jun 2021 11:39:26 -0300 Subject: [PATCH 021/107] Updated oracle and inquirer nodes Featuring status topic, query service, and rite action. Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/apps/BUILD.bazel | 7 +- drake_ros_bazel_installed/apps/inquirer.cc | 99 ++++++++++++++- .../apps/msg/{Stats.msg => Status.msg} | 3 +- drake_ros_bazel_installed/apps/oracle.cc | 115 ++++++++++++++++-- .../apps/srv/QueryStats.srv | 2 - .../common/action/Do.action | 4 +- .../common/msg/Status.msg | 1 - .../common/srv/Query.srv | 2 +- 8 files changed, 209 insertions(+), 24 deletions(-) rename drake_ros_bazel_installed/apps/msg/{Stats.msg => Status.msg} (53%) delete mode 100644 drake_ros_bazel_installed/apps/srv/QueryStats.srv diff --git a/drake_ros_bazel_installed/apps/BUILD.bazel b/drake_ros_bazel_installed/apps/BUILD.bazel index 2ab3a84f2..c8ff8f2c9 100644 --- a/drake_ros_bazel_installed/apps/BUILD.bazel +++ b/drake_ros_bazel_installed/apps/BUILD.bazel @@ -5,8 +5,7 @@ load("@ros2//:cc_tools.bzl", "ros_cc_binary") rosidl_interfaces_group( name = "apps_msgs", interfaces = [ - "msg/Stats.msg", - "srv/QueryStats.srv", + "msg/Status.msg", ], deps = [ "//common:common_msgs", @@ -21,8 +20,10 @@ ros_cc_binary( "@ros2//:rmw_cyclonedds_cpp", # pick RMW impl ], deps = [ + ":apps_msgs_cc", "//common:common_msgs_cc", "@ros2//:rclcpp", + "@ros2//:rclcpp_action", ], tags = ["requires-network"], ) @@ -34,8 +35,10 @@ ros_cc_binary( "@ros2//:rmw_cyclonedds_cpp", # pick RMW impl ], deps = [ + ":apps_msgs_cc", "//common:common_msgs_cc", "@ros2//:rclcpp", + "@ros2//:rclcpp_action", ], tags = ["requires-network"], ) diff --git a/drake_ros_bazel_installed/apps/inquirer.cc b/drake_ros_bazel_installed/apps/inquirer.cc index 2f5ff10a3..17169e287 100644 --- a/drake_ros_bazel_installed/apps/inquirer.cc +++ b/drake_ros_bazel_installed/apps/inquirer.cc @@ -2,8 +2,12 @@ #include #include +#include +#include -#include "common_msgs/msg/status.hpp" +#include "apps_msgs/msg/status.hpp" +#include "common_msgs/srv/query.hpp" +#include "common_msgs/action/do.hpp" using namespace std::chrono_literals; @@ -14,19 +18,102 @@ class Inquirer : public rclcpp::Node { Inquirer() : Node("inquirer") { using namespace std::placeholders; status_sub_ = - this->create_subscription( + this->create_subscription( "status", rclcpp::QoS(rclcpp::KeepLast(1)), std::bind(&Inquirer::on_status, this, _1)); + + oracle_query_client_ = this->create_client("query"); + + oracle_action_client_ = rclcpp_action::create_client(this, "do"); + + inquire_timer_ = this->create_wall_timer(5s, std::bind(&Inquirer::inquire, this)); } private: - void on_status(const common_msgs::msg::Status & msg) { + using QueryClient = rclcpp::Client; + + void handle_query_response(QueryClient::SharedFuture future) const { + RCLCPP_INFO(this->get_logger(), "oracle said: %s", future.get()->reply.c_str()); + } + + using ActionGoalHandle = rclcpp_action::ClientGoalHandle; + void handle_rite_request_response(const std::shared_ptr handle) const { + if (!handle) { + RCLCPP_ERROR(this->get_logger(), "oracle rejected rite request"); + } else { + RCLCPP_INFO(this->get_logger(), "oracle rite in progress"); + } + } + + void handle_rite_feedback( + std::shared_ptr handle, + const std::shared_ptr feedback) const + { + RCLCPP_INFO(this->get_logger(), "oracle is %s", feedback->message.c_str()); + } + + void handle_rite_result(const ActionGoalHandle::WrappedResult& result) const + { + switch (result.code) { + case rclcpp_action::ResultCode::SUCCEEDED: + RCLCPP_INFO(this->get_logger(), "oracle rite is complete"); + break; + case rclcpp_action::ResultCode::ABORTED: + RCLCPP_ERROR(this->get_logger(), + "oracle rite aborted due to %s", + result.result->reason.c_str()); + break; + case rclcpp_action::ResultCode::CANCELED: + RCLCPP_WARN(this->get_logger(), "oracle rite was cancelled"); + break; + default: + RCLCPP_ERROR(this->get_logger(), "oracle rite outcome unknown"); + break; + } + } + + void inquire() const { + using namespace std::placeholders; + + auto request = std::make_shared(); + request->query = "how's it going?"; + if (oracle_query_client_->service_is_ready()) { + RCLCPP_INFO(this->get_logger(), "oracle, %s", request->query.c_str()); + oracle_query_client_->async_send_request( + request, std::bind(&Inquirer::handle_query_response, this, _1)); + } else { + RCLCPP_WARN(this->get_logger(), "oracle not available for queries"); + } + + if (oracle_action_client_->action_server_is_ready()) { + rclcpp_action::Client::SendGoalOptions options; + options.goal_response_callback = + std::bind(&Inquirer::handle_rite_request_response, this, _1); + options.feedback_callback = + std::bind(&Inquirer::handle_rite_feedback, this, _1, _2); + options.result_callback = + std::bind(&Inquirer::handle_rite_result, this, _1); + common_msgs::action::Do::Goal goal; + goal.action = "rite"; + goal.period = rclcpp::Duration::from_seconds(0.1); + goal.timeout = rclcpp::Duration::from_seconds(1.0); + oracle_action_client_->async_send_goal(goal, options); + } else { + RCLCPP_WARN(this->get_logger(), "oracle not available for actions"); + } + } + + void on_status(const apps_msgs::msg::Status & msg) const { RCLCPP_INFO( - get_logger(), "(%lu) %s", - msg.sequence_id, msg.message.c_str()); + get_logger(), "%s status (%lu): %s", + msg.origin.c_str(), msg.status.sequence_id, + msg.status.message.c_str()); } - std::shared_ptr> status_sub_; + std::shared_ptr> status_sub_; + std::shared_ptr> oracle_query_client_; + std::shared_ptr> oracle_action_client_; + std::shared_ptr inquire_timer_; }; } // namespace apps diff --git a/drake_ros_bazel_installed/apps/msg/Stats.msg b/drake_ros_bazel_installed/apps/msg/Status.msg similarity index 53% rename from drake_ros_bazel_installed/apps/msg/Stats.msg rename to drake_ros_bazel_installed/apps/msg/Status.msg index 2dd905423..bf0496267 100644 --- a/drake_ros_bazel_installed/apps/msg/Stats.msg +++ b/drake_ros_bazel_installed/apps/msg/Status.msg @@ -1,4 +1,3 @@ builtin_interfaces/Time stamp common_msgs/Status status -float64[100] memory_usage -float64[100] cpu_load +string origin diff --git a/drake_ros_bazel_installed/apps/oracle.cc b/drake_ros_bazel_installed/apps/oracle.cc index 825a8c036..7be098892 100644 --- a/drake_ros_bazel_installed/apps/oracle.cc +++ b/drake_ros_bazel_installed/apps/oracle.cc @@ -1,11 +1,16 @@ #include #include #include +#include #include +#include +#include +#include -#include -#include "common_msgs/msg/status.hpp" +#include "apps_msgs/msg/status.hpp" +#include "common_msgs/action/do.hpp" +#include "common_msgs/srv/query.hpp" using namespace std::chrono_literals; @@ -13,26 +18,120 @@ namespace apps { class Oracle : public rclcpp::Node { public: - Oracle() : Node("oracle") { - status_pub_ = this->create_publisher( + Oracle() : Node("oracle") + { + using namespace std::placeholders; + + status_pub_ = this->create_publisher( "status", rclcpp::QoS(rclcpp::KeepLast(1))); + query_server_ = this->create_service( + "query", std::bind(&Oracle::handle_query, this, _1, _2)); + + action_server_ = rclcpp_action::create_server( + this, "do", std::bind(&Oracle::handle_action_goal, this, _1, _2), + std::bind(&Oracle::handle_cancelled_action, this, _1), + std::bind(&Oracle::handle_accepted_action, this, _1)); + status_timer_ = this->create_wall_timer( 1s, std::bind(&Oracle::publish_status, this)); } private: + + rclcpp_action::GoalResponse + handle_action_goal( + const rclcpp_action::GoalUUID &, + const std::shared_ptr goal) + { + if (goal->action != "rite") + { + RCLCPP_WARN(this->get_logger(), "Don't know how to %s", goal->action.c_str()); + return rclcpp_action::GoalResponse::REJECT; + } + return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE; + } + + using GoalHandle = rclcpp_action::ServerGoalHandle; + + rclcpp_action::CancelResponse handle_cancelled_action(const std::shared_ptr) + { + return rclcpp_action::CancelResponse::ACCEPT; + } + + void handle_accepted_action(const std::shared_ptr handle) + { + action_start_time_ = this->get_clock()->now(); + action_loop_ = this->create_wall_timer( + rclcpp::Duration{handle->get_goal()->period}.to_chrono(), + [this, handle]() { this->handle_rite_action(handle); }); + } + + void handle_rite_action(const std::shared_ptr handle) + { + if (handle->is_canceling()) + { + auto result = std::make_shared(); + handle->canceled(result); + action_loop_->cancel(); + return; + } + + auto current_time = this->get_clock()->now(); + if (current_time - action_start_time_ > handle->get_goal()->timeout) + { + auto result = std::make_shared(); + result->reason = "timeout"; + handle->abort(result); + action_loop_->cancel(); + return; + } + + if (is_rite_complete_(gen_)) + { + auto result = std::make_shared(); + handle->succeed(result); + action_loop_->cancel(); + return; + } + + auto feedback = std::make_shared(); + feedback->message = "chanting"; + handle->publish_feedback(feedback); + } + + void handle_query( + const std::shared_ptr request, + std::shared_ptr response) const + { + if (request->query == "how's it going?") { + response->reply = "all good!"; + } else { + response->reply = "don't know"; + } + } + + void publish_status() { - common_msgs::msg::Status msg; + apps_msgs::msg::Status msg; msg.stamp = this->get_clock()->now(); - msg.sequence_id = sequence_id_++; - msg.message = "All good!"; + msg.status.sequence_id = sequence_id_++; + msg.status.message = "OK"; + msg.origin = "oracle"; status_pub_->publish(msg); } + std::random_device rd_; + std::mt19937 gen_{rd_()}; + std::bernoulli_distribution is_rite_complete_{0.1}; + uint64_t sequence_id_{0u}; + rclcpp::Time action_start_time_; + std::shared_ptr action_loop_; std::shared_ptr status_timer_; - std::shared_ptr> status_pub_; + std::shared_ptr> status_pub_; + std::shared_ptr> query_server_; + std::shared_ptr> action_server_; }; } // namespace apps diff --git a/drake_ros_bazel_installed/apps/srv/QueryStats.srv b/drake_ros_bazel_installed/apps/srv/QueryStats.srv deleted file mode 100644 index f21c7c9f3..000000000 --- a/drake_ros_bazel_installed/apps/srv/QueryStats.srv +++ /dev/null @@ -1,2 +0,0 @@ ---- -Stats stats diff --git a/drake_ros_bazel_installed/common/action/Do.action b/drake_ros_bazel_installed/common/action/Do.action index 0fe8346c8..ac9667a22 100644 --- a/drake_ros_bazel_installed/common/action/Do.action +++ b/drake_ros_bazel_installed/common/action/Do.action @@ -1,7 +1,7 @@ string action +builtin_interfaces/Duration period builtin_interfaces/Duration timeout --- -bool ok string reason --- -string feedback +string message diff --git a/drake_ros_bazel_installed/common/msg/Status.msg b/drake_ros_bazel_installed/common/msg/Status.msg index e2876cd80..ffa19e47a 100644 --- a/drake_ros_bazel_installed/common/msg/Status.msg +++ b/drake_ros_bazel_installed/common/msg/Status.msg @@ -1,3 +1,2 @@ -builtin_interfaces/Time stamp uint64 sequence_id string message diff --git a/drake_ros_bazel_installed/common/srv/Query.srv b/drake_ros_bazel_installed/common/srv/Query.srv index 974c97c04..911e03928 100644 --- a/drake_ros_bazel_installed/common/srv/Query.srv +++ b/drake_ros_bazel_installed/common/srv/Query.srv @@ -1,3 +1,3 @@ string query --- -string[] results +string reply From 0be49b58fa95bcf729d672b9c125f2b91aed4a38 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 30 Jun 2021 11:41:08 -0300 Subject: [PATCH 022/107] Add fastcdr to ROS 2 repository package whitelist Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl b/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl index fd0e9d975..72084573c 100644 --- a/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl +++ b/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl @@ -11,7 +11,7 @@ def ros2_repository(name, overlays = []): "geometry_msgs", "visualization_msgs", "rosidl_default_generators", - "rosidl_generator_cpp", + "fastcdr", "rcpputils", "rcutils", "rclcpp", From 351f065bdfdbf72dd48704982957564e2a672468 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 2 Jul 2021 14:22:10 -0300 Subject: [PATCH 023/107] Use single source as main in ros_py_binary() Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl index cefb520cb..0964eb66d 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl @@ -56,7 +56,10 @@ def ros_py_binary( """ binary = "_" + name if not main: - main = name + ".py" + if len(srcs) == 1: + main = srcs[0] + else: + main = name + ".py" native.py_binary( name = binary, srcs = srcs, From 8b97a60bded8f1c6a8d518ce96a7ab2a207dd4bb Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 2 Jul 2021 14:24:40 -0300 Subject: [PATCH 024/107] Fix to_starlark_string_dict() utility Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/ros2bzl/utilities.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/utilities.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/utilities.py index f8a1d8230..4f8eb11be 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/utilities.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/utilities.py @@ -1,8 +1,19 @@ import functools +import json + +class StarlarkEncoder(json.JSONEncoder): + # Use JSON format as a basis + def default(self, obj): + if isinstance(obj, bool): + return repr(obj) + return super().default(obj) def to_starlark_string_dict(d): - # Replace single-quotes with double-quotes - return {k: repr(v).replace("'", '"') for k, v in d.items()} + encoder = StarlarkEncoder() + return { + k: encoder.encode(v) + for k, v in d.items() + } def interpolate(template, config): content = template From 361ae642d62228ecf530cb4fc6401a47d9a1a6d0 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 2 Jul 2021 14:25:07 -0300 Subject: [PATCH 025/107] Pick up buildtool_depends as build dependencies Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/ros2bzl/scrapping/metadata.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/metadata.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/metadata.py index 0dd3015b3..4d97bcf6e 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/metadata.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/metadata.py @@ -11,6 +11,9 @@ def parse_package_xml(path_to_package_xml): exec_depends = set([ tag.text for tag in tree.findall('./exec_depend') ]) + buildtool_depends = set([ + tag.text for tag in tree.findall('./buildtool_depend') + ]) build_export_depends = set([ tag.text for tag in tree.findall('./build_export_depend') ]) @@ -23,7 +26,7 @@ def parse_package_xml(path_to_package_xml): build_type = tree.find('./export/build_type').text return dict( - build_dependencies=build_export_depends | depends, + build_dependencies=build_export_depends | depends | buildtool_depends, run_dependencies=exec_depends | depends, group_dependencies=group_depends, groups=member_of_groups, From 8a0445f9d183d7c03f356a286f4d102a3159c997 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 2 Jul 2021 14:26:14 -0300 Subject: [PATCH 026/107] Refactor ROS 2 Python package scrapping Expose non-extension shared libraries for downstream consumption Signed-off-by: Michel Hidalgo --- .../package_py_library_with_cc_libs.bazel.tpl | 21 ++++++++ .../tools/skylark/ros2/ros2.bzl | 1 + .../ros2/ros2bzl/scrapping/ament_python.py | 44 +++++++++-------- .../tools/skylark/ros2/ros2bzl/templates.py | 48 ++++++++++++------- 4 files changed, 77 insertions(+), 37 deletions(-) create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_py_library_with_cc_libs.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_py_library_with_cc_libs.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_py_library_with_cc_libs.bazel.tpl new file mode 100644 index 000000000..6b51de3c5 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_py_library_with_cc_libs.bazel.tpl @@ -0,0 +1,21 @@ +cc_library( + name = @cc_name@, + srcs = @cc_libs@, + data = @cc_libs@, + deps = @cc_deps@, +) + +py_library( + name = @name@, + srcs = glob(["{}/**/*.py".format(x) for x in @tops@]), + data = glob( + include=[ + "{}/**/*.*".format(x) for x in @tops@ + ] + [ + "{}/*".format(x) for x in @eggs@ + ], + exclude=["**/*.py", "**/*.so"], + ) + @data@, + imports = @imports@, + deps = @deps@, +) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl index 1604dba80..5940b2d56 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -16,6 +16,7 @@ MANIFEST = [ "resources/bazel/snippets/package_cc_library_with_runtime_environ.bazel.tpl", "resources/bazel/snippets/package_meta_py_library.bazel.tpl", "resources/bazel/snippets/package_py_library.bazel.tpl", + "resources/bazel/snippets/package_py_library_with_cc_libs.bazel.tpl", "resources/bazel/snippets/package_share_filegroup.bazel.tpl", "resources/bazel/snippets/prologue.bazel.tpl", diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py index 19ae4845f..be93ff688 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py @@ -1,4 +1,5 @@ import glob +import sysconfig from importlib.metadata import distribution from importlib.metadata import PackageNotFoundError @@ -7,6 +8,9 @@ from ros2bzl.scrapping.system import is_system_library +EXTENSION_SUFFIX = sysconfig.get_config_var('EXT_SUFFIX') + + def find_python_package(name): dist = distribution(name) top_level = dist.read_text('top_level.txt') @@ -17,19 +21,21 @@ def find_python_package(name): def collect_ament_python_package_properties(name, metadata): egg_path, top_level = find_python_package(name) properties = {'python_packages': [(egg_path, top_level)]} - cc_extensions = glob.glob( - '{}/**/*.so'.format(top_level), recursive=True - ) - if cc_extensions: - cc_extensions_deps = set() - for ext in cc_extensions: - cc_extensions_deps.update([ - library - for library in find_library_dependencies(ext) - if not is_system_library(library) - ]) - properties['cc_extensions'] = cc_extensions - properties['cc_extensions'] += list(cc_extensions_deps) + cc_libraries = glob.glob('{}/**/*.so'.format(top_level), recursive=True) + if cc_libraries: + cc_libraries.extend(set( + dep for library in cc_libraries + for dep in find_library_dependencies(library) + if not is_system_library(dep) + )) + properties['cc_extensions'] = [ + lib for lib in cc_libraries + if lib.endswith(EXTENSION_SUFFIX) + ] + properties['cc_libraries'] = [ + lib for lib in cc_libraries + if not lib.endswith(EXTENSION_SUFFIX) + ] return properties @@ -44,7 +50,7 @@ def collect_ament_python_package_direct_properties(name, metadata, dependencies, properties = dict(ament_python_cache[name]) - if 'cc_extensions' in properties: + if 'cc_libraries' in properties: ament_cmake_cache = cache['ament_cmake'] for dependency_name, dependency_metadata in dependencies.items(): dependency_libraries = [] @@ -66,12 +72,12 @@ def collect_ament_python_package_direct_properties(name, metadata, dependencies, dependency_name, dependency_metadata ) dependency_properties = ament_python_cache[dependency_name] - if 'cc_extensions' in dependency_properties: + if 'cc_libraries' in dependency_properties: dependency_libraries.extend( - dependency_properties['cc_extensions']) + dependency_properties['cc_libraries']) # Remove duplicates maintaining order - properties['cc_extensions'] = [ - ext for ext in properties['cc_extensions'] - if ext not in dependency_libraries + properties['cc_libraries'] = [ + lib for lib in properties['cc_libraries'] + if lib not in dependency_libraries ] return properties diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py index 355c19fe5..11651acb0 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py @@ -28,7 +28,7 @@ def labels_with(suffix): share_name, share_label = labels_with(suffix='_share') -c_name, _ = labels_with(suffix='_c') +c_name, c_label = labels_with(suffix='_c') cc_name, cc_label = labels_with(suffix='_cc') py_name, py_label = labels_with(suffix='_py') meta_py_name, meta_py_label = labels_with(suffix='_transitively_py') @@ -145,48 +145,60 @@ def configure_package_py_library(name, metadata, properties, dependencies, extra tops = [sandbox(top_level) for _, top_level in properties['python_packages']] imports = [os.path.dirname(egg) for egg in eggs] + template = 'bazel/snippets/package_py_library.bazel.tpl' + config = {'name': target_name, 'tops': tops, 'eggs': eggs, 'imports': imports} + deps = [] for dependency_name, dependency_metadata in dependencies.items(): if 'py' in dependency_metadata.get('langs', []): deps.append(py_label(dependency_name, dependency_metadata)) elif 'py (transitively)' in dependency_metadata.get('langs', []): deps.append(meta_py_label(dependency_name, dependency_metadata)) + config['deps'] = deps data = [share_label(name, metadata)] if 'cc' in metadata.get('langs', []): data.append(cc_label(name, metadata)) if 'cc_extensions' in properties: + # Bring in C/C++ dependencies cc_deps = [ cc_label(dependency_name, dependency_metadata) for dependency_name, dependency_metadata in dependencies.items() if 'cc' in dependency_metadata.get('langs', []) ] - cc_extensions = [sandbox(ext) for ext in properties['cc_extensions']] + # Expose C/C++ libraries if any + if 'cc_libraries' in properties: + template = 'bazel/snippets/package_py_library_with_cc_libs.bazel.tpl' + config.update({ + 'cc_name': c_name(target_name, metadata), + 'cc_libs': [sandbox(lib) for lib in properties['cc_libraries']], + 'cc_deps': cc_deps + }) + data.append(c_label(target_name, metadata)) + else: + data.extend(cc_deps) # Prepare runfiles to support dynamic loading + cc_extensions = [ + sandbox(extension) for extension in properties['cc_extensions']] data.extend(cc_extensions) - data.extend(cc_deps) - # Add in plugins, if any - if 'plugin_libraries' in metadata: - data.extend( - sandbox(find_library_path(library)) - for library in metadata['plugin_libraries'] - ) + + # Add in plugins, if any + if 'plugin_libraries' in metadata: + data.extend( + sandbox(find_library_path(library)) + for library in metadata['plugin_libraries'] + ) if extras and 'data' in extras: data.extend(extras['data'].get(target_name, [])) + config['data'] = data + return ( target_name, - load_resource('bazel/snippets/package_py_library.bazel.tpl'), - to_starlark_string_dict({ - 'name': target_name, - 'tops': tops, - 'eggs': eggs, - 'imports': imports, - 'data': data, - 'deps': deps - }) + load_resource(template), + to_starlark_string_dict(config) ) From be67addf0ff1f436aa4746d80495b86f954e3eaf Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 2 Jul 2021 14:31:19 -0300 Subject: [PATCH 027/107] Fix up rosidl_tools for Python interface generation Signed-off-by: Michel Hidalgo --- .../ros2/resources/bazel/rosidl_tools.bzl.tpl | 83 ++++++++++--------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl index b6a8f2667..4af393ae7 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl @@ -210,24 +210,18 @@ def rosidl_cc_library( **kwargs ) -def _c_extension(name): - return name + "_c_extension" + PYTHON_EXTENSION_SUFFIX +def _c_typesupport_extension(group, typesupport_name): + return "{}_s__{}".format(group, typesupport_name) + PYTHON_EXTENSION_SUFFIX -def _c_extension_label(name): - return ":" + _c_extension(name) - -def _c_typesupport_extension(package, name): - return "{}__{}".format(package, name) + PYTHON_EXTENSION_SUFFIX - -def _c_typesupport_extension_label(package, name): - return ":" + _c_typesupport_extension(package, name) +def _c_typesupport_extension_label(group, typesupport_name): + return ":" + _c_typesupport_extension(group, typesupport_name) def rosidl_py_library( name, group, interfaces, typesupports, includes = [], c_deps = [], py_deps = [], **kwargs ): - include, root = _generated_source_paths(group, "py") + import_, root = _generated_source_paths(group, "py") generated_c_sources = [] generated_py_sources = [] @@ -262,39 +256,41 @@ def rosidl_py_library( **kwargs ) - native.cc_binary( - name = _c_extension(name), + native.cc_library( + name = c(name), srcs = generated_c_sources, - linkshared = 1, deps = c_deps + [ - "@python_dev//:libs", - ], + c(dep) for dep in py_deps + ] + ["@python_dev//:libs"], **kwargs ) c_typesupport_extension_deps = c_deps + [ - _c_extension_label(name), + c_label(name), "@REPOSITORY_ROOT@:rosidl_generator_py_cc", "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", "@REPOSITORY_ROOT@:rosidl_typesupport_c_cc", "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", "@python_dev//:libs", ] - py_data = [_c_extension_label(name)] + py_data = [] for typesupport_name, typesupport_library in typesupports.items(): deps = list(c_typesupport_extension_deps) if typesupport_library not in deps: deps.append(typesupport_library) + c_typesupport_extension = "{}/{}".format( + root, _c_typesupport_extension(group, typesupport_name)) native.cc_binary( - name = _c_typesupport_extension(group, typesupport_name), + name = c_typesupport_extension, srcs = generated_c_sources_per_typesupport[typesupport_name], - deps = deps, linkshared = 1, **kwargs + deps = deps, linkshared = True, **kwargs ) - py_data.append(_c_typesupport_extension_label(group, typesupport_name)) + py_data.append(c_typesupport_extension) native.py_library( name = name, srcs = generated_py_sources, + imports = [import_], data = py_data, deps = py_deps, **kwargs @@ -331,7 +327,6 @@ def rosidl_typesupport_fastrtps_cc_library( name = name, srcs = generated_cc_sources + generated_cc_headers, includes = [include], - linkshared = 1, deps = deps + [ "@REPOSITORY_ROOT@:fastcdr_cc", "@REPOSITORY_ROOT@:rmw_cc", @@ -339,6 +334,7 @@ def rosidl_typesupport_fastrtps_cc_library( "@REPOSITORY_ROOT@:rosidl_typesupport_fastrtps_cpp_cc", "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", ], + linkshared = True, **kwargs ) @@ -373,7 +369,7 @@ def rosidl_typesupport_fastrtps_c_library( name = name, srcs = generated_c_sources + generated_c_headers, includes = [include], - linkshared = 1, + linkshared = True, deps = deps + [ "@REPOSITORY_ROOT@:fastcdr_cc", "@REPOSITORY_ROOT@:rmw_cc", @@ -412,12 +408,20 @@ def rosidl_typesupport_introspection_c_library( **kwargs ) + native.cc_library( + name = name + "_hdrs", + hdrs = generated_c_headers, + linkstatic = True, + **kwargs + ) + native.cc_binary( name = name, - srcs = generated_c_sources + generated_c_headers, + srcs = generated_c_sources, includes = [include], - linkshared = 1, + linkshared = True, deps = deps + [ + ":" + name + "_hdrs", "@REPOSITORY_ROOT@:rosidl_typesupport_introspection_c_cc", ], **kwargs @@ -453,7 +457,7 @@ def rosidl_typesupport_introspection_cc_library( name = name, srcs = generated_cc_sources + generated_cc_headers, includes = [include], - linkshared = 1, + linkshared = True, deps = deps + [ "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", @@ -492,10 +496,10 @@ def rosidl_typesupport_c_library( native.cc_binary( name = name, - srcs = generated_c_sources + generated_c_headers + [ - ts for ts in typesupports.values()], + srcs = generated_c_sources + generated_c_headers, includes = [include], - linkshared = 1, + linkshared = True, + data = typesupports.values(), deps = deps + [ "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", "@REPOSITORY_ROOT@:rosidl_typesupport_c_cc", @@ -531,10 +535,10 @@ def rosidl_typesupport_cc_library( native.cc_binary( name = name, - srcs = generated_cc_sources + [ - ts for ts in typesupports.values()], + srcs = generated_cc_sources, + data = typesupports.values(), includes = [include], - linkshared = 1, + linkshared = True, deps = deps + [ "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", "@REPOSITORY_ROOT@:rosidl_runtime_cpp_cc", @@ -622,10 +626,10 @@ def rosidl_cc_support(name, interfaces, deps, **kwargs): native.cc_library( name = cc(name), srcs = [ - typesupport_cc_label(name), + typesupport_cc_label(name) ] + typesupports.values(), deps = [cc_types_label(name)], - linkstatic = 1, + linkstatic = True, **kwargs ) @@ -708,12 +712,13 @@ def rosidl_py_support(name, interfaces, deps, **kwargs): ) typesupports["rosidl_typesupport_c"] = typesupport_c_label(name) - # Use C typesupport library as entrypoint native.cc_library( name = c(name), srcs = typesupports.values(), - deps = [c_types_label(name)], - linkstatic = 1, + deps = [ + c_types_label(name) + ] + typesupports.values(), + linkstatic = True, **kwargs ) @@ -723,9 +728,7 @@ def rosidl_py_support(name, interfaces, deps, **kwargs): group = name, interfaces = interfaces, includes = [defs(dep) for dep in deps], py_deps = [py(dep) for dep in deps], - c_deps = [c(dep) for dep in deps] + [ - c_types_label(name), typesupports["rosidl_typesupport_c"] - ], + c_deps = [c_label(name)] + [c(dep) for dep in deps], **kwargs ) From f65feb1088cbddd25fa7ad0a2a584e2761b21c36 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 2 Jul 2021 14:32:27 -0300 Subject: [PATCH 028/107] Functional oracle and inquirer nodes in C++ and Python Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/apps/BUILD.bazel | 33 +++++++- drake_ros_bazel_installed/apps/inquirer.cc | 26 +++--- drake_ros_bazel_installed/apps/inquirer.py | 90 ++++++++++++++++++++ drake_ros_bazel_installed/apps/oracle.cc | 1 - drake_ros_bazel_installed/apps/oracle.py | 98 ++++++++++++++++++++++ 5 files changed, 232 insertions(+), 16 deletions(-) create mode 100644 drake_ros_bazel_installed/apps/inquirer.py create mode 100644 drake_ros_bazel_installed/apps/oracle.py diff --git a/drake_ros_bazel_installed/apps/BUILD.bazel b/drake_ros_bazel_installed/apps/BUILD.bazel index c8ff8f2c9..84d4189be 100644 --- a/drake_ros_bazel_installed/apps/BUILD.bazel +++ b/drake_ros_bazel_installed/apps/BUILD.bazel @@ -1,5 +1,6 @@ load("@ros2//:rosidl_tools.bzl", "rosidl_interfaces_group") load("@ros2//:cc_tools.bzl", "ros_cc_binary") +load("@ros2//:py_tools.bzl", "ros_py_binary") rosidl_interfaces_group( @@ -14,7 +15,7 @@ rosidl_interfaces_group( ) ros_cc_binary( - name = "oracle", + name = "oracle_cc", srcs = ["oracle.cc"], data = [ "@ros2//:rmw_cyclonedds_cpp", # pick RMW impl @@ -29,7 +30,7 @@ ros_cc_binary( ) ros_cc_binary( - name = "inquirer", + name = "inquirer_cc", srcs = ["inquirer.cc"], data = [ "@ros2//:rmw_cyclonedds_cpp", # pick RMW impl @@ -42,3 +43,31 @@ ros_cc_binary( ], tags = ["requires-network"], ) + +ros_py_binary( + name = "oracle_py", + srcs = ["oracle.py"], + data = [ + "@ros2//:rmw_cyclonedds_cpp", # pick RMW impl + ], + deps = [ + ":apps_msgs_py", + "//common:common_msgs_py", + "@ros2//:rclpy", + ], + tags = ["requires-network"], +) + +ros_py_binary( + name = "inquirer_py", + srcs = ["inquirer.py"], + data = [ + "@ros2//:rmw_cyclonedds_cpp", # pick RMW impl + ], + deps = [ + ":apps_msgs_py", + "//common:common_msgs_py", + "@ros2//:rclpy", + ], + tags = ["requires-network"], +) diff --git a/drake_ros_bazel_installed/apps/inquirer.cc b/drake_ros_bazel_installed/apps/inquirer.cc index 17169e287..b86d0f435 100644 --- a/drake_ros_bazel_installed/apps/inquirer.cc +++ b/drake_ros_bazel_installed/apps/inquirer.cc @@ -22,9 +22,9 @@ class Inquirer : public rclcpp::Node { "status", rclcpp::QoS(rclcpp::KeepLast(1)), std::bind(&Inquirer::on_status, this, _1)); - oracle_query_client_ = this->create_client("query"); + query_client_ = this->create_client("query"); - oracle_action_client_ = rclcpp_action::create_client(this, "do"); + action_client_ = rclcpp_action::create_client(this, "do"); inquire_timer_ = this->create_wall_timer(5s, std::bind(&Inquirer::inquire, this)); } @@ -32,7 +32,7 @@ class Inquirer : public rclcpp::Node { private: using QueryClient = rclcpp::Client; - void handle_query_response(QueryClient::SharedFuture future) const { + void handle_reply(QueryClient::SharedFuture future) const { RCLCPP_INFO(this->get_logger(), "oracle said: %s", future.get()->reply.c_str()); } @@ -67,7 +67,7 @@ class Inquirer : public rclcpp::Node { RCLCPP_WARN(this->get_logger(), "oracle rite was cancelled"); break; default: - RCLCPP_ERROR(this->get_logger(), "oracle rite outcome unknown"); + RCLCPP_ERROR(this->get_logger(), "oracle rite status unknown"); break; } } @@ -75,17 +75,17 @@ class Inquirer : public rclcpp::Node { void inquire() const { using namespace std::placeholders; - auto request = std::make_shared(); - request->query = "how's it going?"; - if (oracle_query_client_->service_is_ready()) { + if (query_client_->service_is_ready()) { + auto request = std::make_shared(); + request->query = "how's it going?"; RCLCPP_INFO(this->get_logger(), "oracle, %s", request->query.c_str()); - oracle_query_client_->async_send_request( - request, std::bind(&Inquirer::handle_query_response, this, _1)); + query_client_->async_send_request( + request, std::bind(&Inquirer::handle_reply, this, _1)); } else { RCLCPP_WARN(this->get_logger(), "oracle not available for queries"); } - if (oracle_action_client_->action_server_is_ready()) { + if (action_client_->action_server_is_ready()) { rclcpp_action::Client::SendGoalOptions options; options.goal_response_callback = std::bind(&Inquirer::handle_rite_request_response, this, _1); @@ -97,7 +97,7 @@ class Inquirer : public rclcpp::Node { goal.action = "rite"; goal.period = rclcpp::Duration::from_seconds(0.1); goal.timeout = rclcpp::Duration::from_seconds(1.0); - oracle_action_client_->async_send_goal(goal, options); + action_client_->async_send_goal(goal, options); } else { RCLCPP_WARN(this->get_logger(), "oracle not available for actions"); } @@ -111,8 +111,8 @@ class Inquirer : public rclcpp::Node { } std::shared_ptr> status_sub_; - std::shared_ptr> oracle_query_client_; - std::shared_ptr> oracle_action_client_; + std::shared_ptr> query_client_; + std::shared_ptr> action_client_; std::shared_ptr inquire_timer_; }; diff --git a/drake_ros_bazel_installed/apps/inquirer.py b/drake_ros_bazel_installed/apps/inquirer.py new file mode 100644 index 000000000..5e772d68f --- /dev/null +++ b/drake_ros_bazel_installed/apps/inquirer.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 + +import action_msgs.msg +import rclpy +import rclpy.action +import rclpy.duration +import rclpy.node + +import apps_msgs.msg +import common_msgs.action +import common_msgs.srv + + +class Inquirer(rclpy.node.Node): + + def __init__(self): + super().__init__('inquirer') + self._status_sub = self.create_subscription( + apps_msgs.msg.Status, 'status', self._on_status, 1) + self._query_client = self.create_client(common_msgs.srv.Query, 'query') + self._action_client = rclpy.action.ActionClient( + self, common_msgs.action.Do, 'do') + self._inquire_timer = self.create_timer(5.0, self.inquire) + + def _on_status(self, msg): + self.get_logger().info('{} status ({}): {}'.format( + msg.origin, msg.status.sequence_id, msg.status.message + )) + + def _handle_reply(self, future): + self.get_logger().info('oracle said: ' + future.result().reply) + + def _handle_rite_request_response(self, future): + handle = future.result() + if not handle.accepted: + self.get_logger().error('oracle rejected rite request') + return + self.get_logger().info('oracle rite in progress') + future = handle.get_result_async() + future.add_done_callback(self._handle_rite_result) + + def _handle_rite_feedback(self, msg): + self.get_logger().info('oracle is ' + msg.feedback.message) + + def _handle_rite_result(self, future): + result = future.result() + if result.status == action_msgs.msg.GoalStatus.STATUS_SUCCEEDED: + self.get_logger().info('oracle rite is complete') + elif result.status == action_msgs.msg.GoalStatus.STATUS_ABORTED: + self.get_logger().error( + 'oracle rite aborted due to ' + result.result.reason + ) + elif result.status == action_msgs.msg.GoalStatus.STATUS_CANCELED: + self.get_logger().error('oracle rite was cancelled') + else: + self.get_logger().error('oracle rite status unknown') + + def inquire(self): + if self._query_client.service_is_ready(): + request = common_msgs.srv.Query.Request() + request.query = "how's it going?" + self.get_logger().info('oracle, ' + request.query) + future = self._query_client.call_async(request) + future.add_done_callback(self._handle_reply) + else: + self.get_logger().warning('oracle not available for queries') + if self._action_client.server_is_ready(): + goal = common_msgs.action.Do.Goal() + goal.action = 'rite' + goal.period = rclpy.duration.Duration(seconds=0.1).to_msg() + goal.timeout = rclpy.duration.Duration(seconds=1.0).to_msg() + future = self._action_client.send_goal_async( + goal, feedback_callback=self._handle_rite_feedback) + future.add_done_callback(self._handle_rite_request_response) + else: + self.get_logger().warning('oracle not available for actions') + +def main(): + rclpy.init() + + try: + rclpy.spin(Inquirer()) + except KeyboardInterrupt: + pass + finally: + rclpy.shutdown() + + +if __name__ == '__main__': + main() diff --git a/drake_ros_bazel_installed/apps/oracle.cc b/drake_ros_bazel_installed/apps/oracle.cc index 7be098892..99c56711f 100644 --- a/drake_ros_bazel_installed/apps/oracle.cc +++ b/drake_ros_bazel_installed/apps/oracle.cc @@ -111,7 +111,6 @@ class Oracle : public rclcpp::Node { } } - void publish_status() { apps_msgs::msg::Status msg; msg.stamp = this->get_clock()->now(); diff --git a/drake_ros_bazel_installed/apps/oracle.py b/drake_ros_bazel_installed/apps/oracle.py new file mode 100644 index 000000000..f9dd86567 --- /dev/null +++ b/drake_ros_bazel_installed/apps/oracle.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 + +import random + +import rclpy +import rclpy.action +import rclpy.duration +import rclpy.node + +import apps_msgs.msg +import common_msgs.action +import common_msgs.srv + + +class Oracle(rclpy.node.Node): + + def __init__(self): + super().__init__('oracle') + self._sequence_id = 0 + self._status_pub = self.create_publisher( + apps_msgs.msg.Status, 'status', 1) + self._query_server = self.create_service( + common_msgs.srv.Query, 'query', self._handle_query) + self._action_server = rclpy.action.ActionServer( + self, common_msgs.action.Do, 'do', + execute_callback=self._handle_rite_action, + goal_callback=self._handle_action_request, + cancel_callback=self._handle_cancelled_action, + ) + self._status_timer = self.create_timer(1.0, self._publish_status) + + def _handle_action_request(self, goal): + if goal.action != 'rite': + self.get_logger().warning( + "Don't know how to " + goal.action) + return rclpy.action.GoalResponse.REJECT + return rclpy.action.GoalResponse.ACCEPT + + def _handle_cancelled_action(self, handle): + return rclpy.action.CancelResponse.ACCEPT + + def _handle_rite_action(self, handle): + result = common_msgs.action.Do.Result() + timeout = rclpy.duration.Duration.from_msg( + handle.request.timeout) + period = rclpy.duration.Duration.from_msg( + handle.request.period) + period = period.nanoseconds / 1e9 + start_time = self.get_clock().now() + rate = self.create_rate(1 / period) + while rclpy.ok(): + if handle.is_cancel_requested: + handle.canceled() + break + current_time = self.get_clock().now() + if current_time - start_time > timeout: + result.reason = 'timeout' + handle.abort() + break + if bool(random.getrandbits(1)): + handle.succeed() + break + feedback = common_msgs.action.Do.Feedback() + feedback.message = 'chanting' + handle.publish_feedback(feedback) + rate.sleep() + return result + + def _handle_query(self, request, response): + if request.query == "how's it going?": + response.reply = 'all good!' + else: + response.reply = "don't know" + return response + + def _publish_status(self): + msg = apps_msgs.msg.Status() + msg.status.sequence_id = self._sequence_id + self._sequence_id = self._sequence_id + 1 + msg.status.message = 'OK' + msg.origin = 'oracle' + self._status_pub.publish(msg) + + +def main(): + rclpy.init() + + try: + executor = rclpy.executors.MultiThreadedExecutor() + rclpy.spin(Oracle(), executor=executor) + except KeyboardInterrupt: + pass + finally: + rclpy.shutdown() + + +if __name__ == '__main__': + main() From 10447710a52c079c827ab5111ce13cb60d40f600 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 16 Jul 2021 11:59:45 -0300 Subject: [PATCH 029/107] Sandbox rmw_cyclonedds_cpp, not rmw_fastrtps_cpp Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl b/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl index 72084573c..7ea51961a 100644 --- a/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl +++ b/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl @@ -18,6 +18,6 @@ def ros2_repository(name, overlays = []): "rclpy", "rviz2", # RMW implementation - "rmw_fastrtps_cpp", + "rmw_cyclonedds_cpp", ] ) From fd1e8e4ffd18648df1e2eb9175320bd621a8c842 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 4 Aug 2021 14:38:06 +0000 Subject: [PATCH 030/107] Update ROS 2 Skylark tools - Improve repository-bound Bazel tooling for C/C++ and Python - Deal with interface packages not exporting all its artifacts - Deal with non-REP 122 compliant packages in symlink installs Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/apps/BUILD.bazel | 30 +-- .../tools/skylark/dload.bzl | 153 ++--------- .../tools/skylark/dload_cc.bzl | 14 - .../tools/skylark/dload_py.bzl | 14 - .../tools/skylark/kwargs.bzl | 21 ++ .../tools/skylark/ros2/BUILD.bazel | 5 + .../skylark/ros2/cmake_tools/packages.py | 6 +- .../skylark/ros2/generate_repository_files.py | 30 +-- .../ros2/resources/bazel/cc_tools.bzl.tpl | 185 +++++++------ .../ros2/resources/bazel/common.bzl.tpl | 21 ++ .../ros2/resources/bazel/py_tools.bzl.tpl | 255 +++++++++--------- .../ros2/resources/bazel/rosidl_tools.bzl.tpl | 112 +++++--- ..._cc_library_with_runtime_environ.bazel.tpl | 19 -- .../bazel/snippets/prologue.bazel.tpl | 1 - .../tools/skylark/ros2/rmw.bzl | 26 +- .../tools/skylark/ros2/ros2.bzl | 5 +- .../ros2/ros2bzl/scrapping/__init__.py | 4 +- .../ros2/ros2bzl/scrapping/ament_cmake.py | 12 +- .../tools/skylark/ros2/ros2bzl/templates.py | 23 +- .../tools/workspace/ros2/repository.bzl | 3 +- 20 files changed, 436 insertions(+), 503 deletions(-) create mode 100644 drake_ros_bazel_installed/tools/skylark/kwargs.bzl create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_cc_library_with_runtime_environ.bazel.tpl diff --git a/drake_ros_bazel_installed/apps/BUILD.bazel b/drake_ros_bazel_installed/apps/BUILD.bazel index 84d4189be..9c9793ec0 100644 --- a/drake_ros_bazel_installed/apps/BUILD.bazel +++ b/drake_ros_bazel_installed/apps/BUILD.bazel @@ -17,57 +17,51 @@ rosidl_interfaces_group( ros_cc_binary( name = "oracle_cc", srcs = ["oracle.cc"], - data = [ - "@ros2//:rmw_cyclonedds_cpp", # pick RMW impl - ], deps = [ ":apps_msgs_cc", "//common:common_msgs_cc", - "@ros2//:rclcpp", - "@ros2//:rclcpp_action", + "@ros2//:rclcpp_cc", + "@ros2//:rclcpp_action_cc", ], + rmw_implementation = "rmw_cyclonedds_cpp", tags = ["requires-network"], ) ros_cc_binary( name = "inquirer_cc", srcs = ["inquirer.cc"], - data = [ - "@ros2//:rmw_cyclonedds_cpp", # pick RMW impl - ], deps = [ ":apps_msgs_cc", "//common:common_msgs_cc", - "@ros2//:rclcpp", - "@ros2//:rclcpp_action", + "@ros2//:rclcpp_cc", + "@ros2//:rclcpp_action_cc", ], + rmw_implementation = "rmw_cyclonedds_cpp", tags = ["requires-network"], ) ros_py_binary( name = "oracle_py", srcs = ["oracle.py"], - data = [ - "@ros2//:rmw_cyclonedds_cpp", # pick RMW impl - ], + main = "oracle.py", deps = [ ":apps_msgs_py", "//common:common_msgs_py", - "@ros2//:rclpy", + "@ros2//:rclpy_py", ], + rmw_implementation = "rmw_cyclonedds_cpp", tags = ["requires-network"], ) ros_py_binary( name = "inquirer_py", srcs = ["inquirer.py"], - data = [ - "@ros2//:rmw_cyclonedds_cpp", # pick RMW impl - ], + main = "inquirer.py", deps = [ ":apps_msgs_py", "//common:common_msgs_py", - "@ros2//:rclpy", + "@ros2//:rclpy_py", ], + rmw_implementation = "rmw_cyclonedds_cpp", tags = ["requires-network"], ) diff --git a/drake_ros_bazel_installed/tools/skylark/dload.bzl b/drake_ros_bazel_installed/tools/skylark/dload.bzl index 77d29536e..4a6528b5e 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload.bzl @@ -1,34 +1,17 @@ # -*- python -*- """ -The purpose of these macros is to support the propagation of runtime information -that is key for proper execution from libraries to executables that depend on -them. +The purpose of these macros is to support configuration of the runtime +environment in which executables are run. -The two primary macros of interest are `do_dload_shim`, which aids language -specific shim generation, and `do_dload_aware_target`, which can decorate -existing targets with runtime information. +The primary macro of interest is `do_dload_shim`, which aids language +specific shim generation. """ MAGIC_VARIABLES = { "${LOAD_PATH}": "LD_LIBRARY_PATH" # for Linux } -RuntimeInfo = provider(fields = ['env_changes']) -""" -This provider carries runtime information through the build graph. - -Attributes - - env_changes: runtime environment changes to be applied, as a mapping from - environment variable names to actions to be performed on them. - Actions are (action_type, action_args) tuples. Supported action types - are: 'path-prepend', 'path-replace', and 'replace'. Paths are resolved - relative to the runfiles directory of the downstream executable. - Also, see MAGIC_VARIABLES for platform-independent runtime environment - specification. -""" - def unique(input_list): """Extracts unique values from input list, while preserving their order.""" output_list = [] @@ -37,38 +20,6 @@ def unique(input_list): output_list.append(item) return output_list -def merge_runtime_environment_changes(base, head): - """ - Merges runtime environment head changes into base. - """ - for envvar, head_action in head.items(): - if envvar in base: - base_action = base[envvar] - tpl = "Got '{}' and '{}' actions on {}, " - tpl += "results will depend on dependency order" - fail(msg = tpl.format(base_action[0], head_action[0], envvar)) - base[envvar] = head_action - return base - -def merge_runtime_info(base_info, head_info): - """ - Merges 'head' runtime information into 'base' runtime information, - then returns the latter. - """ - merge_runtime_environment_changes( - base_info.env_changes, head_info.env_changes - ) - return base_info - -def collect_runtime_info(targets): - """Returns all targets' runtime information merged into one.""" - runtime_info = RuntimeInfo(env_changes = {}) - for target in targets: - if RuntimeInfo not in target: - continue - merge_runtime_info(runtime_info, target[RuntimeInfo]) - return runtime_info - def normpath(path): """ Normalizes a path by removing redundant separators and up-level references. @@ -96,17 +47,13 @@ def get_dload_shim_attributes(): cfg = "target", ), "data": attr.label_list(allow_files = True), - "deps": attr.label_list(), - "env": attr.string_list_dict(), + "env_changes": attr.string_list_dict(), } def do_dload_shim(ctx, template, to_list): """ Implements common dload_shim rule functionality. - All runtime information is merged, and made available to the - executable by the shim. - Args: ctx: context of a Bazel rule template: string template for the shim @@ -115,23 +62,25 @@ def do_dload_shim(ctx, template, to_list): It expects the following attributes on ctx: target: executable target to be shimmed - data: executable data dependencies, may provide RuntimeInfo - deps: executable dependencies, may provide RuntimeInfo + env_changes: runtime environment changes to be applied, as a mapping from + environment variable names to actions to be performed on them. + Actions are (action_type, action_args) tuples. Supported action types + are: 'path-prepend', 'path-replace', and 'replace'. Paths are resolved + relative to the runfiles directory of the downstream executable. + Also, see MAGIC_VARIABLES for platform-independent runtime environment + specification. You may use get_dload_shim_attributes() on rule definition. """ executable_file = ctx.executable.target - runtime_info = RuntimeInfo(env_changes = { + env_changes = { MAGIC_VARIABLES.get(name, default=name): parse_runtime_environment_action(ctx, action) - for name, action in ctx.attr.env.items() - }) - merge_runtime_info(runtime_info, collect_runtime_info(ctx.attr.data)) - merge_runtime_info(runtime_info, collect_runtime_info(ctx.attr.deps)) - - envvars = runtime_info.env_changes.keys() - actions = runtime_info.env_changes.values() + for name, action in ctx.attr.env_changes.items() + } + envvars = env_changes.keys() + actions = env_changes.values() shim_content = template.format( # Deal with usage in external workspaces' BUILD.bazel files @@ -187,71 +136,3 @@ def parse_runtime_environment_action(ctx, action): else: fail(msg = "'{}' action is unknown".format(action_type)) return [action_type] + action_args - -def get_dload_aware_target_attributes(): - """ - Yields attributes common to all dload_aware_target rules. - - See do_dload_aware_target() documentation for further reference. - """ - return { - "base": attr.label(mandatory = True), - "data": attr.label_list(allow_files = True), - "deps": attr.label_list(), - "env": attr.string_list_dict(), - } - -def do_dload_aware_target(ctx): - """ - Implements common dload_aware_target rule functionality. - - Any specified runtime info is augmented with that of build and runtime - dependencies, and then propagated along with base target runfiles - i.e. its DefaultInfo provider. - - Args: - ctx: context of a Bazel rule - - It expects the following attributes on ctx: - - base: target to extend with runtime information - data: base target's data dependencies, may provide RuntimeInfo - deps: base target's dependencies, may provide RuntimeInfo - runenv: runtime environment changes for this target - - You may use get_dload_aware_target_attributes() on rule definition. - """ - runtime_info = RuntimeInfo(env_changes = { - MAGIC_VARIABLES.get(name, default=name): - parse_runtime_environment_action(ctx, action) - for name, action in ctx.attr.env.items() - }) - merge_runtime_info(runtime_info, collect_runtime_info(ctx.attr.data)) - merge_runtime_info(runtime_info, collect_runtime_info(ctx.attr.deps)) - return [ - # Recreate base's DefaultInfo to workaround - # https://github.com/bazelbuild/bazel/issues/9442 - DefaultInfo( - files = ctx.attr.base[DefaultInfo].files, - data_runfiles = ctx.attr.base[DefaultInfo].data_runfiles, - default_runfiles = ctx.attr.base[DefaultInfo].default_runfiles, - ), - runtime_info - ] - -def do_dload_aware_library(ctx, kind): - """ - Implements common dload_aware_library rule functionality. - - It builds on top of dload_aware_target functionality, also propagating - library specific providers e.g. CcInfo providers for cc_library base - targets. - """ - providers = do_dload_aware_target(ctx) - providers.append(ctx.attr.base[kind]) - return providers - -dload_aware_target = rule( - attrs = get_dload_aware_target_attributes(), - implementation = do_dload_aware_target, -) diff --git a/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl b/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl index 0940b246e..d9d28ce2d 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl @@ -9,9 +9,7 @@ tests. load( "//tools/skylark:dload.bzl", "do_dload_shim", - "do_dload_aware_library", "get_dload_shim_attributes", - "get_dload_aware_target_attributes", ) DLOAD_CC_SHIM_TEMPLATE = """\ @@ -97,15 +95,3 @@ dload_cc_shim = rule( This rule() generates a C++ shim that can carry runtime information. See do_dload_shim() documentation for further reference. """ - -def _dload_aware_cc_library_impl(ctx): - return do_dload_aware_library(ctx, CcInfo) - -dload_aware_cc_library = rule( - attrs = get_dload_aware_target_attributes(), - implementation = _dload_aware_cc_library_impl, -) -""" -This rule() decorates a cc_library() rule to carry runtime information. -See do_dload_aware_library() documentation for further reference. -""" diff --git a/drake_ros_bazel_installed/tools/skylark/dload_py.bzl b/drake_ros_bazel_installed/tools/skylark/dload_py.bzl index bf1e875ca..9207d14ce 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload_py.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload_py.bzl @@ -10,9 +10,7 @@ tests. load( "//tools/skylark:dload.bzl", "do_dload_shim", - "do_dload_aware_library", "get_dload_shim_attributes", - "get_dload_aware_target_attributes", ) DLOAD_PY_SHIM_TEMPLATE = """\ @@ -68,15 +66,3 @@ dload_py_shim = rule( This rule() generates a Python shim that can carry runtime information. See do_dload_shim() documentation for further reference. """ - -def _dload_aware_py_library_impl(ctx): - return do_dload_aware_library(ctx, PyInfo) - -dload_aware_py_library = rule( - attrs = get_dload_aware_target_attributes(), - implementation = _dload_aware_py_library_impl, -) -""" -This rule() decorates a py_library() rule to carry runtime information. -See do_dload_aware_library() documentation for further reference. -""" diff --git a/drake_ros_bazel_installed/tools/skylark/kwargs.bzl b/drake_ros_bazel_installed/tools/skylark/kwargs.bzl new file mode 100644 index 000000000..94424c504 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/kwargs.bzl @@ -0,0 +1,21 @@ +# -*- python -*- + +DEFAULT_COMMON_KWARGS = { + "compatible_with": [], + "deprecation": None, + "exec_compatible_with": [], + "exec_properties": {}, + "features": [], + "restricted_to": None, + "tags": [], + "target_compatible_with": [], + "testonly": False, + "toolchains": [], + "visibility": [] +} + +def keep_common(kwargs): + return { + key: kwargs.get(key, value) + for key, value in DEFAULT_COMMON_KWARGS.items() + } diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel index d2ec10fa7..e735a65c6 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel +++ b/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel @@ -1,7 +1,12 @@ # -*- mode: python -*- +# vi: set ft=python : + +load("//tools/lint:lint.bzl", "add_lint_tests") py_binary( name = "rmw_fastrtps_profile_gen", srcs = ["rmw_fastrtps_profile_gen.py"], visibility = ["//visibility:public"], ) + +add_lint_tests() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/packages.py b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/packages.py index c4289e4b6..c1b0cd9d3 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/packages.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/packages.py @@ -2,13 +2,15 @@ import os -def get_packages_with_prefixes(prefixes): +def get_packages_with_prefixes(prefixes=None): + if prefixes is None: + prefixes = os.environ['CMAKE_PREFIX_PATH'].split(os.pathsep) suffixes = 'Config.cmake', '-config.cmake' return { os.path.basename(path)[:-len(suffix)]: prefix for prefix in prefixes for suffix in suffixes for path in glob.glob( - '{}/*/**/*{}'.format( + '{}/**/*{}'.format( prefix, suffix), recursive=True ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py index 9d5628a8a..3dde51b94 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -19,10 +19,10 @@ from ros2bzl.scrapping.ament_python import PackageNotFoundError from ros2bzl.templates import configure_cc_tools +from ros2bzl.templates import configure_common from ros2bzl.templates import configure_distro from ros2bzl.templates import configure_executable_imports from ros2bzl.templates import configure_package_meta_py_library -from ros2bzl.templates import configure_package_alias from ros2bzl.templates import configure_package_c_library_alias from ros2bzl.templates import configure_package_cc_library from ros2bzl.templates import configure_package_executable_imports @@ -93,6 +93,12 @@ def generate_distro_file(packages): fd.write(interpolate(template, config) + '\n') +def generate_common_file(repo_name): + with open('common.bzl', 'w') as fd: + template, config = configure_common(repo_name) + fd.write(interpolate(template, config) + '\n') + + def generate_cc_tools_file(repo_name): with open('cc_tools.bzl', 'w') as fd: template, config = configure_cc_tools(repo_name) @@ -129,31 +135,26 @@ def generate_build_file(repo_name, distro, cache, extras, sandbox): for dependency_name in distro['dependency_graph'][name] } - targets = [] - if 'share_directory' in metadata: _, template, config = \ configure_package_share_filegroup(name, metadata, sandbox) fd.write(interpolate(template, config) + '\n') - + if 'rosidl_interface_packages' in metadata.get('groups', []): - label, template, config = \ + _, template, config = \ configure_package_interfaces_filegroup(name, metadata, sandbox) fd.write(interpolate(template, config) + '\n') - targets.append(label) + if 'cmake' in metadata.get('build_type'): properties = collect_ament_cmake_package_direct_properties( name, metadata, dependencies, cache ) - label, template, config = configure_package_cc_library( + _, template, config = configure_package_cc_library( name, metadata, properties, dependencies, extras, sandbox ) - if any(properties.values()): - targets.append(label) - fd.write(interpolate(template, config) + '\n') if 'rosidl_interface_packages' in metadata.get('groups', []): @@ -184,15 +185,10 @@ def generate_build_file(repo_name, distro, cache, extras, sandbox): properties = {} if properties: - label, template, config = configure_package_py_library( + _, template, config = configure_package_py_library( name, metadata, properties, dependencies, extras, sandbox ) fd.write(interpolate(template, config) + '\n') - targets.append(label) - - if len(targets) == 1 and targets[0] != name: - _, template, config = configure_package_alias(name, targets[0]) - fd.write(interpolate(template, config) + '\n') if metadata.get('executables'): dependencies.update(rmw_implementation_packages) @@ -227,6 +223,8 @@ def main(): generate_distro_file(distro) + generate_common_file(args.repository_name) + generate_cc_tools_file(args.repository_name) generate_py_tools_file(args.repository_name) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/cc_tools.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/cc_tools.bzl.tpl index b4da4930d..9fe8091ff 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/cc_tools.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/cc_tools.bzl.tpl @@ -1,113 +1,122 @@ # -*- python -*- -load("@REPOSITORY_ROOT@:distro.bzl", "RUNTIME_ENVIRONMENT") -load("@drake_ros//tools/skylark:dload_cc.bzl", "dload_cc_shim") +load( + "@REPOSITORY_ROOT@:distro.bzl", + "RUNTIME_ENVIRONMENT" +) +load( + "@REPOSITORY_ROOT@:common.bzl", + "incorporate_rmw_implementation", + "incorporate_fastrtps_profile" +) +load( + "@drake_ros//tools/skylark:dload_cc.bzl", + "dload_cc_shim" +) +load( + "@drake_ros//tools/skylark:kwargs.bzl", + "keep_common" +) +load( + "@drake_ros//tools/skylark/ros2:rmw.bzl", + "generate_isolated_fastrtps_profile", +) -def ros_cc_binary( - name, srcs = [], data = [], deps = [], linkshared = None, - tags = [], testonly = False, visibility = None, **kwargs -): + +def ros_cc_binary(name, rmw_implementation = None, **kwargs): """ Builds a C/C++ binary, injecting the runtime environment specific to this ROS overlay. - - Equivalent to the cc_binary() rule. """ - if linkshared: - native.cc_binary( - name = name, - srcs = srcs, - data = data, - deps = deps, - linkshared = linkshared, - visibility = visibility, - testonly = testonly, - tags = tags, - **kwargs - ) + if kwargs.get("linkshared", False): + native.cc_binary(name = name, **kwargs) return - binary = "_" + name + + binary_name = "_" + name + binary_kwargs = kwargs + binary_env_changes = dict(RUNTIME_ENVIRONMENT) + + if rmw_implementation: + binary_kwargs, binary_env_changes = \ + incorporate_rmw_implementation( + binary_kwargs, binary_env_changes, + rmw_implementation = rmw_implementation + ) + if "fastrtps" in rmw_implementation: + if "block-network" in kwargs.get("tags", []): + profile_name = name + ".fastrtps_profile.xml" + generate_isolated_fastrtps_profile(profile_name) + binary_kwargs, binary_env_changes = \ + incorporate_fastrtps_profile( + binary_kwargs, binary_env_changes, profile_name + ) + native.cc_binary( - name = binary, - srcs = srcs, - data = data, - deps = deps, - linkshared = linkshared, - visibility = visibility, - testonly = testonly, - tags = tags, - **kwargs + name = binary_name, + **binary_kwargs ) - shim = binary + "_shim.cc" + shim_name = binary_name + "_shim.cc" + shim_kwargs = keep_common(kwargs) dload_cc_shim( - name = shim, - target = ":" + binary, - env = RUNTIME_ENVIRONMENT, - data = data, - deps = deps, - testonly = testonly, - visibility = visibility, - ) - native.cc_binary( - name = name, - srcs = [shim], - data = [":" + binary], - deps = [ - "@bazel_tools//tools/cpp/runfiles", - ], - tags = ["nolint"] + tags, - visibility = visibility, - testonly = testonly, - **kwargs + name = shim_name, + target = ":" + binary_name, + env_changes = binary_env_changes, + **shim_kwargs ) -def ros_cc_test( - name, srcs = [], data = [], deps = [], - testonly = 1, visibility = [], tags = [], - **kwargs -): + kwargs.update( + srcs = [shim_name], + data = [":" + binary_name], + deps = ["@bazel_tools//tools/cpp/runfiles"], + tags = ["nolint"] + kwargs.get("tags", []) + ) + native.cc_binary(name = name, **kwargs) + +def ros_cc_test(name, rmw_implementation = None, **kwargs): """ - Builds C/C++ test, injecting the runtime environment + Builds a C/C++ test, injecting the runtime environment specific to this ROS overlay. Equivalent to the cc_test() rule. """ - - if not srcs: - srcs = ["test/%s.cc" % name] - - test = "_" + name + test_name = "_" + name + test_env_changes = dict(RUNTIME_ENVIRONMENT) + kwargs["tags"] = ["manual"] + kwargs.get("tags", []) + if rmw_implementation: + test_kwargs, test_env_changes = \ + incorporate_rmw_implementation( + test_kwargs, test_env_changes, + rmw_implementation = rmw_implementation + ) + if "fastrtps" in rmw_implementation: + if "block-network" in kwargs.get("tags", []): + profile_name = name + ".fastrtps_profile.xml" + generate_isolated_fastrtps_profile(profile_name) + test_kwargs, test_env_changes = \ + incorporate_fastrtps_profile( + test_kwargs, test_env_changes, profile_name + ) native.cc_test( - name = test, - srcs = srcs, - data = data, - deps = deps, - tags = ["manual"] + tags, - testonly = testonly, - visibility = visibility, - **kwargs + name = test_name, + **test_kwargs ) - shim = test + "_shim.cc" + + shim_name = test_name + "_shim.cc" + shim_kwargs = keep_common(kwargs) + shim_kwargs.update(testonly = True) dload_cc_shim( - name = shim, - target = ":" + test, - env = RUNTIME_ENVIRONMENT, - data = data, - deps = deps, - testonly = testonly, - visibility = visibility, + name = shim_name, + target = ":" + test_name, + env_changes = test_env_changes, + **shim_kwargs ) - native.cc_test( - name = name, - srcs = [shim], - data = [":" + test], - deps = [ - "@bazel_tools//tools/cpp/runfiles", - ], - tags = ["nolint"] + tags, - testonly = testonly, - visibility = visibility, - **kwargs + + kwargs.update( + srcs = [shim_name], + data = [":" + test_name], + deps = ["@bazel_tools//tools/cpp/runfiles"], + tags = ["nolint"] + kwargs.get("tags", []) ) + native.cc_test(name = name, **kwargs) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl new file mode 100644 index 000000000..4901294ec --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl @@ -0,0 +1,21 @@ +# -*- python -*- + +load( + "@drake_ros//tools/skylark/ros2:rmw.bzl", + "use_fastrtps_profile", +) + +def incorporate_rmw_implementation(kwargs, env_changes, rmw_implementation): + target = "@REPOSITORY_ROOT@:%s_cc" % rmw_implementation + kwargs["data"] = kwargs.get("data", []) + [target] + env_changes = dict(env_changes) + env_changes.update({ + "RMW_IMPLEMENTATION": ["replace", rmw_implementation] + }) + return kwargs, env_changes + +def incorporate_fastrtps_profile(kwargs, env_changes, profile_name): + kwargs["data"] = kwargs.get("data", []) + [profile_name] + env_changes = dict(env_changes) + env_changes.update(use_fastrtps_profile(profile_name)) + return kwargs, env_changes diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl index 0964eb66d..5809a7637 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl @@ -1,13 +1,28 @@ # -*- python -*- -load("@REPOSITORY_ROOT@:distro.bzl", "RUNTIME_ENVIRONMENT") -load("@drake_ros//tools/skylark:dload_py.bzl", "dload_py_shim") - -def ros_py_import( - name, executable = [], imports = [], args = [], - data = [], deps = [], tags = [], testonly = 0, - visibility = None, **kwargs -): +load( + "@REPOSITORY_ROOT@:distro.bzl", + "RUNTIME_ENVIRONMENT" +) +load( + "@REPOSITORY_ROOT@:common.bzl", + "incorporate_rmw_implementation", + "incorporate_fastrtps_profile" +) +load( + "@drake_ros//tools/skylark:dload_py.bzl", + "dload_py_shim" +) +load( + "@drake_ros//tools/skylark:kwargs.bzl", + "keep_common" +) +load( + "@drake_ros//tools/skylark/ros2:rmw.bzl", + "generate_isolated_fastrtps_profile", +) + +def ros_py_import(name, executable, rmw_implementation = None, **kwargs): """ Imports an executable, injecting the runtime environment specific to this ROS overlay. @@ -17,144 +32,142 @@ def ros_py_import( Akin to the cc_import() rule. """ - shim = name + "_shim.py" + + env_changes = dict(RUNTIME_ENVIRONMENT) + if rmw_implementation: + kwargs, env_changes = \ + incorporate_rmw_implementation( + kwargs, env_changes, + rmw_implementation = rmw_implementation + ) + if "fastrtps" in rmw_implementation: + if "block-network" in kwargs.get("tags", []): + profile_name = name + ".fastrtps_profile.xml" + generate_isolated_fastrtps_profile(profile_name) + kwargs, env_changes = \ + incorporate_fastrtps_profile( + kwargs, env_changes, profile_name + ) + + shim_name = name + "_shim.py" + shim_kwargs = keep_common(kwargs) dload_py_shim( - name = shim, + name = shim_name, target = executable, - env = RUNTIME_ENVIRONMENT, - data = data, - deps = deps, - testonly = testonly, - visibility = visibility, + env_changes = env_changes, + **shim_kwargs ) - native.py_binary( - name = name, - srcs = [shim], - main = shim, - imports = imports, - data = [executable] + data, - deps = [ - "@bazel_tools//tools/python/runfiles", - ] + deps, - tags = ["nolint"] + tags, - testonly = testonly, - visibility = visibility, - python_version = "PY3", - **kwargs + + kwargs.update( + srcs = [shim_name], + main = shim_name, + tags = ["nolint"] + kwargs.get("tags", []), + data = [executable] + kwargs.get("data", []), + deps = kwargs.get("deps", []) + [ + "@bazel_tools//tools/python/runfiles"] ) + native.py_binary(name = name, **kwargs) -def ros_py_binary( - name, srcs = [], main = None, imports = [], args = [], - srcs_version = None, data = [], deps = [], tags = [], - testonly = 0, visibility = None, **kwargs -): +def ros_py_binary(name, rmw_implementation = None, **kwargs): """ - Adds a Python executable, injecting the runtime environment + Builds a Python binary, injecting the runtime environment specific to this ROS overlay. - - Equivalent to the py_library() rule. """ - binary = "_" + name - if not main: - if len(srcs) == 1: - main = srcs[0] - else: - main = name + ".py" + + binary_name = "_" + name + binary_kwargs = dict(kwargs) + if "main" not in binary_kwargs: + binary_kwargs["main"] = name + ".py" + binary_env_changes = dict(RUNTIME_ENVIRONMENT) + + if rmw_implementation: + binary_kwargs, binary_env_changes = \ + incorporate_rmw_implementation( + binary_kwargs, binary_env_changes, + rmw_implementation = rmw_implementation + ) + if "fastrtps" in rmw_implementation: + if "block-network" in binary_kwargs.get("tags", []): + profile_name = name + ".fastrtps_profile.xml" + generate_isolated_fastrtps_profile(profile_name) + binary_kwargs, binary_env_changes = \ + incorporate_fastrtps_profile( + binary_kwargs, binary_env_changes, profile_name + ) + native.py_binary( - name = binary, - srcs = srcs, - main = main, - imports = imports, - srcs_version = srcs_version, - data = data, - deps = deps, - tags = tags, - testonly = testonly, - visibility = visibility, - **kwargs + name = binary_name, + **binary_kwargs ) - shim = binary + "_shim.py" + + shim_name = binary_name + "_shim.py" + shim_kwargs = keep_common(kwargs) dload_py_shim( - name = shim, - target = ":" + binary, - env = RUNTIME_ENVIRONMENT, - data = data, - deps = deps, - testonly = testonly, - visibility = visibility, + name = shim_name, + target = ":" + binary_name, + env_changes = binary_env_changes, + **shim_kwargs ) - native.py_binary( - name = name, - srcs = [shim], - main = shim, - data = [":" + binary], + + kwargs.update( + srcs = [shim_name], + main = shim_name, + data = [":" + binary_name], deps = [ "@bazel_tools//tools/python/runfiles", - ":" + binary, # Support py_binary being used a dependency + ":" + binary_name, # Support py_binary being used a dependency ], - srcs_version = srcs_version, - tags = ["nolint"] + tags, - testonly = testonly, - visibility = visibility, - **kwargs + tags = ["nolint"] + kwargs.get("tags", []) ) + native.py_binary(name = name, **kwargs) -def ros_py_test( - name, srcs = [], main = None, imports = [], srcs_version = None, - data = [], args = [], deps = [], tags = [], visibility = None, - testonly = 1, **kwargs -): +def ros_py_test(name, rmw_implementation = None, **kwargs): """ - Adds a Python test, injecting the runtime environment + Builds a Python test, injecting the runtime environment specific to this ROS overlay. Equivalent to the py_test() rule. """ - if not srcs: - srcs = ["test/%s.py" % name] - - if not main: - if len(srcs) > 1: - fail("You must specify main if you have more than one source.") - main = srcs[0] - - test = "_" + name + test_name = "_" + name + test_kwargs = dict(kwargs) + test_kwargs.update( + tags = ["manual"] + test_kwargs.get("tags", [])) + test_env_changes = dict(RUNTIME_ENVIRONMENT) + if rmw_implementation: + test_kwargs, test_env_changes = \ + incorporate_rmw_implementation( + test_kwargs, test_env_changes, + rmw_implementation = rmw_implementation, + ) + if "fastrtps" in rmw_implementation: + if "block-network" in test_kwargs.get("tags", []): + profile_name = name + ".fastrtps_profile.xml" + generate_isolated_fastrtps_profile(profile_name) + test_kwargs, test_env_changes = \ + incorporate_fastrtps_profile( + test_kwargs, test_env_changes, profile_name + ) native.py_test( - name = test, - srcs = srcs, - main = main, - args = args, - imports = imports, - srcs_version = srcs_version, - data = data, - deps = deps, - tags = ["manual"] + tags, - testonly = testonly, - visibility = visibility, - **kwargs + name = test_name, + **test_kwargs ) - shim = test + "_shim.py" + + shim_name = test_name + "_shim.py" + shim_kwargs = keep_common(kwargs) + shim_kwargs.update(testonly = True) dload_py_shim( - name = shim, - target = ":" + test, - env = RUNTIME_ENVIRONMENT, - data = data, - deps = deps, - testonly = testonly, - visibility = visibility, + name = shim_name, + target = ":" + test_name, + env_changes = test_env_changes, + **shim_kwargs ) - native.py_test( - name = name, - srcs = [shim], - data = [":" + test], - main = shim, - deps = [ - "@bazel_tools//tools/python/runfiles", - ], - srcs_version = srcs_version, - tags = ["nolint"] + tags, - testonly = testonly, - visibility = visibility, - **kwargs + + kwargs.update( + srcs = [shim_name], + main = shim_name, + data = [":" + test_name], + deps = ["@bazel_tools//tools/python/runfiles"], + tags = ["nolint"] + kwargs.get("tags", []) ) + native.py_test(name = name, **kwargs) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl index 4af393ae7..a037624c4 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl @@ -4,23 +4,22 @@ load("@REPOSITORY_ROOT@:distro.bzl", "AVAILABLE_TYPESUPPORTS") load("@python_dev//:version.bzl", "PYTHON_EXTENSION_SUFFIX") def _as_idl_tuple(file): - path, parent, base = file.short_path.rsplit("/", 2) + path, parent, base = file.path.rsplit("/", 2) if parent not in ("msg", "srv", "action"): fail("Interface parent folder must be one of: 'msg', 'srv', 'action'") return "{}:{}/{}".format(path, parent, base) def _as_include_flag(file): - return "-I" + file.short_path.rsplit("/", 3)[0] + return "-I" + file.path.rsplit("/", 3)[0] def _rosidl_generate_genrule_impl(ctx): args = ctx.actions.args() args.add("generate") - output_path = "{}/{}".format( - ctx.var["GENDIR"], - ctx.label.package) - if ctx.attr.output_dir: - output_path = "{}/{}".format( - output_path, ctx.attr.output_dir) + output_path_parts = [ + ctx.var["GENDIR"], ctx.label.workspace_root, + ctx.label.package, ctx.attr.output_dir] + output_path = "/".join([ + part for part in output_path_parts if part]) args.add("--output-path", output_path) for type in ctx.attr.types: args.add("--type", type) @@ -61,12 +60,11 @@ rosidl_generate_genrule = rule( def _rosidl_translate_genrule_impl(ctx): args = ctx.actions.args() args.add("translate") - output_path = "{}/{}".format( - ctx.var["GENDIR"], - ctx.label.package) - if ctx.attr.output_dir: - output_path = "{}/{}".format( - output_path, ctx.attr.output_dir) + output_path_parts = [ + ctx.var["GENDIR"], ctx.label.workspace_root, + ctx.label.package, ctx.attr.output_dir] + output_path = "/".join([ + part for part in output_path_parts if part]) args.add("--output-path", output_path) args.add("--output-format", ctx.attr.output_format) if ctx.attr.input_format: @@ -129,7 +127,6 @@ def rosidl_definitions_filegroup(name, group, interfaces, includes, **kwargs): def _generated_source_paths(group, kind): base = "{}/{}".format(group, kind) - include = "{}/{}".format(native.package_name(), base) root = "{}/{}".format(base, group) return base, root @@ -323,11 +320,19 @@ def rosidl_typesupport_fastrtps_cc_library( **kwargs ) + native.cc_library( + name = name + "_hdrs", + hdrs = generated_cc_headers, + includes = [include], + linkstatic = True, + **kwargs + ) + native.cc_binary( name = name, - srcs = generated_cc_sources + generated_cc_headers, - includes = [include], + srcs = generated_cc_sources, deps = deps + [ + ":" + name + "_hdrs", "@REPOSITORY_ROOT@:fastcdr_cc", "@REPOSITORY_ROOT@:rmw_cc", "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", @@ -365,12 +370,20 @@ def rosidl_typesupport_fastrtps_c_library( **kwargs ) + native.cc_library( + name = name + "_hdrs", + hdrs = generated_c_headers, + includes = [include], + linkstatic = True, + **kwargs + ) + native.cc_binary( name = name, - srcs = generated_c_sources + generated_c_headers, - includes = [include], + srcs = generated_c_sources, linkshared = True, deps = deps + [ + ":" + name + "_hdrs", "@REPOSITORY_ROOT@:fastcdr_cc", "@REPOSITORY_ROOT@:rmw_cc", "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", @@ -411,6 +424,7 @@ def rosidl_typesupport_introspection_c_library( native.cc_library( name = name + "_hdrs", hdrs = generated_c_headers, + includes = [include], linkstatic = True, **kwargs ) @@ -418,7 +432,6 @@ def rosidl_typesupport_introspection_c_library( native.cc_binary( name = name, srcs = generated_c_sources, - includes = [include], linkshared = True, deps = deps + [ ":" + name + "_hdrs", @@ -453,13 +466,22 @@ def rosidl_typesupport_introspection_cc_library( **kwargs ) + native.cc_library( + name = name + "_hdrs", + hdrs = generated_cc_headers, + includes = [include], + linkstatic = True, + **kwargs + ) + native.cc_binary( name = name, - srcs = generated_cc_sources + generated_cc_headers, - includes = [include], + srcs = generated_cc_sources, linkshared = True, deps = deps + [ + ":" + name + "_hdrs", "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", + "@REPOSITORY_ROOT@:rosidl_runtime_cpp_cc", "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", "@REPOSITORY_ROOT@:rosidl_typesupport_introspection_cpp_cc", ], @@ -496,11 +518,11 @@ def rosidl_typesupport_c_library( native.cc_binary( name = name, - srcs = generated_c_sources + generated_c_headers, + srcs = generated_sources, includes = [include], linkshared = True, data = typesupports.values(), - deps = deps + [ + deps = deps + [label + "_hdrs" for label in typesupports.values()] + [ "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", "@REPOSITORY_ROOT@:rosidl_typesupport_c_cc", "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", @@ -539,7 +561,7 @@ def rosidl_typesupport_cc_library( data = typesupports.values(), includes = [include], linkshared = True, - deps = deps + [ + deps = deps + [label + "_hdrs" for label in typesupports.values()] + [ "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", "@REPOSITORY_ROOT@:rosidl_runtime_cpp_cc", "@REPOSITORY_ROOT@:rosidl_typesupport_cpp_cc", @@ -581,10 +603,11 @@ def typesupport_fastrtps_cc(name): def typesupport_fastrtps_cc_label(name): return ":" + typesupport_fastrtps_cc(name) -def rosidl_cc_support(name, interfaces, deps, **kwargs): +def rosidl_cc_support(name, interfaces, deps, group = None, **kwargs): rosidl_cc_library( name = cc_types(name), - group = name, interfaces = interfaces, + group = group or name, + interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [cc(dep) for dep in deps], **kwargs @@ -595,7 +618,8 @@ def rosidl_cc_support(name, interfaces, deps, **kwargs): if "rosidl_typesupport_introspection_cpp" in AVAILABLE_TYPESUPPORTS: rosidl_typesupport_introspection_cc_library( name = typesupport_introspection_cc(name), - group = name, interfaces = interfaces, + group = group or name, + interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [cc_types_label(name)] + [cc(dep) for dep in deps], **kwargs @@ -606,7 +630,8 @@ def rosidl_cc_support(name, interfaces, deps, **kwargs): if "rosidl_typesupport_fastrtps_cpp" in AVAILABLE_TYPESUPPORTS: rosidl_typesupport_fastrtps_cc_library( name = typesupport_fastrtps_cc(name), - group = name, interfaces = interfaces, + group = group or name, + interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [cc_types_label(name)] + [cc(dep) for dep in deps], **kwargs @@ -617,7 +642,8 @@ def rosidl_cc_support(name, interfaces, deps, **kwargs): rosidl_typesupport_cc_library( name = typesupport_cc(name), typesupports = typesupports, - group = name, interfaces = interfaces, + group = group or name, + interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [cc_types_label(name)] + [cc(dep) for dep in deps], **kwargs @@ -669,10 +695,11 @@ def typesupport_fastrtps_c(name): def typesupport_fastrtps_c_label(name): return ":" + typesupport_fastrtps_c(name) -def rosidl_py_support(name, interfaces, deps, **kwargs): +def rosidl_py_support(name, interfaces, deps, group = None, **kwargs): rosidl_c_library( name = c_types(name), - group = name, interfaces = interfaces, + group = group or name, + interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [c(dep) for dep in deps], **kwargs @@ -683,7 +710,8 @@ def rosidl_py_support(name, interfaces, deps, **kwargs): if "rosidl_typesupport_introspection_c" in AVAILABLE_TYPESUPPORTS: rosidl_typesupport_introspection_c_library( name = typesupport_introspection_c(name), - group = name, interfaces = interfaces, + group = group or name, + interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [c_types_label(name)] + [c(dep) for dep in deps], **kwargs @@ -694,7 +722,8 @@ def rosidl_py_support(name, interfaces, deps, **kwargs): if "rosidl_typesupport_fastrtps_c" in AVAILABLE_TYPESUPPORTS: rosidl_typesupport_fastrtps_c_library( name = typesupport_fastrtps_c(name), - group = name, interfaces = interfaces, + group = group or name, + interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [c_types_label(name)] + [c(dep) for dep in deps], **kwargs @@ -705,7 +734,8 @@ def rosidl_py_support(name, interfaces, deps, **kwargs): rosidl_typesupport_c_library( name = typesupport_c(name), typesupports = typesupports, - group = name, interfaces = interfaces, + group = group or name, + interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [c_types_label(name)] + [c(dep) for dep in deps], **kwargs @@ -725,21 +755,23 @@ def rosidl_py_support(name, interfaces, deps, **kwargs): rosidl_py_library( name = py(name), typesupports = typesupports, - group = name, interfaces = interfaces, + group = group or name, + interfaces = interfaces, includes = [defs(dep) for dep in deps], py_deps = [py(dep) for dep in deps], c_deps = [c_label(name)] + [c(dep) for dep in deps], **kwargs ) -def rosidl_interfaces_group(name, interfaces, deps, **kwargs): +def rosidl_interfaces_group(name, interfaces, deps, group = None, **kwargs): rosidl_definitions_filegroup( name = defs(name), - group = name, interfaces = interfaces, + group = group or name, + interfaces = interfaces, includes = [defs(dep) for dep in deps], **kwargs ) - rosidl_cc_support(name, interfaces, deps, **kwargs) + rosidl_cc_support(name, interfaces, deps, group, **kwargs) - rosidl_py_support(name, interfaces, deps, **kwargs) + rosidl_py_support(name, interfaces, deps, group, **kwargs) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_cc_library_with_runtime_environ.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_cc_library_with_runtime_environ.bazel.tpl deleted file mode 100644 index d88295bd9..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_cc_library_with_runtime_environ.bazel.tpl +++ /dev/null @@ -1,19 +0,0 @@ -cc_library( - name = "_" + @name@, - srcs = @srcs@, - hdrs = glob(["{}/**/*.*".format(x) for x in @headers@]), - includes = @includes@, - copts = @copts@, - defines = @defines@, - linkopts = @linkopts@, - data = @data@, - deps = @deps@, -) - -dload_aware_cc_library( - name = @name@, - base = ":_" + @name@, - data = @data@, - deps = [":_" + @name@] + @deps@, - env = @env@, -) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/prologue.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/prologue.bazel.tpl index 27ea52545..6b4b38524 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/prologue.bazel.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/prologue.bazel.tpl @@ -3,6 +3,5 @@ package(default_visibility = ["//visibility:public"]) load("@REPOSITORY_ROOT@:py_tools.bzl", "ros_py_import") -load("@drake_ros//tools/skylark:dload_cc.bzl", "dload_aware_cc_library") load("@drake_ros//tools/skylark/ros2:common.bzl", "interfaces_filegroup") load("@drake_ros//tools/skylark/ros2:common.bzl", "share_filegroup") diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl index bddf538a6..4e3cfdf1d 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl @@ -1,11 +1,6 @@ # -*- python -*- # vi: set ft=python : -load( - "//tools/skylark:dload.bzl", - "dload_aware_target", -) - BASE_PORT = 16384 def to_bitstrings(integer, *bitsizes): @@ -15,7 +10,7 @@ def to_bitstrings(integer, *bitsizes): integer = integer >> bitsize return reversed(bitstrings) -def rmw_fastrtps_netconf(name): +def generate_isolated_fastrtps_profile(name): tool = "//tools/skylark/ros2:rmw_fastrtps_profile_gen" label_name = "{}//{}:{}".format( native.repository_name(), native.package_name(), name @@ -32,21 +27,16 @@ def rmw_fastrtps_netconf(name): "--use-as-default", ] - profile = name + "_profile" native.genrule( - name = profile, - outs = [profile + ".xml"], + name = "gen_" + name, + outs = [name], cmd = "./$(location {}) {} > $@".format(tool, " ".join(args)), tools = [tool], output_to_bindir = True, ) - profile_path = "{}/{}/{}.xml".format( - native.repository_name(), native.package_name(), profile - ) - dload_aware_target( - name = name, - base = ":" + profile, - runenv = { - "FASTRTPS_DEFAULT_PROFILES_FILE": ["path-replace", profile_path], - } + +def use_fastrtps_profile(name): + path = "{}/{}/{}".format( + native.repository_name(), native.package_name(), name ) + return {"FASTRTPS_DEFAULT_PROFILES_FILE": ["path-replace", path]} diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl index 5940b2d56..28c6b2924 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -1,4 +1,4 @@ -load("//tools/skylark:execute.bzl", "execute_or_fail") +load("@drake_ros//tools/skylark:execute.bzl", "execute_or_fail") MANIFEST = [ "cmake_tools/packages.py", @@ -6,14 +6,13 @@ MANIFEST = [ "cmake_tools/__init__.py", "resources/bazel/cc_tools.bzl.tpl", + "resources/bazel/common.bzl.tpl", "resources/bazel/distro.bzl.tpl", "resources/bazel/py_tools.bzl.tpl", "resources/bazel/rosidl_tools.bzl.tpl", "resources/bazel/snippets/overlay_executable.bazel.tpl", - "resources/bazel/snippets/package_alias.bazel.tpl", "resources/bazel/snippets/package_interfaces_filegroup.bazel.tpl", "resources/bazel/snippets/package_cc_library.bazel.tpl", - "resources/bazel/snippets/package_cc_library_with_runtime_environ.bazel.tpl", "resources/bazel/snippets/package_meta_py_library.bazel.tpl", "resources/bazel/snippets/package_py_library.bazel.tpl", "resources/bazel/snippets/package_py_library_with_cc_libs.bazel.tpl", diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py index 278c119ff..d65079b9f 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py @@ -28,9 +28,7 @@ def index_all_packages(): for name, prefix in ament_index_python.get_packages_with_prefixes().items() } - for name, prefix in cmake_tools.get_packages_with_prefixes( - ament_index_python.get_search_paths() - ).items(): + for name, prefix in cmake_tools.get_packages_with_prefixes().items(): if name in packages: # Assume unique package names across package types continue diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py index 806abeddd..55ebbf74c 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py @@ -1,3 +1,4 @@ +import glob import os from multiprocessing.dummy import Pool @@ -143,7 +144,16 @@ def collect_ament_cmake_package_properties(name, metadata): assert project_name in targets target = targets[project_name] - return collect_ament_cmake_shared_library_codemodel(target) + properties = collect_ament_cmake_shared_library_codemodel(target) + if 'rosidl_interface_packages' in metadata.get('groups', []): + # Pick up extra shared libraries in interface + # packages for adequate sandboxing + glob_pattern = os.path.join( + metadata['prefix'], 'lib', f'lib{name}__rosidl*.so') + for library in glob.glob(glob_pattern): + if library not in properties['link_libraries']: + properties['link_libraries'].append(library) + return properties def collect_ament_cmake_package_direct_properties(name, metadata, dependencies, cache): diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py index 11651acb0..f386d7520 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py @@ -63,8 +63,13 @@ def configure_package_cc_library(name, metadata, properties, dependencies, extra libraries = [sandbox(library) for library in properties['link_libraries']] include_directories = [sandbox(include) for include in properties['include_directories']] local_includes = [include for include in include_directories if not os.path.isabs(include)] - # Assume package abides to REP-122 FHS layout - headers = [os.path.join(include, name) for include in local_includes] + headers = [] + for include in local_includes: + if not include.endswith(os.path.join(name, 'include')): + # Assume package lives in a merged install space + # Assume package abides to REP-122 FHS layout + include = os.path.join(include, name) + headers.append(include) # Push remaining nonlocal includes through compiler options copts = ['-isystem ' + include for include in include_directories if os.path.isabs(include)] copts.extend(properties['compile_flags']) @@ -103,6 +108,8 @@ def configure_package_cc_library(name, metadata, properties, dependencies, extra if label_or_path not in data ) + template_path = 'bazel/snippets/package_cc_library.bazel.tpl' + config = { 'name': target_name, 'srcs': libraries, @@ -115,12 +122,6 @@ def configure_package_cc_library(name, metadata, properties, dependencies, extra 'deps': deps, } - if 'rmw_implementation_packages' in metadata.get('groups', []): - template_path = 'bazel/snippets/package_cc_library_with_runtime_environ.bazel.tpl' - config['env'] = {'RMW_IMPLEMENTATION': ['replace', name]} - else: - template_path = 'bazel/snippets/package_cc_library.bazel.tpl' - return target_name, load_resource(template_path), to_starlark_string_dict(config) @@ -289,6 +290,12 @@ def configure_py_tools(repo_name): } +def configure_common(repo_name): + return load_resource('bazel/common.bzl.tpl'), { + 'REPOSITORY_ROOT': '@{}//'.format(repo_name) + } + + def configure_distro(distro): typesupport_groups = [ 'rosidl_typesupport_c_packages', diff --git a/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl b/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl index 7ea51961a..8be7ed67f 100644 --- a/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl +++ b/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl @@ -3,9 +3,10 @@ load("//tools/skylark/ros2:ros2.bzl", "ros2_local_repository") ROS2_DIST = "rolling" def ros2_repository(name, overlays = []): + overlays = ["/home/michel/Workspaces/drake_ros_ws/install"] ros2_local_repository( name = name, - workspaces = ["/opt/ros/{}".format(ROS2_DIST)] + overlays, + workspaces = overlays, include_packages = [ "std_msgs", "geometry_msgs", From f0ecfe27840f89661ea8e91b3900f435498250c6 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 22 Sep 2021 11:11:27 -0300 Subject: [PATCH 031/107] Add READMEs to packages Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/apps/README.md | 10 ++++++++++ drake_ros_bazel_installed/common/README.md | 3 +++ 2 files changed, 13 insertions(+) create mode 100644 drake_ros_bazel_installed/apps/README.md create mode 100644 drake_ros_bazel_installed/common/README.md diff --git a/drake_ros_bazel_installed/apps/README.md b/drake_ros_bazel_installed/apps/README.md new file mode 100644 index 000000000..2d6602a12 --- /dev/null +++ b/drake_ros_bazel_installed/apps/README.md @@ -0,0 +1,10 @@ +## Demo apps + +This package exercises `rosidl` interface generation and usage in C++ and Python, with cross-package interface dependencies. + +To check that the resulting interfaces are functional, it includes a demo applications in C++ and Python. Namely: + +- `oracle_cc`/`oracle_py` applications that publish `apps_msgs/msg/Status` messages, serve a `common_msgs/srv/Query` + service, and provide a `common_msgs/action/Do` action. +- `inquirer_cc`/`inquirer_py` applications that subscribe to `apps_msgs/msg/Status` messages, make `common_msgs/srv/Query` + service requests, and invoke the `common_msgs/action/Do` action. diff --git a/drake_ros_bazel_installed/common/README.md b/drake_ros_bazel_installed/common/README.md new file mode 100644 index 000000000..bfc747060 --- /dev/null +++ b/drake_ros_bazel_installed/common/README.md @@ -0,0 +1,3 @@ +## Common interfaces + +This package exercises `rosidl` interface generation in C++ and Python. From 055193a4e2cc40f33a04d0562778df14f1a984e2 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 22 Sep 2021 11:14:12 -0300 Subject: [PATCH 032/107] Use explicit QoS settings in Python Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/apps/inquirer.py | 4 +++- drake_ros_bazel_installed/apps/oracle.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drake_ros_bazel_installed/apps/inquirer.py b/drake_ros_bazel_installed/apps/inquirer.py index 5e772d68f..789d382c7 100644 --- a/drake_ros_bazel_installed/apps/inquirer.py +++ b/drake_ros_bazel_installed/apps/inquirer.py @@ -5,6 +5,7 @@ import rclpy.action import rclpy.duration import rclpy.node +import rclpy.qos import apps_msgs.msg import common_msgs.action @@ -16,7 +17,8 @@ class Inquirer(rclpy.node.Node): def __init__(self): super().__init__('inquirer') self._status_sub = self.create_subscription( - apps_msgs.msg.Status, 'status', self._on_status, 1) + apps_msgs.msg.Status, 'status', self._on_status, + rclpy.qos.QoSProfile(depth=1)) self._query_client = self.create_client(common_msgs.srv.Query, 'query') self._action_client = rclpy.action.ActionClient( self, common_msgs.action.Do, 'do') diff --git a/drake_ros_bazel_installed/apps/oracle.py b/drake_ros_bazel_installed/apps/oracle.py index f9dd86567..36d64393f 100644 --- a/drake_ros_bazel_installed/apps/oracle.py +++ b/drake_ros_bazel_installed/apps/oracle.py @@ -6,6 +6,7 @@ import rclpy.action import rclpy.duration import rclpy.node +import rclpy.qos import apps_msgs.msg import common_msgs.action @@ -18,7 +19,8 @@ def __init__(self): super().__init__('oracle') self._sequence_id = 0 self._status_pub = self.create_publisher( - apps_msgs.msg.Status, 'status', 1) + apps_msgs.msg.Status, 'status', + rclpy.qos.QoSProfile(depth=1)) self._query_server = self.create_service( common_msgs.srv.Query, 'query', self._handle_query) self._action_server = rclpy.action.ActionServer( From 701fd860104a331cf4fddafb8aa0eed680d25532 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 22 Sep 2021 11:20:07 -0300 Subject: [PATCH 033/107] Make dload*.bzl utilities private Signed-off-by: Michel Hidalgo --- .../tools/skylark/dload.bzl | 22 +++++++++---------- .../tools/skylark/dload_cc.bzl | 4 ++-- .../tools/skylark/dload_py.bzl | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/dload.bzl b/drake_ros_bazel_installed/tools/skylark/dload.bzl index 4a6528b5e..59412bcaf 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload.bzl @@ -12,7 +12,7 @@ MAGIC_VARIABLES = { "${LOAD_PATH}": "LD_LIBRARY_PATH" # for Linux } -def unique(input_list): +def _unique(input_list): """Extracts unique values from input list, while preserving their order.""" output_list = [] for item in input_list: @@ -20,7 +20,7 @@ def unique(input_list): output_list.append(item) return output_list -def normpath(path): +def _normpath(path): """ Normalizes a path by removing redundant separators and up-level references. @@ -76,7 +76,7 @@ def do_dload_shim(ctx, template, to_list): env_changes = { MAGIC_VARIABLES.get(name, default=name): - parse_runtime_environment_action(ctx, action) + _parse_runtime_environment_action(ctx, action) for name, action in ctx.attr.env_changes.items() } envvars = env_changes.keys() @@ -84,7 +84,7 @@ def do_dload_shim(ctx, template, to_list): shim_content = template.format( # Deal with usage in external workspaces' BUILD.bazel files - executable_path=normpath("{}/{}".format( + executable_path=_normpath("{}/{}".format( ctx.workspace_name, executable_file.short_path )), names=to_list([repr(name) for name in envvars]), @@ -101,20 +101,20 @@ def do_dload_shim(ctx, template, to_list): data_runfiles = ctx.runfiles(files = [shim]), )] -def resolve_runfile_path(ctx, path): +def _resolve_runfile_path(ctx, path): """ Resolves a package relative path into an (expected) runfiles directory relative path. All `$(rootpath...)` templates in the given path, if any, will be expanded. """ - path = normpath(ctx.expand_location(path)) + path = _normpath(ctx.expand_location(path)) path = path.lstrip("@") if path.startswith("/"): path = ctx.workspace_name + path return path -def parse_runtime_environment_action(ctx, action): +def _parse_runtime_environment_action(ctx, action): """ Parses a runtime environment action, validating types and resolving paths. """ @@ -123,16 +123,16 @@ def parse_runtime_environment_action(ctx, action): if len(action_args) == 0: tpl = "'{}' action requires at least one argument" fail(msg = tpl.format(action_type)) - action_args = unique([ - resolve_runfile_path(ctx, path[:-1]) + "!" if path.endswith("!") - else resolve_runfile_path(ctx, path) for path in action_args + action_args = _unique([ + _resolve_runfile_path(ctx, path[:-1]) + "!" if path.endswith("!") + else _resolve_runfile_path(ctx, path) for path in action_args ]) elif action_type in ("path-replace", "replace"): if len(action_args) != 1: tpl = "'{}' action requires exactly one argument" fail(msg = tpl.format(action_type)) if action_type.startswith("path"): - action_args = [resolve_runfile_path(ctx, action_args[0])] + action_args = [_resolve_runfile_path(ctx, action_args[0])] else: fail(msg = "'{}' action is unknown".format(action_type)) return [action_type] + action_args diff --git a/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl b/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl index d9d28ce2d..c1a663481 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl @@ -79,12 +79,12 @@ int main(int argc, const char * argv[]) {{ }} """ -def to_cc_list(collection): +def _to_cc_list(collection): """Turn collection into a C++ aggregate initializer expression.""" return "{" + ", ".join(collection) + "}" def _dload_cc_shim_impl(ctx): - return do_dload_shim(ctx, DLOAD_CC_SHIM_TEMPLATE, to_cc_list) + return do_dload_shim(ctx, DLOAD_CC_SHIM_TEMPLATE, _to_cc_list) dload_cc_shim = rule( attrs = get_dload_shim_attributes(), diff --git a/drake_ros_bazel_installed/tools/skylark/dload_py.bzl b/drake_ros_bazel_installed/tools/skylark/dload_py.bzl index 9207d14ce..b57f620fa 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload_py.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload_py.bzl @@ -50,12 +50,12 @@ argv = [executable_path] + sys.argv[1:] os.execv(executable_path, argv) """ -def to_py_list(collection): +def _to_py_list(collection): """Turn collection into a Python list expression.""" return "[" + ", ".join(collection) + "]" def _dload_py_shim_impl(ctx): - return do_dload_shim(ctx, DLOAD_PY_SHIM_TEMPLATE, to_py_list) + return do_dload_shim(ctx, DLOAD_PY_SHIM_TEMPLATE, _to_py_list) dload_py_shim = rule( attrs = get_dload_shim_attributes(), From e567c106fb8d40ed5470208603721c5e300aad47 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 22 Sep 2021 11:34:56 -0300 Subject: [PATCH 034/107] Put Python shim logic in a main() function Signed-off-by: Michel Hidalgo --- .../tools/skylark/dload_py.bzl | 58 ++++++++++--------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/dload_py.bzl b/drake_ros_bazel_installed/tools/skylark/dload_py.bzl index b57f620fa..3df247c2c 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload_py.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload_py.bzl @@ -19,35 +19,41 @@ import sys from bazel_tools.tools.python.runfiles import runfiles -r = runfiles.Create() -# NOTE(hidmic): unlike its C++ equivalent, Python runfiles' -# builtin tools will only look for runfiles in the manifest -# if there is a manifest -runfiles_dir = r.EnvVars()['RUNFILES_DIR'] -def rlocation(path): - return r.Rlocation(path) or os.path.join(runfiles_dir, path) +def main(argv): + r = runfiles.Create() + # NOTE(hidmic): unlike its C++ equivalent, Python runfiles' + # builtin tools will only look for runfiles in the manifest + # if there is a manifest + runfiles_dir = r.EnvVars()['RUNFILES_DIR'] -for name, action in zip({names}, {actions}): # noqa - action_type, action_args = action[0], action[1:] - if action_type == 'replace': - assert len(action_args) == 1 - value = action_args[0] - elif action_type == 'path-replace': - assert len(action_args) == 1 - value = rlocation(action_args[0]) - elif action_type == 'path-prepend': - assert len(action_args) > 0 - value = ':'.join([rlocation(path) for path in action_args]) - if name in os.environ: - value += ':' + os.environ[name] - else: - assert False # should never get here - os.environ[name] = value + def rlocation(path): + return r.Rlocation(path) or os.path.join(runfiles_dir, path) -executable_path = r.Rlocation('{executable_path}') # noqa -argv = [executable_path] + sys.argv[1:] -os.execv(executable_path, argv) + for name, action in zip({names}, {actions}): # noqa + action_type, action_args = action[0], action[1:] + if action_type == 'replace': + assert len(action_args) == 1 + value = action_args[0] + elif action_type == 'path-replace': + assert len(action_args) == 1 + value = rlocation(action_args[0]) + elif action_type == 'path-prepend': + assert len(action_args) > 0 + value = ':'.join([rlocation(path) for path in action_args]) + if name in os.environ: + value += ':' + os.environ[name] + else: + assert False # should never get here + os.environ[name] = value + + executable_path = r.Rlocation('{executable_path}') # noqa + argv = [executable_path] + argv[1:] + os.execv(executable_path, argv) + + +if __name__ == '__main__': + main(sys.argv) """ def _to_py_list(collection): From ed1bcab6b3e51977ebe085fe5e915789bd99dd7a Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 22 Sep 2021 11:38:54 -0300 Subject: [PATCH 035/107] Add comments to BUILD.bazel files Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/BUILD.bazel | 1 + drake_ros_bazel_installed/apps/BUILD.bazel | 3 +++ drake_ros_bazel_installed/common/BUILD.bazel | 3 +++ drake_ros_bazel_installed/tools/BUILD.bazel | 1 + drake_ros_bazel_installed/tools/skylark/BUILD.bazel | 1 + drake_ros_bazel_installed/tools/workspace/BUILD.bazel | 1 + drake_ros_bazel_installed/tools/workspace/python/BUILD.bazel | 1 + drake_ros_bazel_installed/tools/workspace/ros2/BUILD.bazel | 1 + 8 files changed, 12 insertions(+) diff --git a/drake_ros_bazel_installed/BUILD.bazel b/drake_ros_bazel_installed/BUILD.bazel index e69de29bb..868c8a80d 100644 --- a/drake_ros_bazel_installed/BUILD.bazel +++ b/drake_ros_bazel_installed/BUILD.bazel @@ -0,0 +1 @@ +# Empty build file to mark package root. diff --git a/drake_ros_bazel_installed/apps/BUILD.bazel b/drake_ros_bazel_installed/apps/BUILD.bazel index 9c9793ec0..b74e8eb01 100644 --- a/drake_ros_bazel_installed/apps/BUILD.bazel +++ b/drake_ros_bazel_installed/apps/BUILD.bazel @@ -1,3 +1,6 @@ +# -*- mode: python -*- +# vi: set ft=python : + load("@ros2//:rosidl_tools.bzl", "rosidl_interfaces_group") load("@ros2//:cc_tools.bzl", "ros_cc_binary") load("@ros2//:py_tools.bzl", "ros_py_binary") diff --git a/drake_ros_bazel_installed/common/BUILD.bazel b/drake_ros_bazel_installed/common/BUILD.bazel index 00afe00e5..9494e611d 100644 --- a/drake_ros_bazel_installed/common/BUILD.bazel +++ b/drake_ros_bazel_installed/common/BUILD.bazel @@ -1,3 +1,6 @@ +# -*- mode: python -*- +# vi: set ft=python : + load("@ros2//:rosidl_tools.bzl", "rosidl_interfaces_group") rosidl_interfaces_group( diff --git a/drake_ros_bazel_installed/tools/BUILD.bazel b/drake_ros_bazel_installed/tools/BUILD.bazel index e69de29bb..cc844a317 100644 --- a/drake_ros_bazel_installed/tools/BUILD.bazel +++ b/drake_ros_bazel_installed/tools/BUILD.bazel @@ -0,0 +1 @@ +# Empty build file to mark package boundaries. diff --git a/drake_ros_bazel_installed/tools/skylark/BUILD.bazel b/drake_ros_bazel_installed/tools/skylark/BUILD.bazel index e69de29bb..cc844a317 100644 --- a/drake_ros_bazel_installed/tools/skylark/BUILD.bazel +++ b/drake_ros_bazel_installed/tools/skylark/BUILD.bazel @@ -0,0 +1 @@ +# Empty build file to mark package boundaries. diff --git a/drake_ros_bazel_installed/tools/workspace/BUILD.bazel b/drake_ros_bazel_installed/tools/workspace/BUILD.bazel index e69de29bb..cc844a317 100644 --- a/drake_ros_bazel_installed/tools/workspace/BUILD.bazel +++ b/drake_ros_bazel_installed/tools/workspace/BUILD.bazel @@ -0,0 +1 @@ +# Empty build file to mark package boundaries. diff --git a/drake_ros_bazel_installed/tools/workspace/python/BUILD.bazel b/drake_ros_bazel_installed/tools/workspace/python/BUILD.bazel index e69de29bb..cc844a317 100644 --- a/drake_ros_bazel_installed/tools/workspace/python/BUILD.bazel +++ b/drake_ros_bazel_installed/tools/workspace/python/BUILD.bazel @@ -0,0 +1 @@ +# Empty build file to mark package boundaries. diff --git a/drake_ros_bazel_installed/tools/workspace/ros2/BUILD.bazel b/drake_ros_bazel_installed/tools/workspace/ros2/BUILD.bazel index e69de29bb..cc844a317 100644 --- a/drake_ros_bazel_installed/tools/workspace/ros2/BUILD.bazel +++ b/drake_ros_bazel_installed/tools/workspace/ros2/BUILD.bazel @@ -0,0 +1 @@ +# Empty build file to mark package boundaries. From 53c114f7976bce129a26441896a4658138a50dc5 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 22 Sep 2021 12:27:48 -0300 Subject: [PATCH 036/107] Use ROS 2 Rolling binary installation Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl b/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl index 8be7ed67f..9a9f4ce36 100644 --- a/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl +++ b/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl @@ -3,10 +3,9 @@ load("//tools/skylark/ros2:ros2.bzl", "ros2_local_repository") ROS2_DIST = "rolling" def ros2_repository(name, overlays = []): - overlays = ["/home/michel/Workspaces/drake_ros_ws/install"] ros2_local_repository( name = name, - workspaces = overlays, + workspaces = ['/opt/ros/{}'.format(ROS2_DIST)], include_packages = [ "std_msgs", "geometry_msgs", From c1fc00ba351ed2f98f58f3c55be4ee678842b3a6 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 22 Sep 2021 12:28:26 -0300 Subject: [PATCH 037/107] Drop nonexistent lint tests Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel index e735a65c6..191fe6f4c 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel +++ b/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel @@ -1,12 +1,8 @@ # -*- mode: python -*- # vi: set ft=python : -load("//tools/lint:lint.bzl", "add_lint_tests") - py_binary( name = "rmw_fastrtps_profile_gen", srcs = ["rmw_fastrtps_profile_gen.py"], visibility = ["//visibility:public"], ) - -add_lint_tests() From 91392b56311c769820aa1c33a40492f5fd03c0da Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 22 Sep 2021 12:28:43 -0300 Subject: [PATCH 038/107] Fix pure CMake package scrapping Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/cmake_tools/packages.py | 1 + .../tools/skylark/ros2/resources/shell/setup.sh.in | 1 + drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl | 1 + 3 files changed, 3 insertions(+) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/packages.py b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/packages.py index c1b0cd9d3..2bb5179fd 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/packages.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/packages.py @@ -5,6 +5,7 @@ def get_packages_with_prefixes(prefixes=None): if prefixes is None: prefixes = os.environ['CMAKE_PREFIX_PATH'].split(os.pathsep) + prefixes = [prefix for prefix in prefixes if prefix] suffixes = 'Config.cmake', '-config.cmake' return { os.path.basename(path)[:-len(suffix)]: prefix diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/shell/setup.sh.in b/drake_ros_bazel_installed/tools/skylark/ros2/resources/shell/setup.sh.in index 3ed4fdf4b..28addc617 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/shell/setup.sh.in +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/shell/setup.sh.in @@ -8,6 +8,7 @@ for ws in @WORKSPACES@; do done # Setup environment +export CMAKE_PREFIX_PATH="@CMAKE_PREFIX_PATH@:$CMAKE_PREFIX_PATH" export PYTHONPATH="@REPOSITORY_DIR@:$PYTHONPATH" export PATH="@REPOSITORY_DIR@:$PATH" diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl index 28c6b2924..695fb08ef 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -50,6 +50,7 @@ def _impl(repo_ctx): "@ID@": _uuid(repo_ctx), "@REPOSITORY_DIR@": str(repo_ctx.path(".")), "@WORKSPACES@": " ".join(repo_ctx.attr.workspaces), + "@CMAKE_PREFIX_PATH@": ":".join(repo_ctx.attr.workspaces), }, executable = True ) From 6f0f56619af9eea1efa36f0f1e5ad58791dc9ecb Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 30 Sep 2021 15:25:40 -0300 Subject: [PATCH 039/107] Add drake_ros_ prefix to package names Signed-off-by: Michel Hidalgo --- .../{apps => drake_ros_apps}/BUILD.bazel | 20 ++++----- .../{apps => drake_ros_apps}/README.md | 2 +- .../{apps => drake_ros_apps}/inquirer.cc | 37 ++++++++-------- .../{apps => drake_ros_apps}/inquirer.py | 17 ++++---- .../{apps => drake_ros_apps}/msg/Status.msg | 2 +- .../{apps => drake_ros_apps}/oracle.cc | 42 +++++++++---------- .../{apps => drake_ros_apps}/oracle.py | 18 ++++---- .../{common => drake_ros_common}/BUILD.bazel | 2 +- .../{common => drake_ros_common}/README.md | 2 +- .../action/Do.action | 0 .../msg/Status.msg | 0 .../srv/Query.srv | 0 12 files changed, 72 insertions(+), 70 deletions(-) rename drake_ros_bazel_installed/{apps => drake_ros_apps}/BUILD.bazel (73%) rename drake_ros_bazel_installed/{apps => drake_ros_apps}/README.md (96%) rename drake_ros_bazel_installed/{apps => drake_ros_apps}/inquirer.cc (72%) rename drake_ros_bazel_installed/{apps => drake_ros_apps}/inquirer.py (86%) rename drake_ros_bazel_installed/{apps => drake_ros_apps}/msg/Status.msg (55%) rename drake_ros_bazel_installed/{apps => drake_ros_apps}/oracle.cc (67%) rename drake_ros_bazel_installed/{apps => drake_ros_apps}/oracle.py (85%) rename drake_ros_bazel_installed/{common => drake_ros_common}/BUILD.bazel (91%) rename drake_ros_bazel_installed/{common => drake_ros_common}/README.md (68%) rename drake_ros_bazel_installed/{common => drake_ros_common}/action/Do.action (100%) rename drake_ros_bazel_installed/{common => drake_ros_common}/msg/Status.msg (100%) rename drake_ros_bazel_installed/{common => drake_ros_common}/srv/Query.srv (100%) diff --git a/drake_ros_bazel_installed/apps/BUILD.bazel b/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel similarity index 73% rename from drake_ros_bazel_installed/apps/BUILD.bazel rename to drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel index b74e8eb01..145a5adb9 100644 --- a/drake_ros_bazel_installed/apps/BUILD.bazel +++ b/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel @@ -7,12 +7,12 @@ load("@ros2//:py_tools.bzl", "ros_py_binary") rosidl_interfaces_group( - name = "apps_msgs", + name = "drake_ros_apps_msgs", interfaces = [ "msg/Status.msg", ], deps = [ - "//common:common_msgs", + "//drake_ros_common:drake_ros_common_msgs", "@ros2//:builtin_interfaces", ], ) @@ -21,8 +21,8 @@ ros_cc_binary( name = "oracle_cc", srcs = ["oracle.cc"], deps = [ - ":apps_msgs_cc", - "//common:common_msgs_cc", + ":drake_ros_apps_msgs_cc", + "//drake_ros_common:drake_ros_common_msgs_cc", "@ros2//:rclcpp_cc", "@ros2//:rclcpp_action_cc", ], @@ -34,8 +34,8 @@ ros_cc_binary( name = "inquirer_cc", srcs = ["inquirer.cc"], deps = [ - ":apps_msgs_cc", - "//common:common_msgs_cc", + ":drake_ros_apps_msgs_cc", + "//drake_ros_common:drake_ros_common_msgs_cc", "@ros2//:rclcpp_cc", "@ros2//:rclcpp_action_cc", ], @@ -48,8 +48,8 @@ ros_py_binary( srcs = ["oracle.py"], main = "oracle.py", deps = [ - ":apps_msgs_py", - "//common:common_msgs_py", + ":drake_ros_apps_msgs_py", + "//drake_ros_common:drake_ros_common_msgs_py", "@ros2//:rclpy_py", ], rmw_implementation = "rmw_cyclonedds_cpp", @@ -61,8 +61,8 @@ ros_py_binary( srcs = ["inquirer.py"], main = "inquirer.py", deps = [ - ":apps_msgs_py", - "//common:common_msgs_py", + ":drake_ros_apps_msgs_py", + "//drake_ros_common:drake_ros_common_msgs_py", "@ros2//:rclpy_py", ], rmw_implementation = "rmw_cyclonedds_cpp", diff --git a/drake_ros_bazel_installed/apps/README.md b/drake_ros_bazel_installed/drake_ros_apps/README.md similarity index 96% rename from drake_ros_bazel_installed/apps/README.md rename to drake_ros_bazel_installed/drake_ros_apps/README.md index 2d6602a12..5b0787762 100644 --- a/drake_ros_bazel_installed/apps/README.md +++ b/drake_ros_bazel_installed/drake_ros_apps/README.md @@ -1,4 +1,4 @@ -## Demo apps +## Demo `drake_ros` apps This package exercises `rosidl` interface generation and usage in C++ and Python, with cross-package interface dependencies. diff --git a/drake_ros_bazel_installed/apps/inquirer.cc b/drake_ros_bazel_installed/drake_ros_apps/inquirer.cc similarity index 72% rename from drake_ros_bazel_installed/apps/inquirer.cc rename to drake_ros_bazel_installed/drake_ros_apps/inquirer.cc index b86d0f435..5953b37ea 100644 --- a/drake_ros_bazel_installed/apps/inquirer.cc +++ b/drake_ros_bazel_installed/drake_ros_apps/inquirer.cc @@ -5,38 +5,38 @@ #include #include -#include "apps_msgs/msg/status.hpp" -#include "common_msgs/srv/query.hpp" -#include "common_msgs/action/do.hpp" +#include "drake_ros_apps_msgs/msg/status.hpp" +#include "drake_ros_common_msgs/srv/query.hpp" +#include "drake_ros_common_msgs/action/do.hpp" using namespace std::chrono_literals; -namespace apps { +namespace drake_ros_apps { class Inquirer : public rclcpp::Node { public: Inquirer() : Node("inquirer") { using namespace std::placeholders; status_sub_ = - this->create_subscription( + this->create_subscription( "status", rclcpp::QoS(rclcpp::KeepLast(1)), std::bind(&Inquirer::on_status, this, _1)); - query_client_ = this->create_client("query"); + query_client_ = this->create_client("query"); - action_client_ = rclcpp_action::create_client(this, "do"); + action_client_ = rclcpp_action::create_client(this, "do"); inquire_timer_ = this->create_wall_timer(5s, std::bind(&Inquirer::inquire, this)); } private: - using QueryClient = rclcpp::Client; + using QueryClient = rclcpp::Client; void handle_reply(QueryClient::SharedFuture future) const { RCLCPP_INFO(this->get_logger(), "oracle said: %s", future.get()->reply.c_str()); } - using ActionGoalHandle = rclcpp_action::ClientGoalHandle; + using ActionGoalHandle = rclcpp_action::ClientGoalHandle; void handle_rite_request_response(const std::shared_ptr handle) const { if (!handle) { RCLCPP_ERROR(this->get_logger(), "oracle rejected rite request"); @@ -76,7 +76,8 @@ class Inquirer : public rclcpp::Node { using namespace std::placeholders; if (query_client_->service_is_ready()) { - auto request = std::make_shared(); + auto request = + std::make_shared(); request->query = "how's it going?"; RCLCPP_INFO(this->get_logger(), "oracle, %s", request->query.c_str()); query_client_->async_send_request( @@ -86,14 +87,14 @@ class Inquirer : public rclcpp::Node { } if (action_client_->action_server_is_ready()) { - rclcpp_action::Client::SendGoalOptions options; + rclcpp_action::Client::SendGoalOptions options; options.goal_response_callback = std::bind(&Inquirer::handle_rite_request_response, this, _1); options.feedback_callback = std::bind(&Inquirer::handle_rite_feedback, this, _1, _2); options.result_callback = std::bind(&Inquirer::handle_rite_result, this, _1); - common_msgs::action::Do::Goal goal; + drake_ros_common_msgs::action::Do::Goal goal; goal.action = "rite"; goal.period = rclcpp::Duration::from_seconds(0.1); goal.timeout = rclcpp::Duration::from_seconds(1.0); @@ -103,25 +104,25 @@ class Inquirer : public rclcpp::Node { } } - void on_status(const apps_msgs::msg::Status & msg) const { + void on_status(const drake_ros_apps_msgs::msg::Status & msg) const { RCLCPP_INFO( get_logger(), "%s status (%lu): %s", msg.origin.c_str(), msg.status.sequence_id, msg.status.message.c_str()); } - std::shared_ptr> status_sub_; - std::shared_ptr> query_client_; - std::shared_ptr> action_client_; + std::shared_ptr> status_sub_; + std::shared_ptr> query_client_; + std::shared_ptr> action_client_; std::shared_ptr inquire_timer_; }; -} // namespace apps +} // namespace drake_ros_apps int main(int argc, char ** argv) { rclcpp::init(argc, argv); - rclcpp::spin(std::make_shared()); + rclcpp::spin(std::make_shared()); rclcpp::shutdown(); } diff --git a/drake_ros_bazel_installed/apps/inquirer.py b/drake_ros_bazel_installed/drake_ros_apps/inquirer.py similarity index 86% rename from drake_ros_bazel_installed/apps/inquirer.py rename to drake_ros_bazel_installed/drake_ros_apps/inquirer.py index 789d382c7..b13287ce4 100644 --- a/drake_ros_bazel_installed/apps/inquirer.py +++ b/drake_ros_bazel_installed/drake_ros_apps/inquirer.py @@ -7,9 +7,9 @@ import rclpy.node import rclpy.qos -import apps_msgs.msg -import common_msgs.action -import common_msgs.srv +import drake_ros_apps_msgs.msg +import drake_ros_common_msgs.action +import drake_ros_common_msgs.srv class Inquirer(rclpy.node.Node): @@ -17,11 +17,12 @@ class Inquirer(rclpy.node.Node): def __init__(self): super().__init__('inquirer') self._status_sub = self.create_subscription( - apps_msgs.msg.Status, 'status', self._on_status, + drake_ros_apps_msgs.msg.Status, 'status', self._on_status, rclpy.qos.QoSProfile(depth=1)) - self._query_client = self.create_client(common_msgs.srv.Query, 'query') + self._query_client = self.create_client( + drake_ros_common_msgs.srv.Query, 'query') self._action_client = rclpy.action.ActionClient( - self, common_msgs.action.Do, 'do') + self, drake_ros_common_msgs.action.Do, 'do') self._inquire_timer = self.create_timer(5.0, self.inquire) def _on_status(self, msg): @@ -59,7 +60,7 @@ def _handle_rite_result(self, future): def inquire(self): if self._query_client.service_is_ready(): - request = common_msgs.srv.Query.Request() + request = drake_ros_common_msgs.srv.Query.Request() request.query = "how's it going?" self.get_logger().info('oracle, ' + request.query) future = self._query_client.call_async(request) @@ -67,7 +68,7 @@ def inquire(self): else: self.get_logger().warning('oracle not available for queries') if self._action_client.server_is_ready(): - goal = common_msgs.action.Do.Goal() + goal = drake_ros_common_msgs.action.Do.Goal() goal.action = 'rite' goal.period = rclpy.duration.Duration(seconds=0.1).to_msg() goal.timeout = rclpy.duration.Duration(seconds=1.0).to_msg() diff --git a/drake_ros_bazel_installed/apps/msg/Status.msg b/drake_ros_bazel_installed/drake_ros_apps/msg/Status.msg similarity index 55% rename from drake_ros_bazel_installed/apps/msg/Status.msg rename to drake_ros_bazel_installed/drake_ros_apps/msg/Status.msg index bf0496267..111599f39 100644 --- a/drake_ros_bazel_installed/apps/msg/Status.msg +++ b/drake_ros_bazel_installed/drake_ros_apps/msg/Status.msg @@ -1,3 +1,3 @@ builtin_interfaces/Time stamp -common_msgs/Status status +drake_ros_common_msgs/Status status string origin diff --git a/drake_ros_bazel_installed/apps/oracle.cc b/drake_ros_bazel_installed/drake_ros_apps/oracle.cc similarity index 67% rename from drake_ros_bazel_installed/apps/oracle.cc rename to drake_ros_bazel_installed/drake_ros_apps/oracle.cc index 99c56711f..a61e4e84a 100644 --- a/drake_ros_bazel_installed/apps/oracle.cc +++ b/drake_ros_bazel_installed/drake_ros_apps/oracle.cc @@ -8,13 +8,13 @@ #include #include -#include "apps_msgs/msg/status.hpp" -#include "common_msgs/action/do.hpp" -#include "common_msgs/srv/query.hpp" +#include "drake_ros_apps_msgs/msg/status.hpp" +#include "drake_ros_common_msgs/action/do.hpp" +#include "drake_ros_common_msgs/srv/query.hpp" using namespace std::chrono_literals; -namespace apps { +namespace drake_ros_apps { class Oracle : public rclcpp::Node { public: @@ -22,13 +22,13 @@ class Oracle : public rclcpp::Node { { using namespace std::placeholders; - status_pub_ = this->create_publisher( + status_pub_ = this->create_publisher( "status", rclcpp::QoS(rclcpp::KeepLast(1))); - query_server_ = this->create_service( + query_server_ = this->create_service( "query", std::bind(&Oracle::handle_query, this, _1, _2)); - action_server_ = rclcpp_action::create_server( + action_server_ = rclcpp_action::create_server( this, "do", std::bind(&Oracle::handle_action_goal, this, _1, _2), std::bind(&Oracle::handle_cancelled_action, this, _1), std::bind(&Oracle::handle_accepted_action, this, _1)); @@ -42,7 +42,7 @@ class Oracle : public rclcpp::Node { rclcpp_action::GoalResponse handle_action_goal( const rclcpp_action::GoalUUID &, - const std::shared_ptr goal) + const std::shared_ptr goal) { if (goal->action != "rite") { @@ -52,7 +52,7 @@ class Oracle : public rclcpp::Node { return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE; } - using GoalHandle = rclcpp_action::ServerGoalHandle; + using GoalHandle = rclcpp_action::ServerGoalHandle; rclcpp_action::CancelResponse handle_cancelled_action(const std::shared_ptr) { @@ -71,7 +71,7 @@ class Oracle : public rclcpp::Node { { if (handle->is_canceling()) { - auto result = std::make_shared(); + auto result = std::make_shared(); handle->canceled(result); action_loop_->cancel(); return; @@ -80,7 +80,7 @@ class Oracle : public rclcpp::Node { auto current_time = this->get_clock()->now(); if (current_time - action_start_time_ > handle->get_goal()->timeout) { - auto result = std::make_shared(); + auto result = std::make_shared(); result->reason = "timeout"; handle->abort(result); action_loop_->cancel(); @@ -89,20 +89,20 @@ class Oracle : public rclcpp::Node { if (is_rite_complete_(gen_)) { - auto result = std::make_shared(); + auto result = std::make_shared(); handle->succeed(result); action_loop_->cancel(); return; } - auto feedback = std::make_shared(); + auto feedback = std::make_shared(); feedback->message = "chanting"; handle->publish_feedback(feedback); } void handle_query( - const std::shared_ptr request, - std::shared_ptr response) const + const std::shared_ptr request, + std::shared_ptr response) const { if (request->query == "how's it going?") { response->reply = "all good!"; @@ -112,7 +112,7 @@ class Oracle : public rclcpp::Node { } void publish_status() { - apps_msgs::msg::Status msg; + drake_ros_apps_msgs::msg::Status msg; msg.stamp = this->get_clock()->now(); msg.status.sequence_id = sequence_id_++; msg.status.message = "OK"; @@ -128,17 +128,17 @@ class Oracle : public rclcpp::Node { rclcpp::Time action_start_time_; std::shared_ptr action_loop_; std::shared_ptr status_timer_; - std::shared_ptr> status_pub_; - std::shared_ptr> query_server_; - std::shared_ptr> action_server_; + std::shared_ptr> status_pub_; + std::shared_ptr> query_server_; + std::shared_ptr> action_server_; }; -} // namespace apps +} // namespace drake_ros_apps int main(int argc, char ** argv) { rclcpp::init(argc, argv); - rclcpp::spin(std::make_shared()); + rclcpp::spin(std::make_shared()); rclcpp::shutdown(); } diff --git a/drake_ros_bazel_installed/apps/oracle.py b/drake_ros_bazel_installed/drake_ros_apps/oracle.py similarity index 85% rename from drake_ros_bazel_installed/apps/oracle.py rename to drake_ros_bazel_installed/drake_ros_apps/oracle.py index 36d64393f..6b2b22645 100644 --- a/drake_ros_bazel_installed/apps/oracle.py +++ b/drake_ros_bazel_installed/drake_ros_apps/oracle.py @@ -8,9 +8,9 @@ import rclpy.node import rclpy.qos -import apps_msgs.msg -import common_msgs.action -import common_msgs.srv +import drake_ros_apps_msgs.msg +import drake_ros_common_msgs.action +import drake_ros_common_msgs.srv class Oracle(rclpy.node.Node): @@ -19,12 +19,12 @@ def __init__(self): super().__init__('oracle') self._sequence_id = 0 self._status_pub = self.create_publisher( - apps_msgs.msg.Status, 'status', + drake_ros_apps_msgs.msg.Status, 'status', rclpy.qos.QoSProfile(depth=1)) self._query_server = self.create_service( - common_msgs.srv.Query, 'query', self._handle_query) + drake_ros_common_msgs.srv.Query, 'query', self._handle_query) self._action_server = rclpy.action.ActionServer( - self, common_msgs.action.Do, 'do', + self, drake_ros_common_msgs.action.Do, 'do', execute_callback=self._handle_rite_action, goal_callback=self._handle_action_request, cancel_callback=self._handle_cancelled_action, @@ -42,7 +42,7 @@ def _handle_cancelled_action(self, handle): return rclpy.action.CancelResponse.ACCEPT def _handle_rite_action(self, handle): - result = common_msgs.action.Do.Result() + result = drake_ros_common_msgs.action.Do.Result() timeout = rclpy.duration.Duration.from_msg( handle.request.timeout) period = rclpy.duration.Duration.from_msg( @@ -62,7 +62,7 @@ def _handle_rite_action(self, handle): if bool(random.getrandbits(1)): handle.succeed() break - feedback = common_msgs.action.Do.Feedback() + feedback = drake_ros_common_msgs.action.Do.Feedback() feedback.message = 'chanting' handle.publish_feedback(feedback) rate.sleep() @@ -76,7 +76,7 @@ def _handle_query(self, request, response): return response def _publish_status(self): - msg = apps_msgs.msg.Status() + msg = drake_ros_apps_msgs.msg.Status() msg.status.sequence_id = self._sequence_id self._sequence_id = self._sequence_id + 1 msg.status.message = 'OK' diff --git a/drake_ros_bazel_installed/common/BUILD.bazel b/drake_ros_bazel_installed/drake_ros_common/BUILD.bazel similarity index 91% rename from drake_ros_bazel_installed/common/BUILD.bazel rename to drake_ros_bazel_installed/drake_ros_common/BUILD.bazel index 9494e611d..26677ec08 100644 --- a/drake_ros_bazel_installed/common/BUILD.bazel +++ b/drake_ros_bazel_installed/drake_ros_common/BUILD.bazel @@ -4,7 +4,7 @@ load("@ros2//:rosidl_tools.bzl", "rosidl_interfaces_group") rosidl_interfaces_group( - name = "common_msgs", + name = "drake_ros_common_msgs", interfaces = [ "msg/Status.msg", "srv/Query.srv", diff --git a/drake_ros_bazel_installed/common/README.md b/drake_ros_bazel_installed/drake_ros_common/README.md similarity index 68% rename from drake_ros_bazel_installed/common/README.md rename to drake_ros_bazel_installed/drake_ros_common/README.md index bfc747060..7d01e36fd 100644 --- a/drake_ros_bazel_installed/common/README.md +++ b/drake_ros_bazel_installed/drake_ros_common/README.md @@ -1,3 +1,3 @@ -## Common interfaces +## Common `drake_ros` interfaces This package exercises `rosidl` interface generation in C++ and Python. diff --git a/drake_ros_bazel_installed/common/action/Do.action b/drake_ros_bazel_installed/drake_ros_common/action/Do.action similarity index 100% rename from drake_ros_bazel_installed/common/action/Do.action rename to drake_ros_bazel_installed/drake_ros_common/action/Do.action diff --git a/drake_ros_bazel_installed/common/msg/Status.msg b/drake_ros_bazel_installed/drake_ros_common/msg/Status.msg similarity index 100% rename from drake_ros_bazel_installed/common/msg/Status.msg rename to drake_ros_bazel_installed/drake_ros_common/msg/Status.msg diff --git a/drake_ros_bazel_installed/common/srv/Query.srv b/drake_ros_bazel_installed/drake_ros_common/srv/Query.srv similarity index 100% rename from drake_ros_bazel_installed/common/srv/Query.srv rename to drake_ros_bazel_installed/drake_ros_common/srv/Query.srv From 2ead16b972d0ce43aecf4698413b0e74f6a2ac84 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 30 Sep 2021 15:33:55 -0300 Subject: [PATCH 040/107] Drop use_fastrtps_profile() macro Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/resources/bazel/common.bzl.tpl | 11 +++++------ drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl | 6 ------ 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl index 4901294ec..1ce33de5b 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl @@ -1,10 +1,5 @@ # -*- python -*- -load( - "@drake_ros//tools/skylark/ros2:rmw.bzl", - "use_fastrtps_profile", -) - def incorporate_rmw_implementation(kwargs, env_changes, rmw_implementation): target = "@REPOSITORY_ROOT@:%s_cc" % rmw_implementation kwargs["data"] = kwargs.get("data", []) + [target] @@ -16,6 +11,10 @@ def incorporate_rmw_implementation(kwargs, env_changes, rmw_implementation): def incorporate_fastrtps_profile(kwargs, env_changes, profile_name): kwargs["data"] = kwargs.get("data", []) + [profile_name] + profile_path = "{}/{}/{}".format( + native.repository_name(), native.package_name(), profile_name) env_changes = dict(env_changes) - env_changes.update(use_fastrtps_profile(profile_name)) + env_changes.update({ + "FASTRTPS_DEFAULT_PROFILES_FILE": ["path-replace", profile_path] + }) return kwargs, env_changes diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl index 4e3cfdf1d..e18d0a7bf 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl @@ -34,9 +34,3 @@ def generate_isolated_fastrtps_profile(name): tools = [tool], output_to_bindir = True, ) - -def use_fastrtps_profile(name): - path = "{}/{}/{}".format( - native.repository_name(), native.package_name(), name - ) - return {"FASTRTPS_DEFAULT_PROFILES_FILE": ["path-replace", path]} From 5192b552add9663521e8e098bf210f874502d52f Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 30 Sep 2021 15:49:58 -0300 Subject: [PATCH 041/107] Lint Python sources Signed-off-by: Michel Hidalgo --- .../skylark/ros2/cmake_tools/__init__.py | 1 + .../skylark/ros2/generate_repository_files.py | 25 +++++++++++-------- .../skylark/ros2/rmw_fastrtps_profile_gen.py | 24 +++++++++--------- .../tools/skylark/ros2/ros2bzl/sandboxing.py | 1 + .../ros2/ros2bzl/scrapping/ament_cmake.py | 11 ++++---- .../ros2/ros2bzl/scrapping/ament_python.py | 6 ++--- .../skylark/ros2/ros2bzl/scrapping/system.py | 1 + .../tools/skylark/ros2/ros2bzl/templates.py | 25 ++++++++++++++----- .../tools/skylark/ros2/ros2bzl/utilities.py | 4 +++ 9 files changed, 61 insertions(+), 37 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/__init__.py b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/__init__.py index 8cf624e5d..f8d6559c0 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/__init__.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/__init__.py @@ -5,6 +5,7 @@ from .server_mode import server_mode from .packages import get_packages_with_prefixes + def configure_file(src, dest, subs): with open(src, 'r') as f: text = f.read() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py index 3dde51b94..74623022a 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -12,10 +12,13 @@ sys.path.insert(0, os.path.dirname(__file__)) # noqa from ros2bzl.scrapping import load_distribution -from ros2bzl.scrapping.ament_cmake import collect_ament_cmake_package_properties -from ros2bzl.scrapping.ament_cmake import collect_ament_cmake_package_direct_properties +from ros2bzl.scrapping.ament_cmake \ + import collect_ament_cmake_package_properties +from ros2bzl.scrapping.ament_cmake \ + import collect_ament_cmake_package_direct_properties from ros2bzl.scrapping.ament_cmake import precache_ament_cmake_properties -from ros2bzl.scrapping.ament_python import collect_ament_python_package_direct_properties +from ros2bzl.scrapping.ament_python \ + import collect_ament_python_package_direct_properties from ros2bzl.scrapping.ament_python import PackageNotFoundError from ros2bzl.templates import configure_cc_tools @@ -139,13 +142,13 @@ def generate_build_file(repo_name, distro, cache, extras, sandbox): _, template, config = \ configure_package_share_filegroup(name, metadata, sandbox) fd.write(interpolate(template, config) + '\n') - + if 'rosidl_interface_packages' in metadata.get('groups', []): _, template, config = \ - configure_package_interfaces_filegroup(name, metadata, sandbox) + configure_package_interfaces_filegroup( + name, metadata, sandbox) fd.write(interpolate(template, config) + '\n') - if 'cmake' in metadata.get('build_type'): properties = collect_ament_cmake_package_direct_properties( name, metadata, dependencies, cache @@ -175,11 +178,13 @@ def generate_build_file(repo_name, distro, cache, extras, sandbox): metadata['langs'] = set() metadata['langs'].add('py') except PackageNotFoundError: - if any('py' in metadata.get('langs', []) for metadata in dependencies.values()): + if any('py' in metadata.get('langs', []) + for metadata in dependencies.values() + ): metadata['langs'].add('py (transitively)') # Dependencies still need to be propagated. - _, template, config = \ - configure_package_meta_py_library(name, metadata, dependencies) + _, template, config = configure_package_meta_py_library( + name, metadata, dependencies) fd.write(interpolate(template, config) + '\n') properties = {} @@ -224,7 +229,7 @@ def main(): generate_distro_file(distro) generate_common_file(args.repository_name) - + generate_cc_tools_file(args.repository_name) generate_py_tools_file(args.repository_name) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/rmw_fastrtps_profile_gen.py b/drake_ros_bazel_installed/tools/skylark/ros2/rmw_fastrtps_profile_gen.py index 8b4aae02b..956b63296 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/rmw_fastrtps_profile_gen.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/rmw_fastrtps_profile_gen.py @@ -65,18 +65,18 @@ def main(): builder.start('rtps') if args.metatraffic_multicast_locators or \ args.metatraffic_unicast_locators: - builder.start('builtin') - if args.metatraffic_multicast_locators: - builder.start('metatrafficMulticastLocatorList') - for locator in args.metatraffic_multicast_locators: - build_udpv4_locator(builder, locator) - builder.end('metatrafficMulticastLocatorList') - if args.metatraffic_unicast_locators: - builder.start('metatrafficUnicastLocatorList') - for locator in args.metatraffic_unicast_locators: - build_udpv4_locator(builder, locator) - builder.end('metatrafficUnicastLocatorList') - builder.end('builtin') + builder.start('builtin') + if args.metatraffic_multicast_locators: + builder.start('metatrafficMulticastLocatorList') + for locator in args.metatraffic_multicast_locators: + build_udpv4_locator(builder, locator) + builder.end('metatrafficMulticastLocatorList') + if args.metatraffic_unicast_locators: + builder.start('metatrafficUnicastLocatorList') + for locator in args.metatraffic_unicast_locators: + build_udpv4_locator(builder, locator) + builder.end('metatrafficUnicastLocatorList') + builder.end('builtin') if args.unicast_locators: builder.start('defaultUnicastLocatorList') for locator in args.unicast_locators: diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/sandboxing.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/sandboxing.py index 0468c02df..975d0a7f8 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/sandboxing.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/sandboxing.py @@ -1,5 +1,6 @@ import os + def configure(name, mapping): def sandbox(path, external=False): path = os.path.normpath(path) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py index 55ebbf74c..ccec2d2d3 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py @@ -108,7 +108,8 @@ def collect_ament_cmake_package_properties(name, metadata): # speed with TemporaryDirectory(dir=os.getcwd()) as project_path: project_name = 'empty_using_' + name - cmakelists_template_path = path_to_resource('cmake/ament_cmake_CMakeLists.txt.in') + cmakelists_template_path = path_to_resource( + 'cmake/ament_cmake_CMakeLists.txt.in') cmakelists_path = os.path.join(project_path, 'CMakeLists.txt') cmake_tools.configure_file(cmakelists_template_path, cmakelists_path, { '@NAME@': project_name, '@PACKAGE@': name @@ -172,8 +173,7 @@ def collect_ament_cmake_package_direct_properties(name, metadata, dependencies, if dependency_name not in ament_cmake_cache: ament_cmake_cache[dependency_name] = \ collect_ament_cmake_package_properties( - dependency_name, dependency_metadata - ) + dependency_name, dependency_metadata) dependency_properties = ament_cmake_cache[dependency_name] # Remove duplicates maintaining order. @@ -196,7 +196,8 @@ def collect_ament_cmake_package_direct_properties(name, metadata, dependencies, properties['include_directories'] = [ directory for directory in properties['include_directories'] if directory not in dependency_properties['include_directories'] - or os.path.exists(os.path.join(directory, name)) # leverage REP-122 + # leverage REP-122 + or os.path.exists(os.path.join(directory, name)) ] # Do not deduplicate link directories in case we're dealing with merge installs. @@ -210,7 +211,7 @@ def precache_ament_cmake_properties(packages, jobs=None): if metadata.get('build_type') == 'ament_cmake' } with Pool(jobs) as pool: - return dict(zip( + return dict(zip( ament_cmake_packages.keys(), pool.starmap( collect_ament_cmake_package_properties, ament_cmake_packages.items() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py index be93ff688..c52f4ea7e 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py @@ -59,8 +59,7 @@ def collect_ament_python_package_direct_properties(name, metadata, dependencies, if dependency_name not in ament_cmake_cache: ament_cmake_cache[dependency_name] = \ collect_ament_cmake_package_properties( - dependency_name, dependency_metadata - ) + dependency_name, dependency_metadata) dependency_properties = ament_cmake_cache[dependency_name] dependency_libraries.extend( dependency_properties['link_libraries'] @@ -69,8 +68,7 @@ def collect_ament_python_package_direct_properties(name, metadata, dependencies, if dependency_name not in ament_python_cache: ament_python_cache[dependency_name] = \ collect_ament_python_package_properties( - dependency_name, dependency_metadata - ) + dependency_name, dependency_metadata) dependency_properties = ament_python_cache[dependency_name] if 'cc_libraries' in dependency_properties: dependency_libraries.extend( diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/system.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/system.py index ec0129b47..5a06670fa 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/system.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/system.py @@ -5,6 +5,7 @@ DEFAULT_INCLUDE_DIRECTORIES = ['/usr/include', '/usr/local/include'] + def is_system_include(include_path): include_path = os.path.realpath(include_path) return any(include_path.startswith(path) for path in DEFAULT_INCLUDE_DIRECTORIES) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py index f386d7520..894aad49c 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py @@ -61,8 +61,11 @@ def configure_package_interfaces_filegroup(name, metadata, sandbox): def configure_package_cc_library(name, metadata, properties, dependencies, extras, sandbox): target_name = cc_name(name, metadata) libraries = [sandbox(library) for library in properties['link_libraries']] - include_directories = [sandbox(include) for include in properties['include_directories']] - local_includes = [include for include in include_directories if not os.path.isabs(include)] + include_directories = [ + sandbox(include) for include in properties['include_directories']] + local_includes = [ + include for include in include_directories + if not os.path.isabs(include)] headers = [] for include in local_includes: if not include.endswith(os.path.join(name, 'include')): @@ -71,7 +74,10 @@ def configure_package_cc_library(name, metadata, properties, dependencies, extra include = os.path.join(include, name) headers.append(include) # Push remaining nonlocal includes through compiler options - copts = ['-isystem ' + include for include in include_directories if os.path.isabs(include)] + copts = [ + '-isystem ' + include + for include in include_directories + if os.path.isabs(include)] copts.extend(properties['compile_flags']) defines = properties['defines'] @@ -143,11 +149,17 @@ def configure_package_meta_py_library(name, metadata, dependencies): def configure_package_py_library(name, metadata, properties, dependencies, extras, sandbox): target_name = py_name(name, metadata) eggs = [sandbox(egg_path) for egg_path, _ in properties['python_packages']] - tops = [sandbox(top_level) for _, top_level in properties['python_packages']] + tops = [ + sandbox(top_level) for _, top_level in properties['python_packages']] imports = [os.path.dirname(egg) for egg in eggs] template = 'bazel/snippets/package_py_library.bazel.tpl' - config = {'name': target_name, 'tops': tops, 'eggs': eggs, 'imports': imports} + config = { + 'name': target_name, + 'tops': tops, + 'eggs': eggs, + 'imports': imports + } deps = [] for dependency_name, dependency_metadata in dependencies.items(): @@ -235,7 +247,8 @@ def configure_executable_imports( if 'py' in dependency_metadata.get('langs', []): deps.append(py_label(dependency_name, dependency_metadata)) elif 'py (transitively)' in dependency_metadata.get('langs', []): - common_data.append(meta_py_label(dependency_name, dependency_metadata)) + common_data.append(meta_py_label( + dependency_name, dependency_metadata)) for executable in executables: target_name = os.path.basename(executable) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/utilities.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/utilities.py index 4f8eb11be..77d457f61 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/utilities.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/utilities.py @@ -1,6 +1,7 @@ import functools import json + class StarlarkEncoder(json.JSONEncoder): # Use JSON format as a basis def default(self, obj): @@ -8,6 +9,7 @@ def default(self, obj): return repr(obj) return super().default(obj) + def to_starlark_string_dict(d): encoder = StarlarkEncoder() return { @@ -15,12 +17,14 @@ def to_starlark_string_dict(d): for k, v in d.items() } + def interpolate(template, config): content = template for key, value in config.items(): content = content.replace('@{}@'.format(key), value) return content + def compose(f, g): @functools.wraps(f) def wrapped(head, *args, **kwargs): From 7381bb986ffe78b4e79a95f0603b3dc163c6e306 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 4 Oct 2021 15:19:10 -0300 Subject: [PATCH 042/107] Add support for CycloneDDS network isolation Signed-off-by: Michel Hidalgo --- .../tools/skylark/dload_cc.bzl | 19 ++++-- .../tools/skylark/dload_py.bzl | 2 + .../tools/skylark/ros2/BUILD.bazel | 10 ++- .../ros2/generate_cyclonedds_profile.py | 64 +++++++++++++++++++ ...le_gen.py => generate_fastrtps_profile.py} | 0 .../ros2/resources/bazel/cc_tools.bzl.tpl | 26 ++++++-- .../ros2/resources/bazel/common.bzl.tpl | 17 ++++- .../ros2/resources/bazel/py_tools.bzl.tpl | 28 ++++++-- .../tools/skylark/ros2/rmw.bzl | 26 +++++++- 9 files changed, 170 insertions(+), 22 deletions(-) create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/generate_cyclonedds_profile.py rename drake_ros_bazel_installed/tools/skylark/ros2/{rmw_fastrtps_profile_gen.py => generate_fastrtps_profile.py} (100%) diff --git a/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl b/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl index c1a663481..d57cf10d0 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl @@ -18,6 +18,7 @@ DLOAD_CC_SHIM_TEMPLATE = """\ #include #include +#include #include #include #include @@ -38,28 +39,34 @@ int main(int argc, const char * argv[]) {{ std::vector names = {names}; std::vector> actions = {actions}; // NOLINT for (size_t i = 0; i < names.size(); ++i) {{ - std::stringstream value; + std::stringstream value_stream; if (actions[i][0] == "replace") {{ assert(actions[i].size() == 2); - value << actions[i][1]; + value_stream << actions[i][1]; }} else if (actions[i][0] == "path-replace") {{ assert(actions[i].size() == 2); - value << runfiles->Rlocation(actions[i][1]); + value_stream << runfiles->Rlocation(actions[i][1]); }} else if (actions[i][0] == "path-prepend") {{ assert(actions[i].size() >= 2); for (size_t j = 1; j < actions[i].size(); ++j) {{ - value << runfiles->Rlocation(actions[i][j]) << ":"; + value_stream << runfiles->Rlocation(actions[i][j]) << ":"; }} const char * raw_value = getenv(names[i].c_str()); if (raw_value != nullptr) {{ - value << raw_value; + value_stream << raw_value; }} }} else {{ assert(false); // should never get here }} + std::string value = value_stream.str(); - if (setenv(names[i].c_str(), value.str().c_str(), 1) != 0) {{ + std::string::size_type location; + if ((location = value.find("$PWD")) != std::string::npos) {{ + value.replace(location, 4, std::filesystem::current_path()); + }} + + if (setenv(names[i].c_str(), value.c_str(), 1) != 0) {{ std::cerr << "ERROR: failed to set " << names[i] << std::endl; }} }} diff --git a/drake_ros_bazel_installed/tools/skylark/dload_py.bzl b/drake_ros_bazel_installed/tools/skylark/dload_py.bzl index 3df247c2c..d70849a57 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload_py.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload_py.bzl @@ -45,6 +45,8 @@ def main(argv): value += ':' + os.environ[name] else: assert False # should never get here + if '$PWD' in value: + value = value.replace('$PWD', os.getcwd()) os.environ[name] = value executable_path = r.Rlocation('{executable_path}') # noqa diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel index 191fe6f4c..892cf1e96 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel +++ b/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel @@ -2,7 +2,13 @@ # vi: set ft=python : py_binary( - name = "rmw_fastrtps_profile_gen", - srcs = ["rmw_fastrtps_profile_gen.py"], + name = "generate_fastrtps_profile", + srcs = ["generate_fastrtps_profile.py"], + visibility = ["//visibility:public"], +) + +py_binary( + name = "generate_cyclonedds_profile", + srcs = ["generate_cyclonedds_profile.py"], visibility = ["//visibility:public"], ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_cyclonedds_profile.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_cyclonedds_profile.py new file mode 100644 index 000000000..f4a346001 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_cyclonedds_profile.py @@ -0,0 +1,64 @@ +import argparse +import sys + +import xml.dom.minidom as minidom +import xml.etree.ElementTree as ET + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--discovery-multicast-address') + parser.add_argument('--discovery-multicast-port-offset') + parser.add_argument('--discovery-unicast-port-offset') + parser.add_argument('--user-multicast-port-offset') + parser.add_argument('--user-unicast-port-offset') + parser.add_argument('--domain-gain') + args = parser.parse_args() + + builder = ET.TreeBuilder() + xmlns = 'http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles' + schema_location = ( + 'https://cdds.io/config https://raw.githubusercontent.com' + '/eclipse-cyclonedds/cyclonedds/master/etc/cyclonedds.xsd') + builder.start('CycloneDDS', { + 'xmlns:xsi': xmlns, 'xsi:schemaLocation': schema_location}) + builder.start('Domain', {'id': 'any'}) + builder.start('Discovery') + if args.discovery_multicast_address: + builder.start('SPDPMulticastAddress') + builder.data(args.discovery_multicast_address) + builder.end('SPDPMulticastAddress') + builder.start('Ports') + if args.domain_gain: + builder.start('DomainGain') + builder.data(args.domain_gain) + builder.end('DomainGain') + if args.discovery_multicast_port_offset: + builder.start('MulticastMetaOffset') + builder.data(args.discovery_multicast_port_offset) + builder.end('MulticastMetaOffset') + if args.user_multicast_port_offset: + builder.start('MulticastDataOffset') + builder.data(args.user_multicast_port_offset) + builder.end('MulticastDataOffset') + if args.discovery_unicast_port_offset: + builder.start('UnicastMetaOffset') + builder.data(args.discovery_unicast_port_offset) + builder.end('UnicastMetaOffset') + if args.user_unicast_port_offset: + builder.start('UnicastDataOffset') + builder.data(args.user_unicast_port_offset) + builder.end('UnicastDataOffset') + builder.end('Ports') + builder.end('Discovery') + builder.end('Domain') + builder.end('CycloneDDS') + tree = builder.close() + inline_xml = ET.tostring(tree, encoding='utf-8') + dom = minidom.parseString(inline_xml) + pretty_xml = dom.toprettyxml(indent=' ' * 4, encoding='utf-8') + sys.stdout.buffer.write(pretty_xml) + + +if __name__ == '__main__': + main() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/rmw_fastrtps_profile_gen.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_fastrtps_profile.py similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/rmw_fastrtps_profile_gen.py rename to drake_ros_bazel_installed/tools/skylark/ros2/generate_fastrtps_profile.py diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/cc_tools.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/cc_tools.bzl.tpl index 9fe8091ff..125e15868 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/cc_tools.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/cc_tools.bzl.tpl @@ -6,8 +6,9 @@ load( ) load( "@REPOSITORY_ROOT@:common.bzl", + "incorporate_cyclonedds_profile", + "incorporate_fastrtps_profile", "incorporate_rmw_implementation", - "incorporate_fastrtps_profile" ) load( "@drake_ros//tools/skylark:dload_cc.bzl", @@ -19,6 +20,7 @@ load( ) load( "@drake_ros//tools/skylark/ros2:rmw.bzl", + "generate_isolated_cyclonedds_profile", "generate_isolated_fastrtps_profile", ) @@ -42,14 +44,21 @@ def ros_cc_binary(name, rmw_implementation = None, **kwargs): binary_kwargs, binary_env_changes, rmw_implementation = rmw_implementation ) - if "fastrtps" in rmw_implementation: - if "block-network" in kwargs.get("tags", []): + if "block-network" in kwargs.get("tags", []): + if "fastrtps" in rmw_implementation: profile_name = name + ".fastrtps_profile.xml" generate_isolated_fastrtps_profile(profile_name) binary_kwargs, binary_env_changes = \ incorporate_fastrtps_profile( binary_kwargs, binary_env_changes, profile_name ) + if "cyclonedds" in rmw_implementation: + profile_name = name + ".cyclonedds_profile.xml" + generate_isolated_cyclonedds_profile(profile_name) + binary_kwargs, binary_env_changes = \ + incorporate_cyclonedds_profile( + binary_kwargs, binary_env_changes, profile_name + ) native.cc_binary( name = binary_name, @@ -89,14 +98,21 @@ def ros_cc_test(name, rmw_implementation = None, **kwargs): test_kwargs, test_env_changes, rmw_implementation = rmw_implementation ) - if "fastrtps" in rmw_implementation: - if "block-network" in kwargs.get("tags", []): + if "block-network" in kwargs.get("tags", []): + if "fastrtps" in rmw_implementation: profile_name = name + ".fastrtps_profile.xml" generate_isolated_fastrtps_profile(profile_name) test_kwargs, test_env_changes = \ incorporate_fastrtps_profile( test_kwargs, test_env_changes, profile_name ) + if "cyclonedds" in rmw_implementation: + profile_name = name + ".cyclonedds_profile.xml" + generate_isolated_cyclonedds_profile(profile_name) + test_kwargs, test_env_changes = \ + incorporate_cyclonedds_profile( + test_kwargs, test_env_changes, profile_name + ) native.cc_test( name = test_name, diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl index 1ce33de5b..07012b7e7 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl @@ -11,10 +11,23 @@ def incorporate_rmw_implementation(kwargs, env_changes, rmw_implementation): def incorporate_fastrtps_profile(kwargs, env_changes, profile_name): kwargs["data"] = kwargs.get("data", []) + [profile_name] - profile_path = "{}/{}/{}".format( - native.repository_name(), native.package_name(), profile_name) + profile_path = "{}/{}".format(native.package_name(), profile_name) + if native.repository_name() != "@": + repository_name = native.repository_name().lstrip("Q") + profile_path = "{}/{}".format(repository_name, profile_path) env_changes = dict(env_changes) env_changes.update({ "FASTRTPS_DEFAULT_PROFILES_FILE": ["path-replace", profile_path] }) return kwargs, env_changes + +def incorporate_cyclonedds_profile(kwargs, env_changes, profile_name): + kwargs["data"] = kwargs.get("data", []) + [profile_name] + profile_path = "{}/{}".format(native.package_name(), profile_name) + if native.repository_name() != "@": + repository_name = native.repository_name().lstrip("Q") + profile_path = "{}/{}".format(repository_name, profile_path) + profile_uri = "file://$PWD/" + profile_path + env_changes = dict(env_changes) + env_changes.update({"CYCLONEDDS_URI": ["replace", profile_uri]}) + return kwargs, env_changes diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl index 5809a7637..f2694ebad 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl @@ -6,8 +6,9 @@ load( ) load( "@REPOSITORY_ROOT@:common.bzl", + "incorporate_cyclonedds_profile", + "incorporate_fastrtps_profile", "incorporate_rmw_implementation", - "incorporate_fastrtps_profile" ) load( "@drake_ros//tools/skylark:dload_py.bzl", @@ -19,6 +20,7 @@ load( ) load( "@drake_ros//tools/skylark/ros2:rmw.bzl", + "generate_isolated_cyclonedds_profile", "generate_isolated_fastrtps_profile", ) @@ -86,15 +88,22 @@ def ros_py_binary(name, rmw_implementation = None, **kwargs): binary_kwargs, binary_env_changes, rmw_implementation = rmw_implementation ) - if "fastrtps" in rmw_implementation: - if "block-network" in binary_kwargs.get("tags", []): + if "block-network" in kwargs.get("tags", []): + if "fastrtps" in rmw_implementation: profile_name = name + ".fastrtps_profile.xml" generate_isolated_fastrtps_profile(profile_name) binary_kwargs, binary_env_changes = \ incorporate_fastrtps_profile( binary_kwargs, binary_env_changes, profile_name ) - + if "cyclonedds" in rmw_implementation: + profile_name = name + ".cyclonedds_profile.xml" + generate_isolated_cyclonedds_profile(profile_name) + binary_kwargs, binary_env_changes = \ + incorporate_cyclonedds_profile( + binary_kwargs, binary_env_changes, profile_name + ) + native.py_binary( name = binary_name, **binary_kwargs @@ -139,14 +148,21 @@ def ros_py_test(name, rmw_implementation = None, **kwargs): test_kwargs, test_env_changes, rmw_implementation = rmw_implementation, ) - if "fastrtps" in rmw_implementation: - if "block-network" in test_kwargs.get("tags", []): + if "block-network" in kwargs.get("tags", []): + if "fastrtps" in rmw_implementation: profile_name = name + ".fastrtps_profile.xml" generate_isolated_fastrtps_profile(profile_name) test_kwargs, test_env_changes = \ incorporate_fastrtps_profile( test_kwargs, test_env_changes, profile_name ) + if "cyclonedds" in rmw_implementation: + profile_name = name + ".cyclonedds_profile.xml" + generate_isolated_cyclonedds_profile(profile_name) + test_kwargs, test_env_changes = \ + incorporate_cyclonedds_profile( + test_kwargs, test_env_changes, profile_name + ) native.py_test( name = test_name, diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl index e18d0a7bf..6071c3f96 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl @@ -11,7 +11,7 @@ def to_bitstrings(integer, *bitsizes): return reversed(bitstrings) def generate_isolated_fastrtps_profile(name): - tool = "//tools/skylark/ros2:rmw_fastrtps_profile_gen" + tool = "//tools/skylark/ros2:generate_fastrtps_profile" label_name = "{}//{}:{}".format( native.repository_name(), native.package_name(), name ) @@ -34,3 +34,27 @@ def generate_isolated_fastrtps_profile(name): tools = [tool], output_to_bindir = True, ) + +def generate_isolated_cyclonedds_profile(name): + tool = "//tools/skylark/ros2:generate_cyclonedds_profile" + label_name = "{}//{}:{}".format( + native.repository_name(), native.package_name(), name + ) + bitstrings = to_bitstrings(hash(label_name), 8, 8, 8, 10, 10, 10, 10) + args = [ + "--discovery-multicast-address {}".format( + ".".join(["239"] + [str(octet) for octet in bitstrings[:3]])), + "--discovery-multicast-port-offset {}".format(bitstrings[3]), + "--user-multicast-port-offset {}".format(bitstrings[4]), + "--discovery-unicast-port-offset {}".format(bitstrings[5]), + "--user-unicast-port-offset {}".format(bitstrings[6]), + "--domain-gain {}".format(0), # ignore domain IDs + ] + + native.genrule( + name = "gen_" + name, + outs = [name], + cmd = "./$(location {}) {} > $@".format(tool, " ".join(args)), + tools = [tool], + output_to_bindir = True, + ) From 7a87131f3939f5dcc58aaf6c7655ca5cd8d795d7 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 4 Oct 2021 15:54:53 -0300 Subject: [PATCH 043/107] Fix typo: replace Q by @ Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/resources/bazel/common.bzl.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl index 07012b7e7..ae24c4052 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl @@ -13,7 +13,7 @@ def incorporate_fastrtps_profile(kwargs, env_changes, profile_name): kwargs["data"] = kwargs.get("data", []) + [profile_name] profile_path = "{}/{}".format(native.package_name(), profile_name) if native.repository_name() != "@": - repository_name = native.repository_name().lstrip("Q") + repository_name = native.repository_name().lstrip("@") profile_path = "{}/{}".format(repository_name, profile_path) env_changes = dict(env_changes) env_changes.update({ @@ -25,7 +25,7 @@ def incorporate_cyclonedds_profile(kwargs, env_changes, profile_name): kwargs["data"] = kwargs.get("data", []) + [profile_name] profile_path = "{}/{}".format(native.package_name(), profile_name) if native.repository_name() != "@": - repository_name = native.repository_name().lstrip("Q") + repository_name = native.repository_name().lstrip("@") profile_path = "{}/{}".format(repository_name, profile_path) profile_uri = "file://$PWD/" + profile_path env_changes = dict(env_changes) From c74037c6bb74de3704685f1ef1a8da1ea977efd4 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 14 Oct 2021 12:18:30 -0300 Subject: [PATCH 044/107] Update keep_common() macro implementation Signed-off-by: Michel Hidalgo --- .../tools/skylark/kwargs.bzl | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/kwargs.bzl b/drake_ros_bazel_installed/tools/skylark/kwargs.bzl index 94424c504..3caa473e3 100644 --- a/drake_ros_bazel_installed/tools/skylark/kwargs.bzl +++ b/drake_ros_bazel_installed/tools/skylark/kwargs.bzl @@ -1,21 +1,19 @@ # -*- python -*- -DEFAULT_COMMON_KWARGS = { - "compatible_with": [], - "deprecation": None, - "exec_compatible_with": [], - "exec_properties": {}, - "features": [], - "restricted_to": None, - "tags": [], - "target_compatible_with": [], - "testonly": False, - "toolchains": [], - "visibility": [] -} +_COMMON_KWARGS = [ + "compatible_with", + "deprecation", + "exec_compatible_with", + "exec_properties", + "features", + "restricted_to", + "tags", + "target_compatible_with", + "testonly", + "toolchains", + "visibility", +] def keep_common(kwargs): - return { - key: kwargs.get(key, value) - for key, value in DEFAULT_COMMON_KWARGS.items() - } + """Fetch keyword arguments common to all rules from `kwargs`.""" + return {key: value for key, value in kwargs.items() if key in _COMMON_KWARGS} From 5fbb60e403beb2c21a092455741f6cba884bc4ed Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 14 Oct 2021 12:52:59 -0300 Subject: [PATCH 045/107] Document //tools/ros2:generate_repository_files.py script Signed-off-by: Michel Hidalgo --- .../skylark/ros2/generate_repository_files.py | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py index 74623022a..9a690c16b 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -1,5 +1,19 @@ #!/usr/bin/env python3 +""" +Scrap ROS 2 workspaces and expose its artifacts through a Bazel local repository. + + +This script generates: + +- a BUILD.bazel file, with targets for all C/C++ libraries, Python libraries, executables, and share + data files found in each scrapped ROS 2 package +- a ros_cc.bzl file, with rules for C/C++ binaries and tests that depend on ROS 2 C/C++ libraries +- a ros_py.bzl file, with rules for Python binaries and tests that depend on ROS 2 Python libraries +- a rosidl.bzl file, with rules for C++ and Python ROS 2 message generation including typesupports +- a distro.bzl file, with ROS 2 metadata as constants +""" + import argparse import collections import os @@ -9,6 +23,10 @@ import toposort +# NOTE: this script is typically invoked from the `ros2_local_repository()` +# repository rule. As repository rules are executed in Bazel's loading phase, +# `py_library()` cannot be relied on to make modules such as `ros2bzl` and +# `cmake_tools` reachable through PYTHONPATH. Thus, we force it here. sys.path.insert(0, os.path.dirname(__file__)) # noqa from ros2bzl.scrapping import load_distribution @@ -44,29 +62,36 @@ def parse_arguments(): - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter + ) parser.add_argument( 'repository_name', help='Bazel repository name' ) parser.add_argument( - '-s', '--sandbox', action='append', default=[], - help='Path mappings for sandboxing, in "outer:inner" format' + '-s', '--sandbox', action='append', + metavar='OUTER_PATH:INNER_PATH', default=[], + help='Path mappings for sandboxing' ) parser.add_argument( - '-i', '--include-package', action='append', dest='include_packages', default=[], + '-i', '--include-package', action='append', + dest='include_packages', metavar='PACKAGE_NAME', default=[], help='Packages to be included (plus their dependencies)' ) parser.add_argument( - '-e', '--exclude-package', action='append', dest='exclude_packages', - default=[], help='Packages to be explicitly excluded' + '-e', '--exclude-package', action='append', + dest='exclude_packages', metavar='PACKAGE_NAME', default=[], + help='Packages to be explicitly excluded' ) parser.add_argument( - '-x', '--extras', action='append', default=[], - help=('Additional dependencies for generated targets,' - ' in "label.attribute+=label_or_string" format') + '-x', '--extras', metavar='LABEL.ATTR+=(LABEL|STRING)', + action='append', default=[], + help=('Additional dependencies for generated targets') ) parser.add_argument( - '-j', '--jobs', type=int, default=None, help='Number of jobs to use' + '-j', '--jobs', metavar='N', type=int, default=None, + help='Number of jobs to use to scrap packages' ) args = parser.parse_args() From 7789fbd8311743e3c07bb941b4fce3d11a804228 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 14 Oct 2021 13:10:56 -0300 Subject: [PATCH 046/107] Rename *_tools.bzl files Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel | 7 +++---- drake_ros_bazel_installed/drake_ros_common/BUILD.bazel | 2 +- .../tools/skylark/ros2/generate_repository_files.py | 6 +++--- .../resources/bazel/{cc_tools.bzl.tpl => ros_cc.bzl.tpl} | 1 - .../resources/bazel/{py_tools.bzl.tpl => ros_py.bzl.tpl} | 0 .../bazel/{rosidl_tools.bzl.tpl => rosidl.bzl.tpl} | 0 .../ros2/resources/bazel/snippets/prologue.bazel.tpl | 2 +- drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl | 6 +++--- .../tools/skylark/ros2/ros2bzl/templates.py | 6 +++--- 9 files changed, 14 insertions(+), 16 deletions(-) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/{cc_tools.bzl.tpl => ros_cc.bzl.tpl} (99%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/{py_tools.bzl.tpl => ros_py.bzl.tpl} (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/{rosidl_tools.bzl.tpl => rosidl.bzl.tpl} (100%) diff --git a/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel b/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel index 145a5adb9..5b79a22d8 100644 --- a/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel +++ b/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel @@ -1,10 +1,9 @@ # -*- mode: python -*- # vi: set ft=python : -load("@ros2//:rosidl_tools.bzl", "rosidl_interfaces_group") -load("@ros2//:cc_tools.bzl", "ros_cc_binary") -load("@ros2//:py_tools.bzl", "ros_py_binary") - +load("@ros2//:ros_cc.bzl", "ros_cc_binary") +load("@ros2//:ros_py.bzl", "ros_py_binary") +load("@ros2//:rosidl.bzl", "rosidl_interfaces_group") rosidl_interfaces_group( name = "drake_ros_apps_msgs", diff --git a/drake_ros_bazel_installed/drake_ros_common/BUILD.bazel b/drake_ros_bazel_installed/drake_ros_common/BUILD.bazel index 26677ec08..806512497 100644 --- a/drake_ros_bazel_installed/drake_ros_common/BUILD.bazel +++ b/drake_ros_bazel_installed/drake_ros_common/BUILD.bazel @@ -1,7 +1,7 @@ # -*- mode: python -*- # vi: set ft=python : -load("@ros2//:rosidl_tools.bzl", "rosidl_interfaces_group") +load("@ros2//:rosidl.bzl", "rosidl_interfaces_group") rosidl_interfaces_group( name = "drake_ros_common_msgs", diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py index 9a690c16b..95dffedec 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -128,19 +128,19 @@ def generate_common_file(repo_name): def generate_cc_tools_file(repo_name): - with open('cc_tools.bzl', 'w') as fd: + with open('ros_cc.bzl', 'w') as fd: template, config = configure_cc_tools(repo_name) fd.write(interpolate(template, config) + '\n') def generate_py_tools_file(repo_name): - with open('py_tools.bzl', 'w') as fd: + with open('ros_py.bzl', 'w') as fd: template, config = configure_py_tools(repo_name) fd.write(interpolate(template, config) + '\n') def generate_rosidl_tools_file(repo_name): - with open('rosidl_tools.bzl', 'w') as fd: + with open('rosidl.bzl', 'w') as fd: template, config = configure_rosidl_tools(repo_name) fd.write(interpolate(template, config) + '\n') diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/cc_tools.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl similarity index 99% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/cc_tools.bzl.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl index 125e15868..d794074cf 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/cc_tools.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl @@ -24,7 +24,6 @@ load( "generate_isolated_fastrtps_profile", ) - def ros_cc_binary(name, rmw_implementation = None, **kwargs): """ Builds a C/C++ binary, injecting the runtime environment diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/py_tools.bzl.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl_tools.bzl.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/prologue.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/prologue.bazel.tpl index 6b4b38524..7ad78df37 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/prologue.bazel.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/prologue.bazel.tpl @@ -2,6 +2,6 @@ package(default_visibility = ["//visibility:public"]) -load("@REPOSITORY_ROOT@:py_tools.bzl", "ros_py_import") +load("@REPOSITORY_ROOT@:ros_py.bzl", "ros_py_import") load("@drake_ros//tools/skylark/ros2:common.bzl", "interfaces_filegroup") load("@drake_ros//tools/skylark/ros2:common.bzl", "share_filegroup") diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl index 695fb08ef..610047817 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -5,11 +5,11 @@ MANIFEST = [ "cmake_tools/server_mode.py", "cmake_tools/__init__.py", - "resources/bazel/cc_tools.bzl.tpl", "resources/bazel/common.bzl.tpl", "resources/bazel/distro.bzl.tpl", - "resources/bazel/py_tools.bzl.tpl", - "resources/bazel/rosidl_tools.bzl.tpl", + "resources/bazel/ros_cc.bzl.tpl", + "resources/bazel/ros_py.bzl.tpl", + "resources/bazel/rosidl.bzl.tpl", "resources/bazel/snippets/overlay_executable.bazel.tpl", "resources/bazel/snippets/package_interfaces_filegroup.bazel.tpl", "resources/bazel/snippets/package_cc_library.bazel.tpl", diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py index 894aad49c..1317529a0 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py @@ -286,19 +286,19 @@ def configure_prologue(repo_name): def configure_rosidl_tools(repo_name): - return load_resource('bazel/rosidl_tools.bzl.tpl'), { + return load_resource('bazel/rosidl.bzl.tpl'), { 'REPOSITORY_ROOT': '@{}//'.format(repo_name) } def configure_cc_tools(repo_name): - return load_resource('bazel/cc_tools.bzl.tpl'), { + return load_resource('bazel/ros_cc.bzl.tpl'), { 'REPOSITORY_ROOT': '@{}//'.format(repo_name) } def configure_py_tools(repo_name): - return load_resource('bazel/py_tools.bzl.tpl'), { + return load_resource('bazel/ros_py.bzl.tpl'), { 'REPOSITORY_ROOT': '@{}//'.format(repo_name) } From e113236d6f4fc1f37ac080115a5eb186f1936945 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 14 Oct 2021 14:10:04 -0300 Subject: [PATCH 047/107] Enable native rule overrides Signed-off-by: Michel Hidalgo --- .../ros2/resources/bazel/ros_cc.bzl.tpl | 18 +-- .../ros2/resources/bazel/ros_py.bzl.tpl | 22 ++-- .../ros2/resources/bazel/rosidl.bzl.tpl | 118 +++++++++++++----- 3 files changed, 113 insertions(+), 45 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl index d794074cf..7a2ac0ec3 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl @@ -24,7 +24,9 @@ load( "generate_isolated_fastrtps_profile", ) -def ros_cc_binary(name, rmw_implementation = None, **kwargs): +def ros_cc_binary( + name, rmw_implementation = None, cc_binary_rule = native.cc_binary, **kwargs +): """ Builds a C/C++ binary, injecting the runtime environment specific to this ROS overlay. @@ -59,7 +61,7 @@ def ros_cc_binary(name, rmw_implementation = None, **kwargs): binary_kwargs, binary_env_changes, profile_name ) - native.cc_binary( + cc_binary_rule( name = binary_name, **binary_kwargs ) @@ -79,9 +81,11 @@ def ros_cc_binary(name, rmw_implementation = None, **kwargs): deps = ["@bazel_tools//tools/cpp/runfiles"], tags = ["nolint"] + kwargs.get("tags", []) ) - native.cc_binary(name = name, **kwargs) - -def ros_cc_test(name, rmw_implementation = None, **kwargs): + cc_binary_rule(name = name, **kwargs) + +def ros_cc_test( + name, rmw_implementation = None, cc_test_rule = native.cc_test, **kwargs +): """ Builds a C/C++ test, injecting the runtime environment specific to this ROS overlay. @@ -113,7 +117,7 @@ def ros_cc_test(name, rmw_implementation = None, **kwargs): test_kwargs, test_env_changes, profile_name ) - native.cc_test( + cc_test_rule( name = test_name, **test_kwargs ) @@ -134,4 +138,4 @@ def ros_cc_test(name, rmw_implementation = None, **kwargs): deps = ["@bazel_tools//tools/cpp/runfiles"], tags = ["nolint"] + kwargs.get("tags", []) ) - native.cc_test(name = name, **kwargs) + cc_test_rule(name = name, **kwargs) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl index f2694ebad..043e1a871 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl @@ -24,7 +24,9 @@ load( "generate_isolated_fastrtps_profile", ) -def ros_py_import(name, executable, rmw_implementation = None, **kwargs): +def ros_py_import( + name, executable, rmw_implementation = None, py_binary_rule = native.py_binary, **kwargs +): """ Imports an executable, injecting the runtime environment specific to this ROS overlay. @@ -68,9 +70,11 @@ def ros_py_import(name, executable, rmw_implementation = None, **kwargs): deps = kwargs.get("deps", []) + [ "@bazel_tools//tools/python/runfiles"] ) - native.py_binary(name = name, **kwargs) + py_binary_rule(name = name, **kwargs) -def ros_py_binary(name, rmw_implementation = None, **kwargs): +def ros_py_binary( + name, rmw_implementation = None, py_binary_rule = native.py_binary, **kwargs +): """ Builds a Python binary, injecting the runtime environment specific to this ROS overlay. @@ -104,7 +108,7 @@ def ros_py_binary(name, rmw_implementation = None, **kwargs): binary_kwargs, binary_env_changes, profile_name ) - native.py_binary( + py_binary_rule( name = binary_name, **binary_kwargs ) @@ -128,9 +132,11 @@ def ros_py_binary(name, rmw_implementation = None, **kwargs): ], tags = ["nolint"] + kwargs.get("tags", []) ) - native.py_binary(name = name, **kwargs) + py_binary_rule(name = name, **kwargs) -def ros_py_test(name, rmw_implementation = None, **kwargs): +def ros_py_test( + name, rmw_implementation = None, py_test_rule = native.py_test, **kwargs +): """ Builds a Python test, injecting the runtime environment specific to this ROS overlay. @@ -164,7 +170,7 @@ def ros_py_test(name, rmw_implementation = None, **kwargs): test_kwargs, test_env_changes, profile_name ) - native.py_test( + py_test_rule( name = test_name, **test_kwargs ) @@ -186,4 +192,4 @@ def ros_py_test(name, rmw_implementation = None, **kwargs): deps = ["@bazel_tools//tools/python/runfiles"], tags = ["nolint"] + kwargs.get("tags", []) ) - native.py_test(name = name, **kwargs) + py_test_rule(name = name, **kwargs) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl index a037624c4..e690c98c7 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl @@ -131,7 +131,8 @@ def _generated_source_paths(group, kind): return base, root def rosidl_c_library( - name, group, interfaces, includes = [], deps = [], **kwargs + name, group, interfaces, includes = [], deps = [], + cc_library_rule = native.cc_library, **kwargs ): include, root = _generated_source_paths(group, "c") @@ -164,7 +165,7 @@ def rosidl_c_library( "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", ] - native.cc_library( + cc_library_rule( name = name, srcs = generated_c_sources, hdrs = generated_c_headers, @@ -174,7 +175,8 @@ def rosidl_c_library( ) def rosidl_cc_library( - name, group, interfaces, includes = [], deps = [], **kwargs + name, group, interfaces, includes = [], deps = [], + cc_library_rule = native.cc_library, **kwargs ): include, root = _generated_source_paths(group, "cpp") @@ -197,7 +199,7 @@ def rosidl_cc_library( **kwargs ) - native.cc_library( + cc_library_rule( name = name, hdrs = generated_cc_headers, includes = [include], @@ -216,6 +218,9 @@ def _c_typesupport_extension_label(group, typesupport_name): def rosidl_py_library( name, group, interfaces, typesupports, includes = [], c_deps = [], py_deps = [], + cc_binary_rule = native.cc_binary, + cc_library_rule = native.cc_library, + py_library_rule = native.py_library, **kwargs ): import_, root = _generated_source_paths(group, "py") @@ -253,7 +258,7 @@ def rosidl_py_library( **kwargs ) - native.cc_library( + cc_library_rule( name = c(name), srcs = generated_c_sources, deps = c_deps + [ @@ -277,14 +282,14 @@ def rosidl_py_library( deps.append(typesupport_library) c_typesupport_extension = "{}/{}".format( root, _c_typesupport_extension(group, typesupport_name)) - native.cc_binary( + cc_binary_rule( name = c_typesupport_extension, srcs = generated_c_sources_per_typesupport[typesupport_name], deps = deps, linkshared = True, **kwargs ) py_data.append(c_typesupport_extension) - native.py_library( + py_library_rule( name = name, srcs = generated_py_sources, imports = [import_], @@ -294,7 +299,9 @@ def rosidl_py_library( ) def rosidl_typesupport_fastrtps_cc_library( - name, group, interfaces, includes = [], deps = [], **kwargs + name, group, interfaces, includes = [], deps = [], + cc_binary_rule = native.cc_binary, cc_library_rule = native.cc_library, + **kwargs ): include, root = _generated_source_paths(group, "typesupport/fastrtps_cpp") @@ -320,7 +327,7 @@ def rosidl_typesupport_fastrtps_cc_library( **kwargs ) - native.cc_library( + cc_library_rule( name = name + "_hdrs", hdrs = generated_cc_headers, includes = [include], @@ -328,7 +335,7 @@ def rosidl_typesupport_fastrtps_cc_library( **kwargs ) - native.cc_binary( + cc_binary_rule( name = name, srcs = generated_cc_sources, deps = deps + [ @@ -344,7 +351,9 @@ def rosidl_typesupport_fastrtps_cc_library( ) def rosidl_typesupport_fastrtps_c_library( - name, group, interfaces, includes = [], deps = [], **kwargs + name, group, interfaces, includes = [], deps = [], + cc_binary_rule = native.cc_binary, cc_library_rule = native.cc_library, + **kwargs ): include, root = _generated_source_paths(group, "typesupport/fastrtps_c") @@ -370,7 +379,7 @@ def rosidl_typesupport_fastrtps_c_library( **kwargs ) - native.cc_library( + cc_library_rule( name = name + "_hdrs", hdrs = generated_c_headers, includes = [include], @@ -378,7 +387,7 @@ def rosidl_typesupport_fastrtps_c_library( **kwargs ) - native.cc_binary( + cc_binary_rule( name = name, srcs = generated_c_sources, linkshared = True, @@ -395,7 +404,9 @@ def rosidl_typesupport_fastrtps_c_library( ) def rosidl_typesupport_introspection_c_library( - name, group, interfaces, includes = [], deps = [], **kwargs + name, group, interfaces, includes = [], deps = [], + cc_binary_rule = native.cc_binary, cc_library_rule = native.cc_library, + **kwargs ): include, root = _generated_source_paths(group, "typesupport/introspection_c") @@ -421,7 +432,7 @@ def rosidl_typesupport_introspection_c_library( **kwargs ) - native.cc_library( + cc_library_rule( name = name + "_hdrs", hdrs = generated_c_headers, includes = [include], @@ -429,7 +440,7 @@ def rosidl_typesupport_introspection_c_library( **kwargs ) - native.cc_binary( + cc_binary_rule( name = name, srcs = generated_c_sources, linkshared = True, @@ -441,7 +452,9 @@ def rosidl_typesupport_introspection_c_library( ) def rosidl_typesupport_introspection_cc_library( - name, group, interfaces, includes = [], deps = [], **kwargs + name, group, interfaces, includes = [], deps = [], + cc_binary_rule = native.cc_binary, cc_library_rule = native.cc_library, + **kwargs ): include, root = _generated_source_paths(group, "typesupport/introspection_cpp") @@ -466,7 +479,7 @@ def rosidl_typesupport_introspection_cc_library( **kwargs ) - native.cc_library( + cc_library_rule( name = name + "_hdrs", hdrs = generated_cc_headers, includes = [include], @@ -474,7 +487,7 @@ def rosidl_typesupport_introspection_cc_library( **kwargs ) - native.cc_binary( + cc_binary_rule( name = name, srcs = generated_cc_sources, linkshared = True, @@ -489,7 +502,8 @@ def rosidl_typesupport_introspection_cc_library( ) def rosidl_typesupport_c_library( - name, group, interfaces, typesupports, includes = [], deps = [], **kwargs + name, group, interfaces, typesupports, includes = [], + deps = [], cc_binary_rule = native.cc_binary, **kwargs ): include, root = _generated_source_paths(group, "typesupport/c") @@ -516,7 +530,7 @@ def rosidl_typesupport_c_library( **kwargs ) - native.cc_binary( + cc_binary_rule( name = name, srcs = generated_sources, includes = [include], @@ -531,7 +545,8 @@ def rosidl_typesupport_c_library( ) def rosidl_typesupport_cc_library( - name, group, interfaces, typesupports, includes = [], deps = [], **kwargs + name, group, interfaces, typesupports, includes = [], + deps = [], cc_binary_rule = native.cc_binary, **kwargs ): include, root = _generated_source_paths(group, "typesupport/cpp") @@ -555,7 +570,7 @@ def rosidl_typesupport_cc_library( **kwargs ) - native.cc_binary( + cc_binary_rule( name = name, srcs = generated_cc_sources, data = typesupports.values(), @@ -603,13 +618,19 @@ def typesupport_fastrtps_cc(name): def typesupport_fastrtps_cc_label(name): return ":" + typesupport_fastrtps_cc(name) -def rosidl_cc_support(name, interfaces, deps, group = None, **kwargs): +def rosidl_cc_support( + name, interfaces, deps, group = None, + cc_binary_rule = native.cc_binary, + cc_library_rule = native.cc_library, + **kwargs +): rosidl_cc_library( name = cc_types(name), group = group or name, interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [cc(dep) for dep in deps], + cc_library_rule = cc_library_rule, **kwargs ) @@ -622,6 +643,8 @@ def rosidl_cc_support(name, interfaces, deps, group = None, **kwargs): interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [cc_types_label(name)] + [cc(dep) for dep in deps], + cc_binary_rule = cc_binary_rule, + cc_library_rule = cc_library_rule, **kwargs ) typesupports["rosidl_typesupport_introspection_cpp"] = \ @@ -634,6 +657,8 @@ def rosidl_cc_support(name, interfaces, deps, group = None, **kwargs): interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [cc_types_label(name)] + [cc(dep) for dep in deps], + cc_binary_rule = cc_binary_rule, + cc_library_rule = cc_library_rule, **kwargs ) typesupports["rosidl_typesupport_fastrtps_cpp"] = \ @@ -646,10 +671,11 @@ def rosidl_cc_support(name, interfaces, deps, group = None, **kwargs): interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [cc_types_label(name)] + [cc(dep) for dep in deps], + cc_binary_rule = cc_binary_rule, **kwargs ) - native.cc_library( + cc_library_rule( name = cc(name), srcs = [ typesupport_cc_label(name) @@ -695,13 +721,20 @@ def typesupport_fastrtps_c(name): def typesupport_fastrtps_c_label(name): return ":" + typesupport_fastrtps_c(name) -def rosidl_py_support(name, interfaces, deps, group = None, **kwargs): +def rosidl_py_support( + name, interfaces, deps, group = None, + cc_binary_rule = native.cc_binary, + cc_library_rule = native.cc_library, + py_library_rule = native.py_library, + **kwargs +): rosidl_c_library( name = c_types(name), group = group or name, interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [c(dep) for dep in deps], + cc_library_rule = cc_library_rule, **kwargs ) @@ -714,6 +747,8 @@ def rosidl_py_support(name, interfaces, deps, group = None, **kwargs): interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [c_types_label(name)] + [c(dep) for dep in deps], + cc_binary_rule = cc_binary_rule, + cc_library_rule = cc_library_rule, **kwargs ) typesupports["rosidl_typesupport_introspection_c"] = \ @@ -726,6 +761,8 @@ def rosidl_py_support(name, interfaces, deps, group = None, **kwargs): interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [c_types_label(name)] + [c(dep) for dep in deps], + cc_binary_rule = cc_binary_rule, + cc_library_rule = cc_library_rule, **kwargs ) typesupports["rosidl_typesupport_fastrtps_c"] = \ @@ -738,11 +775,12 @@ def rosidl_py_support(name, interfaces, deps, group = None, **kwargs): interfaces = interfaces, includes = [defs(dep) for dep in deps], deps = [c_types_label(name)] + [c(dep) for dep in deps], + cc_binary_rule = cc_binary_rule, **kwargs ) typesupports["rosidl_typesupport_c"] = typesupport_c_label(name) - native.cc_library( + cc_library_rule( name = c(name), srcs = typesupports.values(), deps = [ @@ -760,10 +798,19 @@ def rosidl_py_support(name, interfaces, deps, group = None, **kwargs): includes = [defs(dep) for dep in deps], py_deps = [py(dep) for dep in deps], c_deps = [c_label(name)] + [c(dep) for dep in deps], + cc_binary_rule = cc_binary_rule, + cc_library_rule = cc_library_rule, + py_library_rule = py_library_rule, **kwargs ) -def rosidl_interfaces_group(name, interfaces, deps, group = None, **kwargs): +def rosidl_interfaces_group( + name, interfaces, deps, group = None, + cc_binary_rule = native.cc_binary, + cc_library_rule = native.cc_library, + py_library_rule = native.py_library, + **kwargs +): rosidl_definitions_filegroup( name = defs(name), group = group or name, @@ -772,6 +819,17 @@ def rosidl_interfaces_group(name, interfaces, deps, group = None, **kwargs): **kwargs ) - rosidl_cc_support(name, interfaces, deps, group, **kwargs) + rosidl_cc_support( + name, interfaces, deps, group, + cc_binary_rule = cc_binary_rule, + cc_library_rule = cc_library_rule, + **kwargs + ) - rosidl_py_support(name, interfaces, deps, group, **kwargs) + rosidl_py_support( + name, interfaces, deps, group, + cc_binary_rule = cc_binary_rule, + cc_library_rule = cc_library_rule, + py_library_rule = py_library_rule, + **kwargs + ) From dc2280c1d3dcef064147015c2ef271c96363102f Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 14 Oct 2021 14:18:41 -0300 Subject: [PATCH 048/107] Rename AVAILABLE_TYPESUPPORTS to AVAILABLE_TYPESUPPORT_LIST Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/resources/bazel/distro.bzl.tpl | 2 +- .../tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl | 10 +++++----- .../tools/skylark/ros2/ros2bzl/templates.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/distro.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/distro.bzl.tpl index 3f451535f..69b538214 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/distro.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/distro.bzl.tpl @@ -1,4 +1,4 @@ -AVAILABLE_TYPESUPPORTS = @AVAILABLE_TYPESUPPORTS@ +AVAILABLE_TYPESUPPORT_LIST = @AVAILABLE_TYPESUPPORT_LIST@ # Prepend full paths to not break workspace overlays RUNTIME_ENVIRONMENT = { diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl index e690c98c7..291cbe238 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl @@ -1,6 +1,6 @@ # -*- python -*- -load("@REPOSITORY_ROOT@:distro.bzl", "AVAILABLE_TYPESUPPORTS") +load("@REPOSITORY_ROOT@:distro.bzl", "AVAILABLE_TYPESUPPORT_LIST") load("@python_dev//:version.bzl", "PYTHON_EXTENSION_SUFFIX") def _as_idl_tuple(file): @@ -636,7 +636,7 @@ def rosidl_cc_support( typesupports = {} - if "rosidl_typesupport_introspection_cpp" in AVAILABLE_TYPESUPPORTS: + if "rosidl_typesupport_introspection_cpp" in AVAILABLE_TYPESUPPORT_LIST: rosidl_typesupport_introspection_cc_library( name = typesupport_introspection_cc(name), group = group or name, @@ -650,7 +650,7 @@ def rosidl_cc_support( typesupports["rosidl_typesupport_introspection_cpp"] = \ typesupport_introspection_cc_label(name) - if "rosidl_typesupport_fastrtps_cpp" in AVAILABLE_TYPESUPPORTS: + if "rosidl_typesupport_fastrtps_cpp" in AVAILABLE_TYPESUPPORT_LIST: rosidl_typesupport_fastrtps_cc_library( name = typesupport_fastrtps_cc(name), group = group or name, @@ -740,7 +740,7 @@ def rosidl_py_support( typesupports = {} - if "rosidl_typesupport_introspection_c" in AVAILABLE_TYPESUPPORTS: + if "rosidl_typesupport_introspection_c" in AVAILABLE_TYPESUPPORT_LIST: rosidl_typesupport_introspection_c_library( name = typesupport_introspection_c(name), group = group or name, @@ -754,7 +754,7 @@ def rosidl_py_support( typesupports["rosidl_typesupport_introspection_c"] = \ typesupport_introspection_c_label(name) - if "rosidl_typesupport_fastrtps_c" in AVAILABLE_TYPESUPPORTS: + if "rosidl_typesupport_fastrtps_c" in AVAILABLE_TYPESUPPORT_LIST: rosidl_typesupport_fastrtps_c_library( name = typesupport_fastrtps_c(name), group = group or name, diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py index 1317529a0..e9dfeccc9 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py @@ -317,7 +317,7 @@ def configure_distro(distro): return load_resource('bazel/distro.bzl.tpl'), to_starlark_string_dict({ 'AMENT_PREFIX_PATH': distro['paths']['ament_prefix'], 'LOAD_PATH': distro['paths']['library_load'], # Linux only - 'AVAILABLE_TYPESUPPORTS': [ + 'AVAILABLE_TYPESUPPORT_LIST': [ name for name, metadata in distro['packages'].items() if any( group in typesupport_groups for group in metadata.get('groups', []) From a79e41f21313ee9dce8bc42357f421771889627f Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 14 Oct 2021 16:01:14 -0300 Subject: [PATCH 049/107] Document ROS 2 C++, Python, and rosidl rules Signed-off-by: Michel Hidalgo --- .../ros2/resources/bazel/ros_cc.bzl.tpl | 28 +- .../ros2/resources/bazel/ros_py.bzl.tpl | 42 ++- .../ros2/resources/bazel/rosidl.bzl.tpl | 320 +++++++++++++++--- 3 files changed, 336 insertions(+), 54 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl index 7a2ac0ec3..108c1abb8 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl @@ -28,8 +28,18 @@ def ros_cc_binary( name, rmw_implementation = None, cc_binary_rule = native.cc_binary, **kwargs ): """ - Builds a C/C++ binary, injecting the runtime environment - specific to this ROS overlay. + Builds a C/C++ binary and wraps it with a shim that will inject the minimal + runtime environment necessary for execution when depending on targets from + this ROS 2 local repository. + + Equivalent to the cc_binary() rule, which this rule decorates. + + Args: + name: C/C++ binary target name + rmw_implementation: optional RMW implementation to run against + cc_binary_rule: optional cc_binary() rule override + + Additional keyword arguments are forwarded to the `cc_binary_rule`. """ if kwargs.get("linkshared", False): native.cc_binary(name = name, **kwargs) @@ -87,10 +97,18 @@ def ros_cc_test( name, rmw_implementation = None, cc_test_rule = native.cc_test, **kwargs ): """ - Builds a C/C++ test, injecting the runtime environment - specific to this ROS overlay. + Builds a C/C++ test and wraps it with a shim that will inject the minimal + runtime environment necessary for execution when depending on targets from + this ROS 2 local repository. + + Equivalent to the cc_test() rule, which this rule decorates. + + Args: + name: C/C++ test target name + rmw_implementation: optional RMW implementation to run against + cc_test_rule: optional cc_test() rule override - Equivalent to the cc_test() rule. + Additional keyword arguments are forwarded to the `cc_test_rule`. """ test_name = "_" + name test_env_changes = dict(RUNTIME_ENVIRONMENT) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl index 043e1a871..22266a1c5 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl @@ -28,13 +28,19 @@ def ros_py_import( name, executable, rmw_implementation = None, py_binary_rule = native.py_binary, **kwargs ): """ - Imports an executable, injecting the runtime environment - specific to this ROS overlay. + Imports an existing, potentially pre-built executable by wrapping it with a shim that + will inject the minimal runtime environment necessary for execution when depending on + this ROS 2 local repository. + + Akin to the cc_import() rule. Args: - executable: executable file + name: imported executable target name + executable: path to an executable file + rmw_implementation: optional RMW implementation to run against + py_binary_rule: optional py_binary() rule override - Akin to the cc_import() rule. + Additional keyword arguments are forwarded to the `py_binary_rule`. """ env_changes = dict(RUNTIME_ENVIRONMENT) @@ -76,8 +82,18 @@ def ros_py_binary( name, rmw_implementation = None, py_binary_rule = native.py_binary, **kwargs ): """ - Builds a Python binary, injecting the runtime environment - specific to this ROS overlay. + Builds a Python binary and wraps it with a shim that will inject the minimal + runtime environment necessary for execution when depending on targets from + this ROS 2 local repository. + + Equivalent to the py_binary() rule, which this rule decorates. + + Args: + name: Python binary target name + rmw_implementation: optional RMW implementation to run against + py_binary_rule: optional py_binary() rule override + + Additional keyword arguments are forwarded to the `py_binary_rule`. """ binary_name = "_" + name @@ -138,10 +154,18 @@ def ros_py_test( name, rmw_implementation = None, py_test_rule = native.py_test, **kwargs ): """ - Builds a Python test, injecting the runtime environment - specific to this ROS overlay. + Builds a Python test and wraps it with a shim that will inject the minimal + runtime environment necessary for execution when depending on targets from + this ROS 2 local repository. + + Equivalent to the py_test() rule, which this rule decorates. + + Args: + name: Python test target name + rmw_implementation: optional RMW implementation to run against + py_test_rule: optional py_test() rule override - Equivalent to the py_test() rule. + Additional keyword arguments are forwarded to the `py_test_rule`. """ test_name = "_" + name test_kwargs = dict(kwargs) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl index 291cbe238..c6f12e7c9 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl @@ -4,12 +4,18 @@ load("@REPOSITORY_ROOT@:distro.bzl", "AVAILABLE_TYPESUPPORT_LIST") load("@python_dev//:version.bzl", "PYTHON_EXTENSION_SUFFIX") def _as_idl_tuple(file): + """ + Returns IDL tuple for a file as expected by `rosidl` commands. + """ path, parent, base = file.path.rsplit("/", 2) if parent not in ("msg", "srv", "action"): fail("Interface parent folder must be one of: 'msg', 'srv', 'action'") return "{}:{}/{}".format(path, parent, base) def _as_include_flag(file): + """ + Returns path to file as an include flag for `rosidl` commands. + """ return "-I" + file.path.rsplit("/", 3)[0] def _rosidl_generate_genrule_impl(ctx): @@ -26,7 +32,7 @@ def _rosidl_generate_genrule_impl(ctx): for typesupport in ctx.attr.typesupports: args.add("--type-support", typesupport) args.add_all(ctx.files.includes, map_each=_as_include_flag, uniquify=True) - args.add(ctx.attr.package) + args.add(ctx.attr.group) args.add_all(ctx.files.interfaces, map_each=_as_idl_tuple) inputs, input_manifests = ctx.resolve_tools(tools = [ctx.attr._tool]) inputs = inputs.to_list() + ctx.files.interfaces + ctx.files.includes @@ -42,7 +48,7 @@ rosidl_generate_genrule = rule( generated_sources = attr.output_list(mandatory = True), types = attr.string_list(mandatory = False), typesupports = attr.string_list(mandatory = False), - package = attr.string(mandatory = True), + group = attr.string(mandatory = True), interfaces = attr.label_list( mandatory = True, allow_files = True ), @@ -56,6 +62,20 @@ rosidl_generate_genrule = rule( implementation = _rosidl_generate_genrule_impl, output_to_genfiles = True, ) +""" +Generates ROS 2 interface type representation and/or type support sources. + +Args: + generated_sources: expected sources after generation. + types: list of type representations to generate (e.g. cpp, py). + typesupports: list of type supports to generate (e.g. typesupport_cpp). + group: interface group name (i.e. ROS 2 package name). + interfaces: interface definition files, both files and filegroups are allowed. + includes: optional interface definition includes, both files and filegroups are allowed. + output_dir: optional output subdirectory. + +See `rosidl generate` CLI for further reference. +""" def _rosidl_translate_genrule_impl(ctx): args = ctx.actions.args() @@ -70,7 +90,7 @@ def _rosidl_translate_genrule_impl(ctx): if ctx.attr.input_format: args.add("--input-format", ctx.attr.input_format) args.add_all(ctx.files.includes, map_each=_as_include_flag, uniquify=True) - args.add(ctx.attr.package) + args.add(ctx.attr.group) args.add_all(ctx.files.interfaces, map_each=_as_idl_tuple) inputs, input_manifests = ctx.resolve_tools(tools = [ctx.attr._tool]) inputs = inputs.to_list() + ctx.files.interfaces + ctx.files.includes @@ -86,7 +106,7 @@ rosidl_translate_genrule = rule( translated_interfaces = attr.output_list(mandatory = True), output_format = attr.string(mandatory = True), input_format = attr.string(mandatory = False), - package = attr.string(mandatory = True), + group = attr.string(mandatory = True), interfaces = attr.label_list( mandatory = True, allow_files = True ), @@ -100,9 +120,26 @@ rosidl_translate_genrule = rule( implementation = _rosidl_translate_genrule_impl, output_to_genfiles = True, ) - -def _extract_interface_parts(path): - parent, _, base = path.rpartition("/") +""" +Translates ROS 2 interface definition files. + +Args: + translated_interfaces: execpted interface definition files after translation. + output_format: output format to translate interface definition files to. + input_format: optional input format, deduced from file extensions by default. + group: interface group name (i.e. ROS 2 package name). + interfaces: interface definition files, both files and filegroups are allowed. + includes: optional interface definition includes, both files and filegroups are allowed. + output_dir: optional output subdirectory. + +See `rosidl translate` CLI for further reference. +""" + +def _deduce_source_parts(interface_path): + """ + Deduces source subdirectory and basename for a given path to an interface definition file. + """ + parent, _, base = interface_path.rpartition("/") basename, _, ext = base.rpartition(".") basename = basename[0].lower() + "".join([ "_" + char.lower() if char.isupper() else char @@ -111,6 +148,19 @@ def _extract_interface_parts(path): return parent, basename def rosidl_definitions_filegroup(name, group, interfaces, includes, **kwargs): + """ + Generates ROS 2 interfaces .idl definitions. + + This rule standardizes all interface definitions' format to IDL. + + Args: + name: filegroup target name. + group: interface group name (i.e. ROS 2 package name). + interfaces: interface definition files, both files and filegroups are allowed + includes: optional interface definition includes, both files and filegroups are allowed + + Additional keyword arguments are those common to all rules. + """ translated_interfaces = [] for ifc in interfaces: base, _, ext = ifc.rpartition(".") @@ -119,28 +169,44 @@ def rosidl_definitions_filegroup(name, group, interfaces, includes, **kwargs): name = name, output_format = "idl", translated_interfaces = translated_interfaces, - package = group, + group = group, interfaces = interfaces, includes = includes, **kwargs ) -def _generated_source_paths(group, kind): - base = "{}/{}".format(group, kind) - root = "{}/{}".format(base, group) - return base, root +def _deduce_source_paths(group, kind): + """ + Deduces include and root paths for generated sources of a given group and kind. + """ + include = "{}/{}".format(group, kind) + root = "{}/{}".format(include, group) + return include, root def rosidl_c_library( name, group, interfaces, includes = [], deps = [], cc_library_rule = native.cc_library, **kwargs ): - include, root = _generated_source_paths(group, "c") + """ + Generates and builds C ROS 2 interfaces. + + Args: + name: C library target name. + group: interface group name (i.e. ROS 2 package name). + interfaces: interface definition files, only files are allowed + includes: optional interface definition includes, both files and filegroups are allowed. + deps: optional library dependencies. + cc_library_rule: optional cc_library() rule override. + + Additional keyword arguments are those common to all rules. + """ + include, root = _deduce_source_paths(group, "c") generated_c_sources = [] visibility_header = "msg/rosidl_generator_c__visibility_control.h" generated_c_headers = ["{}/{}".format(root, visibility_header)] for ifc in interfaces: - parent, basename = _extract_interface_parts(ifc) + parent, basename = _deduce_source_parts(ifc) generated_c_headers.append("{}/{}/{}.h".format(root, parent, basename)) generated_c_headers.append("{}/{}/detail/{}__functions.h".format(root, parent, basename)) generated_c_headers.append("{}/{}/detail/{}__struct.h".format(root, parent, basename)) @@ -152,7 +218,7 @@ def rosidl_c_library( name = name + "_gen", generated_sources = generated_sources, types = ["c"], - package = group, + group = group, interfaces = interfaces, includes = includes, output_dir = root, @@ -178,11 +244,24 @@ def rosidl_cc_library( name, group, interfaces, includes = [], deps = [], cc_library_rule = native.cc_library, **kwargs ): - include, root = _generated_source_paths(group, "cpp") + """ + Generates and builds C++ ROS 2 interfaces. + + Args: + name: C++ library target name. + group: interface group name (i.e. ROS 2 package name). + interfaces: interface definition files. + includes: optional interface definition includes. + deps: optional library dependencies. + cc_library_rule: optional cc_library() rule override. + + Additional keyword arguments are those common to all rules. + """ + include, root = _deduce_source_paths(group, "cpp") generated_cc_headers = [] for ifc in interfaces: - parent, basename = _extract_interface_parts(ifc) + parent, basename = _deduce_source_parts(ifc) generated_cc_headers.append("{}/{}/{}.hpp".format(root, parent, basename)) generated_cc_headers.append("{}/{}/detail/{}__builder.hpp".format(root, parent, basename)) generated_cc_headers.append("{}/{}/detail/{}__struct.hpp".format(root, parent, basename)) @@ -192,7 +271,7 @@ def rosidl_cc_library( name = name + "_gen", generated_sources = generated_cc_headers, types = ["cpp"], - package = group, + group = group, interfaces = interfaces, includes = includes, output_dir = root, @@ -223,12 +302,31 @@ def rosidl_py_library( py_library_rule = native.py_library, **kwargs ): - import_, root = _generated_source_paths(group, "py") + """ + Generates and builds Python ROS 2 interfaces, including any C extensions. + + Args: + name: Python library target name. + group: interface group name (i.e. ROS 2 package name). + interfaces: interface definition files. + typesupports: a mapping of available, vendor-specific + C typesupport libraries, from typesupport name to + library target label. + includes: optional interface definition includes. + c_deps: optional Python C extension dependencies. + py_deps: optional Python dependencies. + cc_binary_rule: optional cc_binary() rule override. + cc_library_rule: optional cc_library() rule override. + py_library_rule: optional py_library() rule override. + + Additional keyword arguments are those common to all rules. + """ + import_, root = _deduce_source_paths(group, "py") generated_c_sources = [] generated_py_sources = [] for ifc in interfaces: - parent, basename = _extract_interface_parts(ifc) + parent, basename = _deduce_source_parts(ifc) py_source = "{}/{}/__init__.py".format(root, parent) if py_source not in generated_py_sources: generated_py_sources.append(py_source) @@ -251,7 +349,7 @@ def rosidl_py_library( "py[typesupport_implementations:[{}]]" .format(",".join(typesupports)) ], - package = group, + group = group, interfaces = interfaces, includes = includes, output_dir = root, @@ -303,13 +401,27 @@ def rosidl_typesupport_fastrtps_cc_library( cc_binary_rule = native.cc_binary, cc_library_rule = native.cc_library, **kwargs ): - include, root = _generated_source_paths(group, "typesupport/fastrtps_cpp") + """ + Generates and builds FastRTPS C++ typesupport for ROS 2 interfaces. + + Args: + name: FastRTPS C++ typesupport library target name. + group: interface group name (i.e. ROS 2 package name). + interfaces: interface definition files. + includes: optional interface definition includes. + deps: optional library dependencies. + cc_binary_rule: optional cc_binary() rule override. + cc_library_rule: optional cc_library() rule override. + + Additional keyword arguments are those common to all rules. + """ + include, root = _deduce_source_paths(group, "typesupport/fastrtps_cpp") generated_cc_sources = [] visibility_header = "msg/rosidl_typesupport_fastrtps_cpp__visibility_control.h" generated_cc_headers = ["{}/{}".format(root, visibility_header)] for ifc in interfaces: - parent, basename = _extract_interface_parts(ifc) + parent, basename = _deduce_source_parts(ifc) template = "{}/{}/detail/dds_fastrtps/{}__type_support.cpp" generated_cc_sources.append(template.format(root, parent, basename)) template = "{}/{}/detail/{}__rosidl_typesupport_fastrtps_cpp.hpp" @@ -320,7 +432,7 @@ def rosidl_typesupport_fastrtps_cc_library( name = name + "_gen", generated_sources = generated_sources, typesupports = ["fastrtps_cpp"], - package = group, + group = group, interfaces = interfaces, includes = includes, output_dir = root, @@ -355,13 +467,27 @@ def rosidl_typesupport_fastrtps_c_library( cc_binary_rule = native.cc_binary, cc_library_rule = native.cc_library, **kwargs ): - include, root = _generated_source_paths(group, "typesupport/fastrtps_c") + """ + Generates and builds FastRTPS C typesupport for ROS 2 interfaces. + + Args: + name: FastRTPS C typesupport library target name. + group: interface group name (i.e. ROS 2 package name). + interfaces: interface definition files. + includes: optional interface definition includes. + deps: optional library dependencies. + cc_binary_rule: optional cc_binary() rule override. + cc_library_rule: optional cc_library() rule override. + + Additional keyword arguments are those common to all rules. + """ + include, root = _deduce_source_paths(group, "typesupport/fastrtps_c") generated_c_sources = [] visibility_header = "msg/rosidl_typesupport_fastrtps_c__visibility_control.h" generated_c_headers = ["{}/{}".format(root, visibility_header)] for ifc in interfaces: - parent, basename = _extract_interface_parts(ifc) + parent, basename = _deduce_source_parts(ifc) template = "{}/{}/detail/{}__type_support_c.cpp" generated_c_sources.append(template.format(root, parent, basename)) template = "{}/{}/detail/{}__rosidl_typesupport_fastrtps_c.h" @@ -372,7 +498,7 @@ def rosidl_typesupport_fastrtps_c_library( name = name + "_gen", generated_sources = generated_sources, typesupports = ["fastrtps_c"], - package = group, + group = group, interfaces = interfaces, includes = includes, output_dir = root, @@ -408,13 +534,27 @@ def rosidl_typesupport_introspection_c_library( cc_binary_rule = native.cc_binary, cc_library_rule = native.cc_library, **kwargs ): - include, root = _generated_source_paths(group, "typesupport/introspection_c") + """ + Generates and builds C introspection typesupport for ROS 2 interfaces. + + Args: + name: C introspection typesupport library target name. + group: interface group name (i.e. ROS 2 package name). + interfaces: interface definition files. + includes: optional interface definition includes. + deps: optional library dependencies. + cc_binary_rule: optional cc_binary() rule override. + cc_library_rule: optional cc_library() rule override. + + Additional keyword arguments are those common to all rules. + """ + include, root = _deduce_source_paths(group, "typesupport/introspection_c") generated_c_sources = [] visibility_header = "msg/rosidl_typesupport_introspection_c__visibility_control.h" generated_c_headers = ["{}/{}".format(root, visibility_header)] for ifc in interfaces: - parent, basename = _extract_interface_parts(ifc) + parent, basename = _deduce_source_parts(ifc) generated_c_sources.append( "{}/{}/detail/{}__type_support.c".format(root, parent, basename)) template = "{}/{}/detail/{}__rosidl_typesupport_introspection_c.h" @@ -425,7 +565,7 @@ def rosidl_typesupport_introspection_c_library( name = name + "_gen", generated_sources = generated_sources, typesupports = ["introspection_c"], - package = group, + group = group, interfaces = interfaces, includes = includes, output_dir = root, @@ -456,12 +596,26 @@ def rosidl_typesupport_introspection_cc_library( cc_binary_rule = native.cc_binary, cc_library_rule = native.cc_library, **kwargs ): - include, root = _generated_source_paths(group, "typesupport/introspection_cpp") + """ + Generates and builds C++ introspection typesupport for ROS 2 interfaces. + + Args: + name: C++ introspection typesupport library target name. + group: interface group name (i.e. ROS 2 package name). + interfaces: interface definition files. + includes: optional interface definition includes. + deps: optional library dependencies. + cc_binary_rule: optional cc_binary() rule override. + cc_library_rule: optional cc_library() rule override. + + Additional keyword arguments are those common to all rules. + """ + include, root = _deduce_source_paths(group, "typesupport/introspection_cpp") generated_cc_sources = [] generated_cc_headers = [] for ifc in interfaces: - parent, basename = _extract_interface_parts(ifc) + parent, basename = _deduce_source_parts(ifc) generated_cc_sources.append( "{}/{}/detail/{}__type_support.cpp".format(root, parent, basename)) template = "{}/{}/detail/{}__rosidl_typesupport_introspection_cpp.hpp" @@ -472,7 +626,7 @@ def rosidl_typesupport_introspection_cc_library( name = name + "_gen", generated_sources = generated_sources, typesupports = ["introspection_cpp"], - package = group, + group = group, interfaces = interfaces, includes = includes, output_dir = root, @@ -505,13 +659,29 @@ def rosidl_typesupport_c_library( name, group, interfaces, typesupports, includes = [], deps = [], cc_binary_rule = native.cc_binary, **kwargs ): - include, root = _generated_source_paths(group, "typesupport/c") + """ + Generates and builds ROS 2 interfaces' C typesupport. + + Args: + name: C typesupport library target name + group: interface group name (i.e. ROS 2 package name) + interfaces: interface definition files + typesupports: a mapping of available, vendor-specific + C typesupport libraries, from typesupport name to + library target label. + includes: optional interface definition includes + deps: optional library dependencies + cc_binary_rule: optional cc_binary() rule override + + Additional keyword arguments are those common to all rules. + """ + include, root = _deduce_source_paths(group, "typesupport/c") generated_c_sources = [] visibility_header = "msg/rosidl_typesupport_c__visibility_control.h" generated_c_headers = ["{}/{}".format(root, visibility_header)] for ifc in interfaces: - parent, basename = _extract_interface_parts(ifc) + parent, basename = _deduce_source_parts(ifc) generated_c_sources.append( "{}/{}/{}__type_support.cpp".format(root, parent, basename)) generated_sources = generated_c_sources + generated_c_headers @@ -523,7 +693,7 @@ def rosidl_typesupport_c_library( "c[typesupport_implementations:[{}]]" .format(",".join(typesupports)) ], - package = group, + group = group, interfaces = interfaces, includes = includes, output_dir = root, @@ -548,11 +718,27 @@ def rosidl_typesupport_cc_library( name, group, interfaces, typesupports, includes = [], deps = [], cc_binary_rule = native.cc_binary, **kwargs ): - include, root = _generated_source_paths(group, "typesupport/cpp") + """ + Generates and builds ROS 2 interfaces' C++ typesupport. + + Args: + name: C++ typesupport library target name + group: interface group name (i.e. ROS 2 package name) + interfaces: interface definition files + typesupports: a mapping of available, vendor-specific + C++ typesupport libraries, from typesupport name to + library target label. + includes: optional interface definition includes + deps: optional library dependencies + cc_binary_rule: optional cc_binary() rule override + + Additional keyword arguments are those common to all rules. + """ + include, root = _deduce_source_paths(group, "typesupport/cpp") generated_cc_sources = [] for ifc in interfaces: - parent, basename = _extract_interface_parts(ifc) + parent, basename = _deduce_source_parts(ifc) generated_cc_sources.append( "{}/{}/{}__type_support.cpp".format(root, parent, basename)) @@ -563,7 +749,7 @@ def rosidl_typesupport_cc_library( "cpp[typesupport_implementations:[{}]]" .format(",".join(typesupports)) ], - package = group, + group = group, interfaces = interfaces, includes = includes, output_dir = root, @@ -624,6 +810,23 @@ def rosidl_cc_support( cc_library_rule = native.cc_library, **kwargs ): + """ + Generates and builds C++ ROS 2 interfaces. + + To depend on C++ interfaces, use the `_cc` target. + + Args: + name: interface group name, used as prefix for target names + interfaces: interface definition files + deps: optional interface group dependencies + group: optional interface group name override, useful when + target name cannot be forced to match the intended package + name for these interfaces + cc_binary_rule: optional cc_binary() rule override + cc_library_rule: optional cc_library() rule override + + Additional keyword arguments are those common to all rules. + """ rosidl_cc_library( name = cc_types(name), group = group or name, @@ -728,6 +931,24 @@ def rosidl_py_support( py_library_rule = native.py_library, **kwargs ): + """ + Generates and builds Python ROS 2 interfaces. + + To depend on Python interfaces, use the `_py` target. + + Args: + name: interface group name, used as prefix for target names + interfaces: interface definition files + deps: optional interface group dependencies + group: optional interface group name override, useful when + target name cannot be forced to match the intended package + name for these interfaces + cc_binary_rule: optional cc_binary() rule override + cc_library_rule: optional cc_library() rule override + py_library_rule: optional py_library() rule override + + Additional keyword arguments are those common to all rules. + """ rosidl_c_library( name = c_types(name), group = group or name, @@ -805,12 +1026,31 @@ def rosidl_py_support( ) def rosidl_interfaces_group( - name, interfaces, deps, group = None, + name, interfaces, deps = [], group = None, cc_binary_rule = native.cc_binary, cc_library_rule = native.cc_library, py_library_rule = native.py_library, **kwargs ): + """ + Generates and builds C++ and Python ROS 2 interfaces. + + To depend on C++ interfaces, use the `_cc` target. + To depend on Python interfaces, use the `_py` target. + + Args: + name: interface group name, used as prefix for target names + interfaces: interface definition files + deps: optional interface group dependencies + group: optional interface group name override, useful when + target name cannot be forced to match the intended package + name for these interfaces + cc_binary_rule: optional cc_binary() rule override + cc_library_rule: optional cc_library() rule override + py_library_rule: optional py_library() rule override + + Additional keyword arguments are those common to all rules. + """ rosidl_definitions_filegroup( name = defs(name), group = group or name, From 2cf57786239c478909f939b96a69a6dcebc37164 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 14 Oct 2021 16:33:02 -0300 Subject: [PATCH 050/107] Document and simplify ros2.bzl Signed-off-by: Michel Hidalgo --- .../skylark/ros2/generate_repository_files.py | 5 ++- .../skylark/ros2/resources/shell/setup.sh.in | 2 -- .../tools/skylark/ros2/ros2.bzl | 36 +++++++------------ 3 files changed, 14 insertions(+), 29 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py index 95dffedec..23fe0a81c 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 """ -Scrap ROS 2 workspaces and expose its artifacts through a Bazel local repository. - +Scraps ROS 2 workspaces and exposes their artifacts through a Bazel local repository. This script generates: @@ -91,7 +90,7 @@ def parse_arguments(): ) parser.add_argument( '-j', '--jobs', metavar='N', type=int, default=None, - help='Number of jobs to use to scrap packages' + help='Number of CMake jobs to use during package configuration and scrapping' ) args = parser.parse_args() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/shell/setup.sh.in b/drake_ros_bazel_installed/tools/skylark/ros2/resources/shell/setup.sh.in index 28addc617..9f6e18098 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/shell/setup.sh.in +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/shell/setup.sh.in @@ -1,7 +1,5 @@ #! /bin/sh -# Run @ID@ - # Source all workspaces for ws in @WORKSPACES@; do . $ws/setup.sh diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl index 610047817..fedf4b3b2 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -1,3 +1,5 @@ +# -*- python -*- + load("@drake_ros//tools/skylark:execute.bzl", "execute_or_fail") MANIFEST = [ @@ -36,10 +38,6 @@ MANIFEST = [ def _label(relpath): return Label("//tools/skylark/ros2:" + relpath) -def _uuid(repo_ctx): - cmd = [repo_ctx.which("python3"), "-c", "import uuid; print(uuid.uuid1())"] - return execute_or_fail(repo_ctx, cmd, quiet=True).stdout - def _impl(repo_ctx): for relpath in MANIFEST: repo_ctx.symlink(_label(relpath), relpath) @@ -47,7 +45,6 @@ def _impl(repo_ctx): repo_ctx.template( "setup.sh", _label("resources/shell/setup.sh.in"), substitutions = { - "@ID@": _uuid(repo_ctx), "@REPOSITORY_DIR@": str(repo_ctx.path(".")), "@WORKSPACES@": " ".join(repo_ctx.attr.workspaces), "@CMAKE_PREFIX_PATH@": ":".join(repo_ctx.attr.workspaces), @@ -72,33 +69,24 @@ def _impl(repo_ctx): cmd.append(repo_ctx.name) execute_or_fail(repo_ctx, cmd, quiet=True) -""" -Extracts relevant properties from CMake and Python stuff for ROS 2 install -trees (see https://www.ros.org/reps/rep-0122.html). Assumes `ament_cmake` -for C++ packages and generic Python packages. -""" - -# NOTE(hidmic): `ament_cmake` is not using modern CMake targets as of Dashing, -# and as of Foxy not all packages are bound to. Stick to non-standard dependency -# recollection. - -# TODO(eric): How to handle licenses? ros2_local_repository = repository_rule( attrs = dict( - # Workspaces - # - FHS install trees (incl. ABI compatible overlays). workspaces = attr.string_list(mandatory = True), - # Explicit set of packages to include, with its - # recursive dependencies. Default to all. include_packages = attr.string_list(), - # Set of packages to exclude. Default to none. exclude_packages = attr.string_list(), - # Extra data dependencies for targets extra_data = attr.string_list_dict(), - # Maximum CMake jobs to use during configuration jobs = attr.int(default=0), ), local = False, - configure = True, implementation = _impl, ) +""" +Scraps ROS 2 workspaces and exposes its artifacts as a Bazel local repository. + +Args: + workspaces: paths to ROS 2 workspace install trees. Each workspace specified overlays the previous one. + include_packages: optional set of packages to include, with its recursive dependencies. Defaults to all. + exclude_packages: optional set of packages to exclude, with precedence over included files. Defaults to none. + extra_data: optional extra data dependencies for given targets + jobs: number of CMake jobs to use during package configuration and scrapping. Defaults to using all cores. +""" From cf5cc5b1f147d11ad66ce2ddd536c779b75f2d50 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 14 Oct 2021 17:10:56 -0300 Subject: [PATCH 051/107] Clarify dload* macro visibility Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/tools/skylark/dload.bzl | 11 +++++++++-- .../tools/skylark/dload_cc.bzl | 15 +++++++++------ .../tools/skylark/dload_py.bzl | 14 ++++++++------ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/dload.bzl b/drake_ros_bazel_installed/tools/skylark/dload.bzl index 59412bcaf..5e0d4039c 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload.bzl @@ -38,7 +38,12 @@ def _normpath(path): return "/".join(normalized_path_parts) def get_dload_shim_attributes(): - """Yields attributes common to all dload_shim-based rules.""" + """ + Yields attributes common to all dload_shim-based rules. + + This macro aids rule declaration and as such it is not meant + to be used in any other context (like a BUILD.bazel file). + """ return { "target": attr.label( mandatory = True, @@ -46,7 +51,6 @@ def get_dload_shim_attributes(): executable = True, cfg = "target", ), - "data": attr.label_list(allow_files = True), "env_changes": attr.string_list_dict(), } @@ -54,6 +58,9 @@ def do_dload_shim(ctx, template, to_list): """ Implements common dload_shim rule functionality. + This macro is a parametrized rule implementation and as such it is not meant + to be used in any other context (like a BUILD.bazel file). + Args: ctx: context of a Bazel rule template: string template for the shim diff --git a/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl b/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl index d57cf10d0..ca907e4ef 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl @@ -1,9 +1,8 @@ # -*- python -*- """ -The purpose of these macros is to support the propagation of runtime information -that is key for proper execution from C/C++ libraries to C/C++ binaries and -tests. +The purpose of these rules is to support the propagation of runtime information +that is necessary for the execution of C/C++ binaries and tests that require it. """ load( @@ -12,7 +11,7 @@ load( "get_dload_shim_attributes", ) -DLOAD_CC_SHIM_TEMPLATE = """\ +_DLOAD_CC_SHIM_TEMPLATE = """\ #include #include #include @@ -91,7 +90,7 @@ def _to_cc_list(collection): return "{" + ", ".join(collection) + "}" def _dload_cc_shim_impl(ctx): - return do_dload_shim(ctx, DLOAD_CC_SHIM_TEMPLATE, _to_cc_list) + return do_dload_shim(ctx, _DLOAD_CC_SHIM_TEMPLATE, _to_cc_list) dload_cc_shim = rule( attrs = get_dload_shim_attributes(), @@ -99,6 +98,10 @@ dload_cc_shim = rule( output_to_genfiles = True, ) """ -This rule() generates a C++ shim that can carry runtime information. +Generates a C++ shim that can inject runtime environment information for +C/C++ binaries that have such requirements. Using a C++ shim for C++ binaries +simplifies UX during debugging sessions, as fork-follow behavior in common +debuggers like gdb and lldb makes it transparent. + See do_dload_shim() documentation for further reference. """ diff --git a/drake_ros_bazel_installed/tools/skylark/dload_py.bzl b/drake_ros_bazel_installed/tools/skylark/dload_py.bzl index d70849a57..7cb5d4e26 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload_py.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload_py.bzl @@ -2,9 +2,8 @@ # vi: set ft=python : """ -The purpose of these macros is to support the propagation of runtime information -that is key for proper execution from Python libraries to Python binaries and -tests. +The purpose of these rules is to support the propagation of runtime information +that is necessary for the execution of Python binaries and tests that require it. """ load( @@ -13,7 +12,7 @@ load( "get_dload_shim_attributes", ) -DLOAD_PY_SHIM_TEMPLATE = """\ +_DLOAD_PY_SHIM_TEMPLATE = """\ import os import sys @@ -63,7 +62,7 @@ def _to_py_list(collection): return "[" + ", ".join(collection) + "]" def _dload_py_shim_impl(ctx): - return do_dload_shim(ctx, DLOAD_PY_SHIM_TEMPLATE, _to_py_list) + return do_dload_shim(ctx, _DLOAD_PY_SHIM_TEMPLATE, _to_py_list) dload_py_shim = rule( attrs = get_dload_shim_attributes(), @@ -71,6 +70,9 @@ dload_py_shim = rule( implementation = _dload_py_shim_impl, ) """ -This rule() generates a Python shim that can carry runtime information. +Generates a Python shim that can inject runtime environment information for +Python binaries that have such requirements. Using a Python shim for Python +binaries enables downstream usage of the latter through transitive dependencies. + See do_dload_shim() documentation for further reference. """ From 2f866ac4b89c90a1ca849f581f32131549c0fbcd Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 14 Oct 2021 17:46:33 -0300 Subject: [PATCH 052/107] Add debugger fork-follow smoke tests Signed-off-by: Michel Hidalgo --- .../drake_ros_apps/BUILD.bazel | 26 +++++++++++++++++++ .../drake_ros_apps/test/exec.sh | 3 +++ .../drake_ros_apps/test/oracle_cc.gdb | 7 +++++ .../drake_ros_apps/test/oracle_cc.lldb | 5 ++++ 4 files changed, 41 insertions(+) create mode 100755 drake_ros_bazel_installed/drake_ros_apps/test/exec.sh create mode 100644 drake_ros_bazel_installed/drake_ros_apps/test/oracle_cc.gdb create mode 100644 drake_ros_bazel_installed/drake_ros_apps/test/oracle_cc.lldb diff --git a/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel b/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel index 5b79a22d8..76112426a 100644 --- a/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel +++ b/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel @@ -67,3 +67,29 @@ ros_py_binary( rmw_implementation = "rmw_cyclonedds_cpp", tags = ["requires-network"], ) + +sh_test( + name = "gdb_oracle_cc_test", + srcs = ["test/exec.sh"], + args = [ + "gdb", + "-x", "$(location :test/oracle_cc.gdb)", "--args", + "$(location :oracle_cc)", "--ros-args", "--disable-external-lib-logs", + ], + data = ["test/oracle_cc.gdb", ":oracle_cc"], + tags = ["manual"], + size = "small", +) + +sh_test( + name = "lldb_oracle_cc_test", + srcs = ["test/exec.sh"], + args = [ + "lldb", + "-s", "$(location :test/oracle_cc.lldb)", "--", + "$(location :oracle_cc)", "--ros-args", "--disable-external-lib-logs", + ], + data = ["test/oracle_cc.lldb", ":oracle_cc"], + tags = ["manual"], + size = "small", +) diff --git a/drake_ros_bazel_installed/drake_ros_apps/test/exec.sh b/drake_ros_bazel_installed/drake_ros_apps/test/exec.sh new file mode 100755 index 000000000..0daad140e --- /dev/null +++ b/drake_ros_bazel_installed/drake_ros_apps/test/exec.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec $@ diff --git a/drake_ros_bazel_installed/drake_ros_apps/test/oracle_cc.gdb b/drake_ros_bazel_installed/drake_ros_apps/test/oracle_cc.gdb new file mode 100644 index 000000000..149ec45e8 --- /dev/null +++ b/drake_ros_bazel_installed/drake_ros_apps/test/oracle_cc.gdb @@ -0,0 +1,7 @@ +set breakpoint pending on +break main +run +continue +break Oracle::publish_status +continue +bt diff --git a/drake_ros_bazel_installed/drake_ros_apps/test/oracle_cc.lldb b/drake_ros_bazel_installed/drake_ros_apps/test/oracle_cc.lldb new file mode 100644 index 000000000..54701f54d --- /dev/null +++ b/drake_ros_bazel_installed/drake_ros_apps/test/oracle_cc.lldb @@ -0,0 +1,5 @@ +settings set target.process.stop-on-exec false +process launch --stop-at-entry +breakpoint set --method 'Oracle::publish_status' +continue +thread backtrace From 491db1e727eb0d43e747cd69bd06d6692724cea5 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 14 Oct 2021 18:01:41 -0300 Subject: [PATCH 053/107] Cleanup ROS 2 dependencies Signed-off-by: Michel Hidalgo --- .../tools/workspace/ros2/repository.bzl | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl b/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl index 9a9f4ce36..ef5382e0f 100644 --- a/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl +++ b/drake_ros_bazel_installed/tools/workspace/ros2/repository.bzl @@ -7,17 +7,14 @@ def ros2_repository(name, overlays = []): name = name, workspaces = ['/opt/ros/{}'.format(ROS2_DIST)], include_packages = [ - "std_msgs", - "geometry_msgs", - "visualization_msgs", + "action_msgs", + "builtin_interfaces", "rosidl_default_generators", - "fastcdr", - "rcpputils", - "rcutils", + "rclcpp_action", "rclcpp", "rclpy", - "rviz2", - # RMW implementation + # RMW implementations "rmw_cyclonedds_cpp", + "rmw_fastrtps_cpp", ] ) From c5d349e246514d4eacaba9d41cae7b3429a7e73c Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 14 Oct 2021 18:02:08 -0300 Subject: [PATCH 054/107] Add prerequisites' install script Signed-off-by: Michel Hidalgo --- .../setup/install_prereqs.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100755 drake_ros_bazel_installed/setup/install_prereqs.sh diff --git a/drake_ros_bazel_installed/setup/install_prereqs.sh b/drake_ros_bazel_installed/setup/install_prereqs.sh new file mode 100755 index 000000000..4e5381e7d --- /dev/null +++ b/drake_ros_bazel_installed/setup/install_prereqs.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -eux pipefail + +# Install Bazel +apt install -y apt-transport-https curl gnupg +curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg +mv bazel.gpg /etc/apt/trusted.gpg.d/ +echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list +apt update && apt install bazel + +# Install CPython development libraries +apt install -y python3-dev + +# Install ROS 2 Rolling +apt install -y ros-rolling-ros-base ros-rolling-rmw-fastrtps-cpp ros-rolling-rmw-cyclonedds-cpp + +# Install debuggers +apt install -y gdb lldb From e83151db383d737f93db80d02a9c579c76e886d8 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 15 Oct 2021 18:49:31 -0300 Subject: [PATCH 055/107] Make rmw isolation an explicit runtime choice Signed-off-by: Michel Hidalgo --- .../tools/ros2/BUILD.bazel | 89 ++++++++ .../tools/ros2/isolated_rmw_env.py | 47 +++++ .../tools/ros2/rmw_isolation.cc | 64 ++++++ .../tools/ros2/rmw_isolation.h | 20 ++ .../tools/ros2/rmw_isolation.py | 199 ++++++++++++++++++ .../tools/ros2/test/isolated_listener.cc | 50 +++++ .../tools/ros2/test/isolated_listener.py | 41 ++++ .../tools/ros2/test/rmw_isolation_test.sh | 7 + .../tools/ros2/test/talker.py | 27 +++ .../tools/skylark/ros2/BUILD.bazel | 15 +- .../ros2/generate_cyclonedds_profile.py | 64 ------ .../skylark/ros2/generate_fastrtps_profile.py | 104 --------- .../ros2/resources/bazel/common.bzl.tpl | 23 -- .../ros2/resources/bazel/ros_cc.bzl.tpl | 37 ---- .../ros2/resources/bazel/ros_py.bzl.tpl | 45 ---- .../tools/skylark/ros2/rmw.bzl | 60 ------ 16 files changed, 545 insertions(+), 347 deletions(-) create mode 100644 drake_ros_bazel_installed/tools/ros2/BUILD.bazel create mode 100644 drake_ros_bazel_installed/tools/ros2/isolated_rmw_env.py create mode 100644 drake_ros_bazel_installed/tools/ros2/rmw_isolation.cc create mode 100644 drake_ros_bazel_installed/tools/ros2/rmw_isolation.h create mode 100644 drake_ros_bazel_installed/tools/ros2/rmw_isolation.py create mode 100644 drake_ros_bazel_installed/tools/ros2/test/isolated_listener.cc create mode 100644 drake_ros_bazel_installed/tools/ros2/test/isolated_listener.py create mode 100755 drake_ros_bazel_installed/tools/ros2/test/rmw_isolation_test.sh create mode 100644 drake_ros_bazel_installed/tools/ros2/test/talker.py delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/generate_cyclonedds_profile.py delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/generate_fastrtps_profile.py delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl diff --git a/drake_ros_bazel_installed/tools/ros2/BUILD.bazel b/drake_ros_bazel_installed/tools/ros2/BUILD.bazel new file mode 100644 index 000000000..28f4fcbc0 --- /dev/null +++ b/drake_ros_bazel_installed/tools/ros2/BUILD.bazel @@ -0,0 +1,89 @@ +# -*- mode: python -*- +# vi: set ft=python : + +package(default_visibility = ["//visibility:public"]) + +load("@ros2//:ros_cc.bzl", "ros_cc_binary") +load("@ros2//:ros_py.bzl", "ros_py_binary") + +py_library( + name = "rmw_isolation_py", + srcs = ["rmw_isolation.py"], + deps = ["@ros2//:rclpy_py"], +) + +ros_py_binary( + name = "isolated_rmw_env", + srcs = ["isolated_rmw_env.py"], + main = "isolated_rmw_env.py", + deps = [":rmw_isolation_py"], +) + +cc_library( + name = "rmw_isolation_cc", + srcs = ["rmw_isolation.cc"], + hdrs = ["rmw_isolation.h"], + data = [":isolated_rmw_env"], + deps = [ + "@bazel_tools//tools/cpp/runfiles", + "@ros2//:rclcpp_cc", + ], +) + +ros_py_binary( + name = "talker_py", + srcs = ["test/talker.py"], + main = "test/talker.py", + deps = [ + "@ros2//:rclpy_py", + "@ros2//:std_msgs_py" + ], + rmw_implementation = 'rmw_cyclonedds_cpp', +) + +ros_py_binary( + name = "isolated_listener_py", + srcs = ["test/isolated_listener.py"], + main = "test/isolated_listener.py", + deps = [ + "@ros2//:rclpy_py", + "@ros2//:std_msgs_py", + ":rmw_isolation_py", + ], + rmw_implementation = 'rmw_cyclonedds_cpp', +) + +sh_test( + name = "rmw_isolation_py_test", + srcs = ["test/rmw_isolation_test.sh"], + args = [ + "$(location :talker_py)", + "$(location :isolated_listener_py)", + ], + data = [":talker_py", ":isolated_listener_py"], + tags = ["manual"], + size = "small", +) + +ros_cc_binary( + name = "isolated_listener_cc", + srcs = ["test/isolated_listener.cc"], + deps = [ + "@ros2//:rclcpp_cc", + "@ros2//:std_msgs_cc", + ":rmw_isolation_cc", + ], + rmw_implementation = 'rmw_cyclonedds_cpp', +) + +sh_test( + name = "rmw_isolation_cc_test", + srcs = ["test/rmw_isolation_test.sh"], + args = [ + "$(location :talker_py)", + "$(location :isolated_listener_cc)", + ], + data = [":talker_py", ":isolated_listener_cc"], + tags = ["manual"], + size = "small", +) diff --git a/drake_ros_bazel_installed/tools/ros2/isolated_rmw_env.py b/drake_ros_bazel_installed/tools/ros2/isolated_rmw_env.py new file mode 100644 index 000000000..9278558d3 --- /dev/null +++ b/drake_ros_bazel_installed/tools/ros2/isolated_rmw_env.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 + +""" +Generates a runtime environment for RMW isolation. + +Environment variables are written in the NAME=VALUE format, +one per line, suitable for e.g. the `env` command and the +`putenv` API. +""" + +import argparse +import pathlib +import sys + +from rmw_isolation import isolated_rmw_env + +def main(argv=None): + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument( + '-o', '--output', metavar='PATH', + type=argparse.FileType('w'), default=sys.stdout, + help='Path to output file. Defaults to stdout.') + parser.add_argument( + '-s', '--scratch-directory', metavar='PATH', default=pathlib.Path.cwd(), + help=('Path to scratch directory for generated files, if any. ' + 'Defaults to the current working directory.')) + parser.add_argument( + '-r', '--rmw-implementation', default=None, + help=('Middleware implementation to be isolated. ' + 'Defaults to currently applicable implementation, ' + 'as returned by rclpy.get_rmw_implementation_identifier().')) + parser.add_argument( + 'unique_identifier', nargs='?', default=str(pathlib.Path.cwd()), + help=('Unique arbitrary identifier for isolation. ' + 'Defaults to current working directory.')) + args = parser.parse_args(argv) + for key, value in isolated_rmw_env( + args.unique_identifier, + rmw_implementation=args.rmw_implementation, + scratch_directory=args.scratch_directory + ).items(): + args.output.write(f'{key}={value}\n') + +if __name__ == '__main__': + main() diff --git a/drake_ros_bazel_installed/tools/ros2/rmw_isolation.cc b/drake_ros_bazel_installed/tools/ros2/rmw_isolation.cc new file mode 100644 index 000000000..ffc5691a8 --- /dev/null +++ b/drake_ros_bazel_installed/tools/ros2/rmw_isolation.cc @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "tools/cpp/runfiles/runfiles.h" +#include "tools/ros2/rmw_isolation.h" + +using bazel::tools::cpp::runfiles::Runfiles; + +namespace ros2 { + +void isolate_rmw_by_path(const std::string& argv0, const std::string& path) +{ + if (rclcpp::ok()) + { + throw std::runtime_error( + "middleware already initialized, too late for isolation"); + } + std::string error; + std::unique_ptr runfiles(Runfiles::Create(argv0, &error)); + if (!runfiles) { + throw std::runtime_error(error); + } + // NOTE(hidmic): no way around hard-coding this. Bazel's $(rootpath) + // won't prepend the workspace name (drake_ros in this case). Maybe + // relocate to an external repository? + std::string isolated_env_rmw_location = + runfiles->Rlocation("drake_ros/tools/ros2/isolated_rmw_env"); + std::string env_file_path{"isolated_rmw.env"}; + std::stringstream sstr; + sstr << isolated_env_rmw_location << " --scratch-directory " << path + << " --rmw-implementation " << rmw_get_implementation_identifier() + << " --output " << env_file_path << " " << path; + std::string command = sstr.str(); + // NOTE(hidmic): no way to redirect stderr here. No feedback on error. + // Use POSIX popen? + int status = std::system(command.c_str()); + if (status < 0) { + throw std::system_error(errno, std::system_category(), strerror(errno)); + } + if (status != 0) { + throw std::runtime_error(isolated_env_rmw_location + " exited abnormally"); + } + std::ifstream env_file{env_file_path}; + for(std::string line; std::getline(env_file, line); ) { + const size_t separator_index = line.find("="); + const std::string name = line.substr(0, separator_index); + const std::string value = + separator_index != std::string::npos ? + line.substr(separator_index + 1) : ""; + if (setenv(name.c_str(), value.c_str(), 1) != 0) { + throw std::system_error(errno, std::system_category(), strerror(errno)); + } + } +} + +} // namespace ros2 diff --git a/drake_ros_bazel_installed/tools/ros2/rmw_isolation.h b/drake_ros_bazel_installed/tools/ros2/rmw_isolation.h new file mode 100644 index 000000000..259f465c1 --- /dev/null +++ b/drake_ros_bazel_installed/tools/ros2/rmw_isolation.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace ros2 { + +/// Isolates rmw implementation network traffic. +/** + * This function relies on the `isolated_rmw_env` CLI to populate + * the calling process environment and achieve network isolation. + * + * \param argv0 program name to help locate `isolated_rmw_env` + * \param path unique path to use as a basis for isolation. + * \throws std::runtime_error if called after rmw initialization. + * \throws std::runtime_error if `isolated_rmw_env` exits abnormally. + * \throws std::system_error if a system call fails. + */ +void isolate_rmw_by_path(const std::string& argv0, const std::string& path); + +} // namespace ros2 diff --git a/drake_ros_bazel_installed/tools/ros2/rmw_isolation.py b/drake_ros_bazel_installed/tools/ros2/rmw_isolation.py new file mode 100644 index 000000000..f31e5baec --- /dev/null +++ b/drake_ros_bazel_installed/tools/ros2/rmw_isolation.py @@ -0,0 +1,199 @@ +import hashlib +import os +import pathlib +import tempfile + +import xml.dom.minidom as minidom +import xml.etree.ElementTree as ET + +import rclpy + +PARTICIPANT_ID_GAIN = 2 +MAX_PARTICIPANTS_PER_PROCESS = 10 + +# 2 multicast ports + 2 unicast ports per participant +DISCOVERY_PORTS_INTERVAL = MAX_PARTICIPANTS_PER_PROCESS * PARTICIPANT_ID_GAIN + 2 + +def isolated_rmw_fastrtps_cpp_env(unique_identifier, scratch_directory=None): + """ + Generates an environment that forces rmw_fastrtps_cpp network traffic isolation. + + This function achieves network traffic isolation by modifying multicast discovery + IPv4 address and well-known ports offsets, which FastRTPS (now FastDDS) allows + through the FASTRTPS_DEFAULT_PROFILES_FILE environment variable. For further reference, + see https://fast-dds.docs.eprosima.com/en/latest/fastdds/transport/listening_locators.html. + + :param unique_identifier: unique arbitrary string to be used as a basis for isolation. + :param scratch_directory: optional directory for generated files. + If not provided, a temporary directory will be created. + :returns: a dictionary of environment variables. + """ + profile_name = unique_identifier + transport_name = unique_identifier + '/transport' + + digest = hashlib.sha256(unique_identifier.encode('utf8')).digest() + + builder = ET.TreeBuilder() + xmlns = 'http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles' + builder.start('profiles', {'xmlns': xmlns}) + builder.start('transport_descriptors') + builder.start('transport_descriptor') + builder.start('transport_id') + builder.data(transport_name) + builder.end('transport_id') + builder.start('type') + builder.data('UDPv4') + builder.end('type') + builder.end('transport_descriptor') + builder.end('transport_descriptors') + builder.start('participant', { + 'profile_name': profile_name, + 'is_default_profile': 'true' + }) + builder.start('rtps') + builder.start('builtin') + builder.start('metatrafficMulticastLocatorList') + builder.start('locator') + builder.start('udpv4') + builder.start('address') + multicast_discovery_ip_address = '.'.join( + map(str, [239] + [int(c) for c in digest[:3]])) + builder.data(multicast_discovery_ip_address) + builder.end('address') + builder.end('udpv4') + builder.end('locator') + builder.end('metatrafficMulticastLocatorList') + builder.end('builtin') + builder.start('port') + builder.start('domainIDGain') + builder.data('0') # ignore domain IDs + builder.end('domainIDGain') + builder.start('participantIDGain') + builder.data(str(PARTICIPANT_ID_GAIN)) + builder.end('participantIDGain') + discovery_ports_offset = ( + int(digest[3]) * DISCOVERY_PORTS_INTERVAL) + builder.start('offsetd0') + builder.data(str(discovery_ports_offset)) + builder.end('offsetd0') + builder.start('offsetd1') + builder.data(str(discovery_ports_offset + 2)) + builder.end('offsetd1') + builder.start('offsetd2') + builder.data(str(discovery_ports_offset + 1)) + builder.end('offsetd2') + builder.start('offsetd3') + builder.data(str(discovery_ports_offset + 3)) + builder.end('offsetd3') + builder.end('port') + builder.start('userTransports') + builder.start('transport_id') + builder.data(transport_name) + builder.end('transport_id') + builder.end('userTransports') + builder.start('useBuiltinTransports') + builder.data('false') + builder.end('useBuiltinTransports') + builder.end('rtps') + builder.end('participant') + builder.end('profiles') + tree = builder.close() + inline_xml = ET.tostring(tree, encoding='utf-8') + dom = minidom.parseString(inline_xml) + pretty_xml = dom.toprettyxml(indent=' ' * 4, encoding='utf-8') + if scratch_directory is None: + scratch_directory = tempfile.mkdtemp() + profiles_path = pathlib.Path(scratch_directory) / 'fastrtps_profiles.xml' + with profiles_path.open('wb') as f: + f.write(pretty_xml) + return {'FASTRTPS_DEFAULT_PROFILES_FILE': str(profiles_path), 'ROS_DOMAIN_ID': '0'} + +def isolated_rmw_cyclonedds_cpp_env(unique_identifier, scratch_directory=None): + """ + Generates an environment that forces rmw_cyclonedds_cpp network traffic isolation. + + This function achieves network traffic isolation by modifying multicast discovery + IPv4 address and domain ID, which CycloneDDS allows through the CYCLONEDDS_URI + environment variable. For further reference, see + https://github.com/eclipse-cyclonedds/cyclonedds/blob/master/docs/manual/config.rst. + + :param unique_identifier: unique arbitrary string to be used as a basis for isolation. + :param scratch_directory: optional directory for generated files. + If not provided, a temporary directory will be created. + :returns: a dictionary of environment variables. + """ + digest = hashlib.sha256(unique_identifier.encode('utf8')).digest() + + builder = ET.TreeBuilder() + xmlns = 'http://www.w3.org/2001/XMLSchema-instance' + schema_location = ( + 'https://cdds.io/config https://raw.githubusercontent.com' + '/eclipse-cyclonedds/cyclonedds/master/etc/cyclonedds.xsd') + builder.start('CycloneDDS', { + 'xmlns:xsi': xmlns, 'xsi:schemaLocation': schema_location}) + builder.start('Domain', {'id': 'any'}) + builder.start('Discovery') + builder.start('SPDPMulticastAddress') + multicast_discovery_ip_address = '.'.join( + map(str, [239] + [int(c) for c in digest[:3]])) + builder.data(multicast_discovery_ip_address) + builder.end('SPDPMulticastAddress') + builder.start('ParticipantIndex') + builder.data('none') # disable unicast discovery + builder.end('ParticipantIndex') + builder.start('Ports') + builder.start('DomainGain') + builder.data('225') # ensure all 256 domain IDs are valid + builder.end('DomainGain') + builder.end('Ports') + builder.end('Discovery') + builder.end('Domain') + builder.end('CycloneDDS') + tree = builder.close() + inline_xml = ET.tostring(tree, encoding='utf-8') + dom = minidom.parseString(inline_xml) + pretty_xml = dom.toprettyxml(indent=' ' * 4, encoding='utf-8') + if scratch_directory is None: + scratch_directory = tempfile.mkdtemp() + configuration_path = pathlib.Path(scratch_directory) / 'cyclonedds_configuration.xml' + with configuration_path.open('wb') as f: + f.write(pretty_xml) + return { + 'CYCLONEDDS_URI': f'file://{configuration_path.resolve()}', + 'ROS_DOMAIN_ID': str(int(digest[3]))} + +def isolated_rmw_env(unique_identifier, rmw_implementation=None, scratch_directory=None): + """ + Generates an environment that forces rmw implementation network traffic isolation. + + :param unique_identifier: unique arbitrary string to be used as + a basis for isolation. + :param rmw_implementation: optional target rmw implementation. + If not provided, the currently applicable implementation + will be used, as returned by rclpy.get_rmw_implementation_identifier(). + :param scratch_directory: optional directory for generated files, if any. + If not provided, a temporary directory will be created. + :returns: a dictionary of environment variables. + :raises ValueError: if the target rmw implementation is unknown. + """ + if rmw_implementation is None: + rmw_implementation = rclpy.get_rmw_implementation_identifier() + target_function_name = f'isolated_{rmw_implementation}_env' + if target_function_name not in globals(): + raise ValueError(f'cannot isolate unknown {rmw_implementation} implementation') + return globals()[target_function_name]( + unique_identifier, scratch_directory=scratch_directory) + +def isolate_rmw_by_path(path): + """ + Isolates rmw implementation network traffic. + + This function relies on `isolated_rmw_env` to populate + the calling process environment and achieve network isolation. + + :param path: unique path to use as a basis for isolation. + :raises RuntimeError: if called after rmw initialization. + """ + if rclpy.ok(): + raise RuntimeError('middleware already initialized, too late for isolation') + os.environ.update(isolated_rmw_env(str(path), scratch_directory=path)) diff --git a/drake_ros_bazel_installed/tools/ros2/test/isolated_listener.cc b/drake_ros_bazel_installed/tools/ros2/test/isolated_listener.cc new file mode 100644 index 000000000..d66f1b02f --- /dev/null +++ b/drake_ros_bazel_installed/tools/ros2/test/isolated_listener.cc @@ -0,0 +1,50 @@ +#include +#include + +#include +#include + +#include "tools/ros2/rmw_isolation.h" + +using std::placeholders::_1; + +class IsolatedListener : public rclcpp::Node +{ + public: + IsolatedListener() + : rclcpp::Node("isolated_listener") + { + double timeout = this->declare_parameter("timeout", 2.0); + subscription_ = this->create_subscription( + "topic", 10, std::bind(&IsolatedListener::topic_callback, this, _1)); + timer_ = this->create_wall_timer( + std::chrono::duration(timeout), + std::bind(&IsolatedListener::timer_callback, this)); + } + + private: + void topic_callback(const std_msgs::msg::String::SharedPtr msg) const + { + throw std::runtime_error("I heard '" + msg->data + "'"); + } + + void timer_callback() const + { + rclcpp::shutdown(); + } + + rclcpp::Subscription::SharedPtr subscription_; + rclcpp::TimerBase::SharedPtr timer_; +}; + +int main(int argc, char * argv[]) +{ + const char * TEST_TMPDIR = std::getenv("TEST_TMPDIR"); + if (TEST_TMPDIR != nullptr) { + ros2::isolate_rmw_by_path(argv[0], TEST_TMPDIR); + } + rclcpp::init(argc, argv); + rclcpp::spin(std::make_shared()); + rclcpp::shutdown(); + return 0; +} diff --git a/drake_ros_bazel_installed/tools/ros2/test/isolated_listener.py b/drake_ros_bazel_installed/tools/ros2/test/isolated_listener.py new file mode 100644 index 000000000..9987be5ea --- /dev/null +++ b/drake_ros_bazel_installed/tools/ros2/test/isolated_listener.py @@ -0,0 +1,41 @@ +import os + +import rclpy +import rclpy.node +import rclpy.executors +import std_msgs.msg + + +class IsolatedListener(rclpy.node.Node): + + def __init__(self): + super().__init__('isolated_listener') + self._subscription = self.create_subscription( + std_msgs.msg.String, + 'topic', + self._topic_callback, + 10) + timeout = self.declare_parameter('timeout', 2.0) + self._timer = self.create_timer( + timeout.value, self._timer_callback) + + def _topic_callback(self, msg): + assert False, f"I heard '{msg.data}'!" + + def _timer_callback(self): + rclpy.shutdown() + +if __name__ == '__main__': + if 'TEST_TMPDIR' in os.environ: + from drake_ros.tools.ros2.rmw_isolation import isolate_rmw_by_path + isolate_rmw_by_path(os.environ['TEST_TMPDIR']) + + rclpy.init() + + try: + executor = rclpy.executors.SingleThreadedExecutor() + rclpy.spin(IsolatedListener(), executor) + finally: + # NOTE(hidmic): try_shutdown raises AttributeError + # Need https://github.com/ros2/rclpy/pull/812 + pass # rclpy.try_shutdown() diff --git a/drake_ros_bazel_installed/tools/ros2/test/rmw_isolation_test.sh b/drake_ros_bazel_installed/tools/ros2/test/rmw_isolation_test.sh new file mode 100755 index 000000000..977bbbeb6 --- /dev/null +++ b/drake_ros_bazel_installed/tools/ros2/test/rmw_isolation_test.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +talker=$1 +listener=$2 +$talker --ros-args --disable-external-lib-logs -- & $listener --ros-args --disable-external-lib-logs -p timeout:=2.0 +rc=$?; kill $! +exit $rc diff --git a/drake_ros_bazel_installed/tools/ros2/test/talker.py b/drake_ros_bazel_installed/tools/ros2/test/talker.py new file mode 100644 index 000000000..3796137ba --- /dev/null +++ b/drake_ros_bazel_installed/tools/ros2/test/talker.py @@ -0,0 +1,27 @@ +import rclpy +import rclpy.node +import std_msgs.msg + + +class Talker(rclpy.node.Node): + + def __init__(self): + super().__init__('talker') + self._publisher = self.create_publisher( + std_msgs.msg.String, 'topic', 10) + self._timer = self.create_timer(0.1, self._timer_callback) + + def _timer_callback(self): + msg = std_msgs.msg.String() + msg.data = 'Howdy' + self._publisher.publish(msg) + +if __name__ == '__main__': + rclpy.init() + + try: + rclpy.spin(Talker()) + except KeyboardInterrupt: + pass + finally: + rclpy.shutdown() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel index 892cf1e96..cc844a317 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel +++ b/drake_ros_bazel_installed/tools/skylark/ros2/BUILD.bazel @@ -1,14 +1 @@ -# -*- mode: python -*- -# vi: set ft=python : - -py_binary( - name = "generate_fastrtps_profile", - srcs = ["generate_fastrtps_profile.py"], - visibility = ["//visibility:public"], -) - -py_binary( - name = "generate_cyclonedds_profile", - srcs = ["generate_cyclonedds_profile.py"], - visibility = ["//visibility:public"], -) +# Empty build file to mark package boundaries. diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_cyclonedds_profile.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_cyclonedds_profile.py deleted file mode 100644 index f4a346001..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_cyclonedds_profile.py +++ /dev/null @@ -1,64 +0,0 @@ -import argparse -import sys - -import xml.dom.minidom as minidom -import xml.etree.ElementTree as ET - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument('--discovery-multicast-address') - parser.add_argument('--discovery-multicast-port-offset') - parser.add_argument('--discovery-unicast-port-offset') - parser.add_argument('--user-multicast-port-offset') - parser.add_argument('--user-unicast-port-offset') - parser.add_argument('--domain-gain') - args = parser.parse_args() - - builder = ET.TreeBuilder() - xmlns = 'http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles' - schema_location = ( - 'https://cdds.io/config https://raw.githubusercontent.com' - '/eclipse-cyclonedds/cyclonedds/master/etc/cyclonedds.xsd') - builder.start('CycloneDDS', { - 'xmlns:xsi': xmlns, 'xsi:schemaLocation': schema_location}) - builder.start('Domain', {'id': 'any'}) - builder.start('Discovery') - if args.discovery_multicast_address: - builder.start('SPDPMulticastAddress') - builder.data(args.discovery_multicast_address) - builder.end('SPDPMulticastAddress') - builder.start('Ports') - if args.domain_gain: - builder.start('DomainGain') - builder.data(args.domain_gain) - builder.end('DomainGain') - if args.discovery_multicast_port_offset: - builder.start('MulticastMetaOffset') - builder.data(args.discovery_multicast_port_offset) - builder.end('MulticastMetaOffset') - if args.user_multicast_port_offset: - builder.start('MulticastDataOffset') - builder.data(args.user_multicast_port_offset) - builder.end('MulticastDataOffset') - if args.discovery_unicast_port_offset: - builder.start('UnicastMetaOffset') - builder.data(args.discovery_unicast_port_offset) - builder.end('UnicastMetaOffset') - if args.user_unicast_port_offset: - builder.start('UnicastDataOffset') - builder.data(args.user_unicast_port_offset) - builder.end('UnicastDataOffset') - builder.end('Ports') - builder.end('Discovery') - builder.end('Domain') - builder.end('CycloneDDS') - tree = builder.close() - inline_xml = ET.tostring(tree, encoding='utf-8') - dom = minidom.parseString(inline_xml) - pretty_xml = dom.toprettyxml(indent=' ' * 4, encoding='utf-8') - sys.stdout.buffer.write(pretty_xml) - - -if __name__ == '__main__': - main() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_fastrtps_profile.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_fastrtps_profile.py deleted file mode 100644 index 956b63296..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_fastrtps_profile.py +++ /dev/null @@ -1,104 +0,0 @@ -import argparse -import sys - -import xml.dom.minidom as minidom -import xml.etree.ElementTree as ET - - -def build_udpv4_locator(builder, locator): - builder.start('locator') - builder.start('udpv4') - ip, _, port = locator.rpartition(':') - if ip: - builder.start('address') - builder.data(ip) - builder.end('address') - builder.start('port') - builder.data(port) - builder.end('port') - builder.end('udpv4') - builder.end('locator') - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument('profile_name') - parser.add_argument( - '--use-as-default', action='store_true', default=False - ) - parser.add_argument( - '--metatraffic-multicast-locator', - dest='metatraffic_multicast_locators', - action='append', default=[] - ) - parser.add_argument( - '--metatraffic-unicast-locator', - dest='metatraffic_unicast_locators', - action='append', default=[] - ) - parser.add_argument( - '--unicast-locator', - dest='unicast_locators', - action='append', default=[] - ) - args = parser.parse_args() - - transport_name = args.profile_name + '_transport' - - builder = ET.TreeBuilder() - xmlns = 'http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles' - builder.start('profiles', {'xmlns': xmlns}) - builder.start('transport_descriptors') - builder.start('transport_descriptor') - builder.start('transport_id') - builder.data(transport_name) - builder.end('transport_id') - builder.start('type') - builder.data('UDPv4') - builder.end('type') - builder.end('transport_descriptor') - builder.end('transport_descriptors') - builder.start('participant', { - 'profile_name': args.profile_name, - 'is_default_profile': str(args.use_as_default).lower() - }) - builder.start('rtps') - if args.metatraffic_multicast_locators or \ - args.metatraffic_unicast_locators: - builder.start('builtin') - if args.metatraffic_multicast_locators: - builder.start('metatrafficMulticastLocatorList') - for locator in args.metatraffic_multicast_locators: - build_udpv4_locator(builder, locator) - builder.end('metatrafficMulticastLocatorList') - if args.metatraffic_unicast_locators: - builder.start('metatrafficUnicastLocatorList') - for locator in args.metatraffic_unicast_locators: - build_udpv4_locator(builder, locator) - builder.end('metatrafficUnicastLocatorList') - builder.end('builtin') - if args.unicast_locators: - builder.start('defaultUnicastLocatorList') - for locator in args.unicast_locators: - build_udpv4_locator(builder, locator) - builder.end('defaultUnicastLocatorList') - builder.start('userTransports') - builder.start('transport_id') - builder.data(transport_name) - builder.end('transport_id') - builder.end('userTransports') - builder.start('useBuiltinTransports') - builder.data('false') - builder.end('useBuiltinTransports') - builder.end('rtps') - builder.end('participant') - builder.end('profiles') - tree = builder.close() - inline_xml = ET.tostring(tree, encoding='utf-8') - dom = minidom.parseString(inline_xml) - pretty_xml = dom.toprettyxml(indent=' ' * 4, encoding='utf-8') - sys.stdout.buffer.write(pretty_xml) - - -if __name__ == '__main__': - main() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl index ae24c4052..53ff1bdea 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl @@ -8,26 +8,3 @@ def incorporate_rmw_implementation(kwargs, env_changes, rmw_implementation): "RMW_IMPLEMENTATION": ["replace", rmw_implementation] }) return kwargs, env_changes - -def incorporate_fastrtps_profile(kwargs, env_changes, profile_name): - kwargs["data"] = kwargs.get("data", []) + [profile_name] - profile_path = "{}/{}".format(native.package_name(), profile_name) - if native.repository_name() != "@": - repository_name = native.repository_name().lstrip("@") - profile_path = "{}/{}".format(repository_name, profile_path) - env_changes = dict(env_changes) - env_changes.update({ - "FASTRTPS_DEFAULT_PROFILES_FILE": ["path-replace", profile_path] - }) - return kwargs, env_changes - -def incorporate_cyclonedds_profile(kwargs, env_changes, profile_name): - kwargs["data"] = kwargs.get("data", []) + [profile_name] - profile_path = "{}/{}".format(native.package_name(), profile_name) - if native.repository_name() != "@": - repository_name = native.repository_name().lstrip("@") - profile_path = "{}/{}".format(repository_name, profile_path) - profile_uri = "file://$PWD/" + profile_path - env_changes = dict(env_changes) - env_changes.update({"CYCLONEDDS_URI": ["replace", profile_uri]}) - return kwargs, env_changes diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl index 108c1abb8..52a057638 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl @@ -6,8 +6,6 @@ load( ) load( "@REPOSITORY_ROOT@:common.bzl", - "incorporate_cyclonedds_profile", - "incorporate_fastrtps_profile", "incorporate_rmw_implementation", ) load( @@ -18,11 +16,6 @@ load( "@drake_ros//tools/skylark:kwargs.bzl", "keep_common" ) -load( - "@drake_ros//tools/skylark/ros2:rmw.bzl", - "generate_isolated_cyclonedds_profile", - "generate_isolated_fastrtps_profile", -) def ros_cc_binary( name, rmw_implementation = None, cc_binary_rule = native.cc_binary, **kwargs @@ -55,21 +48,6 @@ def ros_cc_binary( binary_kwargs, binary_env_changes, rmw_implementation = rmw_implementation ) - if "block-network" in kwargs.get("tags", []): - if "fastrtps" in rmw_implementation: - profile_name = name + ".fastrtps_profile.xml" - generate_isolated_fastrtps_profile(profile_name) - binary_kwargs, binary_env_changes = \ - incorporate_fastrtps_profile( - binary_kwargs, binary_env_changes, profile_name - ) - if "cyclonedds" in rmw_implementation: - profile_name = name + ".cyclonedds_profile.xml" - generate_isolated_cyclonedds_profile(profile_name) - binary_kwargs, binary_env_changes = \ - incorporate_cyclonedds_profile( - binary_kwargs, binary_env_changes, profile_name - ) cc_binary_rule( name = binary_name, @@ -119,21 +97,6 @@ def ros_cc_test( test_kwargs, test_env_changes, rmw_implementation = rmw_implementation ) - if "block-network" in kwargs.get("tags", []): - if "fastrtps" in rmw_implementation: - profile_name = name + ".fastrtps_profile.xml" - generate_isolated_fastrtps_profile(profile_name) - test_kwargs, test_env_changes = \ - incorporate_fastrtps_profile( - test_kwargs, test_env_changes, profile_name - ) - if "cyclonedds" in rmw_implementation: - profile_name = name + ".cyclonedds_profile.xml" - generate_isolated_cyclonedds_profile(profile_name) - test_kwargs, test_env_changes = \ - incorporate_cyclonedds_profile( - test_kwargs, test_env_changes, profile_name - ) cc_test_rule( name = test_name, diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl index 22266a1c5..08af08cef 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl @@ -6,8 +6,6 @@ load( ) load( "@REPOSITORY_ROOT@:common.bzl", - "incorporate_cyclonedds_profile", - "incorporate_fastrtps_profile", "incorporate_rmw_implementation", ) load( @@ -18,11 +16,6 @@ load( "@drake_ros//tools/skylark:kwargs.bzl", "keep_common" ) -load( - "@drake_ros//tools/skylark/ros2:rmw.bzl", - "generate_isolated_cyclonedds_profile", - "generate_isolated_fastrtps_profile", -) def ros_py_import( name, executable, rmw_implementation = None, py_binary_rule = native.py_binary, **kwargs @@ -50,14 +43,6 @@ def ros_py_import( kwargs, env_changes, rmw_implementation = rmw_implementation ) - if "fastrtps" in rmw_implementation: - if "block-network" in kwargs.get("tags", []): - profile_name = name + ".fastrtps_profile.xml" - generate_isolated_fastrtps_profile(profile_name) - kwargs, env_changes = \ - incorporate_fastrtps_profile( - kwargs, env_changes, profile_name - ) shim_name = name + "_shim.py" shim_kwargs = keep_common(kwargs) @@ -108,21 +93,6 @@ def ros_py_binary( binary_kwargs, binary_env_changes, rmw_implementation = rmw_implementation ) - if "block-network" in kwargs.get("tags", []): - if "fastrtps" in rmw_implementation: - profile_name = name + ".fastrtps_profile.xml" - generate_isolated_fastrtps_profile(profile_name) - binary_kwargs, binary_env_changes = \ - incorporate_fastrtps_profile( - binary_kwargs, binary_env_changes, profile_name - ) - if "cyclonedds" in rmw_implementation: - profile_name = name + ".cyclonedds_profile.xml" - generate_isolated_cyclonedds_profile(profile_name) - binary_kwargs, binary_env_changes = \ - incorporate_cyclonedds_profile( - binary_kwargs, binary_env_changes, profile_name - ) py_binary_rule( name = binary_name, @@ -178,21 +148,6 @@ def ros_py_test( test_kwargs, test_env_changes, rmw_implementation = rmw_implementation, ) - if "block-network" in kwargs.get("tags", []): - if "fastrtps" in rmw_implementation: - profile_name = name + ".fastrtps_profile.xml" - generate_isolated_fastrtps_profile(profile_name) - test_kwargs, test_env_changes = \ - incorporate_fastrtps_profile( - test_kwargs, test_env_changes, profile_name - ) - if "cyclonedds" in rmw_implementation: - profile_name = name + ".cyclonedds_profile.xml" - generate_isolated_cyclonedds_profile(profile_name) - test_kwargs, test_env_changes = \ - incorporate_cyclonedds_profile( - test_kwargs, test_env_changes, profile_name - ) py_test_rule( name = test_name, diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl deleted file mode 100644 index 6071c3f96..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/rmw.bzl +++ /dev/null @@ -1,60 +0,0 @@ -# -*- python -*- -# vi: set ft=python : - -BASE_PORT = 16384 - -def to_bitstrings(integer, *bitsizes): - bitstrings = [] - for bitsize in reversed(bitsizes): - bitstrings.append(integer & ((1 << bitsize) - 1)) - integer = integer >> bitsize - return reversed(bitstrings) - -def generate_isolated_fastrtps_profile(name): - tool = "//tools/skylark/ros2:generate_fastrtps_profile" - label_name = "{}//{}:{}".format( - native.repository_name(), native.package_name(), name - ) - bitstrings = to_bitstrings(hash(label_name), 8, 8, 8, 12, 12, 12) - args = [ - name, - "--metatraffic-multicast-locator {}:{}".format( - ".".join(["239"] + [str(octet) for octet in bitstrings[:3]]), - BASE_PORT + bitstrings[3] - ), - "--metatraffic-unicast-locator {}".format(BASE_PORT + bitstrings[4]), - "--unicast-locator {}".format(BASE_PORT + bitstrings[5]), - "--use-as-default", - ] - - native.genrule( - name = "gen_" + name, - outs = [name], - cmd = "./$(location {}) {} > $@".format(tool, " ".join(args)), - tools = [tool], - output_to_bindir = True, - ) - -def generate_isolated_cyclonedds_profile(name): - tool = "//tools/skylark/ros2:generate_cyclonedds_profile" - label_name = "{}//{}:{}".format( - native.repository_name(), native.package_name(), name - ) - bitstrings = to_bitstrings(hash(label_name), 8, 8, 8, 10, 10, 10, 10) - args = [ - "--discovery-multicast-address {}".format( - ".".join(["239"] + [str(octet) for octet in bitstrings[:3]])), - "--discovery-multicast-port-offset {}".format(bitstrings[3]), - "--user-multicast-port-offset {}".format(bitstrings[4]), - "--discovery-unicast-port-offset {}".format(bitstrings[5]), - "--user-unicast-port-offset {}".format(bitstrings[6]), - "--domain-gain {}".format(0), # ignore domain IDs - ] - - native.genrule( - name = "gen_" + name, - outs = [name], - cmd = "./$(location {}) {} > $@".format(tool, " ".join(args)), - tools = [tool], - output_to_bindir = True, - ) From c6e6f0bda682a8777e6dda88283417807001491e Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 25 Oct 2021 15:47:53 -0300 Subject: [PATCH 056/107] Fix ros_cc_test() kwargs handling Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl index 52a057638..646f180b7 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl @@ -89,8 +89,9 @@ def ros_cc_test( Additional keyword arguments are forwarded to the `cc_test_rule`. """ test_name = "_" + name + test_kwargs = dict(kwargs) + test_kwargs.update(tags = ["manual"] + test_kwargs.get("tags", [])) test_env_changes = dict(RUNTIME_ENVIRONMENT) - kwargs["tags"] = ["manual"] + kwargs.get("tags", []) if rmw_implementation: test_kwargs, test_env_changes = \ incorporate_rmw_implementation( From fd50e23f6ee772fd5cc18343051050ccd4652d35 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 25 Oct 2021 16:09:19 -0300 Subject: [PATCH 057/107] Use plain binaries as inner ros_*_test rules' executable. Signed-off-by: Michel Hidalgo --- .../tools/skylark/kwargs.bzl | 13 +++++++ .../ros2/resources/bazel/ros_cc.bzl.tpl | 39 +++++++++++-------- .../ros2/resources/bazel/ros_py.bzl.tpl | 38 ++++++++++-------- 3 files changed, 56 insertions(+), 34 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/kwargs.bzl b/drake_ros_bazel_installed/tools/skylark/kwargs.bzl index 3caa473e3..41d6f7ff9 100644 --- a/drake_ros_bazel_installed/tools/skylark/kwargs.bzl +++ b/drake_ros_bazel_installed/tools/skylark/kwargs.bzl @@ -17,3 +17,16 @@ _COMMON_KWARGS = [ def keep_common(kwargs): """Fetch keyword arguments common to all rules from `kwargs`.""" return {key: value for key, value in kwargs.items() if key in _COMMON_KWARGS} + +_TEST_KWARGS = [ + "env_inherit", + "flaky", + "local", + "shard_count", + "size", + "timeout", +] + +def remove_test_specific(kwargs): + """Filter keyword arguments specific to test rules from `kwargs`.""" + return {key: value for key, value in kwargs.items() if key not in _TEST_KWARGS} diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl index 646f180b7..a128cfcea 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl @@ -14,7 +14,8 @@ load( ) load( "@drake_ros//tools/skylark:kwargs.bzl", - "keep_common" + "keep_common", + "remove_test_specific" ) def ros_cc_binary( @@ -72,51 +73,55 @@ def ros_cc_binary( cc_binary_rule(name = name, **kwargs) def ros_cc_test( - name, rmw_implementation = None, cc_test_rule = native.cc_test, **kwargs + name, rmw_implementation = None, + cc_binary_rule = native.cc_binary, + cc_test_rule = native.cc_test, **kwargs ): """ Builds a C/C++ test and wraps it with a shim that will inject the minimal runtime environment necessary for execution when depending on targets from this ROS 2 local repository. - Equivalent to the cc_test() rule, which this rule decorates. + Equivalent to the cc_test() rule. Args: name: C/C++ test target name rmw_implementation: optional RMW implementation to run against + cc_binary_rule: optional cc_binary() rule override. cc_test_rule: optional cc_test() rule override - Additional keyword arguments are forwarded to the `cc_test_rule`. + Additional keyword arguments are forwarded to the `cc_test_rule` and to the + `cc_binary_rule` (minus the test specific ones). """ - test_name = "_" + name - test_kwargs = dict(kwargs) - test_kwargs.update(tags = ["manual"] + test_kwargs.get("tags", [])) - test_env_changes = dict(RUNTIME_ENVIRONMENT) + binary_name = "_" + name + binary_kwargs = remove_test_specific(kwargs) + binary_kwargs.update(testonly = True) + binary_env_changes = dict(RUNTIME_ENVIRONMENT) if rmw_implementation: - test_kwargs, test_env_changes = \ + binary_kwargs, test_env_changes = \ incorporate_rmw_implementation( - test_kwargs, test_env_changes, + binary_kwargs, binary_env_changes, rmw_implementation = rmw_implementation ) - cc_test_rule( - name = test_name, - **test_kwargs + cc_binary_rule( + name = binary_name, + **binary_kwargs ) - shim_name = test_name + "_shim.cc" + shim_name = binary_name + "_shim.cc" shim_kwargs = keep_common(kwargs) shim_kwargs.update(testonly = True) dload_cc_shim( name = shim_name, - target = ":" + test_name, - env_changes = test_env_changes, + target = ":" + binary_name, + env_changes = binary_env_changes, **shim_kwargs ) kwargs.update( srcs = [shim_name], - data = [":" + test_name], + data = [":" + binary_name], deps = ["@bazel_tools//tools/cpp/runfiles"], tags = ["nolint"] + kwargs.get("tags", []) ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl index 08af08cef..0a19f1e56 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl @@ -14,7 +14,8 @@ load( ) load( "@drake_ros//tools/skylark:kwargs.bzl", - "keep_common" + "keep_common", + "remove_test_specific" ) def ros_py_import( @@ -121,7 +122,9 @@ def ros_py_binary( py_binary_rule(name = name, **kwargs) def ros_py_test( - name, rmw_implementation = None, py_test_rule = native.py_test, **kwargs + name, rmw_implementation = None, + py_binary_rule = native.py_binary, + py_test_rule = native.py_test, **kwargs ): """ Builds a Python test and wraps it with a shim that will inject the minimal @@ -133,41 +136,42 @@ def ros_py_test( Args: name: Python test target name rmw_implementation: optional RMW implementation to run against + py_binary_rule: optional py_binary() rule override py_test_rule: optional py_test() rule override - Additional keyword arguments are forwarded to the `py_test_rule`. + Additional keyword arguments are forwarded to the `py_test_rule` and to the + `py_binary_rule` (minus the test specific ones). """ - test_name = "_" + name - test_kwargs = dict(kwargs) - test_kwargs.update( - tags = ["manual"] + test_kwargs.get("tags", [])) - test_env_changes = dict(RUNTIME_ENVIRONMENT) + binary_name = "_" + name + binary_kwargs = remove_test_specific(kwargs) + binary_kwargs.update(testonly = True) + binary_env_changes = dict(RUNTIME_ENVIRONMENT) if rmw_implementation: - test_kwargs, test_env_changes = \ + binary_kwargs, binary_env_changes = \ incorporate_rmw_implementation( - test_kwargs, test_env_changes, + binary_kwargs, binary_env_changes, rmw_implementation = rmw_implementation, ) - py_test_rule( - name = test_name, - **test_kwargs + py_binary_rule( + name = binary_name, + **binary_kwargs ) - shim_name = test_name + "_shim.py" + shim_name = binary_name + "_shim.py" shim_kwargs = keep_common(kwargs) shim_kwargs.update(testonly = True) dload_py_shim( name = shim_name, - target = ":" + test_name, - env_changes = test_env_changes, + target = ":" + binary_name, + env_changes = binary_env_changes, **shim_kwargs ) kwargs.update( srcs = [shim_name], main = shim_name, - data = [":" + test_name], + data = [":" + binary_name], deps = ["@bazel_tools//tools/python/runfiles"], tags = ["nolint"] + kwargs.get("tags", []) ) From f35b7839968854e18ac6cd8a2b5e0474499b2c76 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 25 Oct 2021 16:42:25 -0300 Subject: [PATCH 058/107] Minimize .bzl templates in local ros2 repositories. Signed-off-by: Michel Hidalgo --- .../skylark/ros2/generate_repository_files.py | 42 +---------- .../ros2/{ => resources/bazel}/common.bzl | 12 ++++ .../ros2/resources/bazel/common.bzl.tpl | 10 --- .../ros2/resources/bazel/distro.bzl.tpl | 2 + .../overlay_executable.bazel.tpl | 0 .../{snippets => }/package_alias.bazel.tpl | 0 .../package_cc_library.bazel.tpl | 0 .../package_interfaces_filegroup.bazel.tpl | 0 .../package_meta_py_library.bazel.tpl | 0 .../package_py_library.bazel.tpl | 0 .../package_py_library_with_cc_libs.bazel.tpl | 0 .../package_share_filegroup.bazel.tpl | 0 .../ros2/resources/bazel/prologue.bazel | 7 ++ .../bazel/{ros_cc.bzl.tpl => ros_cc.bzl} | 16 ++--- .../bazel/{ros_py.bzl.tpl => ros_py.bzl} | 16 ++--- .../bazel/{rosidl.bzl.tpl => rosidl.bzl} | 72 ++++++++++--------- .../bazel/snippets/prologue.bazel.tpl | 7 -- .../tools/skylark/ros2/ros2.bzl | 34 +++++---- .../tools/skylark/ros2/ros2bzl/templates.py | 49 ++++--------- 19 files changed, 110 insertions(+), 157 deletions(-) rename drake_ros_bazel_installed/tools/skylark/ros2/{ => resources/bazel}/common.bzl (69%) delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl rename drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/{snippets => }/overlay_executable.bazel.tpl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/{snippets => }/package_alias.bazel.tpl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/{snippets => }/package_cc_library.bazel.tpl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/{snippets => }/package_interfaces_filegroup.bazel.tpl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/{snippets => }/package_meta_py_library.bazel.tpl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/{snippets => }/package_py_library.bazel.tpl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/{snippets => }/package_py_library_with_cc_libs.bazel.tpl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/{snippets => }/package_share_filegroup.bazel.tpl (100%) create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/prologue.bazel rename drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/{ros_cc.bzl.tpl => ros_cc.bzl} (98%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/{ros_py.bzl.tpl => ros_py.bzl} (98%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/{rosidl.bzl.tpl => rosidl.bzl} (94%) delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/prologue.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py index 23fe0a81c..078945ca0 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -38,8 +38,6 @@ import collect_ament_python_package_direct_properties from ros2bzl.scrapping.ament_python import PackageNotFoundError -from ros2bzl.templates import configure_cc_tools -from ros2bzl.templates import configure_common from ros2bzl.templates import configure_distro from ros2bzl.templates import configure_executable_imports from ros2bzl.templates import configure_package_meta_py_library @@ -49,9 +47,7 @@ from ros2bzl.templates import configure_package_py_library from ros2bzl.templates import configure_package_share_filegroup from ros2bzl.templates import configure_package_interfaces_filegroup -from ros2bzl.templates import configure_py_tools from ros2bzl.templates import configure_prologue -from ros2bzl.templates import configure_rosidl_tools from ros2bzl.resources import load_resource @@ -114,33 +110,9 @@ def parse_arguments(): return args -def generate_distro_file(packages): +def generate_distro_file(repo_name, distro): with open('distro.bzl', 'w') as fd: - template, config = configure_distro(packages) - fd.write(interpolate(template, config) + '\n') - - -def generate_common_file(repo_name): - with open('common.bzl', 'w') as fd: - template, config = configure_common(repo_name) - fd.write(interpolate(template, config) + '\n') - - -def generate_cc_tools_file(repo_name): - with open('ros_cc.bzl', 'w') as fd: - template, config = configure_cc_tools(repo_name) - fd.write(interpolate(template, config) + '\n') - - -def generate_py_tools_file(repo_name): - with open('ros_py.bzl', 'w') as fd: - template, config = configure_py_tools(repo_name) - fd.write(interpolate(template, config) + '\n') - - -def generate_rosidl_tools_file(repo_name): - with open('rosidl.bzl', 'w') as fd: - template, config = configure_rosidl_tools(repo_name) + template, config = configure_distro(repo_name, distro) fd.write(interpolate(template, config) + '\n') @@ -250,15 +222,7 @@ def main(): args.repository_name, distro, cache, args.extras, args.sandbox) - generate_distro_file(distro) - - generate_common_file(args.repository_name) - - generate_cc_tools_file(args.repository_name) - - generate_py_tools_file(args.repository_name) - - generate_rosidl_tools_file(args.repository_name) + generate_distro_file(args.repository_name, distro) if __name__ == '__main__': diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/common.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl similarity index 69% rename from drake_ros_bazel_installed/tools/skylark/ros2/common.bzl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl index 9bfe7bed5..a3d503679 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/common.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl @@ -1,3 +1,6 @@ +# -*- python -*- + +load(":distro.bzl", "REPOSITORY_ROOT") def share_filegroup(name, share_directories): native.filegroup( @@ -26,3 +29,12 @@ def interfaces_filegroup(name, share_directory): "{}/**/*.action".format(share_directory), ]) ) + +def incorporate_rmw_implementation(kwargs, env_changes, rmw_implementation): + target = REPOSITORY_ROOT + ":%s_cc" % rmw_implementation + kwargs["data"] = kwargs.get("data", []) + [target] + env_changes = dict(env_changes) + env_changes.update({ + "RMW_IMPLEMENTATION": ["replace", rmw_implementation] + }) + return kwargs, env_changes diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl deleted file mode 100644 index 53ff1bdea..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl.tpl +++ /dev/null @@ -1,10 +0,0 @@ -# -*- python -*- - -def incorporate_rmw_implementation(kwargs, env_changes, rmw_implementation): - target = "@REPOSITORY_ROOT@:%s_cc" % rmw_implementation - kwargs["data"] = kwargs.get("data", []) + [target] - env_changes = dict(env_changes) - env_changes.update({ - "RMW_IMPLEMENTATION": ["replace", rmw_implementation] - }) - return kwargs, env_changes diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/distro.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/distro.bzl.tpl index 69b538214..1cb010e84 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/distro.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/distro.bzl.tpl @@ -1,5 +1,7 @@ AVAILABLE_TYPESUPPORT_LIST = @AVAILABLE_TYPESUPPORT_LIST@ +REPOSITORY_ROOT = @REPOSITORY_ROOT@ + # Prepend full paths to not break workspace overlays RUNTIME_ENVIRONMENT = { "AMENT_PREFIX_PATH": ["path-prepend"] + @AMENT_PREFIX_PATH@, diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/overlay_executable.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/overlay_executable.bazel.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/overlay_executable.bazel.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/overlay_executable.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_alias.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_alias.bazel.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_alias.bazel.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_alias.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_cc_library.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_cc_library.bazel.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_cc_library.bazel.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_cc_library.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_interfaces_filegroup.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_interfaces_filegroup.bazel.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_interfaces_filegroup.bazel.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_interfaces_filegroup.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_meta_py_library.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_meta_py_library.bazel.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_meta_py_library.bazel.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_meta_py_library.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_py_library.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_py_library.bazel.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_py_library.bazel.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_py_library.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_py_library_with_cc_libs.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_py_library_with_cc_libs.bazel.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_py_library_with_cc_libs.bazel.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_py_library_with_cc_libs.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_share_filegroup.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_share_filegroup.bazel.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/package_share_filegroup.bazel.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_share_filegroup.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/prologue.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/prologue.bazel new file mode 100644 index 000000000..d2267b1a9 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/prologue.bazel @@ -0,0 +1,7 @@ +# -*- python -*- + +package(default_visibility = ["//visibility:public"]) + +load(":common.bzl", "interfaces_filegroup") +load(":common.bzl", "share_filegroup") +load(":ros_py.bzl", "ros_py_import") diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl similarity index 98% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl index a128cfcea..3c2e8707a 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl @@ -1,13 +1,5 @@ # -*- python -*- -load( - "@REPOSITORY_ROOT@:distro.bzl", - "RUNTIME_ENVIRONMENT" -) -load( - "@REPOSITORY_ROOT@:common.bzl", - "incorporate_rmw_implementation", -) load( "@drake_ros//tools/skylark:dload_cc.bzl", "dload_cc_shim" @@ -17,6 +9,14 @@ load( "keep_common", "remove_test_specific" ) +load( + ":common.bzl", + "incorporate_rmw_implementation", +) +load( + ":distro.bzl", + "RUNTIME_ENVIRONMENT" +) def ros_cc_binary( name, rmw_implementation = None, cc_binary_rule = native.cc_binary, **kwargs diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl similarity index 98% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl index 0a19f1e56..a664012d5 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl @@ -1,13 +1,5 @@ # -*- python -*- -load( - "@REPOSITORY_ROOT@:distro.bzl", - "RUNTIME_ENVIRONMENT" -) -load( - "@REPOSITORY_ROOT@:common.bzl", - "incorporate_rmw_implementation", -) load( "@drake_ros//tools/skylark:dload_py.bzl", "dload_py_shim" @@ -17,6 +9,14 @@ load( "keep_common", "remove_test_specific" ) +load( + ":common.bzl", + "incorporate_rmw_implementation", +) +load( + ":distro.bzl", + "RUNTIME_ENVIRONMENT" +) def ros_py_import( name, executable, rmw_implementation = None, py_binary_rule = native.py_binary, **kwargs diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl similarity index 94% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl index c6f12e7c9..0b399a25c 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl @@ -1,7 +1,11 @@ # -*- python -*- -load("@REPOSITORY_ROOT@:distro.bzl", "AVAILABLE_TYPESUPPORT_LIST") load("@python_dev//:version.bzl", "PYTHON_EXTENSION_SUFFIX") +load( + ":distro.bzl", + "AVAILABLE_TYPESUPPORT_LIST", + "REPOSITORY_ROOT" +) def _as_idl_tuple(file): """ @@ -55,7 +59,7 @@ rosidl_generate_genrule = rule( includes = attr.label_list(mandatory = False), output_dir = attr.string(mandatory = False), _tool = attr.label( - default = "@REPOSITORY_ROOT@:rosidl", + default = REPOSITORY_ROOT + ":rosidl", executable = True, cfg = "exec" ) ), @@ -113,7 +117,7 @@ rosidl_translate_genrule = rule( includes = attr.label_list(mandatory = False), output_dir = attr.string(mandatory = False), _tool = attr.label( - default = "@REPOSITORY_ROOT@:rosidl", + default = REPOSITORY_ROOT + ":rosidl", executable = True, cfg = "exec" ) ), @@ -226,9 +230,9 @@ def rosidl_c_library( ) deps = deps + [ - "@REPOSITORY_ROOT@:rcutils_cc", - "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", - "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", + REPOSITORY_ROOT + ":rcutils_cc", + REPOSITORY_ROOT + ":rosidl_runtime_c_cc", + REPOSITORY_ROOT + ":rosidl_typesupport_interface_cc", ] cc_library_rule( @@ -283,7 +287,7 @@ def rosidl_cc_library( hdrs = generated_cc_headers, includes = [include], deps = deps + [ - "@REPOSITORY_ROOT@:rosidl_runtime_cpp_cc" + REPOSITORY_ROOT + ":rosidl_runtime_cpp_cc" ], **kwargs ) @@ -367,10 +371,10 @@ def rosidl_py_library( c_typesupport_extension_deps = c_deps + [ c_label(name), - "@REPOSITORY_ROOT@:rosidl_generator_py_cc", - "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", - "@REPOSITORY_ROOT@:rosidl_typesupport_c_cc", - "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", + REPOSITORY_ROOT + ":rosidl_generator_py_cc", + REPOSITORY_ROOT + ":rosidl_runtime_c_cc", + REPOSITORY_ROOT + ":rosidl_typesupport_c_cc", + REPOSITORY_ROOT + ":rosidl_typesupport_interface_cc", "@python_dev//:libs", ] py_data = [] @@ -452,11 +456,11 @@ def rosidl_typesupport_fastrtps_cc_library( srcs = generated_cc_sources, deps = deps + [ ":" + name + "_hdrs", - "@REPOSITORY_ROOT@:fastcdr_cc", - "@REPOSITORY_ROOT@:rmw_cc", - "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", - "@REPOSITORY_ROOT@:rosidl_typesupport_fastrtps_cpp_cc", - "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", + REPOSITORY_ROOT + ":fastcdr_cc", + REPOSITORY_ROOT + ":rmw_cc", + REPOSITORY_ROOT + ":rosidl_runtime_c_cc", + REPOSITORY_ROOT + ":rosidl_typesupport_fastrtps_cpp_cc", + REPOSITORY_ROOT + ":rosidl_typesupport_interface_cc", ], linkshared = True, **kwargs @@ -519,12 +523,12 @@ def rosidl_typesupport_fastrtps_c_library( linkshared = True, deps = deps + [ ":" + name + "_hdrs", - "@REPOSITORY_ROOT@:fastcdr_cc", - "@REPOSITORY_ROOT@:rmw_cc", - "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", - "@REPOSITORY_ROOT@:rosidl_typesupport_fastrtps_c_cc", - "@REPOSITORY_ROOT@:rosidl_typesupport_fastrtps_cpp_cc", - "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", + REPOSITORY_ROOT + ":fastcdr_cc", + REPOSITORY_ROOT + ":rmw_cc", + REPOSITORY_ROOT + ":rosidl_runtime_c_cc", + REPOSITORY_ROOT + ":rosidl_typesupport_fastrtps_c_cc", + REPOSITORY_ROOT + ":rosidl_typesupport_fastrtps_cpp_cc", + REPOSITORY_ROOT + ":rosidl_typesupport_interface_cc", ], **kwargs ) @@ -586,7 +590,7 @@ def rosidl_typesupport_introspection_c_library( linkshared = True, deps = deps + [ ":" + name + "_hdrs", - "@REPOSITORY_ROOT@:rosidl_typesupport_introspection_c_cc", + REPOSITORY_ROOT + ":rosidl_typesupport_introspection_c_cc", ], **kwargs ) @@ -647,10 +651,10 @@ def rosidl_typesupport_introspection_cc_library( linkshared = True, deps = deps + [ ":" + name + "_hdrs", - "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", - "@REPOSITORY_ROOT@:rosidl_runtime_cpp_cc", - "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", - "@REPOSITORY_ROOT@:rosidl_typesupport_introspection_cpp_cc", + REPOSITORY_ROOT + ":rosidl_runtime_c_cc", + REPOSITORY_ROOT + ":rosidl_runtime_cpp_cc", + REPOSITORY_ROOT + ":rosidl_typesupport_interface_cc", + REPOSITORY_ROOT + ":rosidl_typesupport_introspection_cpp_cc", ], **kwargs ) @@ -707,9 +711,9 @@ def rosidl_typesupport_c_library( linkshared = True, data = typesupports.values(), deps = deps + [label + "_hdrs" for label in typesupports.values()] + [ - "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", - "@REPOSITORY_ROOT@:rosidl_typesupport_c_cc", - "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", + REPOSITORY_ROOT + ":rosidl_runtime_c_cc", + REPOSITORY_ROOT + ":rosidl_typesupport_c_cc", + REPOSITORY_ROOT + ":rosidl_typesupport_interface_cc", ], **kwargs ) @@ -763,10 +767,10 @@ def rosidl_typesupport_cc_library( includes = [include], linkshared = True, deps = deps + [label + "_hdrs" for label in typesupports.values()] + [ - "@REPOSITORY_ROOT@:rosidl_runtime_c_cc", - "@REPOSITORY_ROOT@:rosidl_runtime_cpp_cc", - "@REPOSITORY_ROOT@:rosidl_typesupport_cpp_cc", - "@REPOSITORY_ROOT@:rosidl_typesupport_interface_cc", + REPOSITORY_ROOT + ":rosidl_runtime_c_cc", + REPOSITORY_ROOT + ":rosidl_runtime_cpp_cc", + REPOSITORY_ROOT + ":rosidl_typesupport_cpp_cc", + REPOSITORY_ROOT + ":rosidl_typesupport_interface_cc", ], **kwargs ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/prologue.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/prologue.bazel.tpl deleted file mode 100644 index 7ad78df37..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/snippets/prologue.bazel.tpl +++ /dev/null @@ -1,7 +0,0 @@ -# -*- python -*- - -package(default_visibility = ["//visibility:public"]) - -load("@REPOSITORY_ROOT@:ros_py.bzl", "ros_py_import") -load("@drake_ros//tools/skylark/ros2:common.bzl", "interfaces_filegroup") -load("@drake_ros//tools/skylark/ros2:common.bzl", "share_filegroup") diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl index fedf4b3b2..81b0ea79a 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -2,24 +2,25 @@ load("@drake_ros//tools/skylark:execute.bzl", "execute_or_fail") -MANIFEST = [ +GENERATE_TOOL_RESOURCES_MANIFEST = [ "cmake_tools/packages.py", "cmake_tools/server_mode.py", "cmake_tools/__init__.py", - "resources/bazel/common.bzl.tpl", + "resources/bazel/common.bzl", + "resources/bazel/ros_cc.bzl", + "resources/bazel/ros_py.bzl", + "resources/bazel/rosidl.bzl", + "resources/bazel/distro.bzl.tpl", - "resources/bazel/ros_cc.bzl.tpl", - "resources/bazel/ros_py.bzl.tpl", - "resources/bazel/rosidl.bzl.tpl", - "resources/bazel/snippets/overlay_executable.bazel.tpl", - "resources/bazel/snippets/package_interfaces_filegroup.bazel.tpl", - "resources/bazel/snippets/package_cc_library.bazel.tpl", - "resources/bazel/snippets/package_meta_py_library.bazel.tpl", - "resources/bazel/snippets/package_py_library.bazel.tpl", - "resources/bazel/snippets/package_py_library_with_cc_libs.bazel.tpl", - "resources/bazel/snippets/package_share_filegroup.bazel.tpl", - "resources/bazel/snippets/prologue.bazel.tpl", + "resources/bazel/overlay_executable.bazel.tpl", + "resources/bazel/package_interfaces_filegroup.bazel.tpl", + "resources/bazel/package_cc_library.bazel.tpl", + "resources/bazel/package_meta_py_library.bazel.tpl", + "resources/bazel/package_py_library.bazel.tpl", + "resources/bazel/package_py_library_with_cc_libs.bazel.tpl", + "resources/bazel/package_share_filegroup.bazel.tpl", + "resources/bazel/prologue.bazel", "resources/cmake/ament_cmake_CMakeLists.txt.in", @@ -39,7 +40,12 @@ def _label(relpath): return Label("//tools/skylark/ros2:" + relpath) def _impl(repo_ctx): - for relpath in MANIFEST: + repo_ctx.symlink(_label("resources/bazel/common.bzl"), "common.bzl") + repo_ctx.symlink(_label("resources/bazel/ros_cc.bzl"), "ros_cc.bzl") + repo_ctx.symlink(_label("resources/bazel/ros_py.bzl"), "ros_py.bzl") + repo_ctx.symlink(_label("resources/bazel/rosidl.bzl"), "rosidl.bzl") + + for relpath in GENERATE_TOOL_RESOURCES_MANIFEST: repo_ctx.symlink(_label(relpath), relpath) repo_ctx.template( diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py index e9dfeccc9..7514795f2 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py @@ -41,7 +41,7 @@ def configure_package_share_filegroup(name, metadata, sandbox): shared_directories.append(sandbox(metadata['ament_index_directory'])) return ( target_name, - load_resource('bazel/snippets/package_share_filegroup.bazel.tpl'), + load_resource('bazel/package_share_filegroup.bazel.tpl'), to_starlark_string_dict({ 'name': target_name, 'share_directories': shared_directories }) @@ -51,7 +51,7 @@ def configure_package_share_filegroup(name, metadata, sandbox): def configure_package_interfaces_filegroup(name, metadata, sandbox): return ( name, - load_resource('bazel/snippets/package_interfaces_filegroup.bazel.tpl'), + load_resource('bazel/package_interfaces_filegroup.bazel.tpl'), to_starlark_string_dict({ 'name': name, 'share_directory': sandbox(metadata['share_directory']) }) @@ -114,7 +114,7 @@ def configure_package_cc_library(name, metadata, properties, dependencies, extra if label_or_path not in data ) - template_path = 'bazel/snippets/package_cc_library.bazel.tpl' + template_path = 'bazel/package_cc_library.bazel.tpl' config = { 'name': target_name, @@ -141,7 +141,7 @@ def configure_package_meta_py_library(name, metadata, dependencies): target_name = meta_py_name(name, metadata) return ( target_name, - load_resource('bazel/snippets/package_meta_py_library.bazel.tpl'), + load_resource('bazel/package_meta_py_library.bazel.tpl'), to_starlark_string_dict({'name': target_name, 'deps': deps}) ) @@ -153,7 +153,7 @@ def configure_package_py_library(name, metadata, properties, dependencies, extra sandbox(top_level) for _, top_level in properties['python_packages']] imports = [os.path.dirname(egg) for egg in eggs] - template = 'bazel/snippets/package_py_library.bazel.tpl' + template = 'bazel/package_py_library.bazel.tpl' config = { 'name': target_name, 'tops': tops, @@ -182,7 +182,7 @@ def configure_package_py_library(name, metadata, properties, dependencies, extra ] # Expose C/C++ libraries if any if 'cc_libraries' in properties: - template = 'bazel/snippets/package_py_library_with_cc_libs.bazel.tpl' + template = 'bazel/package_py_library_with_cc_libs.bazel.tpl' config.update({ 'cc_name': c_name(target_name, metadata), 'cc_libs': [sandbox(lib) for lib in properties['cc_libraries']], @@ -218,7 +218,7 @@ def configure_package_py_library(name, metadata, properties, dependencies, extra def configure_package_alias(name, target): return ( name, - load_resource('bazel/snippets/package_alias.bazel.tpl'), + load_resource('bazel/package_alias.bazel.tpl'), to_starlark_string_dict({'name': name, 'actual': ':' + target}) ) @@ -227,7 +227,7 @@ def configure_package_c_library_alias(name, metadata): target_name = c_name(name, metadata) return ( target_name, - load_resource('bazel/snippets/package_alias.bazel.tpl'), + load_resource('bazel/package_alias.bazel.tpl'), to_starlark_string_dict({ 'name': target_name, 'actual': cc_label(name, metadata) @@ -259,7 +259,7 @@ def configure_executable_imports( data = data + extras['data'][target_name] yield ( target_name, - load_resource('bazel/snippets/overlay_executable.bazel.tpl'), + load_resource('bazel/overlay_executable.bazel.tpl'), to_starlark_string_dict({ 'name': target_name, 'executable': sandbox(executable), @@ -280,36 +280,10 @@ def configure_package_executable_imports( def configure_prologue(repo_name): - return load_resource('bazel/snippets/prologue.bazel.tpl'), { - 'REPOSITORY_ROOT': '@{}//'.format(repo_name) - } - - -def configure_rosidl_tools(repo_name): - return load_resource('bazel/rosidl.bzl.tpl'), { - 'REPOSITORY_ROOT': '@{}//'.format(repo_name) - } - - -def configure_cc_tools(repo_name): - return load_resource('bazel/ros_cc.bzl.tpl'), { - 'REPOSITORY_ROOT': '@{}//'.format(repo_name) - } - - -def configure_py_tools(repo_name): - return load_resource('bazel/ros_py.bzl.tpl'), { - 'REPOSITORY_ROOT': '@{}//'.format(repo_name) - } - - -def configure_common(repo_name): - return load_resource('bazel/common.bzl.tpl'), { - 'REPOSITORY_ROOT': '@{}//'.format(repo_name) - } + return load_resource('bazel/prologue.bazel'), {} -def configure_distro(distro): +def configure_distro(repo_name, distro): typesupport_groups = [ 'rosidl_typesupport_c_packages', 'rosidl_typesupport_cpp_packages' @@ -323,4 +297,5 @@ def configure_distro(distro): for group in metadata.get('groups', []) ) ], + 'REPOSITORY_ROOT': '@{}//'.format(repo_name), }) From daad62929137e0ac315c7c8cca3210271b98e3a7 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 25 Oct 2021 17:06:54 -0300 Subject: [PATCH 059/107] Restrict rosidl_*_genrules output to stderr only Signed-off-by: Michel Hidalgo --- .../skylark/ros2/resources/bazel/rosidl.bzl | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl index 0b399a25c..368973c89 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl @@ -38,12 +38,13 @@ def _rosidl_generate_genrule_impl(ctx): args.add_all(ctx.files.includes, map_each=_as_include_flag, uniquify=True) args.add(ctx.attr.group) args.add_all(ctx.files.interfaces, map_each=_as_idl_tuple) - inputs, input_manifests = ctx.resolve_tools(tools = [ctx.attr._tool]) - inputs = inputs.to_list() + ctx.files.interfaces + ctx.files.includes - ctx.actions.run( - executable = ctx.executable._tool, + inputs = ctx.files.interfaces + ctx.files.includes + ctx.actions.run_shell( + tools = [ctx.executable._tool], arguments = [args], inputs = inputs, - input_manifests = input_manifests, + command = "{} $@ > /dev/null".format( + ctx.executable._tool.path + ), outputs = ctx.outputs.generated_sources, ) @@ -96,12 +97,13 @@ def _rosidl_translate_genrule_impl(ctx): args.add_all(ctx.files.includes, map_each=_as_include_flag, uniquify=True) args.add(ctx.attr.group) args.add_all(ctx.files.interfaces, map_each=_as_idl_tuple) - inputs, input_manifests = ctx.resolve_tools(tools = [ctx.attr._tool]) - inputs = inputs.to_list() + ctx.files.interfaces + ctx.files.includes - ctx.actions.run( - executable = ctx.executable._tool, + inputs = ctx.files.interfaces + ctx.files.includes + ctx.actions.run_shell( + tools = [ctx.executable._tool], arguments = [args], inputs = inputs, - input_manifests = input_manifests, + command = "{} $@ > /dev/null".format( + ctx.executable._tool.path + ), outputs = ctx.outputs.translated_interfaces ) From 8c1c2ed12d28ef079396e4d9d5dc6010b51cf97f Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 25 Oct 2021 17:10:56 -0300 Subject: [PATCH 060/107] Remove 'manual' tag from RMW isolation tests Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/tools/ros2/BUILD.bazel | 2 -- 1 file changed, 2 deletions(-) diff --git a/drake_ros_bazel_installed/tools/ros2/BUILD.bazel b/drake_ros_bazel_installed/tools/ros2/BUILD.bazel index 28f4fcbc0..d9c3d175a 100644 --- a/drake_ros_bazel_installed/tools/ros2/BUILD.bazel +++ b/drake_ros_bazel_installed/tools/ros2/BUILD.bazel @@ -61,7 +61,6 @@ sh_test( "$(location :isolated_listener_py)", ], data = [":talker_py", ":isolated_listener_py"], - tags = ["manual"], size = "small", ) @@ -84,6 +83,5 @@ sh_test( "$(location :isolated_listener_cc)", ], data = [":talker_py", ":isolated_listener_cc"], - tags = ["manual"], size = "small", ) From c35fbb8a5494c0e4d39a6c9d1faf5ef016902ee0 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 25 Oct 2021 17:11:17 -0300 Subject: [PATCH 061/107] Add --batch flag to debugger fork-follow tests. Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel b/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel index 76112426a..505e015e3 100644 --- a/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel +++ b/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel @@ -73,7 +73,7 @@ sh_test( srcs = ["test/exec.sh"], args = [ "gdb", - "-x", "$(location :test/oracle_cc.gdb)", "--args", + "-x", "$(location :test/oracle_cc.gdb)", "--batch", "--args", "$(location :oracle_cc)", "--ros-args", "--disable-external-lib-logs", ], data = ["test/oracle_cc.gdb", ":oracle_cc"], @@ -86,7 +86,7 @@ sh_test( srcs = ["test/exec.sh"], args = [ "lldb", - "-s", "$(location :test/oracle_cc.lldb)", "--", + "-s", "$(location :test/oracle_cc.lldb)", "--batch", "--", "$(location :oracle_cc)", "--ros-args", "--disable-external-lib-logs", ], data = ["test/oracle_cc.lldb", ":oracle_cc"], From eb7eafe41c98dc5df3404292820a0e58498a8e69 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 25 Oct 2021 18:19:48 -0300 Subject: [PATCH 062/107] Improve setup prerequisites script Signed-off-by: Michel Hidalgo --- .../setup/install_prereqs.sh | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drake_ros_bazel_installed/setup/install_prereqs.sh b/drake_ros_bazel_installed/setup/install_prereqs.sh index 4e5381e7d..63412dbe3 100755 --- a/drake_ros_bazel_installed/setup/install_prereqs.sh +++ b/drake_ros_bazel_installed/setup/install_prereqs.sh @@ -2,18 +2,21 @@ set -eux pipefail +apt update && apt install -y apt-transport-https curl gnupg lsb-release + # Install Bazel -apt install -y apt-transport-https curl gnupg curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg mv bazel.gpg /etc/apt/trusted.gpg.d/ -echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list +echo "deb [arch=$(dpkg --print-architecture)] https://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list apt update && apt install bazel -# Install CPython development libraries -apt install -y python3-dev - # Install ROS 2 Rolling -apt install -y ros-rolling-ros-base ros-rolling-rmw-fastrtps-cpp ros-rolling-rmw-cyclonedds-cpp +curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg +echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/ros2.list +apt update && apt install ros-rolling-ros-base ros-rolling-rmw-fastrtps-cpp ros-rolling-rmw-cyclonedds-cpp + +# Install Python dependencies +apt install python3-toposort python3-dev # Install debuggers -apt install -y gdb lldb +apt install gdb lldb From f712156fc1df8b51800945d66658e2c184139833 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 26 Oct 2021 14:20:05 -0300 Subject: [PATCH 063/107] Add basic Bazel+ROS 2 CI Signed-off-by: Michel Hidalgo --- .github/workflows/bazelized.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/bazelized.yml diff --git a/.github/workflows/bazelized.yml b/.github/workflows/bazelized.yml new file mode 100644 index 000000000..b5b043cc9 --- /dev/null +++ b/.github/workflows/bazelized.yml @@ -0,0 +1,30 @@ +name: Bazelized Continuous Integration + +on: + pull_request: + branches: + - main + - develop + +defaults: + run: + working-directory: drake_ros_bazel_installed + +jobs: + build_and_test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: "/home/runner/.cache/bazel" + key: bazel + - name: Install prerequisites + run: | + yes | sudo ./setup/install_prereqs.sh + - name: Build Bazel workspace + run: | + bazel build //... + - name: Test Bazel workspace + run: | + bazel test //... From 1a6c08968859fa53bbd172291f64da8ddad9fe20 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 26 Oct 2021 14:28:34 -0300 Subject: [PATCH 064/107] Remove Drake from WORKSPACE Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/WORKSPACE | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drake_ros_bazel_installed/WORKSPACE b/drake_ros_bazel_installed/WORKSPACE index c3d1bcd0a..2ef502c46 100644 --- a/drake_ros_bazel_installed/WORKSPACE +++ b/drake_ros_bazel_installed/WORKSPACE @@ -1,15 +1,5 @@ workspace(name = "drake_ros") -new_local_repository( - name = "drake_repository", - path = "/opt/drake", - build_file_content = "#", -) - -load("@drake_repository//:share/drake/repo.bzl", "drake_repository") - -drake_repository(name = "drake") - load("//tools/workspace/python:repository.bzl", "python_repository") python_repository(name = "python_dev") From 678b58c4d8dd26194e6acce39912038c7701b68c Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 26 Oct 2021 15:53:39 -0300 Subject: [PATCH 065/107] Use ROS 2 Rolling Docker image for CI Signed-off-by: Michel Hidalgo --- .github/workflows/bazelized.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/bazelized.yml b/.github/workflows/bazelized.yml index b5b043cc9..00b2be808 100644 --- a/.github/workflows/bazelized.yml +++ b/.github/workflows/bazelized.yml @@ -13,6 +13,8 @@ defaults: jobs: build_and_test: runs-on: ubuntu-latest + container: + image: ros:rolling-ros-core-focal steps: - uses: actions/checkout@v2 - uses: actions/cache@v2 From 73dad8a70a2ed1c698c30f94f9644598cc3c8b66 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 26 Oct 2021 15:55:28 -0300 Subject: [PATCH 066/107] No sudo within Docker container Signed-off-by: Michel Hidalgo --- .github/workflows/bazelized.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bazelized.yml b/.github/workflows/bazelized.yml index 00b2be808..77890692b 100644 --- a/.github/workflows/bazelized.yml +++ b/.github/workflows/bazelized.yml @@ -23,7 +23,7 @@ jobs: key: bazel - name: Install prerequisites run: | - yes | sudo ./setup/install_prereqs.sh + yes | ./setup/install_prereqs.sh - name: Build Bazel workspace run: | bazel build //... From 4a2b0dda11621593100f21bc3ded623e1ddd8ca0 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 26 Oct 2021 16:00:09 -0300 Subject: [PATCH 067/107] Complete build prerequisites Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/setup/install_prereqs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drake_ros_bazel_installed/setup/install_prereqs.sh b/drake_ros_bazel_installed/setup/install_prereqs.sh index 63412dbe3..b9902934a 100755 --- a/drake_ros_bazel_installed/setup/install_prereqs.sh +++ b/drake_ros_bazel_installed/setup/install_prereqs.sh @@ -2,7 +2,7 @@ set -eux pipefail -apt update && apt install -y apt-transport-https curl gnupg lsb-release +apt update && apt install -y apt-transport-https curl gnupg lsb-release cmake build-essential # Install Bazel curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg From 3f62d273aa8a1c22aa2733e0647a91b9b565ab63 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 26 Oct 2021 16:06:40 -0300 Subject: [PATCH 068/107] Complete Python prerequisites Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/setup/install_prereqs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drake_ros_bazel_installed/setup/install_prereqs.sh b/drake_ros_bazel_installed/setup/install_prereqs.sh index b9902934a..7982fd717 100755 --- a/drake_ros_bazel_installed/setup/install_prereqs.sh +++ b/drake_ros_bazel_installed/setup/install_prereqs.sh @@ -16,7 +16,7 @@ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-a apt update && apt install ros-rolling-ros-base ros-rolling-rmw-fastrtps-cpp ros-rolling-rmw-cyclonedds-cpp # Install Python dependencies -apt install python3-toposort python3-dev +apt install python3 python3-toposort python3-dev python-is-python3 # Install debuggers apt install gdb lldb From 5f82eeec3fbb1a0f54af7144bf5cb83e575fc828 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 28 Oct 2021 15:03:02 -0300 Subject: [PATCH 069/107] Merge and document all ROS 2 tooling. Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/.bazelignore | 1 + drake_ros_bazel_installed/README.md | 33 +++++++++++ .../drake_ros_apps/README.md | 16 +++-- .../tools/skylark/ros2/README.md | 59 +++++++++++++++++++ .../skylark/ros2/generate_repository_files.py | 3 - .../ros2/resources/{bazel => }/common.bzl | 0 .../ros2/resources/rmw_isolation}/BUILD.bazel | 48 ++++++++++----- .../ros2/resources/rmw_isolation/__init__.py | 1 + .../rmw_isolation}/isolated_rmw_env.py | 0 .../rmw_isolation/rmw_isolation.cc.in} | 7 +-- .../resources/rmw_isolation}/rmw_isolation.h | 0 .../resources/rmw_isolation}/rmw_isolation.py | 0 .../rmw_isolation}/test/isolated_listener.cc | 2 +- .../rmw_isolation}/test/isolated_listener.py | 2 +- .../rmw_isolation}/test/rmw_isolation_test.sh | 0 .../resources/rmw_isolation}/test/talker.py | 0 .../ros2/resources/{bazel => }/ros_cc.bzl | 0 .../ros2/resources/{bazel => }/ros_py.bzl | 0 .../ros2/resources/{bazel => }/rosidl.bzl | 0 .../ament_cmake_CMakeLists.txt.in | 0 .../{bazel => templates}/distro.bzl.tpl | 0 .../overlay_executable.bazel.tpl | 0 .../package_alias.bazel.tpl | 0 .../package_cc_library.bazel.tpl | 0 .../package_interfaces_filegroup.bazel.tpl | 0 .../package_meta_py_library.bazel.tpl | 0 .../package_py_library.bazel.tpl | 0 .../package_py_library_with_cc_libs.bazel.tpl | 0 .../package_share_filegroup.bazel.tpl | 0 .../{bazel => templates}/prologue.bazel | 1 + .../{shell => templates}/setup.sh.in | 0 .../tools/skylark/ros2/ros2.bzl | 51 +++++++++------- .../ros2/ros2bzl/scrapping/ament_cmake.py | 2 +- .../tools/skylark/ros2/ros2bzl/templates.py | 22 +++---- 34 files changed, 187 insertions(+), 61 deletions(-) create mode 100644 drake_ros_bazel_installed/.bazelignore create mode 100644 drake_ros_bazel_installed/README.md create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/README.md rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{bazel => }/common.bzl (100%) rename drake_ros_bazel_installed/tools/{ros2 => skylark/ros2/resources/rmw_isolation}/BUILD.bazel (66%) create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/__init__.py rename drake_ros_bazel_installed/tools/{ros2 => skylark/ros2/resources/rmw_isolation}/isolated_rmw_env.py (100%) rename drake_ros_bazel_installed/tools/{ros2/rmw_isolation.cc => skylark/ros2/resources/rmw_isolation/rmw_isolation.cc.in} (86%) rename drake_ros_bazel_installed/tools/{ros2 => skylark/ros2/resources/rmw_isolation}/rmw_isolation.h (100%) rename drake_ros_bazel_installed/tools/{ros2 => skylark/ros2/resources/rmw_isolation}/rmw_isolation.py (100%) rename drake_ros_bazel_installed/tools/{ros2 => skylark/ros2/resources/rmw_isolation}/test/isolated_listener.cc (96%) rename drake_ros_bazel_installed/tools/{ros2 => skylark/ros2/resources/rmw_isolation}/test/isolated_listener.py (93%) rename drake_ros_bazel_installed/tools/{ros2 => skylark/ros2/resources/rmw_isolation}/test/rmw_isolation_test.sh (100%) rename drake_ros_bazel_installed/tools/{ros2 => skylark/ros2/resources/rmw_isolation}/test/talker.py (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{bazel => }/ros_cc.bzl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{bazel => }/ros_py.bzl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{bazel => }/rosidl.bzl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{cmake => templates}/ament_cmake_CMakeLists.txt.in (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{bazel => templates}/distro.bzl.tpl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{bazel => templates}/overlay_executable.bazel.tpl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{bazel => templates}/package_alias.bazel.tpl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{bazel => templates}/package_cc_library.bazel.tpl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{bazel => templates}/package_interfaces_filegroup.bazel.tpl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{bazel => templates}/package_meta_py_library.bazel.tpl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{bazel => templates}/package_py_library.bazel.tpl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{bazel => templates}/package_py_library_with_cc_libs.bazel.tpl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{bazel => templates}/package_share_filegroup.bazel.tpl (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{bazel => templates}/prologue.bazel (89%) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/{shell => templates}/setup.sh.in (100%) diff --git a/drake_ros_bazel_installed/.bazelignore b/drake_ros_bazel_installed/.bazelignore new file mode 100644 index 000000000..eb0f413e8 --- /dev/null +++ b/drake_ros_bazel_installed/.bazelignore @@ -0,0 +1 @@ +tools/skylark/ros2/resources diff --git a/drake_ros_bazel_installed/README.md b/drake_ros_bazel_installed/README.md new file mode 100644 index 000000000..eaa2ab3cc --- /dev/null +++ b/drake_ros_bazel_installed/README.md @@ -0,0 +1,33 @@ +# Bazel Project with ROS 2 as a Precompiled External + +This project pulls in a system installed ROS 2 distribution as a Bazel external repository. + +For an introduction to Bazel, refer to [Getting Started with Bazel](https://docs.bazel.build/versions/master/getting-started.html). + +## Platform support + +This project targets ROS 2 Rolling distributions on Ubuntu Focal 20.04 only. + +## Instructions + +First, install the required dependencies: + +```sh +sudo ./setup/install_prereqs.sh +``` + +To build all packages: + +```sh +bazel build //... +``` + +To run binaries directly: + +```sh +bazel run //drake_ros_apps:oracle_cc +``` + +```sh +bazel run //drake_ros_apps:inquirer_py +``` diff --git a/drake_ros_bazel_installed/drake_ros_apps/README.md b/drake_ros_bazel_installed/drake_ros_apps/README.md index 5b0787762..b1efe8a5d 100644 --- a/drake_ros_bazel_installed/drake_ros_apps/README.md +++ b/drake_ros_bazel_installed/drake_ros_apps/README.md @@ -4,7 +4,15 @@ This package exercises `rosidl` interface generation and usage in C++ and Python To check that the resulting interfaces are functional, it includes a demo applications in C++ and Python. Namely: -- `oracle_cc`/`oracle_py` applications that publish `apps_msgs/msg/Status` messages, serve a `common_msgs/srv/Query` - service, and provide a `common_msgs/action/Do` action. -- `inquirer_cc`/`inquirer_py` applications that subscribe to `apps_msgs/msg/Status` messages, make `common_msgs/srv/Query` - service requests, and invoke the `common_msgs/action/Do` action. +- Equivalent `oracle_(cc|py)` applications that publish `drake_ros_apps_msgs/msg/Status` messages, serve a `drake_ros_common_msgs/srv/Query`service, and provide a `drake_ros_common_msgs/action/Do` action. +- Equivalent `inquirer_(cc|py)` applications that subscribe to `drake_ros_apps_msgs/msg/Status` messages, make `drake_ros_common_msgs/srv/Query` service requests, and invoke the `drake_ros_common_msgs/action/Do` action. + +You may run a C++ oracle against a Python inquirer, and viceversa: + +```sh +bazel run //drake_ros_apps:oracle_py +``` + +```sh +bazel run //drake_ros_apps:inquirer_cc +``` diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/README.md b/drake_ros_bazel_installed/tools/skylark/ros2/README.md new file mode 100644 index 000000000..182187822 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/README.md @@ -0,0 +1,59 @@ +## Infrastructure to use ROS 2 from a Bazel workspace + +This package encapsulates all the machinery to pull a ROS 2 workspace install space or a subset thereof as a Bazel repository. +Both system-installed binary distributions and source builds can pulled in this way, whether symlink- or merged-installed. + +A single repository rule, `ros2_local_repository()`, is the sole entrypoint. This rule heavily relies on two Python packages: + +- the `cmake_tools` Python package, which provides an idiomatic API to collect a CMake project's exported configuration. +- the `ros2bzl` Python package, which provides tools to crawl a ROS 2 workspace install space, collects CMake packages' + exported configuration, collect Python packages' egg metadata, symlink relevant directories and files, and generates + a root BUILD.bazel file that recreates the dependency graph in the workspace. This package constitutes the backbone of + the `generate_repository_files.py` Python binary which `ros2_local_repository()` invokes. + +### Repository layout + +A ROS 2 local repository has the following layout: + +``` + . + ├── BUILD.bazel + ├── rmw_isolation + │ └── BUILD.bazel + ├── distro.bzl + ├── common.bzl + ├── ros_cc.bzl + ├── ros_py.bzl + └── rosidl.bzl +``` + +Note that all files and subdirectories that are mere implementation details have been excluded from this layout. + +#### Targets + +For every package in the underlying ROS 2 workspace install space, the following targets may be found at the root `BUILD.bazel` file: + +- A `_share` filegroup for every package that installs artifacts under the share directory. +- A `_cc` C/C++ library for every CMake package that installs C++ binary artifacts (in addition to compiler/linker flags, includes, etc.). +- A `_py` Python library for every package that installs a Python egg. +- A `_defs` filegroup for every package that installs ROS 2 interfaces (.msg, .srv, .action, and .idl files). +- A `_c` C/C++ library for every CMake package that installs C binary artifacts. Typically an alias of the `_cc` C/C++ library if C and C++ binary artifacts cannot be told apart. +- A `_transitively_py` Python library for every package that does not install a Python egg yet it depends and it is a dependency of packages that do. This helps maintain the dependency graph. +- A `_` for every executable installed at the package-level (i.e. under `lib/`, where `ros2 run` can find them). +- A `` for every executable installed under the `bin` directory (and thus in the `$PATH`). + +#### Rules + +To build C++ binaries and tests that depend on ROS 2, `ros_cc_binary` and `ros_cc_test` rules are available in the `ros_cc.bzl` file. These rules, equivalent to the native `cc_binary` and `cc_test` rules, ensure these binaries run in a environment that is tightly coupled with the underlying ROS 2 workspace install space. + +To build Python binaries and tests that depend on ROS 2, `ros_py_binary` and `ros_pytest` rules are available in the `ros_py.bzl` file. These rules, equivalent to the native `py_binary` and `py_test` rules, ensure these binaries run in a environment that is tightly coupled with the underlying ROS 2 workspace install space. + +To generate and build ROS 2 interfaces, a `rosidl_interfaces_group` rule is available in the `rosidl.bzl` file. This rule generates C++ and Python code for the given ROS 2 interface definition files and builds them, off of what is available in the ROS 2 workspace install space: code generators, interface definition translators, runtime dependencies, etc. Several targets are created, following strict naming conventions (e.g. C++ and Python interface libraries carry `_cc` and `_py` suffixes, respectively), though finer-grained control over what is generated and built can be achieved through via other rules available in the same file. By default, these naming conventions allow downstream `rosidl_interfaces_group` rules to depend on upstream `rosidl_interface_group` rules. + +#### Tools + +The `rmw_isolation` subpackage provides C++ and Python `isolate_rmw_by_path` APIs to enforce RMW network isolation. To that end, a unique path must be provided (such as Bazel's `$TEST_TMPDIR`). + +#### Metadata + +The `distro.bzl` file bears relevant ROS 2 workspace metadata for rules, tools, and downstream packages to use. diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py index 078945ca0..00a84aa4b 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -7,9 +7,6 @@ - a BUILD.bazel file, with targets for all C/C++ libraries, Python libraries, executables, and share data files found in each scrapped ROS 2 package -- a ros_cc.bzl file, with rules for C/C++ binaries and tests that depend on ROS 2 C/C++ libraries -- a ros_py.bzl file, with rules for Python binaries and tests that depend on ROS 2 Python libraries -- a rosidl.bzl file, with rules for C++ and Python ROS 2 message generation including typesupports - a distro.bzl file, with ROS 2 metadata as constants """ diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/common.bzl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/common.bzl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/common.bzl diff --git a/drake_ros_bazel_installed/tools/ros2/BUILD.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/BUILD.bazel similarity index 66% rename from drake_ros_bazel_installed/tools/ros2/BUILD.bazel rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/BUILD.bazel index d9c3d175a..9a35a116f 100644 --- a/drake_ros_bazel_installed/tools/ros2/BUILD.bazel +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/BUILD.bazel @@ -1,22 +1,38 @@ -# -*- mode: python -*- +# -*- python -*- # vi: set ft=python : -package(default_visibility = ["//visibility:public"]) - -load("@ros2//:ros_cc.bzl", "ros_cc_binary") -load("@ros2//:ros_py.bzl", "ros_py_binary") +load("//:ros_cc.bzl", "ros_cc_binary") +load("//:ros_py.bzl", "ros_py_binary") py_library( name = "rmw_isolation_py", srcs = ["rmw_isolation.py"], - deps = ["@ros2//:rclpy_py"], + deps = ["//:rclpy_py"], +) + +py_library( + name = "module_py", + srcs = ["__init__.py"], + deps = [":rmw_isolation_py"], ) ros_py_binary( name = "isolated_rmw_env", srcs = ["isolated_rmw_env.py"], main = "isolated_rmw_env.py", - deps = [":rmw_isolation_py"], + deps = [":module_py"], + legacy_create_init = False, +) + +genrule( + name = "rmw_isolation_cc_gen", + srcs = ["rmw_isolation.cc.in"], + outs = ["rmw_isolation.cc"], + cmd = ( + "ISOLATED_RMW_ENV_PATH={}/rmw_isolation/isolated_rmw_env ".format( + repository_name().strip("@") or "." + ) + "envsubst < $< > $@" + ), ) cc_library( @@ -26,7 +42,7 @@ cc_library( data = [":isolated_rmw_env"], deps = [ "@bazel_tools//tools/cpp/runfiles", - "@ros2//:rclcpp_cc", + "//:rclcpp_cc", ], ) @@ -35,10 +51,11 @@ ros_py_binary( srcs = ["test/talker.py"], main = "test/talker.py", deps = [ - "@ros2//:rclpy_py", - "@ros2//:std_msgs_py" + "//:rclpy_py", + "//:std_msgs_py" ], rmw_implementation = 'rmw_cyclonedds_cpp', + legacy_create_init = False, ) ros_py_binary( @@ -46,11 +63,12 @@ ros_py_binary( srcs = ["test/isolated_listener.py"], main = "test/isolated_listener.py", deps = [ - "@ros2//:rclpy_py", - "@ros2//:std_msgs_py", - ":rmw_isolation_py", + "//:rclpy_py", + "//:std_msgs_py", + ":module_py", ], rmw_implementation = 'rmw_cyclonedds_cpp', + legacy_create_init = False, ) sh_test( @@ -68,8 +86,8 @@ ros_cc_binary( name = "isolated_listener_cc", srcs = ["test/isolated_listener.cc"], deps = [ - "@ros2//:rclcpp_cc", - "@ros2//:std_msgs_cc", + "//:rclcpp_cc", + "//:std_msgs_cc", ":rmw_isolation_cc", ], rmw_implementation = 'rmw_cyclonedds_cpp', diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/__init__.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/__init__.py new file mode 100644 index 000000000..16c094eb9 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/__init__.py @@ -0,0 +1 @@ +from .rmw_isolation import isolate_rmw_by_path diff --git a/drake_ros_bazel_installed/tools/ros2/isolated_rmw_env.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/isolated_rmw_env.py similarity index 100% rename from drake_ros_bazel_installed/tools/ros2/isolated_rmw_env.py rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/isolated_rmw_env.py diff --git a/drake_ros_bazel_installed/tools/ros2/rmw_isolation.cc b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.cc.in similarity index 86% rename from drake_ros_bazel_installed/tools/ros2/rmw_isolation.cc rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.cc.in index ffc5691a8..a3c49ac36 100644 --- a/drake_ros_bazel_installed/tools/ros2/rmw_isolation.cc +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.cc.in @@ -10,7 +10,7 @@ #include #include "tools/cpp/runfiles/runfiles.h" -#include "tools/ros2/rmw_isolation.h" +#include "rmw_isolation/rmw_isolation.h" using bazel::tools::cpp::runfiles::Runfiles; @@ -28,11 +28,8 @@ void isolate_rmw_by_path(const std::string& argv0, const std::string& path) if (!runfiles) { throw std::runtime_error(error); } - // NOTE(hidmic): no way around hard-coding this. Bazel's $(rootpath) - // won't prepend the workspace name (drake_ros in this case). Maybe - // relocate to an external repository? std::string isolated_env_rmw_location = - runfiles->Rlocation("drake_ros/tools/ros2/isolated_rmw_env"); + runfiles->Rlocation("$ISOLATED_RMW_ENV_PATH"); std::string env_file_path{"isolated_rmw.env"}; std::stringstream sstr; sstr << isolated_env_rmw_location << " --scratch-directory " << path diff --git a/drake_ros_bazel_installed/tools/ros2/rmw_isolation.h b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.h similarity index 100% rename from drake_ros_bazel_installed/tools/ros2/rmw_isolation.h rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.h diff --git a/drake_ros_bazel_installed/tools/ros2/rmw_isolation.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.py similarity index 100% rename from drake_ros_bazel_installed/tools/ros2/rmw_isolation.py rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.py diff --git a/drake_ros_bazel_installed/tools/ros2/test/isolated_listener.cc b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.cc similarity index 96% rename from drake_ros_bazel_installed/tools/ros2/test/isolated_listener.cc rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.cc index d66f1b02f..26ce1a703 100644 --- a/drake_ros_bazel_installed/tools/ros2/test/isolated_listener.cc +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.cc @@ -4,7 +4,7 @@ #include #include -#include "tools/ros2/rmw_isolation.h" +#include "rmw_isolation/rmw_isolation.h" using std::placeholders::_1; diff --git a/drake_ros_bazel_installed/tools/ros2/test/isolated_listener.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py similarity index 93% rename from drake_ros_bazel_installed/tools/ros2/test/isolated_listener.py rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py index 9987be5ea..dbf5f0cef 100644 --- a/drake_ros_bazel_installed/tools/ros2/test/isolated_listener.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py @@ -27,7 +27,7 @@ def _timer_callback(self): if __name__ == '__main__': if 'TEST_TMPDIR' in os.environ: - from drake_ros.tools.ros2.rmw_isolation import isolate_rmw_by_path + from rmw_isolation import isolate_rmw_by_path isolate_rmw_by_path(os.environ['TEST_TMPDIR']) rclpy.init() diff --git a/drake_ros_bazel_installed/tools/ros2/test/rmw_isolation_test.sh b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/rmw_isolation_test.sh similarity index 100% rename from drake_ros_bazel_installed/tools/ros2/test/rmw_isolation_test.sh rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/rmw_isolation_test.sh diff --git a/drake_ros_bazel_installed/tools/ros2/test/talker.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/talker.py similarity index 100% rename from drake_ros_bazel_installed/tools/ros2/test/talker.py rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/talker.py diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_cc.bzl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_cc.bzl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_cc.bzl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/ros_py.bzl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/rosidl.bzl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/cmake/ament_cmake_CMakeLists.txt.in b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/ament_cmake_CMakeLists.txt.in similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/cmake/ament_cmake_CMakeLists.txt.in rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/ament_cmake_CMakeLists.txt.in diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/distro.bzl.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/distro.bzl.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/distro.bzl.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/distro.bzl.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/overlay_executable.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/overlay_executable.bazel.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/overlay_executable.bazel.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/overlay_executable.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_alias.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/package_alias.bazel.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_alias.bazel.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/package_alias.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_cc_library.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/package_cc_library.bazel.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_cc_library.bazel.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/package_cc_library.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_interfaces_filegroup.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/package_interfaces_filegroup.bazel.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_interfaces_filegroup.bazel.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/package_interfaces_filegroup.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_meta_py_library.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/package_meta_py_library.bazel.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_meta_py_library.bazel.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/package_meta_py_library.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_py_library.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/package_py_library.bazel.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_py_library.bazel.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/package_py_library.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_py_library_with_cc_libs.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/package_py_library_with_cc_libs.bazel.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_py_library_with_cc_libs.bazel.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/package_py_library_with_cc_libs.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_share_filegroup.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/package_share_filegroup.bazel.tpl similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/package_share_filegroup.bazel.tpl rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/package_share_filegroup.bazel.tpl diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/prologue.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/prologue.bazel similarity index 89% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/prologue.bazel rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/prologue.bazel index d2267b1a9..3b957e4c5 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/bazel/prologue.bazel +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/prologue.bazel @@ -1,4 +1,5 @@ # -*- python -*- +# vi: set ft=python : package(default_visibility = ["//visibility:public"]) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/shell/setup.sh.in b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/setup.sh.in similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/shell/setup.sh.in rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/setup.sh.in diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl index 81b0ea79a..76c986e16 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -2,27 +2,40 @@ load("@drake_ros//tools/skylark:execute.bzl", "execute_or_fail") +PACKAGE_MANIFEST = [ + "common.bzl", + "ros_cc.bzl", + "ros_py.bzl", + "rosidl.bzl", + + "rmw_isolation/BUILD.bazel", + "rmw_isolation/__init__.py", + "rmw_isolation/isolated_rmw_env.py", + "rmw_isolation/rmw_isolation.cc.in", + "rmw_isolation/rmw_isolation.h", + "rmw_isolation/rmw_isolation.py", + "rmw_isolation/test/isolated_listener.cc", + "rmw_isolation/test/isolated_listener.py", + "rmw_isolation/test/rmw_isolation_test.sh", + "rmw_isolation/test/talker.py", +] + GENERATE_TOOL_RESOURCES_MANIFEST = [ "cmake_tools/packages.py", "cmake_tools/server_mode.py", "cmake_tools/__init__.py", - "resources/bazel/common.bzl", - "resources/bazel/ros_cc.bzl", - "resources/bazel/ros_py.bzl", - "resources/bazel/rosidl.bzl", - - "resources/bazel/distro.bzl.tpl", - "resources/bazel/overlay_executable.bazel.tpl", - "resources/bazel/package_interfaces_filegroup.bazel.tpl", - "resources/bazel/package_cc_library.bazel.tpl", - "resources/bazel/package_meta_py_library.bazel.tpl", - "resources/bazel/package_py_library.bazel.tpl", - "resources/bazel/package_py_library_with_cc_libs.bazel.tpl", - "resources/bazel/package_share_filegroup.bazel.tpl", - "resources/bazel/prologue.bazel", + "resources/templates/distro.bzl.tpl", + "resources/templates/overlay_executable.bazel.tpl", + "resources/templates/package_interfaces_filegroup.bazel.tpl", + "resources/templates/package_cc_library.bazel.tpl", + "resources/templates/package_meta_py_library.bazel.tpl", + "resources/templates/package_py_library.bazel.tpl", + "resources/templates/package_py_library_with_cc_libs.bazel.tpl", + "resources/templates/package_share_filegroup.bazel.tpl", + "resources/templates/prologue.bazel", - "resources/cmake/ament_cmake_CMakeLists.txt.in", + "resources/templates/ament_cmake_CMakeLists.txt.in", "ros2bzl/utilities.py", "ros2bzl/sandboxing.py", @@ -40,16 +53,14 @@ def _label(relpath): return Label("//tools/skylark/ros2:" + relpath) def _impl(repo_ctx): - repo_ctx.symlink(_label("resources/bazel/common.bzl"), "common.bzl") - repo_ctx.symlink(_label("resources/bazel/ros_cc.bzl"), "ros_cc.bzl") - repo_ctx.symlink(_label("resources/bazel/ros_py.bzl"), "ros_py.bzl") - repo_ctx.symlink(_label("resources/bazel/rosidl.bzl"), "rosidl.bzl") + for relpath in PACKAGE_MANIFEST: + repo_ctx.symlink(_label("resources/" + relpath), relpath) for relpath in GENERATE_TOOL_RESOURCES_MANIFEST: repo_ctx.symlink(_label(relpath), relpath) repo_ctx.template( - "setup.sh", _label("resources/shell/setup.sh.in"), + "setup.sh", _label("resources/templates/setup.sh.in"), substitutions = { "@REPOSITORY_DIR@": str(repo_ctx.path(".")), "@WORKSPACES@": " ".join(repo_ctx.attr.workspaces), diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py index ccec2d2d3..28de8e8af 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py @@ -109,7 +109,7 @@ def collect_ament_cmake_package_properties(name, metadata): with TemporaryDirectory(dir=os.getcwd()) as project_path: project_name = 'empty_using_' + name cmakelists_template_path = path_to_resource( - 'cmake/ament_cmake_CMakeLists.txt.in') + 'templates/ament_cmake_CMakeLists.txt.in') cmakelists_path = os.path.join(project_path, 'CMakeLists.txt') cmake_tools.configure_file(cmakelists_template_path, cmakelists_path, { '@NAME@': project_name, '@PACKAGE@': name diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py index 7514795f2..2edc9f9be 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py @@ -41,7 +41,7 @@ def configure_package_share_filegroup(name, metadata, sandbox): shared_directories.append(sandbox(metadata['ament_index_directory'])) return ( target_name, - load_resource('bazel/package_share_filegroup.bazel.tpl'), + load_resource('templates/package_share_filegroup.bazel.tpl'), to_starlark_string_dict({ 'name': target_name, 'share_directories': shared_directories }) @@ -51,7 +51,7 @@ def configure_package_share_filegroup(name, metadata, sandbox): def configure_package_interfaces_filegroup(name, metadata, sandbox): return ( name, - load_resource('bazel/package_interfaces_filegroup.bazel.tpl'), + load_resource('templates/package_interfaces_filegroup.bazel.tpl'), to_starlark_string_dict({ 'name': name, 'share_directory': sandbox(metadata['share_directory']) }) @@ -114,7 +114,7 @@ def configure_package_cc_library(name, metadata, properties, dependencies, extra if label_or_path not in data ) - template_path = 'bazel/package_cc_library.bazel.tpl' + template_path = 'templates/package_cc_library.bazel.tpl' config = { 'name': target_name, @@ -141,7 +141,7 @@ def configure_package_meta_py_library(name, metadata, dependencies): target_name = meta_py_name(name, metadata) return ( target_name, - load_resource('bazel/package_meta_py_library.bazel.tpl'), + load_resource('templates/package_meta_py_library.bazel.tpl'), to_starlark_string_dict({'name': target_name, 'deps': deps}) ) @@ -153,7 +153,7 @@ def configure_package_py_library(name, metadata, properties, dependencies, extra sandbox(top_level) for _, top_level in properties['python_packages']] imports = [os.path.dirname(egg) for egg in eggs] - template = 'bazel/package_py_library.bazel.tpl' + template = 'templates/package_py_library.bazel.tpl' config = { 'name': target_name, 'tops': tops, @@ -182,7 +182,7 @@ def configure_package_py_library(name, metadata, properties, dependencies, extra ] # Expose C/C++ libraries if any if 'cc_libraries' in properties: - template = 'bazel/package_py_library_with_cc_libs.bazel.tpl' + template = 'templates/package_py_library_with_cc_libs.bazel.tpl' config.update({ 'cc_name': c_name(target_name, metadata), 'cc_libs': [sandbox(lib) for lib in properties['cc_libraries']], @@ -218,7 +218,7 @@ def configure_package_py_library(name, metadata, properties, dependencies, extra def configure_package_alias(name, target): return ( name, - load_resource('bazel/package_alias.bazel.tpl'), + load_resource('templates/package_alias.bazel.tpl'), to_starlark_string_dict({'name': name, 'actual': ':' + target}) ) @@ -227,7 +227,7 @@ def configure_package_c_library_alias(name, metadata): target_name = c_name(name, metadata) return ( target_name, - load_resource('bazel/package_alias.bazel.tpl'), + load_resource('templates/package_alias.bazel.tpl'), to_starlark_string_dict({ 'name': target_name, 'actual': cc_label(name, metadata) @@ -259,7 +259,7 @@ def configure_executable_imports( data = data + extras['data'][target_name] yield ( target_name, - load_resource('bazel/overlay_executable.bazel.tpl'), + load_resource('templates/overlay_executable.bazel.tpl'), to_starlark_string_dict({ 'name': target_name, 'executable': sandbox(executable), @@ -280,7 +280,7 @@ def configure_package_executable_imports( def configure_prologue(repo_name): - return load_resource('bazel/prologue.bazel'), {} + return load_resource('templates/prologue.bazel'), {} def configure_distro(repo_name, distro): @@ -288,7 +288,7 @@ def configure_distro(repo_name, distro): 'rosidl_typesupport_c_packages', 'rosidl_typesupport_cpp_packages' ] - return load_resource('bazel/distro.bzl.tpl'), to_starlark_string_dict({ + return load_resource('templates/distro.bzl.tpl'), to_starlark_string_dict({ 'AMENT_PREFIX_PATH': distro['paths']['ament_prefix'], 'LOAD_PATH': distro['paths']['library_load'], # Linux only 'AVAILABLE_TYPESUPPORT_LIST': [ From b5f5f43802eebd74aed19529beca8ec5f80946db Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 28 Oct 2021 15:24:54 -0300 Subject: [PATCH 070/107] Update basic Bazel+ROS 2 CI Signed-off-by: Michel Hidalgo --- .github/workflows/bazelized.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/bazelized.yml b/.github/workflows/bazelized.yml index 77890692b..2120a6a05 100644 --- a/.github/workflows/bazelized.yml +++ b/.github/workflows/bazelized.yml @@ -29,4 +29,6 @@ jobs: bazel build //... - name: Test Bazel workspace run: | - bazel test //... + bazel test //drake_ros_apps:gdb_oracle_cc_test + bazel test //drake_ros_apps:lldb_oracle_cc_test + bazel test @ros2//... From 56d7962a19e0f48329bd4efbca65478af531d0bb Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 28 Oct 2021 15:47:02 -0300 Subject: [PATCH 071/107] Add envsubst to prereqs Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/setup/install_prereqs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drake_ros_bazel_installed/setup/install_prereqs.sh b/drake_ros_bazel_installed/setup/install_prereqs.sh index 7982fd717..f7b4f1f4d 100755 --- a/drake_ros_bazel_installed/setup/install_prereqs.sh +++ b/drake_ros_bazel_installed/setup/install_prereqs.sh @@ -2,7 +2,7 @@ set -eux pipefail -apt update && apt install -y apt-transport-https curl gnupg lsb-release cmake build-essential +apt update && apt install -y apt-transport-https curl gnupg lsb-release cmake build-essential gettext-base # Install Bazel curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg From 244adec733b6a7443b4ba6ca0462d780c55290ef Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 8 Nov 2021 16:38:08 -0300 Subject: [PATCH 072/107] Remove 'manual' tags in debugger fork-follow tests Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel | 2 -- 1 file changed, 2 deletions(-) diff --git a/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel b/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel index 505e015e3..05b299264 100644 --- a/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel +++ b/drake_ros_bazel_installed/drake_ros_apps/BUILD.bazel @@ -77,7 +77,6 @@ sh_test( "$(location :oracle_cc)", "--ros-args", "--disable-external-lib-logs", ], data = ["test/oracle_cc.gdb", ":oracle_cc"], - tags = ["manual"], size = "small", ) @@ -90,6 +89,5 @@ sh_test( "$(location :oracle_cc)", "--ros-args", "--disable-external-lib-logs", ], data = ["test/oracle_cc.lldb", ":oracle_cc"], - tags = ["manual"], size = "small", ) From d0c020bdc5872e3c63d6b5c94dc01d6e3324876e Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 8 Nov 2021 16:40:49 -0300 Subject: [PATCH 073/107] Cleanup Bazel ROS CI workflow Signed-off-by: Michel Hidalgo --- .github/workflows/bazelized.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/bazelized.yml b/.github/workflows/bazelized.yml index 2120a6a05..de239dd65 100644 --- a/.github/workflows/bazelized.yml +++ b/.github/workflows/bazelized.yml @@ -1,4 +1,4 @@ -name: Bazelized Continuous Integration +name: Bazel ROS Continuous Integration on: pull_request: @@ -22,13 +22,8 @@ jobs: path: "/home/runner/.cache/bazel" key: bazel - name: Install prerequisites - run: | - yes | ./setup/install_prereqs.sh + run: yes | ./setup/install_prereqs.sh - name: Build Bazel workspace - run: | - bazel build //... + run: bazel build //... - name: Test Bazel workspace - run: | - bazel test //drake_ros_apps:gdb_oracle_cc_test - bazel test //drake_ros_apps:lldb_oracle_cc_test - bazel test @ros2//... + run: bazel test //... @ros2//... From 7c998b8b4f6c8303aefe709ef8e7bb8143d1f392 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 8 Nov 2021 16:44:47 -0300 Subject: [PATCH 074/107] Clean up trailing whitespace Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/README.md | 4 ++-- drake_ros_bazel_installed/tools/skylark/dload.bzl | 2 +- .../tools/skylark/ros2/resources/rosidl.bzl | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drake_ros_bazel_installed/README.md b/drake_ros_bazel_installed/README.md index eaa2ab3cc..741f47424 100644 --- a/drake_ros_bazel_installed/README.md +++ b/drake_ros_bazel_installed/README.md @@ -13,7 +13,7 @@ This project targets ROS 2 Rolling distributions on Ubuntu Focal 20.04 only. First, install the required dependencies: ```sh -sudo ./setup/install_prereqs.sh +sudo ./setup/install_prereqs.sh ``` To build all packages: @@ -22,7 +22,7 @@ To build all packages: bazel build //... ``` -To run binaries directly: +To run binaries directly: ```sh bazel run //drake_ros_apps:oracle_cc diff --git a/drake_ros_bazel_installed/tools/skylark/dload.bzl b/drake_ros_bazel_installed/tools/skylark/dload.bzl index 5e0d4039c..0cb8c1b18 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload.bzl @@ -1,7 +1,7 @@ # -*- python -*- """ -The purpose of these macros is to support configuration of the runtime +The purpose of these macros is to support configuration of the runtime environment in which executables are run. The primary macro of interest is `do_dload_shim`, which aids language diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl index 368973c89..4d580abbf 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl @@ -518,7 +518,7 @@ def rosidl_typesupport_fastrtps_c_library( linkstatic = True, **kwargs ) - + cc_binary_rule( name = name, srcs = generated_c_sources, From 687f6180edbf38124920cc4dcfcd7adf5d6ba05c Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 8 Nov 2021 16:52:53 -0300 Subject: [PATCH 075/107] Rename kwargs.bzl functions Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/tools/skylark/kwargs.bzl | 4 ++-- .../tools/skylark/ros2/resources/ros_cc.bzl | 10 +++++----- .../tools/skylark/ros2/resources/ros_py.bzl | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/kwargs.bzl b/drake_ros_bazel_installed/tools/skylark/kwargs.bzl index 41d6f7ff9..3f471e4ea 100644 --- a/drake_ros_bazel_installed/tools/skylark/kwargs.bzl +++ b/drake_ros_bazel_installed/tools/skylark/kwargs.bzl @@ -14,7 +14,7 @@ _COMMON_KWARGS = [ "visibility", ] -def keep_common(kwargs): +def filter_to_only_common_kwargs(kwargs): """Fetch keyword arguments common to all rules from `kwargs`.""" return {key: value for key, value in kwargs.items() if key in _COMMON_KWARGS} @@ -27,6 +27,6 @@ _TEST_KWARGS = [ "timeout", ] -def remove_test_specific(kwargs): +def remove_test_specific_kwargs(kwargs): """Filter keyword arguments specific to test rules from `kwargs`.""" return {key: value for key, value in kwargs.items() if key not in _TEST_KWARGS} diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_cc.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_cc.bzl index 3c2e8707a..c6ffb7a24 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_cc.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_cc.bzl @@ -6,8 +6,8 @@ load( ) load( "@drake_ros//tools/skylark:kwargs.bzl", - "keep_common", - "remove_test_specific" + "filter_to_only_common_kwargs", + "remove_test_specific_kwargs" ) load( ":common.bzl", @@ -56,7 +56,7 @@ def ros_cc_binary( ) shim_name = binary_name + "_shim.cc" - shim_kwargs = keep_common(kwargs) + shim_kwargs = filter_to_only_common_kwargs(kwargs) dload_cc_shim( name = shim_name, target = ":" + binary_name, @@ -94,7 +94,7 @@ def ros_cc_test( `cc_binary_rule` (minus the test specific ones). """ binary_name = "_" + name - binary_kwargs = remove_test_specific(kwargs) + binary_kwargs = remove_test_specific_kwargs(kwargs) binary_kwargs.update(testonly = True) binary_env_changes = dict(RUNTIME_ENVIRONMENT) if rmw_implementation: @@ -110,7 +110,7 @@ def ros_cc_test( ) shim_name = binary_name + "_shim.cc" - shim_kwargs = keep_common(kwargs) + shim_kwargs = filter_to_only_common_kwargs(kwargs) shim_kwargs.update(testonly = True) dload_cc_shim( name = shim_name, diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl index a664012d5..b716e01e9 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl @@ -6,8 +6,8 @@ load( ) load( "@drake_ros//tools/skylark:kwargs.bzl", - "keep_common", - "remove_test_specific" + "filter_to_only_common_kwargs", + "remove_test_specific_kwargs" ) load( ":common.bzl", @@ -46,7 +46,7 @@ def ros_py_import( ) shim_name = name + "_shim.py" - shim_kwargs = keep_common(kwargs) + shim_kwargs = filter_to_only_common_kwargs(kwargs) dload_py_shim( name = shim_name, target = executable, @@ -101,7 +101,7 @@ def ros_py_binary( ) shim_name = binary_name + "_shim.py" - shim_kwargs = keep_common(kwargs) + shim_kwargs = filter_to_only_common_kwargs(kwargs) dload_py_shim( name = shim_name, target = ":" + binary_name, @@ -143,7 +143,7 @@ def ros_py_test( `py_binary_rule` (minus the test specific ones). """ binary_name = "_" + name - binary_kwargs = remove_test_specific(kwargs) + binary_kwargs = remove_test_specific_kwargs(kwargs) binary_kwargs.update(testonly = True) binary_env_changes = dict(RUNTIME_ENVIRONMENT) if rmw_implementation: @@ -159,7 +159,7 @@ def ros_py_test( ) shim_name = binary_name + "_shim.py" - shim_kwargs = keep_common(kwargs) + shim_kwargs = filter_to_only_common_kwargs(kwargs) shim_kwargs.update(testonly = True) dload_py_shim( name = shim_name, From fa5ec0d0a6be353378c10458143240060bc93c26 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 8 Nov 2021 19:38:09 -0300 Subject: [PATCH 076/107] Improve rmw_isolation API tests Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/.bazelignore | 1 - .../{BUILD.bazel => build.bazel} | 27 ++++++++--- .../rmw_isolation/test/isolated_listener.cc | 24 +++++++--- .../rmw_isolation/test/isolated_listener.py | 16 ++++--- .../rmw_isolation/test/isolated_talker.cc | 45 +++++++++++++++++++ .../rmw_isolation/test/isolated_talker.py | 35 +++++++++++++++ .../rmw_isolation/test/rmw_isolation_test.sh | 44 +++++++++++++++--- .../resources/rmw_isolation/test/talker.py | 27 ----------- .../tools/skylark/ros2/ros2.bzl | 7 ++- 9 files changed, 172 insertions(+), 54 deletions(-) delete mode 100644 drake_ros_bazel_installed/.bazelignore rename drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/{BUILD.bazel => build.bazel} (77%) create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.cc create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/talker.py diff --git a/drake_ros_bazel_installed/.bazelignore b/drake_ros_bazel_installed/.bazelignore deleted file mode 100644 index eb0f413e8..000000000 --- a/drake_ros_bazel_installed/.bazelignore +++ /dev/null @@ -1 +0,0 @@ -tools/skylark/ros2/resources diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/BUILD.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/build.bazel similarity index 77% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/BUILD.bazel rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/build.bazel index 9a35a116f..a6ec480d0 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/BUILD.bazel +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/build.bazel @@ -47,9 +47,9 @@ cc_library( ) ros_py_binary( - name = "talker_py", - srcs = ["test/talker.py"], - main = "test/talker.py", + name = "isolated_talker_py", + srcs = ["test/isolated_talker.py"], + main = "test/isolated_talker.py", deps = [ "//:rclpy_py", "//:std_msgs_py" @@ -75,13 +75,25 @@ sh_test( name = "rmw_isolation_py_test", srcs = ["test/rmw_isolation_test.sh"], args = [ - "$(location :talker_py)", + "$(location :isolated_talker_py)", "$(location :isolated_listener_py)", ], - data = [":talker_py", ":isolated_listener_py"], + data = [":isolated_talker_py", ":isolated_listener_py"], + shard_count = 10, size = "small", ) +ros_cc_binary( + name = "isolated_talker_cc", + srcs = ["test/isolated_talker.cc"], + deps = [ + "//:rclcpp_cc", + "//:std_msgs_cc", + ":rmw_isolation_cc", + ], + rmw_implementation = 'rmw_cyclonedds_cpp', +) + ros_cc_binary( name = "isolated_listener_cc", srcs = ["test/isolated_listener.cc"], @@ -97,9 +109,10 @@ sh_test( name = "rmw_isolation_cc_test", srcs = ["test/rmw_isolation_test.sh"], args = [ - "$(location :talker_py)", + "$(location :isolated_talker_cc)", "$(location :isolated_listener_cc)", ], - data = [":talker_py", ":isolated_listener_cc"], + data = [":isolated_talker_cc", ":isolated_listener_cc"], + shard_count = 10, size = "small", ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.cc b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.cc index 26ce1a703..16ff76b44 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.cc +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.cc @@ -11,30 +11,41 @@ using std::placeholders::_1; class IsolatedListener : public rclcpp::Node { public: - IsolatedListener() - : rclcpp::Node("isolated_listener") + IsolatedListener(const std::string & uuid) + : rclcpp::Node("isolated_listener"), uuid_(uuid) { double timeout = this->declare_parameter("timeout", 2.0); subscription_ = this->create_subscription( - "topic", 10, std::bind(&IsolatedListener::topic_callback, this, _1)); + "uuid", 10, std::bind(&IsolatedListener::topic_callback, this, _1)); timer_ = this->create_wall_timer( std::chrono::duration(timeout), std::bind(&IsolatedListener::timer_callback, this)); } private: - void topic_callback(const std_msgs::msg::String::SharedPtr msg) const + void topic_callback(const std_msgs::msg::String::SharedPtr msg) { - throw std::runtime_error("I heard '" + msg->data + "'"); + if (msg->data != uuid_) { + throw std::runtime_error( + "I heard '" + msg->data + "' yet" + + "I was expecting '" + uuid_ + "'!"); + } + ++expected_messages_count_; } void timer_callback() const { + if (0u == expected_messages_count_) { + throw std::runtime_error( + "I did not hear '" + uuid_ + "' even once!"); + } rclcpp::shutdown(); } rclcpp::Subscription::SharedPtr subscription_; rclcpp::TimerBase::SharedPtr timer_; + size_t expected_messages_count_{0u}; + std::string uuid_; }; int main(int argc, char * argv[]) @@ -44,7 +55,8 @@ int main(int argc, char * argv[]) ros2::isolate_rmw_by_path(argv[0], TEST_TMPDIR); } rclcpp::init(argc, argv); - rclcpp::spin(std::make_shared()); + std::string uuid{TEST_TMPDIR != nullptr? TEST_TMPDIR : "none"}; + rclcpp::spin(std::make_shared(uuid)); rclcpp::shutdown(); return 0; } diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py index dbf5f0cef..e3e9e280c 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py @@ -8,21 +8,27 @@ class IsolatedListener(rclpy.node.Node): - def __init__(self): + def __init__(self, uuid): super().__init__('isolated_listener') self._subscription = self.create_subscription( std_msgs.msg.String, - 'topic', + 'uuid', self._topic_callback, 10) timeout = self.declare_parameter('timeout', 2.0) self._timer = self.create_timer( timeout.value, self._timer_callback) + self._expected_messages_received = 0 + self._uuid = uuid def _topic_callback(self, msg): - assert False, f"I heard '{msg.data}'!" + assert msg.data == self._uuid, \ + f"I heard '{msg.data}' yet I was expecting '{self._uuid}'!" + self._expected_messages_received += 1 def _timer_callback(self): + assert self._expected_messages_received > 0, \ + f"I did not hear '{elf._uuid}' even once!" rclpy.shutdown() if __name__ == '__main__': @@ -31,10 +37,10 @@ def _timer_callback(self): isolate_rmw_by_path(os.environ['TEST_TMPDIR']) rclpy.init() - + uuid = os.environ.get('TEST_TMPDIR', 'none') try: executor = rclpy.executors.SingleThreadedExecutor() - rclpy.spin(IsolatedListener(), executor) + rclpy.spin(IsolatedListener(uuid), executor) finally: # NOTE(hidmic): try_shutdown raises AttributeError # Need https://github.com/ros2/rclpy/pull/812 diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.cc b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.cc new file mode 100644 index 000000000..e6930a908 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.cc @@ -0,0 +1,45 @@ +#include +#include + +#include +#include + +#include "rmw_isolation/rmw_isolation.h" + +class IsolatedTalker : public rclcpp::Node +{ + public: + IsolatedTalker(const std::string & uuid) + : rclcpp::Node("isolated_talker"), uuid_(uuid) + { + publisher_ = this->create_publisher("uuid", 10); + timer_ = this->create_wall_timer( + std::chrono::duration(0.1), + std::bind(&IsolatedTalker::timer_callback, this)); + } + + private: + void timer_callback() const + { + std_msgs::msg::String message; + message.data = uuid_; + publisher_->publish(message); + } + + rclcpp::Publisher::SharedPtr publisher_; + rclcpp::TimerBase::SharedPtr timer_; + std::string uuid_; +}; + +int main(int argc, char * argv[]) +{ + const char * TEST_TMPDIR = std::getenv("TEST_TMPDIR"); + if (TEST_TMPDIR != nullptr) { + ros2::isolate_rmw_by_path(argv[0], TEST_TMPDIR); + } + rclcpp::init(argc, argv); + std::string uuid{TEST_TMPDIR != nullptr? TEST_TMPDIR : "none"}; + rclcpp::spin(std::make_shared(uuid)); + rclcpp::shutdown(); + return 0; +} diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py new file mode 100644 index 000000000..1a4e4a7e7 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py @@ -0,0 +1,35 @@ +import os + +import rclpy +import rclpy.node +import std_msgs.msg + + +class IsolatedTalker(rclpy.node.Node): + + def __init__(self, uuid): + super().__init__('isolated_talker') + self._publisher = self.create_publisher( + std_msgs.msg.String, 'uuid', 10) + self._timer = self.create_timer(0.1, self._timer_callback) + self._uuid = uuid + + def _timer_callback(self): + msg = std_msgs.msg.String() + msg.data = self._uuid + self._publisher.publish(msg) + +if __name__ == '__main__': + if 'TEST_TMPDIR' in os.environ: + from rmw_isolation import isolate_rmw_by_path + isolate_rmw_by_path(os.environ['TEST_TMPDIR']) + + rclpy.init() + + uuid = os.environ.get('TEST_TMPDIR', 'none') + try: + rclpy.spin(IsolatedTalker(uuid)) + except KeyboardInterrupt: + pass + finally: + rclpy.shutdown() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/rmw_isolation_test.sh b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/rmw_isolation_test.sh index 977bbbeb6..c639e891d 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/rmw_isolation_test.sh +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/rmw_isolation_test.sh @@ -1,7 +1,39 @@ -#!/bin/sh +#!/bin/bash -talker=$1 -listener=$2 -$talker --ros-args --disable-external-lib-logs -- & $listener --ros-args --disable-external-lib-logs -p timeout:=2.0 -rc=$?; kill $! -exit $rc +TALKER_EXECUTABLE=$1 +LISTENER_EXECUTABLE=$2 +TIMEOUT=2.0 # in seconds + +# Avoid issues when trying to expand '~' +DEFAULT_ROS_ARGS="--disable-external-lib-logs" + +function test_isolation +{ + rc=0 + + # Start talker in the background + TALKER_ROS_ARGS=$DEFAULT_ROS_ARGS + $TALKER_EXECUTABLE --ros-args $TALKER_ROS_ARGS -- & + TALKER_PID=$! + + # Run listener in the foreground + LISTENER_ROS_ARGS="$DEFAULT_ROS_ARGS --param timeout:=$TIMEOUT" + if ! $LISTENER_EXECUTABLE --ros-args $LISTENER_ROS_ARGS; then + # Listener exited with an error + rc=$? + fi + + # Kill talker in the background + if ! kill $TALKER_PID; then + # Talker exited too early, likely with an error + wait $TALKER_PID + rc=$? + fi + + return $rc +} + +# Run as many concurrent tests as shards are available +[ -z "${TEST_SHARD_STATUS_FILE-}" ] || touch "$TEST_SHARD_STATUS_FILE" + +test_isolation diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/talker.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/talker.py deleted file mode 100644 index 3796137ba..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/talker.py +++ /dev/null @@ -1,27 +0,0 @@ -import rclpy -import rclpy.node -import std_msgs.msg - - -class Talker(rclpy.node.Node): - - def __init__(self): - super().__init__('talker') - self._publisher = self.create_publisher( - std_msgs.msg.String, 'topic', 10) - self._timer = self.create_timer(0.1, self._timer_callback) - - def _timer_callback(self): - msg = std_msgs.msg.String() - msg.data = 'Howdy' - self._publisher.publish(msg) - -if __name__ == '__main__': - rclpy.init() - - try: - rclpy.spin(Talker()) - except KeyboardInterrupt: - pass - finally: - rclpy.shutdown() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl index 76c986e16..5270b3c92 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -8,7 +8,6 @@ PACKAGE_MANIFEST = [ "ros_py.bzl", "rosidl.bzl", - "rmw_isolation/BUILD.bazel", "rmw_isolation/__init__.py", "rmw_isolation/isolated_rmw_env.py", "rmw_isolation/rmw_isolation.cc.in", @@ -16,8 +15,9 @@ PACKAGE_MANIFEST = [ "rmw_isolation/rmw_isolation.py", "rmw_isolation/test/isolated_listener.cc", "rmw_isolation/test/isolated_listener.py", + "rmw_isolation/test/isolated_talker.cc", + "rmw_isolation/test/isolated_talker.py", "rmw_isolation/test/rmw_isolation_test.sh", - "rmw_isolation/test/talker.py", ] GENERATE_TOOL_RESOURCES_MANIFEST = [ @@ -53,6 +53,9 @@ def _label(relpath): return Label("//tools/skylark/ros2:" + relpath) def _impl(repo_ctx): + repo_ctx.symlink( + _label("resources/rmw_isolation/build.bazel"), + "rmw_isolation/BUILD.bazel") for relpath in PACKAGE_MANIFEST: repo_ctx.symlink(_label("resources/" + relpath), relpath) From d9dcd224451f0a3080e5a7bc537f493f376684d5 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 9 Nov 2021 11:53:38 -0300 Subject: [PATCH 077/107] Avoid half upgrades when installing prereqs Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/setup/install_prereqs.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/drake_ros_bazel_installed/setup/install_prereqs.sh b/drake_ros_bazel_installed/setup/install_prereqs.sh index f7b4f1f4d..9851224e0 100755 --- a/drake_ros_bazel_installed/setup/install_prereqs.sh +++ b/drake_ros_bazel_installed/setup/install_prereqs.sh @@ -14,6 +14,7 @@ apt update && apt install bazel curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/ros2.list apt update && apt install ros-rolling-ros-base ros-rolling-rmw-fastrtps-cpp ros-rolling-rmw-cyclonedds-cpp +apt install --only-upgrade $(apt-cache pkgnames ros-rolling) # NOTE(hidmic): avoid half upgrades (shouldn't the ros-rolling-ros-base install prevent that?) # Install Python dependencies apt install python3 python3-toposort python3-dev python-is-python3 From c9264db8b9cb3506a7e33a565d4267f28e22217c Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 9 Nov 2021 12:13:24 -0300 Subject: [PATCH 078/107] Reduce Bazel ROS CI times Signed-off-by: Michel Hidalgo --- .github/workflows/bazelized.yml | 2 +- drake_ros_bazel_installed/setup/install_prereqs.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bazelized.yml b/.github/workflows/bazelized.yml index de239dd65..71b2c55bc 100644 --- a/.github/workflows/bazelized.yml +++ b/.github/workflows/bazelized.yml @@ -14,7 +14,7 @@ jobs: build_and_test: runs-on: ubuntu-latest container: - image: ros:rolling-ros-core-focal + image: ros:rolling-ros-base-focal steps: - uses: actions/checkout@v2 - uses: actions/cache@v2 diff --git a/drake_ros_bazel_installed/setup/install_prereqs.sh b/drake_ros_bazel_installed/setup/install_prereqs.sh index 9851224e0..2ddf16b41 100755 --- a/drake_ros_bazel_installed/setup/install_prereqs.sh +++ b/drake_ros_bazel_installed/setup/install_prereqs.sh @@ -14,7 +14,7 @@ apt update && apt install bazel curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/ros2.list apt update && apt install ros-rolling-ros-base ros-rolling-rmw-fastrtps-cpp ros-rolling-rmw-cyclonedds-cpp -apt install --only-upgrade $(apt-cache pkgnames ros-rolling) # NOTE(hidmic): avoid half upgrades (shouldn't the ros-rolling-ros-base install prevent that?) +apt install --only-upgrade $(dpkg-query -f '${Package}\n' -W 'ros-rolling-*') # NOTE(hidmic): avoid half upgrades (shouldn't the ros-rolling-ros-base install prevent that?) # Install Python dependencies apt install python3 python3-toposort python3-dev python-is-python3 From d28ae86f06138081b919eeb9908a7e0de014dd1d Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 7 Dec 2021 11:06:34 -0300 Subject: [PATCH 079/107] Clarify drake_ros_common README.md Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/drake_ros_common/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drake_ros_bazel_installed/drake_ros_common/README.md b/drake_ros_bazel_installed/drake_ros_common/README.md index 7d01e36fd..9128c1fce 100644 --- a/drake_ros_bazel_installed/drake_ros_common/README.md +++ b/drake_ros_bazel_installed/drake_ros_common/README.md @@ -1,3 +1,5 @@ ## Common `drake_ros` interfaces This package exercises `rosidl` interface generation in C++ and Python. + +**Note**: this package does not host utilities but isolates commonalities among examples, enabling reuse. From e40de0161d099c0500b6edcc7bc2f937e147b031 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 7 Dec 2021 11:57:26 -0300 Subject: [PATCH 080/107] Improve ROS 2 Skylark tools documentation. Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/README.md | 102 +++++++++++++----- .../skylark/ros2/generate_repository_files.py | 2 +- .../tools/skylark/ros2/ros2.bzl | 4 +- 3 files changed, 77 insertions(+), 31 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/README.md b/drake_ros_bazel_installed/tools/skylark/ros2/README.md index 182187822..29eafcbcc 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/README.md +++ b/drake_ros_bazel_installed/tools/skylark/ros2/README.md @@ -1,15 +1,21 @@ ## Infrastructure to use ROS 2 from a Bazel workspace -This package encapsulates all the machinery to pull a ROS 2 workspace install space or a subset thereof as a Bazel repository. -Both system-installed binary distributions and source builds can pulled in this way, whether symlink- or merged-installed. - -A single repository rule, `ros2_local_repository()`, is the sole entrypoint. This rule heavily relies on two Python packages: - -- the `cmake_tools` Python package, which provides an idiomatic API to collect a CMake project's exported configuration. -- the `ros2bzl` Python package, which provides tools to crawl a ROS 2 workspace install space, collects CMake packages' - exported configuration, collect Python packages' egg metadata, symlink relevant directories and files, and generates - a root BUILD.bazel file that recreates the dependency graph in the workspace. This package constitutes the backbone of - the `generate_repository_files.py` Python binary which `ros2_local_repository()` invokes. +This package encapsulates all the machinery to pull a ROS 2 workspace install +space or a subset thereof as a Bazel repository. Both system-installed binary +distributions and source builds can pulled in this way, whether symlink- or +merged-installed. + +A single repository rule, `ros2_local_repository()`, is the sole entrypoint. +This rule heavily relies on two Python packages: + +- The `cmake_tools` Python package, which provides an idiomatic API to collect + a CMake project's exported configuration. +- The `ros2bzl` Python package, which provides tools to crawl a ROS 2 workspace + install space, collects CMake packages' exported configuration, collect Python + packages' egg metadata, symlink relevant directories and files, and generates + a root BUILD.bazel file that recreates the dependency graph in the workspace. + This package constitutes the backbone of the `generate_repository_files.py` + Python binary which `ros2_local_repository()` invokes. ### Repository layout @@ -27,33 +33,73 @@ A ROS 2 local repository has the following layout: └── rosidl.bzl ``` -Note that all files and subdirectories that are mere implementation details have been excluded from this layout. +Note that all files and subdirectories that are mere implementation details +have been excluded from this layout. #### Targets -For every package in the underlying ROS 2 workspace install space, the following targets may be found at the root `BUILD.bazel` file: - -- A `_share` filegroup for every package that installs artifacts under the share directory. -- A `_cc` C/C++ library for every CMake package that installs C++ binary artifacts (in addition to compiler/linker flags, includes, etc.). -- A `_py` Python library for every package that installs a Python egg. -- A `_defs` filegroup for every package that installs ROS 2 interfaces (.msg, .srv, .action, and .idl files). -- A `_c` C/C++ library for every CMake package that installs C binary artifacts. Typically an alias of the `_cc` C/C++ library if C and C++ binary artifacts cannot be told apart. -- A `_transitively_py` Python library for every package that does not install a Python egg yet it depends and it is a dependency of packages that do. This helps maintain the dependency graph. -- A `_` for every executable installed at the package-level (i.e. under `lib/`, where `ros2 run` can find them). -- A `` for every executable installed under the `bin` directory (and thus in the `$PATH`). +For each package in the underlying ROS 2 workspace install space, depending on +the artifacts it generates, the following targets may be found at the root +`BUILD.bazel` file: + +- A `_share` filegroup for files in the `/share` + directory but excluding those files that are build system specific. +- A `_cc` C/C++ library for C++ libraries, typically found in + the `/lib` directory. In addition to headers, compiler flags, + linker flags, etc., C/C++ library targets for upstream packages that are + immediate dependencies are declared as dependencies themselves. +- A `_py` Python library for Python eggs in the + `/lib/python*/site-packages` directory. All Python library + targets for upstream packages that are immediate dependencies are declared + as dependencies themselves. +- A `_defs` filegroup for interface definition files (.msg, .srv, + .action, and .idl files) in the `/share` directory. +- A `_c` C/C++ library for C libraries. Typically an alias of the + `_cc` target if C and C++ libraries cannot be told apart. +- A `_transitively_py` Python library if the package has does not + install any Python libraries but it depends on (and it is a dependency of) + packages that do. This helps maintain the dependency graph (as Python library + targets can only depend on other Python library targets). +- A `_` Python binary for each executable + installed at the package-level (i.e. under `lib/`, where + `ros2 run` can find them). +- A `` Python binary for each executable installed under the + `/bin` directory (and thus accessible via `$PATH` when + sourcing the workspace install space). #### Rules -To build C++ binaries and tests that depend on ROS 2, `ros_cc_binary` and `ros_cc_test` rules are available in the `ros_cc.bzl` file. These rules, equivalent to the native `cc_binary` and `cc_test` rules, ensure these binaries run in a environment that is tightly coupled with the underlying ROS 2 workspace install space. - -To build Python binaries and tests that depend on ROS 2, `ros_py_binary` and `ros_pytest` rules are available in the `ros_py.bzl` file. These rules, equivalent to the native `py_binary` and `py_test` rules, ensure these binaries run in a environment that is tightly coupled with the underlying ROS 2 workspace install space. - -To generate and build ROS 2 interfaces, a `rosidl_interfaces_group` rule is available in the `rosidl.bzl` file. This rule generates C++ and Python code for the given ROS 2 interface definition files and builds them, off of what is available in the ROS 2 workspace install space: code generators, interface definition translators, runtime dependencies, etc. Several targets are created, following strict naming conventions (e.g. C++ and Python interface libraries carry `_cc` and `_py` suffixes, respectively), though finer-grained control over what is generated and built can be achieved through via other rules available in the same file. By default, these naming conventions allow downstream `rosidl_interfaces_group` rules to depend on upstream `rosidl_interface_group` rules. +To build C++ binaries and tests that depend on ROS 2, `ros_cc_binary` and +`ros_cc_test` rules are available in the `ros_cc.bzl` file. These rules, +equivalent to the native `cc_binary` and `cc_test` rules, ensure these binaries +run in a environment that is tightly coupled with the underlying ROS 2 workspace +install space. + +To build Python binaries and tests that depend on ROS 2, `ros_py_binary` and +`ros_pytest` rules are available in the `ros_py.bzl` file. These rules, +equivalent to the native `py_binary` and `py_test` rules, ensure these binaries +run in a environment that is tightly coupled with the underlying ROS 2 workspace +install space. + +To generate and build ROS 2 interfaces, a `rosidl_interfaces_group` rule is +available in the `rosidl.bzl` file. This rule generates C++ and Python code +for the given ROS 2 interface definition files and builds them using what is +available in the ROS 2 workspace install space: code generators, interface +definition translators, runtime dependencies, etc. Several targets are created, +following strict naming conventions (e.g. C++ and Python interface libraries +carry `_cc` and `_py` suffixes, respectively), though finer-grained control over +what is generated and built can be achieved through via other rules available in +the same file. By default, these naming conventions allow downstream +`rosidl_interfaces_group` rules to depend on upstream `rosidl_interface_group` +rules. #### Tools -The `rmw_isolation` subpackage provides C++ and Python `isolate_rmw_by_path` APIs to enforce RMW network isolation. To that end, a unique path must be provided (such as Bazel's `$TEST_TMPDIR`). +The `rmw_isolation` subpackage provides C++ and Python `isolate_rmw_by_path` +APIs to enforce RMW network isolation. To that end, a unique path must be +provided (such as Bazel's `$TEST_TMPDIR`). #### Metadata -The `distro.bzl` file bears relevant ROS 2 workspace metadata for rules, tools, and downstream packages to use. +The `distro.bzl` file bears relevant ROS 2 workspace metadata for rules, tools, +and downstream packages to use. diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py index 00a84aa4b..b6680bf1d 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ -Scraps ROS 2 workspaces and exposes their artifacts through a Bazel local repository. +Scrapes ROS 2 workspaces and exposes their artifacts through a Bazel local repository. This script generates: diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl index 5270b3c92..3445b3aac 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -101,12 +101,12 @@ ros2_local_repository = repository_rule( implementation = _impl, ) """ -Scraps ROS 2 workspaces and exposes its artifacts as a Bazel local repository. +Scrapes ROS 2 workspaces and exposes its artifacts as a Bazel local repository. Args: workspaces: paths to ROS 2 workspace install trees. Each workspace specified overlays the previous one. include_packages: optional set of packages to include, with its recursive dependencies. Defaults to all. - exclude_packages: optional set of packages to exclude, with precedence over included files. Defaults to none. + exclude_packages: optional set of packages to exclude, with precedence over included packages. Defaults to none. extra_data: optional extra data dependencies for given targets jobs: number of CMake jobs to use during package configuration and scrapping. Defaults to using all cores. """ From bb2ec9a4f4f840c85098aee33e9bbea098e086cf Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 7 Dec 2021 11:59:31 -0300 Subject: [PATCH 081/107] Use transitive instead of transitively Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/tools/skylark/ros2/README.md | 2 +- .../tools/skylark/ros2/generate_repository_files.py | 2 +- .../tools/skylark/ros2/ros2bzl/templates.py | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/README.md b/drake_ros_bazel_installed/tools/skylark/ros2/README.md index 29eafcbcc..dc9edf55e 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/README.md +++ b/drake_ros_bazel_installed/tools/skylark/ros2/README.md @@ -56,7 +56,7 @@ the artifacts it generates, the following targets may be found at the root .action, and .idl files) in the `/share` directory. - A `_c` C/C++ library for C libraries. Typically an alias of the `_cc` target if C and C++ libraries cannot be told apart. -- A `_transitively_py` Python library if the package has does not +- A `_transitive_py` Python library if the package has does not install any Python libraries but it depends on (and it is a dependency of) packages that do. This helps maintain the dependency graph (as Python library targets can only depend on other Python library targets). diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py index b6680bf1d..bba7a13ad 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -174,7 +174,7 @@ def generate_build_file(repo_name, distro, cache, extras, sandbox): if any('py' in metadata.get('langs', []) for metadata in dependencies.values() ): - metadata['langs'].add('py (transitively)') + metadata['langs'].add('py (transitive)') # Dependencies still need to be propagated. _, template, config = configure_package_meta_py_library( name, metadata, dependencies) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py index 2edc9f9be..a00ddac58 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py @@ -31,7 +31,7 @@ def labels_with(suffix): c_name, c_label = labels_with(suffix='_c') cc_name, cc_label = labels_with(suffix='_cc') py_name, py_label = labels_with(suffix='_py') -meta_py_name, meta_py_label = labels_with(suffix='_transitively_py') +meta_py_name, meta_py_label = labels_with(suffix='_transitive_py') def configure_package_share_filegroup(name, metadata, sandbox): @@ -136,7 +136,7 @@ def configure_package_meta_py_library(name, metadata, dependencies): for dependency_name, dependency_metadata in dependencies.items(): if 'py' in dependency_metadata.get('langs', []): deps.append(py_label(dependency_name, dependency_metadata)) - elif 'py (transitively)' in dependency_metadata.get('langs', []): + elif 'py (transitive)' in dependency_metadata.get('langs', []): deps.append(meta_py_label(dependency_name, dependency_metadata)) target_name = meta_py_name(name, metadata) return ( @@ -165,7 +165,7 @@ def configure_package_py_library(name, metadata, properties, dependencies, extra for dependency_name, dependency_metadata in dependencies.items(): if 'py' in dependency_metadata.get('langs', []): deps.append(py_label(dependency_name, dependency_metadata)) - elif 'py (transitively)' in dependency_metadata.get('langs', []): + elif 'py (transitive)' in dependency_metadata.get('langs', []): deps.append(meta_py_label(dependency_name, dependency_metadata)) config['deps'] = deps @@ -246,7 +246,7 @@ def configure_executable_imports( common_data.append(cc_label(dependency_name, dependency_metadata)) if 'py' in dependency_metadata.get('langs', []): deps.append(py_label(dependency_name, dependency_metadata)) - elif 'py (transitively)' in dependency_metadata.get('langs', []): + elif 'py (transitive)' in dependency_metadata.get('langs', []): common_data.append(meta_py_label( dependency_name, dependency_metadata)) From 98042d4d332416a095b6640b8ee980b008ef667a Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 20 Dec 2021 13:15:53 -0300 Subject: [PATCH 082/107] Improve install prerequisites - Force user intervention upon every system change - Use Bazel 4.2.1 to match Drake's prerequisites Signed-off-by: Michel Hidalgo --- .../setup/install_prereqs.sh | 57 +++++++++++++++++-- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/drake_ros_bazel_installed/setup/install_prereqs.sh b/drake_ros_bazel_installed/setup/install_prereqs.sh index 2ddf16b41..d7929b5ae 100755 --- a/drake_ros_bazel_installed/setup/install_prereqs.sh +++ b/drake_ros_bazel_installed/setup/install_prereqs.sh @@ -2,13 +2,58 @@ set -eux pipefail -apt update && apt install -y apt-transport-https curl gnupg lsb-release cmake build-essential gettext-base +apt update && apt install apt-transport-https curl gnupg lsb-release cmake build-essential gettext-base coreutils -# Install Bazel -curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg -mv bazel.gpg /etc/apt/trusted.gpg.d/ -echo "deb [arch=$(dpkg --print-architecture)] https://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list -apt update && apt install bazel +# Install Bazel (derived from setup/ubuntu/source_distribution/install_prereqs.sh at +# https://github.com/RobotLocomotion/drake/tree/e91e62f524788081a8fd231129b64ff80607c1dd) +function dpkg_install_from_curl() { + package="$1" + version="$2" + url="$3" + checksum="$4" + + installed_version=$(dpkg-query --showformat='${Version}\n' --show "${package}" 2>/dev/null || true) + if [[ "${installed_version}" != "" ]]; then + # Skip the install if we're already at the exact version. + if [[ "${installed_version}" == "${version}" ]]; then + echo "${package} is already at the desired version ${version}" + return + fi + + # If installing our desired version would be a downgrade or an upgrade, ask the user first. + echo "This system has ${package} version ${installed_version} installed." + action="upgrade" # Assume an upgrade + if dpkg --compare-versions "${installed_version}" gt "${version}"; then + action="downgrade" # Switch to a downgrade + fi + echo "Drake ROS intends to ${action} to version ${version}, the supported version." + read -r -p "Do you want to ${action}? [Y/n] " reply + if [[ ! "${reply}" =~ ^([yY][eE][sS]|[yY])*$ ]]; then + echo "Skipping ${package} ${version} installation." + return + fi + fi + # Download and verify. + tmpdeb="/tmp/${package}_${version}-amd64.deb" + curl -sSL "${url}" -o "${tmpdeb}" + if echo "${checksum} ${tmpdeb}" | sha256sum -c -; then + echo # Blank line between checkout output and dpkg output. + else + echo "ERROR: The ${package} deb does NOT have the expected SHA256. Not installing." >&2 + exit 2 + fi + + # Install. + dpkg -i "${tmpdeb}" + rm "${tmpdeb}" +} + +apt install g++ unzip zlib1g-dev + +dpkg_install_from_curl \ + bazel 4.2.1 \ + https://releases.bazel.build/4.2.1/release/bazel_4.2.1-linux-x86_64.deb \ + 67447658b8313316295cd98323dfda2a27683456a237f7a3226b68c9c6c81b3a # Install ROS 2 Rolling curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg From 0bffdc958b5986d58383de42b7606d4e52a1ff4f Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 20 Dec 2021 13:48:05 -0300 Subject: [PATCH 083/107] Add comment about execv() error handling Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/tools/skylark/dload_cc.bzl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl b/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl index ca907e4ef..d91795822 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload_cc.bzl @@ -80,6 +80,8 @@ int main(int argc, const char * argv[]) {{ }} other_argv[argc] = NULL; int ret = execv(other_argv[0], other_argv); + // What follows applies if and only if execv() itself fails + // (e.g. can't find the binary) and returns control std::cout << "ERROR: " << strerror(errno) << std::endl; return ret; }} From 84c8910c2794ee8f9b0046abe4387c38372625d6 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 20 Dec 2021 13:52:14 -0300 Subject: [PATCH 084/107] Use scraping, not scrapping Signed-off-by: Michel Hidalgo --- .../skylark/ros2/generate_repository_files.py | 14 +++++++------- .../ros2bzl/{scrapping => scraping}/__init__.py | 4 ++-- .../ros2bzl/{scrapping => scraping}/ament_cmake.py | 8 ++++---- .../{scrapping => scraping}/ament_python.py | 4 ++-- .../ros2bzl/{scrapping => scraping}/metadata.py | 0 .../ros2/ros2bzl/{scrapping => scraping}/system.py | 0 .../tools/skylark/ros2/ros2bzl/templates.py | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) rename drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/{scrapping => scraping}/__init__.py (96%) rename drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/{scrapping => scraping}/ament_cmake.py (97%) rename drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/{scrapping => scraping}/ament_python.py (96%) rename drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/{scrapping => scraping}/metadata.py (100%) rename drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/{scrapping => scraping}/system.py (100%) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py index bba7a13ad..3889a55c1 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -25,15 +25,15 @@ # `cmake_tools` reachable through PYTHONPATH. Thus, we force it here. sys.path.insert(0, os.path.dirname(__file__)) # noqa -from ros2bzl.scrapping import load_distribution -from ros2bzl.scrapping.ament_cmake \ +from ros2bzl.scraping import load_distribution +from ros2bzl.scraping.ament_cmake \ import collect_ament_cmake_package_properties -from ros2bzl.scrapping.ament_cmake \ +from ros2bzl.scraping.ament_cmake \ import collect_ament_cmake_package_direct_properties -from ros2bzl.scrapping.ament_cmake import precache_ament_cmake_properties -from ros2bzl.scrapping.ament_python \ +from ros2bzl.scraping.ament_cmake import precache_ament_cmake_properties +from ros2bzl.scraping.ament_python \ import collect_ament_python_package_direct_properties -from ros2bzl.scrapping.ament_python import PackageNotFoundError +from ros2bzl.scraping.ament_python import PackageNotFoundError from ros2bzl.templates import configure_distro from ros2bzl.templates import configure_executable_imports @@ -83,7 +83,7 @@ def parse_arguments(): ) parser.add_argument( '-j', '--jobs', metavar='N', type=int, default=None, - help='Number of CMake jobs to use during package configuration and scrapping' + help='Number of CMake jobs to use during package configuration and scraping' ) args = parser.parse_args() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/__init__.py similarity index 96% rename from drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py rename to drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/__init__.py index d65079b9f..018fe036f 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/__init__.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/__init__.py @@ -3,8 +3,8 @@ import ament_index_python import cmake_tools -from ros2bzl.scrapping.metadata import collect_cmake_package_metadata -from ros2bzl.scrapping.metadata import collect_ros_package_metadata +from ros2bzl.scraping.metadata import collect_cmake_package_metadata +from ros2bzl.scraping.metadata import collect_ros_package_metadata def list_all_executables(): diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py similarity index 97% rename from drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py rename to drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py index 28de8e8af..547072c47 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_cmake.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py @@ -8,10 +8,10 @@ from ros2bzl.resources import path_to_resource -from ros2bzl.scrapping.system import find_library_path -from ros2bzl.scrapping.system import find_library_dependencies -from ros2bzl.scrapping.system import is_system_include -from ros2bzl.scrapping.system import is_system_library +from ros2bzl.scraping.system import find_library_path +from ros2bzl.scraping.system import find_library_dependencies +from ros2bzl.scraping.system import is_system_include +from ros2bzl.scraping.system import is_system_library def collect_ament_cmake_shared_library_codemodel(codemodel): diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_python.py similarity index 96% rename from drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py rename to drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_python.py index c52f4ea7e..3387da17e 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/ament_python.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_python.py @@ -4,8 +4,8 @@ from importlib.metadata import distribution from importlib.metadata import PackageNotFoundError -from ros2bzl.scrapping.system import find_library_dependencies -from ros2bzl.scrapping.system import is_system_library +from ros2bzl.scraping.system import find_library_dependencies +from ros2bzl.scraping.system import is_system_library EXTENSION_SUFFIX = sysconfig.get_config_var('EXT_SUFFIX') diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/metadata.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/metadata.py similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/metadata.py rename to drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/metadata.py diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/system.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scrapping/system.py rename to drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py index a00ddac58..2f0316c55 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py @@ -1,7 +1,7 @@ import os from ros2bzl.resources import load_resource -from ros2bzl.scrapping.system import find_library_path +from ros2bzl.scraping.system import find_library_path from ros2bzl.utilities import to_starlark_string_dict From ff444cc7c9ef0edad5eb08cd715a3c870b8e8d78 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 20 Dec 2021 14:12:56 -0300 Subject: [PATCH 085/107] Add note on rationale behind forgoing rules_foreign_cc Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/tools/skylark/ros2/README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/README.md b/drake_ros_bazel_installed/tools/skylark/ros2/README.md index dc9edf55e..24416af28 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/README.md +++ b/drake_ros_bazel_installed/tools/skylark/ros2/README.md @@ -9,7 +9,7 @@ A single repository rule, `ros2_local_repository()`, is the sole entrypoint. This rule heavily relies on two Python packages: - The `cmake_tools` Python package, which provides an idiomatic API to collect - a CMake project's exported configuration. + a CMake project's exported configuration (see Note 1). - The `ros2bzl` Python package, which provides tools to crawl a ROS 2 workspace install space, collects CMake packages' exported configuration, collect Python packages' egg metadata, symlink relevant directories and files, and generates @@ -17,6 +17,14 @@ This rule heavily relies on two Python packages: This package constitutes the backbone of the `generate_repository_files.py` Python binary which `ros2_local_repository()` invokes. +**Note 1** +: [`rules_foreign_cc`](https://github.com/bazelbuild/rules_foreign_cc) tooling + was initially considered but later discarded, as it serves a fundamentally + different purpose. This infrastructure does **not** build CMake projects + within Bazel (like `rules_foreign_cc` does), it exposes the artifacts and + build configuration of pre-installed CMake projects for Bazel packages to + depend on. + ### Repository layout A ROS 2 local repository has the following layout: From bdaba927c828e0e11a5a750763fbd8862563310f Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 20 Dec 2021 14:17:38 -0300 Subject: [PATCH 086/107] Rename ros_py_import() as ros_import_binary() Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/resources/ros_py.bzl | 9 +++++---- .../resources/templates/overlay_executable.bazel.tpl | 2 +- .../skylark/ros2/resources/templates/prologue.bazel | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl index b716e01e9..04ef1b940 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl @@ -18,13 +18,14 @@ load( "RUNTIME_ENVIRONMENT" ) -def ros_py_import( +def ros_import_binary( name, executable, rmw_implementation = None, py_binary_rule = native.py_binary, **kwargs ): """ - Imports an existing, potentially pre-built executable by wrapping it with a shim that - will inject the minimal runtime environment necessary for execution when depending on - this ROS 2 local repository. + Imports an existing executable by wrapping it with a Python shim that will inject the + minimal runtime environment necessary for execution when depending on this ROS 2 local + repository. Imported executables need not be Python -- binary executables will work the + same. Akin to the cc_import() rule. diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/overlay_executable.bazel.tpl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/overlay_executable.bazel.tpl index 356a445cb..9213412d3 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/overlay_executable.bazel.tpl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/overlay_executable.bazel.tpl @@ -1,4 +1,4 @@ -ros_py_import( +ros_import_binary( name = @name@, executable = @executable@, data = @data@, diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/prologue.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/prologue.bazel index 3b957e4c5..5cb0d74eb 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/prologue.bazel +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/prologue.bazel @@ -5,4 +5,4 @@ package(default_visibility = ["//visibility:public"]) load(":common.bzl", "interfaces_filegroup") load(":common.bzl", "share_filegroup") -load(":ros_py.bzl", "ros_py_import") +load(":ros_py.bzl", "ros_import_binary") From 42469f37692ca1f09f92be42a28c8be58f59903e Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 20 Dec 2021 15:00:30 -0300 Subject: [PATCH 087/107] Annotate shimmed targets in ROS C++/Python rules Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/resources/ros_cc.bzl | 8 ++++---- .../tools/skylark/ros2/resources/ros_py.bzl | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_cc.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_cc.bzl index c6ffb7a24..88d5e9bc3 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_cc.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_cc.bzl @@ -39,7 +39,7 @@ def ros_cc_binary( native.cc_binary(name = name, **kwargs) return - binary_name = "_" + name + binary_name = "_" + name + "_shimmed" binary_kwargs = kwargs binary_env_changes = dict(RUNTIME_ENVIRONMENT) @@ -55,7 +55,7 @@ def ros_cc_binary( **binary_kwargs ) - shim_name = binary_name + "_shim.cc" + shim_name = "_" + name + "_shim.cc" shim_kwargs = filter_to_only_common_kwargs(kwargs) dload_cc_shim( name = shim_name, @@ -93,7 +93,7 @@ def ros_cc_test( Additional keyword arguments are forwarded to the `cc_test_rule` and to the `cc_binary_rule` (minus the test specific ones). """ - binary_name = "_" + name + binary_name = "_" + name + "_shimmed" binary_kwargs = remove_test_specific_kwargs(kwargs) binary_kwargs.update(testonly = True) binary_env_changes = dict(RUNTIME_ENVIRONMENT) @@ -109,7 +109,7 @@ def ros_cc_test( **binary_kwargs ) - shim_name = binary_name + "_shim.cc" + shim_name = "_" + name + "_shim.cc" shim_kwargs = filter_to_only_common_kwargs(kwargs) shim_kwargs.update(testonly = True) dload_cc_shim( diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl index 04ef1b940..8ac84bd9c 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl @@ -83,7 +83,7 @@ def ros_py_binary( Additional keyword arguments are forwarded to the `py_binary_rule`. """ - binary_name = "_" + name + binary_name = "_" + name + "_shimmed" binary_kwargs = dict(kwargs) if "main" not in binary_kwargs: binary_kwargs["main"] = name + ".py" @@ -101,7 +101,7 @@ def ros_py_binary( **binary_kwargs ) - shim_name = binary_name + "_shim.py" + shim_name = "_" + name + "_shim.py" shim_kwargs = filter_to_only_common_kwargs(kwargs) dload_py_shim( name = shim_name, @@ -143,7 +143,7 @@ def ros_py_test( Additional keyword arguments are forwarded to the `py_test_rule` and to the `py_binary_rule` (minus the test specific ones). """ - binary_name = "_" + name + binary_name = "_" + name + "_shimmed" binary_kwargs = remove_test_specific_kwargs(kwargs) binary_kwargs.update(testonly = True) binary_env_changes = dict(RUNTIME_ENVIRONMENT) @@ -159,7 +159,7 @@ def ros_py_test( **binary_kwargs ) - shim_name = binary_name + "_shim.py" + shim_name = "_" + name + "_shim.py" shim_kwargs = filter_to_only_common_kwargs(kwargs) shim_kwargs.update(testonly = True) dload_py_shim( From c8336af17d008b9e2646ddaa0ca847fb6cdda4d4 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 20 Dec 2021 15:03:34 -0300 Subject: [PATCH 088/107] Rename build.bazel as package.BUILD.bazel Signed-off-by: Michel Hidalgo --- .../rmw_isolation/{build.bazel => package.BUILD.bazel} | 0 drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/{build.bazel => package.BUILD.bazel} (100%) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/build.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel similarity index 100% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/build.bazel rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl index 3445b3aac..c5fd88ffe 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -54,7 +54,7 @@ def _label(relpath): def _impl(repo_ctx): repo_ctx.symlink( - _label("resources/rmw_isolation/build.bazel"), + _label("resources/rmw_isolation/package.BUILD.bazel"), "rmw_isolation/BUILD.bazel") for relpath in PACKAGE_MANIFEST: repo_ctx.symlink(_label("resources/" + relpath), relpath) From 78fb9397a8e007e571ac1e6731e6edd36acaece2 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 20 Dec 2021 15:05:41 -0300 Subject: [PATCH 089/107] Remove lru_cache instance Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/ros2bzl/resources.py | 1 - 1 file changed, 1 deletion(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/resources.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/resources.py index fe5fa0a3e..023d2001b 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/resources.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/resources.py @@ -10,7 +10,6 @@ ) -@functools.lru_cache(maxsize=None) def path_to_resource(name): return os.path.join(PATH_TO_RESOURCES, name) From 3faa640f34df55cb351e815e71f564c9cb8de818 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 20 Dec 2021 15:37:37 -0300 Subject: [PATCH 090/107] Improve ros2bzl.sandboxing module Signed-off-by: Michel Hidalgo --- .../skylark/ros2/generate_repository_files.py | 2 +- .../tools/skylark/ros2/ros2bzl/sandboxing.py | 29 ++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py index 3889a55c1..8a4c82ccd 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -98,7 +98,7 @@ def parse_arguments(): extras[attribute_name][target_name].append(rhs) args.extras = extras - args.sandbox = sandboxing.configure( + args.sandbox = sandboxing.make_symlink_forest_mapping( name=args.repository_name, mapping=dict( entry.partition(':')[0::2] for entry in args.sandbox ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/sandboxing.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/sandboxing.py index 975d0a7f8..237bb8c7c 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/sandboxing.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/sandboxing.py @@ -1,8 +1,30 @@ +""" +Utility functions to aid repository sandboxing of dependencies +external to the enclosing Bazel workspace. +""" + import os -def configure(name, mapping): - def sandbox(path, external=False): +def make_symlink_forest_mapping(name, mapping): + """ + Makes a function that maps paths outside a Bazel workspace + to paths within a repository in that workspace. + + All provided outer directory paths are symlinked at their + corresponding inner directory paths. Paths that cannot be + mapped will be returned unchanged. For paths to be found + among runfiles in runtime, set the ``external`` keyword + argument to True. + + :param name: name of the enclosing repository + :param mapping: a key-value mapping from outer directory paths + to inner directory paths + :returns: callable that takes a path outside the workspace + and yields a path inside the workspace, and optionall takes + an ``external`` keyword argument. + """ + def _map(path, external=False): path = os.path.normpath(path) for outer_path, inner_path in mapping.items(): if path.startswith(outer_path): @@ -13,11 +35,10 @@ def sandbox(path, external=False): path = os.path.join(name, path) return path return path - sandbox.kwargs = dict(name=name, mapping=mapping) for outer_path, inner_path in mapping.items(): if inner_path == '.': continue os.symlink(outer_path, inner_path, target_is_directory=True) - return sandbox + return _map From c8c51f0548c5089677f2d272e5f05439d8b59569 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 20 Dec 2021 16:01:33 -0300 Subject: [PATCH 091/107] Improve ros2bzl.scraping.system module Signed-off-by: Michel Hidalgo --- .../skylark/ros2/ros2bzl/scraping/system.py | 54 +++++++++++++++---- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py index 5a06670fa..5d07f136e 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py @@ -1,3 +1,8 @@ +""" +Utility functions to aid compilation and linkage configuration scraping by +resorting to (Linux) system-wide conventions and tooling. +""" + import os import re import subprocess @@ -7,6 +12,10 @@ def is_system_include(include_path): + """ + Checks whether `include_path` is in a system include directory + i.e. known to compilers. + """ include_path = os.path.realpath(include_path) return any(include_path.startswith(path) for path in DEFAULT_INCLUDE_DIRECTORIES) @@ -15,24 +24,39 @@ def is_system_include(include_path): def is_system_library(library_path): + """ + Checks whether `include_path` is in a system library directory + i.e. known to linkers. + """ library_path = os.path.realpath(library_path) return any(library_path.startswith(path) for path in DEFAULT_LINK_DIRECTORIES) -LD_LIBRARY_PATHS = [] -if 'LD_LIBRARY_PATH' in os.environ: - value = os.environ['LD_LIBRARY_PATH'] - LD_LIBRARY_PATHS = [path for path in value.split(':') if path] +LD_LIBRARY_PATHS = [ + path for path in os.environ.get('LD_LIBRARY_PATH', '').split(':') if path] + +def find_library_path(library_name, link_directories=None, link_flags=None): + """ + Finds the path to a library. -def find_library_path(library_name, link_directories=[], link_flags=[]): + To do so it relies on the GNU `ld` linker and its library naming spec, + effectively reusing all of its search logic. + + :param library_name: name of the library to be searched + :param link_directories: optional list of directories to search in + :param link_flags: optional list of linker flags to account for + :returns: the path to the library if found, None otherwise + """ # Adapted from ctypes.util.find_library_path() implementation. pattern = r'/(?:[^\(\)\s]*/)*lib{}\.[^\(\)\s]*'.format(library_name) cmd = ['ld', '-t'] - for path in link_directories: - cmd.extend(['-L', path]) - cmd.extend(link_flags) + if link_directories: + for path in link_directories: + cmd.extend(['-L', path]) + if link_flags: + cmd.extend(link_flags) for path in LD_LIBRARY_PATHS: cmd.extend(['-L', path]) for path in DEFAULT_LINK_DIRECTORIES: @@ -51,7 +75,7 @@ def find_library_path(library_name, link_directories=[], link_flags=[]): path = match.group(0) # Remove double forward slashes, if any return os.path.join('/', os.path.relpath(path, '/')) - except Exception as e: + except Exception: pass return None @@ -60,6 +84,15 @@ def find_library_path(library_name, link_directories=[], link_flags=[]): def find_library_dependencies(library_path): + """ + Lists all shared library dependencies of a given library. + + To do so, it relies on the `ldd` tool as found in Linux. + + :param library_path: path to the library to be inspected + :returns: a generator that iterates over the paths to + library dependencies + """ try: lines = subprocess.run( ['ldd', library_path], @@ -72,4 +105,5 @@ def find_library_dependencies(library_path): if match: yield match.group(1) except Exception: - return + pass + return From 93fa595feb5cc1edd56cdd0eb235be5865ef4a07 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 20 Dec 2021 16:09:09 -0300 Subject: [PATCH 092/107] Fail on /usr/local dependencies Signed-off-by: Michel Hidalgo --- .../skylark/ros2/ros2bzl/scraping/ament_cmake.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py index 547072c47..dd0d0f70c 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py @@ -58,6 +58,14 @@ def collect_ament_cmake_shared_library_codemodel(codemodel): library for library in library_plus_dependencies if library not in link_libraries and not is_system_library(library) ]) + # Fail on any /usr/local libraries + local_link_libraries = [ + path for path in link_libraries + if path.startswith('/usr/local')] + if local_link_libraries: + raise RuntimeError( + 'Found libraries under /usr/local: ' + + ', '.join(local_link_libraries)) file_groups = codemodel['fileGroups'] assert len(file_groups) == 1 @@ -69,6 +77,14 @@ def collect_ament_cmake_shared_library_codemodel(codemodel): entry['path'] for entry in file_group['includePath'] if not is_system_include(entry['path']) ]) + # Fail on any /usr/local include directories + local_include_directories = [ + path for path in include_directores + if path.startswith('/usr/local')] + if local_include_directories: + raise RuntimeError( + 'Found include directories under /usr/local: ' + + ', '.join(local_include_directories)) defines = [] if 'defines' in file_group: From 24c5c7bc866908505acd34a84d875ce5f77874ab Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 20 Dec 2021 16:21:30 -0300 Subject: [PATCH 093/107] Make all implementation details private Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/resources/ros_py.bzl | 2 +- .../tools/skylark/ros2/resources/rosidl.bzl | 262 +++++++++--------- .../tools/skylark/ros2/ros2.bzl | 10 +- .../ros2/ros2bzl/scraping/ament_cmake.py | 2 +- .../tools/skylark/ros2/ros2bzl/templates.py | 4 +- 5 files changed, 133 insertions(+), 147 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl index 8ac84bd9c..51e8d36ed 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/ros_py.bzl @@ -46,7 +46,7 @@ def ros_import_binary( rmw_implementation = rmw_implementation ) - shim_name = name + "_shim.py" + shim_name = "_" + name + "_shim.py" shim_kwargs = filter_to_only_common_kwargs(kwargs) dload_py_shim( name = shim_name, diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl index 4d580abbf..0c3957db7 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rosidl.bzl @@ -189,6 +189,46 @@ def _deduce_source_paths(group, kind): root = "{}/{}".format(include, group) return include, root +def _make_public_name(name, suffix=""): + """ + Builds a public name (i.e. with no leading underscore) from a + private or public name. + """ + return name.lstrip("_") + suffix + +def _make_private_name(name, suffix=""): + """ + Builds a private name (i.e. with leading underscore) from a + private or public name. + """ + if not name.startswith("_"): + name = "_" + name + return name + suffix + +def _make_public_label(label, suffix=""): + """ + Builds a public label (i.e. name with no leading underscore) + from a private or public label, or a plain name (assumed to + be local to the calling package). + """ + package, _, name = label.rpartition(":") + prefix, _, name = name.rpartition("/") + if prefix: + prefix = prefix + "/" + return package + ":" + prefix + _make_public_name(name, suffix) + +def _make_private_label(label, suffix=""): + """ + Builds a private label (i.e. name with leading underscore) + from a private or public label, or a plain name (assumed to + be local to the calling package). + """ + package, _, name = label.rpartition(":") + prefix, _, name = name.rpartition("/") + if prefix: + prefix = prefix + "/" + return package + ":" + prefix + _make_private_name(name, suffix) + def rosidl_c_library( name, group, interfaces, includes = [], deps = [], cc_library_rule = native.cc_library, **kwargs @@ -221,7 +261,7 @@ def rosidl_c_library( generated_sources = generated_c_sources + generated_c_headers rosidl_generate_genrule( - name = name + "_gen", + name = _make_private_name(name, "_gen"), generated_sources = generated_sources, types = ["c"], group = group, @@ -274,7 +314,7 @@ def rosidl_cc_library( generated_cc_headers.append("{}/{}/detail/{}__traits.hpp".format(root, parent, basename)) rosidl_generate_genrule( - name = name + "_gen", + name = _make_private_name(name, "_gen"), generated_sources = generated_cc_headers, types = ["cpp"], group = group, @@ -294,12 +334,9 @@ def rosidl_cc_library( **kwargs ) -def _c_typesupport_extension(group, typesupport_name): +def _make_c_typesupport_extension_name(group, typesupport_name): return "{}_s__{}".format(group, typesupport_name) + PYTHON_EXTENSION_SUFFIX -def _c_typesupport_extension_label(group, typesupport_name): - return ":" + _c_typesupport_extension(group, typesupport_name) - def rosidl_py_library( name, group, interfaces, typesupports, includes = [], c_deps = [], py_deps = [], @@ -349,7 +386,7 @@ def rosidl_py_library( generated_sources += generated_c_sources_per_typesupport[typesupport_name] rosidl_generate_genrule( - name = name + "_gen", + name = _make_private_name(name, "_gen"), generated_sources = generated_sources, types = [ "py[typesupport_implementations:[{}]]" @@ -363,16 +400,16 @@ def rosidl_py_library( ) cc_library_rule( - name = c(name), + name = _make_private_name(name, "_c"), srcs = generated_c_sources, deps = c_deps + [ - c(dep) for dep in py_deps + _make_private_label(dep, "_c") for dep in py_deps ] + ["@python_dev//:libs"], **kwargs ) c_typesupport_extension_deps = c_deps + [ - c_label(name), + _make_private_label(name, "_c"), REPOSITORY_ROOT + ":rosidl_generator_py_cc", REPOSITORY_ROOT + ":rosidl_runtime_c_cc", REPOSITORY_ROOT + ":rosidl_typesupport_c_cc", @@ -385,7 +422,7 @@ def rosidl_py_library( if typesupport_library not in deps: deps.append(typesupport_library) c_typesupport_extension = "{}/{}".format( - root, _c_typesupport_extension(group, typesupport_name)) + root, _make_c_typesupport_extension_name(group, typesupport_name)) cc_binary_rule( name = c_typesupport_extension, srcs = generated_c_sources_per_typesupport[typesupport_name], @@ -435,7 +472,7 @@ def rosidl_typesupport_fastrtps_cc_library( generated_sources = generated_cc_sources + generated_cc_headers rosidl_generate_genrule( - name = name + "_gen", + name = _make_private_name(name, "_gen"), generated_sources = generated_sources, typesupports = ["fastrtps_cpp"], group = group, @@ -446,7 +483,7 @@ def rosidl_typesupport_fastrtps_cc_library( ) cc_library_rule( - name = name + "_hdrs", + name = _make_private_name(name, "_hdrs"), hdrs = generated_cc_headers, includes = [include], linkstatic = True, @@ -457,7 +494,7 @@ def rosidl_typesupport_fastrtps_cc_library( name = name, srcs = generated_cc_sources, deps = deps + [ - ":" + name + "_hdrs", + _make_private_label(name, "_hdrs"), REPOSITORY_ROOT + ":fastcdr_cc", REPOSITORY_ROOT + ":rmw_cc", REPOSITORY_ROOT + ":rosidl_runtime_c_cc", @@ -501,7 +538,7 @@ def rosidl_typesupport_fastrtps_c_library( generated_sources = generated_c_sources + generated_c_headers rosidl_generate_genrule( - name = name + "_gen", + name = _make_private_name(name, "_gen"), generated_sources = generated_sources, typesupports = ["fastrtps_c"], group = group, @@ -512,7 +549,7 @@ def rosidl_typesupport_fastrtps_c_library( ) cc_library_rule( - name = name + "_hdrs", + name = _make_private_name(name, "_hdrs"), hdrs = generated_c_headers, includes = [include], linkstatic = True, @@ -524,7 +561,7 @@ def rosidl_typesupport_fastrtps_c_library( srcs = generated_c_sources, linkshared = True, deps = deps + [ - ":" + name + "_hdrs", + _make_private_label(name, "_hdrs"), REPOSITORY_ROOT + ":fastcdr_cc", REPOSITORY_ROOT + ":rmw_cc", REPOSITORY_ROOT + ":rosidl_runtime_c_cc", @@ -568,7 +605,7 @@ def rosidl_typesupport_introspection_c_library( generated_sources = generated_c_sources + generated_c_headers rosidl_generate_genrule( - name = name + "_gen", + name = _make_private_name(name, "_gen"), generated_sources = generated_sources, typesupports = ["introspection_c"], group = group, @@ -579,7 +616,7 @@ def rosidl_typesupport_introspection_c_library( ) cc_library_rule( - name = name + "_hdrs", + name = _make_private_name(name, "_hdrs"), hdrs = generated_c_headers, includes = [include], linkstatic = True, @@ -591,7 +628,7 @@ def rosidl_typesupport_introspection_c_library( srcs = generated_c_sources, linkshared = True, deps = deps + [ - ":" + name + "_hdrs", + _make_private_label(name, "_hdrs"), REPOSITORY_ROOT + ":rosidl_typesupport_introspection_c_cc", ], **kwargs @@ -629,7 +666,7 @@ def rosidl_typesupport_introspection_cc_library( generated_sources = generated_cc_sources + generated_cc_headers rosidl_generate_genrule( - name = name + "_gen", + name = _make_private_name(name, "_gen"), generated_sources = generated_sources, typesupports = ["introspection_cpp"], group = group, @@ -640,7 +677,7 @@ def rosidl_typesupport_introspection_cc_library( ) cc_library_rule( - name = name + "_hdrs", + name = _make_private_name(name, "_hdrs"), hdrs = generated_cc_headers, includes = [include], linkstatic = True, @@ -652,7 +689,7 @@ def rosidl_typesupport_introspection_cc_library( srcs = generated_cc_sources, linkshared = True, deps = deps + [ - ":" + name + "_hdrs", + _make_private_label(name, "_hdrs"), REPOSITORY_ROOT + ":rosidl_runtime_c_cc", REPOSITORY_ROOT + ":rosidl_runtime_cpp_cc", REPOSITORY_ROOT + ":rosidl_typesupport_interface_cc", @@ -693,7 +730,7 @@ def rosidl_typesupport_c_library( generated_sources = generated_c_sources + generated_c_headers rosidl_generate_genrule( - name = name + "_gen", + name = _make_private_name(name, "_gen"), generated_sources = generated_sources, typesupports = [ "c[typesupport_implementations:[{}]]" @@ -712,7 +749,10 @@ def rosidl_typesupport_c_library( includes = [include], linkshared = True, data = typesupports.values(), - deps = deps + [label + "_hdrs" for label in typesupports.values()] + [ + deps = deps + [ + _make_private_label(label, "_hdrs") + for label in typesupports.values() + ] + [ REPOSITORY_ROOT + ":rosidl_runtime_c_cc", REPOSITORY_ROOT + ":rosidl_typesupport_c_cc", REPOSITORY_ROOT + ":rosidl_typesupport_interface_cc", @@ -749,7 +789,7 @@ def rosidl_typesupport_cc_library( "{}/{}/{}__type_support.cpp".format(root, parent, basename)) rosidl_generate_genrule( - name = name + "_gen", + name = _make_private_name(name, "_gen"), generated_sources = generated_cc_sources, typesupports = [ "cpp[typesupport_implementations:[{}]]" @@ -768,7 +808,10 @@ def rosidl_typesupport_cc_library( data = typesupports.values(), includes = [include], linkshared = True, - deps = deps + [label + "_hdrs" for label in typesupports.values()] + [ + deps = deps + [ + _make_private_label(label, "_hdrs") + for label in typesupports.values() + ] + [ REPOSITORY_ROOT + ":rosidl_runtime_c_cc", REPOSITORY_ROOT + ":rosidl_runtime_cpp_cc", REPOSITORY_ROOT + ":rosidl_typesupport_cpp_cc", @@ -777,39 +820,6 @@ def rosidl_typesupport_cc_library( **kwargs ) -def defs(name): - return name + "_defs" - -def cc(name): - return name + "_cc" - -def cc_label(name): - return ":" + cc(name) - -def cc_types(name): - return name + "__rosidl_cpp" - -def cc_types_label(name): - return ":" + cc_types(name) - -def typesupport_cc(name): - return name + "__rosidl_typesupport_cpp" - -def typesupport_cc_label(name): - return ":" + typesupport_cc(name) - -def typesupport_introspection_cc(name): - return name + "__rosidl_typesupport_introspection_cpp" - -def typesupport_introspection_cc_label(name): - return ":" + typesupport_introspection_cc(name) - -def typesupport_fastrtps_cc(name): - return name + "__rosidl_typesupport_fastrtps_cpp" - -def typesupport_fastrtps_cc_label(name): - return ":" + typesupport_fastrtps_cc(name) - def rosidl_cc_support( name, interfaces, deps, group = None, cc_binary_rule = native.cc_binary, @@ -834,102 +844,71 @@ def rosidl_cc_support( Additional keyword arguments are those common to all rules. """ rosidl_cc_library( - name = cc_types(name), + name = _make_private_name(name, "__rosidl_cpp"), group = group or name, interfaces = interfaces, - includes = [defs(dep) for dep in deps], - deps = [cc(dep) for dep in deps], + includes = [_make_public_label(dep, "_defs") for dep in deps], + deps = [_make_public_label(dep, "_cc") for dep in deps], cc_library_rule = cc_library_rule, **kwargs ) typesupports = {} - + # NOTE: typesupport binary files must not have any leading + # underscores or C++ generated code will not be able to + # dlopen it. if "rosidl_typesupport_introspection_cpp" in AVAILABLE_TYPESUPPORT_LIST: rosidl_typesupport_introspection_cc_library( - name = typesupport_introspection_cc(name), + name = _make_public_name(name, "__rosidl_typesupport_introspection_cpp"), group = group or name, interfaces = interfaces, - includes = [defs(dep) for dep in deps], - deps = [cc_types_label(name)] + [cc(dep) for dep in deps], + includes = [_make_public_label(dep, "_defs") for dep in deps], + deps = [_make_private_label(name, "__rosidl_cpp")] + [ + _make_public_label(dep, "_cc") for dep in deps], cc_binary_rule = cc_binary_rule, cc_library_rule = cc_library_rule, **kwargs ) typesupports["rosidl_typesupport_introspection_cpp"] = \ - typesupport_introspection_cc_label(name) + _make_public_label(name, "__rosidl_typesupport_introspection_cpp") if "rosidl_typesupport_fastrtps_cpp" in AVAILABLE_TYPESUPPORT_LIST: rosidl_typesupport_fastrtps_cc_library( - name = typesupport_fastrtps_cc(name), + name = _make_public_name(name, "__rosidl_typesupport_fastrtps_cpp"), group = group or name, interfaces = interfaces, - includes = [defs(dep) for dep in deps], - deps = [cc_types_label(name)] + [cc(dep) for dep in deps], + includes = [_make_public_label(dep, "_defs") for dep in deps], + deps = [_make_private_label(name, "__rosidl_cpp")] + [ + _make_public_label(dep, "_cc") for dep in deps], cc_binary_rule = cc_binary_rule, cc_library_rule = cc_library_rule, **kwargs ) typesupports["rosidl_typesupport_fastrtps_cpp"] = \ - typesupport_fastrtps_cc_label(name) + _make_public_label(name, "__rosidl_typesupport_fastrtps_cpp") rosidl_typesupport_cc_library( - name = typesupport_cc(name), + name = _make_public_name(name, "__rosidl_typesupport_cpp"), typesupports = typesupports, group = group or name, interfaces = interfaces, - includes = [defs(dep) for dep in deps], - deps = [cc_types_label(name)] + [cc(dep) for dep in deps], + includes = [_make_public_label(dep, "_defs") for dep in deps], + deps = [_make_private_label(name, "__rosidl_cpp")] + [ + _make_public_label(dep, "_cc") for dep in deps], cc_binary_rule = cc_binary_rule, **kwargs ) cc_library_rule( - name = cc(name), + name = _make_public_name(name, "_cc"), srcs = [ - typesupport_cc_label(name) + _make_public_label(name, "__rosidl_typesupport_cpp") ] + typesupports.values(), - deps = [cc_types_label(name)], + deps = [_make_private_label(name, "__rosidl_cpp")], linkstatic = True, **kwargs ) -def c(name): - return name + "_c" - -def c_label(name): - return ":" + c(name) - -def c_types(name): - return name + "__rosidl_c" - -def c_types_label(name): - return ":" + c_types(name) - -def py(name): - return name + "_py" - -def py_label(name): - return ":" + py(name) - -def typesupport_c(name): - return name + "__rosidl_typesupport_c" - -def typesupport_c_label(name): - return ":" + typesupport_c(name) - -def typesupport_introspection_c(name): - return name + "__rosidl_typesupport_introspection_c" - -def typesupport_introspection_c_label(name): - return ":" + typesupport_introspection_c(name) - -def typesupport_fastrtps_c(name): - return name + "__rosidl_typesupport_fastrtps_c" - -def typesupport_fastrtps_c_label(name): - return ":" + typesupport_fastrtps_c(name) - def rosidl_py_support( name, interfaces, deps, group = None, cc_binary_rule = native.cc_binary, @@ -956,75 +935,82 @@ def rosidl_py_support( Additional keyword arguments are those common to all rules. """ rosidl_c_library( - name = c_types(name), + name = _make_private_name(name, "__rosidl_c"), group = group or name, interfaces = interfaces, - includes = [defs(dep) for dep in deps], - deps = [c(dep) for dep in deps], + includes = [_make_public_label(dep, "_defs") for dep in deps], + deps = [_make_public_label(dep, "_c") for dep in deps], cc_library_rule = cc_library_rule, **kwargs ) typesupports = {} - + # NOTE: typesupport binary files must not have any leading + # underscores or C generated code will not be able to + # dlopen it. if "rosidl_typesupport_introspection_c" in AVAILABLE_TYPESUPPORT_LIST: rosidl_typesupport_introspection_c_library( - name = typesupport_introspection_c(name), + name = _make_public_name(name, "__rosidl_typesupport_introspection_c"), group = group or name, interfaces = interfaces, - includes = [defs(dep) for dep in deps], - deps = [c_types_label(name)] + [c(dep) for dep in deps], + includes = [_make_public_label(dep, "_defs") for dep in deps], + deps = [_make_private_label(name, "__rosidl_c")] + [ + _make_public_label(dep, "_c") for dep in deps], cc_binary_rule = cc_binary_rule, cc_library_rule = cc_library_rule, **kwargs ) typesupports["rosidl_typesupport_introspection_c"] = \ - typesupport_introspection_c_label(name) + _make_public_label(name, "__rosidl_typesupport_introspection_c") if "rosidl_typesupport_fastrtps_c" in AVAILABLE_TYPESUPPORT_LIST: rosidl_typesupport_fastrtps_c_library( - name = typesupport_fastrtps_c(name), + name = _make_public_name(name, "__rosidl_typesupport_fastrtps_c"), group = group or name, interfaces = interfaces, - includes = [defs(dep) for dep in deps], - deps = [c_types_label(name)] + [c(dep) for dep in deps], + includes = [_make_public_label(dep, "_defs") for dep in deps], + deps = [_make_private_label(name, "__rosidl_c")] + [ + _make_public_label(dep, "_c") for dep in deps], cc_binary_rule = cc_binary_rule, cc_library_rule = cc_library_rule, **kwargs ) typesupports["rosidl_typesupport_fastrtps_c"] = \ - typesupport_fastrtps_c_label(name) + _make_public_label(name, "__rosidl_typesupport_fastrtps_c") rosidl_typesupport_c_library( - name = typesupport_c(name), + name = _make_public_name(name, "__rosidl_typesupport_c"), typesupports = typesupports, group = group or name, interfaces = interfaces, - includes = [defs(dep) for dep in deps], - deps = [c_types_label(name)] + [c(dep) for dep in deps], + includes = [_make_public_label(dep, "_defs") for dep in deps], + deps = [_make_private_label(name, "__rosidl_c")] + [ + _make_public_label(dep, "_c") for dep in deps], cc_binary_rule = cc_binary_rule, **kwargs ) - typesupports["rosidl_typesupport_c"] = typesupport_c_label(name) + typesupports["rosidl_typesupport_c"] = \ + _make_public_label(name, "__rosidl_typesupport_c") cc_library_rule( - name = c(name), + name = _make_public_name(name, "_c"), srcs = typesupports.values(), deps = [ - c_types_label(name) + _make_private_label(name, "__rosidl_c") ] + typesupports.values(), linkstatic = True, **kwargs ) rosidl_py_library( - name = py(name), + name = _make_public_name(name, "_py"), typesupports = typesupports, group = group or name, interfaces = interfaces, - includes = [defs(dep) for dep in deps], - py_deps = [py(dep) for dep in deps], - c_deps = [c_label(name)] + [c(dep) for dep in deps], + includes = [_make_public_label(dep, "_defs") for dep in deps], + py_deps = [_make_public_label(dep, "_py") for dep in deps], + c_deps = [_make_public_label(name, "_c")] + [ + _make_public_label(dep, "_c") for dep in deps], cc_binary_rule = cc_binary_rule, cc_library_rule = cc_library_rule, py_library_rule = py_library_rule, @@ -1058,10 +1044,10 @@ def rosidl_interfaces_group( Additional keyword arguments are those common to all rules. """ rosidl_definitions_filegroup( - name = defs(name), + name = _make_public_name(name, "_defs"), group = group or name, interfaces = interfaces, - includes = [defs(dep) for dep in deps], + includes = [_make_public_label(dep, "_defs") for dep in deps], **kwargs ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl index c5fd88ffe..03bdeae26 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -42,11 +42,11 @@ GENERATE_TOOL_RESOURCES_MANIFEST = [ "ros2bzl/resources.py", "ros2bzl/templates.py", "ros2bzl/__init__.py", - "ros2bzl/scrapping/system.py", - "ros2bzl/scrapping/metadata.py", - "ros2bzl/scrapping/ament_python.py", - "ros2bzl/scrapping/ament_cmake.py", - "ros2bzl/scrapping/__init__.py", + "ros2bzl/scraping/system.py", + "ros2bzl/scraping/metadata.py", + "ros2bzl/scraping/ament_python.py", + "ros2bzl/scraping/ament_cmake.py", + "ros2bzl/scraping/__init__.py", ] def _label(relpath): diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py index dd0d0f70c..b9c5104bd 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py @@ -79,7 +79,7 @@ def collect_ament_cmake_shared_library_codemodel(codemodel): ]) # Fail on any /usr/local include directories local_include_directories = [ - path for path in include_directores + path for path in include_directories if path.startswith('/usr/local')] if local_include_directories: raise RuntimeError( diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py index 2f0316c55..d4edb9200 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py @@ -184,11 +184,11 @@ def configure_package_py_library(name, metadata, properties, dependencies, extra if 'cc_libraries' in properties: template = 'templates/package_py_library_with_cc_libs.bazel.tpl' config.update({ - 'cc_name': c_name(target_name, metadata), + 'cc_name': c_name("_" + target_name, metadata), 'cc_libs': [sandbox(lib) for lib in properties['cc_libraries']], 'cc_deps': cc_deps }) - data.append(c_label(target_name, metadata)) + data.append(c_label("_" + target_name, metadata)) else: data.extend(cc_deps) # Prepare runfiles to support dynamic loading From 499f99bde476d6abd2b9bfb3e751a17cf2618513 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 21 Dec 2021 12:55:38 -0300 Subject: [PATCH 094/107] Revamp ROS 2 rmw_isolation package - Use compiler definition instead of a C++ template - Rename isolated_rmw_env as generate_isolated_rmw_env - Use an isolation function registry instead of globals() - Avoid polluting the global scope in Python tests - Use popen() to avoid temporary files - Avoid rmw configuration file collision Signed-off-by: Michel Hidalgo --- ...mw_env.py => generate_isolated_rmw_env.py} | 14 +- .../rmw_isolation/package.BUILD.bazel | 23 ++-- .../resources/rmw_isolation/rmw_isolation.cc | 123 ++++++++++++++++++ .../rmw_isolation/rmw_isolation.cc.in | 61 --------- .../resources/rmw_isolation/rmw_isolation.h | 7 +- .../resources/rmw_isolation/rmw_isolation.py | 35 ++--- .../rmw_isolation/test/isolated_listener.py | 7 +- .../rmw_isolation/test/isolated_talker.py | 7 +- .../tools/skylark/ros2/ros2.bzl | 4 +- 9 files changed, 176 insertions(+), 105 deletions(-) rename drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/{isolated_rmw_env.py => generate_isolated_rmw_env.py} (75%) create mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.cc delete mode 100644 drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.cc.in diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/isolated_rmw_env.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/generate_isolated_rmw_env.py similarity index 75% rename from drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/isolated_rmw_env.py rename to drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/generate_isolated_rmw_env.py index 9278558d3..0b3d38822 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/isolated_rmw_env.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/generate_isolated_rmw_env.py @@ -3,16 +3,20 @@ """ Generates a runtime environment for RMW isolation. -Environment variables are written in the NAME=VALUE format, -one per line, suitable for e.g. the `env` command and the -`putenv` API. +Environment variables are written in the NAME=VALUE format, one per line, +suitable for e.g. the `env` command and the `setenv` API. + +This script may be used to achieve isolation in a shell environment. +For a C++ (resp. Python) API to easily leverage isolation from within +C++ (resp. Python) executables, see the `rmw_isolation_cc` +(resp. `rmw_isolation_py`) libray. """ import argparse import pathlib import sys -from rmw_isolation import isolated_rmw_env +from rmw_isolation import generate_isolated_rmw_env def main(argv=None): parser = argparse.ArgumentParser( @@ -36,7 +40,7 @@ def main(argv=None): help=('Unique arbitrary identifier for isolation. ' 'Defaults to current working directory.')) args = parser.parse_args(argv) - for key, value in isolated_rmw_env( + for key, value in generate_isolated_rmw_env( args.unique_identifier, rmw_implementation=args.rmw_implementation, scratch_directory=args.scratch_directory diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel index a6ec480d0..56df67b0f 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel @@ -17,29 +17,22 @@ py_library( ) ros_py_binary( - name = "isolated_rmw_env", - srcs = ["isolated_rmw_env.py"], - main = "isolated_rmw_env.py", + name = "generate_isolated_rmw_env", + srcs = ["generate_isolated_rmw_env.py"], + main = "generate_isolated_rmw_env.py", deps = [":module_py"], legacy_create_init = False, ) -genrule( - name = "rmw_isolation_cc_gen", - srcs = ["rmw_isolation.cc.in"], - outs = ["rmw_isolation.cc"], - cmd = ( - "ISOLATED_RMW_ENV_PATH={}/rmw_isolation/isolated_rmw_env ".format( - repository_name().strip("@") or "." - ) + "envsubst < $< > $@" - ), -) - cc_library( name = "rmw_isolation_cc", srcs = ["rmw_isolation.cc"], hdrs = ["rmw_isolation.h"], - data = [":isolated_rmw_env"], + data = [":generate_isolated_rmw_env"], + local_defines = [ + "RMW_ISOLATION_ROOTPATH={}/rmw_isolation".format( + repository_name().strip("@") or ".") + ], deps = [ "@bazel_tools//tools/cpp/runfiles", "//:rclcpp_cc", diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.cc b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.cc new file mode 100644 index 000000000..e146fa978 --- /dev/null +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.cc @@ -0,0 +1,123 @@ +#include "rmw_isolation/rmw_isolation.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tools/cpp/runfiles/runfiles.h" +#include +#include + +#define LITERAL_STRINGIFY(x) #x +#define STRINGIFY(x) LITERAL_STRINGIFY(x) + +using bazel::tools::cpp::runfiles::Runfiles; + +namespace ros2 { + +namespace { + +// Non-copyable, non-movable wrapper to bound an object's lifetime +// to local scope. Useful for objects that do not follow RAII, such +// as heap-allocated libc objects. +template +class scoped_instance { + public: + scoped_instance(ValueT&& value, DeleterT&& deleter) + : value_(value), deleter_(deleter) {} + scoped_instance(const scoped_instance&) = delete; + scoped_instance(scoped_instance&&) = delete; + scoped_instance& operator=(const scoped_instance&) = delete; + scoped_instance& operator=(scoped_instance&&) = delete; + + ~scoped_instance() { deleter_(std::move(value_)); } + + ValueT& get() { return value_; } + + private: + ValueT value_; + DeleterT deleter_; +}; + +// Helper function to enable type deduction on scoped_instance construction. +template +auto make_scoped_instance(ValueT&& value, DeleterT&& deleter) { + return scoped_instance(std::forward(value), + std::forward(deleter)); +} + +} // namespace + +void isolate_rmw_by_path(const std::string& argv0, const std::string& path) { + if (rclcpp::ok()) { + throw std::runtime_error( + "middleware already initialized, too late for isolation"); + } + std::string error; + std::unique_ptr runfiles(Runfiles::Create(argv0, &error)); + if (!runfiles) { + throw std::runtime_error(error); + } + const std::string generate_isolated_env_rmw_location = runfiles->Rlocation( + STRINGIFY(RMW_ISOLATION_ROOTPATH) "/generate_isolated_rmw_env"); + const std::string scratch_directory_template = path + "/XXXXXX"; + auto mutable_scratch_directory_template = make_scoped_instance( + strdup(scratch_directory_template.c_str()), free); + if (mkdtemp(mutable_scratch_directory_template.get()) == nullptr) { + throw std::system_error(errno, std::system_category(), strerror(errno)); + } + std::ostringstream command_line; + command_line << generate_isolated_env_rmw_location << " --scratch-directory " + << mutable_scratch_directory_template.get() + << " --rmw-implementation " + << rmw_get_implementation_identifier() << " " << path; + std::string command = command_line.str(); + fflush(stdout); // Flush stdout to avoid interactions with forked process + auto command_stream = make_scoped_instance( + popen(command.c_str(), "r"), [&](FILE* command_stream) { + if (command_stream != nullptr) { + int return_code = pclose(command_stream); + if (return_code == -1 && errno == ECHILD) { + throw std::system_error(errno, std::system_category(), + strerror(errno)); + } + if (return_code != 0) { + std::ostringstream error_message; + error_message << generate_isolated_env_rmw_location + << " exited with " + return_code; + throw std::runtime_error(error_message.str()); + } + } + }); + if (command_stream.get() == nullptr) { + throw std::system_error(errno, std::system_category(), strerror(errno)); + } + size_t length = 0; + auto buffer = make_scoped_instance(nullptr, free); + while (getline(&buffer.get(), &length, command_stream.get()) != -1) { + char* bufferp = buffer.get(); // let strsep() mutate bufferp but not buffer + const std::string line{strsep(&bufferp, "\r\n")}; // drop trailing newline + const size_t separator_index = line.find("="); + const std::string name = line.substr(0, separator_index); + if (name.empty()) { + throw std::runtime_error("internal error: invalid environment variable"); + } + const std::string value = separator_index != std::string::npos + ? line.substr(separator_index + 1) + : ""; + if (setenv(name.c_str(), value.c_str(), 1) != 0) { + throw std::system_error(errno, std::system_category(), strerror(errno)); + } + } + if (!feof(command_stream.get())) { + throw std::system_error(errno, std::system_category(), strerror(errno)); + } +} + +} // namespace ros2 diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.cc.in b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.cc.in deleted file mode 100644 index a3c49ac36..000000000 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.cc.in +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "tools/cpp/runfiles/runfiles.h" -#include "rmw_isolation/rmw_isolation.h" - -using bazel::tools::cpp::runfiles::Runfiles; - -namespace ros2 { - -void isolate_rmw_by_path(const std::string& argv0, const std::string& path) -{ - if (rclcpp::ok()) - { - throw std::runtime_error( - "middleware already initialized, too late for isolation"); - } - std::string error; - std::unique_ptr runfiles(Runfiles::Create(argv0, &error)); - if (!runfiles) { - throw std::runtime_error(error); - } - std::string isolated_env_rmw_location = - runfiles->Rlocation("$ISOLATED_RMW_ENV_PATH"); - std::string env_file_path{"isolated_rmw.env"}; - std::stringstream sstr; - sstr << isolated_env_rmw_location << " --scratch-directory " << path - << " --rmw-implementation " << rmw_get_implementation_identifier() - << " --output " << env_file_path << " " << path; - std::string command = sstr.str(); - // NOTE(hidmic): no way to redirect stderr here. No feedback on error. - // Use POSIX popen? - int status = std::system(command.c_str()); - if (status < 0) { - throw std::system_error(errno, std::system_category(), strerror(errno)); - } - if (status != 0) { - throw std::runtime_error(isolated_env_rmw_location + " exited abnormally"); - } - std::ifstream env_file{env_file_path}; - for(std::string line; std::getline(env_file, line); ) { - const size_t separator_index = line.find("="); - const std::string name = line.substr(0, separator_index); - const std::string value = - separator_index != std::string::npos ? - line.substr(separator_index + 1) : ""; - if (setenv(name.c_str(), value.c_str(), 1) != 0) { - throw std::system_error(errno, std::system_category(), strerror(errno)); - } - } -} - -} // namespace ros2 diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.h b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.h index 259f465c1..0915d3302 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.h +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.h @@ -6,10 +6,11 @@ namespace ros2 { /// Isolates rmw implementation network traffic. /** - * This function relies on the `isolated_rmw_env` CLI to populate - * the calling process environment and achieve network isolation. + * This function relies on the `generate_isolated_rmw_env` CLI to + * populate the calling process environment and achieve network + * isolation. * - * \param argv0 program name to help locate `isolated_rmw_env` + * \param argv0 program name to help locate `generate_isolated_rmw_env` * \param path unique path to use as a basis for isolation. * \throws std::runtime_error if called after rmw initialization. * \throws std::runtime_error if `isolated_rmw_env` exits abnormally. diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.py index f31e5baec..a654b3958 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.py @@ -14,7 +14,7 @@ # 2 multicast ports + 2 unicast ports per participant DISCOVERY_PORTS_INTERVAL = MAX_PARTICIPANTS_PER_PROCESS * PARTICIPANT_ID_GAIN + 2 -def isolated_rmw_fastrtps_cpp_env(unique_identifier, scratch_directory=None): +def generate_isolated_rmw_fastrtps_cpp_env(unique_identifier, scratch_directory): """ Generates an environment that forces rmw_fastrtps_cpp network traffic isolation. @@ -24,8 +24,7 @@ def isolated_rmw_fastrtps_cpp_env(unique_identifier, scratch_directory=None): see https://fast-dds.docs.eprosima.com/en/latest/fastdds/transport/listening_locators.html. :param unique_identifier: unique arbitrary string to be used as a basis for isolation. - :param scratch_directory: optional directory for generated files. - If not provided, a temporary directory will be created. + :param scratch_directory: output directory for generated files. :returns: a dictionary of environment variables. """ profile_name = unique_identifier @@ -101,14 +100,12 @@ def isolated_rmw_fastrtps_cpp_env(unique_identifier, scratch_directory=None): inline_xml = ET.tostring(tree, encoding='utf-8') dom = minidom.parseString(inline_xml) pretty_xml = dom.toprettyxml(indent=' ' * 4, encoding='utf-8') - if scratch_directory is None: - scratch_directory = tempfile.mkdtemp() profiles_path = pathlib.Path(scratch_directory) / 'fastrtps_profiles.xml' with profiles_path.open('wb') as f: f.write(pretty_xml) - return {'FASTRTPS_DEFAULT_PROFILES_FILE': str(profiles_path), 'ROS_DOMAIN_ID': '0'} + return {'FASTRTPS_DEFAULT_PROFILES_FILE': str(profiles_path.resolve()), 'ROS_DOMAIN_ID': '0'} -def isolated_rmw_cyclonedds_cpp_env(unique_identifier, scratch_directory=None): +def generate_isolated_rmw_cyclonedds_cpp_env(unique_identifier, scratch_directory): """ Generates an environment that forces rmw_cyclonedds_cpp network traffic isolation. @@ -118,8 +115,7 @@ def isolated_rmw_cyclonedds_cpp_env(unique_identifier, scratch_directory=None): https://github.com/eclipse-cyclonedds/cyclonedds/blob/master/docs/manual/config.rst. :param unique_identifier: unique arbitrary string to be used as a basis for isolation. - :param scratch_directory: optional directory for generated files. - If not provided, a temporary directory will be created. + :param scratch_directory: output directory for generated files. :returns: a dictionary of environment variables. """ digest = hashlib.sha256(unique_identifier.encode('utf8')).digest() @@ -153,8 +149,6 @@ def isolated_rmw_cyclonedds_cpp_env(unique_identifier, scratch_directory=None): inline_xml = ET.tostring(tree, encoding='utf-8') dom = minidom.parseString(inline_xml) pretty_xml = dom.toprettyxml(indent=' ' * 4, encoding='utf-8') - if scratch_directory is None: - scratch_directory = tempfile.mkdtemp() configuration_path = pathlib.Path(scratch_directory) / 'cyclonedds_configuration.xml' with configuration_path.open('wb') as f: f.write(pretty_xml) @@ -162,7 +156,12 @@ def isolated_rmw_cyclonedds_cpp_env(unique_identifier, scratch_directory=None): 'CYCLONEDDS_URI': f'file://{configuration_path.resolve()}', 'ROS_DOMAIN_ID': str(int(digest[3]))} -def isolated_rmw_env(unique_identifier, rmw_implementation=None, scratch_directory=None): +_RMW_ISOLATION_FUNCTIONS = { + 'rmw_fastrtps_cpp': generate_isolated_rmw_fastrtps_cpp_env, + 'rmw_cyclonedds_cpp': generate_isolated_rmw_cyclonedds_cpp_env +} + +def generate_isolated_rmw_env(unique_identifier, rmw_implementation=None, scratch_directory=None): """ Generates an environment that forces rmw implementation network traffic isolation. @@ -178,10 +177,11 @@ def isolated_rmw_env(unique_identifier, rmw_implementation=None, scratch_directo """ if rmw_implementation is None: rmw_implementation = rclpy.get_rmw_implementation_identifier() - target_function_name = f'isolated_{rmw_implementation}_env' - if target_function_name not in globals(): - raise ValueError(f'cannot isolate unknown {rmw_implementation} implementation') - return globals()[target_function_name]( + if scratch_directory is None: + scratch_directory = tempfile.mkdtemp() + if rmw_implementation not in _RMW_ISOLATION_FUNCTIONS: + raise ValueError(f"cannot isolate unknown '{rmw_implementation}' implementation") + return _RMW_ISOLATION_FUNCTIONS[rmw_implementation]( unique_identifier, scratch_directory=scratch_directory) def isolate_rmw_by_path(path): @@ -196,4 +196,5 @@ def isolate_rmw_by_path(path): """ if rclpy.ok(): raise RuntimeError('middleware already initialized, too late for isolation') - os.environ.update(isolated_rmw_env(str(path), scratch_directory=path)) + os.environ.update(generate_isolated_rmw_env( + str(path), scratch_directory=tempfile.mkdtemp(dir=str(path)))) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py index e3e9e280c..1d1f9731c 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py @@ -31,7 +31,8 @@ def _timer_callback(self): f"I did not hear '{elf._uuid}' even once!" rclpy.shutdown() -if __name__ == '__main__': + +def main(): if 'TEST_TMPDIR' in os.environ: from rmw_isolation import isolate_rmw_by_path isolate_rmw_by_path(os.environ['TEST_TMPDIR']) @@ -45,3 +46,7 @@ def _timer_callback(self): # NOTE(hidmic): try_shutdown raises AttributeError # Need https://github.com/ros2/rclpy/pull/812 pass # rclpy.try_shutdown() + + +if __name__ == '__main__': + main() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py index 1a4e4a7e7..a91e18e0d 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py @@ -19,7 +19,8 @@ def _timer_callback(self): msg.data = self._uuid self._publisher.publish(msg) -if __name__ == '__main__': + +def main(): if 'TEST_TMPDIR' in os.environ: from rmw_isolation import isolate_rmw_by_path isolate_rmw_by_path(os.environ['TEST_TMPDIR']) @@ -33,3 +34,7 @@ def _timer_callback(self): pass finally: rclpy.shutdown() + + +if __name__ == '__main__': + main() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl index 03bdeae26..c97dac05c 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2.bzl @@ -9,8 +9,8 @@ PACKAGE_MANIFEST = [ "rosidl.bzl", "rmw_isolation/__init__.py", - "rmw_isolation/isolated_rmw_env.py", - "rmw_isolation/rmw_isolation.cc.in", + "rmw_isolation/generate_isolated_rmw_env.py", + "rmw_isolation/rmw_isolation.cc", "rmw_isolation/rmw_isolation.h", "rmw_isolation/rmw_isolation.py", "rmw_isolation/test/isolated_listener.cc", From 20fae246a32db035c25b7546d298324db10adaf0 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 21 Dec 2021 13:16:13 -0300 Subject: [PATCH 095/107] Clarify ament_cmake_CMakeLists.txt.in purpose Signed-off-by: Michel Hidalgo --- .../ros2/resources/templates/ament_cmake_CMakeLists.txt.in | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/ament_cmake_CMakeLists.txt.in b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/ament_cmake_CMakeLists.txt.in index 6b1394ae8..02d583442 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/ament_cmake_CMakeLists.txt.in +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/templates/ament_cmake_CMakeLists.txt.in @@ -1,4 +1,8 @@ -# Generated by Bazel. +# @NAME@ is an ament CMake project to help collect @PACKAGE@'s exported +# configuration. It does so by means of an empty binary library that +# declares a direct dependency on @PACKAGE@. All relevant paths and +# flags can then be retrived from CMake's code model (e.g. using +# CMake's server mode on this project). cmake_minimum_required(VERSION 3.5) project(@NAME@ C CXX) From 21718a9a07767d3fcba47a31e7c18db6288f455b Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 21 Dec 2021 16:58:45 -0300 Subject: [PATCH 096/107] Improve rmw_isolation collision rates - Extend effective UUID size to 48 bits - Parallelize in rmw_isolation_test.sh to go above Bazel limits - Drop shard_count from tests in lieu of --runs_per_test - Mark rmw_isolation tests as flaky - Leave a note about collision rates Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/README.md | 5 ++ .../rmw_isolation/package.BUILD.bazel | 4 +- .../resources/rmw_isolation/rmw_isolation.py | 47 ++++++++++++++----- .../rmw_isolation/test/isolated_listener.cc | 2 +- .../rmw_isolation/test/isolated_listener.py | 11 +++-- .../rmw_isolation/test/isolated_talker.py | 8 +++- .../rmw_isolation/test/rmw_isolation_test.sh | 35 +++++++++----- 7 files changed, 79 insertions(+), 33 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/README.md b/drake_ros_bazel_installed/tools/skylark/ros2/README.md index 24416af28..cfb03c91b 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/README.md +++ b/drake_ros_bazel_installed/tools/skylark/ros2/README.md @@ -107,6 +107,11 @@ The `rmw_isolation` subpackage provides C++ and Python `isolate_rmw_by_path` APIs to enforce RMW network isolation. To that end, a unique path must be provided (such as Bazel's `$TEST_TMPDIR`). +**DISCLAIMER** +: Isolation relies on `rmw`-specific configuration. Support is available for + Tier 1 `rmw` implementations only. Collision rates are below 1% but not null. + Use with care. + #### Metadata The `distro.bzl` file bears relevant ROS 2 workspace metadata for rules, tools, diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel index 56df67b0f..00008284b 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel @@ -72,8 +72,8 @@ sh_test( "$(location :isolated_listener_py)", ], data = [":isolated_talker_py", ":isolated_listener_py"], - shard_count = 10, size = "small", + flaky = True, ) ros_cc_binary( @@ -106,6 +106,6 @@ sh_test( "$(location :isolated_listener_cc)", ], data = [":isolated_talker_cc", ":isolated_listener_cc"], - shard_count = 10, size = "small", + flaky = True, ) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.py index a654b3958..b8365a2f9 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.py @@ -11,8 +11,8 @@ PARTICIPANT_ID_GAIN = 2 MAX_PARTICIPANTS_PER_PROCESS = 10 -# 2 multicast ports + 2 unicast ports per participant -DISCOVERY_PORTS_INTERVAL = MAX_PARTICIPANTS_PER_PROCESS * PARTICIPANT_ID_GAIN + 2 +MULTICAST_PORTS_INTERVAL = 2 +UNICAST_PORTS_INTERVAL = MAX_PARTICIPANTS_PER_PROCESS * PARTICIPANT_ID_GAIN def generate_isolated_rmw_fastrtps_cpp_env(unique_identifier, scratch_directory): """ @@ -70,19 +70,23 @@ def generate_isolated_rmw_fastrtps_cpp_env(unique_identifier, scratch_directory) builder.start('participantIDGain') builder.data(str(PARTICIPANT_ID_GAIN)) builder.end('participantIDGain') - discovery_ports_offset = ( - int(digest[3]) * DISCOVERY_PORTS_INTERVAL) + unicast_ports_offset = ( + ((digest[3] << 8) + digest[4]) % 2**10 # Use 10 bits + ) * UNICAST_PORTS_INTERVAL + multicast_ports_offset = ( + ((digest[5] << 8) + digest[6]) % 2**14 # Use 14 bits + ) * MULTICAST_PORTS_INTERVAL builder.start('offsetd0') - builder.data(str(discovery_ports_offset)) + builder.data(str(multicast_ports_offset)) builder.end('offsetd0') builder.start('offsetd1') - builder.data(str(discovery_ports_offset + 2)) + builder.data(str(unicast_ports_offset)) builder.end('offsetd1') builder.start('offsetd2') - builder.data(str(discovery_ports_offset + 1)) + builder.data(str(multicast_ports_offset + 1)) builder.end('offsetd2') builder.start('offsetd3') - builder.data(str(discovery_ports_offset + 3)) + builder.data(str(unicast_ports_offset + 1)) builder.end('offsetd3') builder.end('port') builder.start('userTransports') @@ -139,8 +143,29 @@ def generate_isolated_rmw_cyclonedds_cpp_env(unique_identifier, scratch_director builder.end('ParticipantIndex') builder.start('Ports') builder.start('DomainGain') - builder.data('225') # ensure all 256 domain IDs are valid + builder.data('0') # ignore domain IDs builder.end('DomainGain') + builder.start('ParticipantGain') + builder.data(str(PARTICIPANT_ID_GAIN)) + builder.end('ParticipantGain') + unicast_ports_offset = ( + ((digest[3] << 8) + digest[4]) % 2**10 # Use 10 bits + ) * UNICAST_PORTS_INTERVAL + multicast_ports_offset = ( + ((digest[5] << 8) + digest[6]) % 2**14 # Use 14 bits + ) * MULTICAST_PORTS_INTERVAL + builder.start('MulticastMetaOffset') + builder.data(str(multicast_ports_offset)) + builder.end('MulticastMetaOffset') + builder.start('UnicastMetaOffset') + builder.data(str(unicast_ports_offset)) + builder.end('UnicastMetaOffset') + builder.start('MulticastDataOffset') + builder.data(str(multicast_ports_offset + 1)) + builder.end('MulticastDataOffset') + builder.start('UnicastDataOffset') + builder.data(str(unicast_ports_offset + 1)) + builder.end('UnicastDataOffset') builder.end('Ports') builder.end('Discovery') builder.end('Domain') @@ -152,9 +177,7 @@ def generate_isolated_rmw_cyclonedds_cpp_env(unique_identifier, scratch_director configuration_path = pathlib.Path(scratch_directory) / 'cyclonedds_configuration.xml' with configuration_path.open('wb') as f: f.write(pretty_xml) - return { - 'CYCLONEDDS_URI': f'file://{configuration_path.resolve()}', - 'ROS_DOMAIN_ID': str(int(digest[3]))} + return {'CYCLONEDDS_URI': f'file://{configuration_path.resolve()}', 'ROS_DOMAIN_ID': '0'} _RMW_ISOLATION_FUNCTIONS = { 'rmw_fastrtps_cpp': generate_isolated_rmw_fastrtps_cpp_env, diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.cc b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.cc index 16ff76b44..c51cfe651 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.cc +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.cc @@ -27,7 +27,7 @@ class IsolatedListener : public rclcpp::Node { if (msg->data != uuid_) { throw std::runtime_error( - "I heard '" + msg->data + "' yet" + + "I heard '" + msg->data + "' yet " + "I was expecting '" + uuid_ + "'!"); } ++expected_messages_count_; diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py index 1d1f9731c..9f3220328 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py @@ -42,10 +42,13 @@ def main(): try: executor = rclpy.executors.SingleThreadedExecutor() rclpy.spin(IsolatedListener(uuid), executor) - finally: - # NOTE(hidmic): try_shutdown raises AttributeError - # Need https://github.com/ros2/rclpy/pull/812 - pass # rclpy.try_shutdown() + except rclpy.executors.ExternalShutdownException: + pass + except KeyboardInterrupt: + rclpy.shutdown() + except Exception: + rclpy.shutdown() + raise if __name__ == '__main__': diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py index a91e18e0d..04beddbdc 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py @@ -30,10 +30,14 @@ def main(): uuid = os.environ.get('TEST_TMPDIR', 'none') try: rclpy.spin(IsolatedTalker(uuid)) - except KeyboardInterrupt: + except rclpy.executors.ExternalShutdownException: pass - finally: + except KeyboardInterrupt: rclpy.shutdown() + except Exception: + rclpy.shutdown() + raise + if __name__ == '__main__': diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/rmw_isolation_test.sh b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/rmw_isolation_test.sh index c639e891d..a587aae06 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/rmw_isolation_test.sh +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/rmw_isolation_test.sh @@ -1,39 +1,50 @@ #!/bin/bash -TALKER_EXECUTABLE=$1 -LISTENER_EXECUTABLE=$2 -TIMEOUT=2.0 # in seconds +export TALKER_EXECUTABLE=$1 +export LISTENER_EXECUTABLE=$2 +export TIMEOUT=2.0 # in seconds # Avoid issues when trying to expand '~' -DEFAULT_ROS_ARGS="--disable-external-lib-logs" +export DEFAULT_ROS_ARGS="--disable-external-lib-logs" function test_isolation { - rc=0 + if [ ! -z "${TEST_TMPDIR}" ]; then + if [ ! -z "$1" ]; then + # Handle concurrent subtests in TEST_TMPDIR + export TEST_TMPDIR="${TEST_TMPDIR}/$1" + mkdir -p "${TEST_TMPDIR}" + fi + fi # Start talker in the background TALKER_ROS_ARGS=$DEFAULT_ROS_ARGS $TALKER_EXECUTABLE --ros-args $TALKER_ROS_ARGS -- & TALKER_PID=$! + sleep 1.0 # Let the talker start up + # Run listener in the foreground LISTENER_ROS_ARGS="$DEFAULT_ROS_ARGS --param timeout:=$TIMEOUT" - if ! $LISTENER_EXECUTABLE --ros-args $LISTENER_ROS_ARGS; then - # Listener exited with an error - rc=$? - fi + $LISTENER_EXECUTABLE --ros-args $LISTENER_ROS_ARGS + return_code=$? # Keep listener return code # Kill talker in the background if ! kill $TALKER_PID; then # Talker exited too early, likely with an error wait $TALKER_PID - rc=$? + return_code=$? # Keep talker return code fi - return $rc + return $return_code } +export -f test_isolation # Export to subshells # Run as many concurrent tests as shards are available [ -z "${TEST_SHARD_STATUS_FILE-}" ] || touch "$TEST_SHARD_STATUS_FILE" -test_isolation +# Run as many SUBTESTS_PER_TEST as requested, across WORKER_COUNT processes +SUBTESTS_PER_TEST=${SUBTESTS_PER_TEST:-10} +WORKER_COUNT=${WORKER_COUNT:-$(nproc --all)} + +seq 0 ${SUBTESTS_PER_TEST} | xargs -n 1 -P ${WORKER_COUNT} -I{} bash -c "test_isolation subtest_{}_of_${SUBTESTS_PER_TEST}" From b159cbe211361d31afe47e3674b4b32886cdca49 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 23 Dec 2021 15:02:55 -0300 Subject: [PATCH 097/107] Comment on rmw_isolation tests flakiness Signed-off-by: Michel Hidalgo --- .../ros2/resources/rmw_isolation/package.BUILD.bazel | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel index 00008284b..b7e6d5dd0 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel @@ -73,7 +73,8 @@ sh_test( ], data = [":isolated_talker_py", ":isolated_listener_py"], size = "small", - flaky = True, + flaky = True, # because collision rates are non-zero + # See ROS2 Skylark tooling README.md for further reference. ) ros_cc_binary( @@ -107,5 +108,6 @@ sh_test( ], data = [":isolated_talker_cc", ":isolated_listener_cc"], size = "small", - flaky = True, + flaky = True, # because collision rates are non-zero, + # See ROS2 Skylark tooling README.md for further reference. ) From efc8a6cc4a6e124e3cca41d4970bd8c26803a78b Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 23 Dec 2021 15:43:01 -0300 Subject: [PATCH 098/107] Comment on ros2bzl.scraping.system.DEFAULT_* dirs Signed-off-by: Michel Hidalgo --- .../tools/skylark/ros2/ros2bzl/scraping/system.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py index 5d07f136e..938568e72 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py @@ -8,6 +8,9 @@ import subprocess import sys + +# Standard include files' search paths for compilers in Linux systems. +# Useful to detect system includes in package exported configuration. DEFAULT_INCLUDE_DIRECTORIES = ['/usr/include', '/usr/local/include'] @@ -20,6 +23,8 @@ def is_system_include(include_path): return any(include_path.startswith(path) for path in DEFAULT_INCLUDE_DIRECTORIES) +# Standard library files' search paths for linkers in Linux systems. +# Useful to detect system libraries in package exported configuration. DEFAULT_LINK_DIRECTORIES = ['/lib', '/usr/lib', '/usr/local/lib'] From 700752a98653680b9402ebf6e47c9bc941e5c283 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 23 Dec 2021 15:43:50 -0300 Subject: [PATCH 099/107] Tweak scraping of ROS package dependencies - Bring in rmw_implementation_packages as group dependencies - Only link direct dependencies in the resulting build graph - Rename build_dependencies as build_export_dependendencies - Ignore buildtool_depends tags Signed-off-by: Michel Hidalgo --- .../skylark/ros2/generate_repository_files.py | 27 ++++++++++++------- .../skylark/ros2/ros2bzl/scraping/__init__.py | 10 ++----- .../skylark/ros2/ros2bzl/scraping/metadata.py | 5 +--- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py index 8a4c82ccd..7065638d5 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -126,9 +126,16 @@ def generate_build_file(repo_name, distro, cache, extras, sandbox): for name in toposort.toposort_flatten(distro['dependency_graph']): metadata = distro['packages'][name] - dependencies = { + # Avoid linking non-direct dependencies (e.g. group dependencies) + direct_dependency_names = \ + distro['dependency_graph'][name].intersection( + metadata.get('build_export_dependencies', set()).union( + metadata.get('run_dependencies', set()) + ) + ) + direct_dependencies = { dependency_name: distro['packages'][dependency_name] - for dependency_name in distro['dependency_graph'][name] + for dependency_name in direct_dependency_names } if 'share_directory' in metadata: @@ -144,11 +151,11 @@ def generate_build_file(repo_name, distro, cache, extras, sandbox): if 'cmake' in metadata.get('build_type'): properties = collect_ament_cmake_package_direct_properties( - name, metadata, dependencies, cache + name, metadata, direct_dependencies, cache ) _, template, config = configure_package_cc_library( - name, metadata, properties, dependencies, extras, sandbox + name, metadata, properties, direct_dependencies, extras, sandbox ) fd.write(interpolate(template, config) + '\n') @@ -164,7 +171,7 @@ def generate_build_file(repo_name, distro, cache, extras, sandbox): # but to look for it. try: properties = collect_ament_python_package_direct_properties( - name, metadata, dependencies, cache + name, metadata, direct_dependencies, cache ) # Add 'py' as language if not there. if 'langs' not in metadata: @@ -172,26 +179,26 @@ def generate_build_file(repo_name, distro, cache, extras, sandbox): metadata['langs'].add('py') except PackageNotFoundError: if any('py' in metadata.get('langs', []) - for metadata in dependencies.values() + for metadata in direct_dependencies.values() ): metadata['langs'].add('py (transitive)') # Dependencies still need to be propagated. _, template, config = configure_package_meta_py_library( - name, metadata, dependencies) + name, metadata, direct_dependencies) fd.write(interpolate(template, config) + '\n') properties = {} if properties: _, template, config = configure_package_py_library( - name, metadata, properties, dependencies, extras, sandbox + name, metadata, properties, direct_dependencies, extras, sandbox ) fd.write(interpolate(template, config) + '\n') if metadata.get('executables'): - dependencies.update(rmw_implementation_packages) + direct_dependencies.update(rmw_implementation_packages) for _, template, config in configure_package_executable_imports( - name, metadata, dependencies, sandbox, extras=extras + name, metadata, direct_dependencies, sandbox, extras=extras ): fd.write(interpolate(template, config) + '\n') diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/__init__.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/__init__.py index 018fe036f..9132572e4 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/__init__.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/__init__.py @@ -36,9 +36,6 @@ def index_all_packages(): return packages -SKIPPED_GROUP_DEPENDENCIES = ('rmw_implementation_packages',) - - def build_dependency_graph(packages, include=None, exclude=None): package_set = set(packages) if include: @@ -51,20 +48,17 @@ def build_dependency_graph(packages, include=None, exclude=None): if 'groups' not in metadata: continue for group_name in metadata['groups']: - if group_name not in groups: - groups[group_name] = [] + groups.setdefault(group_name, []) groups[group_name].append(name) dependency_graph = {} while package_set: name = package_set.pop() metadata = packages[name] - dependencies = set(metadata.get('build_dependencies', [])) + dependencies = set(metadata.get('build_export_dependencies', [])) dependencies.update(metadata.get('run_dependencies', [])) if 'group_dependencies' in metadata: for group_name in metadata['group_dependencies']: - if group_name in SKIPPED_GROUP_DEPENDENCIES: - continue dependencies.update(groups[group_name]) if exclude: dependencies -= exclude diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/metadata.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/metadata.py index 4d97bcf6e..ff1dcfdb3 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/metadata.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/metadata.py @@ -11,9 +11,6 @@ def parse_package_xml(path_to_package_xml): exec_depends = set([ tag.text for tag in tree.findall('./exec_depend') ]) - buildtool_depends = set([ - tag.text for tag in tree.findall('./buildtool_depend') - ]) build_export_depends = set([ tag.text for tag in tree.findall('./build_export_depend') ]) @@ -26,7 +23,7 @@ def parse_package_xml(path_to_package_xml): build_type = tree.find('./export/build_type').text return dict( - build_dependencies=build_export_depends | depends | buildtool_depends, + build_export_dependencies=build_export_depends | depends, run_dependencies=exec_depends | depends, group_dependencies=group_depends, groups=member_of_groups, From 674f8795e64ad56b014e2dd99488ac64e94a02c5 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 23 Dec 2021 15:51:33 -0300 Subject: [PATCH 100/107] Correct writing in README.md files Signed-off-by: Michel Hidalgo --- .../drake_ros_apps/README.md | 2 +- .../tools/skylark/ros2/README.md | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drake_ros_bazel_installed/drake_ros_apps/README.md b/drake_ros_bazel_installed/drake_ros_apps/README.md index b1efe8a5d..bfd33f3ba 100644 --- a/drake_ros_bazel_installed/drake_ros_apps/README.md +++ b/drake_ros_bazel_installed/drake_ros_apps/README.md @@ -7,7 +7,7 @@ To check that the resulting interfaces are functional, it includes a demo applic - Equivalent `oracle_(cc|py)` applications that publish `drake_ros_apps_msgs/msg/Status` messages, serve a `drake_ros_common_msgs/srv/Query`service, and provide a `drake_ros_common_msgs/action/Do` action. - Equivalent `inquirer_(cc|py)` applications that subscribe to `drake_ros_apps_msgs/msg/Status` messages, make `drake_ros_common_msgs/srv/Query` service requests, and invoke the `drake_ros_common_msgs/action/Do` action. -You may run a C++ oracle against a Python inquirer, and viceversa: +You may run a C++ oracle against a Python inquirer, and vice versa: ```sh bazel run //drake_ros_apps:oracle_py diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/README.md b/drake_ros_bazel_installed/tools/skylark/ros2/README.md index cfb03c91b..0cab4f8b1 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/README.md +++ b/drake_ros_bazel_installed/tools/skylark/ros2/README.md @@ -64,30 +64,30 @@ the artifacts it generates, the following targets may be found at the root .action, and .idl files) in the `/share` directory. - A `_c` C/C++ library for C libraries. Typically an alias of the `_cc` target if C and C++ libraries cannot be told apart. -- A `_transitive_py` Python library if the package has does not +- A `_transitive_py` Python library if the package does not install any Python libraries but it depends on (and it is a dependency of) packages that do. This helps maintain the dependency graph (as Python library targets can only depend on other Python library targets). - A `_` Python binary for each executable installed at the package-level (i.e. under `lib/`, where `ros2 run` can find them). -- A `` Python binary for each executable installed under the - `/bin` directory (and thus accessible via `$PATH` when - sourcing the workspace install space). +- A `` Python binary for each executable (binary or not) + installed under the `/bin` directory (and thus accessible + via `$PATH` when sourcing the workspace install space). #### Rules To build C++ binaries and tests that depend on ROS 2, `ros_cc_binary` and `ros_cc_test` rules are available in the `ros_cc.bzl` file. These rules, equivalent to the native `cc_binary` and `cc_test` rules, ensure these binaries -run in a environment that is tightly coupled with the underlying ROS 2 workspace -install space. +run in an environment that is tightly coupled with the underlying ROS 2 +workspace install space. To build Python binaries and tests that depend on ROS 2, `ros_py_binary` and `ros_pytest` rules are available in the `ros_py.bzl` file. These rules, equivalent to the native `py_binary` and `py_test` rules, ensure these binaries -run in a environment that is tightly coupled with the underlying ROS 2 workspace -install space. +run in an environment that is tightly coupled with the underlying ROS 2 +workspace install space. To generate and build ROS 2 interfaces, a `rosidl_interfaces_group` rule is available in the `rosidl.bzl` file. This rule generates C++ and Python code @@ -96,7 +96,7 @@ available in the ROS 2 workspace install space: code generators, interface definition translators, runtime dependencies, etc. Several targets are created, following strict naming conventions (e.g. C++ and Python interface libraries carry `_cc` and `_py` suffixes, respectively), though finer-grained control over -what is generated and built can be achieved through via other rules available in +what is generated and built can be achieved through other rules available in the same file. By default, these naming conventions allow downstream `rosidl_interfaces_group` rules to depend on upstream `rosidl_interface_group` rules. @@ -108,7 +108,7 @@ APIs to enforce RMW network isolation. To that end, a unique path must be provided (such as Bazel's `$TEST_TMPDIR`). **DISCLAIMER** -: Isolation relies on `rmw`-specific configuration. Support is available for +: Isolation relies on `rmw`-specific configuration. Support is available for Tier 1 `rmw` implementations only. Collision rates are below 1% but not null. Use with care. From d6dceeeab0f62834ff7245de4c77104ddb66c03d Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Thu, 23 Dec 2021 16:01:16 -0300 Subject: [PATCH 101/107] Lint all C++ and Python code Signed-off-by: Michel Hidalgo --- .../drake_ros_apps/inquirer.cc | 69 ++++++------- .../drake_ros_apps/inquirer.py | 1 + .../drake_ros_apps/oracle.cc | 96 ++++++++++--------- .../skylark/ros2/cmake_tools/__init__.py | 2 +- .../skylark/ros2/cmake_tools/server_mode.py | 2 +- .../skylark/ros2/generate_repository_files.py | 17 ++-- .../generate_isolated_rmw_env.py | 5 +- .../resources/rmw_isolation/rmw_isolation.cc | 5 +- .../resources/rmw_isolation/rmw_isolation.py | 64 +++++++++---- .../rmw_isolation/test/isolated_listener.cc | 59 +++++------- .../rmw_isolation/test/isolated_listener.py | 2 +- .../rmw_isolation/test/isolated_talker.cc | 40 ++++---- .../rmw_isolation/test/isolated_talker.py | 1 - .../ros2/ros2bzl/scraping/ament_cmake.py | 45 +++++---- .../ros2/ros2bzl/scraping/ament_python.py | 7 +- .../skylark/ros2/ros2bzl/scraping/system.py | 8 +- .../tools/skylark/ros2/ros2bzl/templates.py | 18 +++- 17 files changed, 242 insertions(+), 199 deletions(-) diff --git a/drake_ros_bazel_installed/drake_ros_apps/inquirer.cc b/drake_ros_bazel_installed/drake_ros_apps/inquirer.cc index 5953b37ea..04e1255b0 100644 --- a/drake_ros_bazel_installed/drake_ros_apps/inquirer.cc +++ b/drake_ros_bazel_installed/drake_ros_apps/inquirer.cc @@ -6,38 +6,44 @@ #include #include "drake_ros_apps_msgs/msg/status.hpp" -#include "drake_ros_common_msgs/srv/query.hpp" #include "drake_ros_common_msgs/action/do.hpp" +#include "drake_ros_common_msgs/srv/query.hpp" using namespace std::chrono_literals; namespace drake_ros_apps { class Inquirer : public rclcpp::Node { -public: + public: Inquirer() : Node("inquirer") { using namespace std::placeholders; - status_sub_ = - this->create_subscription( + status_sub_ = this->create_subscription( "status", rclcpp::QoS(rclcpp::KeepLast(1)), std::bind(&Inquirer::on_status, this, _1)); - query_client_ = this->create_client("query"); + query_client_ = + this->create_client("query"); - action_client_ = rclcpp_action::create_client(this, "do"); + action_client_ = + rclcpp_action::create_client(this, + "do"); - inquire_timer_ = this->create_wall_timer(5s, std::bind(&Inquirer::inquire, this)); + inquire_timer_ = + this->create_wall_timer(5s, std::bind(&Inquirer::inquire, this)); } -private: + private: using QueryClient = rclcpp::Client; void handle_reply(QueryClient::SharedFuture future) const { - RCLCPP_INFO(this->get_logger(), "oracle said: %s", future.get()->reply.c_str()); + RCLCPP_INFO(this->get_logger(), "oracle said: %s", + future.get()->reply.c_str()); } - using ActionGoalHandle = rclcpp_action::ClientGoalHandle; - void handle_rite_request_response(const std::shared_ptr handle) const { + using ActionGoalHandle = + rclcpp_action::ClientGoalHandle; + void handle_rite_request_response( + const std::shared_ptr handle) const { if (!handle) { RCLCPP_ERROR(this->get_logger(), "oracle rejected rite request"); } else { @@ -46,21 +52,18 @@ class Inquirer : public rclcpp::Node { } void handle_rite_feedback( - std::shared_ptr handle, - const std::shared_ptr feedback) const - { + std::shared_ptr handle, + const std::shared_ptr feedback) const { RCLCPP_INFO(this->get_logger(), "oracle is %s", feedback->message.c_str()); } - void handle_rite_result(const ActionGoalHandle::WrappedResult& result) const - { + void handle_rite_result(const ActionGoalHandle::WrappedResult& result) const { switch (result.code) { case rclcpp_action::ResultCode::SUCCEEDED: RCLCPP_INFO(this->get_logger(), "oracle rite is complete"); break; case rclcpp_action::ResultCode::ABORTED: - RCLCPP_ERROR(this->get_logger(), - "oracle rite aborted due to %s", + RCLCPP_ERROR(this->get_logger(), "oracle rite aborted due to %s", result.result->reason.c_str()); break; case rclcpp_action::ResultCode::CANCELED: @@ -81,19 +84,20 @@ class Inquirer : public rclcpp::Node { request->query = "how's it going?"; RCLCPP_INFO(this->get_logger(), "oracle, %s", request->query.c_str()); query_client_->async_send_request( - request, std::bind(&Inquirer::handle_reply, this, _1)); + request, std::bind(&Inquirer::handle_reply, this, _1)); } else { RCLCPP_WARN(this->get_logger(), "oracle not available for queries"); } if (action_client_->action_server_is_ready()) { - rclcpp_action::Client::SendGoalOptions options; + rclcpp_action::Client::SendGoalOptions + options; options.goal_response_callback = - std::bind(&Inquirer::handle_rite_request_response, this, _1); + std::bind(&Inquirer::handle_rite_request_response, this, _1); options.feedback_callback = - std::bind(&Inquirer::handle_rite_feedback, this, _1, _2); + std::bind(&Inquirer::handle_rite_feedback, this, _1, _2); options.result_callback = - std::bind(&Inquirer::handle_rite_result, this, _1); + std::bind(&Inquirer::handle_rite_result, this, _1); drake_ros_common_msgs::action::Do::Goal goal; goal.action = "rite"; goal.period = rclcpp::Duration::from_seconds(0.1); @@ -104,22 +108,23 @@ class Inquirer : public rclcpp::Node { } } - void on_status(const drake_ros_apps_msgs::msg::Status & msg) const { - RCLCPP_INFO( - get_logger(), "%s status (%lu): %s", - msg.origin.c_str(), msg.status.sequence_id, - msg.status.message.c_str()); + void on_status(const drake_ros_apps_msgs::msg::Status& msg) const { + RCLCPP_INFO(get_logger(), "%s status (%lu): %s", msg.origin.c_str(), + msg.status.sequence_id, msg.status.message.c_str()); } - std::shared_ptr> status_sub_; - std::shared_ptr> query_client_; - std::shared_ptr> action_client_; + std::shared_ptr> + status_sub_; + std::shared_ptr> + query_client_; + std::shared_ptr> + action_client_; std::shared_ptr inquire_timer_; }; } // namespace drake_ros_apps -int main(int argc, char ** argv) { +int main(int argc, char** argv) { rclcpp::init(argc, argv); rclcpp::spin(std::make_shared()); diff --git a/drake_ros_bazel_installed/drake_ros_apps/inquirer.py b/drake_ros_bazel_installed/drake_ros_apps/inquirer.py index b13287ce4..6061fc559 100644 --- a/drake_ros_bazel_installed/drake_ros_apps/inquirer.py +++ b/drake_ros_bazel_installed/drake_ros_apps/inquirer.py @@ -78,6 +78,7 @@ def inquire(self): else: self.get_logger().warning('oracle not available for actions') + def main(): rclpy.init() diff --git a/drake_ros_bazel_installed/drake_ros_apps/oracle.cc b/drake_ros_bazel_installed/drake_ros_apps/oracle.cc index a61e4e84a..a87b17d01 100644 --- a/drake_ros_bazel_installed/drake_ros_apps/oracle.cc +++ b/drake_ros_bazel_installed/drake_ros_apps/oracle.cc @@ -17,93 +17,92 @@ using namespace std::chrono_literals; namespace drake_ros_apps { class Oracle : public rclcpp::Node { -public: - Oracle() : Node("oracle") - { + public: + Oracle() : Node("oracle") { using namespace std::placeholders; status_pub_ = this->create_publisher( - "status", rclcpp::QoS(rclcpp::KeepLast(1))); + "status", rclcpp::QoS(rclcpp::KeepLast(1))); query_server_ = this->create_service( - "query", std::bind(&Oracle::handle_query, this, _1, _2)); + "query", std::bind(&Oracle::handle_query, this, _1, _2)); - action_server_ = rclcpp_action::create_server( - this, "do", std::bind(&Oracle::handle_action_goal, this, _1, _2), - std::bind(&Oracle::handle_cancelled_action, this, _1), - std::bind(&Oracle::handle_accepted_action, this, _1)); + action_server_ = + rclcpp_action::create_server( + this, "do", std::bind(&Oracle::handle_action_goal, this, _1, _2), + std::bind(&Oracle::handle_cancelled_action, this, _1), + std::bind(&Oracle::handle_accepted_action, this, _1)); - status_timer_ = this->create_wall_timer( - 1s, std::bind(&Oracle::publish_status, this)); + status_timer_ = + this->create_wall_timer(1s, std::bind(&Oracle::publish_status, this)); } -private: - - rclcpp_action::GoalResponse - handle_action_goal( - const rclcpp_action::GoalUUID &, - const std::shared_ptr goal) - { - if (goal->action != "rite") - { - RCLCPP_WARN(this->get_logger(), "Don't know how to %s", goal->action.c_str()); + private: + rclcpp_action::GoalResponse handle_action_goal( + const rclcpp_action::GoalUUID&, + const std::shared_ptr + goal) { + if (goal->action != "rite") { + RCLCPP_WARN(this->get_logger(), "Don't know how to %s", + goal->action.c_str()); return rclcpp_action::GoalResponse::REJECT; } return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE; } - using GoalHandle = rclcpp_action::ServerGoalHandle; + using GoalHandle = + rclcpp_action::ServerGoalHandle; - rclcpp_action::CancelResponse handle_cancelled_action(const std::shared_ptr) - { + rclcpp_action::CancelResponse handle_cancelled_action( + const std::shared_ptr) { return rclcpp_action::CancelResponse::ACCEPT; } - void handle_accepted_action(const std::shared_ptr handle) - { + void handle_accepted_action(const std::shared_ptr handle) { action_start_time_ = this->get_clock()->now(); action_loop_ = this->create_wall_timer( - rclcpp::Duration{handle->get_goal()->period}.to_chrono(), - [this, handle]() { this->handle_rite_action(handle); }); + rclcpp::Duration{handle->get_goal()->period} + .to_chrono(), + [this, handle]() { this->handle_rite_action(handle); }); } - void handle_rite_action(const std::shared_ptr handle) - { - if (handle->is_canceling()) - { - auto result = std::make_shared(); + void handle_rite_action(const std::shared_ptr handle) { + if (handle->is_canceling()) { + auto result = + std::make_shared(); handle->canceled(result); action_loop_->cancel(); return; } auto current_time = this->get_clock()->now(); - if (current_time - action_start_time_ > handle->get_goal()->timeout) - { - auto result = std::make_shared(); + if (current_time - action_start_time_ > handle->get_goal()->timeout) { + auto result = + std::make_shared(); result->reason = "timeout"; handle->abort(result); action_loop_->cancel(); return; } - if (is_rite_complete_(gen_)) - { - auto result = std::make_shared(); + if (is_rite_complete_(gen_)) { + auto result = + std::make_shared(); handle->succeed(result); action_loop_->cancel(); return; } - auto feedback = std::make_shared(); + auto feedback = + std::make_shared(); feedback->message = "chanting"; handle->publish_feedback(feedback); } void handle_query( - const std::shared_ptr request, - std::shared_ptr response) const - { + const std::shared_ptr request, + std::shared_ptr response) + const { if (request->query == "how's it going?") { response->reply = "all good!"; } else { @@ -128,14 +127,17 @@ class Oracle : public rclcpp::Node { rclcpp::Time action_start_time_; std::shared_ptr action_loop_; std::shared_ptr status_timer_; - std::shared_ptr> status_pub_; - std::shared_ptr> query_server_; - std::shared_ptr> action_server_; + std::shared_ptr> + status_pub_; + std::shared_ptr> + query_server_; + std::shared_ptr> + action_server_; }; } // namespace drake_ros_apps -int main(int argc, char ** argv) { +int main(int argc, char** argv) { rclcpp::init(argc, argv); rclcpp::spin(std::make_shared()); diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/__init__.py b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/__init__.py index f8d6559c0..d67e488cd 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/__init__.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/__init__.py @@ -2,8 +2,8 @@ import shutil import subprocess -from .server_mode import server_mode from .packages import get_packages_with_prefixes +from .server_mode import server_mode def configure_file(src, dest, subs): diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/server_mode.py b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/server_mode.py index 3639c0e28..8ec1a834c 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/server_mode.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/server_mode.py @@ -55,7 +55,7 @@ def __init__(self, address): assert message['type'] == 'hello' self.__supported_protocol_versions = \ message['supportedProtocolVersions'] - except: + except Exception: self.__socket.close() raise diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py index 7065638d5..c6a702224 100755 --- a/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/generate_repository_files.py @@ -14,7 +14,6 @@ import collections import os import sys - import xml.etree.ElementTree as ET import toposort @@ -25,11 +24,15 @@ # `cmake_tools` reachable through PYTHONPATH. Thus, we force it here. sys.path.insert(0, os.path.dirname(__file__)) # noqa +from ros2bzl.resources import load_resource + +import ros2bzl.sandboxing as sandboxing + from ros2bzl.scraping import load_distribution -from ros2bzl.scraping.ament_cmake \ - import collect_ament_cmake_package_properties from ros2bzl.scraping.ament_cmake \ import collect_ament_cmake_package_direct_properties +from ros2bzl.scraping.ament_cmake \ + import collect_ament_cmake_package_properties from ros2bzl.scraping.ament_cmake import precache_ament_cmake_properties from ros2bzl.scraping.ament_python \ import collect_ament_python_package_direct_properties @@ -37,19 +40,15 @@ from ros2bzl.templates import configure_distro from ros2bzl.templates import configure_executable_imports -from ros2bzl.templates import configure_package_meta_py_library from ros2bzl.templates import configure_package_c_library_alias from ros2bzl.templates import configure_package_cc_library from ros2bzl.templates import configure_package_executable_imports +from ros2bzl.templates import configure_package_interfaces_filegroup +from ros2bzl.templates import configure_package_meta_py_library from ros2bzl.templates import configure_package_py_library from ros2bzl.templates import configure_package_share_filegroup -from ros2bzl.templates import configure_package_interfaces_filegroup from ros2bzl.templates import configure_prologue -from ros2bzl.resources import load_resource - -import ros2bzl.sandboxing as sandboxing - from ros2bzl.utilities import interpolate diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/generate_isolated_rmw_env.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/generate_isolated_rmw_env.py index 0b3d38822..163dd6c39 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/generate_isolated_rmw_env.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/generate_isolated_rmw_env.py @@ -18,6 +18,7 @@ from rmw_isolation import generate_isolated_rmw_env + def main(argv=None): parser = argparse.ArgumentParser( description=__doc__, @@ -27,7 +28,8 @@ def main(argv=None): type=argparse.FileType('w'), default=sys.stdout, help='Path to output file. Defaults to stdout.') parser.add_argument( - '-s', '--scratch-directory', metavar='PATH', default=pathlib.Path.cwd(), + '-s', '--scratch-directory', metavar='PATH', + default=pathlib.Path.cwd(), help=('Path to scratch directory for generated files, if any. ' 'Defaults to the current working directory.')) parser.add_argument( @@ -47,5 +49,6 @@ def main(argv=None): ).items(): args.output.write(f'{key}={value}\n') + if __name__ == '__main__': main() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.cc b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.cc index e146fa978..c4eae2b5e 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.cc +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.cc @@ -10,10 +10,11 @@ #include #include -#include "tools/cpp/runfiles/runfiles.h" #include #include +#include "tools/cpp/runfiles/runfiles.h" + #define LITERAL_STRINGIFY(x) #x #define STRINGIFY(x) LITERAL_STRINGIFY(x) @@ -101,7 +102,7 @@ void isolate_rmw_by_path(const std::string& argv0, const std::string& path) { size_t length = 0; auto buffer = make_scoped_instance(nullptr, free); while (getline(&buffer.get(), &length, command_stream.get()) != -1) { - char* bufferp = buffer.get(); // let strsep() mutate bufferp but not buffer + char* bufferp = buffer.get(); // let strsep() mutate bufferp but not buffer const std::string line{strsep(&bufferp, "\r\n")}; // drop trailing newline const size_t separator_index = line.find("="); const std::string name = line.substr(0, separator_index); diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.py index b8365a2f9..4e12ac7dd 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/rmw_isolation.py @@ -14,16 +14,22 @@ MULTICAST_PORTS_INTERVAL = 2 UNICAST_PORTS_INTERVAL = MAX_PARTICIPANTS_PER_PROCESS * PARTICIPANT_ID_GAIN -def generate_isolated_rmw_fastrtps_cpp_env(unique_identifier, scratch_directory): + +def generate_isolated_rmw_fastrtps_cpp_env( + unique_identifier, scratch_directory +): """ - Generates an environment that forces rmw_fastrtps_cpp network traffic isolation. + Generates an environment that forces rmw_fastrtps_cpp + network traffic isolation. - This function achieves network traffic isolation by modifying multicast discovery - IPv4 address and well-known ports offsets, which FastRTPS (now FastDDS) allows - through the FASTRTPS_DEFAULT_PROFILES_FILE environment variable. For further reference, - see https://fast-dds.docs.eprosima.com/en/latest/fastdds/transport/listening_locators.html. + This function achieves network traffic isolation by modifying multicast + discovery IPv4 address and well-known ports offsets, which FastRTPS (now + FastDDS) allows through the FASTRTPS_DEFAULT_PROFILES_FILE environment + variable. For further reference, see + https://fast-dds.docs.eprosima.com/en/latest/fastdds/transport/listening_locators.html. - :param unique_identifier: unique arbitrary string to be used as a basis for isolation. + :param unique_identifier: unique arbitrary string to be used as + a basis for isolation. :param scratch_directory: output directory for generated files. :returns: a dictionary of environment variables. """ @@ -107,18 +113,25 @@ def generate_isolated_rmw_fastrtps_cpp_env(unique_identifier, scratch_directory) profiles_path = pathlib.Path(scratch_directory) / 'fastrtps_profiles.xml' with profiles_path.open('wb') as f: f.write(pretty_xml) - return {'FASTRTPS_DEFAULT_PROFILES_FILE': str(profiles_path.resolve()), 'ROS_DOMAIN_ID': '0'} + return { + 'ROS_DOMAIN_ID': '0', + 'FASTRTPS_DEFAULT_PROFILES_FILE': str(profiles_path.resolve())} -def generate_isolated_rmw_cyclonedds_cpp_env(unique_identifier, scratch_directory): + +def generate_isolated_rmw_cyclonedds_cpp_env( + unique_identifier, scratch_directory +): """ - Generates an environment that forces rmw_cyclonedds_cpp network traffic isolation. + Generates an environment that forces rmw_cyclonedds_cpp + network traffic isolation. - This function achieves network traffic isolation by modifying multicast discovery - IPv4 address and domain ID, which CycloneDDS allows through the CYCLONEDDS_URI - environment variable. For further reference, see + This function achieves network traffic isolation by modifying multicast + discovery IPv4 address and domain ID, which CycloneDDS allows through the + CYCLONEDDS_URI environment variable. For further reference, see https://github.com/eclipse-cyclonedds/cyclonedds/blob/master/docs/manual/config.rst. - :param unique_identifier: unique arbitrary string to be used as a basis for isolation. + :param unique_identifier: unique arbitrary string to be used as + a basis for isolation. :param scratch_directory: output directory for generated files. :returns: a dictionary of environment variables. """ @@ -174,19 +187,27 @@ def generate_isolated_rmw_cyclonedds_cpp_env(unique_identifier, scratch_director inline_xml = ET.tostring(tree, encoding='utf-8') dom = minidom.parseString(inline_xml) pretty_xml = dom.toprettyxml(indent=' ' * 4, encoding='utf-8') - configuration_path = pathlib.Path(scratch_directory) / 'cyclonedds_configuration.xml' + configuration_path = pathlib.Path( + scratch_directory) / 'cyclonedds_configuration.xml' with configuration_path.open('wb') as f: f.write(pretty_xml) - return {'CYCLONEDDS_URI': f'file://{configuration_path.resolve()}', 'ROS_DOMAIN_ID': '0'} + return { + 'ROS_DOMAIN_ID': '0', + 'CYCLONEDDS_URI': f'file://{configuration_path.resolve()}'} + _RMW_ISOLATION_FUNCTIONS = { 'rmw_fastrtps_cpp': generate_isolated_rmw_fastrtps_cpp_env, 'rmw_cyclonedds_cpp': generate_isolated_rmw_cyclonedds_cpp_env } -def generate_isolated_rmw_env(unique_identifier, rmw_implementation=None, scratch_directory=None): + +def generate_isolated_rmw_env( + unique_identifier, rmw_implementation=None, scratch_directory=None +): """ - Generates an environment that forces rmw implementation network traffic isolation. + Generates an environment that forces rmw implementation + network traffic isolation. :param unique_identifier: unique arbitrary string to be used as a basis for isolation. @@ -203,10 +224,12 @@ def generate_isolated_rmw_env(unique_identifier, rmw_implementation=None, scratc if scratch_directory is None: scratch_directory = tempfile.mkdtemp() if rmw_implementation not in _RMW_ISOLATION_FUNCTIONS: - raise ValueError(f"cannot isolate unknown '{rmw_implementation}' implementation") + raise ValueError( + f"cannot isolate unknown '{rmw_implementation}' implementation") return _RMW_ISOLATION_FUNCTIONS[rmw_implementation]( unique_identifier, scratch_directory=scratch_directory) + def isolate_rmw_by_path(path): """ Isolates rmw implementation network traffic. @@ -218,6 +241,7 @@ def isolate_rmw_by_path(path): :raises RuntimeError: if called after rmw initialization. """ if rclpy.ok(): - raise RuntimeError('middleware already initialized, too late for isolation') + raise RuntimeError( + 'middleware already initialized, too late for isolation') os.environ.update(generate_isolated_rmw_env( str(path), scratch_directory=tempfile.mkdtemp(dir=str(path)))) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.cc b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.cc index c51cfe651..6cbe22e1a 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.cc +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.cc @@ -8,39 +8,33 @@ using std::placeholders::_1; -class IsolatedListener : public rclcpp::Node -{ - public: - IsolatedListener(const std::string & uuid) - : rclcpp::Node("isolated_listener"), uuid_(uuid) - { - double timeout = this->declare_parameter("timeout", 2.0); - subscription_ = this->create_subscription( - "uuid", 10, std::bind(&IsolatedListener::topic_callback, this, _1)); - timer_ = this->create_wall_timer( - std::chrono::duration(timeout), - std::bind(&IsolatedListener::timer_callback, this)); - } +class IsolatedListener : public rclcpp::Node { + public: + IsolatedListener(const std::string& uuid) + : rclcpp::Node("isolated_listener"), uuid_(uuid) { + double timeout = this->declare_parameter("timeout", 2.0); + subscription_ = this->create_subscription( + "uuid", 10, std::bind(&IsolatedListener::topic_callback, this, _1)); + timer_ = this->create_wall_timer( + std::chrono::duration(timeout), + std::bind(&IsolatedListener::timer_callback, this)); + } - private: - void topic_callback(const std_msgs::msg::String::SharedPtr msg) - { - if (msg->data != uuid_) { - throw std::runtime_error( - "I heard '" + msg->data + "' yet " + - "I was expecting '" + uuid_ + "'!"); - } - ++expected_messages_count_; + private: + void topic_callback(const std_msgs::msg::String::SharedPtr msg) { + if (msg->data != uuid_) { + throw std::runtime_error("I heard '" + msg->data + "' yet " + + "I was expecting '" + uuid_ + "'!"); } + ++expected_messages_count_; + } - void timer_callback() const - { - if (0u == expected_messages_count_) { - throw std::runtime_error( - "I did not hear '" + uuid_ + "' even once!"); - } - rclcpp::shutdown(); + void timer_callback() const { + if (0u == expected_messages_count_) { + throw std::runtime_error("I did not hear '" + uuid_ + "' even once!"); } + rclcpp::shutdown(); + } rclcpp::Subscription::SharedPtr subscription_; rclcpp::TimerBase::SharedPtr timer_; @@ -48,14 +42,13 @@ class IsolatedListener : public rclcpp::Node std::string uuid_; }; -int main(int argc, char * argv[]) -{ - const char * TEST_TMPDIR = std::getenv("TEST_TMPDIR"); +int main(int argc, char* argv[]) { + const char* TEST_TMPDIR = std::getenv("TEST_TMPDIR"); if (TEST_TMPDIR != nullptr) { ros2::isolate_rmw_by_path(argv[0], TEST_TMPDIR); } rclcpp::init(argc, argv); - std::string uuid{TEST_TMPDIR != nullptr? TEST_TMPDIR : "none"}; + std::string uuid{TEST_TMPDIR != nullptr ? TEST_TMPDIR : "none"}; rclcpp::spin(std::make_shared(uuid)); rclcpp::shutdown(); return 0; diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py index 9f3220328..1cadb5374 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py @@ -1,8 +1,8 @@ import os import rclpy -import rclpy.node import rclpy.executors +import rclpy.node import std_msgs.msg diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.cc b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.cc index e6930a908..a94c75ef9 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.cc +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.cc @@ -6,39 +6,35 @@ #include "rmw_isolation/rmw_isolation.h" -class IsolatedTalker : public rclcpp::Node -{ - public: - IsolatedTalker(const std::string & uuid) - : rclcpp::Node("isolated_talker"), uuid_(uuid) - { - publisher_ = this->create_publisher("uuid", 10); - timer_ = this->create_wall_timer( - std::chrono::duration(0.1), - std::bind(&IsolatedTalker::timer_callback, this)); - } +class IsolatedTalker : public rclcpp::Node { + public: + IsolatedTalker(const std::string& uuid) + : rclcpp::Node("isolated_talker"), uuid_(uuid) { + publisher_ = this->create_publisher("uuid", 10); + timer_ = this->create_wall_timer( + std::chrono::duration(0.1), + std::bind(&IsolatedTalker::timer_callback, this)); + } - private: - void timer_callback() const - { - std_msgs::msg::String message; - message.data = uuid_; - publisher_->publish(message); - } + private: + void timer_callback() const { + std_msgs::msg::String message; + message.data = uuid_; + publisher_->publish(message); + } rclcpp::Publisher::SharedPtr publisher_; rclcpp::TimerBase::SharedPtr timer_; std::string uuid_; }; -int main(int argc, char * argv[]) -{ - const char * TEST_TMPDIR = std::getenv("TEST_TMPDIR"); +int main(int argc, char* argv[]) { + const char* TEST_TMPDIR = std::getenv("TEST_TMPDIR"); if (TEST_TMPDIR != nullptr) { ros2::isolate_rmw_by_path(argv[0], TEST_TMPDIR); } rclcpp::init(argc, argv); - std::string uuid{TEST_TMPDIR != nullptr? TEST_TMPDIR : "none"}; + std::string uuid{TEST_TMPDIR != nullptr ? TEST_TMPDIR : "none"}; rclcpp::spin(std::make_shared(uuid)); rclcpp::shutdown(); return 0; diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py index 04beddbdc..2b342cbd3 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py @@ -39,6 +39,5 @@ def main(): raise - if __name__ == '__main__': main() diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py index b9c5104bd..a2457c018 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py @@ -54,18 +54,20 @@ def collect_ament_cmake_shared_library_codemodel(codemodel): find_library_dependencies(library) ) # Remove duplicates maintaining order. - link_libraries.extend([ - library for library in library_plus_dependencies - if library not in link_libraries and not is_system_library(library) - ]) + for library in library_plus_dependencies: + if library in link_libraries: + continue + if is_system_library(library): + continue + link_libraries.append(library) # Fail on any /usr/local libraries local_link_libraries = [ path for path in link_libraries if path.startswith('/usr/local')] if local_link_libraries: - raise RuntimeError( - 'Found libraries under /usr/local: ' + - ', '.join(local_link_libraries)) + error_message = 'Found libraries under /usr/local: ' + error_message += ', '.join(local_link_libraries) + raise RuntimeError(error_message) file_groups = codemodel['fileGroups'] assert len(file_groups) == 1 @@ -82,9 +84,9 @@ def collect_ament_cmake_shared_library_codemodel(codemodel): path for path in include_directories if path.startswith('/usr/local')] if local_include_directories: - raise RuntimeError( - 'Found include directories under /usr/local: ' + - ', '.join(local_include_directories)) + error_message = 'Found include directories under /usr/local: ' + error_message += ', '.join(local_include_directories) + raise RuntimeError(error_message) defines = [] if 'defines' in file_group: @@ -143,7 +145,7 @@ def collect_ament_cmake_package_properties(name, metadata): ]}, timeout=30, message_callback=print) cmake.compute(timeout=20, message_callback=print) codemodel = cmake.codemodel(timeout=10) - except: + except Exception: import shutil shutil.rmtree('error_case', ignore_errors=True) shutil.copytree(project_path, 'error_case') @@ -173,7 +175,9 @@ def collect_ament_cmake_package_properties(name, metadata): return properties -def collect_ament_cmake_package_direct_properties(name, metadata, dependencies, cache): +def collect_ament_cmake_package_direct_properties( + name, metadata, dependencies, cache +): if 'ament_cmake' not in cache: cache['ament_cmake'] = {} ament_cmake_cache = cache['ament_cmake'] @@ -209,13 +213,16 @@ def collect_ament_cmake_package_direct_properties(name, metadata, dependencies, library for library in properties['link_libraries'] if library not in dependency_properties['link_libraries'] ] - properties['include_directories'] = [ - directory for directory in properties['include_directories'] - if directory not in dependency_properties['include_directories'] - # leverage REP-122 - or os.path.exists(os.path.join(directory, name)) - ] - # Do not deduplicate link directories in case we're dealing with merge installs. + deduplicated_include_directories = [] + for directory in properties['include_directories']: + if directory in dependency_properties['include_directories']: + # We may be dealing with a merge install. + # Try leverage REP-122. + if not os.path.exists(os.path.join(directory, name)): + continue + deduplicated_include_directories.append(directory) + # Do not deduplicate link directories in case we're dealing with + # merge installs. return properties diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_python.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_python.py index 3387da17e..892553547 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_python.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_python.py @@ -1,8 +1,7 @@ import glob -import sysconfig - from importlib.metadata import distribution from importlib.metadata import PackageNotFoundError +import sysconfig from ros2bzl.scraping.system import find_library_dependencies from ros2bzl.scraping.system import is_system_library @@ -39,7 +38,9 @@ def collect_ament_python_package_properties(name, metadata): return properties -def collect_ament_python_package_direct_properties(name, metadata, dependencies, cache): +def collect_ament_python_package_direct_properties( + name, metadata, dependencies, cache +): if 'ament_python' not in cache: cache['ament_python'] = {} ament_python_cache = cache['ament_python'] diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py index 938568e72..309f7c5c4 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py @@ -20,7 +20,9 @@ def is_system_include(include_path): i.e. known to compilers. """ include_path = os.path.realpath(include_path) - return any(include_path.startswith(path) for path in DEFAULT_INCLUDE_DIRECTORIES) + return any( + include_path.startswith(path) + for path in DEFAULT_INCLUDE_DIRECTORIES) # Standard library files' search paths for linkers in Linux systems. @@ -34,7 +36,9 @@ def is_system_library(library_path): i.e. known to linkers. """ library_path = os.path.realpath(library_path) - return any(library_path.startswith(path) for path in DEFAULT_LINK_DIRECTORIES) + return any( + library_path.startswith(path) + for path in DEFAULT_LINK_DIRECTORIES) LD_LIBRARY_PATHS = [ diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py index d4edb9200..4405c5387 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/templates.py @@ -53,12 +53,15 @@ def configure_package_interfaces_filegroup(name, metadata, sandbox): name, load_resource('templates/package_interfaces_filegroup.bazel.tpl'), to_starlark_string_dict({ - 'name': name, 'share_directory': sandbox(metadata['share_directory']) + 'name': name, + 'share_directory': sandbox(metadata['share_directory']) }) ) -def configure_package_cc_library(name, metadata, properties, dependencies, extras, sandbox): +def configure_package_cc_library( + name, metadata, properties, dependencies, extras, sandbox +): target_name = cc_name(name, metadata) libraries = [sandbox(library) for library in properties['link_libraries']] include_directories = [ @@ -128,7 +131,9 @@ def configure_package_cc_library(name, metadata, properties, dependencies, extra 'deps': deps, } - return target_name, load_resource(template_path), to_starlark_string_dict(config) + return ( + target_name, load_resource(template_path), + to_starlark_string_dict(config)) def configure_package_meta_py_library(name, metadata, dependencies): @@ -146,7 +151,9 @@ def configure_package_meta_py_library(name, metadata, dependencies): ) -def configure_package_py_library(name, metadata, properties, dependencies, extras, sandbox): +def configure_package_py_library( + name, metadata, properties, dependencies, extras, sandbox +): target_name = py_name(name, metadata) eggs = [sandbox(egg_path) for egg_path, _ in properties['python_packages']] tops = [ @@ -185,7 +192,8 @@ def configure_package_py_library(name, metadata, properties, dependencies, extra template = 'templates/package_py_library_with_cc_libs.bazel.tpl' config.update({ 'cc_name': c_name("_" + target_name, metadata), - 'cc_libs': [sandbox(lib) for lib in properties['cc_libraries']], + 'cc_libs': [ + sandbox(lib) for lib in properties['cc_libraries']], 'cc_deps': cc_deps }) data.append(c_label("_" + target_name, metadata)) From 7e44de53b8310d47e67de9aea69faaf4a2e1767a Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Mon, 3 Jan 2022 18:05:18 -0300 Subject: [PATCH 102/107] Manage rosidl generated deps properly Signed-off-by: Michel Hidalgo --- .../tools/skylark/dload.bzl | 6 +-- .../ros2/ros2bzl/scraping/ament_cmake.py | 42 +++++++++++-------- .../skylark/ros2/ros2bzl/scraping/system.py | 2 +- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/dload.bzl b/drake_ros_bazel_installed/tools/skylark/dload.bzl index 0cb8c1b18..cd9e4a581 100644 --- a/drake_ros_bazel_installed/tools/skylark/dload.bzl +++ b/drake_ros_bazel_installed/tools/skylark/dload.bzl @@ -115,11 +115,7 @@ def _resolve_runfile_path(ctx, path): All `$(rootpath...)` templates in the given path, if any, will be expanded. """ - path = _normpath(ctx.expand_location(path)) - path = path.lstrip("@") - if path.startswith("/"): - path = ctx.workspace_name + path - return path + return _normpath(ctx.expand_location(path)) def _parse_runtime_environment_action(ctx, action): """ diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py index a2457c018..017045fce 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/ament_cmake.py @@ -14,7 +14,9 @@ from ros2bzl.scraping.system import is_system_library -def collect_ament_cmake_shared_library_codemodel(codemodel): +def collect_ament_cmake_shared_library_codemodel( + codemodel, additional_libraries +): assert codemodel['type'] == 'SHARED_LIBRARY' link_flags = [] @@ -34,6 +36,11 @@ def collect_ament_cmake_shared_library_codemodel(codemodel): link_flags.append(item) continue libraries.append(item) + for library in additional_libraries: + if library in libraries: + continue + libraries.append(library) + local_link_libraries = [] for library in libraries: if not os.path.isabs(library): if library.startswith('-l'): @@ -58,12 +65,11 @@ def collect_ament_cmake_shared_library_codemodel(codemodel): if library in link_libraries: continue if is_system_library(library): + if library.startswith('/usr/local'): + local_link_libraries.append(library) continue link_libraries.append(library) # Fail on any /usr/local libraries - local_link_libraries = [ - path for path in link_libraries - if path.startswith('/usr/local')] if local_link_libraries: error_message = 'Found libraries under /usr/local: ' error_message += ', '.join(local_link_libraries) @@ -75,14 +81,15 @@ def collect_ament_cmake_shared_library_codemodel(codemodel): include_directories = [] if 'includePath' in file_group: - include_directories.extend([ - entry['path'] for entry in file_group['includePath'] - if not is_system_include(entry['path']) - ]) + local_include_directories = [] + for entry in file_group['includePath']: + path = entry['path'] + if is_system_include(path): + if path.startswith('/usr/local'): + local_include_directories.append(path) + continue + include_directories.append(path) # Fail on any /usr/local include directories - local_include_directories = [ - path for path in include_directories - if path.startswith('/usr/local')] if local_include_directories: error_message = 'Found include directories under /usr/local: ' error_message += ', '.join(local_include_directories) @@ -163,15 +170,16 @@ def collect_ament_cmake_package_properties(name, metadata): assert project_name in targets target = targets[project_name] - properties = collect_ament_cmake_shared_library_codemodel(target) + additional_libraries = [] if 'rosidl_interface_packages' in metadata.get('groups', []): - # Pick up extra shared libraries in interface - # packages for adequate sandboxing + # Pick up extra shared libraries in interface packages for + # proper sandboxing glob_pattern = os.path.join( metadata['prefix'], 'lib', f'lib{name}__rosidl*.so') - for library in glob.glob(glob_pattern): - if library not in properties['link_libraries']: - properties['link_libraries'].append(library) + additional_libraries.extend(glob.glob(glob_pattern)) + properties = collect_ament_cmake_shared_library_codemodel( + target, additional_libraries + ) return properties diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py index 309f7c5c4..8ad570bb0 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/system.py @@ -32,7 +32,7 @@ def is_system_include(include_path): def is_system_library(library_path): """ - Checks whether `include_path` is in a system library directory + Checks whether `library_path` is in a system library directory i.e. known to linkers. """ library_path = os.path.realpath(library_path) From 8d5a489964ef1af23c2b48a56efdcae17ec82e36 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 5 Jan 2022 17:28:01 -0300 Subject: [PATCH 103/107] Simplify cmake_tools members naming Signed-off-by: Michel Hidalgo --- .../skylark/ros2/cmake_tools/server_mode.py | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/server_mode.py b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/server_mode.py index 8ec1a834c..6e29d9ff5 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/server_mode.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/cmake_tools/server_mode.py @@ -19,100 +19,100 @@ class CMakeServer: def __init__(self, address=None): if not address: address = tempfile.mktemp() - self.__address = address + self._address = address cmd = ['cmake', '-E', 'server', '--experimental'] - cmd.append('--pipe=' + self.__address) - self.__process = subprocess.Popen(cmd) - while not os.path.exists(self.__address): - if self.__process.poll() is not None: + cmd.append('--pipe=' + self._address) + self._process = subprocess.Popen(cmd) + while not os.path.exists(self._address): + if self._process.poll() is not None: break time.sleep(1) @property def address(self): - return self.__address + return self._address def shutdown(self, timeout=None): - self.__process.send_signal(signal.SIGINT) + self._process.send_signal(signal.SIGINT) try: - self.__process.wait(timeout) + self._process.wait(timeout) except subprocess.TimeoutExpired: - self.__process.kill() + self._process.kill() class CMakeClient: def __init__(self, address): - self.__socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self._socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) try: - self.__socket.connect(address) - self.__selector = selectors.DefaultSelector() - self.__selector.register(self.__socket, selectors.EVENT_READ) - self.__unpack_buffer = '' + self._socket.connect(address) + self._selector = selectors.DefaultSelector() + self._selector.register(self._socket, selectors.EVENT_READ) + self._unpack_buffer = '' - message = next(self.__receive(timeout=5), None) + message = next(self._receive(timeout=5), None) assert message assert message['type'] == 'hello' - self.__supported_protocol_versions = \ + self._supported_protocol_versions = \ message['supportedProtocolVersions'] except Exception: - self.__socket.close() + self._socket.close() raise @property def supported_protocol_versions(self): - return self.__supported_protocol_versions + return self._supported_protocol_versions def shutdown(self): - self.__socket.close() + self._socket.close() - def __pack(self, payload): + def _pack(self, payload): return '\n'.join([ CMakeServer.START_MAGIC_STRING, json.dumps(payload), CMakeServer.END_MAGIC_STRING ]) + '\n' - def __unpack(self, partial): - self.__unpack_buffer += partial - while self.__unpack_buffer: + def _unpack(self, partial): + self._unpack_buffer += partial + while self._unpack_buffer: packet_start_index = \ - self.__unpack_buffer.find(CMakeServer.START_MAGIC_STRING) + self._unpack_buffer.find(CMakeServer.START_MAGIC_STRING) if packet_start_index < 0: break payload_start_index = \ packet_start_index + len(CMakeServer.START_MAGIC_STRING) - payload_end_index = self.__unpack_buffer.find( + payload_end_index = self._unpack_buffer.find( CMakeServer.END_MAGIC_STRING, payload_start_index ) if payload_end_index < 0: break payload = json.loads( - self.__unpack_buffer[payload_start_index:payload_end_index] + self._unpack_buffer[payload_start_index:payload_end_index] ) packet_end_index = \ payload_end_index + len(CMakeServer.END_MAGIC_STRING) - self.__unpack_buffer = self.__unpack_buffer[packet_end_index:] + self._unpack_buffer = self._unpack_buffer[packet_end_index:] assert type(payload) is dict assert 'type' in payload yield payload - def __send(self, payload): - packet = self.__pack(payload).encode('utf-8') - written = self.__socket.send(packet) + def _send(self, payload): + packet = self._pack(payload).encode('utf-8') + written = self._socket.send(packet) return len(packet) == written - def __receive(self, timeout=None): + def _receive(self, timeout=None): if timeout is not None: deadline = time.time() + timeout while True: - events = self.__selector.select(timeout) + events = self._selector.select(timeout) for key, mask in events: - assert key.fileobj is self.__socket + assert key.fileobj is self._socket assert mask & selectors.EVENT_READ - partial = self.__socket.recv(4096) - yield from self.__unpack(partial.decode('utf-8')) + partial = self._socket.recv(4096) + yield from self._unpack(partial.decode('utf-8')) if timeout is not None: current_time = time.time() if current_time >= deadline: @@ -126,8 +126,8 @@ def exchange( progress_callback=None, **kwargs ): - assert self.__send(request) - for response in self.__receive(**kwargs): + assert self._send(request) + for response in self._receive(**kwargs): if response['type'] == 'signal': # Ignore signal responses continue @@ -154,7 +154,7 @@ def exchange( 'Timeout waiting for reply to {!r} request'.format(request) ) - def __request_method(self, type_, attributes=None, **kwargs): + def _request_method(self, type_, attributes=None, **kwargs): request = {'type': type_} if attributes: request.update(attributes) @@ -166,7 +166,7 @@ def __request_method(self, type_, attributes=None, **kwargs): } def __getattr__(self, name): - return functools.partial(self.__request_method, name) + return functools.partial(self._request_method, name) @contextlib.contextmanager From efb4d54d1d019123cea8cdb2877dc1132cc4137d Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 5 Jan 2022 17:30:34 -0300 Subject: [PATCH 104/107] Rearrange comments in rmw_isolation BUILD file Signed-off-by: Michel Hidalgo --- .../ros2/resources/rmw_isolation/package.BUILD.bazel | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel index b7e6d5dd0..55cc7c9d2 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/package.BUILD.bazel @@ -73,8 +73,9 @@ sh_test( ], data = [":isolated_talker_py", ":isolated_listener_py"], size = "small", - flaky = True, # because collision rates are non-zero + # Set flaky because collision rates are non-zero, # See ROS2 Skylark tooling README.md for further reference. + flaky = True, ) ros_cc_binary( @@ -108,6 +109,7 @@ sh_test( ], data = [":isolated_talker_cc", ":isolated_listener_cc"], size = "small", - flaky = True, # because collision rates are non-zero, + # Set flaky because collision rates are non-zero, # See ROS2 Skylark tooling README.md for further reference. + flaky = True, ) From cae236a5ebd846fecb11d8aecdda755fded5f1df Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 5 Jan 2022 17:42:13 -0300 Subject: [PATCH 105/107] Comment isolated nodes in rmw_isolation tests Signed-off-by: Michel Hidalgo --- .../resources/rmw_isolation/test/isolated_listener.py | 9 +++++++++ .../ros2/resources/rmw_isolation/test/isolated_talker.py | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py index 1cadb5374..91aa703af 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_listener.py @@ -42,6 +42,15 @@ def main(): try: executor = rclpy.executors.SingleThreadedExecutor() rclpy.spin(IsolatedListener(uuid), executor) + # TODO(hidmic): simplify `except` blocks as follows: + # + # except KeyboardInterrupt: + # pass + # finally: + # rclpy.try_shutdown() + # + # when rclpy 3.2.0 is rolled out (or any rclpy version + # including https://github.com/ros2/rclpy/pull/868 is). except rclpy.executors.ExternalShutdownException: pass except KeyboardInterrupt: diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py index 2b342cbd3..fc3622a0e 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/resources/rmw_isolation/test/isolated_talker.py @@ -30,6 +30,15 @@ def main(): uuid = os.environ.get('TEST_TMPDIR', 'none') try: rclpy.spin(IsolatedTalker(uuid)) + # TODO(hidmic): simplify `except` blocks as follows: + # + # except KeyboardInterrupt: + # pass + # finally: + # rclpy.try_shutdown() + # + # when rclpy 3.2.0 is rolled out (or any rclpy version + # including https://github.com/ros2/rclpy/pull/868 is). except rclpy.executors.ExternalShutdownException: pass except KeyboardInterrupt: From 74e2e585b36cbf34bfa50096c44bcb41e7aa0712 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 11 Jan 2022 11:44:39 -0300 Subject: [PATCH 106/107] Update ROS 2 Skylark tools README.md Signed-off-by: Michel Hidalgo --- drake_ros_bazel_installed/tools/skylark/ros2/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/README.md b/drake_ros_bazel_installed/tools/skylark/ros2/README.md index 0cab4f8b1..1eea4f28c 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/README.md +++ b/drake_ros_bazel_installed/tools/skylark/ros2/README.md @@ -71,9 +71,11 @@ the artifacts it generates, the following targets may be found at the root - A `_` Python binary for each executable installed at the package-level (i.e. under `lib/`, where `ros2 run` can find them). -- A `` Python binary for each executable (binary or not) - installed under the `/bin` directory (and thus accessible - via `$PATH` when sourcing the workspace install space). +- An `` Python binary wrapper for each executable of *any* + kind (Python, shell script, compiled binary, etc.) installed under the + `/bin` directory (and thus accessible via `$PATH` when + sourcing the workspace install space). These executables are exposed as + Python binaries for simplicty. #### Rules From f068da47d1ab67a0670e2fdef098e81d97651330 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 11 Jan 2022 11:45:06 -0300 Subject: [PATCH 107/107] Document ros2bzl.scraping.collect_ros_package_metadata() Signed-off-by: Michel Hidalgo --- .../skylark/ros2/ros2bzl/scraping/metadata.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/metadata.py b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/metadata.py index ff1dcfdb3..5edb8184a 100644 --- a/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/metadata.py +++ b/drake_ros_bazel_installed/tools/skylark/ros2/ros2bzl/scraping/metadata.py @@ -75,6 +75,20 @@ def collect_cmake_package_metadata(name, prefix): def collect_ros_package_metadata(name, prefix): + """ + Collects ROS package metadata. + + Metadata includes package `prefix`, `share_directory`, + `ament_index_directory`, `build_type`, `build_export_dependencies`, + `run_dependencies`, `group_dependencies`, `groups`, `plugin_libraries`, + `executables`, and `langs` (i.e expected code languages). + + This function supports both symlink and merged install workspaces. + + :param name: ROS package name + :param prefix: ROS package install prefix + :returns: metadata as a dictionary + """ share_directory = os.path.join(prefix, 'share', name) ament_index_directory = os.path.join(prefix, 'share', 'ament_index')