diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4d97794 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,145 @@ +name: C++ CI Workflow + +# template derived from https://github.com/robotology/human-dynamics-estimation/blob/master/.github/workflows/ci.yml + +on: + push: + pull_request: + schedule: + # run a cron job for a nightly build + # * is a special character in YAML so you have to quote this string + # Execute a "nightly" build at 2 AM UTC + - cron: '0 2 * * *' + +env: + #TODO change with first release after this commit + BipedalLocomotionFramework_TAG: b045e79229ed25a8562c7dfaebb1b6b9cb722f5c + action-restore-cache: 'true' +jobs: + build: + name: '[${{matrix.os}}@${{matrix.build_type}}]' + runs-on: ${{matrix.os}} + strategy: + matrix: + build_type: [Release] + os: [ubuntu-latest, windows-latest, macos-latest] + fail-fast: false + + steps: + - uses: actions/checkout@v3 + + - name: Get current day + shell: bash -l {0} + run: | + echo "DATE=$(date +'%Y-%m-%d')" >> $GITHUB_ENV + + # Use conda for main dependencies + - uses: conda-incubator/setup-miniconda@v2 + with: + miniforge-variant: Mambaforge + miniforge-version: latest + + # Print the environment variables to simplify development and debugging + - name: Environment Variables + # Use bash in order to have same basic commands in all OSs + shell: bash + run: env + + # Remove apt repos on Ubuntu that are known to break from time to time + # See https://github.com/actions/virtual-environments/issues/323 + - name: Remove broken apt repos [Ubuntu] + if: matrix.os == 'ubuntu-latest' + run: | + for apt_file in `grep -lr microsoft /etc/apt/sources.list.d/`; do sudo rm $apt_file; done + + # ============ + # DEPENDENCIES + # ============ + - name: Restore cached conda based dependencies + if: ${{ env.action-restore-cache == 'true' }} + uses: actions/cache/restore@v3 + with: + path: ${{ env.CONDA }}/envs/test + key: ${{ matrix.os }}-conda-${{ hashFiles('.github/workflows/conda-deps.yml') }}-${{ env.DATE }} + id: cache-restore-conda-deps + + - name: Dependencies (using conda) + shell: bash -l {0} + if: steps.cache-restore-conda-deps.outputs.cache-hit != 'true' + run: | + mamba env update -f .github/workflows/conda-deps.yml + + - name: Cache conda based dependencies + if: ${{ steps.cache-restore-conda-deps.outputs.cache-hit != 'true' }} + uses: actions/cache/save@v3 + with: + path: ${{ env.CONDA }}/envs/test + key: ${{ matrix.os }}-conda-${{ hashFiles('.github/workflows/conda-deps.yml') }}-${{ env.DATE }} + id: cache-save-conda-deps + + - name: Restore cached source-based dependencies + if: ${{ env.action-restore-cache == 'true' && steps.cache-restore-conda-deps.outputs.cache-hit == 'true' }} + uses: actions/cache/restore@v3 + with: + path: ${{ github.workspace }}/install/deps + key: ${{ matrix.os }}-${{ matrix.build_type }}-source-${{env.BipedalLocomotionFramework_TAG}}-${{ env.DATE }} + id: cache-restore-source-deps + + - name: Source-based dependencies + if: ${{ steps.cache-restore-conda-deps.outputs.cache-hit != 'true' || steps.cache-restore-source-deps.outputs.cache-hit != 'true' }} + shell: bash -l {0} + run: | + # bipedal-locomotion-framework + cd ${GITHUB_WORKSPACE} + git clone https://github.com/ami-iit/bipedal-locomotion-framework blf + cd blf + git checkout ${BipedalLocomotionFramework_TAG} + mkdir -p build + cd build + cmake -GNinja .. \ + -DCMAKE_PREFIX_PATH=${GITHUB_WORKSPACE}/install/deps \ + -DCMAKE_INSTALL_PREFIX=${GITHUB_WORKSPACE}/install/deps + + cmake --build . --config ${{matrix.build_type}} --target install + + - name: Cache source-based dependencies + if: ${{ steps.cache-restore-source-deps.outputs.cache-hit != 'true' }} + uses: actions/cache/save@v3 + with: + path: ${{ github.workspace }}/install/deps + key: ${{ matrix.os }}-${{ matrix.build_type }}-source-${{env.BipedalLocomotionFramework_TAG}}-${{ env.DATE }} + id: cache-save-source-deps + + + + # =================== + # CMAKE-BASED PROJECT + # =================== + # We will just configure and build the project now. Further modifications and tests can be added + # Configure step + - name: Configure + shell: bash -l {0} + run: | + mkdir -p build + cd build + cmake -G"Ninja" .. \ + -DCMAKE_PREFIX_PATH=${GITHUB_WORKSPACE}/install/deps \ + -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ + -DFRAMEWORK_COMPILE_YarpImplementation=ON \ + -DBUILD_TESTING:BOOL=ON \ + -DCMAKE_INSTALL_PREFIX=${GITHUB_WORKSPACE}/install + + # Build step + - name: Build + shell: bash -l {0} + run: | + cd build + cmake --build . --config ${{matrix.build_type}} --verbose + + # Test step + - name: Test + shell: bash -l {0} + run: | + cd build + export PATH=$PATH:${GITHUB_WORKSPACE}/build/install/bin:${GITHUB_WORKSPACE}/install/deps/bin + ctest --output-on-failure -C ${{ matrix.build_type }} . diff --git a/.github/workflows/conda-deps.yml b/.github/workflows/conda-deps.yml new file mode 100644 index 0000000..d78e3b7 --- /dev/null +++ b/.github/workflows/conda-deps.yml @@ -0,0 +1,13 @@ +#we use the default conda-incubator/setup-miniconda env name +name: test +dependencies: + - cmake + - clangxx>=14.0.0 + - make + - ninja + - pkg-config + - yarp + - idyntree + - spdlog + - catch2>=3.3.0 + diff --git a/CHANGELOG.md b/CHANGELOG.md index 83aa45c..ae45345 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,3 +8,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - The `CHANGELOG.md` file +- The `Logging` feature (https://github.com/ami-iit/biomechanical-analysis-framework/pull/10) diff --git a/CMakeLists.txt b/CMakeLists.txt index 77d14dc..29bff87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,9 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) option(BUILD_SHARED_LIBS "Build libraries as shared as opposed to static" ON) +option(BUILD_TESTING "Create tests using CMake" OFF) +include(CTest) + # Check BiomechanicalAnalysisFramework dependencies, find necessary libraries. include(BiomechanicalAnalysisFrameworkDependencies) @@ -63,6 +66,9 @@ add_install_rpath_support(BIN_DIRS "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BIND DEPENDS ENABLE_RPATH USE_LINK_PATH) +# Setup testing +include(AddBiomechanicalAnalysisUnitTest) + #Function to automatize the process of creating a new library include(AddBiomechanicalAnalysisLibrary) diff --git a/cmake/AddBiomechanicalAnalysisUnitTest.cmake b/cmake/AddBiomechanicalAnalysisUnitTest.cmake new file mode 100644 index 0000000..863a6a2 --- /dev/null +++ b/cmake/AddBiomechanicalAnalysisUnitTest.cmake @@ -0,0 +1,34 @@ + +function(add_baf_test) + + if(FRAMEWORK_COMPILE_tests) + + set(options ) + set(oneValueArgs NAME) + set(multiValueArgs SOURCES LINKS) + + set(prefix "baf") + + cmake_parse_arguments(${prefix} + "${options}" + "${oneValueArgs}" + "${multiValueArgs}" + ${ARGN}) + + set(name ${${prefix}_NAME}) + set(unit_test_files ${${prefix}_SOURCES}) + + set(targetname ${name}UnitTests) + add_executable(${targetname} + "${unit_test_files}") + + target_link_libraries(${targetname} PRIVATE Catch2::Catch2WithMain ${${prefix}_LINKS}) + target_compile_definitions(${targetname} PRIVATE CATCH_CONFIG_FAST_COMPILE CATCH_CONFIG_DISABLE_MATCHERS) + target_compile_features(${targetname} PUBLIC cxx_std_17) + target_compile_definitions(${targetname} PRIVATE -D_USE_MATH_DEFINES) + + add_test(NAME ${targetname} COMMAND ${targetname}) + + endif() + +endfunction() diff --git a/cmake/BiomechanicalAnalysisFrameworkDependencies.cmake b/cmake/BiomechanicalAnalysisFrameworkDependencies.cmake index eeea5ff..c094386 100644 --- a/cmake/BiomechanicalAnalysisFrameworkDependencies.cmake +++ b/cmake/BiomechanicalAnalysisFrameworkDependencies.cmake @@ -2,11 +2,21 @@ # This software may be modified and distributed under the terms of the # GNU Lesser General Public License v2.1 or any later version. -################################################################################ +include(BiomechanicalAnalysisFrameworkFindDependencies) + +########################## Mandatory dependencies ############################### # Find all packages -find_package(iDynTree 1.1.0 REQUIRED) +find_package(BipedalLocomotionFramework 0.11.200 REQUIRED) #TODO version + +########################## Optional dependencies ############################## + +find_package(Catch2 3 QUIET) -find_package(Eigen3 3.2.92 REQUIRED) +find_package(YARP QUIET) +option(FRAMEWORK_COMPILE_YarpImplementation "Compile utilities for YARP" ${YARP_FOUND}) -find_package(OsqpEigen 0.4.0 REQUIRED) +########################## Components ############################## +framework_dependent_option(FRAMEWORK_COMPILE_tests + "Compile tests?" ON + "Catch2_FOUND;BUILD_TESTING" OFF) diff --git a/cmake/BiomechanicalAnalysisFrameworkFindDependencies.cmake b/cmake/BiomechanicalAnalysisFrameworkFindDependencies.cmake new file mode 100644 index 0000000..527e72b --- /dev/null +++ b/cmake/BiomechanicalAnalysisFrameworkFindDependencies.cmake @@ -0,0 +1,53 @@ + +include(CMakeDependentOption) + +macro(FRAMEWORK_DEPENDENT_OPTION _option _doc _default _deps _force) + + if(DEFINED ${_option}) + get_property(_option_strings_set CACHE ${_option} PROPERTY STRINGS SET) + if(_option_strings_set) + # If the user thinks he is smarter than the machine, he deserves an error + get_property(_option_strings CACHE ${_option} PROPERTY STRINGS) + list(GET _option_strings 0 _option_strings_first) + string(REGEX REPLACE ".+\"(.+)\".+" "\\1" _option_strings_first "${_option_strings_first}") + list(LENGTH _option_strings _option_strings_length) + math(EXPR _option_strings_last_index "${_option_strings_length} - 1") + list(GET _option_strings ${_option_strings_last_index} _option_strings_last) + if("${${_option}}" STREQUAL "${_option_strings_last}") + message(SEND_ERROR "That was a trick, you cannot outsmart me! I will never let you win! ${_option} stays OFF until I say so! \"${_option_strings_first}\" is needed to enable ${_option}. Now stop bothering me, and install your dependencies, if you really want to enable this option.") + endif() + unset(${_option} CACHE) + endif() + endif() + + cmake_dependent_option(${_option} "${_doc}" ${_default} "${_deps}" ${_force}) + + unset(_missing_deps) + foreach(_dep ${_deps}) + string(REGEX REPLACE " +" ";" _depx "${_dep}") + if(NOT (${_depx})) + list(APPEND _missing_deps "${_dep}") + endif() + endforeach() + + if(DEFINED _missing_deps) + set(${_option}_disable_reason " (dependencies unsatisfied: \"${_missing_deps}\")") + # Set a value that can be visualized on ccmake and on cmake-gui, but + # still evaluates to false + set(${_option} "OFF - Dependencies unsatisfied: '${_missing_deps}' - ${_option}-NOTFOUND" CACHE STRING "${_option_doc}" FORCE) + string(REPLACE ";" "\;" _missing_deps "${_missing_deps}") + set_property(CACHE ${_option} + PROPERTY STRINGS "OFF - Dependencies unsatisfied: '${_missing_deps}' - ${_option}-NOTFOUND" + "OFF - You can try as much as you want, but '${_missing_deps}' is needed to enable ${_option} - ${_option}-NOTFOUND" + "OFF - Are you crazy or what? '${_missing_deps}' is needed to enable ${_option} - ${_option}-NOTFOUND" + "OFF - Didn't I already tell you that '${_missing_deps}' is needed to enable ${_option}? - ${_option}-NOTFOUND" + "OFF - Stop it! - ${_option}-NOTFOUND" + "OFF - This is insane! Leave me alone! - ${_option}-NOTFOUND" + "ON - All right, you win. The option is enabled. Are you happy now? You just broke the build.") + # Set non-cache variable that will override the value in current scope + # For parent scopes, the "-NOTFOUND ensures that the variable still + # evaluates to false + set(${_option} ${_force}) + endif() + +endmacro() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e69de29..1d4246d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_subdirectory(Logging) diff --git a/src/Logging/CMakeLists.txt b/src/Logging/CMakeLists.txt new file mode 100644 index 0000000..3022c0a --- /dev/null +++ b/src/Logging/CMakeLists.txt @@ -0,0 +1,7 @@ + +add_biomechanical_analysis_library( + NAME Logging + PUBLIC_HEADERS include/BiomechanicalAnalysis/Logging/Logger.h + SOURCES src/Logger.cpp + PUBLIC_LINK_LIBRARIES BipedalLocomotion::TextLogging + SUBDIRECTORIES YarpImplementation tests) diff --git a/src/Logging/YarpImplementation/CMakeLists.txt b/src/Logging/YarpImplementation/CMakeLists.txt new file mode 100644 index 0000000..87c3196 --- /dev/null +++ b/src/Logging/YarpImplementation/CMakeLists.txt @@ -0,0 +1,13 @@ + +if(FRAMEWORK_COMPILE_YarpImplementation) + + add_biomechanical_analysis_library( + NAME TextLoggingYarpImplementation + SOURCES src/YarpLogger.cpp + PUBLIC_HEADERS include/BiomechanicalAnalysis/Logging/YarpLogger.h + PUBLIC_LINK_LIBRARIES BiomechanicalAnalysis::Logging + BipedalLocomotion::TextLoggingYarpImplementation + YARP::YARP_os + INSTALLATION_FOLDER Logging) + +endif() diff --git a/src/Logging/YarpImplementation/include/BiomechanicalAnalysis/Logging/YarpLogger.h b/src/Logging/YarpImplementation/include/BiomechanicalAnalysis/Logging/YarpLogger.h new file mode 100644 index 0000000..fd33963 --- /dev/null +++ b/src/Logging/YarpImplementation/include/BiomechanicalAnalysis/Logging/YarpLogger.h @@ -0,0 +1,18 @@ +#ifndef BIOMECHANICAL_ANALYSIS_YARP_LOGGER_H +#define BIOMECHANICAL_ANALYSIS_YARP_LOGGER_H + +namespace BiomechanicalAnalysis +{ +namespace Logging +{ + +/** + * Use YARP logger instead of the default one. + * The method works only if the log has not yet been used + */ +void useYarpLogger(); + +} +} + +#endif // BIOMECHANICAL_ANALYSIS_YARP_LOGGER_H diff --git a/src/Logging/YarpImplementation/src/YarpLogger.cpp b/src/Logging/YarpImplementation/src/YarpLogger.cpp new file mode 100644 index 0000000..652411c --- /dev/null +++ b/src/Logging/YarpImplementation/src/YarpLogger.cpp @@ -0,0 +1,13 @@ +#include +#include + +#include + +using namespace BipedalLocomotion::TextLogging; + +void BiomechanicalAnalysis::Logging::useYarpLogger() +{ + const static std::shared_ptr _bafYarpLoggerFactoryBasePtr = std::make_shared("baf"); + const static std::shared_ptr _bafYarpLogFactory = std::dynamic_pointer_cast(_bafYarpLoggerFactoryBasePtr); + BiomechanicalAnalysis::Logging::setLoggerFactory(_bafYarpLogFactory); +} diff --git a/src/Logging/include/BiomechanicalAnalysis/Logging/Logger.h b/src/Logging/include/BiomechanicalAnalysis/Logging/Logger.h new file mode 100644 index 0000000..a3edf1f --- /dev/null +++ b/src/Logging/include/BiomechanicalAnalysis/Logging/Logger.h @@ -0,0 +1,47 @@ +/** + * @file Logger.h + * @authors Riccardo Grieco + */ + +#ifndef BIOMECHANICAL_ANALYSIS_LOGGING_LOGGER_H +#define BIOMECHANICAL_ANALYSIS_LOGGING_LOGGER_H + +#include + +namespace BiomechanicalAnalysis +{ +namespace Logging +{ + +using Logger = BipedalLocomotion::TextLogging::Logger; +using Verbosity = BipedalLocomotion::TextLogging::Verbosity; +using LoggerFactory = BipedalLocomotion::TextLogging::LoggerFactory; + +/** + * Set the log provider. + * Calling this method after log() will have no effect. + * + * @param loggerFactory + */ +void setLoggerFactory(const std::shared_ptr& loggerFactory); + +/** + * Set the verbosity of the logger. + * + * @param verbosity the verbosity of the logger + */ +void setVerbosity(const Verbosity verbosity); + +} // namespace Logging +} // namespace BiomechanicalAnalysis + +namespace BiomechanicalAnalysis +{ +/** + * Get an the instance of the log + */ +Logging::Logger* const log(); + +} // namespace BiomechanicalAnalysis + +#endif // BIOMECHANICAL_ANALYSIS_LOGGING_LOGGER_H diff --git a/src/Logging/src/Logger.cpp b/src/Logging/src/Logger.cpp new file mode 100644 index 0000000..a0d82a1 --- /dev/null +++ b/src/Logging/src/Logger.cpp @@ -0,0 +1,29 @@ +#include + +#include +#include + +static std::shared_ptr _defaultFactory = + std::make_shared("baf"); + +static std::shared_ptr _bafFactory = _defaultFactory; + +using namespace BiomechanicalAnalysis; + +void Logging::setLoggerFactory(const std::shared_ptr& loggerFactory) +{ + _bafFactory = loggerFactory; +} + +Logging::Logger* const BiomechanicalAnalysis::log() +{ + static auto _createLogger = [&]{BipedalLocomotion::TextLogging::LoggerBuilder::setFactory(_bafFactory); return true;}; + static bool _loggerCreated = _createLogger(); + return BipedalLocomotion::log(); +} + +void Logging::setVerbosity(const Verbosity verbosity) +{ + BipedalLocomotion::TextLogging::setVerbosity(verbosity); +} + diff --git a/src/Logging/tests/CMakeLists.txt b/src/Logging/tests/CMakeLists.txt new file mode 100644 index 0000000..6b7330b --- /dev/null +++ b/src/Logging/tests/CMakeLists.txt @@ -0,0 +1,5 @@ + +add_baf_test( + NAME DefaultLogger + SOURCES DefaultLoggerTest.cpp + LINKS BiomechanicalAnalysis::Logging) diff --git a/src/Logging/tests/DefaultLoggerTest.cpp b/src/Logging/tests/DefaultLoggerTest.cpp new file mode 100644 index 0000000..5dcf1b7 --- /dev/null +++ b/src/Logging/tests/DefaultLoggerTest.cpp @@ -0,0 +1,13 @@ +// Catch2 +#include + +#include + +/** + * This test simply checks that the log runs fine. + */ +TEST_CASE("Default logger test") +{ + //TODO check that the output message is the expected one? + BiomechanicalAnalysis::log()->info("Test message: {}", "test"); +}