Skip to content

[ROS 2] Support modules on OSX with .so or .bundle suffix #200

@sloretz

Description

@sloretz

I ran into an issue on ros2/urdf#13 where pluginlib is unable to find a module on only OSX. The cause is I buit the library using the MODULE type instead of the SHARED type, and that caused the built library name to be liburdf_xml_parser.so instead of liburdf_xml_parser.dylib.

add_library(urdf_xml_parser MODULE
  src/urdf_plugin.cpp
)

https://github.com/ros2/urdf/pull/13/files#diff-a6cb7a77ca27d5dcfb7fe4ec2004f324R52-R54

It seems like MODULE would be more correct for pluginlib plugins.

SHARED libraries are linked dynamically and loaded at runtime. MODULE libraries are plugins that are not linked into other targets but may be loaded dynamically at runtime using dlopen-like functionality

On OSX Shared Libraries and Loadable Modules are different. Summarizing from the link above: both can be loaded through the dyld api, but loadable modules can't be directly linked against, and shared libraries can't be dynamically unloaded. This means it's advantageous to use MODULE in cases where one wants plugins to be unloaded. The practical difference to pluginlib is loadable modules have different file extensions on OSX. CMake creates modules with the file extension .so, but Apple apparently recommends an extension of .bundle (though I only found second hand references to this recommendation). Pluginlib should search for libraries with all of these extensions.

I can think of a few paths forward

Option 1 Embed the path in the ament index entry

// TODO(wjwwood): probably should avoid "searching" and just embed the
// relative path to the libraries in the ament index, since CMake knows it
// at build time...

This option seems preferable to me because OSX allows modules to have any file extension. If it's known at build time, then no platform specific knowledge is needed in code, and it will take less time to load the module because it's location will be known right away.

Option 2 Add std::vector<std::string> rcpptuils_get_platform_module_names()

This would be a new utility that returned lib???.so, lib???.dylib, and lib???.bundle. It would be called adjacent to the blocks below to add to the search paths.

std::vector<std::string> all_relative_library_paths = {
rcpputils::get_platform_library_name(library_name),
rcpputils::get_platform_library_name(library_name_alternative),
rcpputils::get_platform_library_name(stripped_library_name),
rcpputils::get_platform_library_name(stripped_library_name_alternative)
};
std::vector<std::string> all_relative_debug_library_paths = {
rcpputils::get_platform_library_name(library_name, true),
rcpputils::get_platform_library_name(library_name_alternative, true),
rcpputils::get_platform_library_name(stripped_library_name, true),
rcpputils::get_platform_library_name(stripped_library_name_alternative, true)

Option 3 Recommend SHARED instead of MODULE

This is what we do in practice now, but I think this is the least desirable option because SHARED libraries can't be unloaded on OSX.

See also:
https://stackoverflow.com/questions/2339679/what-are-the-differences-between-so-and-dylib-on-osx

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions