on
CMake Notes #1
A simple demo
A simple demo of what CMake does:
│ CMakeLists.txt
│ main.cpp
CMakeLists.txt
:
# CMake is not case-sensitive.
# Require a minimum version of cmake.
cmake_minimum_required(VERSION 3.1)
# Set the name of the project.
project(demo)
# Add an executable to the project using the specified source files.
ADD_EXECUTABLE(hello ${DIR_SRCS})
main.cpp
:
#include <stdio.h>
int main()
{
printf("hello cmake");
return 0;
}
What CMake does is to select a generator that generates the project based on these files above. For example, if a “Visual Studio 15 2017” generator is selected, CMake would first create a Visual Studio solution (.sln file and other auxiliary resources), then compile it into an executable file (hello.exe
) with compiler provided by Visual Studio.
Run the following commands:
-
List all usable generators for CMake
cmake -G
-
Configure and generate: generates a solution (if Visual Studio generator is selected)
mkdir build cd build cmake ..
-
Compile the executable file
cmake --build ..
Or we can use CMake GUI to build the project with separated “configure” and “generate” functions. In the configure stage, it looks up all CMakeList.txt
files and builds up an internal representation of the project. Then it generates the project files guided by the representation. In command line mode, cmake
command combines the two stages.
Multiple source files within the same directory
│ CMakeLists.txt
│ main.cpp
│ foo.h
│ foo.cpp
# CMakeFiles.txt
# ----------------------- version 1 -----------------------
cmake_minimum_required (VERSION 3.1)
project(demo)
ADD_EXECUTABLE(hello main.cpp foo.cpp)
# ----------------------- version 2 -----------------------
cmake_minimum_required (VERSION 3.1)
project(demo)
# look up all source files in the directory (main.cpp, math.cpp)
# and save them in variable DIR_SRCS
aux_source_directory(. DIR_SRCS)
ADD_EXECUTABLE(hello ${DIR_SRCS})
// ----------------------- main.cpp -----------------------
#include "foo.h"
int main()
{
doFoo();
return 0;
}
// ----------------------- foo.h -----------------------
#ifndef FOO
#define FOO
void doFoo();
#endif
// ----------------------- foo.cpp -----------------------
#include<iostream>
void doFoo() {
std::cout << "foo!\n";
}
Multiple source files within multiple directories
│ CMakeLists.txt
│ main.cpp
│
└─foo_stuff
CMakeList.txt
foo.cpp
foo.h
All sub-directories must contain its own CMakeList.txt
file.
# ----------------------- ./CMakeLists.txt -----------------------
cmake_minimum_required (VERSION 3.1)
project(demo)
aux_source_directory(. DIR_SRCS)
add_executable(hello ${DIR_SRCS})
# add subdirectory (for CMake to look up CMakeFiles.txt)
add_subdirectory(foo_stuff)
# next we need to add link library "FooLib"
# to the target "hello". The library FooLib is
# defined in the other CMakeLists.txt file
target_link_libraries(hello FooLib)
# ----------------------- ./foo_stuff/CMakeLists.txt -----------------------
aux_source_directory(. DIR_LIB_SRCS)
# a library instead of exe
add_library (FooLib ${DIR_LIB_SRCS})
// main.cpp
#include "foo_stuff/foo.h"
The generated VS solution should be something like this:
However, the library FooLib is not included in the “Additional Include Directories” of project “hello”, so foo.h
must be referenced following the directory tree of the source code.
To include the path of the library:
cmake_minimum_required (VERSION 3.1)
project(demo)
aux_source_directory(. DIR_SRCS)
add_executable(hello ${DIR_SRCS})
# add include directories to the build.
include_directories("${PROJECT_SOURCE_DIR}/foo_stuff")
add_subdirectory(foo_stuff)
target_link_libraries(hello FooLib)
Now the “Additional Include Directories” of project “hello” will look like this:
And in main.cpp
the library could be included without the relative path:
// main.cpp
#include "foo.h"
Custom compile options
│ CMakeLists.txt
│ config.h.in
│ main.cpp
├─foo_1
│ CMakeLists.txt
│ foo.cpp
│ foo.h
│
└─foo_2
CMakeLists.txt
foo.cpp
foo.h
Here, two FooLib libraries are provided in foo_1 and foo_2 respectively, both containing its foo.h
. If we want users to customize which FooLib to be compiled into the project, we can add options to CMake.
cmake_minimum_required (VERSION 3.1)
project(demo)
# Copy a file to another location and modify its contents.
configure_file(
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_SOURCE_DIR}/config.h"
)
# Provide an option that the user can optionally select
# Provides an option for the user to select as ``ON`` or ``OFF``
option(USE_FOO_1 "Use foo_1" ON)
if(USE_FOO_1)
include_directories("${PROJECT_SOURCE_DIR}/foo_1")
add_subdirectory(foo_1)
else()
include_directories("${PROJECT_SOURCE_DIR}/foo_2")
add_subdirectory(foo_2)
endif()
aux_source_directory(. DIR_SRCS)
add_executable(hello ${DIR_SRCS})
target_link_libraries(hello FooLib)
// config.h.in
#cmakedefine USE_FOO_1
After clicking “configure” in CMake, an extra option USE_FOO_1
is shown in the option list, which user can check/uncheck it to modify which FooLib to use.
If the option is checked, a config.h
file will be generated that looks like this:
#define USE_FOO_1
Otherwise, it looks like this:
/* #undef USE_FOO_1 */
Configure File
https://cmake.org/cmake/help/latest/command/configure_file.html
The configure_file
command copies the content of config file, set the options, and replace variables (@var@
or ${var}
) with their respective values.
Example:
// config.h.in
#cmakedefine CMAKEDEFINE_TEST
#define NORMAL_TEST @normal_test@
#define CACHE_ENTRY_TEST @cache_entry_test@
# cmake
option(CMAKEDEFINE_TEST "cmakedefine test" ON)
set(normal_test 123)
set(cache_entry_test "stuff" CACHE STRING "This is a test")
set(ENV{PATH} "/home/path/to")
In CMake GUI:
config.h
output:
#define CMAKEDEFINE_TEST
#define NORMAL_TEST 123
#define CACHE_ENTRY_STRING_TEST stuff
#define CACHE_ENTRY_PATH_TEST /home
Set
3 types of set
commands:
- Set normal variable
- Set cache entry
- Set environment variable
https://cmake.org/cmake/help/latest/command/set.html