CMake is a configuration program, sometimes called a "meta" build system. CMake generates a build system based on project, environment, and user-provided configuration information.
Step 0
https://cmake.org/cmake/help/latest/guide/tutorial/Before%20You%20Begin.html
-
-D: Define, preprocessing time -
-l: link time
$ cmake -G # select generator
$ cmake -S # source root dir, default current working dir
$ cmake -B # build dir, output generated build system
$ cmake --build # Build source by generated build system
# make
cmake -B build
cmake --build build && ./build/hello
Step 1
https://cmake.org/cmake/help/latest/guide/tutorial/Getting%20Started%20with%20CMake.html
CMakeLists.txt = CML = lists file
root CML
cmake_minimum_required(VERSION 3.23)
project(MyProjectName)
-
add_executable()oradd_library(): output type -
target_sources(): link input -> output -
target_link_libraries(): link outputs
add_executable()
This command creates a target.
Targets themselves are simply names, a handle to these collection of properties.
- The artifact kind (executable, lib, header collection, etc)
- Source files
- Include dirs
- Output File name
- Dependencies
- Flags (Compiler, Linker)
add_executable(MyProgram)
target_sources()
Now that we have a name for our target, we can start associating properties with it like source files we want to build and link.
target_sources(MyProgram
PRIVATE
main.cxx
)
Above PRIVATE(scope keyword) means main.cxx property only belongs to MyProgram and is not inheritable.
add_library()
We need to know about header files in order to build other parts of a given target.
add_library(MyLibrary)
target_sources(MyLibrary
PRIVATE
library_implementation.cxx
PUBLIC
FILE_SET myHeaders
TYPE HEADERS # HEADERS or MODULES
BASE_DIRS
include
FILES
include/library_header.h
)
PUBLIC: allows consumers of our library to "see" the library's header files.
Notably, if the FILE_SET name is the same as the type, we don't need to provide the TYPE field.
target_sources(MyLibrary
PRIVATE
library_implementation.cxx
PUBLIC
FILE_SET HEADERS
BASE_DIRS
include
FILES
include/library_header.h
)
target_link_libraries()
It describes relationships between targets generally.
target_link_libraries(MyProgram
PRIVATE
MyLibrary
)
scope
- PRIVATE
- INTERFACE
- PUBLIC
- The order here is only loosely relevant. That we call target_link_libraries() prior to defining MathFunctions with add_library() doesn't matter to CMake.
add_subdirectory()
allows us to incorporate CMLs located in subdirectories
When a CMakeLists.txt in a subdirectory is being processed by CMake all relative paths described in the subdirectory CML are relative to that subdirectory, not the top-level CML.
add_subdirectory(SubdirectoryName)
Step2
Data type
The only fundamental types in CMakeLang are strings and lists.
set(var "World!")
message("Hello ${var}")
List
Every object in CMake is a string, and lists are themselves strings which contain semicolons as separators.
set(list "A;B")
list(APPEND list "C")
message(${list}) # string
message("${list}") # list(string with ; format)
set(name "Minwook")
message(name) # name
message(${name}) # Minwook
message("${name}") # Minwook
set(arr "A;B;C")
message(arr) # arr
message(${arr}) # ABC
message("${arr}") # A;B;C
Boolean
Conditionals are entirely by convention of which strings are considered true and which are considered false
function() and macro()
In CMakeLang, both function() and macro() can "see" all the variables created in all the frames above them.
But, a macro() acts semantically like a text replacement, so side effects the macro creates are visible in their calling context. If we create or change a variable in a macro, the caller will see the change.
function() creates its own variable scope.
macro(MyMac)
set(MacVar "GLOBAL")
endmacro()
function(MyFunc)
set(FuncVar "LOCAL")
endfunction()
MyFunc()
MyMac()
message("${FuncVar}") # x
message("${MacVar}") # GLOBAL
Step3
The names created by -D flags and option() are not normal variables, they are cache variables.
option(COMPRESSION_SOFTWARE_USE_ZLIB "Support Zlib compression" ON)
option(COMPRESSION_SOFTWARE_USE_ZSTD "Support Zstd compression" ON)
if(COMPRESSION_SOFTWARE_USE_ZLIB)
# Same as before
# ...
$ cmake -B build \
-DCOMPRESSION_SOFTWARE_USE_ZLIB=OFF
...
I will use Zstd!
set() can also be used to manipulate cache variables, but will not change a variable which has already been created.
set(StickyCacheVariable "I will not change" CACHE STRING "") # Cache 1
set(StickyCacheVariable "Overwrite StickyCache" CACHE STRING "") # Cache 2
message("StickyCacheVariable: ${StickyCacheVariable}")
Because -D flags are processed before any other commands, they take precedence for setting the value of a cache variable.
$ cmake \
-DStickyCacheVariable="Commandline always wins" \
-P StickyCacheVariable.cmake
StickyCacheVariable: Commandline always wins
FYI, cache var cannot be changed, they can be shadowed by normal variables by set() and can be unset()
set(ShadowVariable "In the shadows" CACHE STRING "")
set(ShadowVariable "Hiding the cache variable")
message("ShadowVariable: ${ShadowVariable}")
unset(ShadowVariable)
unset(ShadowVariable)
unset(ShadowVariable)
message("ShadowVariable: ${ShadowVariable}")
$ cmake -P ShadowVariable.cmake
ShadowVariable: Hiding the cache variable
ShadowVariable: In the shadows
Top comments (0)