1
0
mirror of synced 2024-12-22 12:40:00 +01:00

Adding catch and some fixes

This commit is contained in:
Henry Fredrick Schreiner 2018-03-30 15:15:37 +02:00
parent 5a1632ae28
commit f09ca2ca8b
9 changed files with 99 additions and 165 deletions

View File

@ -16,14 +16,14 @@
* [Small but common needs](chapters/features/small.md)
* [Utilities](chapters/features/utilities.md)
* [IDEs](chapters/features/ides.md)
* [Debugging (X)](chapters/features/debug.md)
* [Debugging](chapters/features/debug.md)
* [Including Projects](chapters/projects.md)
* [Submodule](chapters/projects/submodule.md)
* [DownloadProject](chapters/projects/download.md)
* [Fetch (CMake 3.11)](chapters/projects/fetch.md)
* [Fetch (CMake 3.11) (X)](chapters/projects/fetch.md)
* [Testing](chapters/testing.md)
* [GoogleTest](chapters/testing/googletest.md)
* [Catch (X)](chapters/testing/catch.md)
* [Catch](chapters/testing/catch.md)
* [Exporting and Installing](chapters/exporting.md)
* [Looking for libraries](chapters/packages.md)
* [CUDA](chapters/packages/CUDA.md)

View File

@ -1,6 +1,6 @@
# Debugger support
## Building in debug mode
For single-configuration generators, you can build your code with `-DCMAKE_BUILD_TYPE=Debug` to get debugging flags. In multi-configuration generators, like many IDEs, you can pick the configuration in the IDE. There are distinct flags for this mode (variables ending in `_DEBUG` as opposed to `_RELEASE`), as well as a generator expression value `CONFIG:Debug` or `CONFIG:Release`.
{% hint style='working' %}
This document is a work in progress. You can raise an issue or put in a merge request on [GitLab](https://gitlab.com/CLIUtils/modern-cmake).
{% endhint %}

View File

@ -3,70 +3,5 @@
This is where a good Git system plus CMake shines. You might not be able to solve all the world's problems, but
this is pretty close for C++!
## Git Submodule Method
There are several methods listed in the chapters in this section.
If you want to add a Git repository on the same service (GitHub, GitLab, BitBucket, etc), the following is the correct Git command to set that up as a submodule in the `extern` directory:
```bash
git submodule add ../../owner/repo.git extern/repo
```
The relative path to the repo is important; it allows you to keep the same access method (ssh or https) as the parent repository. This works very well in most ways. When you are inside the submodule, you can treat it just like a normal repo, and when you are in the parent repository, you can "add" to change the current commit pointer.
But the traditional downside is that you either have to have your users know git submodule commands, so they can `init` and `update` the repo, or they have to add `--recursive` when they initially clone your repo. CMake can offer a solution:
```cmake
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 ${CMAKE_CURRENT_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 "${PROJECT_SOURCE_DIR}/extern/repo/CMakeLists.txt"
message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.")
endif()
```
The first line checks for Git using CMake's built in `FindGit.cmake`. Then, if you are in a git checkout of your source, add an option (defaulting to `ON`) that allows developers to turn off the feature if they need to. We then run the command to get all repositories, and fail if that command fails, with a nice error message. Finally, we verify that the repositories exist before continuing, regardless of the method used to obtain them. You can use `OR` to list several.
Now, your users can be completely oblivious to the existence of the submodules, and you can still keep up good development practices! The only thing to watch out for is for developers; you will reset the submodule when you rerun CMake if you are developing inside the submodule. Just add new commits to the parent staging area, and you'll be fine.
You can then include projects that provide good CMake support:
```cmake
add_subdirectory(extern/repo)
```
Or, you can build an interface library target yourself if it is a header only project. Or, you can use `find_package` if that is supported, probably preparing the initial search directory to be the one you've added (check the docs or the file for the `Find*.cmake` file you are using). You can also include a CMake helper file directory if you append to your `CMAKE_MODULE_PATH`, for example to add `pybind11`'s improved `FindPython*.cmake` files.
### Bonus: Git version number
Move this to Git section:
```cmake
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
OUTPUT_VARIABLE PACKAGE_GIT_VERSION
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
```
## Downloading Method
There are several different methods to get CMake to download data, either at configure time or compile time. The latter is build directly into CMake with the x command. If you prefer configure time, see the [Crascit/DownloadProject](https://github.com/Crascit/DownloadProject) repository for a drop-in solution. Submodules work so well, though, that I've discontinued most of the downloads for things like GoogleTest and moved them to submodules. Auto downloads are harder to mimic if you
don't have internet access, and they are often implemented in the build directory, wasting time and space if you have multiple build directories.
{% hint style='working' %}
A new module in CMake 3.11, [FetchContent](https://cmake.org/cmake/help/v3.11/module/FetchContent.html) will build in this feature into CMake, stay tuned!
{% endhint %}

View File

@ -1,34 +1,16 @@
# GoogleTest: Download method
You can use the downloader in my [CMake helper repository][CLIUtils/cmake], using CMake's `include` command.
## Downloading Method: build time
This is a downloader for [GoogleTest], based on the excellent [DownloadProject] tool. Downloading a copy for each project is the recommended way to use GoogleTest (so much so, in fact, that they have disabled the automatic CMake install target), so this respects that design decision. This method downloads the project at configure time, so that IDE's correctly find the libraries. Using it is simple:
```cmake
cmake_minimum_required(VERSION 3.4)
project(MyProject CXX)
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
enable_testing() # Must be in main file
include(AddGoogleTest) # Could be in /tests/CMakeLists.txt
add_executable(SimpleTest SimpleTest.cu)
add_gtest(SimpleTest)
```
> Note: `add_gtest` is just a macro that adds `gtest`, `gmock`, and `gtest_main`, and then runs `add_test` to create a test with the same name:
> ```cmake
> target_link_libraries(SimpleTest gtest gmock gtest_main)
> add_test(SimpleTest SimpleTest)
> ```
Until CMake 3.11, the primary download method for packages was done at build time. This causes several issues; most important of which is that `add_subdirectory` doesn't work on a file that doesn't exist yet! The tool for this, ExternalProject, has to work around this by doing the build itself. (It can, however, build non-CMake packages as well).[^1]
## Catch
[^1]: Note that ExternalData is the tool for non-package data.
## Downloading Method: configure time
If you prefer configure time, see the [Crascit/DownloadProject](https://github.com/Crascit/DownloadProject) repository for a drop-in solution. Submodules work so well, though, that I've discontinued most of the downloads for things like GoogleTest and moved them to submodules. Auto downloads are harder to mimic if you
don't have internet access, and they are often implemented in the build directory, wasting time and space if you have multiple build directories.
[^1]: Here I've assumed that you are working on a GitHub repository by using the relative path to googletest.
[CLIUtils/cmake]: https://github.com/CLIUtils/cmake
[GoogleTest]: https://github.com/google/googletest
[DownloadProject]: https://github.com/Crascit/DownloadProject

View File

@ -1,15 +1,6 @@
# Testing
#FetchContent (CMake 3.11+)
## General Testing Information
## GoogleTest
## Fetch
[CLIUtils/cmake]: https://github.com/CLIUtils/cmake
[GoogleTest]: https://github.com/google/googletest
[DownloadProject]: https://github.com/Crascit/DownloadProject
{% hint style='working' %}
This document is a work in progress. You can raise an issue or put in a merge request on [GitLab](https://gitlab.com/CLIUtils/modern-cmake).
{% endhint %}
~

View File

@ -1,67 +1,57 @@
# GoogleTest: Submodule method (preferred)
# Git Submodule Method
To use this method, just checkout GoogleTest as a submodule:[^1]
If you want to add a Git repository on the same service (GitHub, GitLab, BitBucket, etc), the following is the correct Git command to set that up as a submodule in the `extern` directory:
```cmake
git submodule add --branch=release-1.8.0 ../../google/googletest.git extern/googletest
```bash
git submodule add ../../owner/repo.git extern/repo
```
Then, in your main `CMakeLists.txt`:
The relative path to the repo is important; it allows you to keep the same access method (ssh or https) as the parent repository. This works very well in most ways. When you are inside the submodule, you can treat it just like a normal repo, and when you are in the parent repository, you can "add" to change the current commit pointer.
But the traditional downside is that you either have to have your users know git submodule commands, so they can `init` and `update` the repo, or they have to add `--recursive` when they initially clone your repo. CMake can offer a solution:
```cmake
option(PACKAGE_TESTS "Build the tests" ON)
if(PACKAGE_TESTS)
enable_testing()
add_subdirectory(tests)
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 ${CMAKE_CURRENT_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 "${PROJECT_SOURCE_DIR}/extern/repo/CMakeLists.txt"
message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.")
endif()
```
As mentioned before, you have to do the `enable_testing` in your main CMakeLists. Now, in your tests directory:
The first line checks for Git using CMake's built in `FindGit.cmake`. Then, if you are in a git checkout of your source, add an option (defaulting to `ON`) that allows developers to turn off the feature if they need to. We then run the command to get all repositories, and fail if that command fails, with a nice error message. Finally, we verify that the repositories exist before continuing, regardless of the method used to obtain them. You can use `OR` to list several.
Now, your users can be completely oblivious to the existence of the submodules, and you can still keep up good development practices! The only thing to watch out for is for developers; you will reset the submodule when you rerun CMake if you are developing inside the submodule. Just add new commits to the parent staging area, and you'll be fine.
You can then include projects that provide good CMake support:
```cmake
add_subdirectory("${PROJECT_SOURCE_DIR}/extern/googletest" "extern/googletest")
add_subdirectory(extern/repo)
```
If you did this in your main CMakeLists, you could use a normal add_subdirectory; the extra path here is needed to correct the build path because we are calling it from a subdirectory.
Or, you can build an interface library target yourself if it is a header only project. Or, you can use `find_package` if that is supported, probably preparing the initial search directory to be the one you've added (check the docs or the file for the `Find*.cmake` file you are using). You can also include a CMake helper file directory if you append to your `CMAKE_MODULE_PATH`, for example to add `pybind11`'s improved `FindPython*.cmake` files.
The next line is optional, but keeps your `CACHE` cleaner:
### Bonus: Git version number
Move this to Git section:
```cmake
mark_as_advanced(
BUILD_GMOCK BUILD_GTEST BUILD_SHARED_LIBS
gmock_build_tests gtest_build_samples gtest_build_tests
gtest_disable_pthreads gtest_force_shared_crt gtest_hide_internal_symbols
)
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
OUTPUT_VARIABLE PACKAGE_GIT_VERSION
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
```
If you are interested in keeping IDEs that support folders clean, I would also add these lines:
```cmake
set_target_properties(gtest PROPERTIES FOLDER extern)
set_target_properties(gtest_main PROPERTIES FOLDER extern)
set_target_properties(gmock PROPERTIES FOLDER extern)
set_target_properties(gmock_main PROPERTIES FOLDER extern)
```
Then, to add a test, I'd recommend the following macro:
```cmake
macro(package_add_test TESTNAME)
add_executable(${TESTNAME} ${ARGN})
target_link_libraries(${TESTNAME} gtest gmock gtest_main)
add_test(${TESTNAME} ${TESTNAME})
set_target_properties(${TESTNAME} PROPERTIES FOLDER tests)
endmacro()
package_add_test(test1 test1.cpp)
```
This will allow you to quickly and simply add tests. Feel free to adjust to suit your needs. If you haven't seen it before, `ARGN` is "every argument after the listed ones".
[^1]: Here I've assumed that you are working on a GitHub repository by using the relative path to googletest.
[CLIUtils/cmake]: https://github.com/CLIUtils/cmake
[GoogleTest]: https://github.com/google/googletest
[DownloadProject]: https://github.com/Crascit/DownloadProject

View File

@ -8,5 +8,19 @@ In your main CMakeLists.txt you need to add the following function call (not in
enable_testing()
```
You can register targets with:
```cmake
add_test(NAME TestName COMMAND TargetName)
```
If you put something else besides a target name after COMMAND, it will register as a command line to run. It would also be valid to put the generator expression:
```cmake
add_test(NAME TestName COMMAND $<TARGET_FILE:${TESTNAME}>)
```
which would use the output location (thus, the executable) of the produced target.
Look at the subchapters for recipes for popular frameworks.

View File

@ -1,4 +1,27 @@
# Catch
Catch and [Catch2] (C++11 only version) are powerful, idomatic testing solutions similar in philosophy to PyTest for Python. To use Catch in a CMake project, there are several options.
## Vendoring
If you simply drop in the single include release of Catch into your project, this is what you would need to add Catch:
```cmake
# Prepare "Catch" library for other executables
set(CATCH_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/catch)
add_library(Catch2::Catch IMPORTED INTERFACE)
set_property(Catch2::Catch PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CATCH_INCLUDE_DIR}")
```
Then, you would link to Catch2::Catch. This would have been okay as an INTERFACE target since you won't be exporting your tests.
## Direct inclusion
If you add the library using ExternalProject, FetchContent, or git submodules, you can also `add_subdirectory` Catch (CMake 3.1+).
Catch also provides two CMake modules that you can use to register the individual tests with CMake.
[Catch2]: https://github.com/catchorg/Catch2

View File

@ -18,13 +18,13 @@ if(PACKAGE_TESTS)
endif()
```
As mentioned before, you have to do the `enable_testing` in your main CMakeLists. Now, in your tests directory:
I would recommend using something like `PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME` to set the default for the option, since this should only build by default if this is the current project. As mentioned before, you have to do the `enable_testing` in your main CMakeLists. Now, in your tests directory:
```cmake
add_subdirectory("${PROJECT_SOURCE_DIR}/extern/googletest" "extern/googletest")
```
If you did this in your main CMakeLists, you could use a normal add_subdirectory; the extra path here is needed to correct the build path because we are calling it from a subdirectory.
If you did this in your main CMakeLists, you could use a normal `add_subdirectory`; the extra path here is needed to correct the build path because we are calling it from a subdirectory.
The next line is optional, but keeps your `CACHE` cleaner:
@ -85,7 +85,6 @@ add_gtest(SimpleTest)
> ```
[^1]: Here I've assumed that you are working on a GitHub repository by using the relative path to googletest.