diff options
Diffstat (limited to 'cmake/CODESTYLE.md')
-rw-r--r-- | cmake/CODESTYLE.md | 197 |
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` + |