Skip to content

Latest commit

 

History

History
197 lines (150 loc) · 6.93 KB

CODESTYLE.md

File metadata and controls

197 lines (150 loc) · 6.93 KB

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