2. CMake¶
2.1. Formatting¶
2.1.1. No space between command and (
¶
# 'if' is not a keyword, it's a command, like 'execute_process'
if(var1 EQUAL var2)
command(...)
command(...)
endif()
2.1.2. Enclose path with double quotes¶
sugar_foo(sources "./sources")
# sources - local variable
# "./sources" - some directory
Next code produce error set_target_properties called with incorrect number of arguments
:
set(x "")
set_target_properties(foo PROPERTIES INCLUDE_DIRECTORIES ${x})
Works fine:
set(x "")
set_target_properties(foo PROPERTIES INCLUDE_DIRECTORIES "${x}")
Example with file(WRITE ...):
file(WRITE "${A}/path/to/file/with spaces/in" "message") # quotes required
file(WRITE "${A}/path/to/file/without-spaces/in" "message") # quotes not necessary but keep style the same
Note that quotes required for anything related to configure_file:
# script.cmake.in
file(WRITE ${filename} ${content})
set(filename "/path/with/spaces/A B C")
set(content "a b c")
configure_file(.../script.cmake.in .../script.cmake)
execute_process(COMMAND ${CMAKE_COMMAND} -P .../script.cmake ...)
Result will be creation of file /path/with/spaces/A
with content BCabc
.
Fix:
# script.cmake.in
file(WRITE "${filename}" "${content}")
or
# script.cmake.in
file(WRITE "@filename@" "@content@")
Quite the same happens in install(CODE
command:
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E echo hello ...)")
content of cmake_install.cmake
will be:
execute_process(COMMAND /.../bin/cmake.exe -E echo hello ...) # no quotes!
it means that if CMake is installed to path with spaces this command will not be executed.
2.2. Indentation¶
2 spaces is default indentation:
command(...)
command(...)
if(...)
# +2 spaces
command(...)
if(...)
# +2 spaces
endif()
endif()
function(...)
# +2 spaces
command(...)
endfunction()
Use 4 spaces for breaking long line:
command(short line)
command(
# +4 spaces
long line arg1 arg2
)
command(
# +4 spaces
very
long
line
arg1
arg2
arg3
)
alternatively same line can be kept for name-value:
command(
# +4 spaces
VALUE1 value1
VALUE2 value2
VALUE3 value3
# break long line with additional indentation
VALUE4
# +4 spaces
value4a
value4b
value4c
)
2.3. Naming¶
Lower case for commands:
if(A)
command(...)
endif()
Upper case for command specifiers:
list(APPEND list_var append_var)
Lower case for local variables (temps, parameters, ...):
foreach(x ${ARGV})
message(${x})
endforeach()
Upper case for global variables (like variables which Find-modules use/setup):
include(ModuleA) # define MODULE_A_MODE
if(MODULE_A_MODE)
command(...)
endif()
Lower case for function/macro names:
macro(do_foo)
command(...)
endmacro()
do_foo(...)
Start internal variable’s name with the _
in macro and headers:
macro(do_foo)
# command `macro` doesn't introducing new scope
# hence this `set` commands will pollute user's space
set(_value1 "...")
set(_value2 "...")
# ...
endmacro()
# MyModule.cmake
# same for the header
string(COMPARE EQUAL "${A}" "${B}" _is_equal)
if(_is_equal)
# ...
endif()
# variable _is_equal not defined
include(MyModule)
# variable _is_equal defined
Examples:
Note
Use functions when it’s possible!
To prevent collisions guard variable name should match path to the module:
# module flags/gcc.cmake from project Polly
if(DEFINED POLLY_FLAGS_GCC_CMAKE_)
return()
else()
set(POLLY_FLAGS_GCC_CMAKE_ 1)
endif()
# module cmake/Hunter from project Hunter
if(DEFINED HUNTER_CMAKE_HUNTER_)
return()
else()
set(HUNTER_CMAKE_HUNTER_ 1)
endif()
Note
Inspired by Google C++ Style Guide - The #define Guard
2.4. Pitfalls¶
2.4.1. STREQUAL¶
Usage of if(${A} STREQUAL ${B})
is not recommended, see
this SO question.
Preferable function is string:
string(COMPARE EQUAL "${A}" "${B}" result)
if(result)
message("...")
endif()
Note
Fixed in CMake 3.1 by CMP0054 policy
2.4.2. export(PACKAGE ...)¶
- Avoid modification of “global” space. See Bug 14849
- Disabling package registry
2.5. Library of CMake extra modules¶
- All defined functions/macros start with
<libname>_
(example) - no
message
command inside, only wrappers
<libname>_STATUS_PRINT
option controlmessage
output (default value isON
)<libname>_STATUS_DEBUG
option used for more verbose output and additional debug checks (default value isOFF
)
- one function/macro - one file
<include-name>
equal<function-name>
As the result: include only what you need, check that included function
is used by simple in-file search (and, of course, delete it if it’s not). If
you need to use sugar_foo_boo
function, just include sugar_foo_boo.cmake
:
include(sugar_foo_boo) # load sugar_foo_boo.cmake file with sugar_foo_boo function
sugar_foo_boo(some args) # use it
2.6. Note about wrappers¶
Probably some wrappers (like sugar_fatal_error) occupy more space than functionality it is wrapping (: The purpose of this functions is to make additional check. See the difference between this two misprints:
message(FATA_ERROR "SOS!") # Output will be: "FATA_ERRORSOS!", no error report...
include(sugar_fata_error) # include error will be reported
sugar_fata_error(...) # function not found error will be reported
2.7. iOS detection¶
Polly toolchain set IOS variable:
if(IOS)
# iOS code
endif()
also CMAKE_OSX_SYSROOT can be checked:
string(COMPARE EQUAL "${CMAKE_OSX_SYSROOT}" "iphoneos" is_ios)
2.8. Temporary directories¶
${PROJECT_BINARY_DIR}/_3rdParty/<libname>