From 7da6d0a60a8862258fc5edda956a0b9993d8e42b Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Mon, 25 Mar 2019 16:43:20 -0400 Subject: [PATCH 1/8] find_library: Centralize functionality here Signed-off-by: Eric Cousineau --- CMakeLists.txt | 24 ++++++ README.md | 10 +++ include/rcpputils/find_library.hpp | 44 ++++++++++ include/rcpputils/visibility_control.hpp | 67 +++++++++++++++ package.xml | 3 + src/find_library.cpp | 100 +++++++++++++++++++++++ test/test_find_library.cpp | 61 ++++++++++++++ test/test_library.cpp | 26 ++++++ 8 files changed, 335 insertions(+) create mode 100644 include/rcpputils/find_library.hpp create mode 100644 include/rcpputils/visibility_control.hpp create mode 100644 src/find_library.cpp create mode 100644 test/test_find_library.cpp create mode 100644 test/test_library.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 92b3505..d44ce5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,8 @@ cmake_minimum_required(VERSION 3.5) project(rcpputils) find_package(ament_cmake REQUIRED) +find_package(ament_cmake_ros REQUIRED) +find_package(rcutils REQUIRED) # Default to C11 if(NOT CMAKE_C_STANDARD) @@ -21,6 +23,15 @@ include_directories(include) ament_export_include_directories(include) +add_library(${PROJECT_NAME} + src/find_library.cpp) +target_include_directories(${PROJECT_NAME} + PUBLIC + include +) +ament_target_dependencies(${PROJECT_NAME} rcutils) +ament_export_libraries(${PROJECT_NAME}) + if(BUILD_TESTING) find_package(ament_cmake_gtest REQUIRED) find_package(ament_lint_auto REQUIRED) @@ -42,6 +53,13 @@ if(BUILD_TESTING) ament_add_gtest(test_pointer_traits test/test_pointer_traits.cpp) ament_add_gtest(test_endian test/test_endian.cpp) + + add_library(test_library SHARED test/test_library.cpp) + ament_add_gtest(test_find_library test/test_find_library.cpp) + target_link_libraries(test_find_library ${PROJECT_NAME} test_library) + set_tests_properties(test_find_library PROPERTIES + ENVIRONMENT + "_TEST_LIBRARY_DIR=$;_TEST_LIBRARY=$") endif() ament_package() @@ -49,3 +67,9 @@ ament_package() install( DIRECTORY include/ DESTINATION include) +install( + TARGETS ${PROJECT_NAME} + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) diff --git a/README.md b/README.md index a62fde1..394e3a4 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ This package currently contains: * Clang thread safety annotation macros +* Library discovery * String helpers * File system helpers * Type traits helpers @@ -16,3 +17,12 @@ The macros allow you to annotate your code, but expand to nothing when using a n To use thread safety annotation in your package (in a Clang+libcxx build), enable the `-Wthread-safety` compiler flag. 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`: + +* `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..292b984 --- /dev/null +++ b/include/rcpputils/find_library.hpp @@ -0,0 +1,44 @@ +// 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_HPP_ +#define RCPPUTILS__FIND_LIBRARY_HPP_ + +#include + +#include "rcpputils/visibility_control.hpp" + +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. + * \return Absolute path of library. + * \throws std::runtime_error if an error is encountered when accessing + * environment variables. + */ +RCPPUTILS_PUBLIC +std::string find_library_path(const std::string & library_name); + +} // namespace rcpputils + +#endif // RCPPUTILS__FIND_LIBRARY_HPP_ diff --git a/include/rcpputils/visibility_control.hpp b/include/rcpputils/visibility_control.hpp new file mode 100644 index 0000000..b03720e --- /dev/null +++ b/include/rcpputils/visibility_control.hpp @@ -0,0 +1,67 @@ +// Copyright (c) 2019, Open Source Robotics Foundation, Inc. +// All rights reserved. +// +// Software License Agreement (BSD License 2.0) +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef RCPPUTILS__VISIBILITY_CONTROL_HPP_ +#define RCPPUTILS__VISIBILITY_CONTROL_HPP_ + +// This logic was borrowed (then namespaced) from the examples on the gcc wiki: +// https://gcc.gnu.org/wiki/Visibility + +#if defined _WIN32 || defined __CYGWIN__ + #ifdef __GNUC__ + #define RCPPUTILS_EXPORT __attribute__ ((dllexport)) + #define RCPPUTILS_IMPORT __attribute__ ((dllimport)) + #else + #define RCPPUTILS_EXPORT __declspec(dllexport) + #define RCPPUTILS_IMPORT __declspec(dllimport) + #endif + #ifdef RCPPUTILS_BUILDING_LIBRARY + #define RCPPUTILS_PUBLIC RCPPUTILS_EXPORT + #else + #define RCPPUTILS_PUBLIC RCPPUTILS_IMPORT + #endif + #define RCPPUTILS_PUBLIC_TYPE RCPPUTILS_PUBLIC + #define RCPPUTILS_LOCAL +#else + #define RCPPUTILS_EXPORT __attribute__ ((visibility("default"))) + #define RCPPUTILS_IMPORT + #if __GNUC__ >= 4 + #define RCPPUTILS_PUBLIC __attribute__ ((visibility("default"))) + #define RCPPUTILS_LOCAL __attribute__ ((visibility("hidden"))) + #else + #define RCPPUTILS_PUBLIC + #define RCPPUTILS_LOCAL + #endif + #define RCPPUTILS_PUBLIC_TYPE +#endif + +#endif // RCPPUTILS__VISIBILITY_CONTROL_HPP_ diff --git a/package.xml b/package.xml index 2223681..5ecf1cb 100644 --- a/package.xml +++ b/package.xml @@ -8,6 +8,9 @@ Apache License 2.0 ament_cmake + ament_cmake_ros + + rcutils ament_lint_common ament_lint_auto diff --git a/src/find_library.cpp b/src/find_library.cpp new file mode 100644 index 0000000..0ecb21b --- /dev/null +++ b/src/find_library.cpp @@ -0,0 +1,100 @@ +// 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 +#include +#include + +#include "rcutils/get_env.h" + +namespace rcpputils +{ + +namespace +{ + +#ifdef _WIN32 +static constexpr char kPathVar[] = "PATH"; +static constexpr char kPathSeparator = ';'; +static constexpr char kSolibPrefix[] = ""; +static constexpr char kSolibExtension[] = ".dll"; +#elif __APPLE__ +static constexpr char kPathVar[] = "DYLD_LIBRARY_PATH"; +static constexpr char kPathSeparator = ':'; +static constexpr char kSolibPrefix[] = "lib"; +static constexpr char kSolibExtension[] = ".dylib"; +#else +static constexpr char kPathVar[] = "LD_LIBRARY_PATH"; +static constexpr char kPathSeparator = ':'; +static constexpr char kSolibPrefix[] = "lib"; +static constexpr char kSolibExtension[] = ".so"; +#endif + +std::string get_env_var(const char * kPathVar) +{ + const char * value{}; + const char * err = rcutils_get_env(kPathVar, &value); + if (err) { + throw std::runtime_error(err); + } + return value ? value : ""; +} + +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); + } + return list; +} + +bool is_file_exist(const char * filename) +{ + std::ifstream h(filename); + return h.good(); +} + +} // namespace + +std::string find_library_path(const std::string & library_name) +{ + // TODO(eric.cousineau): Does Poco provide this functionality + // (ros2/rcpputils#7)? + // TODO(eric.cousineau): Have this return a library pointer instead + // (ros2/rcpputils#8). + std::string search_path = get_env_var(kPathVar); + std::list search_paths = split(search_path, kPathSeparator); + + std::string filename = kSolibPrefix; + filename += library_name + kSolibExtension; + + for (const auto & search_path : search_paths) { + std::string path = search_path + "/" + 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..e018ff7 --- /dev/null +++ b/test/test_find_library.cpp @@ -0,0 +1,61 @@ +// 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 + +#include "gtest/gtest.h" + +#include "rcutils/get_env.h" +#include "rcpputils/find_library.hpp" + +namespace rcpputils +{ +namespace +{ + +TEST(test_find_library, find_library) +{ + // Get ground-truth values from CTest properties. + const char * test_lib_expected{}; + EXPECT_EQ(rcutils_get_env("_TEST_LIBRARY", &test_lib_expected), nullptr); + EXPECT_NE(test_lib_expected, nullptr); + const char * test_lib_dir{}; + EXPECT_EQ(rcutils_get_env("_TEST_LIBRARY_DIR", &test_lib_dir), nullptr); + EXPECT_NE(test_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, test_lib_dir, override); + + // Positive test. + const std::string test_lib_actual = find_library_path("test_library"); + EXPECT_EQ(test_lib_actual, test_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/test_library.cpp b/test/test_library.cpp new file mode 100644 index 0000000..ca59acf --- /dev/null +++ b/test/test_library.cpp @@ -0,0 +1,26 @@ +// 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 test_library +{ + +int add_one(int x) +{ + return x + 1; +} + +} // namespace test_library From cf2c620bd51c59f7f6533ba3177bd930565bba19 Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Fri, 3 May 2019 17:37:41 -0400 Subject: [PATCH 2/8] fix Signed-off-by: Eric Cousineau --- src/find_library.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/find_library.cpp b/src/find_library.cpp index 0ecb21b..ae8f3ef 100644 --- a/src/find_library.cpp +++ b/src/find_library.cpp @@ -22,6 +22,7 @@ #include #include +#include "rcutils/filesystem.h" #include "rcutils/get_env.h" namespace rcpputils @@ -68,20 +69,10 @@ std::list split(const std::string & value, const char delimiter) return list; } -bool is_file_exist(const char * filename) -{ - std::ifstream h(filename); - return h.good(); -} - } // namespace std::string find_library_path(const std::string & library_name) { - // TODO(eric.cousineau): Does Poco provide this functionality - // (ros2/rcpputils#7)? - // TODO(eric.cousineau): Have this return a library pointer instead - // (ros2/rcpputils#8). std::string search_path = get_env_var(kPathVar); std::list search_paths = split(search_path, kPathSeparator); @@ -90,7 +81,7 @@ std::string find_library_path(const std::string & library_name) for (const auto & search_path : search_paths) { std::string path = search_path + "/" + filename; - if (is_file_exist(path.c_str())) { + if (rcutils_is_file(path.c_str())) { return path; } } From 94cb115998380b82a98c72f0be4f7032c037e69c Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Sun, 5 May 2019 17:22:17 -0400 Subject: [PATCH 3/8] fix for win32 Signed-off-by: Eric Cousineau --- CMakeLists.txt | 4 ++++ test/test_find_library.cpp | 9 ++++++++- test/test_library.cpp | 3 ++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d44ce5a..008d4d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,10 @@ target_include_directories(${PROJECT_NAME} PUBLIC include ) +if(WIN32) + target_compile_definitions(${PROJECT_NAME} + PRIVATE "RCPPUTILS_BUILDING_LIBRARY") +endif() ament_target_dependencies(${PROJECT_NAME} rcutils) ament_export_libraries(${PROJECT_NAME}) diff --git a/test/test_find_library.cpp b/test/test_find_library.cpp index e018ff7..9a007ed 100644 --- a/test/test_find_library.cpp +++ b/test/test_find_library.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include + #include #include "gtest/gtest.h" @@ -43,8 +45,13 @@ TEST(test_find_library, find_library) #else env_var = "LD_LIBRARY_PATH"; #endif + +#ifdef _WIN32 + EXPECT_EQ(_putenv_s(env_var, test_lib_dir), 0); +#else const int override = 1; - setenv(env_var, test_lib_dir, override); + EXPECT_EQ(setenv(env_var, test_lib_dir, override), 0); +#endif // Positive test. const std::string test_lib_actual = find_library_path("test_library"); diff --git a/test/test_library.cpp b/test/test_library.cpp index ca59acf..b984cfd 100644 --- a/test/test_library.cpp +++ b/test/test_library.cpp @@ -13,7 +13,8 @@ // limitations under the License. /// @file -/// Trivial library to ensure we have some linking present. +/// Trivial library to ensure we have some linking present for +/// `test_find_library`. namespace test_library { From 1b0dc53300ade65fc3fbf675e6f0784cc107b954 Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Mon, 6 May 2019 22:17:57 -0400 Subject: [PATCH 4/8] Ensure test_library exports symbols Signed-off-by: Eric Cousineau --- test/test_library.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_library.cpp b/test/test_library.cpp index b984cfd..3a5c48b 100644 --- a/test/test_library.cpp +++ b/test/test_library.cpp @@ -19,6 +19,7 @@ namespace test_library { +RCPPUTILS_PUBLIC int add_one(int x) { return x + 1; From 5f3a73631dd2e2db86d80d6d96ad644196290936 Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Tue, 7 May 2019 10:37:51 -0400 Subject: [PATCH 5/8] Fix missing header Signed-off-by: Eric Cousineau --- test/test_library.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_library.cpp b/test/test_library.cpp index 3a5c48b..56df718 100644 --- a/test/test_library.cpp +++ b/test/test_library.cpp @@ -16,6 +16,8 @@ /// Trivial library to ensure we have some linking present for /// `test_find_library`. +#include "rcpputils/visibility_control.hpp" + namespace test_library { From afcf91e04b5b771c5ba528a90669a1ed061bdeca Mon Sep 17 00:00:00 2001 From: Dirk Thomas Date: Tue, 7 May 2019 08:05:34 -0700 Subject: [PATCH 6/8] fix visibility macro Signed-off-by: Dirk Thomas --- test/test_library.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_library.cpp b/test/test_library.cpp index 56df718..66f4596 100644 --- a/test/test_library.cpp +++ b/test/test_library.cpp @@ -21,7 +21,7 @@ namespace test_library { -RCPPUTILS_PUBLIC +RCPPUTILS_EXPORT int add_one(int x) { return x + 1; From ed7b9fa1a1f757387250aa25b1156383695fb226 Mon Sep 17 00:00:00 2001 From: Dirk Thomas Date: Thu, 5 Sep 2019 14:53:13 -0700 Subject: [PATCH 7/8] fix rcutils_get_env usage Signed-off-by: Dirk Thomas --- test/test_find_library.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/test/test_find_library.cpp b/test/test_find_library.cpp index 9a007ed..d460904 100644 --- a/test/test_find_library.cpp +++ b/test/test_find_library.cpp @@ -29,9 +29,14 @@ namespace TEST(test_find_library, find_library) { // Get ground-truth values from CTest properties. - const char * test_lib_expected{}; - EXPECT_EQ(rcutils_get_env("_TEST_LIBRARY", &test_lib_expected), nullptr); - EXPECT_NE(test_lib_expected, nullptr); + std::string expected_library_path; + { + const char * _expected_library_path{}; + EXPECT_EQ(rcutils_get_env("_TEST_LIBRARY", &_expected_library_path), nullptr); + EXPECT_NE(_expected_library_path, nullptr); + expected_library_path = _expected_library_path; + } + const char * test_lib_dir{}; EXPECT_EQ(rcutils_get_env("_TEST_LIBRARY_DIR", &test_lib_dir), nullptr); EXPECT_NE(test_lib_dir, nullptr); @@ -55,7 +60,7 @@ TEST(test_find_library, find_library) // Positive test. const std::string test_lib_actual = find_library_path("test_library"); - EXPECT_EQ(test_lib_actual, test_lib_expected); + EXPECT_EQ(test_lib_actual, expected_library_path); // (Hopefully) Negative test. const std::string bad_path = find_library_path( From 1a3a57e200395b39bbf608e94767b8fc597c37fd Mon Sep 17 00:00:00 2001 From: Dirk Thomas Date: Thu, 5 Sep 2019 16:28:08 -0700 Subject: [PATCH 8/8] use different name for local variable Signed-off-by: Dirk Thomas --- src/find_library.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/find_library.cpp b/src/find_library.cpp index ae8f3ef..b809d47 100644 --- a/src/find_library.cpp +++ b/src/find_library.cpp @@ -48,10 +48,10 @@ static constexpr char kSolibPrefix[] = "lib"; static constexpr char kSolibExtension[] = ".so"; #endif -std::string get_env_var(const char * kPathVar) +std::string get_env_var(const char * env_var) { const char * value{}; - const char * err = rcutils_get_env(kPathVar, &value); + const char * err = rcutils_get_env(env_var, &value); if (err) { throw std::runtime_error(err); }