From 9ba23008372e9dfb3264417f62ed785e3258a6e6 Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Mon, 25 Mar 2019 16:43:20 -0400 Subject: [PATCH 1/6] find_library: Centralize functionality here Signed-off-by: Eric Cousineau --- CMakeLists.txt | 24 ++++++ README.md | 15 +++- 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, 337 insertions(+), 3 deletions(-) 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 cf1343b..83ad4e2 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) @@ -36,6 +47,13 @@ if(BUILD_TESTING) ament_add_gtest(test_split test/test_split.cpp) ament_add_gtest(test_filesystem_helper test/test_filesystem_helper.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() @@ -43,3 +61,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 18cd41d..28cb5a9 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,23 @@ `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.hpp` header provides macros for Clang's [Thread Safety Analysis](https://clang.llvm.org/docs/ThreadSafetyAnalysis.html) feature. + +The `rcpputils/thread_safety_annotations.hpp` 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. 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 18a080b..c50d0f8 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 0b2155008a012e44739af6691dacf15978ecedbc Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Fri, 3 May 2019 17:37:41 -0400 Subject: [PATCH 2/6] 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 a983b443c5aa62eadf354312002275e39113a8cc Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Sun, 5 May 2019 17:22:17 -0400 Subject: [PATCH 3/6] 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 83ad4e2..a42a0ac 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 aff2a877345ed3c2a11956e6d36eb612923deb32 Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Mon, 6 May 2019 22:17:57 -0400 Subject: [PATCH 4/6] 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 ce50d3747526f581063be46c8e92346ada72ffe1 Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Tue, 7 May 2019 10:37:51 -0400 Subject: [PATCH 5/6] 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 ea92951d871b5dfa208cf95fa9e15f75f4b74b6f Mon Sep 17 00:00:00 2001 From: Dirk Thomas Date: Tue, 7 May 2019 08:05:34 -0700 Subject: [PATCH 6/6] 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;