2

I am writing a small library in c++ with no external dependencies, except for testing where I want to use catch2. So I'd rather not include the project and thought I could just download it when needed with cmake, but so far it looks like cmake only downloads when I execute the makefile

This is problematic since the rest of the CMakeList file depends on it already being downloaded so it can make use of catch2's provided cmake functions; without it the build fails.

My following approach looks like this

./CMakeList.txt:

cmake_minimum_required(VERSION 3.9)
project(projectname VERSION 0.0.1)

enable_language(CXX CUDA)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY bin)

add_definitions(-DNVRTC_GET_TYPE_NAME=1)

include_directories(include)
link_libraries(cuda nvrtc)

file(GLOB SOURCES "*.cpp")

add_subdirectory(examples)
add_subdirectory(test)

./examples/CMakeList.txt:

if (CMAKE_BUILD_TYPE STREQUAL "Debug")
    add_definitions(-Wall -Dcurrent_log_level=loglevel::DEBUG1)
endif ()

add_executable(example_saxpy ${SOURCES} example_saxpy.cpp)
add_executable(example_program ${SOURCES} example_program.cpp)
add_executable(example_template ${SOURCES} example_template.cpp)

./test/CMakeList.txt:

file(GLOB TESTS "*.cpp")

add_executable(tests ${SOURCES} ${TESTS})
find_package(Catch2 CONFIG)
if (${Catch2_FOUND})       
else ()
    message(STATUS "downloading Catch2")
    include(ExternalProject)
    ExternalProject_Add(
            catch2
            PREFIX ${PROJECT_SOURCE_DIR}/lib/catch2
            GIT_REPOSITORY https://github.com/catchorg/Catch2.git
            TIMEOUT 10
            UPDATE_COMMAND ${GIT_EXECUTABLE} pull
            CONFIGURE_COMMAND ""
            BUILD_COMMAND ""
            INSTALL_COMMAND ""
            LOG_DOWNLOAD ON
    )
    add_dependencies(tests catch2)

    # Expose required variable (CATCH_INCLUDE_DIR) to parent scope
    ExternalProject_Get_Property(catch2 source_dir)
    set(CATCH_INCLUDE_DIR ${source_dir}/include CACHE INTERNAL "Path to include folder for Catch2")
    add_subdirectory(${PROJECT_SOURCE_DIR}/lib/catch2)
    include_directories(${CATCH_INCLUDE_DIR})
endif ()
enable_testing(true)
target_link_libraries(tests Catch2::Catch2)

include(CTest)
include(Catch)
catch_discover_tests(tests)

With the following project structure

projectname
├── bin
├── build
├── CMakeLists.txt
├── examples
│   └── CMakeLists.txt
├── include
    └── $projectname
├── src
└── test
    └──  CMakeLists.txt

Now this won't work at all, because I'm still misunderstanding who variables are scoped between multiple cmakefiles, but putting this all in one CmakeList still won't let add_subdirectory discover Catch2.

My other idea was to just include it as a git submodule, if that would allow cmake to download catch2 before executing the makefiles.

So in general I want to keep my gitrepo as small as possible and only acquire dependencies when they're needed.

ZerataX
  • 43
  • 5
  • 1
    Why not just download catch2 once and for all, add it to CMake and your version control and forget about it? Seems like a waste of time to me to do otherwise. Just move on and fry bigger fish.. – Jesper Juhl Nov 25 '19 at 19:17
  • 1
    Try `FetchContent` + `add_subdirectory`. – Guillaume Racicot Nov 25 '19 at 19:24
  • @JesperJuhl well if I would use a git submodule or git clone I can easily update or choose a different version. Always commiting another project to my project seems very bizarre to me – ZerataX Nov 25 '19 at 19:25
  • Bizzare, why? It doesn't take up much space, makes life simple and gives you perfect control over when you update it. Seems perfectly reasonable, simple and straightforward to me. But then, I prefer to get actual work done rather than getting stuck on micro optimizing / procrastinating over little details like this. – Jesper Juhl Nov 25 '19 at 19:28
  • @GuillaumeRacicot that seems to work, I'll test around with this a bit more before accepting the answer – ZerataX Nov 25 '19 at 20:25
  • 1
    @JesperJuhl no need to be so passive aggressive, fwiw the code is all there so it seems fine to now *micro optimize*. For my workflow I like strictly separating dependencies from my code, just linking to a git tag seems convenient to me and it seems like a technique I could reuse, otherwise I definitely did spend more time trying to set this up than I would've just copying this, but to me this was worth it. – ZerataX Nov 25 '19 at 20:29

1 Answers1

1

I did end up choosing submodules, since fetchContent works well, but is only supported in cmake v3.11

I followed this guide for googletest https://cliutils.gitlab.io/modern-cmake/chapters/testing/googletest.html

./test/CMakeList with catch2 submodule at extern/catch2:

file(GLOB TESTS "*.cpp")
file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/src/*.cpp")    

add_executable(tests ${SOURCES} ${TESTS})
SET(catch2_dir ${PROJECT_SOURCE_DIR}/extern/catch2)

find_package(Git QUIET)
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
    # Update submodules as needed
    option(GIT_SUBMODULE "Check submodules during build" ON)
    if(GIT_SUBMODULE)
        message(STATUS "Submodule update")
        execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
                WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
                RESULT_VARIABLE GIT_SUBMOD_RESULT)
        if(NOT GIT_SUBMOD_RESULT EQUAL "0")
            message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules")
        endif()
    endif()
endif()

if(NOT EXISTS "${catch2_dir}/CMakeLists.txt")
    message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.")
endif()

add_subdirectory(${catch2_dir} ${CMAKE_CURRENT_SOURCE_DIR}/catch_build)
list(APPEND CMAKE_MODULE_PATH "${catch2_dir}/contrib/")

target_link_libraries(tests Catch2::Catch2)

include(CTest)
include(Catch)
catch_discover_tests(tests)
ZerataX
  • 43
  • 5