Adding extended example
This commit is contained in:
parent
f0f8878b57
commit
90ff509892
@ -1,18 +1,17 @@
|
||||
|
||||
test_code:
|
||||
image: rootproject/root-ubuntu16:6.12
|
||||
image: rootproject/root:latest
|
||||
stage: test
|
||||
before_script:
|
||||
- yum install -y make cmake boost-devel git
|
||||
# will install latest CMake, even though Fedora has a recent one
|
||||
- mkdir -p $HOME/.local
|
||||
- curl -s "https://cmake.org/files/v3.15/cmake-3.15.1-Linux-x86_64.tar.gz" | tar --strip-components=1 -xz -C $HOME/.local
|
||||
- export PATH=$HOME/.local/bin:$PATH
|
||||
script:
|
||||
- mkdir -p build
|
||||
- cd build
|
||||
- cmake ../examples
|
||||
- VERBOSE=1 cmake --build .
|
||||
- ctest
|
||||
- cd ..
|
||||
- cmake -S examples -B build
|
||||
- cmake --build build -v
|
||||
- cmake --build build -t test
|
||||
|
||||
pages:
|
||||
image: node:10
|
||||
|
@ -6,3 +6,5 @@ and one application, MyExample, with one source file.
|
||||
[import:'main', lang:'cmake'](../../examples/simple-project/CMakeLists.txt)
|
||||
|
||||
The complete example is available in [examples folder](https://gitlab.com/CLIUtils/modern-cmake/tree/master/examples/simple-project).
|
||||
|
||||
A larger, multi-file example is [also available](https://gitlab.com/CLIUtils/modern-cmake/tree/master/examples/extended-project).
|
@ -49,14 +49,17 @@ Here are some common build environments and the CMake version you'll find on the
|
||||
| [Ubuntu 18.04 LTS: Bionic](https://launchpad.net/ubuntu/bionic/+source/cmake) | 3.10.2 | An LTS with a pretty decent minimum version! |
|
||||
| [Ubuntu 18.10: Cosmic](https://launchpad.net/ubuntu/cosmic/+source/cmake) | 3.12.1 | |
|
||||
| [Ubuntu 19.04: Disco](https://launchpad.net/ubuntu/disco/+source/cmake) | 3.13.4 | |
|
||||
| [AlpineLinux 3.10](https://pkgs.alpinelinux.org/packages?name=cmake&branch=v3.10)| 3.14.5 | Useful in Docker |
|
||||
| [Python PyPI](https://pypi.org/project/cmake/) | 3.13.3 | Just `pip install cmake` on many systems. Add `--user` for local installs. |
|
||||
| [Anaconda](https://anaconda.org/anaconda/cmake) | 3.14.0 | For use with Conda |
|
||||
| [Conda-Forge](https://github.com/conda-forge/cmake-feedstock) | 3.14.5 | For use with Conda |
|
||||
| [Conda-Forge](https://github.com/conda-forge/cmake-feedstock) | 3.15.1 | For use with Conda |
|
||||
| [Homebrew on macOS](https://formulae.brew.sh/formula/cmake) | 3.15.1 | On macOS with Homebrew, this is only a few minutesa behind cmake.org. |
|
||||
| [Chocolaty on Windows](https://chocolatey.org/packages/cmake) | 3.14.6 | Also up to date. The normal cmake.org installers are common on Windows, as well. |
|
||||
| TravisCI Trusty | 3.9 | The December 2017 update added a recent version of clang and CMake! Finally! |
|
||||
| TravisCI Xenial | 3.12.4 | Mid November 2018 this image became ready for widescale use. |
|
||||
|
||||
Also see [pkgs.org/download/cmake](https://pkgs.org/download/cmake).
|
||||
|
||||
## Pip
|
||||
|
||||
This is also provided as an official package, maintained by the authors of CMake at KitWare. It's a rather new method, and might fail on some systems (Alpine isn't supported last I checked, but that has CMake 3.8), but works really well when it works (like on Travis CI). If you have pip (Python's package installer), you can do:
|
||||
|
@ -192,13 +192,14 @@ Quite a few more find packages produce targets. The new Visual Studio 16 2019 ge
|
||||
|
||||
## [CMake 3.15][] : CLI upgrade
|
||||
|
||||
This release has many smaller polishing changes, include several of improvements to the CMake command line, such as control over the default generator through environment variables (so now it's easy to change the default generator to Ninja). Multiple targets and `--install` are supported in `--build` mode. CMake finally supports multiple levels of logging. Generator expressions gained a few handy tools. The still very new FindPython module continues to improve, and FindBoost is now more inline with Boost 1.70's new CONFIG
|
||||
This release has many smaller polishing changes, include several of improvements to the CMake command line, such as control over the default generator through environment variables (so now it's easy to change the default generator to Ninja). Multiple targets are supported in `--build` mode, and `--install` mode added. CMake finally supports multiple levels of logging. Generator expressions gained a few handy tools. The still very new FindPython module continues to improve, and FindBoost is now more inline with Boost 1.70's new CONFIG
|
||||
module. `export(PACKAGE)` has drastically changed; it now no longer touches `$HOME/.cmake` by default (if CMake Minimum version is 3.15 or higher), and requires an extra step if a user wants to use it. This is generally less surprising.
|
||||
|
||||
|
||||
* «envvar:CMAKE_GENERATOR» environment variable added to control default generator
|
||||
* Multiple target support in build mode, `cmake . --build --target a b`
|
||||
* Install support, `cmake . --install`
|
||||
* Shortcut `-t` for `--target`
|
||||
* Install support, `cmake . --install`, does not invoke the build system
|
||||
* Support for `--loglevel` and `NOTICE`, `VERBOSE`, `DEBUG`, and `TRACE` for `message`
|
||||
* The «command:list» command gained `PREPEND`, `POP_FRONT`, and `POP_BACK`
|
||||
* «command:execute_process» gained `COMMAND_ECHO` option («variable:CMAKE_EXECUTE_PROCESS_COMMAND_ECHO») allows you to automatically echo commands before running them
|
||||
|
@ -1,4 +1,4 @@
|
||||
#FetchContent (CMake 3.11+)
|
||||
# FetchContent (CMake 3.11+)
|
||||
|
||||
Often, you would like to do your download of data or packages as part of the configure instead of the build. This was invented several times in third party modules, but was finally added to CMake itself as part of CMake 3.11 as the [FetchContent] module.
|
||||
|
||||
@ -14,9 +14,17 @@ For example, to download Catch2:
|
||||
FetchContent_Declare(
|
||||
catch
|
||||
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
||||
GIT_TAG v2.2.1
|
||||
GIT_TAG v2.9.1
|
||||
)
|
||||
|
||||
# CMake 3.14+
|
||||
FetchContent_MakeAvailable(catch2)
|
||||
```
|
||||
|
||||
If you can't use CMake 3.14+, the classic way to prepare code was:
|
||||
|
||||
```cmake
|
||||
# CMake 3.11+
|
||||
FetchContent_GetProperties(catch)
|
||||
if(NOT catch_POPULATED)
|
||||
FetchContent_Populate(catch)
|
||||
@ -24,5 +32,21 @@ if(NOT catch_POPULATED)
|
||||
endif()
|
||||
```
|
||||
|
||||
Of course, you could bundled this up into a macro:
|
||||
|
||||
```cmake
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.14)
|
||||
macro(FetchContent_MakeAvailable NAME)
|
||||
FetchContent_GetProperties(${NAME})
|
||||
if(NOT ${NAME}_POPULATED)
|
||||
FetchContent_Populate(${NAME})
|
||||
add_subdirectory(${${NAME}_SOURCE_DIR} ${${NAME}_BINARY_DIR})
|
||||
endif()
|
||||
endmacro()
|
||||
endif()
|
||||
```
|
||||
|
||||
Now you have the CMake 3.14+ syntax in CMake 3.11+.
|
||||
|
||||
[FetchContent]: https://cmake.org/cmake/help/latest/module/FetchContent.html
|
||||
|
||||
|
@ -5,15 +5,31 @@
|
||||
In your main CMakeLists.txt you need to add the following function call (not in a subfolder):
|
||||
|
||||
```cmake
|
||||
include(CTest)
|
||||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
|
||||
include(CTest)
|
||||
endif()
|
||||
```
|
||||
|
||||
Which will enable testing and set a `BUILD_TESTING` option so users can turn testing on and off (Along with [a few other things](https://gitlab.kitware.com/cmake/cmake/blob/master/Modules/CTest.cmake)). Or you can do this yourself:
|
||||
Which will enable testing and set a `BUILD_TESTING` option so users can turn testing on and off (Along with [a few other things](https://gitlab.kitware.com/cmake/cmake/blob/master/Modules/CTest.cmake)). Or you can do this yourself by directly calling `enable_testing()`.
|
||||
|
||||
When you add your test folder, you should do something like this:
|
||||
|
||||
```cmake
|
||||
enable_testing()
|
||||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
```
|
||||
|
||||
The reason for this is that if someone else includes your package, and they use `BUILD_TESTING`, they probably do not want your tests to build. In the rare case that you really do want to enable testing on both packages, you can provide an override:
|
||||
|
||||
```cmake
|
||||
if((CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME OR MYPROJECT_BUILD_TESTING) AND BUILD_TESTING)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
```
|
||||
|
||||
The main use case for the override above is actually in this book's own examples, as the master CMake project really does want to run all the subproject tests.
|
||||
|
||||
You can register targets with:
|
||||
|
||||
```cmake
|
||||
@ -49,3 +65,7 @@ add_test(
|
||||
## Testing Frameworks
|
||||
|
||||
Look at the subchapters for recipes for popular frameworks.
|
||||
|
||||
* [GoogleTest](testing/googletest.md): A popular option from Google. Development can be a bit slow.
|
||||
* [Catch2](testing/catch.md): A modern, PyTest-like framework with clever macros.
|
||||
* [DocTest](https://github.com/onqtam/doctest): A replacement for Catch2 that is supposed to compile much faster and be cleaner. See Catch2 chapter and replace with DocTest.
|
||||
|
@ -1,10 +1,12 @@
|
||||
cmake_minimum_required(VERSION 3.11)
|
||||
cmake_minimum_required(VERSION 3.11...3.15)
|
||||
|
||||
project(ModernCMakeExamples)
|
||||
set(MODERN_CMAKE_BUILD_TESTING ON)
|
||||
|
||||
enable_testing()
|
||||
include(CTest)
|
||||
|
||||
add_subdirectory(simple-project)
|
||||
add_subdirectory(extended-project)
|
||||
|
||||
add_subdirectory(root-usefile)
|
||||
add_subdirectory(root-simple)
|
||||
|
1
examples/extended-project/.gitignore
vendored
Normal file
1
examples/extended-project/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*build*
|
70
examples/extended-project/CMakeLists.txt
Normal file
70
examples/extended-project/CMakeLists.txt
Normal file
@ -0,0 +1,70 @@
|
||||
# Works with 3.11 and tested through 3.15
|
||||
cmake_minimum_required(VERSION 3.11...3.15)
|
||||
|
||||
# Project name and a few useful settings. Other commands can pick up the results
|
||||
project(ModernCMakeExample
|
||||
VERSION 0.1
|
||||
DESCRIPTION "An example project with CMake"
|
||||
LANGUAGES CXX)
|
||||
|
||||
# Only do these if this is the main project, and not if it is included through add_subdirectory
|
||||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
|
||||
|
||||
# Optionally set things like CMAKE_CXX_STANDARD, CMAKE_POSITION_INDEPENDENT_CODE here
|
||||
|
||||
# Let's ensure -std=c++xx instead of -std=g++xx
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
# Let's nicely support folders in IDE's
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
# Testing only available if this is the main app
|
||||
# Note this needs to be done in the main CMakeLists
|
||||
# since it calls enable_testing, which must be in the
|
||||
# main CMakeLists.
|
||||
include(CTest)
|
||||
|
||||
# Docs only available if this is the main app
|
||||
find_package(Doxygen)
|
||||
if(Doxygen_FOUND)
|
||||
add_subdirectory(docs)
|
||||
else()
|
||||
message(STATUS "Doxygen not found, not building docs")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# FetchContent added in CMake 3.11, downloads during the configure step
|
||||
include(FetchContent)
|
||||
# FetchContent_MakeAvailable was not added until CMake 3.14
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.14)
|
||||
include(cmake/add_FetchContent_MakeAvailable.cmake)
|
||||
endif()
|
||||
|
||||
|
||||
# Accumulator library
|
||||
# This is header only, so could be replaced with git submodules or FetchContent
|
||||
find_package(Boost REQUIRED)
|
||||
# Adds Boost::boost
|
||||
|
||||
# Formatting library
|
||||
FetchContent_Declare(
|
||||
fmtlib
|
||||
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
|
||||
GIT_TAG 5.3.0
|
||||
)
|
||||
FetchContent_MakeAvailable(fmtlib)
|
||||
# Adds fmt::fmt
|
||||
|
||||
# The compiled library code is here
|
||||
add_subdirectory(src)
|
||||
|
||||
# The executable code is here
|
||||
add_subdirectory(apps)
|
||||
|
||||
# Testing only available if this is the main app
|
||||
# Emergency override MODERN_CMAKE_BUILD_TESTING provided as well
|
||||
if((CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME OR MODERN_CMAKE_BUILD_TESTING) AND BUILD_TESTING)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
44
examples/extended-project/README.md
Normal file
44
examples/extended-project/README.md
Normal file
@ -0,0 +1,44 @@
|
||||
This is an example project using CMake.
|
||||
|
||||
The requirements are:
|
||||
|
||||
* CMake 3.11 or better; 3.14+ highly recommended.
|
||||
* A C++17 compatible compiler
|
||||
* The Boost libararies (header only part is fine)
|
||||
* Git
|
||||
* Doxygen (optional)
|
||||
|
||||
To configure:
|
||||
|
||||
```bash
|
||||
cmake -S . -B build
|
||||
```
|
||||
|
||||
Add `-GNinja` if you have Ninja.
|
||||
|
||||
To build:
|
||||
|
||||
```bash
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
To test (`--target` can be written as `-t` in CMake 3.15+):
|
||||
|
||||
```bash
|
||||
cmake --build build --target test
|
||||
```
|
||||
|
||||
To build docs (requires Doxygen, output in `build/docs/html`):
|
||||
|
||||
```bash
|
||||
cmake --build build --target docs
|
||||
```
|
||||
|
||||
To use an IDE, such as Xcode:
|
||||
|
||||
```bash
|
||||
cmake -S . -B xbuild -GXcode
|
||||
cmake --open xbuild
|
||||
```
|
||||
|
||||
The CMakeLists show off several useful design patters for CMake.
|
4
examples/extended-project/apps/CMakeLists.txt
Normal file
4
examples/extended-project/apps/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
add_executable(app app.cpp)
|
||||
target_compile_features(app PRIVATE cxx_std_17)
|
||||
|
||||
target_link_libraries(app PRIVATE modern_library fmt::fmt)
|
17
examples/extended-project/apps/app.cpp
Normal file
17
examples/extended-project/apps/app.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include <modern/lib.hpp>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
|
||||
int main() {
|
||||
std::vector<double> input = {1.2, 2.3, 3.4, 4.5};
|
||||
|
||||
auto [mean, moment] = accumulate_vector(input);
|
||||
|
||||
fmt::print("Mean: {}, Moment: {}\n", mean, moment);
|
||||
|
||||
return 0;
|
||||
}
|
0
examples/extended-project/cmake/FindSomeLib.cmake
Normal file
0
examples/extended-project/cmake/FindSomeLib.cmake
Normal file
@ -0,0 +1,8 @@
|
||||
macro(FetchContent_MakeAvailable NAME)
|
||||
FetchContent_GetProperties(${NAME})
|
||||
if(NOT ${NAME}_POPULATED)
|
||||
FetchContent_Populate(${NAME})
|
||||
add_subdirectory(${${NAME}_SOURCE_DIR} ${${NAME}_BINARY_DIR})
|
||||
endif()
|
||||
endmacro()
|
||||
|
9
examples/extended-project/docs/CMakeLists.txt
Normal file
9
examples/extended-project/docs/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
||||
set(DOXYGEN_EXTRACT_ALL YES)
|
||||
set(DOXYGEN_BUILTIN_STL_SUPPORT YES)
|
||||
|
||||
doxygen_add_docs(docs
|
||||
modern/lib.hpp
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/mainpage.md"
|
||||
WORKING_DIRECTORY
|
||||
"${PROJECT_SOURCE_DIR}/include"
|
||||
)
|
11
examples/extended-project/docs/mainpage.md
Normal file
11
examples/extended-project/docs/mainpage.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Documentation for Modern Library {#mainpage}
|
||||
|
||||
This is the documentation for my simple example library.
|
||||
|
||||
It is good documentation because:
|
||||
|
||||
1. It exists.
|
||||
2. I wrote it.
|
||||
3. Everthing is documented (pretty easy since there's only one function)
|
||||
|
||||
The single provided function is `::accumulate_vector`.
|
14
examples/extended-project/include/modern/lib.hpp
Normal file
14
examples/extended-project/include/modern/lib.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
|
||||
/// \brief Accumulate a vector to produce the mean and the first moment of the distribution.
|
||||
///
|
||||
/// This computes the mean and the first moment of a vector of double values.
|
||||
///
|
||||
std::tuple<double, double> accumulate_vector(
|
||||
const std::vector<double>& values ///< The vector of values
|
||||
);
|
||||
|
||||
|
22
examples/extended-project/src/CMakeLists.txt
Normal file
22
examples/extended-project/src/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
# Note that headers are optional, and do not affect add_library, but they will not
|
||||
# show up in IDEs unless they are listed in add_library.
|
||||
|
||||
# Optionally glob, but only for CMake 3.12 or later:
|
||||
# file(GLOB HEADER_LIST CONFIGURE_DEPENDS "${ModernCMakeExample_SOURCE_DIR}/include/modern/*.hpp")
|
||||
set(HEADER_LIST "${ModernCMakeExample_SOURCE_DIR}/include/modern/lib.hpp")
|
||||
|
||||
# Make an automatic library - will be static or dynamic based on user setting
|
||||
add_library(modern_library lib.cpp ${HEADER_LIST})
|
||||
|
||||
# We need this directory, and users of our library will need it too
|
||||
target_include_directories(modern_library PUBLIC ../include)
|
||||
|
||||
# This depends on (header only) boost
|
||||
target_link_libraries(modern_library PRIVATE Boost::boost)
|
||||
|
||||
# All users of this library will need at least C++11
|
||||
target_compile_features(modern_library PUBLIC cxx_std_11)
|
||||
|
||||
# IDEs should put the headers in a nice place
|
||||
source_group(TREE "${PROJECT_SOURCE_DIR}/include" PREFIX "Header Files" FILES ${HEADER_LIST})
|
21
examples/extended-project/src/lib.cpp
Normal file
21
examples/extended-project/src/lib.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include <modern/lib.hpp>
|
||||
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/accumulators/accumulators.hpp>
|
||||
#include <boost/accumulators/statistics/stats.hpp>
|
||||
#include <boost/accumulators/statistics/mean.hpp>
|
||||
#include <boost/accumulators/statistics/moment.hpp>
|
||||
|
||||
namespace ba = boost::accumulators;
|
||||
|
||||
std::tuple<double, double> accumulate_vector(const std::vector<double>& values) {
|
||||
|
||||
ba::accumulator_set<double, ba::stats<ba::tag::mean, ba::tag::moment<2>>> acc;
|
||||
|
||||
std::for_each(std::begin(values), std::end(values), std::ref(acc));
|
||||
|
||||
return {ba::mean(acc), ba::moment<2>(acc)};
|
||||
}
|
22
examples/extended-project/tests/CMakeLists.txt
Normal file
22
examples/extended-project/tests/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
# Testing library
|
||||
FetchContent_Declare(
|
||||
catch2
|
||||
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
||||
GIT_TAG v2.9.1
|
||||
)
|
||||
FetchContent_MakeAvailable(catch2)
|
||||
# Adds Catch2::Catch2
|
||||
|
||||
# Tests need to be added as executables first
|
||||
add_executable(testlib testlib.cpp)
|
||||
|
||||
# I'm using C++17 in the test
|
||||
target_compile_features(testlib PRIVATE cxx_std_17)
|
||||
|
||||
# Should be linked to the main library, as well as the Catch2 testing library
|
||||
target_link_libraries(testlib PRIVATE modern_library Catch2::Catch2)
|
||||
|
||||
# If you register a test, then ctest and make test will run it.
|
||||
# You can also run examples and check the output, as well.
|
||||
add_test(NAME testlibtest COMMAND testlib) # Command can be a target
|
12
examples/extended-project/tests/testlib.cpp
Normal file
12
examples/extended-project/tests/testlib.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch2/catch.hpp>
|
||||
#include <modern/lib.hpp>
|
||||
|
||||
TEST_CASE( "Quick check", "[main]" ) {
|
||||
std::vector<double> values {1, 2., 3.};
|
||||
auto [mean, moment] = accumulate_vector(values);
|
||||
|
||||
REQUIRE( mean == 2.0 );
|
||||
REQUIRE( moment == Approx(4.666666) );
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ include_directories(ROOT_BUG)
|
||||
root_generate_dictionary(G__DictExample DictExample.h LINKDEF DictLinkDef.h)
|
||||
|
||||
add_library(DictExample SHARED DictExample.cxx DictExample.h G__DictExample.cxx)
|
||||
target_include_directories(DictExample PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
target_link_libraries(DictExample PUBLIC ROOT::Core Threads::Threads)
|
||||
|
||||
## [main]
|
||||
|
Loading…
x
Reference in New Issue
Block a user