From ff7d2acb5569fca81e9b0a12ff087ef2cb245d6b Mon Sep 17 00:00:00 2001 From: John Wason Date: Wed, 13 Dec 2023 13:28:28 -0500 Subject: [PATCH] Add Mac OSX support (#969) --- .github/workflows/mac.yml | 91 +++++++++++++++++++ .../x64-osx-dynamic-release.cmake | 9 ++ .../bullet/src/create_convex_hull.cpp | 1 + .../include/tesseract_common/class_loader.hpp | 2 +- .../tesseract_common/resource_locator.h | 2 + tesseract_common/test/plugin_loader_unit.cpp | 4 +- .../tesseract_common_serialization_unit.cpp | 4 +- tesseract_srdf/src/group_states.cpp | 26 +++--- .../src/group_tool_center_points.cpp | 28 +++--- tesseract_srdf/src/groups.cpp | 34 ++++--- 10 files changed, 156 insertions(+), 45 deletions(-) create mode 100644 .github/workflows/mac.yml create mode 100644 .github/workflows/vcpkg_triplets/x64-osx-dynamic-release.cmake diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml new file mode 100644 index 00000000000..151128c67c1 --- /dev/null +++ b/.github/workflows/mac.yml @@ -0,0 +1,91 @@ +name: Mac OSX + +on: + push: + branches: + - master + - 'dev**' + pull_request: + paths: + - 'tesseract**' + - '.github/workflows/mac.yml' + - '**.repos' + schedule: + - cron: '0 5 * * *' + release: + types: + - released + +env: + VCPKG_PKGS: >- + boost-dll boost-program-options + boost-serialization boost-filesystem + tinyxml2 console-bridge assimp + urdfdom octomap orocos-kdl pcl + gtest benchmark flann jsoncpp + yaml-cpp eigen3 + openblas + fcl ompl taskflow + bullet3[multithreading,double-precision,rtti] + ccd[double-precision] gperftools + +jobs: + build-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + with: + path: ws/src/tesseract + - uses: actions/setup-python@v4 + id: setup-python + with: + python-version: '3.12' + - name: brew + run: | + brew install libomp cmake automake autoconf libtool gcc ninja + - name: vcpkg build + uses: johnwason/vcpkg-action@v5 + with: + pkgs: >- + ${{ env.VCPKG_PKGS }} + triplet: x64-osx-dynamic-release + extra-args: --clean-after-build --overlay-triplets=${{ github.workspace }}/ws/src/tesseract/.github/workflows/vcpkg_triplets + token: ${{ github.token }} + cache-key: osx-x64-vcpkg + revision: master + - name: pip3 + run: | + python3 -m pip install numpy setuptools wheel pytest delvewheel colcon-common-extensions vcstool + - name: vcs import + working-directory: ws/src + run: vcs import --input tesseract/.github/workflows/windows_dependencies.repos + - name: colcon build + working-directory: ws + run: | + export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:$GITHUB_WORKSPACE/vcpkg/installed/x64-osx-dynamic-release/lib + export CMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/vcpkg/installed/x64-osx-dynamic-release + + colcon build --merge-install \ + --packages-ignore tesseract_examples trajopt_ifopt trajopt_sqp ifopt vhacd tesseract_python \ + --event-handlers console_cohesion+ \ + --cmake-force-configure \ + --cmake-args -GNinja -DCMAKE_BUILD_TYPE=Release \ + -DINSTALL_OMPL=OFF -DINSTALL_OMPL_TAG=master -DBUILD_IPOPT=OFF -DBUILD_SNOPT=OFF \ + -DBUILD_SHARED_LIBS=ON -DTESSERACT_ENABLE_EXAMPLES=OFF \ + -DVCPKG_APPLOCAL_DEPS=OFF -DTESSERACT_ENABLE_TESTING=ON \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 \ + -DOpenMP_CXX_INCLUDE_DIR=/usr/local/opt/libomp/include \ + -DOpenMP_C_INCLUDE_DIR=/usr/local/opt/libomp/include \ + -DOpenMP_CXX_LIB_NAMES=libomp -DOpenMP_CXX_FLAGS="-Xpreprocessor -fopenmp" \ + -DOpenMP_C_LIB_NAMES=libomp -DOpenMP_C_FLAGS="-Xpreprocessor -fopenmp" \ + -DOpenMP_libomp_LIBRARY=/usr/local/opt/libomp/lib/libomp.dylib + - name: colcon test + working-directory: ws + run: | + export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:$GITHUB_WORKSPACE/vcpkg/installed/x64-osx-dynamic-release/lib + export CMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/vcpkg/installed/x64-osx-dynamic-release + + colcon test --merge-install \ + --packages-ignore tesseract_examples trajopt_ifopt trajopt_sqp ifopt vhacd tesseract_python \ + --event-handlers console_cohesion+ + diff --git a/.github/workflows/vcpkg_triplets/x64-osx-dynamic-release.cmake b/.github/workflows/vcpkg_triplets/x64-osx-dynamic-release.cmake new file mode 100644 index 00000000000..1e296987cc8 --- /dev/null +++ b/.github/workflows/vcpkg_triplets/x64-osx-dynamic-release.cmake @@ -0,0 +1,9 @@ +set(VCPKG_TARGET_ARCHITECTURE x64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE dynamic) + +set(VCPKG_CMAKE_SYSTEM_NAME Darwin) +set(VCPKG_OSX_ARCHITECTURES x86_64) + +set(VCPKG_BUILD_TYPE release) +set(VCPKG_OSX_DEPLOYMENT_TARGET 12.0) diff --git a/tesseract_collision/bullet/src/create_convex_hull.cpp b/tesseract_collision/bullet/src/create_convex_hull.cpp index 9d3fb2d97e8..454aa0309ce 100644 --- a/tesseract_collision/bullet/src/create_convex_hull.cpp +++ b/tesseract_collision/bullet/src/create_convex_hull.cpp @@ -28,6 +28,7 @@ TESSERACT_COMMON_IGNORE_WARNINGS_PUSH #include #include #include +#include TESSERACT_COMMON_IGNORE_WARNINGS_POP #include diff --git a/tesseract_common/include/tesseract_common/class_loader.hpp b/tesseract_common/include/tesseract_common/class_loader.hpp index abffd1316f6..f3a46fc0c48 100644 --- a/tesseract_common/include/tesseract_common/class_loader.hpp +++ b/tesseract_common/include/tesseract_common/class_loader.hpp @@ -186,7 +186,7 @@ std::vector ClassLoader::getAvailableSections(const std::string& li if (include_hidden) return false; - return (section.substr(0, 1) == "."); + return (section.substr(0, 1) == ".") || (section.substr(0, 1) == "_"); }; sections.erase(std::remove_if(sections.begin(), sections.end(), search_fn), sections.end()); diff --git a/tesseract_common/include/tesseract_common/resource_locator.h b/tesseract_common/include/tesseract_common/resource_locator.h index 3189e267dee..22a9d30f416 100644 --- a/tesseract_common/include/tesseract_common/resource_locator.h +++ b/tesseract_common/include/tesseract_common/resource_locator.h @@ -32,6 +32,8 @@ TESSERACT_COMMON_IGNORE_WARNINGS_PUSH #include #include #include +#include +#include TESSERACT_COMMON_IGNORE_WARNINGS_POP namespace tesseract_common diff --git a/tesseract_common/test/plugin_loader_unit.cpp b/tesseract_common/test/plugin_loader_unit.cpp index c9835ce9e24..fb880648bf6 100644 --- a/tesseract_common/test/plugin_loader_unit.cpp +++ b/tesseract_common/test/plugin_loader_unit.cpp @@ -80,7 +80,7 @@ TEST(TesseractClassLoaderUnit, LoadTestPlugin) // NOLINT } // For some reason on Ubuntu 18.04 it does not search the current directory when only the library name is provided -#if BOOST_VERSION > 106800 +#if BOOST_VERSION > 106800 && !__APPLE__ { EXPECT_TRUE(ClassLoader::isClassAvailable(symbol_name, lib_name)); auto plugin = ClassLoader::createSharedInstance(symbol_name, lib_name); @@ -147,7 +147,7 @@ TEST(TesseractPluginLoaderUnit, LoadTestPlugin) // NOLINT } // For some reason on Ubuntu 18.04 it does not search the current directory when only the library name is provided -#if BOOST_VERSION > 106800 +#if BOOST_VERSION > 106800 && !__APPLE__ { PluginLoader plugin_loader; plugin_loader.search_libraries.insert("tesseract_common_test_plugin_multiply"); diff --git a/tesseract_common/test/tesseract_common_serialization_unit.cpp b/tesseract_common/test/tesseract_common_serialization_unit.cpp index 8763cb8a491..4a5ad2cce61 100644 --- a/tesseract_common/test/tesseract_common_serialization_unit.cpp +++ b/tesseract_common/test/tesseract_common_serialization_unit.cpp @@ -427,7 +427,7 @@ TEST(TesseractCommonSerializeUnit, VectorXi) // NOLINT TEST(TesseractCommonSerializeUnit, Vector3d) // NOLINT { { // Serialize empty object - Eigen::Vector3d ev; + Eigen::Vector3d ev = Eigen::Vector3d::Zero(); { std::ofstream os(tesseract_common::getTempPath() + "eigen_vector_3d_boost.xml"); boost::archive::xml_oarchive oa(os); @@ -497,7 +497,7 @@ TEST(TesseractCommonSerializeUnit, Vector3d) // NOLINT TEST(TesseractCommonSerializeUnit, Vector4d) // NOLINT { { // Serialize empty object - Eigen::Vector4d ev; + Eigen::Vector4d ev = Eigen::Vector4d::Zero(); { std::ofstream os(tesseract_common::getTempPath() + "eigen_vector_4d_boost.xml"); boost::archive::xml_oarchive oa(os); diff --git a/tesseract_srdf/src/group_states.cpp b/tesseract_srdf/src/group_states.cpp index 718f807f495..b7699d12d8e 100644 --- a/tesseract_srdf/src/group_states.cpp +++ b/tesseract_srdf/src/group_states.cpp @@ -57,8 +57,8 @@ GroupJointStates parseGroupStates(const tesseract_scene_graph::SceneGraph& scene bool found = std::find(group_names.begin(), group_names.end(), group_name) != group_names.end(); if (!found) - std::throw_with_nested(std::runtime_error( - tesseract_common::strFormat("GroupStates: State '%s' group '%s' does not exist!", state_name, group_name))); + std::throw_with_nested(std::runtime_error(tesseract_common::strFormat( + "GroupStates: State '%s' group '%s' does not exist!", state_name.c_str(), group_name.c_str()))); GroupsJointState joint_state; @@ -73,16 +73,16 @@ GroupJointStates parseGroupStates(const tesseract_scene_graph::SceneGraph& scene std::throw_with_nested(std::runtime_error(tesseract_common::strFormat("GroupStates: Missing or failed to parse " "attribute 'name' from joint element for " "state '%s' in group '%s'!", - state_name, - group_name))); + state_name.c_str(), + group_name.c_str()))); if (!scene_graph.getJoint(joint_name)) std::throw_with_nested(std::runtime_error(tesseract_common::strFormat("GroupStates: State '%s' for group '%s' " "joint name '%s' is not know to the " "URDF!", - state_name, - group_name, - joint_name))); + state_name.c_str(), + group_name.c_str(), + joint_name.c_str()))); status = tesseract_common::QueryDoubleAttributeRequired(joint_xml, "value", joint_value); if (status != tinyxml2::XML_SUCCESS) @@ -90,16 +90,18 @@ GroupJointStates parseGroupStates(const tesseract_scene_graph::SceneGraph& scene "joint element with joint name '%s' is " "missing or failed to parse attribute " "'value'!", - state_name, - group_name, - joint_name))); + state_name.c_str(), + group_name.c_str(), + joint_name.c_str()))); joint_state[joint_name] = joint_value; } if (joint_state.empty()) - std::throw_with_nested(std::runtime_error(tesseract_common::strFormat( - "GroupStates: State '%s' for group '%s' is missing joint elements!", state_name, group_name))); + std::throw_with_nested(std::runtime_error(tesseract_common::strFormat("GroupStates: State '%s' for group '%s' is " + "missing joint elements!", + state_name.c_str(), + group_name.c_str()))); auto gs = group_states.find(group_name); if (gs == group_states.end()) diff --git a/tesseract_srdf/src/group_tool_center_points.cpp b/tesseract_srdf/src/group_tool_center_points.cpp index 7e73e4a93e0..f7960e5343e 100644 --- a/tesseract_srdf/src/group_tool_center_points.cpp +++ b/tesseract_srdf/src/group_tool_center_points.cpp @@ -68,8 +68,8 @@ GroupTCPs parseGroupTCPs(const tesseract_scene_graph::SceneGraph& /*scene_graph* if (xml_element->Attribute("name") == nullptr || xml_element->Attribute("xyz") == nullptr || (xml_element->Attribute("rpy") == nullptr && xml_element->Attribute("wxyz") == nullptr)) - std::throw_with_nested( - std::runtime_error(strFormat("GroupTCPs: Invalid tcp definition for group '%s'!", group_name_string))); + std::throw_with_nested(std::runtime_error( + strFormat("GroupTCPs: Invalid tcp definition for group '%s'!", group_name_string.c_str()))); std::string tcp_name_string; tinyxml2::XMLError status = tesseract_common::QueryStringAttributeRequired(xml_element, "name", tcp_name_string); @@ -85,8 +85,8 @@ GroupTCPs parseGroupTCPs(const tesseract_scene_graph::SceneGraph& /*scene_graph* if (status != tinyxml2::XML_NO_ATTRIBUTE && status != tinyxml2::XML_SUCCESS) std::throw_with_nested(std::runtime_error(strFormat("GroupTCPS: TCP '%s' for group '%s' failed to parse " "attribute 'xyz'!", - tcp_name_string, - group_name_string))); + tcp_name_string.c_str(), + group_name_string.c_str()))); // LCOV_EXCL_STOP if (status != tinyxml2::XML_NO_ATTRIBUTE) @@ -96,8 +96,8 @@ GroupTCPs parseGroupTCPs(const tesseract_scene_graph::SceneGraph& /*scene_graph* if (tokens.size() != 3 || !tesseract_common::isNumeric(tokens)) std::throw_with_nested(std::runtime_error(strFormat("GroupTCPS: TCP '%s' for group '%s' failed to parse " "attribute 'xyz'!", - tcp_name_string, - group_name_string))); + tcp_name_string.c_str(), + group_name_string.c_str()))); double x{ 0 }, y{ 0 }, z{ 0 }; // No need to check return values because the tokens are verified above @@ -115,8 +115,8 @@ GroupTCPs parseGroupTCPs(const tesseract_scene_graph::SceneGraph& /*scene_graph* if (status != tinyxml2::XML_NO_ATTRIBUTE && status != tinyxml2::XML_SUCCESS) std::throw_with_nested(std::runtime_error(strFormat("GroupTCPS: TCP '%s' for group '%s' failed to parse " "attribute 'rpy'!", - tcp_name_string, - group_name_string))); + tcp_name_string.c_str(), + group_name_string.c_str()))); // LCOV_EXCL_STOP if (status != tinyxml2::XML_NO_ATTRIBUTE) @@ -126,8 +126,8 @@ GroupTCPs parseGroupTCPs(const tesseract_scene_graph::SceneGraph& /*scene_graph* if (tokens.size() != 3 || !tesseract_common::isNumeric(tokens)) std::throw_with_nested(std::runtime_error(strFormat("GroupTCPS: TCP '%s' for group '%s' failed to parse " "attribute 'rpy'!", - tcp_name_string, - group_name_string))); + tcp_name_string.c_str(), + group_name_string.c_str()))); double r{ 0 }, p{ 0 }, y{ 0 }; // No need to check return values because the tokens are verified above @@ -151,8 +151,8 @@ GroupTCPs parseGroupTCPs(const tesseract_scene_graph::SceneGraph& /*scene_graph* if (status != tinyxml2::XML_NO_ATTRIBUTE && status != tinyxml2::XML_SUCCESS) std::throw_with_nested(std::runtime_error(strFormat("GroupTCPS: TCP '%s' for group '%s' failed to parse " "attribute 'wxyz'!", - tcp_name_string, - group_name_string))); + tcp_name_string.c_str(), + group_name_string.c_str()))); // LCOV_EXCL_STOP if (status != tinyxml2::XML_NO_ATTRIBUTE) @@ -162,8 +162,8 @@ GroupTCPs parseGroupTCPs(const tesseract_scene_graph::SceneGraph& /*scene_graph* if (tokens.size() != 4 || !tesseract_common::isNumeric(tokens)) std::throw_with_nested(std::runtime_error(strFormat("GroupTCPS: TCP '%s' for group '%s' failed to parse " "attribute 'wxyz'!", - tcp_name_string, - group_name_string))); + tcp_name_string.c_str(), + group_name_string.c_str()))); double qw{ 0 }, qx{ 0 }, qy{ 0 }, qz{ 0 }; // No need to check return values because the tokens are verified above diff --git a/tesseract_srdf/src/groups.cpp b/tesseract_srdf/src/groups.cpp index 53a7f5ca39f..7ad96fac073 100644 --- a/tesseract_srdf/src/groups.cpp +++ b/tesseract_srdf/src/groups.cpp @@ -27,6 +27,7 @@ #include TESSERACT_COMMON_IGNORE_WARNINGS_PUSH #include +#include TESSERACT_COMMON_IGNORE_WARNINGS_POP #include @@ -67,11 +68,11 @@ parseGroups(const tesseract_scene_graph::SceneGraph& scene_graph, tinyxml2::XMLError status = tesseract_common::QueryStringAttributeRequired(link_xml, "name", link_name); if (status != tinyxml2::XML_SUCCESS) std::throw_with_nested(std::runtime_error( - strFormat("Group: '%s' link element is missing or failed to parse attribute 'name'!", group_name))); + strFormat("Group: '%s' link element is missing or failed to parse attribute 'name'!", group_name.c_str()))); if (!scene_graph.getLink(link_name)) - std::throw_with_nested(std::runtime_error( - strFormat("Group: '%s' link '%s' is not known to the Scene Graph!", group_name, link_name))); + std::throw_with_nested(std::runtime_error(strFormat( + "Group: '%s' link '%s' is not known to the Scene Graph!", group_name.c_str(), link_name.c_str()))); links.push_back(link_name); } @@ -83,12 +84,13 @@ parseGroups(const tesseract_scene_graph::SceneGraph& scene_graph, std::string joint_name; tinyxml2::XMLError status = tesseract_common::QueryStringAttributeRequired(joint_xml, "name", joint_name); if (status != tinyxml2::XML_SUCCESS) - std::throw_with_nested(std::runtime_error( - strFormat("Group: '%s' joint element is missing or failed to parse attribute 'name'!", group_name))); + std::throw_with_nested(std::runtime_error(strFormat("Group: '%s' joint element is missing or failed to parse " + "attribute 'name'!", + group_name.c_str()))); if (!scene_graph.getJoint(joint_name)) - std::throw_with_nested(std::runtime_error( - strFormat("Group: '%s' joint '%s' is not known to the Scene Graph!", group_name, joint_name))); + std::throw_with_nested(std::runtime_error(strFormat( + "Group: '%s' joint '%s' is not known to the Scene Graph!", group_name.c_str(), joint_name.c_str()))); joints.push_back(joint_name); } @@ -104,22 +106,26 @@ parseGroups(const tesseract_scene_graph::SceneGraph& scene_graph, std::throw_with_nested(std::runtime_error(strFormat("Group: '%s' chain element is missing or failed to parse " "attribute " "'base_link'!", - group_name))); + group_name.c_str()))); status = tesseract_common::QueryStringAttributeRequired(chain_xml, "tip_link", tip_link_name); if (status != tinyxml2::XML_SUCCESS) std::throw_with_nested(std::runtime_error(strFormat("Group: '%s' chain element is missing or failed to parse " "attribute " "'tip_link'!", - group_name))); + group_name.c_str()))); if (!scene_graph.getLink(base_link_name)) - std::throw_with_nested(std::runtime_error(strFormat( - "Group: '%s' chain element base link '%s' is not known to the Scene Graph!", group_name, base_link_name))); + std::throw_with_nested(std::runtime_error(strFormat("Group: '%s' chain element base link '%s' is not known to " + "the Scene Graph!", + group_name.c_str(), + base_link_name.c_str()))); if (!scene_graph.getLink(tip_link_name)) - std::throw_with_nested(std::runtime_error(strFormat( - "Group: '%s' chain element tip link '%s' is not known to the Scene Graph!", group_name, tip_link_name))); + std::throw_with_nested(std::runtime_error(strFormat("Group: '%s' chain element tip link '%s' is not known to " + "the Scene Graph!", + group_name.c_str(), + tip_link_name.c_str()))); chains.emplace_back(base_link_name, tip_link_name); } @@ -142,7 +148,7 @@ parseGroups(const tesseract_scene_graph::SceneGraph& scene_graph, else { std::throw_with_nested( - std::runtime_error(strFormat("Group: '%s' is empty or multiple types were provided!", group_name))); + std::runtime_error(strFormat("Group: '%s' is empty or multiple types were provided!", group_name.c_str()))); } }