summaryrefslogtreecommitdiffstats
path: root/cmake/CODESTYLE.md
diff options
context:
space:
mode:
Diffstat (limited to 'cmake/CODESTYLE.md')
-rw-r--r--cmake/CODESTYLE.md197
1 files changed, 197 insertions, 0 deletions
diff --git a/cmake/CODESTYLE.md b/cmake/CODESTYLE.md
new file mode 100644
index 0000000000..6dfc676d16
--- /dev/null
+++ b/cmake/CODESTYLE.md
@@ -0,0 +1,197 @@
+# Unofficial Qt CMake Coding Style
+
+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.
+
+## Common conventions
+
+* 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
+
+## Case Styles
+
+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` |
+
+## Indentation
+
+* When in doubt, follow local indentation
+* Prefer indenting with 4 spaces, e.g.
+
+```
+if(1)
+ if(2)
+ message("3")
+ endif()
+endif()
+
+```
+
+## Variables
+
+* 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`
+* cache variables should be shouty cased => `BUILD_SHARED_LIBS`
+ * Qt cache variables should be prefixed with `QT_`
+
+## Properties
+
+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`
+
+## Functions
+
+* 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
+* 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`
+ and `qt6_add_plugin`
+
+### Macros
+
+* 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`
+* Macro names should be snake cased => `qt_find_package`
+* Prefix macro local variables to avoid naming collisions => `__qt_find_package_arguments`
+
+## Commands
+
+Command names in CMake are case-insensitive, therefore:
+* Only use lower case command names e.g `add_executable(app main.cpp)` not `ADD_EXECUTABLE(app
+ main.cpp)`
+* Command flags / options are usually shouty cased => `file(GENERATE OUTPUT "foo" CONTENT "bar")`
+
+## 'If' command
+
+* Keep the parenthesis next to the `if()`, so don't write `if ()`. `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()
+```
+
+## find_package
+
+* 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.
+* Inside examples / projects (which only use public CMake API) use the regular `find_package`
+ command.
+
+## CMake Target names
+
+* 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 the `Qt::` 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```
+
+## Finding 3rd party libraries via Find modules (FindWrapFoo.cmake)
+
+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`
+