diff --git a/.cmake-format.py b/.cmake-format.py new file mode 100644 index 0000000..d7539f5 --- /dev/null +++ b/.cmake-format.py @@ -0,0 +1,241 @@ +# ---------------------------------- +# Options affecting listfile parsing +# ---------------------------------- +with section("parse"): + + # Specify structure for custom cmake functions + additional_commands = { 'foo': { 'flags': ['BAR', 'BAZ'], + 'kwargs': {'DEPENDS': '*', 'HEADERS': '*', 'SOURCES': '*'}}} + + # Override configurations per-command where available + override_spec = {} + + # Specify variable tags. + vartags = [] + + # Specify property tags. + proptags = [] + +# ----------------------------- +# Options affecting formatting. +# ----------------------------- +with section("format"): + + # Disable formatting entirely, making cmake-format a no-op + disable = False + + # How wide to allow formatted cmake files + line_width = 100 + + # How many spaces to tab for indent + tab_size = 4 + + # If true, lines are indented using tab characters (utf-8 0x09) instead of + # space characters (utf-8 0x20). In cases where the layout would + # require a fractional tab character, the behavior of the fractional + # indentation is governed by + use_tabchars = False + + # If is True, then the value of this variable indicates how + # fractional indentions are handled during whitespace replacement. If set to + # 'use-space', fractional indentation is left as spaces (utf-8 0x20). If set + # to `round-up` fractional indentation is replaced with a single tab character + # (utf-8 0x09) effectively shifting the column to the next tabstop + fractional_tab_policy = 'use-space' + + # If an argument group contains more than this many sub-groups (parg or kwarg + # groups) then force it to a vertical layout. + max_subgroups_hwrap = 2 + + # If a positional argument group contains more than this many arguments, then + # force it to a vertical layout. + max_pargs_hwrap = 6 + + # If a cmdline positional group consumes more than this many lines without + # nesting, then invalidate the layout (and nest) + max_rows_cmdline = 2 + + # If true, separate flow control names from their parentheses with a space + separate_ctrl_name_with_space = False + + # If true, separate function names from parentheses with a space + separate_fn_name_with_space = False + + # If a statement is wrapped to more than one line, than dangle the closing + # parenthesis on its own line. + dangle_parens = True + + # If the trailing parenthesis must be 'dangled' on its on line, then align it + # to this reference: `prefix`: the start of the statement, `prefix-indent`: + # the start of the statement, plus one indentation level, `child`: align to + # the column of the arguments + dangle_align = 'prefix' + + # If the statement spelling length (including space and parenthesis) is + # smaller than this amount, then force reject nested layouts. + min_prefix_chars = 4 + + # If the statement spelling length (including space and parenthesis) is larger + # than the tab width by more than this amount, then force reject un-nested + # layouts. + max_prefix_chars = 10 + + # If a candidate layout is wrapped horizontally but it exceeds this many + # lines, then reject the layout. + max_lines_hwrap = 2 + + # What style line endings to use in the output. + line_ending = 'unix' + + # Format command names consistently as 'lower' or 'upper' case + command_case = 'canonical' + + # Format keywords consistently as 'lower' or 'upper' case + keyword_case = 'unchanged' + + # A list of command names which should always be wrapped + always_wrap = [] + + # If true, the argument lists which are known to be sortable will be sorted + # lexicographicall + enable_sort = True + + # If true, the parsers may infer whether or not an argument list is sortable + # (without annotation). + autosort = True + + # By default, if cmake-format cannot successfully fit everything into the + # desired linewidth it will apply the last, most aggressive attempt that it + # made. If this flag is True, however, cmake-format will print error, exit + # with non-zero status code, and write-out nothing + require_valid_layout = False + + # A dictionary mapping layout nodes to a list of wrap decisions. See the + # documentation for more information. + layout_passes = {} + +# ------------------------------------------------ +# Options affecting comment reflow and formatting. +# ------------------------------------------------ +with section("markup"): + + # What character to use for bulleted lists + bullet_char = '-' + + # What character to use as punctuation after numerals in an enumerated list + enum_char = '.' + + # If comment markup is enabled, don't reflow the first comment block in each + # listfile. Use this to preserve formatting of your copyright/license + # statements. + first_comment_is_literal = False + + # If comment markup is enabled, don't reflow any comment block which matches + # this (regex) pattern. Default is `None` (disabled). + literal_comment_pattern = None + + # Regular expression to match preformat fences in comments default= + # ``r'^\s*([`~]{3}[`~]*)(.*)$'`` + fence_pattern = '^\\s*([`~]{3}[`~]*)(.*)$' + + # Regular expression to match rulers in comments default= + # ``r'^\s*[^\w\s]{3}.*[^\w\s]{3}$'`` + ruler_pattern = '^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$' + + # If a comment line matches starts with this pattern then it is explicitly a + # trailing comment for the preceding argument. Default is '#<' + explicit_trailing_pattern = '#<' + + # If a comment line starts with at least this many consecutive hash + # characters, then don't lstrip() them off. This allows for lazy hash rulers + # where the first hash char is not separated by space + hashruler_min_length = 10 + + # If true, then insert a space between the first hash char and remaining hash + # chars in a hash ruler, and normalize its length to fill the column + canonicalize_hashrulers = True + + # enable comment markup parsing and reflow + enable_markup = True + +# ---------------------------- +# Options affecting the linter +# ---------------------------- +with section("lint"): + + # a list of lint codes to disable + disabled_codes = [] + + # regular expression pattern describing valid function names + function_pattern = '[0-9a-z_]+' + + # regular expression pattern describing valid macro names + macro_pattern = '[0-9A-Z_]+' + + # regular expression pattern describing valid names for variables with global + # (cache) scope + global_var_pattern = '[A-Z][0-9A-Z_]+' + + # regular expression pattern describing valid names for variables with global + # scope (but internal semantic) + internal_var_pattern = '[a-z][0-9a-z_]+' + + # regular expression pattern describing valid names for variables with local + # scope + local_var_pattern = '[a-z][a-z0-9_]+' + + # regular expression pattern describing valid names for privatedirectory + # variables + private_var_pattern = '[0-9a-z_]+' + + # regular expression pattern describing valid names for public directory + # variables + public_var_pattern = '[A-Z][0-9A-Z_]+' + + # regular expression pattern describing valid names for function/macro + # arguments and loop variables. + argument_var_pattern = '[a-z][a-z0-9_]+' + + # regular expression pattern describing valid names for keywords used in + # functions or macros + keyword_pattern = '[A-Z][0-9A-Z_]+' + + # In the heuristic for C0201, how many conditionals to match within a loop in + # before considering the loop a parser. + max_conditionals_custom_parser = 2 + + # Require at least this many newlines between statements + min_statement_spacing = 1 + + # Require no more than this many newlines between statements + max_statement_spacing = 2 + max_returns = 6 + max_branches = 12 + max_arguments = 8 + max_localvars = 15 + max_statements = 50 + +# ------------------------------- +# Options affecting file encoding +# ------------------------------- +with section("encode"): + + # If true, emit the unicode byte-order mark (BOM) at the start of the file + emit_byteorder_mark = False + + # Specify the encoding of the input file. Defaults to utf-8 + input_encoding = 'utf-8' + + # Specify the encoding of the output file. Defaults to utf-8. Note that cmake + # only claims to support utf-8 so be careful when using anything else + output_encoding = 'utf-8' + +# ------------------------------------- +# Miscellaneous configurations options. +# ------------------------------------- +with section("misc"): + + # A dictionary containing any per-command configuration overrides. Currently + # only `command_case` is supported. + per_command = {} + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ef3e08..ee89794 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -137,3 +137,39 @@ jobs: - name: Build project and run doxygen run: cmake --build --preset doxygen + + cmake_format: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install requirements + run: | + sudo apt-get update + sudo apt-get install -y cmake-format + + - name: Configure project + run: cmake --preset cmake-format + + - name: Build project and check CMake code formatting with cmake-format + run: cmake --build --preset cmake-format-check + + cmake_lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install requirements + run: | + sudo apt-get update + sudo apt-get install -y cmake-format + + - name: Configure project + run: cmake --preset cmake-lint + + - name: Build project and check CMake code with cmake-lint + run: cmake --build --preset cmake-lint diff --git a/BUILDING.md b/BUILDING.md index 0c3fc55..d362fec 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -19,6 +19,7 @@ This guide provides detailed instructions to build the project, namely how to co - [Thread sanitizer](#thread-sanitizer) - [Undefined behavior sanitizer](#undefined-behavior-sanitizer) - [Source code documentation](#source-code-documentation) +- [CMake coding style and format](#cmake-coding-style-and-format) ## Requirements @@ -37,6 +38,8 @@ The following are the code quality tools used by the project (only required for - Clang-format: code formatting. - Clang-tidy: code static analysis. - Doxygen: generation of documentation. +- cmake-format: CMake code formatting. +- cmake-lint: CMake code linting. Please consult the [code quality tools](./doc/code_quality_tools.md) documentation to know more details about some of those tools. @@ -108,7 +111,7 @@ Using the standard CMake commands: $ cd $ mkdir build-debug $ cd build-debug -$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCXXPROJT_BUILD_TESTS=ON +$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPROJT_BUILD_TESTS=ON $ cmake --build . -j 4 $ ctest ``` @@ -130,7 +133,7 @@ Using the standard CMake commands (with GCC compiler only): $ cd $ mkdir build-coverage $ cd build-coverage -$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCXXPROJT_ENABLE_COVERAGE=ON +$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPROJT_ENABLE_COVERAGE=ON $ cmake --build . --target coverage ``` @@ -156,7 +159,7 @@ Using the standard CMake commands: $ cd $ mkdir build-clang-format $ cd build-clang-format -$ cmake .. -DCXXPROJT_ENABLE_CLANG_FORMAT=ON +$ cmake .. -DCPROJT_ENABLE_CLANG_FORMAT=ON $ # To just check the files without modifying them. $ cmake --build . --target clang_format_check $ # To format the files. @@ -176,7 +179,7 @@ $ cmake --build --preset clang-format-apply These targets use clang-format to verify/apply the desired format of the code, and creates a report file in the `build-clang-format` directory (used build directory in this example), named as `clang-format-report.log`. -The build succeeds only if the source files are formatted accordingly to the [configuration](.clang-format) file, when using the target to only check the files. The project source files to be verified are configured through CMake. +The build succeeds only if the source files are formatted accordingly to the [configuration](.clang-format) file. The project source files to be verified are configured through CMake. Please consult the [code quality tools](./doc/code_quality_tools.md) documentation to know more details about clang-format. @@ -190,7 +193,7 @@ Using the standard CMake commands: $ cd $ mkdir build-clang-tidy $ cd build-clang-tidy -$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCXXPROJT_ENABLE_CLANG_TIDY=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ +$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPROJT_ENABLE_CLANG_TIDY=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ $ cmake --build . --target clang_tidy ``` @@ -224,7 +227,7 @@ Using the standard CMake commands: $ cd $ mkdir build-sanitizer-address $ cd build-sanitizer-address -$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCXXPROJT_BUILD_TESTS=ON -DCXXPROJT_ENABLE_ASAN=ON +$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPROJT_BUILD_TESTS=ON -DCPROJT_ENABLE_ASAN=ON $ cmake --build . -j 4 $ ctest ``` @@ -246,7 +249,7 @@ Using the standard CMake commands: $ cd $ mkdir build-sanitizer-leak $ cd build-sanitizer-leak -$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCXXPROJT_BUILD_TESTS=ON -DCXXPROJT_ENABLE_LSAN=ON +$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPROJT_BUILD_TESTS=ON -DCPROJT_ENABLE_LSAN=ON $ cmake --build . -j 4 $ ctest ``` @@ -268,7 +271,7 @@ Using the standard CMake commands (with clang compiler only): $ cd $ mkdir build-sanitizer-memory $ cd build-sanitizer-memory -$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCXXPROJT_BUILD_TESTS=ON -DCXXPROJT_ENABLE_MSAN=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ +$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPROJT_BUILD_TESTS=ON -DCPROJT_ENABLE_MSAN=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ $ cmake --build . -j 4 $ ctest ``` @@ -290,7 +293,7 @@ Using the standard CMake commands: $ cd $ mkdir build-sanitizer-thread $ cd build-sanitizer-thread -$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCXXPROJT_BUILD_TESTS=ON -DCXXPROJT_ENABLE_TSAN=ON +$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPROJT_BUILD_TESTS=ON -DCPROJT_ENABLE_TSAN=ON $ cmake --build . -j 4 $ ctest ``` @@ -312,7 +315,7 @@ Using the standard CMake commands: $ cd $ mkdir build-sanitizer-undefined $ cd build-sanitizer-undefined -$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCXXPROJT_BUILD_TESTS=ON -DCXXPROJT_ENABLE_UBSAN=ON +$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPROJT_BUILD_TESTS=ON -DCPROJT_ENABLE_UBSAN=ON $ cmake --build . -j 4 $ ctest ``` @@ -336,7 +339,7 @@ Using the standard CMake commands: $ cd $ mkdir build-doxygen $ cd build-doxygen -$ cmake .. -DCXXPROJT_ENABLE_DOXYGEN=ON +$ cmake .. -DCPROJT_ENABLE_DOXYGEN=ON $ cmake --build . --target doxygen ``` @@ -353,3 +356,57 @@ This target generates documentation from the source files using doxygen, in the This target only succeeds if the source files are correctly documented. The doxygen [configuration](./doxygen/Doxyfile.in) file in this project is prepared to be automatically configured through CMake, namely the source files from which documentation should be generated, as well as other parameters related to the project. Please consult the [code quality tools](./doc/code_quality_tools.md) documentation to know more details about doxygen. + +## CMake coding style and format + +To ensure consistency, cmake-format and cmake-lint are used to format and check the CMake code. + +Using the standard CMake commands for cmake-format: + +```sh +$ cd +$ mkdir build-cmake-format +$ cd build-cmake-format +$ cmake .. -DCPROJT_ENABLE_CMAKE_FORMAT=ON +$ # To just check the files without modifying them. +$ cmake --build . --target cmake_format_check +$ # To format the files. +$ cmake --build . --target cmake_format_apply +``` + +CMake Preset equivalent: + +```sh +$ cd +$ cmake --preset cmake-format +$ # To just check the files without modifying them. +$ cmake --build --preset cmake-format-check +$ # To format the files. +$ cmake --build --preset cmake-format-apply +``` + +These targets use cmake-format to verify/apply the desired format of the CMake code, and creates a report file in the `build-cmake-format` directory (used build directory in this example), named as `cmake-format-report.log`. + +Relatively to cmake-lint, using the standard CMake commands: + +```sh +$ cd +$ mkdir build-cmake-lint +$ cd build-cmake-lint +$ cmake .. -DCPROJT_ENABLE_CMAKE_LINT=ON +$ cmake --build . --target cmake_lint +``` + +CMake Preset equivalent: + +```sh +$ cd +$ cmake --preset cmake-lint +$ cmake --build --preset cmake-lint +``` + +The target uses cmake-lint to verify the desired lint options of the CMake code, and creates a report file in the `build-cmake-lint` directory (used build directory in this example), named as `cmake-lint-report.log`. + +The builds for the cmake-format and cmake-lint targets succeed only if the CMake files are formatted accordingly to the [configuration](.cmake-format.py) file. The CMake files to be verified are configured through CMake. + +Please consult the [code quality tools](./doc/code_quality_tools.md) documentation to know more details about cmake-format and cmake-lint. diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d67340..fba152a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,21 +9,22 @@ cmake_minimum_required(VERSION 3.21) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) include(ProjectVersion) -set(VERSION_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/version/version.hpp) -parse_project_version(${VERSION_FILE}) +set(version_file ${CMAKE_CURRENT_SOURCE_DIR}/src/version/version.hpp) +parse_project_version(${version_file}) -project(cpp_project_template +project( + cpp_project_template VERSION ${PROJECT_VERSION_BASE} LANGUAGES CXX DESCRIPTION "A C++ project template." ) -set(CXXPROJT_PROJECT_NAME ${PROJECT_NAME}) -set(CXXPROJT_PROJECT_VERSION ${PROJECT_VERSION_FULL}) +set(CPROJT_PROJECT_NAME ${PROJECT_NAME}) +set(CPROJT_PROJECT_VERSION ${PROJECT_VERSION_FULL}) -set(MAIN_PROJECT OFF) +set(is_main_project OFF) if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) - set(MAIN_PROJECT ON) + set(is_main_project ON) endif() # ---------------------------------------------------------------------------- @@ -36,45 +37,40 @@ validate_build_directory() # Project information # ---------------------------------------------------------------------------- include(ProjectGitInfo) -set(GIT_INFO_HEADER_TEMPLATE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/project_git_info.hpp.in") -set(GIT_INFO_HEADER_OUT_FILE "git_info.hpp") -set(GIT_INFO_HEADER_GUARD "CPP_PROJECT_TEMPLATE_GIT_INFO_HPP") -set(GIT_INFO_NAMESPACE "cpp_project_template") -generate_git_info_file( - ${GIT_INFO_HEADER_TEMPLATE_FILE} - ${GIT_INFO_HEADER_OUT_FILE} - ${GIT_INFO_HEADER_GUARD} - ${GIT_INFO_NAMESPACE} -) +set(header_template_file "${CMAKE_CURRENT_SOURCE_DIR}/cmake/project_git_info.hpp.in") +set(header_out_file "git_info.hpp") +set(header_guard "CPP_PROJECT_TEMPLATE_GIT_INFO_HPP") +set(namespace "cpp_project_template") +generate_git_info_file(${header_template_file} ${header_out_file} ${header_guard} ${namespace}) # ---------------------------------------------------------------------------- # Configuration options # ---------------------------------------------------------------------------- -option(CXXPROJT_BUILD_TESTS "Build tests" ${MAIN_PROJECT}) -option(CXXPROJT_ENABLE_COVERAGE "Enable code coverage analysis" OFF) -option(CXXPROJT_ENABLE_CLANG_FORMAT "Enable code formatting check with clang-format" OFF) -option(CXXPROJT_ENABLE_CLANG_TIDY "Enable code static analysis with clang-tidy" OFF) -option(CXXPROJT_ENABLE_ASAN "Enable Address Sanitizer" OFF) -option(CXXPROJT_ENABLE_LSAN "Enable Leak Sanitizer" OFF) -option(CXXPROJT_ENABLE_MSAN "Enable Memory Sanitizer" OFF) -option(CXXPROJT_ENABLE_TSAN "Enable Thread Sanitizer" OFF) -option(CXXPROJT_ENABLE_UBSAN "Enable Undefined Behavior Sanitizer" OFF) -option(CXXPROJT_ENABLE_DOXYGEN "Enable doxygen documentation generation" OFF) - -if(CXXPROJT_ENABLE_COVERAGE) - set(CXXPROJT_BUILD_TESTS ON) +option(CPROJT_BUILD_TESTS "Build tests" ${is_main_project}) +option(CPROJT_ENABLE_COVERAGE "Enable code coverage analysis" OFF) +option(CPROJT_ENABLE_CLANG_FORMAT "Enable code formatting with clang-format" OFF) +option(CPROJT_ENABLE_CLANG_TIDY "Enable code static analysis with clang-tidy" OFF) +option(CPROJT_ENABLE_ASAN "Enable Address Sanitizer" OFF) +option(CPROJT_ENABLE_LSAN "Enable Leak Sanitizer" OFF) +option(CPROJT_ENABLE_MSAN "Enable Memory Sanitizer" OFF) +option(CPROJT_ENABLE_TSAN "Enable Thread Sanitizer" OFF) +option(CPROJT_ENABLE_UBSAN "Enable Undefined Behavior Sanitizer" OFF) +option(CPROJT_ENABLE_DOXYGEN "Enable doxygen documentation generation" OFF) +option(CPROJT_ENABLE_CMAKE_FORMAT "Enable CMake code formatting with cmake-format" OFF) +option(CPROJT_ENABLE_CMAKE_LINT "Enable CMake lint with cmake-lint" OFF) + +if(CPROJT_ENABLE_COVERAGE) + set(CPROJT_BUILD_TESTS ON) endif() # ---------------------------------------------------------------------------- # CMake options # ---------------------------------------------------------------------------- -if(MAIN_PROJECT) - # Build type on single-configuration generators (e.g. Makefile or Ninja). - # Set default build type if build type not set. +if(is_main_project) if(NOT CMAKE_BUILD_TYPE) - set(DEFAULT_BUILD_TYPE Debug) - message(STATUS "No build type specified, setting it to '${DEFAULT_BUILD_TYPE}'") - set(CMAKE_BUILD_TYPE ${DEFAULT_BUILD_TYPE}) + set(default_build_type Debug) + message(STATUS "No build type specified, setting it to '${default_build_type}'") + set(CMAKE_BUILD_TYPE ${default_build_type}) endif() # Generates a JSON file containing the exact compiler commands. Useful for clang-based tools. @@ -85,7 +81,7 @@ endif() # Dependencies # ---------------------------------------------------------------------------- include(ProjectDependencies) -fetch_project_dependencies(${CXXPROJT_BUILD_TESTS}) +fetch_project_dependencies(${CPROJT_BUILD_TESTS}) # ---------------------------------------------------------------------------- # Compiler options @@ -99,114 +95,105 @@ set(CMAKE_CXX_EXTENSIONS OFF) include(CompilerOptions) -set(PROJECT_COMPILER_OPTIONS_LIBRARY compile_options) -set(COMPILATION_WARNINGS_AS_ERRORS ${MAIN_PROJECT}) +set(compiler_options_library compile_options) +set(compilation_warnings_as_errors ${is_main_project}) set_project_default_compiler_options( - ${PROJECT_COMPILER_OPTIONS_LIBRARY} - ${CXXPROJT_PROJECT_NAME} - ${COMPILATION_WARNINGS_AS_ERRORS} + ${compiler_options_library} ${CPROJT_PROJECT_NAME} ${compilation_warnings_as_errors} ) # ---------------------------------------------------------------------------- # Quality tools # ---------------------------------------------------------------------------- -set(SCRIPTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/scripts") +set(scripts_dir "${CMAKE_CURRENT_SOURCE_DIR}/scripts") -if(CXXPROJT_ENABLE_COVERAGE) +if(CPROJT_ENABLE_COVERAGE) include(CodeCoverage) - set(COVERAGE_EXCLUDE_PATTERNS - "/usr/include/*" - "/usr/local/*" - "*/_deps/*" - "${CMAKE_CURRENT_SOURCE_DIR}/test/*" + set(coverage_exclude_patterns + "/usr/include/*" "/usr/local/*" "*/_deps/*" "${CMAKE_CURRENT_SOURCE_DIR}/test/*" "${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp" ) - set(MIN_LINE_COVERAGE 90) - set(MIN_FUNCTION_COVERAGE 80) - set(COVERAGE_JOBS 4) - set(COVERAGE_CHECK_SCRIPT "${SCRIPTS_DIR}/coverage_report_checker.sh") + set(min_line_coverage 90) + set(min_function_coverage 80) + set(coverage_check_script "${scripts_dir}/coverage_report_checker.sh") enable_coverage( - ${PROJECT_COMPILER_OPTIONS_LIBRARY} - "${COVERAGE_EXCLUDE_PATTERNS}" - ${MIN_LINE_COVERAGE} - ${MIN_FUNCTION_COVERAGE} - ${COVERAGE_JOBS} - ${COVERAGE_CHECK_SCRIPT} + ${compiler_options_library} "${coverage_exclude_patterns}" ${min_line_coverage} + ${min_function_coverage} ${coverage_check_script} ) endif() -if(CXXPROJT_ENABLE_CLANG_FORMAT) +set(source_code_dirs "${CMAKE_CURRENT_SOURCE_DIR}/examples" "${CMAKE_CURRENT_SOURCE_DIR}/include" + "${CMAKE_CURRENT_SOURCE_DIR}/src" "${CMAKE_CURRENT_SOURCE_DIR}/test" +) + +if(CPROJT_ENABLE_CLANG_FORMAT) include(ClangFormat) - set(CLANG_FORMAT_DIRECTORIES - "${CMAKE_CURRENT_SOURCE_DIR}/examples" - "${CMAKE_CURRENT_SOURCE_DIR}/include" - "${CMAKE_CURRENT_SOURCE_DIR}/src" - "${CMAKE_CURRENT_SOURCE_DIR}/test" - ) - set(CLANG_FORMAT_LOG_FILE "clang-format-report.log") - enable_clang_format("${CLANG_FORMAT_DIRECTORIES}" ${CLANG_FORMAT_LOG_FILE}) + set(log_file "clang-format-report.log") + enable_clang_format("${source_code_dirs}" ${log_file}) endif() -if(CXXPROJT_ENABLE_CLANG_TIDY) +if(CPROJT_ENABLE_CLANG_TIDY) include(ClangTidy) - set(CLANG_TIDY_DIRECTORIES - "${CMAKE_CURRENT_SOURCE_DIR}/examples" - "${CMAKE_CURRENT_SOURCE_DIR}/include" - "${CMAKE_CURRENT_SOURCE_DIR}/src" - "${CMAKE_CURRENT_SOURCE_DIR}/test" - ) - set(CLANG_TIDY_LOG_FILE "clang-tidy-report.log") - enable_clang_tidy("${CLANG_TIDY_DIRECTORIES}" ${CLANG_TIDY_LOG_FILE}) + set(log_file "clang-tidy-report.log") + enable_clang_tidy("${source_code_dirs}" ${log_file}) endif() -if(CXXPROJT_ENABLE_ASAN - OR CXXPROJT_ENABLE_LSAN - OR CXXPROJT_ENABLE_MSAN - OR CXXPROJT_ENABLE_TSAN - OR CXXPROJT_ENABLE_UBSAN) +if(CPROJT_ENABLE_ASAN + OR CPROJT_ENABLE_LSAN + OR CPROJT_ENABLE_MSAN + OR CPROJT_ENABLE_TSAN + OR CPROJT_ENABLE_UBSAN +) include(Sanitizers) enable_sanitizers( - ${PROJECT_COMPILER_OPTIONS_LIBRARY} - ${CXXPROJT_ENABLE_ASAN} - ${CXXPROJT_ENABLE_LSAN} - ${CXXPROJT_ENABLE_MSAN} - ${CXXPROJT_ENABLE_TSAN} - ${CXXPROJT_ENABLE_UBSAN} + ${compiler_options_library} ${CPROJT_ENABLE_ASAN} ${CPROJT_ENABLE_LSAN} + ${CPROJT_ENABLE_MSAN} ${CPROJT_ENABLE_TSAN} ${CPROJT_ENABLE_UBSAN} ) endif() -if(CXXPROJT_ENABLE_DOXYGEN) +if(CPROJT_ENABLE_DOXYGEN) include(DoxygenDoc) - set(DOXYGEN_CONFIG_FILE "${CMAKE_CURRENT_SOURCE_DIR}/doxygen/Doxyfile.in") - set(DOXYGEN_PROJECT "C++ Project Template") - set(DOXYGEN_VERSION ${CXXPROJT_PROJECT_VERSION}) - set(DOXYGEN_BRIEF "A C++ project template using CMake") - set(DOXYGEN_INPUT_LIST - "${CMAKE_CURRENT_SOURCE_DIR}/examples" - "${CMAKE_CURRENT_SOURCE_DIR}/include" - "${CMAKE_CURRENT_SOURCE_DIR}/src" - "${CMAKE_CURRENT_SOURCE_DIR}/test" - ) - string(REPLACE ";" " " DOXYGEN_INPUT "${DOXYGEN_INPUT_LIST}") - set(DOXYGEN_OUT_DIR "${CMAKE_BINARY_DIR}/doxygen") - set(DOXYGEN_LOG_FILE "doxygen-report.log") + set(config_file "${CMAKE_CURRENT_SOURCE_DIR}/doxygen/Doxyfile.in") + set(doxygen_project "C++ Project Template") + set(doxygen_version ${CPROJT_PROJECT_VERSION}) + set(doxygen_brief "A C++ project template using CMake") + set(input_list "${source_code_dirs}") + string(REPLACE ";" " " doxygen_input "${input_list}") + set(doxygen_out_dir "${CMAKE_BINARY_DIR}/doxygen") + set(log_file "doxygen-report.log") enable_doxygen_doc( - ${DOXYGEN_CONFIG_FILE} - ${DOXYGEN_PROJECT} - ${DOXYGEN_VERSION} - ${DOXYGEN_BRIEF} - ${DOXYGEN_INPUT} - ${DOXYGEN_OUT_DIR} - ${DOXYGEN_LOG_FILE} + ${config_file} + ${doxygen_project} + ${doxygen_version} + ${doxygen_brief} + ${doxygen_input} + ${doxygen_out_dir} + ${log_file} ) endif() +set(cmake_dirs "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +list(APPEND cmake_dirs_to_check ${cmake_dirs}) +list(APPEND cmake_dirs_to_check ${source_code_dirs}) +set(cmake_files_to_check "${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt") + +if(CPROJT_ENABLE_CMAKE_FORMAT) + include(CmakeFormat) + set(log_file "cmake-format-report.log") + enable_cmake_format("${cmake_dirs_to_check}" "${cmake_files_to_check}" ${log_file}) +endif() + +if(CPROJT_ENABLE_CMAKE_LINT) + include(CmakeLint) + set(log_file "cmake-lint-report.log") + enable_cmake_lint("${cmake_dirs_to_check}" "${cmake_files_to_check}" ${log_file}) +endif() + # ---------------------------------------------------------------------------- # Build # ---------------------------------------------------------------------------- add_subdirectory(src) -if(CXXPROJT_BUILD_TESTS) +if(CPROJT_BUILD_TESTS) include(CTest) include(GoogleTest) enable_testing() @@ -217,21 +204,23 @@ endif() # Configuration report # ---------------------------------------------------------------------------- message(STATUS) -message(STATUS "${CXXPROJT_PROJECT_NAME} ${CXXPROJT_PROJECT_VERSION} configuration:") +message(STATUS "${CPROJT_PROJECT_NAME} ${CPROJT_PROJECT_VERSION} configuration:") message(STATUS "- CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}") message(STATUS "- CMAKE_CXX_COMPILER_ID = ${CMAKE_CXX_COMPILER_ID}") message(STATUS "- CMAKE_CXX_COMPILER_VERSION = ${CMAKE_CXX_COMPILER_VERSION}") message(STATUS "- CMAKE_CXX_STANDARD = ${CMAKE_CXX_STANDARD}") message(STATUS "- CMAKE_EXPORT_COMPILE_COMMANDS = ${CMAKE_EXPORT_COMPILE_COMMANDS}") -message(STATUS "- CXXPROJT_BUILD_TESTS = ${CXXPROJT_BUILD_TESTS}") -message(STATUS "- CXXPROJT_ENABLE_COVERAGE = ${CXXPROJT_ENABLE_COVERAGE}") -message(STATUS "- CXXPROJT_ENABLE_CLANG_FORMAT = ${CXXPROJT_ENABLE_CLANG_FORMAT}") -message(STATUS "- CXXPROJT_ENABLE_CLANG_TIDY = ${CXXPROJT_ENABLE_CLANG_TIDY}") -message(STATUS "- CXXPROJT_ENABLE_ASAN = ${CXXPROJT_ENABLE_ASAN}") -message(STATUS "- CXXPROJT_ENABLE_LSAN = ${CXXPROJT_ENABLE_LSAN}") -message(STATUS "- CXXPROJT_ENABLE_MSAN = ${CXXPROJT_ENABLE_MSAN}") -message(STATUS "- CXXPROJT_ENABLE_TSAN = ${CXXPROJT_ENABLE_TSAN}") -message(STATUS "- CXXPROJT_ENABLE_UBSAN = ${CXXPROJT_ENABLE_UBSAN}") -message(STATUS "- CXXPROJT_ENABLE_DOXYGEN = ${CXXPROJT_ENABLE_DOXYGEN}") -message(STATUS "- MAIN_PROJECT = ${MAIN_PROJECT}") +message(STATUS "- CPROJT_BUILD_TESTS = ${CPROJT_BUILD_TESTS}") +message(STATUS "- CPROJT_ENABLE_COVERAGE = ${CPROJT_ENABLE_COVERAGE}") +message(STATUS "- CPROJT_ENABLE_CLANG_FORMAT = ${CPROJT_ENABLE_CLANG_FORMAT}") +message(STATUS "- CPROJT_ENABLE_CLANG_TIDY = ${CPROJT_ENABLE_CLANG_TIDY}") +message(STATUS "- CPROJT_ENABLE_ASAN = ${CPROJT_ENABLE_ASAN}") +message(STATUS "- CPROJT_ENABLE_LSAN = ${CPROJT_ENABLE_LSAN}") +message(STATUS "- CPROJT_ENABLE_MSAN = ${CPROJT_ENABLE_MSAN}") +message(STATUS "- CPROJT_ENABLE_TSAN = ${CPROJT_ENABLE_TSAN}") +message(STATUS "- CPROJT_ENABLE_UBSAN = ${CPROJT_ENABLE_UBSAN}") +message(STATUS "- CPROJT_ENABLE_DOXYGEN = ${CPROJT_ENABLE_DOXYGEN}") +message(STATUS "- CPROJT_ENABLE_CMAKE_FORMAT = ${CPROJT_ENABLE_CMAKE_FORMAT}") +message(STATUS "- CPROJT_ENABLE_CMAKE_LINT = ${CPROJT_ENABLE_CMAKE_LINT}") +message(STATUS "- is_main_project = ${is_main_project}") message(STATUS) diff --git a/CMakePresets.json b/CMakePresets.json index 302d05e..b563715 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -15,7 +15,7 @@ "inherits": "default", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", - "CXXPROJT_BUILD_TESTS": "ON" + "CPROJT_BUILD_TESTS": "ON" }, "hidden": true }, @@ -26,7 +26,7 @@ "inherits": "default", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", - "CXXPROJT_BUILD_TESTS": "OFF" + "CPROJT_BUILD_TESTS": "OFF" }, "hidden": true }, @@ -123,7 +123,7 @@ "description": "Coverage configuration using the GCC compiler", "inherits": "debug-gcc", "cacheVariables": { - "CXXPROJT_ENABLE_COVERAGE": "ON" + "CPROJT_ENABLE_COVERAGE": "ON" } }, { @@ -132,7 +132,7 @@ "description": "Clang-format configuration", "inherits": "debug", "cacheVariables": { - "CXXPROJT_ENABLE_CLANG_FORMAT": "ON" + "CPROJT_ENABLE_CLANG_FORMAT": "ON" } }, { @@ -141,7 +141,7 @@ "description": "Clang-tidy configuration", "inherits": "debug-clang", "cacheVariables": { - "CXXPROJT_ENABLE_CLANG_TIDY": "ON" + "CPROJT_ENABLE_CLANG_TIDY": "ON" } }, { @@ -150,7 +150,7 @@ "description": "Address Sanitizer configuration using the clang compiler", "inherits": "debug-clang", "cacheVariables": { - "CXXPROJT_ENABLE_ASAN": "ON" + "CPROJT_ENABLE_ASAN": "ON" } }, { @@ -159,7 +159,7 @@ "description": "Leak Sanitizer configuration using the clang compiler", "inherits": "debug-clang", "cacheVariables": { - "CXXPROJT_ENABLE_LSAN": "ON" + "CPROJT_ENABLE_LSAN": "ON" } }, { @@ -168,7 +168,7 @@ "description": "Memory Sanitizer configuration using the clang compiler", "inherits": "debug-clang", "cacheVariables": { - "CXXPROJT_ENABLE_MSAN": "ON" + "CPROJT_ENABLE_MSAN": "ON" } }, { @@ -177,7 +177,7 @@ "description": "Thread Sanitizer configuration using the clang compiler", "inherits": "debug-clang", "cacheVariables": { - "CXXPROJT_ENABLE_TSAN": "ON" + "CPROJT_ENABLE_TSAN": "ON" } }, { @@ -186,7 +186,7 @@ "description": "Undefined Behavior Sanitizer configuration using the clang compiler", "inherits": "debug-clang", "cacheVariables": { - "CXXPROJT_ENABLE_UBSAN": "ON" + "CPROJT_ENABLE_UBSAN": "ON" } }, { @@ -195,7 +195,25 @@ "description": "Doxygen configuration", "inherits": "debug", "cacheVariables": { - "CXXPROJT_ENABLE_DOXYGEN": "ON" + "CPROJT_ENABLE_DOXYGEN": "ON" + } + }, + { + "name": "cmake-format", + "displayName": "CMake format", + "description": "CMake format configuration", + "inherits": "debug", + "cacheVariables": { + "CPROJT_ENABLE_CMAKE_FORMAT": "ON" + } + }, + { + "name": "cmake-lint", + "displayName": "CMake lint", + "description": "CMake lint configuration", + "inherits": "debug", + "cacheVariables": { + "CPROJT_ENABLE_CMAKE_LINT": "ON" } } ], @@ -329,6 +347,30 @@ "inherits": "default", "configurePreset": "doxygen", "targets": "doxygen" + }, + { + "name": "cmake-format-check", + "displayName": "CMake format check", + "description": "CMake format check build target", + "inherits": "default", + "configurePreset": "cmake-format", + "targets": "cmake_format_check" + }, + { + "name": "cmake-format-apply", + "displayName": "CMake format apply", + "description": "CMake format apply build target", + "inherits": "default", + "configurePreset": "cmake-format", + "targets": "cmake_format_apply" + }, + { + "name": "cmake-lint", + "displayName": "CMake lint", + "description": "CMake lint build target", + "inherits": "default", + "configurePreset": "cmake-lint", + "targets": "cmake_lint" } ], "testPresets": [ diff --git a/README.md b/README.md index 995cea7..43c1abf 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,11 @@ It offers a clear project structure, essential configurations, and integrates co - Examples of test suites and mocks. - Integration of code quality tools for: - Code coverage. - - Code formatting. + - Source code formatting. - Static analysis. - Sanitizers. - Generation of documentation. + - CMake code formatting and linting. - Code quality tools configured to be easily executed and ready for integration into CI/CD pipelines. - CI setup, using GitHub Actions, to build the project and run the tests on different operating systems and with different compilers. Additionally, the code quality tools used by this project are also executed in CI. - Automatic dependency fetching, using CMake, for easy integration of third-party libraries. @@ -46,12 +47,12 @@ The following procedure will help you to get started with this template: - Get a copy of this template. - Adjust the `CMakeLists` files to use your project files. -- Update prefix on the name of some CMake variables (`CXXPROJT_`) to use your project name. +- Update prefix on the name of some CMake variables (`CPROJT_`) to use your project name. - Remove unused files and directories. - Update include guards on header files. - Adjust copyrights in files. - Replace the license file with the one specific to your project. -- Adjust clang-format, clang-tidy, and doxygen configuration files, as well as some of its parameters automatically configured by CMake. +- Adjust clang-format, clang-tidy, doxygen and cmake-format/cmake-lint configuration files, as well as some of its parameters automatically configured by CMake. - Update the `CMakePresets.json` file (e.g., CMake variables defined there). - `Dockerfile` might be adjusted to your needs. - Update this README to have only the sections that make sense for your project. @@ -88,7 +89,7 @@ Using this project as example, the following is what this section could have: This project uses CMake as its build system, with support for CMake Presets to simplify configuration and building. -For detailed build instructions, including how to build the project, run tests, enable optional code quality tools (code coverage, sanitizers, static analysis, etc) and generate documentation, please see the [Building guide](BUILDING.md). +For detailed build instructions, including how to build the project, run tests, enable optional code quality tools (code coverage, code formatting, sanitizers, static analysis, etc) and generate documentation, please see the [Building guide](BUILDING.md). ## Examples @@ -149,7 +150,6 @@ This project contains this [CONTRIBUTING](./CONTRIBUTING.md) file, just for demo - Code quality tools: - Add cppcheck (code static analysis). - Add valgrind (memory checker). - - Add tool to check CMake code format. ## License diff --git a/cmake/ClangFormat.cmake b/cmake/ClangFormat.cmake index 6621f5a..4c3e7fd 100644 --- a/cmake/ClangFormat.cmake +++ b/cmake/ClangFormat.cmake @@ -5,72 +5,78 @@ # Enable code formatting using clang-format. # # The following targets are created: +# # - A target that just checks the files without modifying them. # - A target that formats the files. # # Parameters: -# DIRECTORIES: Directories to get the files. To make clang-format ignore certain files, -# .clang-format-ignore files can be created. If not present, all the available files -# (headers and C/C++ files) in these directories will be analyzed. -# LOG_FILE: Log file to be created with the clang-format output. -function(enable_clang_format DIRECTORIES LOG_FILE) +# +# - directories: Directories to get the files. To make clang-format ignore certain files, +# .clang-format-ignore files can be created. If not present, all the available files (headers and +# C/C++ files) in these directories will be analyzed. +# - log_file: Log file to be created with the clang-format output. +function(enable_clang_format directories log_file) message(CHECK_START "Enabling code formatting with clang-format") # Requirements. message(CHECK_START "Checking needed tools") - find_program(CLANG_FORMAT_PATH clang-format REQUIRED) + find_program(clang_format_path clang-format REQUIRED) execute_process( - COMMAND ${CLANG_FORMAT_PATH} --version - OUTPUT_VARIABLE CLANG_FORMAT_VERSION - ERROR_VARIABLE CLANG_FORMAT_VERSION + COMMAND ${clang_format_path} --version + OUTPUT_VARIABLE clang_format_version + ERROR_VARIABLE clang_format_version ) - message(STATUS "Clang-format: ${CLANG_FORMAT_VERSION}") + message(STATUS "Clang-format: ${clang_format_version}") message(CHECK_PASS "done") # Files to check. - set(FILES) - foreach(DIR IN LISTS DIRECTORIES) - if(EXISTS ${DIR}) + set(files) + foreach(dir IN LISTS directories) + if(EXISTS ${dir}) # Search recursively the files. - file(GLOB_RECURSE DIR_FILES - "${DIR}/*.h" "${DIR}/*.hpp" "${DIR}/*.ipp" "${DIR}/*.cpp" "${DIR}/*.c" + file( + GLOB_RECURSE + dir_files + "${dir}/*.h" + "${dir}/*.hpp" + "${dir}/*.ipp" + "${dir}/*.cpp" + "${dir}/*.c" ) - list(APPEND FILES ${DIR_FILES}) + list(APPEND files ${dir_files}) else() - message(WARNING "Directory ${DIR} does not exist") + message(WARNING "Directory ${dir} does not exist") endif() endforeach() # Generated files. - set(REPORT_FILE "${CMAKE_BINARY_DIR}/${LOG_FILE}") + set(report_file "${CMAKE_BINARY_DIR}/${log_file}") - if(FILES) - set(CLANG_FORMAT_CHECK_TARGET "clang_format_check") - add_custom_target(${CLANG_FORMAT_CHECK_TARGET} - COMMENT "Check code formatting using clang-format." + if(files) + add_custom_target( + clang_format_check + COMMENT "Check code formatting using clang-format" COMMAND ${CMAKE_COMMAND} -E echo "Running clang-format" - COMMAND ${CMAKE_COMMAND} -E echo "Report: ${REPORT_FILE}" - COMMAND ${CLANG_FORMAT_PATH} --verbose --dry-run -Werror --style=file - ${FILES} > ${REPORT_FILE} 2>&1 - BYPRODUCTS - ${REPORT_FILE} + COMMAND ${CMAKE_COMMAND} -E echo "Report: ${report_file}" + COMMAND ${clang_format_path} --verbose --dry-run -Werror --style=file ${files} > + ${report_file} 2>&1 + BYPRODUCTS ${report_file} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} VERBATIM ) - set(CLANG_FORMAT_APPLY_TARGET "clang_format_apply") - add_custom_target(${CLANG_FORMAT_APPLY_TARGET} - COMMENT "Apply code formatting using clang-format." + add_custom_target( + clang_format_apply + COMMENT "Apply code formatting using clang-format" COMMAND ${CMAKE_COMMAND} -E echo "Running clang-format" - COMMAND ${CMAKE_COMMAND} -E echo "Report: ${REPORT_FILE}" - COMMAND ${CLANG_FORMAT_PATH} --verbose --style=file -i ${FILES} > ${REPORT_FILE} 2>&1 - BYPRODUCTS - ${REPORT_FILE} + COMMAND ${CMAKE_COMMAND} -E echo "Report: ${report_file}" + COMMAND ${clang_format_path} --verbose --style=file -i ${files} > ${report_file} 2>&1 + BYPRODUCTS ${report_file} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} VERBATIM ) else() - message(WARNING "No files found in the provided directories for code formatting") + message(WARNING "No files found for code formatting") endif() message(CHECK_PASS "done") diff --git a/cmake/ClangTidy.cmake b/cmake/ClangTidy.cmake index 6f28056..15bd36c 100644 --- a/cmake/ClangTidy.cmake +++ b/cmake/ClangTidy.cmake @@ -5,60 +5,49 @@ # Enable code static analysis, using clang-tidy. # # Parameters: -# DIRECTORIES: Directories to get the files to be analyzed. -# LOG_FILE: Log file to be created with the clang-tidy output. -function(enable_clang_tidy DIRECTORIES LOG_FILE) +# +# - directories: Directories to get the files to be analyzed. +# - log_file: Log file to be created with the clang-tidy output. +function(enable_clang_tidy directories log_file) message(CHECK_START "Enabling code static analysis with clang-tidy") # Requirements. message(CHECK_START "Checking needed tools") - find_program(CLANG_TIDY_PATH clang-tidy REQUIRED) + find_program(clang_tidy_path clang-tidy REQUIRED) execute_process( - COMMAND ${CLANG_TIDY_PATH} --version - OUTPUT_VARIABLE CLANG_TIDY_VERSION - ERROR_VARIABLE CLANG_TIDY_VERSION + COMMAND ${clang_tidy_path} --version + OUTPUT_VARIABLE clang_tidy_version + ERROR_VARIABLE clang_tidy_version ) - message(STATUS "Clang-tidy: ${CLANG_TIDY_VERSION}") + message(STATUS "Clang-tidy: ${clang_tidy_version}") message(CHECK_PASS "done") if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") # Files to analyze. - set(FILES) - foreach(DIR IN LISTS DIRECTORIES) - if(EXISTS ${DIR}) + set(files) + foreach(dir IN LISTS directories) + if(EXISTS ${dir}) # Search recursively the files. - file(GLOB_RECURSE DIR_FILES - "${DIR}/*.cpp" "${DIR}/*.c" - ) - list(APPEND FILES ${DIR_FILES}) + file(GLOB_RECURSE dir_files "${dir}/*.cpp" "${dir}/*.c") + list(APPEND files ${dir_files}) else() - message(WARNING "Directory ${DIR} does not exist") + message(WARNING "Directory ${dir} does not exist") endif() endforeach() # Generated files. - set(REPORT_FILE "${CMAKE_BINARY_DIR}/${LOG_FILE}") - - if(FILES) - # List of commands. - set(CLANG_TIDY_LIST_CHECKS_CMD - ${CLANG_TIDY_PATH} --list-checks - ) - set(CLANG_TIDY_RUN_CMD - ${CLANG_TIDY_PATH} -p ${CMAKE_BINARY_DIR} ${FILES} - ) + set(report_file "${CMAKE_BINARY_DIR}/${log_file}") - # Target. - set(CLANG_TIDY_TARGET_NAME "clang_tidy") - add_custom_target(${CLANG_TIDY_TARGET_NAME} - COMMENT "Run code static analysis using clang-tidy." + if(files) + add_custom_target( + clang_tidy + COMMENT "Run code static analysis using clang-tidy" COMMAND ${CMAKE_COMMAND} -E echo "Listing clang-tidy checks" - COMMAND ${CLANG_TIDY_LIST_CHECKS_CMD} + COMMAND ${clang_tidy_path} --list-checks COMMAND ${CMAKE_COMMAND} -E echo "Running clang-tidy" - COMMAND ${CMAKE_COMMAND} -E echo "Report: ${REPORT_FILE}" - COMMAND ${CLANG_TIDY_RUN_CMD} > ${REPORT_FILE} 2>&1 - BYPRODUCTS - ${REPORT_FILE} + COMMAND ${CMAKE_COMMAND} -E echo "Report: ${report_file}" + COMMAND ${clang_tidy_path} -p ${CMAKE_BINARY_DIR} ${files} > ${report_file} 2>&1 + BYPRODUCTS ${report_file} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} VERBATIM ) @@ -66,9 +55,10 @@ function(enable_clang_tidy DIRECTORIES LOG_FILE) message(WARNING "No files found in the provided directories for clang-tidy") endif() else() - message(FATAL_ERROR - "Clang-tidy analysis requires the clang compiler, not available for ${CMAKE_CXX_COMPILER_ID}") - endif() + message(FATAL_ERROR "Clang-tidy analysis requires the clang compiler, not available for + ${CMAKE_CXX_COMPILER_ID}" + ) + endif() message(CHECK_PASS "done") endfunction() diff --git a/cmake/CmakeFormat.cmake b/cmake/CmakeFormat.cmake new file mode 100644 index 0000000..902899b --- /dev/null +++ b/cmake/CmakeFormat.cmake @@ -0,0 +1,74 @@ +# +# Copyright (C) 2025 Hugo Barbosa. +# + +# Enable CMake code formatting using cmake-format. +# +# The following targets are created: +# +# - A target that just checks the files without modifying them. +# - A target that formats the files. +# +# Parameters: +# +# - directories: Directories to get the files. +# - files: Specific files to be analyzed. +# - log_file: Log file to be created with the cmake-format output. +function(enable_cmake_format directories files log_file) + message(CHECK_START "Enabling CMake code formatting with cmake-format") + + # Requirements. + message(CHECK_START "Checking needed tools") + find_program(cmake_format_path cmake-format REQUIRED) + execute_process( + COMMAND ${cmake_format_path} --version + OUTPUT_VARIABLE cmake_format_version + ERROR_VARIABLE cmake_format_version + ) + message(STATUS "cmake-format: ${cmake_format_version}") + message(CHECK_PASS "done") + + # Files to check. + set(files_to_check) + foreach(dir IN LISTS directories) + if(EXISTS ${dir}) + # Search recursively the files. + file(GLOB_RECURSE dir_files "${dir}/*.cmake" "${dir}/CMakeLists.txt") + list(APPEND files_to_check ${dir_files}) + else() + message(WARNING "Directory ${dir} does not exist") + endif() + endforeach() + list(APPEND files_to_check ${files}) + + # Generated files. + set(report_file "${CMAKE_BINARY_DIR}/${log_file}") + + if(files_to_check) + add_custom_target( + cmake_format_check + COMMENT "Check CMake code formatting using cmake-format" + COMMAND ${CMAKE_COMMAND} -E echo "Running cmake-format" + COMMAND ${CMAKE_COMMAND} -E echo "Report: ${report_file}" + COMMAND ${cmake_format_path} --check ${files_to_check} > ${report_file} 2>&1 + BYPRODUCTS ${report_file} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + VERBATIM + ) + + add_custom_target( + cmake_format_apply + COMMENT "Apply CMake code formatting using cmake-format" + COMMAND ${CMAKE_COMMAND} -E echo "Running cmake-format" + COMMAND ${CMAKE_COMMAND} -E echo "Report: ${report_file}" + COMMAND ${cmake_format_path} -i ${files_to_check} > ${report_file} 2>&1 + BYPRODUCTS ${report_file} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + VERBATIM + ) + else() + message(WARNING "No files found for CMake code formatting") + endif() + + message(CHECK_PASS "done") +endfunction() diff --git a/cmake/CmakeLint.cmake b/cmake/CmakeLint.cmake new file mode 100644 index 0000000..06b1723 --- /dev/null +++ b/cmake/CmakeLint.cmake @@ -0,0 +1,58 @@ +# +# Copyright (C) 2025 Hugo Barbosa. +# + +# Enable CMake lint using cmake-lint. +# +# Parameters: +# +# - directories: Directories to get the files. +# - files: Specific files to be analyzed. +# - log_file: Log file to be created with the cmake-lint output. +function(enable_cmake_lint directories files log_file) + message(CHECK_START "Enabling CMake lint with cmake-lint") + + # Requirements. + message(CHECK_START "Checking needed tools") + find_program(cmake_lint_path cmake-lint REQUIRED) + execute_process( + COMMAND ${cmake_lint_path} --version + OUTPUT_VARIABLE cmake_lint_version + ERROR_VARIABLE cmake_lint_version + ) + message(STATUS "cmake-lint: ${cmake_lint_version}") + message(CHECK_PASS "done") + + # Files to check. + set(files_to_check) + foreach(dir IN LISTS directories) + if(EXISTS ${dir}) + # Search recursively the files. + file(GLOB_RECURSE dir_files "${dir}/*.cmake" "${dir}/CMakeLists.txt") + list(APPEND files_to_check ${dir_files}) + else() + message(WARNING "Directory ${dir} does not exist") + endif() + endforeach() + list(APPEND files_to_check ${files}) + + # Generated files. + set(report_file "${CMAKE_BINARY_DIR}/${log_file}") + + if(files_to_check) + add_custom_target( + cmake_lint + COMMENT "Check CMake code using cmake-lint" + COMMAND ${CMAKE_COMMAND} -E echo "Running cmake-lint" + COMMAND ${CMAKE_COMMAND} -E echo "Report: ${report_file}" + COMMAND ${cmake_lint_path} ${files_to_check} -o ${report_file} 2>&1 + BYPRODUCTS ${report_file} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + VERBATIM + ) + else() + message(WARNING "No files found for CMake lint") + endif() + + message(CHECK_PASS "done") +endfunction() diff --git a/cmake/CodeCoverage.cmake b/cmake/CodeCoverage.cmake index ded0273..6331c59 100644 --- a/cmake/CodeCoverage.cmake +++ b/cmake/CodeCoverage.cmake @@ -5,9 +5,10 @@ # Add the compiler options for code coverage to the provided target. # # Parameters: -# TARGET_NAME: Name of the target to add coverage compiler options. -function(add_coverage_compiler_options TARGET_NAME) - set(GCC_COVERAGE_OPTIONS +# +# - target_name: Name of the target to add coverage compiler options. +function(add_coverage_compiler_options target_name) + set(gcc_coverage_options # Compile and link code instrumented for coverage analysis. --coverage # Produce debugging information. @@ -23,29 +24,27 @@ function(add_coverage_compiler_options TARGET_NAME) ) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(${TARGET_NAME} - INTERFACE ${GCC_COVERAGE_OPTIONS} - ) - target_link_libraries(${TARGET_NAME} - INTERFACE gcov - ) - message(STATUS "Added coverage compiler options for target ${TARGET_NAME}") + target_compile_options(${target_name} INTERFACE ${gcc_coverage_options}) + target_link_libraries(${target_name} INTERFACE gcov) + message(STATUS "Added coverage compiler options for target ${target_name}") else() message(FATAL_ERROR "Coverage only for GCC, not available for ${CMAKE_CXX_COMPILER_ID}") - endif() + endif() endfunction() # Enable code coverage for the provided target and create coverage target. # # Parameters: -# TARGET_NAME: Name of the target to add coverage compiler options. -# EXCLUDE_PATTERNS: Patterns to be excluded from the coverage analysis. -# MIN_LINE_COVERAGE: Minimum lines coverage value to succeed. -# MIN_FUNCTION_COVERAGE: Minimum functions coverage value to succeed. -# JOBS: Number of jobs for compilation. -# COV_CHECK_SCRIPT: Coverage report checker script path. -function(enable_coverage TARGET_NAME EXCLUDE_PATTERNS MIN_LINE_COVERAGE MIN_FUNCTION_COVERAGE JOBS COV_CHECK_SCRIPT) - message(CHECK_START "Enabling code coverage for target ${TARGET_NAME}") +# +# - target_name: Name of the target to add coverage compiler options. +# - exclude_patterns: Patterns to be excluded from the coverage analysis. +# - min_line_coverage: Minimum lines coverage value to succeed. +# - min_function_coverage: Minimum functions coverage value to succeed. +# - cov_check_script: Coverage report checker script path. +function(enable_coverage target_name exclude_patterns min_line_coverage min_function_coverage + cov_check_script +) + message(CHECK_START "Enabling code coverage for target ${target_name}") if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") message(WARNING "Code coverage in a non-Debug build may be misleading") @@ -53,109 +52,72 @@ function(enable_coverage TARGET_NAME EXCLUDE_PATTERNS MIN_LINE_COVERAGE MIN_FUNC # Requirements. message(CHECK_START "Checking needed tools") - find_program(GCOV_PATH gcov REQUIRED) - find_program(LCOV_PATH lcov REQUIRED) + find_program(gcov_path gcov REQUIRED) + find_program(lcov_path lcov REQUIRED) execute_process( - COMMAND ${LCOV_PATH} --version - OUTPUT_VARIABLE LCOV_VERSION - ERROR_VARIABLE LCOV_VERSION + COMMAND ${lcov_path} --version + OUTPUT_VARIABLE lcov_version + ERROR_VARIABLE lcov_version ) - message(STATUS "LCOV: ${LCOV_VERSION}") - find_program(GENHTML_PATH genhtml REQUIRED) + message(STATUS "LCOV: ${lcov_version}") + find_program(genhtml_path genhtml REQUIRED) message(CHECK_PASS "done") # Compiler options. message(CHECK_START "Adding coverage compiler options") - add_coverage_compiler_options(${TARGET_NAME}) + add_coverage_compiler_options(${target_name}) message(CHECK_PASS "done") # Excludes. - set(LCOV_EXCLUDES "") - foreach(PATTERN IN LISTS EXCLUDE_PATTERNS) - message(STATUS "Excluding pattern: ${PATTERN}") - list(APPEND LCOV_EXCLUDES "${PATTERN}") + set(lcov_excludes "") + foreach(pattern IN LISTS exclude_patterns) + message(STATUS "Excluding pattern: ${pattern}") + list(APPEND lcov_excludes "${pattern}") endforeach() - list(REMOVE_DUPLICATES LCOV_EXCLUDES) - + list(REMOVE_DUPLICATES lcov_excludes) + # LCOV base directory. - set(LCOV_BASE_DIR ${PROJECT_SOURCE_DIR}) + set(lcov_base_dir ${PROJECT_SOURCE_DIR}) # Generated files. - set(COVERAGE_TARGET_NAME "coverage") - set(COVERAGE_BASE_FILE "${COVERAGE_TARGET_NAME}-base.info") - set(COVERAGE_CAPTURE_FILE "${COVERAGE_TARGET_NAME}-capture.info") - set(COVERAGE_TOTAL_FILE "${COVERAGE_TARGET_NAME}-total.info") - set(COVERAGE_FILTERED_FILE "${COVERAGE_TARGET_NAME}-filtered.info") - set(COVERAGE_REPORT_DIR "${COVERAGE_TARGET_NAME}") - set(COVERAGE_REPORT_FILE "${COVERAGE_TARGET_NAME}/index.html") - - # List of commands. - # Build and test. - set(BUILD_CMD - ${CMAKE_COMMAND} --build . -j ${JOBS} - ) - set(TEST_CMD - ${CMAKE_CTEST_COMMAND} --output-on-failure - ) - # LCOV. - set(LCOV_CLEAN_CMD - ${LCOV_PATH} --directory . -b ${LCOV_BASE_DIR} --zerocounters - ) - set(LCOV_BASELINE_CMD - ${LCOV_PATH} --directory . -b ${LCOV_BASE_DIR} --capture --initial - --output-file ${COVERAGE_BASE_FILE} --ignore-errors mismatch --ignore-errors unused - ) - set(LCOV_CAPTURE_CMD - ${LCOV_PATH} --directory . -b ${LCOV_BASE_DIR} --capture - --output-file ${COVERAGE_CAPTURE_FILE} --ignore-errors mismatch --ignore-errors unused - ) - set(LCOV_TOTAL_CMD - ${LCOV_PATH} --add-tracefile ${COVERAGE_BASE_FILE} --add-tracefile ${COVERAGE_CAPTURE_FILE} - --output-file ${COVERAGE_TOTAL_FILE} - ) - set(LCOV_FILTER_CMD - ${LCOV_PATH} --remove ${COVERAGE_TOTAL_FILE} ${LCOV_EXCLUDES} - --output-file ${COVERAGE_FILTERED_FILE} --ignore-errors mismatch --ignore-errors unused - ) - # HTML report. - set(GENHTML_REPORT_CMD - ${GENHTML_PATH} ${COVERAGE_FILTERED_FILE} --output-directory ${COVERAGE_REPORT_DIR} - --legend --show-details - ) - # Coverage report checker. - set(COV_CHECK_SCRIPT_CMD - ${COV_CHECK_SCRIPT} -b ${LCOV_PATH} -r ${COVERAGE_FILTERED_FILE} -l ${MIN_LINE_COVERAGE} - -f ${MIN_FUNCTION_COVERAGE} - ) + set(coverage_target "coverage") + set(base_file "${coverage_target}-base.info") + set(capture_file "${coverage_target}-capture.info") + set(total_file "${coverage_target}-total.info") + set(filtered_file "${coverage_target}-filtered.info") + set(report_dir "${coverage_target}") + set(report_file "${coverage_target}/index.html") - # Target. - add_custom_target(${COVERAGE_TARGET_NAME} - COMMENT "Run code coverage analysis." + set(jobs 4) + add_custom_target( + ${coverage_target} + COMMENT "Run code coverage analysis" COMMAND ${CMAKE_COMMAND} -E echo "Cleaning coverage data" - COMMAND ${LCOV_CLEAN_CMD} - COMMAND ${CMAKE_COMMAND} -E echo "Building project using ${JOBS} jobs" - COMMAND ${BUILD_CMD} + COMMAND ${lcov_path} --directory . -b ${lcov_base_dir} --zerocounters + COMMAND ${CMAKE_COMMAND} -E echo "Building project using ${jobs} jobs" + COMMAND ${CMAKE_COMMAND} --build . -j ${jobs} COMMAND ${CMAKE_COMMAND} -E echo "Creating coverage baseline" - COMMAND ${LCOV_BASELINE_CMD} + COMMAND ${lcov_path} --directory . -b ${lcov_base_dir} --capture --initial --output-file + ${base_file} --ignore-errors mismatch --ignore-errors unused COMMAND ${CMAKE_COMMAND} -E echo "Running tests" - COMMAND ${TEST_CMD} + COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure COMMAND ${CMAKE_COMMAND} -E echo "Generating code coverage information" - COMMAND ${LCOV_CAPTURE_CMD} - COMMAND ${LCOV_TOTAL_CMD} - COMMAND ${LCOV_FILTER_CMD} + COMMAND ${lcov_path} --directory . -b ${lcov_base_dir} --capture --output-file + ${capture_file} --ignore-errors mismatch --ignore-errors unused + COMMAND ${lcov_path} --add-tracefile ${base_file} --add-tracefile ${capture_file} + --output-file ${total_file} + COMMAND ${lcov_path} --remove ${total_file} ${lcov_excludes} --output-file ${filtered_file} + --ignore-errors mismatch --ignore-errors unused COMMAND ${CMAKE_COMMAND} -E echo "Generating HTML code coverage report" - COMMAND ${GENHTML_REPORT_CMD} - COMMAND ${CMAKE_COMMAND} -E echo "Coverage report: ${CMAKE_BINARY_DIR}/${COVERAGE_REPORT_FILE}" + COMMAND ${genhtml_path} ${filtered_file} --output-directory ${report_dir} --legend + --show-details + COMMAND ${CMAKE_COMMAND} -E echo "Coverage report: ${CMAKE_BINARY_DIR}/${report_file}" COMMAND ${CMAKE_COMMAND} -E echo "Checking code coverage report:" - COMMAND ${CMAKE_COMMAND} -E echo "- Minimum line coverage: ${MIN_LINE_COVERAGE}" - COMMAND ${CMAKE_COMMAND} -E echo "- Minimum function coverage: ${MIN_FUNCTION_COVERAGE}" - COMMAND ${COV_CHECK_SCRIPT_CMD} - BYPRODUCTS - ${COVERAGE_BASE_FILE} - ${COVERAGE_CAPTURE_FILE} - ${COVERAGE_TOTAL_FILE} - ${COVERAGE_FILTERED_FILE} - ${COVERAGE_REPORT_FILE} + COMMAND ${CMAKE_COMMAND} -E echo "- Minimum line coverage: ${min_line_coverage}" + COMMAND ${CMAKE_COMMAND} -E echo "- Minimum function coverage: ${min_function_coverage}" + COMMAND ${cov_check_script} -b ${lcov_path} -r ${filtered_file} -l ${min_line_coverage} -f + ${min_function_coverage} + BYPRODUCTS ${base_file} ${capture_file} ${total_file} ${filtered_file} ${report_file} WORKING_DIRECTORY ${CMAKE_BINARY_DIR} VERBATIM ) diff --git a/cmake/CompilerOptions.cmake b/cmake/CompilerOptions.cmake index f8fd747..27cd17a 100644 --- a/cmake/CompilerOptions.cmake +++ b/cmake/CompilerOptions.cmake @@ -5,15 +5,18 @@ # Add the default compiler options to the provided target. # # Parameters: -# TARGET_NAME: Target name to add the compiler options. -# WARNINGS_AS_ERRORS: Option to set warnings as errors. -function(add_default_compiler_options TARGET_NAME WARNINGS_AS_ERRORS) - set(MSVC_OPTIONS +# +# - target_name: Target name to add the compiler options. +# - warnings_as_errors: Option to set warnings as errors. +function(add_default_compiler_options target_name warnings_as_errors) + set(mscl_options + # ~~~ # Displays level 1 to level 4 (informational) warnings. + # ~~~ /W4 ) - set(CLANG_OPTIONS + set(clang_options # Enable most warning messages. -Wall # Enable some extra warnings. @@ -46,8 +49,8 @@ function(add_default_compiler_options TARGET_NAME WARNINGS_AS_ERRORS) -Wimplicit-fallthrough ) - set(GCC_OPTIONS - ${CLANG_OPTIONS} + set(gcc_options + ${clang_options} # Warn when the indentation of the code does not reflect the block structure. -Wmisleading-indentation # Warn about duplicated conditions in an if-else-if chain. @@ -62,40 +65,35 @@ function(add_default_compiler_options TARGET_NAME WARNINGS_AS_ERRORS) -Wsuggest-override ) - if(WARNINGS_AS_ERRORS) + if(warnings_as_errors) message(STATUS "Compilation warnings will be treated as errors") - list(APPEND MSVC_OPTIONS /WX) - list(APPEND CLANG_OPTIONS -Werror) - list(APPEND GCC_OPTIONS -Werror) + list(APPEND mscl_options /WX) + list(APPEND clang_options -Werror) + list(APPEND gcc_options -Werror) endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - set(COMPILER_OPTIONS ${MSVC_OPTIONS}) + set(compiler_options ${mscl_options}) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - set(COMPILER_OPTIONS ${CLANG_OPTIONS}) + set(compiler_options ${clang_options}) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(COMPILER_OPTIONS ${GCC_OPTIONS}) + set(compiler_options ${gcc_options}) else() message(AUTHOR_WARNING "No compiler options set for ${CMAKE_CXX_COMPILER_ID}") endif() - target_compile_options(${TARGET_NAME} - INTERFACE ${COMPILER_OPTIONS} - ) + target_compile_options(${target_name} INTERFACE ${compiler_options}) endfunction() -# Set the default compiler options to the provided library. -# The library will be created by this function. +# Set the default compiler options to the provided library, which will be created by this function. # # Parameters: -# LIBRARY_NAME: Name of the library to add the compiler options. -# NAMESPACE: Namespace for the library. -# WARNINGS_AS_ERRORS: Option to set warnings as errors. -function(set_project_default_compiler_options LIBRARY_NAME NAMESPACE WARNINGS_AS_ERRORS) - add_library(${LIBRARY_NAME} INTERFACE) - add_library(${NAMESPACE}::${LIBRARY_NAME} ALIAS ${LIBRARY_NAME}) - add_default_compiler_options( - ${LIBRARY_NAME} - ${WARNINGS_AS_ERRORS} - ) +# +# - library_name: Name of the library to add the compiler options. +# - namespace: Namespace for the library. +# - warnings_as_errors: Option to set warnings as errors. +function(set_project_default_compiler_options library_name namespace warnings_as_errors) + add_library(${library_name} INTERFACE) + add_library(${namespace}::${library_name} ALIAS ${library_name}) + add_default_compiler_options(${library_name} ${warnings_as_errors}) endfunction() diff --git a/cmake/DoxygenDoc.cmake b/cmake/DoxygenDoc.cmake index 5352001..e70af18 100644 --- a/cmake/DoxygenDoc.cmake +++ b/cmake/DoxygenDoc.cmake @@ -5,57 +5,58 @@ # Enable source code documentation generation, using doxygen. # # Parameters: -# CONFIG_FILE: Doxygen configuration file. -# PROJECT: Project name used to update the respective option in the configuration file. -# VERSION: Project version used to update the respective option in the configuration file. -# BRIEF: Project brief used to update the respective option in the configuration file. -# INPUT: Input directories/files, separated with spaces, used to update the respective option in -# the configuration file. -# OUT_DIR: Output directory used to update the respective option in the configuration file. -# LOG_FILE: Log file to be created with the doxygen output. -function(enable_doxygen_doc CONFIG_FILE PROJECT VERSION BRIEF INPUT OUT_DIR LOG_FILE) +# +# - config_file: Doxygen configuration file. +# - project: Project name used to update the respective option in the configuration file. +# - version: Project version used to update the respective option in the configuration file. +# - brief: Project brief used to update the respective option in the configuration file. +# - input: Input directories/files, separated with spaces, used to update the respective option in +# the configuration file. +# - out_dir: Output directory used to update the respective option in the configuration file. +# - log_file: Log file to be created with the doxygen output. +function( + enable_doxygen_doc + config_file + project + version + brief + input + out_dir + log_file +) message(CHECK_START "Enabling doxygen documentation generation") # Requirements. message(CHECK_START "Checking needed tools") - find_program(DOXYGEN_PATH doxygen REQUIRED) + find_program(doxygen_path doxygen REQUIRED) execute_process( - COMMAND ${DOXYGEN_PATH} --version - OUTPUT_VARIABLE DOXYGEN_VERSION - ERROR_VARIABLE DOXYGEN_VERSION + COMMAND ${doxygen_path} --version + OUTPUT_VARIABLE doxygen_version + ERROR_VARIABLE doxygen_version ) - message(STATUS "Doxygen: ${DOXYGEN_VERSION}") + message(STATUS "Doxygen: ${doxygen_version}") message(CHECK_PASS "done") # Generate the configuration file using the configuration variables. - set(DOXYGEN_PROJECT_NAME "\"${PROJECT}\"") - set(DOXYGEN_PROJECT_NUMBER "${VERSION}") - set(DOXYGEN_PROJECT_BRIEF "\"${BRIEF}\"") - set(DOXYGEN_INPUT "${INPUT}") - set(DOXYGEN_OUTPUT_DIRECTORY "${OUT_DIR}") - set(CONFIG_FILE_OUT "${CMAKE_BINARY_DIR}/Doxyfile") - configure_file(${CONFIG_FILE} ${CONFIG_FILE_OUT} @ONLY) - - set(REPORT_FILE "${CMAKE_BINARY_DIR}/${LOG_FILE}") - set(DOXYGEN_INDEX_FILE "${OUT_DIR}/index.html") + set(doxygen_project_name "\"${project}\"") + set(doxygen_project_number "${version}") + set(doxygen_project_brief "\"${brief}\"") + set(doxygen_input "${input}") + set(doxygen_output_directory "${out_dir}") + set(config_file_out "${CMAKE_BINARY_DIR}/Doxyfile") + configure_file(${config_file} ${config_file_out} @ONLY) - # List of commands. - set(DOXYGEN_CMD - ${DOXYGEN_PATH} ${CONFIG_FILE_OUT} - ) + set(report_file "${CMAKE_BINARY_DIR}/${log_file}") + set(doxygen_index_file "${out_dir}/index.html") - # Target. - set(DOXYGEN_TARGET_NAME "doxygen") - add_custom_target(${DOXYGEN_TARGET_NAME} - COMMENT "Generate doxygen documentation for project ${PROJECT}." + add_custom_target( + doxygen + COMMENT "Generate doxygen documentation for project ${project}" COMMAND ${CMAKE_COMMAND} -E echo "Running doxygen" - COMMAND ${CMAKE_COMMAND} -E echo "Report: ${REPORT_FILE}" - COMMAND ${CMAKE_COMMAND} -E echo "Generated doc index: ${DOXYGEN_INDEX_FILE}" - COMMAND ${DOXYGEN_CMD} > ${REPORT_FILE} 2>&1 - BYPRODUCTS - ${REPORT_FILE} - ${DOXYGEN_INDEX_FILE} - ${CONFIG_FILE_OUT} + COMMAND ${CMAKE_COMMAND} -E echo "Report: ${report_file}" + COMMAND ${CMAKE_COMMAND} -E echo "Generated doc index: ${doxygen_index_file}" + COMMAND ${doxygen_path} ${config_file_out} > ${report_file} 2>&1 + BYPRODUCTS ${report_file} ${doxygen_index_file} ${config_file_out} WORKING_DIRECTORY ${CMAKE_BINARY_DIR} VERBATIM ) diff --git a/cmake/ProjectDependencies.cmake b/cmake/ProjectDependencies.cmake index 95cb19c..2cd8ed2 100644 --- a/cmake/ProjectDependencies.cmake +++ b/cmake/ProjectDependencies.cmake @@ -13,7 +13,10 @@ function(fetch_googletest) GIT_TAG release-1.11.0 ) # For Windows: Prevent overriding the parent project's compiler/linker settings. - set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + set(GTEST_FORCE_SHARED_CRT + ON + CACHE BOOL "" FORCE + ) FetchContent_MakeAvailable(googletest) message(CHECK_PASS "done") endfunction() @@ -21,10 +24,11 @@ endfunction() # Fetch project dependencies. # # Parameters: -# BUILD_TESTS: Flag to indicate if the build includes tests. -function(fetch_project_dependencies BUILD_TESTS) +# +# - fetch_tests_deps: Flag to indicate if it should fetch also dependencies that are only for tests. +function(fetch_project_dependencies fetch_tests_deps) message(CHECK_START "Fetching project dependencies") - if(BUILD_TESTS) + if(fetch_tests_deps) fetch_googletest() endif() message(CHECK_PASS "done") diff --git a/cmake/ProjectGitInfo.cmake b/cmake/ProjectGitInfo.cmake index 5efcd87..04e7dca 100644 --- a/cmake/ProjectGitInfo.cmake +++ b/cmake/ProjectGitInfo.cmake @@ -7,55 +7,54 @@ set(GIT_INFO_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/git_info") # Generate project git information file, namely with the git branch and commit hash. # # Parameters: -# IN_FILE: Input file to be used as template to generate the git information file. -# OUT_FILE: Output file to be generated that will contain the git information (it will be placed -# in the build directory). -# HEADER_GUARD: Header guard to be used in the generated file. -# NAMESPACE: Namespace to be used in the generated file. -function(generate_git_info_file IN_FILE OUT_FILE HEADER_GUARD NAMESPACE) +# +# - in_file: Input file to be used as template to generate the git information file. +# - out_file: Output file to be generated that will contain the git information (it will be placed +# in the build directory). +# - header_guard: Header guard to be used in the generated file. +# - namespace: Namespace to be used in the generated file. +function(generate_git_info_file in_file out_file header_guard namespace) message(CHECK_START "Generating project git information file") # Requirements. message(CHECK_START "Checking needed tools") - find_program(GIT_PATH git REQUIRED) + find_program(git_path git REQUIRED) execute_process( - COMMAND ${GIT_PATH} --version - OUTPUT_VARIABLE GIT_VERSION - ERROR_VARIABLE GIT_VERSION + COMMAND ${git_path} --version + OUTPUT_VARIABLE git_version + ERROR_VARIABLE git_version ) - message(STATUS "Git: ${GIT_VERSION}") + message(STATUS "Git: ${git_version}") message(CHECK_PASS "done") execute_process( - COMMAND ${GIT_PATH} rev-parse --abbrev-ref HEAD - OUTPUT_VARIABLE GIT_BRANCH - OUTPUT_STRIP_TRAILING_WHITESPACE - COMMAND_ERROR_IS_FATAL ANY + COMMAND ${git_path} rev-parse --abbrev-ref HEAD + OUTPUT_VARIABLE git_branch + OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) execute_process( - COMMAND ${GIT_PATH} rev-parse --short HEAD - OUTPUT_VARIABLE GIT_COMMIT_HASH - OUTPUT_STRIP_TRAILING_WHITESPACE - COMMAND_ERROR_IS_FATAL ANY + COMMAND ${git_path} rev-parse --short HEAD + OUTPUT_VARIABLE git_commit_hash + OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) - message(STATUS "Project git branch: ${GIT_BRANCH}") - message(STATUS "Project git commit hash: ${GIT_COMMIT_HASH}") + message(STATUS "Project git branch: ${git_branch}") + message(STATUS "Project git commit hash: ${git_commit_hash}") file(MAKE_DIRECTORY ${GIT_INFO_OUT_DIR}) # Generate the output file using the input file and the configuration variables. - set(PROJECT_GIT_BRANCH "${GIT_BRANCH}") - set(PROJECT_GIT_COMMIT_HASH "${GIT_COMMIT_HASH}") - set(PROJECT_GIT_INFO_HEADER_GUARD "${HEADER_GUARD}") - set(PROJECT_GIT_INFO_NAMESPACE "${NAMESPACE}") - set(HEADER_FILE_OUT "${GIT_INFO_OUT_DIR}/${OUT_FILE}") - configure_file(${IN_FILE} ${HEADER_FILE_OUT} @ONLY) + set(project_git_branch "${git_branch}") + set(project_git_commit_hash "${git_commit_hash}") + set(project_git_info_header_guard "${header_guard}") + set(project_git_info_namespace "${namespace}") + set(header_file_out "${GIT_INFO_OUT_DIR}/${out_file}") + configure_file(${in_file} ${header_file_out} @ONLY) - message(STATUS "Generated project git information in file ${HEADER_FILE_OUT}") + message(STATUS "Generated project git information in file ${header_file_out}") message(CHECK_PASS "done") endfunction() @@ -63,7 +62,8 @@ endfunction() # Add include project git info directory to the provided target. # # Parameters: -# TARGET_NAME: Name of the target to add include git info directory. -function(target_include_git_info_directory TARGET_NAME) - target_include_directories(${TARGET_NAME} PRIVATE ${GIT_INFO_OUT_DIR}) +# +# - target_name: Name of the target to add include git info directory. +function(target_include_git_info_directory target_name) + target_include_directories(${target_name} PRIVATE ${GIT_INFO_OUT_DIR}) endfunction() diff --git a/cmake/ProjectVersion.cmake b/cmake/ProjectVersion.cmake index 053126f..b540b0d 100644 --- a/cmake/ProjectVersion.cmake +++ b/cmake/ProjectVersion.cmake @@ -5,81 +5,90 @@ # Extract version value from the provided version name and content. # # Parameters: -# OUT_VAR: Output variable that will contain the extracted version. -# VERSION_NAME: Version name (major, minor, etc). -# CONTENT: Content from which the version should be extracted. -macro(extract_version OUT_VAR VERSION_NAME CONTENT) - set(REGEX_PATTERN "${VERSION_NAME}[ \t]*=[ \t]*([0-9]+)") +# +# - out_var: Output variable that will contain the extracted version. +# - version_name: Version name (major, minor, etc). +# - content: Content from which the version should be extracted. +macro(EXTRACT_VERSION out_var version_name content) + set(regex_pattern "${version_name}[ \t]*=[ \t]*([0-9]+)") unset(CMAKE_MATCH_1) - string(REGEX MATCH "${REGEX_PATTERN}" _ "${CONTENT}") + string(REGEX MATCH "${regex_pattern}" _ "${content}") if(NOT DEFINED CMAKE_MATCH_1) - message(FATAL_ERROR "Version '${VERSION_NAME}' not found") + message(FATAL_ERROR "Version '${version_name}' not found") endif() - set(${OUT_VAR} "${CMAKE_MATCH_1}") + set(${out_var} "${CMAKE_MATCH_1}") endmacro() # Extract an optional version string from the provided version name and content. # # Parameters: -# OUT_VAR: Output variable that will contain the extracted version. -# VERSION_NAME: Version name (prerelease, etc). -# CONTENT: Content from which the version should be extracted. -macro(extract_optional_version_str OUT_VAR VERSION_NAME CONTENT) +# +# - out_var: Output variable that will contain the extracted version. +# - version_name: Version name (prerelease, etc). +# - content: Content from which the version should be extracted. +macro(EXTRACT_OPTIONAL_VERSION_STR out_var version_name content) # Match: key = "some string" or key = "". - set(REGEX_PATTERN "${VERSION_NAME}[ \t]*=[ \t]*\"([^\"]*)\"") + set(regex_pattern "${version_name}[ \t]*=[ \t]*\"([^\"]*)\"") unset(CMAKE_MATCH_1) unset(MATCH_LINE) # Match the full line to ensure it exists. - string(REGEX MATCH "${REGEX_PATTERN}" MATCH_LINE "${CONTENT}") + string(REGEX MATCH "${regex_pattern}" MATCH_LINE "${content}") if(MATCH_LINE STREQUAL "") - message(FATAL_ERROR "Version '${VERSION_NAME}' not found") + message(FATAL_ERROR "Version '${version_name}' not found") endif() # Extract the captured value (can be empty). - set(${OUT_VAR} "${CMAKE_MATCH_1}") + set(${out_var} "${CMAKE_MATCH_1}") endmacro() # Parse project version from the provided header file. # # Parameters: -# HEADER_FILE: Header file that contains the project version to be parsed. -function(parse_project_version HEADER_FILE) - message(CHECK_START "Parsing project version from header file ${HEADER_FILE}") +# +# - header_file: Header file that contains the project version to be parsed. +function(parse_project_version header_file) + message(CHECK_START "Parsing project version from header file ${header_file}") - if(NOT EXISTS "${HEADER_FILE}") - message(FATAL_ERROR "Header file not found: ${HEADER_FILE}") + if(NOT EXISTS "${header_file}") + message(FATAL_ERROR "Header file not found: ${header_file}") endif() - file(READ "${HEADER_FILE}" FILE_CONTENT) + file(READ "${header_file}" file_content) # Major version. - extract_version(MAJOR_VERSION "project_version_major" "${FILE_CONTENT}") + extract_version(major_version "project_version_major" "${file_content}") # Minor version. - extract_version(MINOR_VERSION "project_version_minor" "${FILE_CONTENT}") + extract_version(minor_version "project_version_minor" "${file_content}") # Patch version. - extract_version(PATCH_VERSION "project_version_patch" "${FILE_CONTENT}") + extract_version(patch_version "project_version_patch" "${file_content}") # Optional pre-release tag (string literal). - extract_optional_version_str(PRERELEASE_VERSION "project_version_prerelease" "${FILE_CONTENT}") + extract_optional_version_str(prerelease_version "project_version_prerelease" "${file_content}") # Version without prerelease. - set(BASE_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}") + set(base_version "${major_version}.${minor_version}.${patch_version}") # Version with prerelease if exists. - set(FULL_VERSION ${BASE_VERSION}) - if(PRERELEASE_VERSION) - set(FULL_VERSION "${BASE_VERSION}-${PRERELEASE_VERSION}") + set(full_version ${base_version}) + if(prerelease_version) + set(full_version "${base_version}-${prerelease_version}") endif() # Variables to be used in the parent scope. - set(PROJECT_VERSION_BASE ${BASE_VERSION} PARENT_SCOPE) - set(PROJECT_VERSION_FULL ${FULL_VERSION} PARENT_SCOPE) + set(PROJECT_VERSION_BASE + ${base_version} + PARENT_SCOPE + ) + set(PROJECT_VERSION_FULL + ${full_version} + PARENT_SCOPE + ) message(CHECK_PASS "done") endfunction() diff --git a/cmake/Sanitizers.cmake b/cmake/Sanitizers.cmake index 480bd70..a54df45 100644 --- a/cmake/Sanitizers.cmake +++ b/cmake/Sanitizers.cmake @@ -5,14 +5,15 @@ # Add the sanitize compiler options to the provided target. # # Parameters: -# TARGET_NAME: Name of the target to add sanitize compiler options. -# SANITIZE_LIST: List of the sanitize compiler options. -function(add_sanitize_compiler_options TARGET_NAME SANITIZE_LIST) - if(SANITIZE_LIST) - string(REPLACE ";" "," SANITIZE_OPTIONS "${SANITIZE_LIST}") - set(SANITIZE_COMPILER_OPTIONS +# +# - target_name: Name of the target to add sanitize compiler options. +# - sanitize_list: List of the sanitize compiler options. +function(add_sanitize_compiler_options target_name sanitize_list) + if(sanitize_list) + string(REPLACE ";" "," sanitize_options "${sanitize_list}") + set(sanitize_compiler_options # Runtime checks for various forms of undefined or suspicious behavior. - -fsanitize=${SANITIZE_OPTIONS} + -fsanitize=${sanitize_options} # Produce debugging information. -g # Reduce compilation time and make debugging produce the expected results. @@ -23,19 +24,18 @@ function(add_sanitize_compiler_options TARGET_NAME SANITIZE_LIST) ) if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") - message(FATAL_ERROR - "Sanitizers only for Clang or GCC, not available for ${CMAKE_CXX_COMPILER_ID}") + message( + FATAL_ERROR + "Sanitizers only for Clang or GCC, not available for ${CMAKE_CXX_COMPILER_ID}" + ) endif() - target_compile_options(${TARGET_NAME} - INTERFACE ${SANITIZE_COMPILER_OPTIONS} - ) - target_link_options(${TARGET_NAME} - INTERFACE ${SANITIZE_COMPILER_OPTIONS} - ) + target_compile_options(${target_name} INTERFACE ${sanitize_compiler_options}) + target_link_options(${target_name} INTERFACE ${sanitize_compiler_options}) - message(STATUS - "Added sanitize compiler options for target ${TARGET_NAME}: ${SANITIZE_OPTIONS}") + message( + STATUS "Added sanitize compiler options for target ${target_name}: ${sanitize_options}" + ) else() message(STATUS "No sanitize compiler options added") endif() @@ -44,59 +44,74 @@ endfunction() # Enable sanitizers for the provided target. # # Parameters: -# TARGET_NAME: Name of the target to add sanitizers options. -# ENABLE_ASAN: Flag to enable Address Sanitizer. -# ENABLE_LSAN: Flag to enable Leak Sanitizer. -# ENABLE_MSAN: Flag to enable Memory Sanitizer. -# ENABLE_TSAN: Flag to enable Thread Sanitizer. -# ENABLE_UBSAN: Flag to enable Undefined Behavior Sanitizer. -function(enable_sanitizers TARGET_NAME ENABLE_ASAN ENABLE_LSAN ENABLE_MSAN ENABLE_TSAN ENABLE_UBSAN) - message(CHECK_START "Enabling sanitizers for target ${TARGET_NAME}") +# +# - target_name: Name of the target to add sanitizers options. +# - enable_asan: Flag to enable Address Sanitizer. +# - enable_lsan: Flag to enable Leak Sanitizer. +# - enable_msan: Flag to enable Memory Sanitizer. +# - enable_tsan: Flag to enable Thread Sanitizer. +# - enable_ubsan: Flag to enable Undefined Behavior Sanitizer. +function( + enable_sanitizers + target_name + enable_asan + enable_lsan + enable_msan + enable_tsan + enable_ubsan +) + message(CHECK_START "Enabling sanitizers for target ${target_name}") if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") message(WARNING "Sanitizers in a non-Debug build may not give meaningful output") endif() - # Sanitizers. - set(SANITIZER_LIST) + set(sanitizer_list) - if(ENABLE_ASAN) - list(APPEND SANITIZER_LIST "address") + if(enable_asan) + list(APPEND sanitizer_list "address") endif() - if(ENABLE_LSAN) - list(APPEND SANITIZER_LIST "leak") + if(enable_lsan) + list(APPEND sanitizer_list "leak") endif() - if(ENABLE_MSAN) - if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if(enable_msan) + if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") message(FATAL_ERROR "Memory Sanitizer is only supported by Clang compiler") - elseif(ENABLE_ASAN OR ENABLE_LSAN OR ENABLE_TSAN) - message(FATAL_ERROR - "Memory Sanitizer does not work with Address, Leak or Thread Sanitizer enabled") + elseif( + enable_asan + OR enable_lsan + OR enable_tsan + ) + message( + FATAL_ERROR + "Memory Sanitizer does not work with Address, Leak or Thread Sanitizer enabled" + ) else() - message(STATUS - "Memory Sanitizer requires the instrumentation of all the code, including libc++") - list(APPEND SANITIZER_LIST "memory") + message(STATUS "Memory Sanitizer requires the instrumentation of all the code, including + libc++" + ) + list(APPEND sanitizer_list "memory") endif() endif() - if(ENABLE_TSAN) - if(ENABLE_ASAN OR ENABLE_LSAN) - message(FATAL_ERROR - "Thread Sanitizer does not work with Address or Leak Sanitizer enabled") + if(enable_tsan) + if(enable_asan OR enable_lsan) + message( + FATAL_ERROR "Thread Sanitizer does not work with Address or Leak Sanitizer enabled" + ) else() - list(APPEND SANITIZER_LIST "thread") + list(APPEND sanitizer_list "thread") endif() endif() - if(ENABLE_UBSAN) - list(APPEND SANITIZER_LIST "undefined") + if(enable_ubsan) + list(APPEND sanitizer_list "undefined") endif() - # Compiler options. message(CHECK_START "Adding sanitize options to compiler") - add_sanitize_compiler_options(${TARGET_NAME} "${SANITIZER_LIST}") + add_sanitize_compiler_options(${target_name} "${sanitizer_list}") message(CHECK_PASS "done") message(CHECK_PASS "done") diff --git a/cmake/SourceBuilds.cmake b/cmake/SourceBuilds.cmake index 4d66af4..38230c0 100644 --- a/cmake/SourceBuilds.cmake +++ b/cmake/SourceBuilds.cmake @@ -7,9 +7,8 @@ function(validate_build_directory) message(CHECK_START "Validating build directory") if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) - message(FATAL_ERROR - "In-source builds are not allowed.\n" - "Please use a separate build directory" + message(FATAL_ERROR "In-source builds are not allowed.\n" + "Please use a separate build directory" ) endif() diff --git a/cmake/project_git_info.hpp.in b/cmake/project_git_info.hpp.in index 5166f4b..3439a80 100644 --- a/cmake/project_git_info.hpp.in +++ b/cmake/project_git_info.hpp.in @@ -3,18 +3,18 @@ * @copyright Copyright (C) 2025 Hugo Barbosa. */ -#ifndef @PROJECT_GIT_INFO_HEADER_GUARD@ -#define @PROJECT_GIT_INFO_HEADER_GUARD@ +#ifndef @project_git_info_header_guard@ +#define @project_git_info_header_guard@ #include -namespace @PROJECT_GIT_INFO_NAMESPACE@ { +namespace @project_git_info_namespace@ { /// Project git branch. -inline constexpr std::string_view project_git_branch{"@PROJECT_GIT_BRANCH@"}; +inline constexpr std::string_view project_git_branch{"@project_git_branch@"}; /// Project git commit hash. -inline constexpr std::string_view project_git_commit_hash{"@PROJECT_GIT_COMMIT_HASH@"}; +inline constexpr std::string_view project_git_commit_hash{"@project_git_commit_hash@"}; -} // namespace @PROJECT_GIT_INFO_NAMESPACE@ +} // namespace @project_git_info_namespace@ -#endif // @PROJECT_GIT_INFO_HEADER_GUARD@ +#endif // @project_git_info_header_guard@ diff --git a/doc/code_quality_tools.md b/doc/code_quality_tools.md index 56e0a03..17d1f53 100644 --- a/doc/code_quality_tools.md +++ b/doc/code_quality_tools.md @@ -13,6 +13,8 @@ This project uses many code quality tools commonly utilized in a C++ project. Th - [Thread sanitizer](#thread-sanitizer) - [Undefined behavior sanitizer](#undefined-behavior-sanitizer) - [Doxygen](#doxygen) +- [CMake format](#cmake-format) +- [CMake lint](#cmake-lint) - [References](#references) ## Clang-format @@ -301,6 +303,56 @@ $ doxygen Please refer to the [Doxygen page][ref-tool-doxygen] for more details regarding this tool. +## CMake format + +cmake-format is a tool that can be used to format CMake code. + +This tool allows you to specify various options for formatting through a configuration file that can be in JSON, YAML or Python format. + +This project uses this [cmake-format configuration file](../.cmake-format.py), which includes the desired style options for this project, but can be used as example and be adjusted to your needs. + +cmake-format supports many options which can be found using the `--help` option: + +```sh +$ cmake-format --help +``` + +For example, to run cmake-format on a file and apply the necessary changes: + +```sh +$ cmake-format -i +``` + +To just check the file without modifying it: + +```sh +$ cmake-format --check +``` + +Please refer to the [cmake-format page][ref-tool-cmake-format] for more details regarding this tool. + +## CMake lint + +cmake-lint is a tool that can be used to check CMake code problems and conventions. + +This tool allows you to specify various options for linting through a configuration file that can be in JSON, YAML or Python format, and is used together with the cmake-format tool (configuration file is usually the same for both cmake-format and cmake-lint). + +This project uses this [cmake-lint configuration file](../.cmake-format.py) (the same used for cmake-format), which includes the desired options for this project, but can be used as example and be adjusted to your needs. + +cmake-lint supports many options which can be found using the `--help` option: + +```sh +$ cmake-lint --help +``` + +For example, to run cmake-lint on a file: + +```sh +$ cmake-lint +``` + +Please refer to the [cmake-lint page][ref-tool-cmake-format] for more details regarding this tool. + ## References - [Clang-format][ref-tool-clang-format] @@ -315,6 +367,7 @@ Please refer to the [Doxygen page][ref-tool-doxygen] for more details regarding - [Undefined behavior sanitizer checks][ref-tool-sanitizer-undefined-checks] - [GCC program instrumentation options][ref-gcc-instrumentation-options] - [Doxygen][ref-tool-doxygen] +- [cmake-format][ref-tool-cmake-format] - [Visual Studio Code: C/C++ extension][ref-vscode-cpp-extension] [ref-tool-clang-format]: https://clang.llvm.org/docs/ClangFormat.html "Clang-format" @@ -329,4 +382,5 @@ Please refer to the [Doxygen page][ref-tool-doxygen] for more details regarding [ref-tool-sanitizer-undefined-checks]: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#available-checks [ref-gcc-instrumentation-options]: https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html [ref-tool-doxygen]: https://www.doxygen.nl/ "Doxygen" +[ref-tool-cmake-format]: https://github.com/cheshirekow/cmake_format "cmake-format" [ref-vscode-cpp-extension]: https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools "Visual Studio Code: C/C++ extension" diff --git a/doxygen/Doxyfile.in b/doxygen/Doxyfile.in index f2380f8..a4df506 100644 --- a/doxygen/Doxyfile.in +++ b/doxygen/Doxyfile.in @@ -32,19 +32,19 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = @DOXYGEN_PROJECT_NAME@ +PROJECT_NAME = @doxygen_project_name@ # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = @DOXYGEN_PROJECT_NUMBER@ +PROJECT_NUMBER = @doxygen_project_number@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = @DOXYGEN_PROJECT_BRIEF@ +PROJECT_BRIEF = @doxygen_project_brief@ # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 @@ -58,7 +58,7 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = @DOXYGEN_OUTPUT_DIRECTORY@ +OUTPUT_DIRECTORY = @doxygen_output_directory@ # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -874,7 +874,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = @DOXYGEN_INPUT@ +INPUT = @doxygen_input@ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8ba653f..fe82900 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,18 +3,13 @@ project(cpp-project-template) add_subdirectory(my_class) add_subdirectory(version) -add_executable(${PROJECT_NAME} - main.cpp -) +add_executable(${PROJECT_NAME} main.cpp) target_include_git_info_directory(${PROJECT_NAME}) -target_link_libraries(${PROJECT_NAME} - PRIVATE ${CXXPROJT_PROJECT_NAME}::compile_options - PRIVATE ${CXXPROJT_PROJECT_NAME}::version - PRIVATE ${CXXPROJT_PROJECT_NAME}::my_class +target_link_libraries( + ${PROJECT_NAME} PRIVATE ${CPROJT_PROJECT_NAME}::compile_options ${CPROJT_PROJECT_NAME}::version + ${CPROJT_PROJECT_NAME}::my_class ) -set_target_properties(${PROJECT_NAME} PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" -) +set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") diff --git a/src/my_class/CMakeLists.txt b/src/my_class/CMakeLists.txt index 97cf537..7f28785 100644 --- a/src/my_class/CMakeLists.txt +++ b/src/my_class/CMakeLists.txt @@ -1,14 +1,8 @@ project(my_class) -add_library(${PROJECT_NAME} STATIC - my_concrete_class.cpp -) -add_library(${CXXPROJT_PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) +add_library(${PROJECT_NAME} STATIC my_concrete_class.cpp) +add_library(${CPROJT_PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) -target_include_directories(${PROJECT_NAME} - PUBLIC . -) +target_include_directories(${PROJECT_NAME} PUBLIC .) -target_link_libraries(${PROJECT_NAME} - PRIVATE ${CXXPROJT_PROJECT_NAME}::compile_options -) +target_link_libraries(${PROJECT_NAME} PRIVATE ${CPROJT_PROJECT_NAME}::compile_options) diff --git a/src/version/CMakeLists.txt b/src/version/CMakeLists.txt index aedaa05..e9044a2 100644 --- a/src/version/CMakeLists.txt +++ b/src/version/CMakeLists.txt @@ -1,12 +1,8 @@ project(version) add_library(${PROJECT_NAME} INTERFACE) -add_library(${CXXPROJT_PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) +add_library(${CPROJT_PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) -target_include_directories(${PROJECT_NAME} - INTERFACE . -) +target_include_directories(${PROJECT_NAME} INTERFACE .) -target_link_libraries(${PROJECT_NAME} - INTERFACE ${CXXPROJT_PROJECT_NAME}::compile_options -) +target_link_libraries(${PROJECT_NAME} INTERFACE ${CPROJT_PROJECT_NAME}::compile_options) diff --git a/test/mocks/my_class/CMakeLists.txt b/test/mocks/my_class/CMakeLists.txt index 437165f..86e2e4c 100644 --- a/test/mocks/my_class/CMakeLists.txt +++ b/test/mocks/my_class/CMakeLists.txt @@ -1,14 +1,11 @@ project(mock_my_class) add_library(${PROJECT_NAME} INTERFACE) -add_library(${CXXPROJT_PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) +add_library(${CPROJT_PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) -target_include_directories(${PROJECT_NAME} - INTERFACE . -) +target_include_directories(${PROJECT_NAME} INTERFACE .) -target_link_libraries(${PROJECT_NAME} - INTERFACE ${CXXPROJT_PROJECT_NAME}::compile_options - INTERFACE GTest::gmock - INTERFACE ${CXXPROJT_PROJECT_NAME}::my_class +target_link_libraries( + ${PROJECT_NAME} INTERFACE GTest::gmock ${CPROJT_PROJECT_NAME}::compile_options + ${CPROJT_PROJECT_NAME}::my_class ) diff --git a/test/unit/demo_mock_usage/CMakeLists.txt b/test/unit/demo_mock_usage/CMakeLists.txt index e2ffcfd..c8df214 100644 --- a/test/unit/demo_mock_usage/CMakeLists.txt +++ b/test/unit/demo_mock_usage/CMakeLists.txt @@ -1,13 +1,10 @@ project(ut_demo_mock_usage) -add_executable(${PROJECT_NAME} - ut_demo_mock_usage.cpp -) +add_executable(${PROJECT_NAME} ut_demo_mock_usage.cpp) gtest_discover_tests(${PROJECT_NAME}) -target_link_libraries(${PROJECT_NAME} - PRIVATE ${CXXPROJT_PROJECT_NAME}::compile_options - PRIVATE GTest::gtest_main - PRIVATE ${CXXPROJT_PROJECT_NAME}::mock_my_class +target_link_libraries( + ${PROJECT_NAME} PRIVATE GTest::gtest_main ${CPROJT_PROJECT_NAME}::compile_options + ${CPROJT_PROJECT_NAME}::mock_my_class ) diff --git a/test/unit/my_class/CMakeLists.txt b/test/unit/my_class/CMakeLists.txt index 46f2af9..9d4c3ad 100644 --- a/test/unit/my_class/CMakeLists.txt +++ b/test/unit/my_class/CMakeLists.txt @@ -1,13 +1,10 @@ project(ut_my_class) -add_executable(${PROJECT_NAME} - ut_my_concrete_class.cpp -) +add_executable(${PROJECT_NAME} ut_my_concrete_class.cpp) gtest_discover_tests(${PROJECT_NAME}) -target_link_libraries(${PROJECT_NAME} - PRIVATE ${CXXPROJT_PROJECT_NAME}::compile_options - PRIVATE GTest::gtest_main - PRIVATE ${CXXPROJT_PROJECT_NAME}::my_class +target_link_libraries( + ${PROJECT_NAME} PRIVATE GTest::gtest_main ${CPROJT_PROJECT_NAME}::compile_options + ${CPROJT_PROJECT_NAME}::my_class ) diff --git a/test/unit/version/CMakeLists.txt b/test/unit/version/CMakeLists.txt index 097c38a..02c099d 100644 --- a/test/unit/version/CMakeLists.txt +++ b/test/unit/version/CMakeLists.txt @@ -1,13 +1,10 @@ project(ut_version) -add_executable(${PROJECT_NAME} - ut_version.cpp -) +add_executable(${PROJECT_NAME} ut_version.cpp) gtest_discover_tests(${PROJECT_NAME}) -target_link_libraries(${PROJECT_NAME} - PRIVATE ${CXXPROJT_PROJECT_NAME}::compile_options - PRIVATE GTest::gtest_main - PRIVATE ${CXXPROJT_PROJECT_NAME}::version +target_link_libraries( + ${PROJECT_NAME} PRIVATE GTest::gtest_main ${CPROJT_PROJECT_NAME}::compile_options + ${CPROJT_PROJECT_NAME}::version )