diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 840f6b9..af5d3c9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,11 +10,14 @@ on: jobs: industrial-ci: name: Run Industrial CI - + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - ros_distro: ["noetic", "kilted", "rolling"] + env: + - {UPSTREAM_WORKSPACE: liboculus.repos, ROS_DISTRO: noetic} + - {UPSTREAM_WORKSPACE: liboculus.repos, ROS_DISTRO: kilted} + - {UPSTREAM_WORKSPACE: liboculus.repos, ROS_DISTRO: rolling} steps: - name: Checkout repository @@ -24,6 +27,4 @@ jobs: - name: Run ROS Industrial CI uses: ros-industrial/industrial_ci@master - environment: - CI_ROS_DISTRO: ${{ matrix.ros_distro }} - UPSTREAM_WORKSPACE: liboculus.repos + env: ${{matrix.env}} diff --git a/.github/workflows/ci_cmake.yaml b/.github/workflows/ci_cmake.yaml new file mode 100644 index 0000000..f41f9c8 --- /dev/null +++ b/.github/workflows/ci_cmake.yaml @@ -0,0 +1,28 @@ +name: ROS Industrial CI + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +jobs: + cmake: + name: Build with CMake + runs-on: ubuntu-24.04 + + steps: + - name: Install dependencesi with awalsh128/cache-apt-pkgs-action + uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: libboost-all-dev + version: 1.0 + + - name: Checkout repository + uses: actions/checkout@v5 + with: + submodules: recursive + + - name: Build Project + uses: threeal/cmake-action@v2.1.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f6f360..a5809b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,13 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.8) project(liboculus) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +add_compile_options(-std=c++17) + # == Code common to all builds ======================================= list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) -# ROS build, determine which version... -find_package(ros_environment REQUIRED) -set(ROS_VERSION $ENV{ROS_VERSION}) - set(oculus_SRCS lib/DataRx.cpp lib/SonarConfiguration.cpp @@ -19,106 +19,110 @@ set(oculus_SRCS lib/IoServiceThread.cpp ) -if(${ROS_VERSION} EQUAL 2) - # == ament/ROS2 section ================================= - - find_package(ament_cmake REQUIRED) - find_package(Boost REQUIRED COMPONENTS system) - find_package(g3log_ros REQUIRED) - - set(CMAKE_CXX_STANDARD 17) - set(CMAKE_CXX_STANDARD_REQUIRED ON) - add_compile_options(-std=c++17) - - add_library(oculus SHARED ${oculus_SRCS}) - - target_include_directories( - oculus - PUBLIC - "$" - "$" - "$" - "$" - ) - - target_link_libraries(oculus PUBLIC Boost::system g3log_ros::g3log) - - install( - TARGETS oculus - EXPORT export_${PROJECT_NAME} - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib - RUNTIME DESTINATION bin - ) - - install( - DIRECTORY include/${PROJECT_NAME} thirdparty - DESTINATION include - FILES_MATCHING - PATTERN "*.hpp" - PATTERN "*.h" - PATTERN ".git" EXCLUDE - ) - - ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET) - ament_export_libraries(oculus) - - ament_package() -elseif(${ROS_VERSION} EQUAL 1) - # Catkin/ROS1 section ===== - find_package(catkin REQUIRED COMPONENTS g3log_ros) - find_package(Boost REQUIRED COMPONENTS system) - - catkin_package( - CATKIN_DEPENDS g3log_ros - INCLUDE_DIRS include thirdparty - LIBRARIES liboculus - ) - - add_compile_options(-std=c++14) - - add_library(liboculus ${oculus_SRCS}) - - include_directories(liboculus include thirdparty ${catkin_INCLUDE_DIRS}) - - target_link_libraries(liboculus ${catkin_LIBRARIES}) - - install( - TARGETS liboculus - ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} - LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} - RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION} - ) - - ## Install headers - install( - DIRECTORY include/${PROJECT_NAME}/ thirdparty/ - DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} - FILES_MATCHING - PATTERN "*.hpp" - PATTERN "*.h" - PATTERN ".git" EXCLUDE - ) - - if(CATKIN_ENABLE_TESTING) - add_definitions( - -DTEST_DATA_PATH="${CMAKE_CURRENT_SOURCE_DIR}/test/data" +# ROS build, determine which version... +# QUIET will squelch the warning if the package isn't found +find_package(ros_environment QUIET) + +if(${ros_environment_FOUND}) + set(ROS_VERSION $ENV{ROS_VERSION}) + + if(${ROS_VERSION} EQUAL 2) + # == ament/ROS2 section ================================= + + find_package(ament_cmake REQUIRED) + find_package(Boost REQUIRED COMPONENTS system) + find_package(g3log_ros REQUIRED) + + add_library(oculus SHARED ${oculus_SRCS}) + target_link_libraries(oculus PUBLIC Boost::system g3log_ros::g3log) + + target_include_directories( + oculus + PUBLIC + "$" + "$" + "$" + "$" + ) + + install( + TARGETS oculus + EXPORT export_${PROJECT_NAME} + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin ) - include_directories(test/data/) - file(GLOB oculus_test_SRCS test/unit/*cpp) + install( + DIRECTORY include/${PROJECT_NAME} thirdparty + DESTINATION include + FILES_MATCHING + PATTERN "*.hpp" + PATTERN "*.h" + PATTERN ".git" EXCLUDE + ) + + ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET) + ament_export_libraries(oculus) + + ament_package() + elseif(${ROS_VERSION} EQUAL 1) + # Catkin/ROS1 section ===== + find_package(catkin REQUIRED COMPONENTS g3log_ros) + find_package(Boost REQUIRED COMPONENTS system) + + catkin_package( + CATKIN_DEPENDS g3log_ros + INCLUDE_DIRS include thirdparty + LIBRARIES liboculus + ) + + add_library(liboculus ${oculus_SRCS}) - catkin_add_gtest(oculus_test ${oculus_test_SRCS}) + include_directories(liboculus include thirdparty ${catkin_INCLUDE_DIRS}) + + target_link_libraries(liboculus ${catkin_LIBRARIES}) + + install( + TARGETS liboculus + ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION} + ) + + ## Install headers + install( + DIRECTORY include/${PROJECT_NAME}/ thirdparty/ + DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} + FILES_MATCHING + PATTERN "*.hpp" + PATTERN "*.h" + PATTERN ".git" EXCLUDE + ) - target_link_libraries( - oculus_test - ${catkin_LIBRARIES} - liboculus - Boost::system + if(CATKIN_ENABLE_TESTING) + add_definitions( + -DTEST_DATA_PATH="${CMAKE_CURRENT_SOURCE_DIR}/test/data" + ) + include_directories(test/data/) + + file(GLOB oculus_test_SRCS test/unit/*cpp) + + catkin_add_gtest(oculus_test ${oculus_test_SRCS}) + + target_link_libraries( + oculus_test + ${catkin_LIBRARIES} + liboculus + Boost::system + ) + endif() + else() + message( + "Unsure what sort of build to do. Not in a ROS1 or ROS2 environment" ) endif() else() - message( - "Unsure what sort of build to do. Not in a ROS1 or ROS2 environment" - ) + message(NOTICE "!! Performing non-ROS build !!") + include(cmake/BuildNonROS.cmake) endif() diff --git a/README.md b/README.md index 1807faf..48a287f 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,9 @@ This library contains code for: - Raw streams of binary packets. - **Note:** This repo includes scaffold code for reading `.oculus` files saved from the Blueprint GUI, but that format is proprietary and undocumented. **We cannot parse `.oculus` files!!** -This package is designed to build in both ROS1 (catkin) and ROS2 (colcon) environments, however it contains no ROS-specific code and could be integrated into other non-ROS environments with some work. +This package is designed to build in both ROS1 (catkin) and ROS2 (colcon) environments, however it contains no ROS-specific code and could be integrated into other non-ROS environments with some work. + +It also contains the options to build a "bare" library with `cmake`. The library contains no special provisions for *saving* sonar data, but it's straightforward to write packets as a raw binary stream @@ -48,6 +50,18 @@ binary `oc_client` requires [CLI11](https://github.com/CLIUtils/CLI11). Internally, the ethernet interface uses [Boost::asio](https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio.html). +## Build with cmake + +This package can be built with the standard cmake process: + +``` +mkdir build && cd build +cmake .. +make +``` + +Note the `CMakelists.txt` attempts to auto-detect ROS. Cmake build should be done in a session where ROS has not been loaded. + --- ## oc_client binary diff --git a/cmake/BuildNonROS.cmake b/cmake/BuildNonROS.cmake new file mode 100644 index 0000000..93ca322 --- /dev/null +++ b/cmake/BuildNonROS.cmake @@ -0,0 +1,121 @@ +# Specify the minimum version for CMake +cmake_minimum_required(VERSION 3.8) + +# Project's name +project(liboculus) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -march=native -Wl,--no-as-needed") + +# Set the output folder where your program will be created +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) + +# ########################################### +# The following folders will be included # +# ########################################### +include_directories( + "${PROJECT_SOURCE_DIR}/include/" + "${PROJECT_SOURCE_DIR}/thirdparty/" +) + +# Threading +find_package(Threads) + +# Boost +find_package(Boost 1.57 REQUIRED COMPONENTS system) +include_directories(${Boost_INCLUDE_DIR}) +message("Boost_INCLUDE_DIR: " ${Boost_INCLUDE_DIR}) + +# Install g3log as an external package +include(cmake/IncludeProject.cmake) + +if(NOT GIT_TAG) + set(GIT_TAG "2.6") +endif() + +include(ExternalProject) +ExternalProject_Add( + g3log + GIT_REPOSITORY https://github.com/KjellKod/g3log + GIT_TAG ${GIT_TAG} + INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/install/g3log" + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= +) + +ExternalProject_Get_Property(g3log install_dir) +include_directories(${install_dir}/include/) + +# ##################### +# Add Execuatables # +# ##################### +link_directories(${Boost_LIBRARY_DIRS}) + +# Create Library +add_library(oculus SHARED ${oculus_SRCS}) +set_target_properties(oculus PROPERTIES LIBRARY_OUTPUT_NAME oculus) + +# ============================================= +# to allow find_package() +# ============================================= +# +# The following is borrowed heavily from: +# https://github.com/RossHartley/invariant-ekf +# I am responsible for all mistakes +# +# the following case be used in an external project requiring oculus: +# ... +# find_package(oculus) +# include_directories(${oculus_INCLUDE_DIRS}) +# ... + +# NOTE: the following will support find_package for 1) local build (make) and 2) for installed files (make install) + +# 1- local build + +# Register the local build in case one doesn't use "make install" +export(PACKAGE oculus) + +# Create variable for the local build tree +# set_target_properties(oculus PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) +get_property( + oculus_include_dirs + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + PROPERTY INCLUDE_DIRECTORIES +) +get_property( + oculus_library_dirs + TARGET oculus + PROPERTY LIBRARY_OUTPUT_DIRECTORY +) +get_property(oculus_libraries TARGET oculus PROPERTY LIBRARY_OUTPUT_NAME) + +message("oculus_include_dirs: " ${oculus_include_dirs}) +message("oculus_library_dirs: " ${oculus_library_dirs}) +message("oculus_libraries: " ${oculus_libraries}) + +# Configure config file for local build tree +configure_file( + cmake/oculusConfig.cmake.in + "${PROJECT_BINARY_DIR}/oculusConfig.cmake" + @ONLY +) + +message("PROJECT_BINARY_DIR: " ${PROJECT_BINARY_DIR}) + +# # 2- installation build # + +# Change the include location for the case of an install location +set(oculus_include_dirs ${CMAKE_INSTALL_PREFIX}/include ${EIGEN_INCLUDE_DIR}) + +# We put the generated file for installation in a different repository (i.e., ./CMakeFiles/) +configure_file( + cmake/oculusConfig.cmake.in + "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/oculusConfig.cmake" + @ONLY +) + +install( + FILES "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/oculusConfig.cmake" + DESTINATION share/oculus/cmake + COMPONENT dev +) diff --git a/cmake/FindOculusSDK.cmake b/cmake/FindOculusSDK.cmake deleted file mode 100644 index c05372e..0000000 --- a/cmake/FindOculusSDK.cmake +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright Olivier Parcollet 2010, Andrey Antipov 2013. -# -# This module looks for Blueprint Oculus SDK -# -# OCULUS_DIR can be provided as a hint. -# -# It sets up : OCULUS_SDK_ROOT -# -# - -find_package(PkgConfig) - -set(TRIAL_PATHS $ENV{OCULUS_DIR} ${OCULUS_DIR}) - -find_path( - OCULUS_SDK_ROOT - Oculus/Oculus.h - HINTS ${TRIAL_PATHS} - DOC "Location OCULUS SDK" -) - -find_package_handle_standard_args(OCULUS_SDK DEFAULT_MSG OCULUS_SDK_ROOT) diff --git a/cmake/IncludeProject.cmake b/cmake/IncludeProject.cmake new file mode 100644 index 0000000..66d13d2 --- /dev/null +++ b/cmake/IncludeProject.cmake @@ -0,0 +1,78 @@ +# This function is used to force a build on a dependant project at cmake +# configuration phase. + +# FOLLOWING ARGUMENTS are the CMAKE_ARGS of ExternalProject_Add +function(build_external_project target repository tag) + set(trigger_build_dir + ${CATKIN_DEVEL_PREFIX}/lib/${PROJECT_NAME}/force_${target} + ) + + # mktemp dir in build tree + file(MAKE_DIRECTORY ${trigger_build_dir} ${trigger_build_dir}/build) + + # generate false dependency project + set(CMAKE_LIST_CONTENT + " + cmake_minimum_required(VERSION 2.8) + + include(ExternalProject) + ExternalProject_add(${target} + PREFIX ${CATKIN_DEVEL_PREFIX}/lib/${PROJECT_NAME} + GIT_REPOSITORY ${repository} + GIT_TAG ${tag} + CMAKE_ARGS ${ARGN} + INSTALL_COMMAND \"\" + TIMEOUT 20) + add_custom_target(trigger_${target}) + add_dependencies(trigger_${target} ${target})" + ) + + file(WRITE ${trigger_build_dir}/CMakeLists.txt "${CMAKE_LIST_CONTENT}") + + execute_process( + COMMAND ${CMAKE_COMMAND} -DCMAKE_VERBOSE_MAKEFILE=ON .. + WORKING_DIRECTORY ${trigger_build_dir}/build + ) + execute_process( + COMMAND ${CMAKE_COMMAND} --build . + WORKING_DIRECTORY ${trigger_build_dir}/build + ) +endfunction() + +function(build_external_project_nobuild target repository tag) + set(trigger_build_dir + ${CATKIN_DEVEL_PREFIX}/lib/${PROJECT_NAME}/force_${target} + ) + + # mktemp dir in build tree + file(MAKE_DIRECTORY ${trigger_build_dir} ${trigger_build_dir}/build) + + # generate false dependency project + set(CMAKE_LIST_CONTENT + " + cmake_minimum_required(VERSION 2.8) + + include(ExternalProject) + ExternalProject_add(${target} + PREFIX ${CATKIN_DEVEL_PREFIX}/lib/${PROJECT_NAME} + GIT_REPOSITORY ${repository} + GIT_TAG ${tag} + CMAKE_ARGS ${ARGN} + BUILD_COMMAND \"\" + INSTALL_COMMAND \"\" + TIMEOUT 20) + add_custom_target(trigger_${target}) + add_dependencies(trigger_${target} ${target})" + ) + + file(WRITE ${trigger_build_dir}/CMakeLists.txt "${CMAKE_LIST_CONTENT}") + + execute_process( + COMMAND ${CMAKE_COMMAND} -DCMAKE_VERBOSE_MAKEFILE=ON .. + WORKING_DIRECTORY ${trigger_build_dir}/build + ) + execute_process( + COMMAND ${CMAKE_COMMAND} --build . + WORKING_DIRECTORY ${trigger_build_dir}/build + ) +endfunction() diff --git a/cmake/oculusConfig.cmake.in b/cmake/oculusConfig.cmake.in new file mode 100644 index 0000000..487afe9 --- /dev/null +++ b/cmake/oculusConfig.cmake.in @@ -0,0 +1,10 @@ +# - Config file for the oculus package +# It defines the following variables +# oculus_INCLUDE_DIRS - include directories for oculus +# oculus_LIBRARY_DIRS - directories for oculus library + +# Compute paths +get_filename_component(oculus_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +set(oculus_INCLUDE_DIRS "@oculus_include_dirs@") +set(oculus_LIBRARY_DIRS "@oculus_library_dirs@") +set(oculus_LIBRARIES "@oculus_libraries@")