diff --git a/CMakeLists.txt b/CMakeLists.txt index b9e6a8ff..01b2d9a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,9 @@ find_package(rclcpp REQUIRED) find_package(rmw_implementation_cmake REQUIRED) find_package(std_msgs REQUIRED) +# include / ignore lists +include(cmake/find_ignore_include_lists.cmake) + # find ROS 1 packages set(cmake_extras_files cmake/find_ros1_package.cmake cmake/find_ros1_interface_packages.cmake) include(cmake/find_ros1_package.cmake) @@ -44,6 +47,7 @@ find_ros1_package(std_msgs REQUIRED) # find ROS 1 packages with messages / services include(cmake/find_ros1_interface_packages.cmake) find_ros1_interface_packages(ros1_message_packages) +filter_packages("ros1_message_packages") set(prefixed_ros1_message_packages "") foreach(ros1_message_package ${ros1_message_packages}) @@ -52,6 +56,8 @@ foreach(ros1_message_package ${ros1_message_packages}) if(NOT "${ros1_message_package}" STREQUAL "nodelet") find_ros1_package(${ros1_message_package} REQUIRED) list(APPEND prefixed_ros1_message_packages "ros1_${ros1_message_package}") + else() + message(STATUS "Ignoring module '${ros1_message_package}'") endif() endforeach() @@ -90,6 +96,8 @@ list(APPEND generated_files "${generated_path}/get_mappings.cpp") # generate per interface compilation units to keep the memory usage low ament_index_get_resources(ros2_interface_packages "rosidl_interfaces") +filter_packages("ros2_interface_packages") + # actionlib_msgs is deprecated, but we will quiet the warning until the bridge has support for # ROS actions: https://github.com/ros2/design/issues/195 set(actionlib_msgs_DEPRECATED_QUIET TRUE) @@ -110,6 +118,8 @@ foreach(package_name ${ros2_interface_packages}) get_filename_component(interface_name "${interface_name}" NAME_WE) list(APPEND generated_files "${generated_path}/${package_name}__${interface_name}__factories.cpp") endforeach() + else() + message(STATUS " ignoring.") endif() endforeach() @@ -125,7 +135,9 @@ set(target_dependencies add_custom_command( OUTPUT ${generated_files} COMMAND ${PYTHON_EXECUTABLE} bin/ros1_bridge_generate_factories - --output-path "${generated_path}" --template-dir resource + --output-path '${generated_path}' --template-dir resource + --pkg-includes '${BRIDGE_INCLUDE_PKGS}' + --pkg-ignores '${BRIDGE_IGNORE_PKGS}' DEPENDS ${target_dependencies} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating factories for interface types") diff --git a/bin/ros1_bridge_generate_factories b/bin/ros1_bridge_generate_factories index cc7f3d1c..26523e58 100755 --- a/bin/ros1_bridge_generate_factories +++ b/bin/ros1_bridge_generate_factories @@ -23,12 +23,22 @@ def main(argv=sys.argv[1:]): '--template-dir', required=True, help='The location of the template files') + parser.add_argument( + '--pkg-includes', + required=False, + help='ROS packages to include in the bridge') + parser.add_argument( + '--pkg-ignores', + required=False, + help='ROS packages to ignore in the bridge') args = parser.parse_args(argv) try: return generate_cpp( args.output_path, args.template_dir, + args.pkg_includes, + args.pkg_ignores, ) except RuntimeError as e: print(str(e), file=sys.stderr) diff --git a/cmake/find_ignore_include_lists.cmake b/cmake/find_ignore_include_lists.cmake new file mode 100644 index 00000000..2a8010f7 --- /dev/null +++ b/cmake/find_ignore_include_lists.cmake @@ -0,0 +1,68 @@ +# Module/Package resp. message ignores and includes +# +# Finds lists of ROS modules/packages resp. messages that shall +# be included resp. ignored by the ROS bridge according to the +# following rules: +# - Include lists over ignore lists. I.e. if an include list is +# present, the ignore list will be ... well, ignored.) + +set(USE_PKG_IGNORE OFF) +set(USE_PKG_INCLUDE OFF) +set(USE_MSG_IGNORE OFF) +set(USE_MSG_INCLUDE OFF) + +set(BRIDGE_IGNORE_PKGS "") +set(BRIDGE_IGNORE_MSGS "") +set(BRIDGE_INCLUDE_PKGS "") +set(BRIDGE_INCLUDE_MSGS "") + +# File with packages to ignore/include in ROS 1 Bridge +set(BRIDGE_PACKAGE_IGNORE "${CMAKE_SOURCE_DIR}/../../../BridgePackageIgnore.List") +set(BRIDGE_PACKAGE_INCLUDE "${CMAKE_SOURCE_DIR}/../../../BridgePackageInclude.List") + +# File with messages to ignore/include in ROS 1 Bridge +set(BRIDGE_MSG_IGNORE "${CMAKE_SOURCE_DIR}/../../../BridgePackageIgnore.List") +set(BRIDGE_MSG_INCLUDE "${CMAKE_SOURCE_DIR}/../../../BridgePackageInclude.List") + +if(EXISTS "${BRIDGE_PACKAGE_INCLUDE}") + set(USE_PKG_INCLUDE ON) +else() # EXISTS "${BRIDGE_PACKAGE_INCLUDE}" + if(EXISTS "${BRIDGE_PACKAGE_IGNORE}") + set(USE_PKG_IGNORE ON) + endif() # EXISTS "${BRIDGE_PACKAGE_IGNORE}" +endif() # EXISTS "${BRIDGE_PACKAGE_INCLUDE}" + +if(${USE_PKG_INCLUDE}) + FILE(READ "${BRIDGE_PACKAGE_INCLUDE}" BRIDGE_INCLUDE_PKGS) + STRING(REGEX REPLACE ";" "\\\\;" BRIDGE_INCLUDE_PKGS "${BRIDGE_INCLUDE_PKGS}") + STRING(REGEX REPLACE "\n" ";" BRIDGE_INCLUDE_PKGS "${BRIDGE_INCLUDE_PKGS}") + list(REMOVE_DUPLICATES BRIDGE_INCLUDE_PKGS) + message(STATUS "Found package include list, including: ${BRIDGE_INCLUDE_PKGS}") +endif() # USE_PKG_INCLUDE + +if(${USE_PKG_IGNORE}) + FILE(READ "${BRIDGE_PACKAGE_IGNORE}" BRIDGE_IGNORE_PKGS) + STRING(REGEX REPLACE ";" "\\\\;" BRIDGE_IGNORE_PKGS "${BRIDGE_IGNORE_PKGS}") + STRING(REGEX REPLACE "\n" ";" BRIDGE_IGNORE_PKGS "${BRIDGE_IGNORE_PKGS}") + list(REMOVE_DUPLICATES BRIDGE_IGNORE_PKGS) + message(STATUS "Found package ignore list, ignoring: ${BRIDGE_IGNORE_PKGS}") +endif() # USE_PKG_IGNORE + +function(filter_packages pkg_list) + set(ros2_packages ${${pkg_list}}) + if(BRIDGE_INCLUDE_PKGS) + foreach(pkg ${ros2_packages}) + if(NOT ${pkg} IN_LIST BRIDGE_INCLUDE_PKGS) + list(REMOVE_ITEM ros2_packages ${pkg}) + endif(NOT ${pkg} IN_LIST BRIDGE_INCLUDE_PKGS) + endforeach() + elseif(BRIDGE_IGNORE_PKGS) + foreach(pkg ${ros2_packages}) + if(${pkg} IN_LIST BRIDGE_IGNORE_PKGS) + list(REMOVE_ITEM ros2_packages ${pkg}) + endif(${pkg} IN_LIST BRIDGE_IGNORE_PKGS) + endforeach() + endif(BRIDGE_INCLUDE_PKGS) + + set(${pkg_list} "${ros2_packages}" PARENT_SCOPE) +endfunction(filter_packages ros2_packages) diff --git a/ros1_bridge/__init__.py b/ros1_bridge/__init__.py index 9f89c706..62eb4743 100644 --- a/ros1_bridge/__init__.py +++ b/ros1_bridge/__init__.py @@ -64,16 +64,16 @@ import rosmsg # noqa -def generate_cpp(output_path, template_dir): +def generate_cpp(output_path, template_dir, pkg_includes, pkg_ignores): rospack = rospkg.RosPack() - data = generate_messages(rospack) + data = generate_messages(rospack, pkg_includes, pkg_ignores) message_string_pairs = { ( '%s/%s' % (m.ros1_msg.package_name, m.ros1_msg.message_name), '%s/%s' % (m.ros2_msg.package_name, m.ros2_msg.message_name)) for m in data['mappings']} data.update( - generate_services(rospack, message_string_pairs=message_string_pairs)) + generate_services(rospack, message_string_pairs=message_string_pairs, includes=pkg_includes, ignores=pkg_ignores)) template_file = os.path.join(template_dir, 'get_mappings.cpp.em') output_file = os.path.join(output_path, 'get_mappings.cpp') @@ -158,11 +158,11 @@ def generate_cpp(output_path, template_dir): expand_template(template_file, data_idl_cpp, output_file) -def generate_messages(rospack=None): +def generate_messages(rospack=None, includes=[], ignores=[]): ros1_msgs = get_ros1_messages(rospack=rospack) - ros2_package_names, ros2_msgs, mapping_rules = get_ros2_messages() + ros2_package_names, ros2_msgs, mapping_rules = get_ros2_messages(includes, ignores) - package_pairs = determine_package_pairs(ros1_msgs, ros2_msgs, mapping_rules) + package_pairs = determine_package_pairs(ros1_msgs, ros2_msgs, mapping_rules, includes, ignores) message_pairs = determine_message_pairs(ros1_msgs, ros2_msgs, package_pairs, mapping_rules) mappings = [] @@ -225,9 +225,9 @@ def generate_messages(rospack=None): } -def generate_services(rospack=None, message_string_pairs=None): +def generate_services(rospack=None, message_string_pairs=None, includes=[], ignores=[]): ros1_srvs = get_ros1_services(rospack=rospack) - ros2_pkgs, ros2_srvs, mapping_rules = get_ros2_services() + ros2_pkgs, ros2_srvs, mapping_rules = get_ros2_services(includes, ignores) services = determine_common_services( ros1_srvs, ros2_srvs, mapping_rules, message_string_pairs=message_string_pairs) @@ -249,7 +249,7 @@ def get_ros1_messages(rospack=None): return msgs -def get_ros2_messages(): +def get_ros2_messages(includes=[], ignores=[]): pkgs = [] msgs = [] rules = [] @@ -257,6 +257,9 @@ def get_ros2_messages(): resource_type = 'rosidl_interfaces' resources = ament_index_python.get_resources(resource_type) for package_name, prefix_path in resources.items(): + if (includes and not package_name in includes) or (ignores and package_name in ignores): + # Ignore package + continue pkgs.append(package_name) resource, _ = ament_index_python.get_resource(resource_type, package_name) interfaces = resource.splitlines() @@ -303,13 +306,16 @@ def get_ros1_services(rospack=None): return srvs -def get_ros2_services(): +def get_ros2_services(includes=[], ignores=[]): pkgs = [] srvs = [] rules = [] resource_type = 'rosidl_interfaces' resources = ament_index_python.get_resources(resource_type) for package_name, prefix_path in resources.items(): + if (includes and not package_name in includes) or (ignores and package_name in ignores): + # Ignore package + continue pkgs.append(package_name) resource, _ = ament_index_python.get_resource(resource_type, package_name) interfaces = resource.splitlines() @@ -473,7 +479,7 @@ def __str__(self): return 'ServiceMappingRule(%s <-> %s)' % (self.ros1_package_name, self.ros2_package_name) -def determine_package_pairs(ros1_msgs, ros2_msgs, mapping_rules): +def determine_package_pairs(ros1_msgs, ros2_msgs, mapping_rules, includes, ignores): pairs = [] # determine package names considered equal between ROS 1 and ROS 2 ros1_suffix = '_msgs' @@ -481,11 +487,18 @@ def determine_package_pairs(ros1_msgs, ros2_msgs, mapping_rules): ros1_package_names = {m.package_name for m in ros1_msgs} ros2_package_names = {m.package_name for m in ros2_msgs} for ros1_package_name in ros1_package_names: - if not ros1_package_name.endswith(ros1_suffix): + if (not ros1_package_name.endswith(ros1_suffix)): + continue + if (includes and not ros1_package_name in includes) or (ignores and ros1_package_name in ignores): + # Ignore package continue ros1_package_basename = ros1_package_name[:-len(ros1_suffix)] for ros2_package_name in ros2_package_names: + if (includes and not ros2_package_name in includes) or (ignores and ros2_package_name in ignores): + # Ignore package + continue + for ros2_suffix in ros2_suffixes: if ros2_package_name.endswith(ros2_suffix): break