From c7ca4ed0ed8db706bf58a339850e5cc34f1a9c2e Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Mon, 25 Mar 2019 16:43:20 -0400 Subject: [PATCH] find_library: Centralize functionality here README: Update to indicate new functionality Signed-off-by: Eric Cousineau --- CMakeLists.txt | 20 ++++++ README.md | 15 +++- include/rcpputils/find_library.hpp | 37 ++++++++++ src/find_library.cpp | 106 +++++++++++++++++++++++++++++ test/test_find_library.cpp | 53 +++++++++++++++ test/toy_test_library.cpp | 22 ++++++ 6 files changed, 250 insertions(+), 3 deletions(-) create mode 100644 include/rcpputils/find_library.hpp create mode 100644 src/find_library.cpp create mode 100644 test/test_find_library.cpp create mode 100644 test/toy_test_library.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ab7715..2fdb6a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,14 @@ include_directories(include) ament_export_include_directories(include) +add_library(${PROJECT_NAME} SHARED + src/find_library.cpp) +target_include_directories(${PROJECT_NAME} + PUBLIC + include +) +ament_export_libraries(${PROJECT_NAME}) + if(BUILD_TESTING) find_package(ament_cmake_gtest REQUIRED) find_package(ament_lint_auto REQUIRED) @@ -32,6 +40,13 @@ if(BUILD_TESTING) ament_lint_auto_find_test_dependencies() ament_add_gtest(test_basic test/test_basic.cpp) + + add_library(toy_test_library SHARED test/toy_test_library.cpp) + ament_add_gtest(test_find_library test/test_find_library.cpp) + target_link_libraries(test_find_library ${PROJECT_NAME} toy_test_library) + set_tests_properties(test_find_library PROPERTIES + ENVIRONMENT + "_TOY_TEST_LIBRARY_DIR=$;_TOY_TEST_LIBRARY=$") endif() ament_package() @@ -39,3 +54,8 @@ ament_package() install( DIRECTORY include/ DESTINATION include) +install( + TARGETS ${PROJECT_NAME} + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) diff --git a/README.md b/README.md index bce01be..7dfbdc0 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ `rcpputils` is a C++ API consisting of macros, functions, and data structures intended for use throughout the ROS 2 codebase -This package currently contains: -* Clang thread safety annotation macros +See below sections for what this package currently contains. ## Clang Thread Safety Annotation Macros -the `rcpputils/thread_safety_annotations.h` header provides macros for Clang's [Thread Safety Analysis](https://clang.llvm.org/docs/ThreadSafetyAnalysis.html) feature. + +The `rcpputils/thread_safety_annotations.h` header provides macros for Clang's [Thread Safety Analysis](https://clang.llvm.org/docs/ThreadSafetyAnalysis.html) feature. The macros allow you to annotate your code, but expand to nothing when using a non-clang compiler, so they are safe for cross-platform use. @@ -17,3 +17,12 @@ To use thread safety annotation in your package (in the Clang build only), enabl ``` For example usage, see [the documentation of this feature](https://clang.llvm.org/docs/ThreadSafetyAnalysis.html) and the tests in `test/test_basic.cpp` + +## Library Discovery + +In [`rcpputils/find_library.hpp`](./include/rcpputils/find_library.hpp): + +* `find_library(library_name)`: Namely used for dynamically loading RMW +implementations. + * For dynamically loading user-defind plugins in C++, please use +[`pluginlib`](https://github.com/ros/pluginlib) instead. diff --git a/include/rcpputils/find_library.hpp b/include/rcpputils/find_library.hpp new file mode 100644 index 0000000..3f155d6 --- /dev/null +++ b/include/rcpputils/find_library.hpp @@ -0,0 +1,37 @@ +// Copyright 2019 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef RCPPUTILS__FIND_LIBRARY_H_ +#define RCPPUTILS__FIND_LIBRARY_H_ + +#include + +namespace rcpputils { + +/// Finds a library located in the OS's specified environment variable for +/// library paths and returns the absolute filesystem path, including the +/// appropriate prefix and extension. The environment variable and file format +/// per platform: +/// +/// * Linux: `${LD_LIBRARY_PATH}`, `lib{}.so` +/// * Apple: `${DYLD_LIBRARY_PATH}`, `lib{}.dyld` +/// * Windows: `%PATH%`, `{}.dll` +/** + * \param[in] library_name Name of the library to find. + */ +std::string find_library_path(const std::string & library_name); + +} // namespace rcpputils + +#endif // RCPPUTILS__FIND_LIBRARY_H_ diff --git a/src/find_library.cpp b/src/find_library.cpp new file mode 100644 index 0000000..f492375 --- /dev/null +++ b/src/find_library.cpp @@ -0,0 +1,106 @@ +// Copyright 2019 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "rcpputils/find_library.hpp" + +#include + +#include +#include +#include + +namespace rcpputils { + +namespace { + +std::string get_env_var(const char * env_var) +{ + char * value = nullptr; +#ifndef _WIN32 + value = getenv(env_var); +#else + size_t value_size; + _dupenv_s(&value, &value_size, env_var); +#endif + std::string value_str = ""; + if (value) { + value_str = value; +#ifdef _WIN32 + free(value); +#endif + } + // printf("get_env_var(%s) = %s\n", env_var, value_str.c_str()); + return value_str; +} + +std::list split(const std::string & value, const char delimiter) +{ + std::list list; + std::istringstream ss(value); + std::string s; + while (std::getline(ss, s, delimiter)) { + list.push_back(s); + } + // printf("split(%s) = %zu\n", value.c_str(), list.size()); + return list; +} + +bool is_file_exist(const char * filename) +{ + std::ifstream h(filename); + // printf("is_file_exist(%s) = %s\n", filename, h.good() ? "true" : "false"); + return h.good(); +} + +} // namespace + +std::string find_library_path(const std::string & library_name) +{ + // TODO(eric.cousineau): Does Poco provide this functionality? + const char * env_var; + char separator; + const char * filename_prefix; + const char * filename_extension; +#ifdef _WIN32 + env_var = "PATH"; + separator = ';'; + filename_prefix = ""; + filename_extension = ".dll"; +#elif __APPLE__ + env_var = "DYLD_LIBRARY_PATH"; + separator = ':'; + filename_prefix = "lib"; + filename_extension = ".dylib"; +#else + env_var = "LD_LIBRARY_PATH"; + separator = ':'; + filename_prefix = "lib"; + filename_extension = ".so"; +#endif + std::string search_path = get_env_var(env_var); + std::list search_paths = split(search_path, separator); + + std::string filename = filename_prefix; + filename += library_name + filename_extension; + + for (auto it : search_paths) { + std::string path = it + "/" + filename; + if (is_file_exist(path.c_str())) { + return path; + } + } + return ""; +} + +} // namespace rcpputils diff --git a/test/test_find_library.cpp b/test/test_find_library.cpp new file mode 100644 index 0000000..c1e5ba1 --- /dev/null +++ b/test/test_find_library.cpp @@ -0,0 +1,53 @@ +// Copyright 2019 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gtest/gtest.h" + +#include "rcpputils/find_library.hpp" + +namespace rcpputils { +namespace { + +TEST(test_find_library, find_library) { + // Get ground-truth values from CTest properties. + const char * toy_lib_expected = getenv("_TOY_TEST_LIBRARY"); + EXPECT_NE(toy_lib_expected, nullptr); + const char * toy_lib_dir = getenv("_TOY_TEST_LIBRARY_DIR"); + EXPECT_NE(toy_lib_dir, nullptr); + + // Set our relevant path variable. + const char * env_var{}; +#ifdef _WIN32 + env_var = "PATH"; +#elif __APPLE__ + env_var = "DYLD_LIBRARY_PATH"; +#else + env_var = "LD_LIBRARY_PATH"; +#endif + const int override = 1; + setenv(env_var, toy_lib_dir, override); + + // Positive test. + const std::string toy_lib_actual = find_library_path("toy_test_library"); + EXPECT_EQ(toy_lib_actual, toy_lib_expected); + + // (Hopefully) Negative test. + const std::string bad_path = find_library_path( + "this_is_a_junk_libray_name_please_dont_define_this_if_you_do_then_" + "you_are_really_naughty"); + EXPECT_EQ(bad_path, ""); +} + +} // namespace +} // namespace rcpputils diff --git a/test/toy_test_library.cpp b/test/toy_test_library.cpp new file mode 100644 index 0000000..5d6c0c3 --- /dev/null +++ b/test/toy_test_library.cpp @@ -0,0 +1,22 @@ +// Copyright 2019 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// @file +/// Trivial library to ensure we have some linking present. + +namespace toy_test_library { + +int add_one(int x) { return x + 1; } + +} // namespace toy_test_library