CMake scripts are a bit of a wild west. Depending on when the code was written, there were different conventions as well as syntax facilities available. It's also unfortunate that there is no agreed upon CMake code formatter similar to clang-format. https://github.com/cheshirekow/cmake_format exists, but appears abandoned. We might look into using it at some point.
It's hard to convince people to use a certain code style for a language like CMake.
Nevertheless this short document aims to be a guideline for formatting CMake code within the Qt project.
- When in doubt, prefer the local code conventions of the function or file you are editing.
- Prefer functions over macros (macros have certain problems with parameter escaping)
- Prefix macro local variables to avoid naming collisions
For CMake identifiers we refer to following case styles in the text below.
Case style name | Example identifier |
---|---|
Snake case | moc_options |
Shouty case | INTERFACE_LINK_LIBRARIES |
Pascal case | QmlModels |
- When in doubt, follow local indentation
- Prefer indenting with 4 spaces, e.g.
if(1)
if(2)
message("3")
endif()
endif()
- local variables inside a function should be snake cased =>
list_of_arguments
- local variables inside a macro should be snake cased and have a unique prefix =>
__qt_list_of_packages
- If your macro is recursively called, you might need to prepend a dynamically computed prefix
to avoid overriding the same variable in nested calls =>
__qt_${dependency_name}_list_of_packages
- If your macro is recursively called, you might need to prepend a dynamically computed prefix
to avoid overriding the same variable in nested calls =>
- cache variables should be shouty cased =>
BUILD_SHARED_LIBS
- Qt cache variables should be prefixed with
QT_
- Qt cache variables should be prefixed with
Currently the Qt property naming is a bit of a mess. The upstream issue
https://gitlab.kitware.com/cmake/cmake/-/issues/19261 mentions that properties prefixed with
INTERFACE_
are reserved for CMake use.
Prefer to use snake case for new property names.
- Most upstream CMake properties are shouty cased =>
INTERFACE_LINK_LIBRARIES
- Properties created in Qt 5 times follow the same CMake convention =>
QT_ENABLED_PUBLIC_FEATURES
No such new properties should be added. - New Qt properties should be snake cased and prefixed with
qt_
=>qt_qmake_module_name
- Other internal Qt properties should be snake cased and prefixed with
_qt_
=>_qt_target_deps
- Function names should be snake cased =>
qt_add_module
- public Qt functions should be prefixed with
qt_
- internal Qt functions should be prefixed with
qt_internal_
- internal Qt functions that live in public CMake API files should be prefixed with
_qt_internal_
- some internal functions that live in public CMake API files are prefixed with
__qt_internal_
, prefer not to introduce such functions for now
- public Qt functions should be prefixed with
- If a public function takes more than 1 parameter, consider using
cmake_parse_arguments
- If an internal Qt function takes more than 1 parameter, consider using
qt_parse_all_arguments
- Public functions should usually have both a version-full and version-less name =>
qt_add_plugin
andqt6_add_plugin
- Try to avoid macros where a function can be used instead
- A common case when a macro can not be avoided is when it wraps a CMake macro e.g
find_package
=>qt_find_package
- A common case when a macro can not be avoided is when it wraps a CMake macro e.g
- Macro names should be snake cased =>
qt_find_package
- Prefix macro local variables to avoid naming collisions =>
__qt_find_package_arguments
Command names in CMake are case-insensitive, therefore:
- Only use lower case command names e.g
add_executable(app main.cpp)
notADD_EXECUTABLE(app main.cpp)
- Command flags / options are usually shouty cased =>
file(GENERATE OUTPUT "foo" CONTENT "bar")
-
Keep the parenthesis next to the
if()
, so don't writeif ()
.if
is a command name and like other command names e.g.find_package(foo bar)
the parenethesis are next to the name. -
To check that a variable is an empty string (and not just a falsy value, think Javascript's
foo === ""
) use the following snippet
if(var_name STREQUAL "")
message("${var_name}")
endif()
If you are not sure if the variable is defined, make sure to evaluate the variable name
if("${var_name}" STREQUAL "")
message("${var_name}")
endif()
- Falsy values are
0
,OFF
,NO
,FALSE
,N
,IGNORE
,NOTFOUND
, the empty string""
, or a string ending in-NOTFOUND
. To check that a variable is NOT falsy use the first suggested code snippet
# Nice and clean
if(var_name)
message("${var_name}")
endif()
# Wrong, can lead to problems due to double variable evaluation
if(${var_name})
message("${var_name}")
endif()
# Incorrect if you want to check for all falsy values. This only checks for string emptiness.
if("${var_name}" STREQUAL "")
message("${var_name}")
endif()
- To check if a variable is defined, use
if(DEFINED var_name)
endif()
- To compare a defined variable's contents to a string, use
if(var_name STREQUAL "my_string")
endif()
- To compare a defined variable's contents to another defined variable's contents, use
if(var_name STREQUAL var_name_2)
endif()
- To compare a possibly undefined variable make sure to explicitly evaluate it first
if("${var_name}" STREQUAL "my_string")
endif()
if("${var_name}" STREQUAL "${var_name_2}")
endif()
- Inside Qt module projects, use the private Qt-specific
qt_find_package
macro to look for dependencies.- Make sure to specify the
PROVIDED_TARGETS
option to properly register 3rd party target dependencies with Qt's internal build system.
- Make sure to specify the
- Inside examples / projects (which only use public CMake API) use the regular
find_package
command.
- Qt target names should be pascal cased =>
QuickParticles
. - When Qt is installed, the Qt CMake targets are put inside the
Qt6::
namespace. Associated versionless targets in theQt::
namespace are usually automatically created by appropriate functions (qt_internal_add_module
,qt_internal_add_tool
) - Wrapper 3rd party system libraries usually repeat the target name as the namespace e.g.
WrapSystemHarfbuzz::WrapSystemHarfbuzz
qmake-Qt uses configure.json
and configure.pri
and QT_LIBS_FOO
variables to implement a
mechnism that searches for system 3rd party libraries.
The equivalent CMake mechanism are Find modules (and CMake package Config files; not to be confused with pkg-config). Usually Qt provides wrapper Find modules that use any available upstream Find modules or Config package files.
The naming convention for the files are:
FindWrapFoo.cmake
=>FindWrapPCRE2.cmake
FindWrapSystemFoo.cmake
=>FindWrapSystemPCRE2.cmake