Skip to content

Commit 46f0b93

Browse files
authored
Using "modern" cmake (>= 3.0.0) (#2)
Using modern cmake (version >= 3.9)
1 parent c72df97 commit 46f0b93

20 files changed

+599
-189
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# builds
22
build*/*
3+
_build/
4+
_install/
35

46
# kdevelop
57
*.kdev4

CMakeLists.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
cmake_minimum_required(VERSION 2.6)
2-
project(Foo)
1+
# Set minimum version of CMake.
2+
cmake_minimum_required(VERSION 3.9)
33

4-
# Set variables
5-
include(${CMAKE_SOURCE_DIR}/cmake/SetEnv.cmake)
4+
# Set project name and version
5+
project(Foo VERSION 1.2.3)
6+
7+
# Set environment variables
8+
include(${PROJECT_SOURCE_DIR}/cmake/SetEnv.cmake)
69

710
# Library sources
811
add_subdirectory(${LIBRARY_FOLDER})
912

1013
# Library examples
1114
add_subdirectory(example_internal)
12-
13-
# Install targets
14-
include(${CMAKE_SOURCE_DIR}/cmake/InstallConfig.cmake)

README.md

Lines changed: 119 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,51 @@
11
cmake-example-library
22
=====================
33

4-
This is an example of how to install a library with CMake, and then use
5-
`find_package()` command to find it.
4+
CMake library example that can be found using `find_package()`.
5+
Using modern cmake (version >= 3.9).
66

7-
In this example, Foo is the library and Bar a binary (which uses the library).
7+
In this example, `Foo` is the library and `Bar` a binary (which uses the library).
88

9-
The **advantage** of this example is that it is auto-generated. You only need to change
10-
the *project name*.
119

12-
It is based on the these two examples:
13-
* [How to create a ProjectConfig.cmake file (cmake.org)]
14-
(http://www.cmake.org/Wiki/CMake/Tutorials/How_to_create_a_ProjectConfig.cmake_file)
15-
* [How to install a library (kde.org)]
16-
(https://projects.kde.org/projects/kde/kdeexamples/repository/revisions/master/show/buildsystem/HowToInstallALibrary)
10+
### Features
11+
12+
* The main **advantage** of this example is that it is _auto-generated_.
13+
You only need to change the _project name_, and add the files that need to
14+
be compiled in [foo/CMakeLists.txt](foo/CMakeLists.txt).
15+
16+
* Autogenetared library version file: `#include <foo/version.h>`
17+
18+
* `FOO_DEBUG` added on Debug. See [foo/foo.cpp#L7-L11](foo/foo.cpp#L7-L11).
19+
20+
* `CMAKE_DEBUG_POSTFIX = 'd'` (allowing `Debug` and `Release` to not collide).
21+
See [cmake/SetEnv.cmake#L17](cmake/SetEnv.cmake#L17).
22+
23+
* Static library as default (`BUILD_SHARED_LIBS=OFF`).
24+
See [cmake/SetEnv.cmake#L19-L21](cmake/SetEnv.cmake#L19-L21).
25+
26+
* `CMAKE_BUILD_TYPE` possible options in `cmake-gui`.
27+
For multi-config generator, `CMAKE_CONFIGURATION_TYPES` is set instead of
28+
`CMAKE_BUILD_TYPE`.
29+
See [cmake/SetEnv.cmake#L23-L48](cmake/SetEnv.cmake#L23-L48).
30+
31+
* `Uninstall` target.
32+
See [cmake/SetEnv.cmake#L104-L109](cmake/SetEnv.cmake#L104-L109).
33+
34+
* Always full RPATH (for shared libraries).
35+
See [cmake/SetEnv.cmake#L111-L132](cmake/SetEnv.cmake#L111-L132).
36+
37+
38+
### Usage
39+
40+
Once the library is installed (see
41+
"[how to compile?](https://github.com/pablospe/cmake-example-library/tree/moderm_cmake#how-to-compile)"), it can be found externally with
42+
`find_package(...)`:
43+
44+
find_package(Foo <VERSION> REQUIRED)
45+
target_link_libraries(... Foo::foo)
46+
47+
See [more details](https://github.com/pablospe/cmake-example-library/tree/moderm_cmake#how-to-use-the-library-as-dependency-in-an-external-project) below.
48+
1749

1850
### How to create a library from this example?
1951

@@ -25,12 +57,14 @@ Follow these steps:
2557
2658
* Change project name in the top-level `CMakeLists.txt`.
2759

60+
* Add `.cpp` and `.h` files in `foo/CMakeLists.txt`.
61+
2862
* [Optional] Set variables: `LIBRARY_NAME` and `LIBRARY_FOLDER`.
2963
If it is not set explicitally, project name in lowercase will be used.
3064
See `cmake/SetEnv.cmake` file to see the implementation details.
3165

32-
* [Optional] 'example_internal/' folder can be removed, it is the 'bar' example.
33-
In this case, remove the 'add_subdirectory(example_internal)' too.
66+
* [Optional] 'example_internal/' folder can be removed, it is the 'bar'
67+
example. In this case, remove the 'add_subdirectory(example_internal)' too.
3468

3569
### How to compile?
3670

@@ -43,14 +77,14 @@ Assume the following settings:
4377

4478
Example of a local installation:
4579

46-
> mkdir build
47-
> cd build
48-
> cmake -DCMAKE_INSTALL_PREFIX=../installed ..
49-
> make install
80+
> mkdir _build && cd _build
81+
> cmake -DCMAKE_INSTALL_PREFIX=../_install ..
82+
> cmake --build . --target install -j 8
83+
(equivalent to 'make install -j8' in linux)
5084

5185
Installed files:
5286

53-
> tree ../installed
87+
> tree ../_install
5488

5589
├── bin
5690
│   └── bar
@@ -59,32 +93,92 @@ Installed files:
5993
│   ├── foo.h
6094
│   └── version.h
6195
└── lib
62-
├── CMake
96+
├── cmake
6397
│   └── Foo
6498
│   ├── FooConfig.cmake
6599
│   ├── FooConfigVersion.cmake
66100
│   ├── FooTargets.cmake
67-
│   └── FooTargets-noconfig.cmake
68-
└── libfoo.so
101+
│   ├── FooTargets-debug.cmake
102+
│   └── FooTargets-release.cmake
103+
├── libfoo.a (Release)
104+
└── libfood.a (Debug)
69105

70106
Uninstall library:
71107

72108
> make uninstall
73109

110+
111+
#### Compilation scripts
112+
113+
Please check the following files for more complex compilation examples:
114+
- [build_linux.sh](build_linux.sh)
115+
- [build_win.sh](build_win.sh)
116+
- [build_ninja.sh](build_ninja.sh)
117+
118+
119+
### Static vs Shared
120+
121+
By default, a static library will be generated. Modify `BUILD_SHARED_LIBS` in
122+
order to change this behavior. For example,
123+
124+
> cd _build/
125+
> cmake -DBUILD_SHARED_LIBS=ON ..
126+
127+
128+
74129
### How to use the library (internally in subfolders)?
75130

76131
See the [example of internal subfolder](example_internal/).
77132

133+
78134
### How to use the library (as dependency) in an external project?
79135

80-
cmake_minimum_required(VERSION 2.6)
136+
See the [example of external project](example_external/).
137+
Once the library is intalled, cmake would be able to find it using
138+
`find_package(...)` command.
139+
140+
cmake_minimum_required(VERSION 3.9)
81141
project(Bar)
82142

83-
find_package(Foo REQUIRED)
84-
include_directories(${FOO_INCLUDE_DIRS})
143+
find_package(Foo 1.2.3 REQUIRED)
85144

86145
add_executable(bar bar.cpp)
87-
target_link_libraries(bar ${FOO_LIBRARIES})
146+
target_link_libraries(bar PRIVATE Foo::foo)
88147

89-
See the [example of external project](example_external/).
148+
Requirements will propagate automatically:
149+
* `Foo::foo` will link automatically,
150+
* headers can be included by C++ code like `#include <foo/foo.h>`,
151+
* `FOO_DEBUG=1` added on Debug,
152+
* `FOO_DEBUG=0` added otherwise.
153+
154+
155+
### How to use the library as submodule (using add_subdirectory)?
156+
157+
If `Foo` library is intended to be use as a Git submodule:
158+
159+
> git submodule add https://github.com/<user>/Foo Foo
160+
161+
In the `CMakeLists.txt` where the `Foo` submodule will be used,
162+
add the command **add_subdirectory(...)**:
163+
164+
[...]
165+
166+
# Add 'Foo' library as submodule
167+
add_subdirectory(Foo)
168+
169+
# Propagate usage requirements from Foo linked library
170+
target_link_libraries(<target> PRIVATE Foo::foo)
171+
172+
[...]
173+
174+
175+
### Documentation
176+
177+
Some ideas from:
178+
* https://github.com/forexample/package-example
90179

180+
Modern CMake tutorials (youtube):
181+
* C++Now 2017: Daniel Pfeifer
182+
[Effective CMake](https://www.youtube.com/watch?v=bsXLMQ6WgI)
183+
* CppCon 2018: Mateusz Pusz
184+
[Git, CMake, Conan - How to ship and reuse our C++ projects](https://www.youtube.com/watch?v=S4QSKLXdTtA)

build_example_external.sh

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#!/bin/bash
2+
3+
# This script can be called from anywhere and allows to build out of source.
4+
5+
# Determine script absolute path
6+
SCRIPT_ABS_PATH=$(readlink -f ${BASH_SOURCE[0]})
7+
SCRIPT_ABS_PATH=$(dirname ${SCRIPT_ABS_PATH})
8+
9+
# switch to script path
10+
cd ${SCRIPT_ABS_PATH}
11+
12+
# Choose build type
13+
BUILD_TYPE=Release
14+
BUILD_TYPE=Debug
15+
16+
# Choose build type
17+
BUILD_DIR=_build
18+
19+
# Choose install folder
20+
INSTALL_DIR=_install
21+
22+
# Options summary
23+
echo ""
24+
echo "BUILD_TYPE =" ${BUILD_TYPE}
25+
echo "BUILD_DIR =" ${SCRIPT_ABS_PATH}/example_external/${BUILD_DIR}/
26+
echo "INSTALL_DIR =" ${SCRIPT_ABS_PATH}/example_external/${INSTALL_DIR}/
27+
echo ""
28+
29+
30+
# ------------------------------
31+
# example_external (for testing)
32+
# ------------------------------
33+
printf "\n\n ----- example_external ----- \n\n"
34+
35+
# clean
36+
# rm -fr example_external/${BUILD_DIR}
37+
38+
SO=`uname`
39+
if [[ $SO == "Linux" ]]; then
40+
echo "Running on Linux"
41+
42+
# cmake
43+
cmake \
44+
-S ${SCRIPT_ABS_PATH}/example_external/ \
45+
-B ${SCRIPT_ABS_PATH}/example_external/${BUILD_DIR} \
46+
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
47+
-DFoo_DIR="${SCRIPT_ABS_PATH}/${INSTALL_DIR}/lib/cmake/Foo/"
48+
49+
# compile
50+
cmake \
51+
--build ${SCRIPT_ABS_PATH}/example_external/${BUILD_DIR} \
52+
-j 8
53+
54+
else
55+
echo "Running on Windows"
56+
57+
# cmake
58+
cmake \
59+
-S ${SCRIPT_ABS_PATH}/example_external/ \
60+
-B ${SCRIPT_ABS_PATH}/example_external/${BUILD_DIR} \
61+
-DFoo_DIR="${SCRIPT_ABS_PATH}/${INSTALL_DIR}/lib/cmake/Foo/"
62+
63+
# compile
64+
cmake \
65+
--build ${SCRIPT_ABS_PATH}/example_external/${BUILD_DIR} \
66+
--config ${BUILD_TYPE} \
67+
-j 8
68+
fi
69+
70+
71+
# run
72+
BIN_PATH=${SCRIPT_ABS_PATH}/example_external/${BUILD_DIR}
73+
echo ${BIN_PATH}/${BUILD_TYPE}/
74+
if [ -d "${BIN_PATH}/${BUILD_TYPE}" ]; then # multi-config generator
75+
BIN_PATH=${BIN_PATH}/${BUILD_TYPE}
76+
fi
77+
${BIN_PATH}/bar

build_linux.sh

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/bin/bash
2+
3+
# This script can be called from anywhere and allows to build out of source.
4+
5+
# Determine script absolute path
6+
SCRIPT_ABS_PATH=$(readlink -f ${BASH_SOURCE[0]})
7+
SCRIPT_ABS_PATH=$(dirname ${SCRIPT_ABS_PATH})
8+
9+
# switch to script path
10+
cd ${SCRIPT_ABS_PATH}
11+
12+
# Build type
13+
BUILD_TYPE=Release
14+
# BUILD_TYPE=Debug
15+
16+
# Build folder
17+
BUILD_DIR=_build
18+
19+
# Installation folder
20+
INSTALL_DIR=_install
21+
22+
# Library type
23+
BUILD_SHARED_LIBS=OFF # Static
24+
# BUILD_SHARED_LIBS=ON # Shared
25+
26+
# Options summary
27+
echo ""
28+
echo "BUILD_TYPE =" ${BUILD_TYPE}
29+
echo "BUILD_DIR =" ${SCRIPT_ABS_PATH}/${BUILD_DIR}/
30+
echo "INSTALL_DIR =" ${SCRIPT_ABS_PATH}/${INSTALL_DIR}/
31+
echo "BUILD_SHARED_LIBS =" ${BUILD_SHARED_LIBS}
32+
echo ""
33+
34+
35+
# clean
36+
# rm -fr ${BUILD_DIR} ${INSTALL_DIR}
37+
38+
# cmake
39+
cmake \
40+
-S . \
41+
-B ${BUILD_DIR} \
42+
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
43+
-DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} \
44+
-DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}"
45+
46+
# compile & install
47+
cmake \
48+
--build ${BUILD_DIR} \
49+
--target install \
50+
-j 8

0 commit comments

Comments
 (0)