diff --git a/.github/workflows/cppcmake.yml b/.github/workflows/cppcmake.yml index 7788fa99..4e158956 100644 --- a/.github/workflows/cppcmake.yml +++ b/.github/workflows/cppcmake.yml @@ -62,7 +62,6 @@ jobs: -DCMAKE_INSTALL_PREFIX=${PWD}/install \ -DLSL_UNITTESTS=ON \ -DLSL_BENCHMARKS=ON \ - -DLSL_BUILD_EXAMPLES=ON \ -DCPACK_PACKAGE_DIRECTORY=${PWD}/package \ -Dlslgitrevision=${{ github.sha }} \ -Dlslgitbranch=${{ github.ref }} \ @@ -71,6 +70,18 @@ jobs: echo ${PWD} - name: make run: cmake --build build --target install --config Release -j + + - name: test install using examples + run: | + # Test that the in-tree install was successful by building the examples + cmake -S examples -B examples/build \ + -DLSL_INSTALL_ROOT=${PWD}/install \ + -DCMAKE_INSTALL_PREFIX=examples/build/install \ + -DLSL_COMFY_DEFAULTS=ON \ + ${{ matrix.config.cmake_extra }} \ + ${{ github.event.inputs.cmakeextra }} + cmake --build examples/build --target install --config Release -j + ./examples/build/install/bin/HandleMetaData - name: package run: | diff --git a/.github/workflows/mingw_static.yml b/.github/workflows/mingw_static.yml index 07004ed0..fbdc951c 100644 --- a/.github/workflows/mingw_static.yml +++ b/.github/workflows/mingw_static.yml @@ -1,14 +1,16 @@ name: MinGW Windows static test on: -# push: -# branches: ['*'] -# paths: -# - '**' + push: + branches: ['*'] + paths: + - 'src' + - 'include' + - 'cmake' # - '!docs/**' # - '!.github/**' -# - '.github/workflows/mingw_static.yml' -# pull_request: + - '.github/workflows/mingw_static.yml' + pull_request: workflow_dispatch: concurrency: @@ -18,20 +20,31 @@ concurrency: jobs: build: name: MinGW batteries-included - runs-on: windows-2019 - + strategy: + matrix: + include: +# - { sys: mingw64, env: x86_64 } +# - { sys: mingw32, env: i686 } + - { sys: ucrt64, env: ucrt-x86_64 } +# - { sys: clang64, env: clang-x86_64 } + runs-on: windows-latest defaults: run: shell: 'msys2 {0}' - steps: - uses: actions/checkout@v4 - uses: msys2/setup-msys2@v2 with: - release: false + msystem: ${{matrix.sys}} + update: true cache: true -# install: >- -# cmake + install: >- + git + make + pacboy: >- + toolchain:p + cmake:p + ninja:p - name: Configure CMake run: | cmake --version @@ -39,12 +52,11 @@ jobs: -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=${PWD}/install \ -DLSL_UNITTESTS=ON \ - -DLSL_BUILD_EXAMPLES=ON \ -DLSL_BUILD_STATIC=ON \ -Dlslgitrevision=${{ github.sha }} \ -Dlslgitbranch=${{ github.ref }} \ -DLSL_OPTIMIZATIONS=OFF \ - -G 'MSYS Makefiles' + -G Ninja - name: make run: cmake --build build --target install --config Release -j --verbose diff --git a/.gitignore b/.gitignore index 95e7a65f..d30f711d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ # CLion .idea/ /cmake-build-*/ +/examples/build*/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 158efe7f..00000000 --- a/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -language: cpp -env: - CMakeArgs="" -# https://docs.travis-ci.com/user/reference/osx/#macos-version -matrix: - include: - - os: osx - osx_image: xcode10.1 - env: OSXVER=MacOS10.13 -before_script: -- brew update -- brew upgrade cmake -script: -- cmake --version -- cmake -S . -B build ${CMakeArgs} -DLSL_UNITTESTS=ON -DLSL_EXAMPLES=ON -- cd build -- cmake --build . --config Release -j --target install -- testing/lsl_test_internal || true -- testing/lsl_test_exported -- cpack -- cd .. -deploy: - provider: releases - skip_cleanup: true - api_key: - secure: MAin7yY6lRQNC7gLeE/aDGA90a2TZPjAwQGofyuGvih5T0p3OdKxFn64/lFzEWqb3x74MEu6I4AMteL/wjkminlHGn8fd1bSLtp+2TvnKFnaCuxuhlCNX4BmP741MHEmNMTo8qA+StuxDxwfHh8KVjuFxOkiLV9FolSpxZ3jhl8mBi3IMDodilyfsCniAw/WzekfATkJwmhQ9co642rkTwBKZ7goxdXJmewjEjBsiFn2SUT2+MCcy7NltjYvPdSrUH1LyhVKcSJch7lXqFnlGI21mJxlYdOhd9rA6wOYRZ1hzBRpqAWzE8kbDapMMOfLWrLQP+t5U3WZrt5e7Na/iuopYgzofgfzGr4xK0NwHUuHpc8C1Fair3nOke8IGNDraVHcZazEsBwxv5ekCUTOUFr5lka+ukcznP7PDw2ksIbpOR7bb9G2ubkQT4bDzIfMMOJSPzUbeTN+ds6xiVXukndMsXVW1rkZXk7O4uA60hGWBpX8okddask81Fk6jIBXdJomiRySDfd1DvP16vDGOfei2GZFX8iSvvATQIEE8CCL750vgUuEPPeRvyug2m6+CerAjvH73fmtGiKDmeaQNz+eBnbBpMphKCUvKRpSjSsBPNsQH/epPo34JnUczfzVbZGNNv87OeHDz7POe5YY6+PSwJbfIusPTMY6VdI/g9U= - file: build/*.tar.* - file_glob: true - on: - repo: sccn/liblsl - tags: true diff --git a/CMakeLists.txt b/CMakeLists.txt index 98904a83..c4e0dea1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.16) +cmake_minimum_required (VERSION 3.23) project (liblsl VERSION 1.16.2 LANGUAGES C CXX @@ -17,18 +17,12 @@ include(cmake/SourceFiles.cmake) # include(cmake/TargetObjLib.cmake) # include(cmake/TargetLib.cmake) # include(cmake/Installation.cmake) # -include(cmake/LSLCMake.cmake) include(cmake/TargetOther.cmake) if(LSL_UNITTESTS) add_subdirectory(testing) endif() -if(LSL_BUILD_EXAMPLES) - set(LSL_INSTALL_ROOT ${CMAKE_CURRENT_BINARY_DIR}) - add_subdirectory(examples) -endif() - # Config for packaging # TODO: Config for packaging for the library itself is likely to diverge from config for packing applications. e.g., # -> Optionally install to system directories diff --git a/README.md b/README.md index 3aa22a44..961edfa6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ [![GitHub Actions Status](https://github.com/sccn/liblsl/workflows/C%2FC++%20CI/badge.svg)](https://github.com/sccn/liblsl/actions) -[![Travis Build Status](https://travis-ci.org/sccn/liblsl.svg?branch=master)](https://travis-ci.org/sccn/liblsl) [![Azure Build Status](https://dev.azure.com/labstreaminglayer/liblsl/_apis/build/status/sccn.liblsl?branchName=master)](https://dev.azure.com/labstreaminglayer/liblsl/_build/latest?definitionId=1&branchName=master) [![DOI](https://zenodo.org/badge/123265865.svg)](https://zenodo.org/badge/latestdoi/123265865) diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index fd3e4404..80573762 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -19,14 +19,15 @@ endif() add_library(lslboost INTERFACE) if(LSL_BUNDLED_BOOST) message(STATUS "Using bundled header-only Boost") - target_include_directories(lslboost SYSTEM INTERFACE - $) + target_include_directories(lslboost + INTERFACE + $ + ) else() message(STATUS "Using system Boost") find_package(Boost REQUIRED) - target_compile_definitions(lslboost INTERFACE - lslboost=boost # allows the LSL code base to work with the original Boost namespace/headers - ) + # Map `lslboost` namespace, which LSL code base uses, to system `boost` namespace/headers. + target_compile_definitions(lslboost INTERFACE lslboost=boost) target_link_libraries(lslboost INTERFACE Boost::boost Boost::disable_autolinking) endif() target_compile_definitions(lslboost INTERFACE BOOST_ALL_NO_LIB) diff --git a/cmake/Installation.cmake b/cmake/Installation.cmake index 71003cbc..44f99839 100644 --- a/cmake/Installation.cmake +++ b/cmake/Installation.cmake @@ -25,24 +25,17 @@ endif() # Install the targets and store configuration information. install(TARGETS ${LSLTargets} - EXPORT LSLTargets # generates a CMake package config; TODO: Why the same name as the list of targets? + EXPORT LSLTargets COMPONENT liblsl RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - # If we use CMake 3.23 FILE_SET, replace INCLUDES line with: FILE_SET HEADERS DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) - -# TODO: What does this do? Why do we need LSLTargets.cmake in the build dir? -export(EXPORT LSLTargets - FILE "${CMAKE_CURRENT_BINARY_DIR}/LSLTargets.cmake" - NAMESPACE LSL:: + FILE_SET HEADERS DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) # Generate the LSLConfig.cmake file and mark it for installation install(EXPORT LSLTargets - FILE LSLTargets.cmake # TODO: I think we can use this to generate LSLConfig.cmake, no? + FILE LSLConfig.cmake COMPONENT liblsl NAMESPACE "LSL::" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/LSL @@ -53,28 +46,10 @@ install(EXPORT LSLTargets # INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/lsl) # If we use this method, then we need a corresponding install(FILES ...) command to install the generated file. -# Copy hardcoded CMake files to the build directory. -# TODO: Why bother with this copy? Is it not enough to install (into cmake/LSL)? -configure_file(cmake/LSLCMake.cmake "${CMAKE_CURRENT_BINARY_DIR}/LSLCMake.cmake" COPYONLY) -# TODO: Why bother with this copy? Is it not enough to install (into cmake/LSL)? -# TODO: Why use hardcoded files? We can generate the LSLConfig.cmake. -configure_file(cmake/LSLConfig.cmake "${CMAKE_CURRENT_BINARY_DIR}/LSLConfig.cmake" COPYONLY) - -# Install the public headers. -# TODO: Verify that this is necessary, given that we already installed the INCLUDES above. -# TODO: Verify if it is still necessary to install the headers if we use FILE_SET. -install(DIRECTORY include/ - COMPONENT liblsl - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) - # Install the version file and the helper CMake script. install( FILES - # TODO: Keep this. But why does the configure_file(... COPYONLY) above exist? cmake/LSLCMake.cmake - # TODO: Next line shouldn't be necessary if install(EXPORT...) uses LSLConfig.cmake instead of LSLTargets.cmake - ${CMAKE_CURRENT_BINARY_DIR}/LSLConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/LSLConfigVersion.cmake COMPONENT liblsl DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/LSL diff --git a/cmake/LSLConfig.cmake b/cmake/LSLConfig.cmake deleted file mode 100644 index 3d844714..00000000 --- a/cmake/LSLConfig.cmake +++ /dev/null @@ -1,8 +0,0 @@ -if(TARGET lsl AND NOT TARGET LSL::lsl) - add_library(LSL::lsl ALIAS lsl) -endif() - -if(NOT TARGET LSL::lsl) - include("${CMAKE_CURRENT_LIST_DIR}/LSLTargets.cmake") -endif() -include("${CMAKE_CURRENT_LIST_DIR}/LSLCMake.cmake") diff --git a/cmake/ProjectOptions.cmake b/cmake/ProjectOptions.cmake index f0f605d0..48f4c565 100644 --- a/cmake/ProjectOptions.cmake +++ b/cmake/ProjectOptions.cmake @@ -7,7 +7,6 @@ option(LSL_OPTIMIZATIONS "Enable some more compiler optimizations" ON) option(LSL_BUNDLED_BOOST "Use the bundled Boost by default" ON) option(LSL_BUNDLED_PUGIXML "Use the bundled pugixml by default" ON) option(LSL_TOOLS "Build some experimental tools for in-depth tests" OFF) -option(LSL_BUILD_EXAMPLES "Build example programs in examples/" OFF) option(LSL_UNITTESTS "Build LSL library unit tests" OFF) option(LSL_FORCE_FANCY_LIBNAME "Add library name decorations (32/64/-debug)" OFF) mark_as_advanced(LSL_FORCE_FANCY_LIBNAME) diff --git a/cmake/TargetLib.cmake b/cmake/TargetLib.cmake index 356c9c66..7e7d515f 100644 --- a/cmake/TargetLib.cmake +++ b/cmake/TargetLib.cmake @@ -6,6 +6,7 @@ set_source_files_properties("src/buildinfo.cpp" LSL_LIBRARY_INFO_STR="${LSL_VERSION_INFO}/link:${LSL_LIB_TYPE}" ) add_library(lsl ${LSL_LIB_TYPE} src/buildinfo.cpp) +add_library(LSL::lsl ALIAS lsl) # Configure main library @@ -23,19 +24,19 @@ if(LSL_FORCE_FANCY_LIBNAME) ) endif() -# Includes. TODO: Can we not inherit these from lslobj? -target_include_directories(lsl - INTERFACE - $ - $ -) +# Link dependencies. The only dependency is lslobj, which contains the bulk of the library code and linkages. +# Note: We link PRIVATE because lslobj exposes extra symbols that are not part of the public API +# but are used by the internal tests. +target_link_libraries(lsl PRIVATE lslobj) -# Link dependencies. The biggest dependency is lslobj, which contains the bulk of the library code. -target_link_libraries(lsl - PRIVATE - lslobj # TODO: If this is public, does that improve inheritance of includes and compile definitions? - lslboost # TODO: Shouldn't be needed -- lslobj already links it - ${PUGIXML_LIBRARIES} +# Set the include directories for the lsl target. +# Note: We had to link lslobj as a PRIVATE dependency, therefore we must manually expose the include directories +get_target_property(LSLOBJ_HEADERS lslobj HEADER_SET) +target_sources(lsl + INTERFACE + FILE_SET HEADERS + BASE_DIRS include + FILES ${LSLOBJ_HEADERS} ) # Set compile definitions for lsl diff --git a/cmake/TargetObjLib.cmake b/cmake/TargetObjLib.cmake index ad911f80..4e32ef79 100644 --- a/cmake/TargetObjLib.cmake +++ b/cmake/TargetObjLib.cmake @@ -1,32 +1,22 @@ # Create object library so all files are only compiled once add_library(lslobj OBJECT ${lslsources} - ${lslheaders} +# ${lslheaders} # Headers are added later using FILE_SET ) # Set the includes/headers for the lslobj target -# Note: We cannot use the PUBLIC_HEADER property of the target, because -# it flattens the include directories. -# We could use the new FILE_SET feature that comes with CMake 3.23. -# This is how it would look. Note that HEADERS is a special set name and implies its type. -#target_sources(lslobj -# PUBLIC -# FILE_SET HEADERS -# BASE_DIRS include -# FILES ${lslheaders} -#) -# We settle on the older and more common target_include_directories PUBLIC approach. -# If we used the FILET_SET approach then we would remove the PUBLIC includes below. -target_include_directories(lslobj - PUBLIC - $ - $ +# Note: We cannot use the PUBLIC_HEADER property of the target, +# because it flattens the include directories. +# Note: IME, this approach is less error prone than target_include_directories +target_sources(lslobj INTERFACE - # Propagate include directories to consumers - $ # for unit tests + FILE_SET HEADERS # special set name; implies TYPE. + BASE_DIRS include + FILES ${lslheaders} ) # Link system libs +# (boost might be bundled or system) target_link_libraries(lslobj PRIVATE lslboost Threads::Threads) if(MINGW) target_link_libraries(lslobj PRIVATE bcrypt) @@ -45,6 +35,30 @@ elseif(WIN32) target_link_libraries(lslobj PRIVATE iphlpapi winmm mswsock ws2_32) endif() +# Compiler settings +target_compile_definitions(lslobj + PRIVATE + LIBLSL_EXPORTS + LOGURU_DEBUG_LOGGING=$ + PUBLIC + ASIO_NO_DEPRECATED + $<$:LSLNOAUTOLINK> # don't use #pragma(lib) in CMake builds +) +if(WIN32) + target_compile_definitions(lslobj + PRIVATE + _CRT_SECURE_NO_WARNINGS + PUBLIC + _WIN32_WINNT=${LSL_WINVER} + ) + if(BUILD_SHARED_LIBS) + # set_target_properties(lslobj + # PROPERTIES + # WINDOWS_EXPORT_ALL_SYMBOLS ON + # ) + endif(BUILD_SHARED_LIBS) +endif() + # Compiler settings target_compile_definitions(lslobj PRIVATE @@ -72,6 +86,8 @@ endif(WIN32) # Link in 3rd party dependencies # - loguru and asio header-only target_include_directories(lslobj + # Note: We use `SYSTEM` to suppress warnings from 3rd party headers and put these at the end of the include path. + # Note: We use `PUBLIC` because 'internal tests' import individual source files and link lslobj. SYSTEM PUBLIC $ $ @@ -88,10 +104,11 @@ if(NOT LSL_OPTIMIZATIONS) endif() # - pugixml +# Note: We use `PUBLIC` because 'internal tests' import individual source files and link lslobj. if(LSL_BUNDLED_PUGIXML) target_sources(lslobj PRIVATE thirdparty/pugixml/pugixml.cpp) target_include_directories(lslobj - SYSTEM PUBLIC + PUBLIC $ ) else() diff --git a/cmake/TargetOther.cmake b/cmake/TargetOther.cmake index 32b66dab..9900f4b3 100644 --- a/cmake/TargetOther.cmake +++ b/cmake/TargetOther.cmake @@ -1,3 +1,5 @@ +include(cmake/LSLCMake.cmake) # Needed for `installLSLApp` + # Build utilities add_executable(lslver testing/lslver.c) target_link_libraries(lslver PRIVATE lsl) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index eca4d200..aa997882 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,15 +1,25 @@ -cmake_minimum_required(VERSION 3.12) +cmake_minimum_required(VERSION 3.23) project(lslexamples LANGUAGES C CXX VERSION 0.2.0) + +include(GNUInstallDirs) + find_package(LSL REQUIRED - HINTS ${LSL_INSTALL_ROOT} - "${CMAKE_CURRENT_LIST_DIR}/../build" - "${CMAKE_CURRENT_LIST_DIR}/../out/x64-Release" - PATH_SUFFIXES share/LSL) + HINTS + ${LSL_INSTALL_ROOT} + "${CMAKE_CURRENT_LIST_DIR}/../install" # GHA scripts default install directory + "${CMAKE_CURRENT_LIST_DIR}/../cmake-build-release/install" # CLion default if using -DCMAKE_INSTALL_PREFIX=install + "${CMAKE_CURRENT_LIST_DIR}/../cmake-build-release-visual-studio/install" # CLion default if using VS compiler + "${CMAKE_CURRENT_LIST_DIR}/../out/build/x64-Release/install" # MSVC default if using -DCMAKE_INSTALL_PREFIX=install + PATH_SUFFIXES share/LSL +) get_filename_component(LSL_PATH ${LSL_CONFIG} DIRECTORY) message(STATUS "Found LSL lib in ${LSL_PATH}") -message(STATUS "LSL BIN DIR: ${liblsl_BINARY_DIR} & ${LIBLSL_BINARY_DIR}") + +# Include the LSLCMake.cmake file, just for testing. +# This doesn't do much for us now that we don't use installLSLApp() anymore. +include("${LSL_PATH}/LSLCMake.cmake") # convenience function to add an example file # this creates a target, links the necessary libraries and @@ -20,7 +30,30 @@ function(addlslexample name extension) ) target_link_libraries(${name} PRIVATE LSL::lsl) target_compile_features(${name} PRIVATE cxx_constexpr) - installLSLApp(${name}) + + # Set RPATH properties for macOS and Linux + set_target_properties(${name} PROPERTIES + INSTALL_RPATH_USE_LINK_PATH TRUE + BUILD_WITH_INSTALL_RPATH FALSE + ) + + # One might also want to do the following, to give the option of copying the + # LSL library to the same directory or install tree as the executable. + # However, this is not necessary for the examples, as they are not intended to be relocated. +# if(APPLE) +# set_target_properties(${name} PROPERTIES +# INSTALL_RPATH "@loader_path;@loader_path/../lib" +# ) +# elseif(UNIX) +# set_target_properties(${name} PROPERTIES +# INSTALL_RPATH "$ORIGIN:$ORIGIN/../lib" +# ) +# endif() + + install(TARGETS ${name} + COMPONENT ${PROJECT_NAME} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) endfunction() find_package(Threads) @@ -47,3 +80,18 @@ addlslexample(TestSyncWithoutData cpp) target_link_libraries(TestSyncWithoutData PRIVATE Threads::Threads) +# Windows doesn't have RPATH so we put the dll into the same directory as the executable. +if(WIN32) + # For one of the targets, copy the lsl.dll into the build directory so we can debug the examples if needed. + add_custom_command(TARGET HandleMetaData POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + $ + COMMENT "Copying lsl.dll to examples build directory" + ) + # Install the lsl.dll to the same directory as the executable. + install( + CODE "file(INSTALL DESTINATION \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}\" TYPE SHARED_LIBRARY FILES \"$\")" + COMPONENT ${PROJECT_NAME} + ) +endif() diff --git a/examples/HandleMetaData.cpp b/examples/HandleMetaData.cpp index dffe43d7..f56fd561 100644 --- a/examples/HandleMetaData.cpp +++ b/examples/HandleMetaData.cpp @@ -43,7 +43,7 @@ int main(int argc, char *argv[]) { } } catch (std::exception &e) { std::cerr << "Got an exception: " << e.what() << std::endl; } - std::cout << "Press any key to exit. " << std::endl; - std::cin.get(); + // std::cout << "Press any key to exit. " << std::endl; + // std::cin.get(); return 0; }