├── .gitattributes ├── .gitignore ├── .gitmodules ├── CPM.cmake ├── Conversions.cmake ├── LICENSE ├── README.md ├── analysis ├── CodeCoverage.cmake ├── clang-tidy.cmake ├── clang-tidy.sh ├── complexity.cmake ├── coverage.cmake ├── cppcheck.cmake ├── sanitizers.cmake ├── sloccount.cmake └── vale.cmake ├── compiler ├── CheckAndApplyFlags.cmake └── DefaultCompilerSettings.cmake ├── documentation ├── Doxyfile.in └── doxygen.cmake ├── format ├── clang-format.cmake └── format.sh ├── linker-scripts └── stm │ └── STM32F103VBIx_FLASH.ld ├── linker ├── AddExecutableWithLinkerScriptDep.cmake ├── DefaultLinkerSettings.cmake └── map.cmake ├── test ├── catch2.cmake ├── cmocka.cmake └── unity.cmake └── toolchains ├── OverrideBuildTypeSettings.cmake ├── cross ├── STM32F103VBIx.cmake ├── arm-none-eabi-gcc.cmake ├── cortex-m3.cmake ├── cortex-m4_hardfloat.cmake └── cortex-m7_hardfloat.cmake └── native ├── clang.cmake ├── gcc-10.cmake ├── gcc-11.cmake ├── gcc-12.cmake ├── gcc-13.cmake ├── gcc-7.cmake ├── gcc-8.cmake ├── gcc-9.cmake ├── gcc.cmake └── homebrew_clang.cmake /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pdf filter=lfs diff=lfs merge=lfs -text 2 | *.jpg filter=lfs diff=lfs merge=lfs -text 3 | *.png filter=lfs diff=lfs merge=lfs -text 4 | *.xlsx filter=lfs diff=lfs merge=lfs -text 5 | *.docx filter=lfs diff=lfs merge=lfs -text 6 | *.zip filter=lfs diff=lfs merge=lfs -text 7 | *.tar filter=lfs diff=lfs merge=lfs -text 8 | *.tgz filter=lfs diff=lfs merge=lfs -text 9 | *.bz2 filter=lfs diff=lfs merge=lfs -text 10 | *.out filter=lfs diff=lfs merge=lfs -text 11 | *.hex filter=lfs diff=lfs merge=lfs -text 12 | *.bin filter=lfs diff=lfs merge=lfs -text 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated Files 2 | **/buildresults/** 3 | 4 | # Sublime-generated Files 5 | *.sublime-workspace 6 | 7 | ######################## 8 | # C / C++ Ignore Rules # 9 | ######################## 10 | 11 | # File system 12 | .DS_Store 13 | 14 | # Prerequisites 15 | *.d 16 | 17 | # Compiled Object files 18 | *.slo 19 | *.lo 20 | *.o 21 | *.obj 22 | 23 | # Precompiled Headers 24 | *.gch 25 | *.pch 26 | 27 | # Compiled Dynamic libraries 28 | *.so 29 | *.dylib 30 | *.dll 31 | 32 | # Fortran module files 33 | *.mod 34 | *.smod 35 | 36 | # Compiled Static libraries 37 | *.lai 38 | *.la 39 | *.a 40 | *.lib 41 | 42 | # Executables 43 | *.exe 44 | *.out 45 | *.app 46 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "analysis/cppcheck-rules"] 2 | path = analysis/cppcheck-rules 3 | url = https://github.com/embeddedartistry/cppcheck-rules 4 | [submodule "analysis/vale-styleguide"] 5 | path = analysis/vale-styleguide 6 | url = https://github.com/embeddedartistry/vale-styleguide 7 | -------------------------------------------------------------------------------- /CPM.cmake: -------------------------------------------------------------------------------- 1 | # CPM.cmake - CMake's missing package manager 2 | # =========================================== 3 | # See https://github.com/cpm-cmake/CPM.cmake for usage and update instructions. 4 | # 5 | # MIT License 6 | # ----------- 7 | #[[ 8 | Copyright (c) 2019-2023 Lars Melchior and contributors 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | ]] 28 | 29 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 30 | 31 | # Initialize logging prefix 32 | if(NOT CPM_INDENT) 33 | set(CPM_INDENT 34 | "CPM:" 35 | CACHE INTERNAL "" 36 | ) 37 | endif() 38 | 39 | if(NOT COMMAND cpm_message) 40 | function(cpm_message) 41 | message(${ARGV}) 42 | endfunction() 43 | endif() 44 | 45 | if(DEFINED EXTRACTED_CPM_VERSION) 46 | set(CURRENT_CPM_VERSION "${EXTRACTED_CPM_VERSION}${CPM_DEVELOPMENT}") 47 | else() 48 | set(CURRENT_CPM_VERSION 0.40.7) 49 | endif() 50 | 51 | get_filename_component(CPM_CURRENT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" REALPATH) 52 | if(CPM_DIRECTORY) 53 | if(NOT CPM_DIRECTORY STREQUAL CPM_CURRENT_DIRECTORY) 54 | if(CPM_VERSION VERSION_LESS CURRENT_CPM_VERSION) 55 | message( 56 | AUTHOR_WARNING 57 | "${CPM_INDENT} \ 58 | A dependency is using a more recent CPM version (${CURRENT_CPM_VERSION}) than the current project (${CPM_VERSION}). \ 59 | It is recommended to upgrade CPM to the most recent version. \ 60 | See https://github.com/cpm-cmake/CPM.cmake for more information." 61 | ) 62 | endif() 63 | if(${CMAKE_VERSION} VERSION_LESS "3.17.0") 64 | include(FetchContent) 65 | endif() 66 | return() 67 | endif() 68 | 69 | get_property( 70 | CPM_INITIALIZED GLOBAL "" 71 | PROPERTY CPM_INITIALIZED 72 | SET 73 | ) 74 | if(CPM_INITIALIZED) 75 | return() 76 | endif() 77 | endif() 78 | 79 | if(CURRENT_CPM_VERSION MATCHES "development-version") 80 | message( 81 | WARNING "${CPM_INDENT} Your project is using an unstable development version of CPM.cmake. \ 82 | Please update to a recent release if possible. \ 83 | See https://github.com/cpm-cmake/CPM.cmake for details." 84 | ) 85 | endif() 86 | 87 | set_property(GLOBAL PROPERTY CPM_INITIALIZED true) 88 | 89 | macro(cpm_set_policies) 90 | # the policy allows us to change options without caching 91 | cmake_policy(SET CMP0077 NEW) 92 | set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) 93 | 94 | # the policy allows us to change set(CACHE) without caching 95 | if(POLICY CMP0126) 96 | cmake_policy(SET CMP0126 NEW) 97 | set(CMAKE_POLICY_DEFAULT_CMP0126 NEW) 98 | endif() 99 | 100 | # The policy uses the download time for timestamp, instead of the timestamp in the archive. This 101 | # allows for proper rebuilds when a projects url changes 102 | if(POLICY CMP0135) 103 | cmake_policy(SET CMP0135 NEW) 104 | set(CMAKE_POLICY_DEFAULT_CMP0135 NEW) 105 | endif() 106 | 107 | # treat relative git repository paths as being relative to the parent project's remote 108 | if(POLICY CMP0150) 109 | cmake_policy(SET CMP0150 NEW) 110 | set(CMAKE_POLICY_DEFAULT_CMP0150 NEW) 111 | endif() 112 | endmacro() 113 | cpm_set_policies() 114 | 115 | option(CPM_USE_LOCAL_PACKAGES "Always try to use `find_package` to get dependencies" 116 | $ENV{CPM_USE_LOCAL_PACKAGES} 117 | ) 118 | option(CPM_LOCAL_PACKAGES_ONLY "Only use `find_package` to get dependencies" 119 | $ENV{CPM_LOCAL_PACKAGES_ONLY} 120 | ) 121 | option(CPM_DOWNLOAD_ALL "Always download dependencies from source" $ENV{CPM_DOWNLOAD_ALL}) 122 | option(CPM_DONT_UPDATE_MODULE_PATH "Don't update the module path to allow using find_package" 123 | $ENV{CPM_DONT_UPDATE_MODULE_PATH} 124 | ) 125 | option(CPM_DONT_CREATE_PACKAGE_LOCK "Don't create a package lock file in the binary path" 126 | $ENV{CPM_DONT_CREATE_PACKAGE_LOCK} 127 | ) 128 | option(CPM_INCLUDE_ALL_IN_PACKAGE_LOCK 129 | "Add all packages added through CPM.cmake to the package lock" 130 | $ENV{CPM_INCLUDE_ALL_IN_PACKAGE_LOCK} 131 | ) 132 | option(CPM_USE_NAMED_CACHE_DIRECTORIES 133 | "Use additional directory of package name in cache on the most nested level." 134 | $ENV{CPM_USE_NAMED_CACHE_DIRECTORIES} 135 | ) 136 | 137 | set(CPM_VERSION 138 | ${CURRENT_CPM_VERSION} 139 | CACHE INTERNAL "" 140 | ) 141 | set(CPM_DIRECTORY 142 | ${CPM_CURRENT_DIRECTORY} 143 | CACHE INTERNAL "" 144 | ) 145 | set(CPM_FILE 146 | ${CMAKE_CURRENT_LIST_FILE} 147 | CACHE INTERNAL "" 148 | ) 149 | set(CPM_PACKAGES 150 | "" 151 | CACHE INTERNAL "" 152 | ) 153 | set(CPM_DRY_RUN 154 | OFF 155 | CACHE INTERNAL "Don't download or configure dependencies (for testing)" 156 | ) 157 | 158 | if(DEFINED ENV{CPM_SOURCE_CACHE}) 159 | set(CPM_SOURCE_CACHE_DEFAULT $ENV{CPM_SOURCE_CACHE}) 160 | else() 161 | set(CPM_SOURCE_CACHE_DEFAULT OFF) 162 | endif() 163 | 164 | set(CPM_SOURCE_CACHE 165 | ${CPM_SOURCE_CACHE_DEFAULT} 166 | CACHE PATH "Directory to download CPM dependencies" 167 | ) 168 | 169 | if(NOT CPM_DONT_UPDATE_MODULE_PATH AND NOT DEFINED CMAKE_FIND_PACKAGE_REDIRECTS_DIR) 170 | set(CPM_MODULE_PATH 171 | "${CMAKE_BINARY_DIR}/CPM_modules" 172 | CACHE INTERNAL "" 173 | ) 174 | # remove old modules 175 | file(REMOVE_RECURSE ${CPM_MODULE_PATH}) 176 | file(MAKE_DIRECTORY ${CPM_MODULE_PATH}) 177 | # locally added CPM modules should override global packages 178 | set(CMAKE_MODULE_PATH "${CPM_MODULE_PATH};${CMAKE_MODULE_PATH}") 179 | endif() 180 | 181 | if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) 182 | set(CPM_PACKAGE_LOCK_FILE 183 | "${CMAKE_BINARY_DIR}/cpm-package-lock.cmake" 184 | CACHE INTERNAL "" 185 | ) 186 | file(WRITE ${CPM_PACKAGE_LOCK_FILE} 187 | "# CPM Package Lock\n# This file should be committed to version control\n\n" 188 | ) 189 | endif() 190 | 191 | include(FetchContent) 192 | 193 | # Try to infer package name from git repository uri (path or url) 194 | function(cpm_package_name_from_git_uri URI RESULT) 195 | if("${URI}" MATCHES "([^/:]+)/?.git/?$") 196 | set(${RESULT} 197 | ${CMAKE_MATCH_1} 198 | PARENT_SCOPE 199 | ) 200 | else() 201 | unset(${RESULT} PARENT_SCOPE) 202 | endif() 203 | endfunction() 204 | 205 | # Try to infer package name and version from a url 206 | function(cpm_package_name_and_ver_from_url url outName outVer) 207 | if(url MATCHES "[/\\?]([a-zA-Z0-9_\\.-]+)\\.(tar|tar\\.gz|tar\\.bz2|zip|ZIP)(\\?|/|$)") 208 | # We matched an archive 209 | set(filename "${CMAKE_MATCH_1}") 210 | 211 | if(filename MATCHES "([a-zA-Z0-9_\\.-]+)[_-]v?(([0-9]+\\.)*[0-9]+[a-zA-Z0-9]*)") 212 | # We matched - (ie foo-1.2.3) 213 | set(${outName} 214 | "${CMAKE_MATCH_1}" 215 | PARENT_SCOPE 216 | ) 217 | set(${outVer} 218 | "${CMAKE_MATCH_2}" 219 | PARENT_SCOPE 220 | ) 221 | elseif(filename MATCHES "(([0-9]+\\.)+[0-9]+[a-zA-Z0-9]*)") 222 | # We couldn't find a name, but we found a version 223 | # 224 | # In many cases (which we don't handle here) the url would look something like 225 | # `irrelevant/ACTUAL_PACKAGE_NAME/irrelevant/1.2.3.zip`. In such a case we can't possibly 226 | # distinguish the package name from the irrelevant bits. Moreover if we try to match the 227 | # package name from the filename, we'd get bogus at best. 228 | unset(${outName} PARENT_SCOPE) 229 | set(${outVer} 230 | "${CMAKE_MATCH_1}" 231 | PARENT_SCOPE 232 | ) 233 | else() 234 | # Boldly assume that the file name is the package name. 235 | # 236 | # Yes, something like `irrelevant/ACTUAL_NAME/irrelevant/download.zip` will ruin our day, but 237 | # such cases should be quite rare. No popular service does this... we think. 238 | set(${outName} 239 | "${filename}" 240 | PARENT_SCOPE 241 | ) 242 | unset(${outVer} PARENT_SCOPE) 243 | endif() 244 | else() 245 | # No ideas yet what to do with non-archives 246 | unset(${outName} PARENT_SCOPE) 247 | unset(${outVer} PARENT_SCOPE) 248 | endif() 249 | endfunction() 250 | 251 | function(cpm_find_package NAME VERSION) 252 | string(REPLACE " " ";" EXTRA_ARGS "${ARGN}") 253 | find_package(${NAME} ${VERSION} ${EXTRA_ARGS} QUIET) 254 | if(${CPM_ARGS_NAME}_FOUND) 255 | if(DEFINED ${CPM_ARGS_NAME}_VERSION) 256 | set(VERSION ${${CPM_ARGS_NAME}_VERSION}) 257 | endif() 258 | cpm_message(STATUS "${CPM_INDENT} Using local package ${CPM_ARGS_NAME}@${VERSION}") 259 | CPMRegisterPackage(${CPM_ARGS_NAME} "${VERSION}") 260 | set(CPM_PACKAGE_FOUND 261 | YES 262 | PARENT_SCOPE 263 | ) 264 | else() 265 | set(CPM_PACKAGE_FOUND 266 | NO 267 | PARENT_SCOPE 268 | ) 269 | endif() 270 | endfunction() 271 | 272 | # Create a custom FindXXX.cmake module for a CPM package This prevents `find_package(NAME)` from 273 | # finding the system library 274 | function(cpm_create_module_file Name) 275 | if(NOT CPM_DONT_UPDATE_MODULE_PATH) 276 | if(DEFINED CMAKE_FIND_PACKAGE_REDIRECTS_DIR) 277 | # Redirect find_package calls to the CPM package. This is what FetchContent does when you set 278 | # OVERRIDE_FIND_PACKAGE. The CMAKE_FIND_PACKAGE_REDIRECTS_DIR works for find_package in CONFIG 279 | # mode, unlike the Find${Name}.cmake fallback. CMAKE_FIND_PACKAGE_REDIRECTS_DIR is not defined 280 | # in script mode, or in CMake < 3.24. 281 | # https://cmake.org/cmake/help/latest/module/FetchContent.html#fetchcontent-find-package-integration-examples 282 | string(TOLOWER ${Name} NameLower) 283 | file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/${NameLower}-config.cmake 284 | "include(\"\${CMAKE_CURRENT_LIST_DIR}/${NameLower}-extra.cmake\" OPTIONAL)\n" 285 | "include(\"\${CMAKE_CURRENT_LIST_DIR}/${Name}Extra.cmake\" OPTIONAL)\n" 286 | ) 287 | file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/${NameLower}-config-version.cmake 288 | "set(PACKAGE_VERSION_COMPATIBLE TRUE)\n" "set(PACKAGE_VERSION_EXACT TRUE)\n" 289 | ) 290 | else() 291 | file(WRITE ${CPM_MODULE_PATH}/Find${Name}.cmake 292 | "include(\"${CPM_FILE}\")\n${ARGN}\nset(${Name}_FOUND TRUE)" 293 | ) 294 | endif() 295 | endif() 296 | endfunction() 297 | 298 | # Find a package locally or fallback to CPMAddPackage 299 | function(CPMFindPackage) 300 | set(oneValueArgs NAME VERSION GIT_TAG FIND_PACKAGE_ARGUMENTS) 301 | 302 | cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "" ${ARGN}) 303 | 304 | if(NOT DEFINED CPM_ARGS_VERSION) 305 | if(DEFINED CPM_ARGS_GIT_TAG) 306 | cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION) 307 | endif() 308 | endif() 309 | 310 | set(downloadPackage ${CPM_DOWNLOAD_ALL}) 311 | if(DEFINED CPM_DOWNLOAD_${CPM_ARGS_NAME}) 312 | set(downloadPackage ${CPM_DOWNLOAD_${CPM_ARGS_NAME}}) 313 | elseif(DEFINED ENV{CPM_DOWNLOAD_${CPM_ARGS_NAME}}) 314 | set(downloadPackage $ENV{CPM_DOWNLOAD_${CPM_ARGS_NAME}}) 315 | endif() 316 | if(downloadPackage) 317 | CPMAddPackage(${ARGN}) 318 | cpm_export_variables(${CPM_ARGS_NAME}) 319 | return() 320 | endif() 321 | 322 | cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS}) 323 | 324 | if(NOT CPM_PACKAGE_FOUND) 325 | CPMAddPackage(${ARGN}) 326 | cpm_export_variables(${CPM_ARGS_NAME}) 327 | endif() 328 | 329 | endfunction() 330 | 331 | # checks if a package has been added before 332 | function(cpm_check_if_package_already_added CPM_ARGS_NAME CPM_ARGS_VERSION) 333 | if("${CPM_ARGS_NAME}" IN_LIST CPM_PACKAGES) 334 | CPMGetPackageVersion(${CPM_ARGS_NAME} CPM_PACKAGE_VERSION) 335 | if("${CPM_PACKAGE_VERSION}" VERSION_LESS "${CPM_ARGS_VERSION}") 336 | message( 337 | WARNING 338 | "${CPM_INDENT} Requires a newer version of ${CPM_ARGS_NAME} (${CPM_ARGS_VERSION}) than currently included (${CPM_PACKAGE_VERSION})." 339 | ) 340 | endif() 341 | cpm_get_fetch_properties(${CPM_ARGS_NAME}) 342 | set(${CPM_ARGS_NAME}_ADDED NO) 343 | set(CPM_PACKAGE_ALREADY_ADDED 344 | YES 345 | PARENT_SCOPE 346 | ) 347 | cpm_export_variables(${CPM_ARGS_NAME}) 348 | else() 349 | set(CPM_PACKAGE_ALREADY_ADDED 350 | NO 351 | PARENT_SCOPE 352 | ) 353 | endif() 354 | endfunction() 355 | 356 | # Parse the argument of CPMAddPackage in case a single one was provided and convert it to a list of 357 | # arguments which can then be parsed idiomatically. For example gh:foo/bar@1.2.3 will be converted 358 | # to: GITHUB_REPOSITORY;foo/bar;VERSION;1.2.3 359 | function(cpm_parse_add_package_single_arg arg outArgs) 360 | # Look for a scheme 361 | if("${arg}" MATCHES "^([a-zA-Z]+):(.+)$") 362 | string(TOLOWER "${CMAKE_MATCH_1}" scheme) 363 | set(uri "${CMAKE_MATCH_2}") 364 | 365 | # Check for CPM-specific schemes 366 | if(scheme STREQUAL "gh") 367 | set(out "GITHUB_REPOSITORY;${uri}") 368 | set(packageType "git") 369 | elseif(scheme STREQUAL "gl") 370 | set(out "GITLAB_REPOSITORY;${uri}") 371 | set(packageType "git") 372 | elseif(scheme STREQUAL "bb") 373 | set(out "BITBUCKET_REPOSITORY;${uri}") 374 | set(packageType "git") 375 | # A CPM-specific scheme was not found. Looks like this is a generic URL so try to determine 376 | # type 377 | elseif(arg MATCHES ".git/?(@|#|$)") 378 | set(out "GIT_REPOSITORY;${arg}") 379 | set(packageType "git") 380 | else() 381 | # Fall back to a URL 382 | set(out "URL;${arg}") 383 | set(packageType "archive") 384 | 385 | # We could also check for SVN since FetchContent supports it, but SVN is so rare these days. 386 | # We just won't bother with the additional complexity it will induce in this function. SVN is 387 | # done by multi-arg 388 | endif() 389 | else() 390 | if(arg MATCHES ".git/?(@|#|$)") 391 | set(out "GIT_REPOSITORY;${arg}") 392 | set(packageType "git") 393 | else() 394 | # Give up 395 | message(FATAL_ERROR "${CPM_INDENT} Can't determine package type of '${arg}'") 396 | endif() 397 | endif() 398 | 399 | # For all packages we interpret @... as version. Only replace the last occurrence. Thus URIs 400 | # containing '@' can be used 401 | string(REGEX REPLACE "@([^@]+)$" ";VERSION;\\1" out "${out}") 402 | 403 | # Parse the rest according to package type 404 | if(packageType STREQUAL "git") 405 | # For git repos we interpret #... as a tag or branch or commit hash 406 | string(REGEX REPLACE "#([^#]+)$" ";GIT_TAG;\\1" out "${out}") 407 | elseif(packageType STREQUAL "archive") 408 | # For archives we interpret #... as a URL hash. 409 | string(REGEX REPLACE "#([^#]+)$" ";URL_HASH;\\1" out "${out}") 410 | # We don't try to parse the version if it's not provided explicitly. cpm_get_version_from_url 411 | # should do this at a later point 412 | else() 413 | # We should never get here. This is an assertion and hitting it means there's a problem with the 414 | # code above. A packageType was set, but not handled by this if-else. 415 | message(FATAL_ERROR "${CPM_INDENT} Unsupported package type '${packageType}' of '${arg}'") 416 | endif() 417 | 418 | set(${outArgs} 419 | ${out} 420 | PARENT_SCOPE 421 | ) 422 | endfunction() 423 | 424 | # Check that the working directory for a git repo is clean 425 | function(cpm_check_git_working_dir_is_clean repoPath gitTag isClean) 426 | 427 | find_package(Git REQUIRED) 428 | 429 | if(NOT GIT_EXECUTABLE) 430 | # No git executable, assume directory is clean 431 | set(${isClean} 432 | TRUE 433 | PARENT_SCOPE 434 | ) 435 | return() 436 | endif() 437 | 438 | # check for uncommitted changes 439 | execute_process( 440 | COMMAND ${GIT_EXECUTABLE} status --porcelain 441 | RESULT_VARIABLE resultGitStatus 442 | OUTPUT_VARIABLE repoStatus 443 | OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET 444 | WORKING_DIRECTORY ${repoPath} 445 | ) 446 | if(resultGitStatus) 447 | # not supposed to happen, assume clean anyway 448 | message(WARNING "${CPM_INDENT} Calling git status on folder ${repoPath} failed") 449 | set(${isClean} 450 | TRUE 451 | PARENT_SCOPE 452 | ) 453 | return() 454 | endif() 455 | 456 | if(NOT "${repoStatus}" STREQUAL "") 457 | set(${isClean} 458 | FALSE 459 | PARENT_SCOPE 460 | ) 461 | return() 462 | endif() 463 | 464 | # check for committed changes 465 | execute_process( 466 | COMMAND ${GIT_EXECUTABLE} diff -s --exit-code ${gitTag} 467 | RESULT_VARIABLE resultGitDiff 468 | OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_QUIET 469 | WORKING_DIRECTORY ${repoPath} 470 | ) 471 | 472 | if(${resultGitDiff} EQUAL 0) 473 | set(${isClean} 474 | TRUE 475 | PARENT_SCOPE 476 | ) 477 | else() 478 | set(${isClean} 479 | FALSE 480 | PARENT_SCOPE 481 | ) 482 | endif() 483 | 484 | endfunction() 485 | 486 | # Add PATCH_COMMAND to CPM_ARGS_UNPARSED_ARGUMENTS. This method consumes a list of files in ARGN 487 | # then generates a `PATCH_COMMAND` appropriate for `ExternalProject_Add()`. This command is appended 488 | # to the parent scope's `CPM_ARGS_UNPARSED_ARGUMENTS`. 489 | function(cpm_add_patches) 490 | # Return if no patch files are supplied. 491 | if(NOT ARGN) 492 | return() 493 | endif() 494 | 495 | # Find the patch program. 496 | find_program(PATCH_EXECUTABLE patch) 497 | if(CMAKE_HOST_WIN32 AND NOT PATCH_EXECUTABLE) 498 | # The Windows git executable is distributed with patch.exe. Find the path to the executable, if 499 | # it exists, then search `../usr/bin` and `../../usr/bin` for patch.exe. 500 | find_package(Git QUIET) 501 | if(GIT_EXECUTABLE) 502 | get_filename_component(extra_search_path ${GIT_EXECUTABLE} DIRECTORY) 503 | get_filename_component(extra_search_path_1up ${extra_search_path} DIRECTORY) 504 | get_filename_component(extra_search_path_2up ${extra_search_path_1up} DIRECTORY) 505 | find_program( 506 | PATCH_EXECUTABLE patch HINTS "${extra_search_path_1up}/usr/bin" 507 | "${extra_search_path_2up}/usr/bin" 508 | ) 509 | endif() 510 | endif() 511 | if(NOT PATCH_EXECUTABLE) 512 | message(FATAL_ERROR "Couldn't find `patch` executable to use with PATCHES keyword.") 513 | endif() 514 | 515 | # Create a temporary 516 | set(temp_list ${CPM_ARGS_UNPARSED_ARGUMENTS}) 517 | 518 | # Ensure each file exists (or error out) and add it to the list. 519 | set(first_item True) 520 | foreach(PATCH_FILE ${ARGN}) 521 | # Make sure the patch file exists, if we can't find it, try again in the current directory. 522 | if(NOT EXISTS "${PATCH_FILE}") 523 | if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${PATCH_FILE}") 524 | message(FATAL_ERROR "Couldn't find patch file: '${PATCH_FILE}'") 525 | endif() 526 | set(PATCH_FILE "${CMAKE_CURRENT_LIST_DIR}/${PATCH_FILE}") 527 | endif() 528 | 529 | # Convert to absolute path for use with patch file command. 530 | get_filename_component(PATCH_FILE "${PATCH_FILE}" ABSOLUTE) 531 | 532 | # The first patch entry must be preceded by "PATCH_COMMAND" while the following items are 533 | # preceded by "&&". 534 | if(first_item) 535 | set(first_item False) 536 | list(APPEND temp_list "PATCH_COMMAND") 537 | else() 538 | list(APPEND temp_list "&&") 539 | endif() 540 | # Add the patch command to the list 541 | list(APPEND temp_list "${PATCH_EXECUTABLE}" "-p1" "<" "${PATCH_FILE}") 542 | endforeach() 543 | 544 | # Move temp out into parent scope. 545 | set(CPM_ARGS_UNPARSED_ARGUMENTS 546 | ${temp_list} 547 | PARENT_SCOPE 548 | ) 549 | 550 | endfunction() 551 | 552 | # method to overwrite internal FetchContent properties, to allow using CPM.cmake to overload 553 | # FetchContent calls. As these are internal cmake properties, this method should be used carefully 554 | # and may need modification in future CMake versions. Source: 555 | # https://github.com/Kitware/CMake/blob/dc3d0b5a0a7d26d43d6cfeb511e224533b5d188f/Modules/FetchContent.cmake#L1152 556 | function(cpm_override_fetchcontent contentName) 557 | cmake_parse_arguments(PARSE_ARGV 1 arg "" "SOURCE_DIR;BINARY_DIR" "") 558 | if(NOT "${arg_UNPARSED_ARGUMENTS}" STREQUAL "") 559 | message(FATAL_ERROR "${CPM_INDENT} Unsupported arguments: ${arg_UNPARSED_ARGUMENTS}") 560 | endif() 561 | 562 | string(TOLOWER ${contentName} contentNameLower) 563 | set(prefix "_FetchContent_${contentNameLower}") 564 | 565 | set(propertyName "${prefix}_sourceDir") 566 | define_property( 567 | GLOBAL 568 | PROPERTY ${propertyName} 569 | BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" 570 | FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" 571 | ) 572 | set_property(GLOBAL PROPERTY ${propertyName} "${arg_SOURCE_DIR}") 573 | 574 | set(propertyName "${prefix}_binaryDir") 575 | define_property( 576 | GLOBAL 577 | PROPERTY ${propertyName} 578 | BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" 579 | FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" 580 | ) 581 | set_property(GLOBAL PROPERTY ${propertyName} "${arg_BINARY_DIR}") 582 | 583 | set(propertyName "${prefix}_populated") 584 | define_property( 585 | GLOBAL 586 | PROPERTY ${propertyName} 587 | BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" 588 | FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" 589 | ) 590 | set_property(GLOBAL PROPERTY ${propertyName} TRUE) 591 | endfunction() 592 | 593 | # Download and add a package from source 594 | function(CPMAddPackage) 595 | cpm_set_policies() 596 | 597 | list(LENGTH ARGN argnLength) 598 | if(argnLength EQUAL 1) 599 | cpm_parse_add_package_single_arg("${ARGN}" ARGN) 600 | 601 | # The shorthand syntax implies EXCLUDE_FROM_ALL and SYSTEM 602 | set(ARGN "${ARGN};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;") 603 | endif() 604 | 605 | set(oneValueArgs 606 | NAME 607 | FORCE 608 | VERSION 609 | GIT_TAG 610 | DOWNLOAD_ONLY 611 | GITHUB_REPOSITORY 612 | GITLAB_REPOSITORY 613 | BITBUCKET_REPOSITORY 614 | GIT_REPOSITORY 615 | SOURCE_DIR 616 | FIND_PACKAGE_ARGUMENTS 617 | NO_CACHE 618 | SYSTEM 619 | GIT_SHALLOW 620 | EXCLUDE_FROM_ALL 621 | SOURCE_SUBDIR 622 | CUSTOM_CACHE_KEY 623 | ) 624 | 625 | set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND PATCHES) 626 | 627 | cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}") 628 | 629 | # Set default values for arguments 630 | 631 | if(NOT DEFINED CPM_ARGS_VERSION) 632 | if(DEFINED CPM_ARGS_GIT_TAG) 633 | cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION) 634 | endif() 635 | endif() 636 | 637 | if(CPM_ARGS_DOWNLOAD_ONLY) 638 | set(DOWNLOAD_ONLY ${CPM_ARGS_DOWNLOAD_ONLY}) 639 | else() 640 | set(DOWNLOAD_ONLY NO) 641 | endif() 642 | 643 | if(DEFINED CPM_ARGS_GITHUB_REPOSITORY) 644 | set(CPM_ARGS_GIT_REPOSITORY "https://github.com/${CPM_ARGS_GITHUB_REPOSITORY}.git") 645 | elseif(DEFINED CPM_ARGS_GITLAB_REPOSITORY) 646 | set(CPM_ARGS_GIT_REPOSITORY "https://gitlab.com/${CPM_ARGS_GITLAB_REPOSITORY}.git") 647 | elseif(DEFINED CPM_ARGS_BITBUCKET_REPOSITORY) 648 | set(CPM_ARGS_GIT_REPOSITORY "https://bitbucket.org/${CPM_ARGS_BITBUCKET_REPOSITORY}.git") 649 | endif() 650 | 651 | if(DEFINED CPM_ARGS_GIT_REPOSITORY) 652 | list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_REPOSITORY ${CPM_ARGS_GIT_REPOSITORY}) 653 | if(NOT DEFINED CPM_ARGS_GIT_TAG) 654 | set(CPM_ARGS_GIT_TAG v${CPM_ARGS_VERSION}) 655 | endif() 656 | 657 | # If a name wasn't provided, try to infer it from the git repo 658 | if(NOT DEFINED CPM_ARGS_NAME) 659 | cpm_package_name_from_git_uri(${CPM_ARGS_GIT_REPOSITORY} CPM_ARGS_NAME) 660 | endif() 661 | endif() 662 | 663 | set(CPM_SKIP_FETCH FALSE) 664 | 665 | if(DEFINED CPM_ARGS_GIT_TAG) 666 | list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_TAG ${CPM_ARGS_GIT_TAG}) 667 | # If GIT_SHALLOW is explicitly specified, honor the value. 668 | if(DEFINED CPM_ARGS_GIT_SHALLOW) 669 | list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_SHALLOW ${CPM_ARGS_GIT_SHALLOW}) 670 | endif() 671 | endif() 672 | 673 | if(DEFINED CPM_ARGS_URL) 674 | # If a name or version aren't provided, try to infer them from the URL 675 | list(GET CPM_ARGS_URL 0 firstUrl) 676 | cpm_package_name_and_ver_from_url(${firstUrl} nameFromUrl verFromUrl) 677 | # If we fail to obtain name and version from the first URL, we could try other URLs if any. 678 | # However multiple URLs are expected to be quite rare, so for now we won't bother. 679 | 680 | # If the caller provided their own name and version, they trump the inferred ones. 681 | if(NOT DEFINED CPM_ARGS_NAME) 682 | set(CPM_ARGS_NAME ${nameFromUrl}) 683 | endif() 684 | if(NOT DEFINED CPM_ARGS_VERSION) 685 | set(CPM_ARGS_VERSION ${verFromUrl}) 686 | endif() 687 | 688 | list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS URL "${CPM_ARGS_URL}") 689 | endif() 690 | 691 | # Check for required arguments 692 | 693 | if(NOT DEFINED CPM_ARGS_NAME) 694 | message( 695 | FATAL_ERROR 696 | "${CPM_INDENT} 'NAME' was not provided and couldn't be automatically inferred for package added with arguments: '${ARGN}'" 697 | ) 698 | endif() 699 | 700 | # Check if package has been added before 701 | cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}") 702 | if(CPM_PACKAGE_ALREADY_ADDED) 703 | cpm_export_variables(${CPM_ARGS_NAME}) 704 | return() 705 | endif() 706 | 707 | # Check for manual overrides 708 | if(NOT CPM_ARGS_FORCE AND NOT "${CPM_${CPM_ARGS_NAME}_SOURCE}" STREQUAL "") 709 | set(PACKAGE_SOURCE ${CPM_${CPM_ARGS_NAME}_SOURCE}) 710 | set(CPM_${CPM_ARGS_NAME}_SOURCE "") 711 | CPMAddPackage( 712 | NAME "${CPM_ARGS_NAME}" 713 | SOURCE_DIR "${PACKAGE_SOURCE}" 714 | EXCLUDE_FROM_ALL "${CPM_ARGS_EXCLUDE_FROM_ALL}" 715 | SYSTEM "${CPM_ARGS_SYSTEM}" 716 | PATCHES "${CPM_ARGS_PATCHES}" 717 | OPTIONS "${CPM_ARGS_OPTIONS}" 718 | SOURCE_SUBDIR "${CPM_ARGS_SOURCE_SUBDIR}" 719 | DOWNLOAD_ONLY "${DOWNLOAD_ONLY}" 720 | FORCE True 721 | ) 722 | cpm_export_variables(${CPM_ARGS_NAME}) 723 | return() 724 | endif() 725 | 726 | # Check for available declaration 727 | if(NOT CPM_ARGS_FORCE AND NOT "${CPM_DECLARATION_${CPM_ARGS_NAME}}" STREQUAL "") 728 | set(declaration ${CPM_DECLARATION_${CPM_ARGS_NAME}}) 729 | set(CPM_DECLARATION_${CPM_ARGS_NAME} "") 730 | CPMAddPackage(${declaration}) 731 | cpm_export_variables(${CPM_ARGS_NAME}) 732 | # checking again to ensure version and option compatibility 733 | cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}") 734 | return() 735 | endif() 736 | 737 | if(NOT CPM_ARGS_FORCE) 738 | if(CPM_USE_LOCAL_PACKAGES OR CPM_LOCAL_PACKAGES_ONLY) 739 | cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS}) 740 | 741 | if(CPM_PACKAGE_FOUND) 742 | cpm_export_variables(${CPM_ARGS_NAME}) 743 | return() 744 | endif() 745 | 746 | if(CPM_LOCAL_PACKAGES_ONLY) 747 | message( 748 | SEND_ERROR 749 | "${CPM_INDENT} ${CPM_ARGS_NAME} not found via find_package(${CPM_ARGS_NAME} ${CPM_ARGS_VERSION})" 750 | ) 751 | endif() 752 | endif() 753 | endif() 754 | 755 | CPMRegisterPackage("${CPM_ARGS_NAME}" "${CPM_ARGS_VERSION}") 756 | 757 | if(DEFINED CPM_ARGS_GIT_TAG) 758 | set(PACKAGE_INFO "${CPM_ARGS_GIT_TAG}") 759 | elseif(DEFINED CPM_ARGS_SOURCE_DIR) 760 | set(PACKAGE_INFO "${CPM_ARGS_SOURCE_DIR}") 761 | else() 762 | set(PACKAGE_INFO "${CPM_ARGS_VERSION}") 763 | endif() 764 | 765 | if(DEFINED FETCHCONTENT_BASE_DIR) 766 | # respect user's FETCHCONTENT_BASE_DIR if set 767 | set(CPM_FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR}) 768 | else() 769 | set(CPM_FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/_deps) 770 | endif() 771 | 772 | cpm_add_patches(${CPM_ARGS_PATCHES}) 773 | 774 | if(DEFINED CPM_ARGS_DOWNLOAD_COMMAND) 775 | list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS DOWNLOAD_COMMAND ${CPM_ARGS_DOWNLOAD_COMMAND}) 776 | elseif(DEFINED CPM_ARGS_SOURCE_DIR) 777 | list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${CPM_ARGS_SOURCE_DIR}) 778 | if(NOT IS_ABSOLUTE ${CPM_ARGS_SOURCE_DIR}) 779 | # Expand `CPM_ARGS_SOURCE_DIR` relative path. This is important because EXISTS doesn't work 780 | # for relative paths. 781 | get_filename_component( 782 | source_directory ${CPM_ARGS_SOURCE_DIR} REALPATH BASE_DIR ${CMAKE_CURRENT_BINARY_DIR} 783 | ) 784 | else() 785 | set(source_directory ${CPM_ARGS_SOURCE_DIR}) 786 | endif() 787 | if(NOT EXISTS ${source_directory}) 788 | string(TOLOWER ${CPM_ARGS_NAME} lower_case_name) 789 | # remove timestamps so CMake will re-download the dependency 790 | file(REMOVE_RECURSE "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild") 791 | endif() 792 | elseif(CPM_SOURCE_CACHE AND NOT CPM_ARGS_NO_CACHE) 793 | string(TOLOWER ${CPM_ARGS_NAME} lower_case_name) 794 | set(origin_parameters ${CPM_ARGS_UNPARSED_ARGUMENTS}) 795 | list(SORT origin_parameters) 796 | if(CPM_ARGS_CUSTOM_CACHE_KEY) 797 | # Application set a custom unique directory name 798 | set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${CPM_ARGS_CUSTOM_CACHE_KEY}) 799 | elseif(CPM_USE_NAMED_CACHE_DIRECTORIES) 800 | string(SHA1 origin_hash "${origin_parameters};NEW_CACHE_STRUCTURE_TAG") 801 | set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}/${CPM_ARGS_NAME}) 802 | else() 803 | string(SHA1 origin_hash "${origin_parameters}") 804 | set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}) 805 | endif() 806 | # Expand `download_directory` relative path. This is important because EXISTS doesn't work for 807 | # relative paths. 808 | get_filename_component(download_directory ${download_directory} ABSOLUTE) 809 | list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${download_directory}) 810 | 811 | if(CPM_SOURCE_CACHE) 812 | file(LOCK ${download_directory}/../cmake.lock) 813 | endif() 814 | 815 | if(EXISTS ${download_directory}) 816 | if(CPM_SOURCE_CACHE) 817 | file(LOCK ${download_directory}/../cmake.lock RELEASE) 818 | endif() 819 | 820 | cpm_store_fetch_properties( 821 | ${CPM_ARGS_NAME} "${download_directory}" 822 | "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-build" 823 | ) 824 | cpm_get_fetch_properties("${CPM_ARGS_NAME}") 825 | 826 | if(DEFINED CPM_ARGS_GIT_TAG AND NOT (PATCH_COMMAND IN_LIST CPM_ARGS_UNPARSED_ARGUMENTS)) 827 | # warn if cache has been changed since checkout 828 | cpm_check_git_working_dir_is_clean(${download_directory} ${CPM_ARGS_GIT_TAG} IS_CLEAN) 829 | if(NOT ${IS_CLEAN}) 830 | message( 831 | WARNING "${CPM_INDENT} Cache for ${CPM_ARGS_NAME} (${download_directory}) is dirty" 832 | ) 833 | endif() 834 | endif() 835 | 836 | cpm_add_subdirectory( 837 | "${CPM_ARGS_NAME}" 838 | "${DOWNLOAD_ONLY}" 839 | "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" 840 | "${${CPM_ARGS_NAME}_BINARY_DIR}" 841 | "${CPM_ARGS_EXCLUDE_FROM_ALL}" 842 | "${CPM_ARGS_SYSTEM}" 843 | "${CPM_ARGS_OPTIONS}" 844 | ) 845 | set(PACKAGE_INFO "${PACKAGE_INFO} at ${download_directory}") 846 | 847 | # As the source dir is already cached/populated, we override the call to FetchContent. 848 | set(CPM_SKIP_FETCH TRUE) 849 | cpm_override_fetchcontent( 850 | "${lower_case_name}" SOURCE_DIR "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" 851 | BINARY_DIR "${${CPM_ARGS_NAME}_BINARY_DIR}" 852 | ) 853 | 854 | else() 855 | # Enable shallow clone when GIT_TAG is not a commit hash. Our guess may not be accurate, but 856 | # it should guarantee no commit hash get mis-detected. 857 | if(NOT DEFINED CPM_ARGS_GIT_SHALLOW) 858 | cpm_is_git_tag_commit_hash("${CPM_ARGS_GIT_TAG}" IS_HASH) 859 | if(NOT ${IS_HASH}) 860 | list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_SHALLOW TRUE) 861 | endif() 862 | endif() 863 | 864 | # remove timestamps so CMake will re-download the dependency 865 | file(REMOVE_RECURSE ${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild) 866 | set(PACKAGE_INFO "${PACKAGE_INFO} to ${download_directory}") 867 | endif() 868 | endif() 869 | 870 | cpm_create_module_file(${CPM_ARGS_NAME} "CPMAddPackage(\"${ARGN}\")") 871 | 872 | if(CPM_PACKAGE_LOCK_ENABLED) 873 | if((CPM_ARGS_VERSION AND NOT CPM_ARGS_SOURCE_DIR) OR CPM_INCLUDE_ALL_IN_PACKAGE_LOCK) 874 | cpm_add_to_package_lock(${CPM_ARGS_NAME} "${ARGN}") 875 | elseif(CPM_ARGS_SOURCE_DIR) 876 | cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "local directory") 877 | else() 878 | cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "${ARGN}") 879 | endif() 880 | endif() 881 | 882 | cpm_message( 883 | STATUS "${CPM_INDENT} Adding package ${CPM_ARGS_NAME}@${CPM_ARGS_VERSION} (${PACKAGE_INFO})" 884 | ) 885 | 886 | if(NOT CPM_SKIP_FETCH) 887 | # CMake 3.28 added EXCLUDE, SYSTEM (3.25), and SOURCE_SUBDIR (3.18) to FetchContent_Declare. 888 | # Calling FetchContent_MakeAvailable will then internally forward these options to 889 | # add_subdirectory. Up until these changes, we had to call FetchContent_Populate and 890 | # add_subdirectory separately, which is no longer necessary and has been deprecated as of 3.30. 891 | # A Bug in CMake prevents us to use the non-deprecated functions until 3.30.3. 892 | set(fetchContentDeclareExtraArgs "") 893 | if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30.3") 894 | if(${CPM_ARGS_EXCLUDE_FROM_ALL}) 895 | list(APPEND fetchContentDeclareExtraArgs EXCLUDE_FROM_ALL) 896 | endif() 897 | if(${CPM_ARGS_SYSTEM}) 898 | list(APPEND fetchContentDeclareExtraArgs SYSTEM) 899 | endif() 900 | if(DEFINED CPM_ARGS_SOURCE_SUBDIR) 901 | list(APPEND fetchContentDeclareExtraArgs SOURCE_SUBDIR ${CPM_ARGS_SOURCE_SUBDIR}) 902 | endif() 903 | # For CMake version <3.28 OPTIONS are parsed in cpm_add_subdirectory 904 | if(CPM_ARGS_OPTIONS AND NOT DOWNLOAD_ONLY) 905 | foreach(OPTION ${CPM_ARGS_OPTIONS}) 906 | cpm_parse_option("${OPTION}") 907 | set(${OPTION_KEY} "${OPTION_VALUE}") 908 | endforeach() 909 | endif() 910 | endif() 911 | cpm_declare_fetch( 912 | "${CPM_ARGS_NAME}" ${fetchContentDeclareExtraArgs} "${CPM_ARGS_UNPARSED_ARGUMENTS}" 913 | ) 914 | 915 | cpm_fetch_package("${CPM_ARGS_NAME}" ${DOWNLOAD_ONLY} populated ${CPM_ARGS_UNPARSED_ARGUMENTS}) 916 | if(CPM_SOURCE_CACHE AND download_directory) 917 | file(LOCK ${download_directory}/../cmake.lock RELEASE) 918 | endif() 919 | if(${populated} AND ${CMAKE_VERSION} VERSION_LESS "3.30.3") 920 | cpm_add_subdirectory( 921 | "${CPM_ARGS_NAME}" 922 | "${DOWNLOAD_ONLY}" 923 | "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" 924 | "${${CPM_ARGS_NAME}_BINARY_DIR}" 925 | "${CPM_ARGS_EXCLUDE_FROM_ALL}" 926 | "${CPM_ARGS_SYSTEM}" 927 | "${CPM_ARGS_OPTIONS}" 928 | ) 929 | endif() 930 | cpm_get_fetch_properties("${CPM_ARGS_NAME}") 931 | endif() 932 | 933 | set(${CPM_ARGS_NAME}_ADDED YES) 934 | cpm_export_variables("${CPM_ARGS_NAME}") 935 | endfunction() 936 | 937 | # Fetch a previously declared package 938 | macro(CPMGetPackage Name) 939 | if(DEFINED "CPM_DECLARATION_${Name}") 940 | CPMAddPackage(NAME ${Name}) 941 | else() 942 | message(SEND_ERROR "${CPM_INDENT} Cannot retrieve package ${Name}: no declaration available") 943 | endif() 944 | endmacro() 945 | 946 | # export variables available to the caller to the parent scope expects ${CPM_ARGS_NAME} to be set 947 | macro(cpm_export_variables name) 948 | set(${name}_SOURCE_DIR 949 | "${${name}_SOURCE_DIR}" 950 | PARENT_SCOPE 951 | ) 952 | set(${name}_BINARY_DIR 953 | "${${name}_BINARY_DIR}" 954 | PARENT_SCOPE 955 | ) 956 | set(${name}_ADDED 957 | "${${name}_ADDED}" 958 | PARENT_SCOPE 959 | ) 960 | set(CPM_LAST_PACKAGE_NAME 961 | "${name}" 962 | PARENT_SCOPE 963 | ) 964 | endmacro() 965 | 966 | # declares a package, so that any call to CPMAddPackage for the package name will use these 967 | # arguments instead. Previous declarations will not be overridden. 968 | macro(CPMDeclarePackage Name) 969 | if(NOT DEFINED "CPM_DECLARATION_${Name}") 970 | set("CPM_DECLARATION_${Name}" "${ARGN}") 971 | endif() 972 | endmacro() 973 | 974 | function(cpm_add_to_package_lock Name) 975 | if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) 976 | cpm_prettify_package_arguments(PRETTY_ARGN false ${ARGN}) 977 | file(APPEND ${CPM_PACKAGE_LOCK_FILE} "# ${Name}\nCPMDeclarePackage(${Name}\n${PRETTY_ARGN})\n") 978 | endif() 979 | endfunction() 980 | 981 | function(cpm_add_comment_to_package_lock Name) 982 | if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) 983 | cpm_prettify_package_arguments(PRETTY_ARGN true ${ARGN}) 984 | file(APPEND ${CPM_PACKAGE_LOCK_FILE} 985 | "# ${Name} (unversioned)\n# CPMDeclarePackage(${Name}\n${PRETTY_ARGN}#)\n" 986 | ) 987 | endif() 988 | endfunction() 989 | 990 | # includes the package lock file if it exists and creates a target `cpm-update-package-lock` to 991 | # update it 992 | macro(CPMUsePackageLock file) 993 | if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) 994 | get_filename_component(CPM_ABSOLUTE_PACKAGE_LOCK_PATH ${file} ABSOLUTE) 995 | if(EXISTS ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH}) 996 | include(${CPM_ABSOLUTE_PACKAGE_LOCK_PATH}) 997 | endif() 998 | if(NOT TARGET cpm-update-package-lock) 999 | add_custom_target( 1000 | cpm-update-package-lock COMMAND ${CMAKE_COMMAND} -E copy ${CPM_PACKAGE_LOCK_FILE} 1001 | ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH} 1002 | ) 1003 | endif() 1004 | set(CPM_PACKAGE_LOCK_ENABLED true) 1005 | endif() 1006 | endmacro() 1007 | 1008 | # registers a package that has been added to CPM 1009 | function(CPMRegisterPackage PACKAGE VERSION) 1010 | list(APPEND CPM_PACKAGES ${PACKAGE}) 1011 | set(CPM_PACKAGES 1012 | ${CPM_PACKAGES} 1013 | CACHE INTERNAL "" 1014 | ) 1015 | set("CPM_PACKAGE_${PACKAGE}_VERSION" 1016 | ${VERSION} 1017 | CACHE INTERNAL "" 1018 | ) 1019 | endfunction() 1020 | 1021 | # retrieve the current version of the package to ${OUTPUT} 1022 | function(CPMGetPackageVersion PACKAGE OUTPUT) 1023 | set(${OUTPUT} 1024 | "${CPM_PACKAGE_${PACKAGE}_VERSION}" 1025 | PARENT_SCOPE 1026 | ) 1027 | endfunction() 1028 | 1029 | # declares a package in FetchContent_Declare 1030 | function(cpm_declare_fetch PACKAGE) 1031 | if(${CPM_DRY_RUN}) 1032 | cpm_message(STATUS "${CPM_INDENT} Package not declared (dry run)") 1033 | return() 1034 | endif() 1035 | 1036 | FetchContent_Declare(${PACKAGE} ${ARGN}) 1037 | endfunction() 1038 | 1039 | # returns properties for a package previously defined by cpm_declare_fetch 1040 | function(cpm_get_fetch_properties PACKAGE) 1041 | if(${CPM_DRY_RUN}) 1042 | return() 1043 | endif() 1044 | 1045 | set(${PACKAGE}_SOURCE_DIR 1046 | "${CPM_PACKAGE_${PACKAGE}_SOURCE_DIR}" 1047 | PARENT_SCOPE 1048 | ) 1049 | set(${PACKAGE}_BINARY_DIR 1050 | "${CPM_PACKAGE_${PACKAGE}_BINARY_DIR}" 1051 | PARENT_SCOPE 1052 | ) 1053 | endfunction() 1054 | 1055 | function(cpm_store_fetch_properties PACKAGE source_dir binary_dir) 1056 | if(${CPM_DRY_RUN}) 1057 | return() 1058 | endif() 1059 | 1060 | set(CPM_PACKAGE_${PACKAGE}_SOURCE_DIR 1061 | "${source_dir}" 1062 | CACHE INTERNAL "" 1063 | ) 1064 | set(CPM_PACKAGE_${PACKAGE}_BINARY_DIR 1065 | "${binary_dir}" 1066 | CACHE INTERNAL "" 1067 | ) 1068 | endfunction() 1069 | 1070 | # adds a package as a subdirectory if viable, according to provided options 1071 | function( 1072 | cpm_add_subdirectory 1073 | PACKAGE 1074 | DOWNLOAD_ONLY 1075 | SOURCE_DIR 1076 | BINARY_DIR 1077 | EXCLUDE 1078 | SYSTEM 1079 | OPTIONS 1080 | ) 1081 | 1082 | if(NOT DOWNLOAD_ONLY AND EXISTS ${SOURCE_DIR}/CMakeLists.txt) 1083 | set(addSubdirectoryExtraArgs "") 1084 | if(EXCLUDE) 1085 | list(APPEND addSubdirectoryExtraArgs EXCLUDE_FROM_ALL) 1086 | endif() 1087 | if("${SYSTEM}" AND "${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.25") 1088 | # https://cmake.org/cmake/help/latest/prop_dir/SYSTEM.html#prop_dir:SYSTEM 1089 | list(APPEND addSubdirectoryExtraArgs SYSTEM) 1090 | endif() 1091 | if(OPTIONS) 1092 | foreach(OPTION ${OPTIONS}) 1093 | cpm_parse_option("${OPTION}") 1094 | set(${OPTION_KEY} "${OPTION_VALUE}") 1095 | endforeach() 1096 | endif() 1097 | set(CPM_OLD_INDENT "${CPM_INDENT}") 1098 | set(CPM_INDENT "${CPM_INDENT} ${PACKAGE}:") 1099 | add_subdirectory(${SOURCE_DIR} ${BINARY_DIR} ${addSubdirectoryExtraArgs}) 1100 | set(CPM_INDENT "${CPM_OLD_INDENT}") 1101 | endif() 1102 | endfunction() 1103 | 1104 | # downloads a previously declared package via FetchContent and exports the variables 1105 | # `${PACKAGE}_SOURCE_DIR` and `${PACKAGE}_BINARY_DIR` to the parent scope 1106 | function(cpm_fetch_package PACKAGE DOWNLOAD_ONLY populated) 1107 | set(${populated} 1108 | FALSE 1109 | PARENT_SCOPE 1110 | ) 1111 | if(${CPM_DRY_RUN}) 1112 | cpm_message(STATUS "${CPM_INDENT} Package ${PACKAGE} not fetched (dry run)") 1113 | return() 1114 | endif() 1115 | 1116 | FetchContent_GetProperties(${PACKAGE}) 1117 | 1118 | string(TOLOWER "${PACKAGE}" lower_case_name) 1119 | 1120 | if(NOT ${lower_case_name}_POPULATED) 1121 | if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30.3") 1122 | if(DOWNLOAD_ONLY) 1123 | # MakeAvailable will call add_subdirectory internally which is not what we want when 1124 | # DOWNLOAD_ONLY is set. Populate will only download the dependency without adding it to the 1125 | # build 1126 | FetchContent_Populate( 1127 | ${PACKAGE} 1128 | SOURCE_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-src" 1129 | BINARY_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-build" 1130 | SUBBUILD_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild" 1131 | ${ARGN} 1132 | ) 1133 | else() 1134 | FetchContent_MakeAvailable(${PACKAGE}) 1135 | endif() 1136 | else() 1137 | FetchContent_Populate(${PACKAGE}) 1138 | endif() 1139 | set(${populated} 1140 | TRUE 1141 | PARENT_SCOPE 1142 | ) 1143 | endif() 1144 | 1145 | cpm_store_fetch_properties( 1146 | ${CPM_ARGS_NAME} ${${lower_case_name}_SOURCE_DIR} ${${lower_case_name}_BINARY_DIR} 1147 | ) 1148 | 1149 | set(${PACKAGE}_SOURCE_DIR 1150 | ${${lower_case_name}_SOURCE_DIR} 1151 | PARENT_SCOPE 1152 | ) 1153 | set(${PACKAGE}_BINARY_DIR 1154 | ${${lower_case_name}_BINARY_DIR} 1155 | PARENT_SCOPE 1156 | ) 1157 | endfunction() 1158 | 1159 | # splits a package option 1160 | function(cpm_parse_option OPTION) 1161 | string(REGEX MATCH "^[^ ]+" OPTION_KEY "${OPTION}") 1162 | string(LENGTH "${OPTION}" OPTION_LENGTH) 1163 | string(LENGTH "${OPTION_KEY}" OPTION_KEY_LENGTH) 1164 | if(OPTION_KEY_LENGTH STREQUAL OPTION_LENGTH) 1165 | # no value for key provided, assume user wants to set option to "ON" 1166 | set(OPTION_VALUE "ON") 1167 | else() 1168 | math(EXPR OPTION_KEY_LENGTH "${OPTION_KEY_LENGTH}+1") 1169 | string(SUBSTRING "${OPTION}" "${OPTION_KEY_LENGTH}" "-1" OPTION_VALUE) 1170 | endif() 1171 | set(OPTION_KEY 1172 | "${OPTION_KEY}" 1173 | PARENT_SCOPE 1174 | ) 1175 | set(OPTION_VALUE 1176 | "${OPTION_VALUE}" 1177 | PARENT_SCOPE 1178 | ) 1179 | endfunction() 1180 | 1181 | # guesses the package version from a git tag 1182 | function(cpm_get_version_from_git_tag GIT_TAG RESULT) 1183 | string(LENGTH ${GIT_TAG} length) 1184 | if(length EQUAL 40) 1185 | # GIT_TAG is probably a git hash 1186 | set(${RESULT} 1187 | 0 1188 | PARENT_SCOPE 1189 | ) 1190 | else() 1191 | string(REGEX MATCH "v?([0123456789.]*).*" _ ${GIT_TAG}) 1192 | set(${RESULT} 1193 | ${CMAKE_MATCH_1} 1194 | PARENT_SCOPE 1195 | ) 1196 | endif() 1197 | endfunction() 1198 | 1199 | # guesses if the git tag is a commit hash or an actual tag or a branch name. 1200 | function(cpm_is_git_tag_commit_hash GIT_TAG RESULT) 1201 | string(LENGTH "${GIT_TAG}" length) 1202 | # full hash has 40 characters, and short hash has at least 7 characters. 1203 | if(length LESS 7 OR length GREATER 40) 1204 | set(${RESULT} 1205 | 0 1206 | PARENT_SCOPE 1207 | ) 1208 | else() 1209 | if(${GIT_TAG} MATCHES "^[a-fA-F0-9]+$") 1210 | set(${RESULT} 1211 | 1 1212 | PARENT_SCOPE 1213 | ) 1214 | else() 1215 | set(${RESULT} 1216 | 0 1217 | PARENT_SCOPE 1218 | ) 1219 | endif() 1220 | endif() 1221 | endfunction() 1222 | 1223 | function(cpm_prettify_package_arguments OUT_VAR IS_IN_COMMENT) 1224 | set(oneValueArgs 1225 | NAME 1226 | FORCE 1227 | VERSION 1228 | GIT_TAG 1229 | DOWNLOAD_ONLY 1230 | GITHUB_REPOSITORY 1231 | GITLAB_REPOSITORY 1232 | BITBUCKET_REPOSITORY 1233 | GIT_REPOSITORY 1234 | SOURCE_DIR 1235 | FIND_PACKAGE_ARGUMENTS 1236 | NO_CACHE 1237 | SYSTEM 1238 | GIT_SHALLOW 1239 | EXCLUDE_FROM_ALL 1240 | SOURCE_SUBDIR 1241 | ) 1242 | set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND) 1243 | cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 1244 | 1245 | foreach(oneArgName ${oneValueArgs}) 1246 | if(DEFINED CPM_ARGS_${oneArgName}) 1247 | if(${IS_IN_COMMENT}) 1248 | string(APPEND PRETTY_OUT_VAR "#") 1249 | endif() 1250 | if(${oneArgName} STREQUAL "SOURCE_DIR") 1251 | string(REPLACE ${CMAKE_SOURCE_DIR} "\${CMAKE_SOURCE_DIR}" CPM_ARGS_${oneArgName} 1252 | ${CPM_ARGS_${oneArgName}} 1253 | ) 1254 | endif() 1255 | string(APPEND PRETTY_OUT_VAR " ${oneArgName} ${CPM_ARGS_${oneArgName}}\n") 1256 | endif() 1257 | endforeach() 1258 | foreach(multiArgName ${multiValueArgs}) 1259 | if(DEFINED CPM_ARGS_${multiArgName}) 1260 | if(${IS_IN_COMMENT}) 1261 | string(APPEND PRETTY_OUT_VAR "#") 1262 | endif() 1263 | string(APPEND PRETTY_OUT_VAR " ${multiArgName}\n") 1264 | foreach(singleOption ${CPM_ARGS_${multiArgName}}) 1265 | if(${IS_IN_COMMENT}) 1266 | string(APPEND PRETTY_OUT_VAR "#") 1267 | endif() 1268 | string(APPEND PRETTY_OUT_VAR " \"${singleOption}\"\n") 1269 | endforeach() 1270 | endif() 1271 | endforeach() 1272 | 1273 | if(NOT "${CPM_ARGS_UNPARSED_ARGUMENTS}" STREQUAL "") 1274 | if(${IS_IN_COMMENT}) 1275 | string(APPEND PRETTY_OUT_VAR "#") 1276 | endif() 1277 | string(APPEND PRETTY_OUT_VAR " ") 1278 | foreach(CPM_ARGS_UNPARSED_ARGUMENT ${CPM_ARGS_UNPARSED_ARGUMENTS}) 1279 | string(APPEND PRETTY_OUT_VAR " ${CPM_ARGS_UNPARSED_ARGUMENT}") 1280 | endforeach() 1281 | string(APPEND PRETTY_OUT_VAR "\n") 1282 | endif() 1283 | 1284 | set(${OUT_VAR} 1285 | ${PRETTY_OUT_VAR} 1286 | PARENT_SCOPE 1287 | ) 1288 | 1289 | endfunction() 1290 | -------------------------------------------------------------------------------- /Conversions.cmake: -------------------------------------------------------------------------------- 1 | function(convert_to_hex TARGET) 2 | add_custom_command( 3 | TARGET ${TARGET} POST_BUILD 4 | COMMAND ${CMAKE_OBJCOPY} -O ihex ${TARGET} ${TARGET}.hex 5 | BYPRODUCTS ${TARGET}.hex 6 | ) 7 | endfunction() 8 | 9 | function(convert_to_bin TARGET) 10 | add_custom_command( 11 | TARGET ${TARGET} POST_BUILD 12 | COMMAND ${CMAKE_OBJCOPY} -O binary ${TARGET} ${TARGET}.bin 13 | BYPRODUCTS ${TARGET}.bin 14 | ) 15 | endfunction() 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Embedded Artistry 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cmake-buildsystem 2 | Helper scripts, cross-compilation-files, makefile shims, and other helpful tools for working with CMake 3 | -------------------------------------------------------------------------------- /analysis/CodeCoverage.cmake: -------------------------------------------------------------------------------- 1 | # Modified locally! 2 | # Source: https://github.com/bilke/cmake-modules/blob/master/CodeCoverage.cmake 3 | # Copyright (c) 2012 - 2017, Lars Bilke 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without modification, 7 | # are permitted provided that the following conditions are met: 8 | # 9 | # 1. Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # 12 | # 2. Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # 3. Neither the name of the copyright holder nor the names of its contributors 17 | # may be used to endorse or promote products derived from this software without 18 | # specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | # CHANGES: 32 | # 33 | # 2012-01-31, Lars Bilke 34 | # - Enable Code Coverage 35 | # 36 | # 2013-09-17, Joakim Söderberg 37 | # - Added support for Clang. 38 | # - Some additional usage instructions. 39 | # 40 | # 2016-02-03, Lars Bilke 41 | # - Refactored functions to use named parameters 42 | # 43 | # 2017-06-02, Lars Bilke 44 | # - Merged with modified version from github.com/ufz/ogs 45 | # 46 | # 2019-05-06, Anatolii Kurotych 47 | # - Remove unnecessary --coverage flag 48 | # 49 | # 2019-12-13, FeRD (Frank Dana) 50 | # - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor 51 | # of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments. 52 | # - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY 53 | # - All setup functions: accept BASE_DIRECTORY, EXCLUDE list 54 | # - Set lcov basedir with -b argument 55 | # - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be 56 | # overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().) 57 | # - Delete output dir, .info file on 'make clean' 58 | # - Remove Python detection, since version mismatches will break gcovr 59 | # - Minor cleanup (lowercase function names, update examples...) 60 | # 61 | # 2019-12-19, FeRD (Frank Dana) 62 | # - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets 63 | # 64 | # 2020-01-19, Bob Apthorpe 65 | # - Added gfortran support 66 | # 67 | # 2020-02-17, FeRD (Frank Dana) 68 | # - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters 69 | # in EXCLUDEs, and remove manual escaping from gcovr targets 70 | # 71 | # USAGE: 72 | # 73 | # 1. Copy this file into your cmake modules path. 74 | # 75 | # 2. Add the following line to your CMakeLists.txt (best inside an if-condition 76 | # using a CMake option() to enable it just optionally): 77 | # include(CodeCoverage) 78 | # 79 | # 3. Append necessary compiler flags: 80 | # append_coverage_compiler_flags() 81 | # 82 | # 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og 83 | # 84 | # 4. If you need to exclude additional directories from the report, specify them 85 | # using full paths in the COVERAGE_EXCLUDES variable before calling 86 | # setup_target_for_coverage_*(). 87 | # Example: 88 | # set(COVERAGE_EXCLUDES 89 | # '${PROJECT_SOURCE_DIR}/src/dir1/*' 90 | # '/path/to/my/src/dir2/*') 91 | # Or, use the EXCLUDE argument to setup_target_for_coverage_*(). 92 | # Example: 93 | # setup_target_for_coverage_lcov( 94 | # NAME coverage 95 | # EXECUTABLE testrunner 96 | # EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*") 97 | # 98 | # 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set 99 | # relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR) 100 | # Example: 101 | # set(COVERAGE_EXCLUDES "dir1/*") 102 | # setup_target_for_coverage_gcovr_html( 103 | # NAME coverage 104 | # EXECUTABLE testrunner 105 | # BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src" 106 | # EXCLUDE "dir2/*") 107 | # 108 | # 5. Use the functions described below to create a custom make target which 109 | # runs your test executable and produces a code coverage report. 110 | # 111 | # 6. Build a Debug build: 112 | # cmake -DCMAKE_BUILD_TYPE=Debug .. 113 | # make 114 | # make my_coverage_target 115 | # 116 | 117 | include(CMakeParseArguments) 118 | 119 | # Check prereqs 120 | find_program( GCOV_PATH gcov ) 121 | find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl) 122 | find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat ) 123 | find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) 124 | find_program( CPPFILT_PATH NAMES c++filt ) 125 | 126 | if(NOT GCOV_PATH) 127 | message(FATAL_ERROR "gcov not found! Aborting...") 128 | endif() # NOT GCOV_PATH 129 | 130 | if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") 131 | if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3) 132 | message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") 133 | endif() 134 | elseif(NOT CMAKE_COMPILER_IS_GNUCXX) 135 | if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang") 136 | # Do nothing; exit conditional without error if true 137 | elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU") 138 | # Do nothing; exit conditional without error if true 139 | else() 140 | message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") 141 | endif() 142 | endif() 143 | 144 | set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage" 145 | CACHE INTERNAL "") 146 | 147 | set(CMAKE_Fortran_FLAGS_COVERAGE 148 | ${COVERAGE_COMPILER_FLAGS} 149 | CACHE STRING "Flags used by the Fortran compiler during coverage builds." 150 | FORCE ) 151 | set(CMAKE_CXX_FLAGS_COVERAGE 152 | ${COVERAGE_COMPILER_FLAGS} 153 | CACHE STRING "Flags used by the C++ compiler during coverage builds." 154 | FORCE ) 155 | set(CMAKE_C_FLAGS_COVERAGE 156 | ${COVERAGE_COMPILER_FLAGS} 157 | CACHE STRING "Flags used by the C compiler during coverage builds." 158 | FORCE ) 159 | set(CMAKE_EXE_LINKER_FLAGS_COVERAGE 160 | "" 161 | CACHE STRING "Flags used for linking binaries during coverage builds." 162 | FORCE ) 163 | set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE 164 | "" 165 | CACHE STRING "Flags used by the shared libraries linker during coverage builds." 166 | FORCE ) 167 | mark_as_advanced( 168 | CMAKE_Fortran_FLAGS_COVERAGE 169 | CMAKE_CXX_FLAGS_COVERAGE 170 | CMAKE_C_FLAGS_COVERAGE 171 | CMAKE_EXE_LINKER_FLAGS_COVERAGE 172 | CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) 173 | 174 | if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") 175 | message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") 176 | endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" 177 | 178 | if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") 179 | link_libraries(gcov) 180 | endif() 181 | 182 | # Defines a target for running and collection code coverage information 183 | # Builds dependencies, runs the given executable and outputs reports. 184 | # NOTE! The executable should always have a ZERO as exit code otherwise 185 | # the coverage generation will not complete. 186 | # 187 | # setup_target_for_coverage_lcov( 188 | # NAME testrunner_coverage # New target name 189 | # EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR 190 | # DEPENDENCIES testrunner # Dependencies to build first 191 | # BASE_DIRECTORY "../" # Base directory for report 192 | # # (defaults to PROJECT_SOURCE_DIR) 193 | # EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative 194 | # # to BASE_DIRECTORY, with CMake 3.4+) 195 | # NO_DEMANGLE # Don't demangle C++ symbols 196 | # # even if c++filt is found 197 | # ) 198 | function(setup_target_for_coverage_lcov) 199 | 200 | set(options NO_DEMANGLE) 201 | set(oneValueArgs BASE_DIRECTORY NAME) 202 | set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS) 203 | cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 204 | 205 | if(NOT LCOV_PATH) 206 | message(FATAL_ERROR "lcov not found! Aborting...") 207 | endif() # NOT LCOV_PATH 208 | 209 | if(NOT GENHTML_PATH) 210 | message(FATAL_ERROR "genhtml not found! Aborting...") 211 | endif() # NOT GENHTML_PATH 212 | 213 | # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR 214 | if(${Coverage_BASE_DIRECTORY}) 215 | get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) 216 | else() 217 | set(BASEDIR ${PROJECT_SOURCE_DIR}) 218 | endif() 219 | 220 | # Collect excludes (CMake 3.4+: Also compute absolute paths) 221 | set(LCOV_EXCLUDES "") 222 | foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES}) 223 | if(CMAKE_VERSION VERSION_GREATER 3.4) 224 | get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) 225 | endif() 226 | list(APPEND LCOV_EXCLUDES "${EXCLUDE}") 227 | endforeach() 228 | list(REMOVE_DUPLICATES LCOV_EXCLUDES) 229 | 230 | # Conditional arguments 231 | if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) 232 | set(GENHTML_EXTRA_ARGS "--demangle-cpp") 233 | endif() 234 | 235 | # Setup target 236 | add_custom_target(${Coverage_NAME} 237 | 238 | # Cleanup lcov 239 | COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory . -b ${BASEDIR} --zerocounters 240 | # Create baseline to make sure untouched files show up in the report 241 | COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b ${BASEDIR} -o ${Coverage_NAME}.base 242 | 243 | # Run tests 244 | COMMAND ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} 245 | 246 | # Capturing lcov counters and generating report 247 | COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b ${BASEDIR} --capture --output-file ${Coverage_NAME}.capture 248 | # add baseline counters 249 | COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total 250 | # filter collected data to final coverage report 251 | COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info 252 | 253 | # Generate HTML output 254 | COMMAND ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o ${Coverage_NAME} ${Coverage_NAME}.info 255 | 256 | # Set output files as GENERATED (will be removed on 'make clean') 257 | BYPRODUCTS 258 | ${Coverage_NAME}.base 259 | ${Coverage_NAME}.capture 260 | ${Coverage_NAME}.total 261 | ${Coverage_NAME}.info 262 | ${Coverage_NAME} # report directory 263 | 264 | WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 265 | DEPENDS ${Coverage_DEPENDENCIES} 266 | VERBATIM # Protect arguments to commands 267 | COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." 268 | ) 269 | 270 | # Show where to find the lcov info report 271 | add_custom_command(TARGET ${Coverage_NAME} POST_BUILD 272 | COMMAND ; 273 | COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info." 274 | ) 275 | 276 | # Show info where to find the report 277 | add_custom_command(TARGET ${Coverage_NAME} POST_BUILD 278 | COMMAND ; 279 | COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." 280 | ) 281 | 282 | endfunction() # setup_target_for_coverage_lcov 283 | 284 | # Defines a target for running and collection code coverage information 285 | # Builds dependencies, runs the given executable and outputs reports. 286 | # NOTE! The executable should always have a ZERO as exit code otherwise 287 | # the coverage generation will not complete. 288 | # 289 | # setup_target_for_coverage_gcovr_xml( 290 | # NAME ctest_coverage # New target name 291 | # EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR 292 | # DEPENDENCIES executable_target # Dependencies to build first 293 | # BASE_DIRECTORY "../" # Base directory for report 294 | # # (defaults to PROJECT_SOURCE_DIR) 295 | # EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative 296 | # # to BASE_DIRECTORY, with CMake 3.4+) 297 | # ) 298 | function(setup_target_for_coverage_gcovr_xml) 299 | 300 | set(options NONE) 301 | set(oneValueArgs BASE_DIRECTORY NAME) 302 | set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) 303 | cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 304 | 305 | if(NOT GCOVR_PATH) 306 | message(FATAL_ERROR "gcovr not found! Aborting...") 307 | endif() # NOT GCOVR_PATH 308 | 309 | # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR 310 | if(${Coverage_BASE_DIRECTORY}) 311 | get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) 312 | else() 313 | set(BASEDIR ${PROJECT_SOURCE_DIR}) 314 | endif() 315 | 316 | # Collect excludes (CMake 3.4+: Also compute absolute paths) 317 | set(GCOVR_EXCLUDES "") 318 | foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) 319 | if(CMAKE_VERSION VERSION_GREATER 3.4) 320 | get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) 321 | endif() 322 | list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") 323 | endforeach() 324 | list(REMOVE_DUPLICATES GCOVR_EXCLUDES) 325 | 326 | # Combine excludes to several -e arguments 327 | set(GCOVR_EXCLUDE_ARGS "") 328 | foreach(EXCLUDE ${GCOVR_EXCLUDES}) 329 | list(APPEND GCOVR_EXCLUDE_ARGS "-e") 330 | list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") 331 | endforeach() 332 | 333 | add_custom_target(${Coverage_NAME} 334 | # Run tests 335 | ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} 336 | 337 | # Running gcovr 338 | COMMAND ${GCOVR_PATH} --xml 339 | -r ${BASEDIR} ${GCOVR_EXCLUDE_ARGS} 340 | --object-directory=${PROJECT_BINARY_DIR} 341 | -o ${Coverage_NAME}.xml 342 | BYPRODUCTS ${Coverage_NAME}.xml 343 | WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 344 | DEPENDS ${Coverage_DEPENDENCIES} 345 | VERBATIM # Protect arguments to commands 346 | COMMENT "Running gcovr to produce Cobertura code coverage report." 347 | ) 348 | 349 | # Show info where to find the report 350 | add_custom_command(TARGET ${Coverage_NAME} POST_BUILD 351 | COMMAND ; 352 | COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml." 353 | ) 354 | endfunction() # setup_target_for_coverage_gcovr_xml 355 | 356 | # Defines a target for running and collection code coverage information 357 | # Builds dependencies, runs the given executable and outputs reports. 358 | # NOTE! The executable should always have a ZERO as exit code otherwise 359 | # the coverage generation will not complete. 360 | # 361 | # setup_target_for_coverage_gcovr_html( 362 | # NAME ctest_coverage # New target name 363 | # EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR 364 | # DEPENDENCIES executable_target # Dependencies to build first 365 | # BASE_DIRECTORY "../" # Base directory for report 366 | # # (defaults to PROJECT_SOURCE_DIR) 367 | # EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative 368 | # # to BASE_DIRECTORY, with CMake 3.4+) 369 | # ) 370 | function(setup_target_for_coverage_gcovr_html) 371 | 372 | set(options NONE) 373 | set(oneValueArgs BASE_DIRECTORY NAME) 374 | set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) 375 | cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 376 | 377 | if(NOT GCOVR_PATH) 378 | message(FATAL_ERROR "gcovr not found! Aborting...") 379 | endif() # NOT GCOVR_PATH 380 | 381 | # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR 382 | if(${Coverage_BASE_DIRECTORY}) 383 | get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) 384 | else() 385 | set(BASEDIR ${PROJECT_SOURCE_DIR}) 386 | endif() 387 | 388 | # Collect excludes (CMake 3.4+: Also compute absolute paths) 389 | set(GCOVR_EXCLUDES "") 390 | foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) 391 | if(CMAKE_VERSION VERSION_GREATER 3.4) 392 | get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) 393 | endif() 394 | list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") 395 | endforeach() 396 | list(REMOVE_DUPLICATES GCOVR_EXCLUDES) 397 | 398 | # Combine excludes to several -e arguments 399 | set(GCOVR_EXCLUDE_ARGS "") 400 | foreach(EXCLUDE ${GCOVR_EXCLUDES}) 401 | list(APPEND GCOVR_EXCLUDE_ARGS "-e") 402 | list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") 403 | endforeach() 404 | 405 | add_custom_target(${Coverage_NAME} 406 | # Run tests 407 | ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} 408 | 409 | # Create folder 410 | COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME} 411 | 412 | # Running gcovr 413 | COMMAND ${GCOVR_PATH} --html --html-details 414 | -r ${BASEDIR} ${GCOVR_EXCLUDE_ARGS} 415 | --object-directory=${PROJECT_BINARY_DIR} 416 | -o ${Coverage_NAME}/index.html 417 | 418 | WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 419 | DEPENDS ${Coverage_DEPENDENCIES} 420 | VERBATIM # Protect arguments to commands 421 | COMMENT "Running gcovr to produce HTML code coverage report." 422 | ) 423 | set_target_properties(${Coverage_NAME} PROPERTIES 424 | ADDITIONAL_CLEAN_FILES ${PROJECT_BINARY_DIR}/${Coverage_NAME}) 425 | 426 | # Show info where to find the report 427 | add_custom_command(TARGET ${Coverage_NAME} POST_BUILD 428 | COMMAND ; 429 | COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." 430 | ) 431 | 432 | endfunction() # setup_target_for_coverage_gcovr_html 433 | 434 | function(append_coverage_compiler_flags) 435 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) 436 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) 437 | set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) 438 | message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") 439 | endfunction() # append_coverage_compiler_flags 440 | -------------------------------------------------------------------------------- /analysis/clang-tidy.cmake: -------------------------------------------------------------------------------- 1 | ##################### 2 | # Clang-tidy Module # 3 | ##################### 4 | # 5 | # This module provides both a `tidy` target and an option to enable building 6 | # the project with clang-tidy analysis. 7 | # 8 | # To enable clang-tidy output during the build process, set the `BUILD_WITH_CLANG_TIDY_ANALYSIS` 9 | # option to "ON". 10 | # 11 | # You can control the behavior of clang-tidy by setting the following variables 12 | # before you include this module: 13 | # CLANG_TIDY_DIRS, which is a CMake list of directories to include in clang-tidy analysis 14 | # By default, the src and test directories are included 15 | # CLANG_TIDY_FILETYPES, which is a CMake list of file types to include in clang-tidy analysis, 16 | # specified as globs (e.g.: "*.c") 17 | # By default, we analyze *.c, and *.cpp files 18 | # 19 | # You can completely override the default vaules by setting the variables above. 20 | # If you wish to use the defaults for CLANG_TIDY_DIRS and CLANG_TIDY_FILETYPES 21 | # but add additional directories or files, you can set these variables: 22 | # CLANG_TIDY_ADDITIONAL_DIRS 23 | # CLANG_TIDY_ADDITIONAL_FILETYPES 24 | # 25 | # If you wish to supply any additional arguments to clang-tidy, you can populate the 26 | # CLANG_TIDY_ADDITIONAL_OPTIONS variable. This should be a CMake list of flags and values. They 27 | # will be directly forwarded to the clang-tidy command. 28 | # Example: 29 | # set(CLANG_TIDY_ADDITIONAL_OPTIONS --fix) 30 | # include(cmake/analysis/clang-tidy.cmake) 31 | 32 | find_program(CLANG_TIDY clang-tidy) 33 | 34 | if(CLANG_TIDY) 35 | option(BUILD_WITH_CLANG_TIDY_ANALYSIS 36 | "Compile the project with clang-tidy support" 37 | OFF) 38 | 39 | if(BUILD_WITH_CLANG_TIDY_ANALYSIS) 40 | set(CMAKE_C_CLANG_TIDY ${CLANG_TIDY}) 41 | set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY}) 42 | endif() 43 | 44 | ### Supply argument defaults 45 | if(NOT CLANG_TIDY_DIRS) 46 | set(CLANG_TIDY_DIRS src test CACHE STRING "CMake list of directories to analyze with clang-tidy.") 47 | endif() 48 | 49 | if(NOT CLANG_TIDY_FILETYPES) 50 | set(CLANG_TIDY_FILETYPES "*.c" "*.cpp" CACHE STRING "CMake list of file types to analyze using clang-tidy.") 51 | endif() 52 | 53 | if(CLANG_TIDY_ADDITIONAL_DIRS) 54 | list(APPEND CLANG_TIDY_DIRS ${CLANG_TIDY_ADDITIONAL_DIRS}) 55 | endif() 56 | 57 | if(CLANG_TIDY_ADDITIONAL_FILETYPES) 58 | list(APPEND CLANG_TIDY_FILETYPES ${CLANG_TIDY_ADDITIONAL_FILETYPES}) 59 | endif() 60 | 61 | ## Convert Variables into script format 62 | string(REPLACE ";" "," CLANG_TIDY_DIRS_ARG "${CLANG_TIDY_DIRS}") 63 | string(REPLACE ";" "," CLANG_TIDY_FILETYPES_ARG "${CLANG_TIDY_FILETYPES}") 64 | 65 | add_custom_target(tidy 66 | COMMAND ${CMAKE_CURRENT_LIST_DIR}/clang-tidy.sh ${CMAKE_BINARY_DIR} 67 | # Directories to include in analysis 68 | ${CLANG_TIDY_DIRS_ARG} 69 | # File types to include in analysis 70 | ${CLANG_TIDY_FILETYPES_ARG} 71 | # Additional user options to pass to clang-tidy 72 | ${CLANG_TIDY_ADDITIONAL_OPTIONS} 73 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 74 | ) 75 | else() 76 | message("[WARNING] Clang-tidy is not installed. Clang-tidy targets are disabled.") 77 | endif() 78 | -------------------------------------------------------------------------------- /analysis/clang-tidy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script invocation: 3 | # clang-tidy.sh [location of compile_commands.json] [directories] [file types] 4 | # 5 | # Argument 1 is the path to the compile_commands.json file for your build. 6 | # Argument 2 is a comma-deliminted list of directories that will be included 7 | # in the clang-tidy analysis 8 | # e.g., "src,arch,test" 9 | # Argument 3 is a comma-deliminted list of file extensions or filenames to include 10 | # in the clang-tidy analysis 11 | # e.g., "*.c,*.cpp" 12 | 13 | # Argument 1 is the path to the directory containing the compile_commands.json file 14 | BUILD_OUTPUT_FOLDER=${1:-buildresults} 15 | 16 | # Argument 2 is a list of directories to include in clang-tidy analysis 17 | IFS=',' read -ra DIRS <<< "$2" 18 | 19 | # Argument 3 is a list of file names/types to include in clang-tidy analysis 20 | FILE_TYPES= 21 | IFS=',' read -ra ENTRIES <<< "$3" 22 | for entry in "${ENTRIES[@]}"; do 23 | FILE_TYPES="$FILE_TYPES -o -iname $entry" 24 | done 25 | 26 | # Remove the initial `-o` argument for the first file type 27 | # otherwise the rules will not be properly parsed 28 | FILE_TYPES=${FILE_TYPES:3:${#FILE_TYPES}} 29 | 30 | find ${DIRS[@]} ${FILE_TYPES} \ 31 | | xargs clang-tidy -p $BUILD_OUTPUT_FOLDER ${@:4} 32 | -------------------------------------------------------------------------------- /analysis/complexity.cmake: -------------------------------------------------------------------------------- 1 | ############################## 2 | # Complexity Analysis Module # 3 | ############################## 4 | # 5 | # This module provides complexity analysis targets to the build. 6 | # 7 | # You can control the behavior of lizard by setting the following variables 8 | # before you include this module: 9 | # LIZARD_DIRS, which is a CMake list of directories to include in complexity analysis 10 | # By default, the src and test directories are included 11 | # LIZARD_EXCLUDES, which is a CMake list of directories/files to exclude from analysis 12 | # By default, this variable is not set 13 | # LIZARD_LENGTH_LIMIT, which specifies the maximum funtion length in lines 14 | # LIZARD_CCN_LIMIT, which specifies the maximum CCN limit 15 | # LIZARD_ARG_LIMIT, which specifies the maximum number of arguments 16 | # 17 | # You can completely override the default vaules by setting the variables above. 18 | # You can also add to the defaults by defining these variables: 19 | # LIZARD_ADDITIONAL_DIRS, which is a CMake List of directories to include 20 | # LIZARD_ADDITIONAL_OPTIONS, which is a CMake list of additional flags and values to pass to Lizard 21 | 22 | find_program(LIZARD lizard) 23 | if(LIZARD) 24 | 25 | ## Set Default Arguments 26 | if(NOT LIZARD_DIRS) 27 | set(LIZARD_DIRS src test CACHE STRING "CMake list of directories to analyze with lizard.") 28 | endif() 29 | 30 | if(LIZARD_ADDITIONAL_DIRS) 31 | list(APPEND LIZARD_DIRS "${LIZARD_ADDITIONAL_DIRS}") 32 | endif() 33 | 34 | if(NOT LIZARD_LENGTH_LIMIT) 35 | set(LIZARD_LENGTH_LIMIT 60 CACHE STRING "The maximum length of a function (in lines) before a failure is triggered.") 36 | endif() 37 | 38 | if(NOT LIZARD_CCN_LIMIT) 39 | set(LIZARD_CCN_LIMIT 10 CACHE STRING "The maximum CCN of a function before a failure is triggered.") 40 | endif() 41 | 42 | if(NOT LIZARD_ARG_LIMIT) 43 | set(LIZARD_ARG_LIMIT 6 CACHE STRING "The maximum number of function arguments before a failure is triggered.") 44 | endif() 45 | 46 | if(LIZARD_EXCLUDES) 47 | foreach(exclude ${LIZARD_EXCLUDES}) 48 | list(APPEND LIZARD_EXCLUDE_ARGS -x ${exclude}) 49 | endforeach() 50 | endif() 51 | 52 | set(LIZARD_BASE_ARGS 53 | ${LIZARD} 54 | --length ${LIZARD_LENGTH_LIMIT} # fail when functions longer than this 55 | --CCN ${LIZARD_CCN_LIMIT} # fail over this CCN 56 | --arguments ${LIZARD_ARG_LIMIT} # fail this arg count 57 | ${LIZARD_EXCLUDE_ARGS} 58 | ${LIZARD_ADDITIONAL_OPTIONS} 59 | ) 60 | 61 | ### Targets 62 | add_custom_target(complexity 63 | COMMAND 64 | ${LIZARD_BASE_ARGS} 65 | -w # Only show warnings 66 | ${LIZARD_DIRS} 67 | WORKING_DIRECTORY 68 | ${CMAKE_SOURCE_DIR} 69 | ) 70 | 71 | add_custom_target(complexity-full 72 | COMMAND 73 | ${LIZARD_BASE_ARGS} 74 | ${LIZARD_DIRS} 75 | WORKING_DIRECTORY 76 | ${CMAKE_SOURCE_DIR} 77 | ) 78 | 79 | add_custom_target(complexity-xml 80 | COMMAND 81 | ${LIZARD_BASE_ARGS} 82 | --xml # Generate XML output 83 | ${LIZARD_DIRS} 84 | # Redirect output to file 85 | > ${CMAKE_BINARY_DIR}/complexity.xml 86 | BYPRODUCTS 87 | ${CMAKE_BINARY_DIR}/complexity.xml 88 | WORKING_DIRECTORY 89 | ${CMAKE_SOURCE_DIR} 90 | ) 91 | else() 92 | message("[WARNING] Lizard is not installed. Complexity targets are disabled.") 93 | endif() 94 | -------------------------------------------------------------------------------- /analysis/coverage.cmake: -------------------------------------------------------------------------------- 1 | ######################## 2 | # Code Coverage Module # 3 | ######################## 4 | # This module is a wrapper around CodeCoverage.cmake, simplifying its use and auto-creating 5 | # code coverage build targets. 6 | # 7 | # This module defines a `ENABLE_COVERAGE_ANALYSIS` option, which defaults to OFF. 8 | # This option is only available when creating a debug build. 9 | # 10 | # Include this module at the top of the build file, before you define any build targets. 11 | # This will ensure that the proper compilation flags are added to the build. 12 | # 13 | # After your targets are defined, you can invoke the `enable_coverage_targets` function. 14 | # Call this function and supply any targets which you want to register as dependencies 15 | # for the coverage targets. 16 | # 17 | # Example: 18 | # enable_coverage_targets(libc_tests printf_tests) 19 | 20 | include(CMakeDependentOption) 21 | 22 | CMAKE_DEPENDENT_OPTION(ENABLE_COVERAGE_ANALYSIS 23 | "Enable code coverage analysis." 24 | OFF 25 | "\"${CMAKE_BUILD_TYPE}\" STREQUAL \"Debug\"" 26 | OFF) 27 | 28 | if(ENABLE_COVERAGE_ANALYSIS) 29 | include(${CMAKE_CURRENT_LIST_DIR}/CodeCoverage.cmake) 30 | append_coverage_compiler_flags() 31 | endif() 32 | 33 | 34 | # Call this function with dependencies that should be added to the coverage targets 35 | function(enable_coverage_targets) 36 | if(ENABLE_COVERAGE_ANALYSIS) 37 | setup_target_for_coverage_gcovr_xml( 38 | NAME coverage-xml 39 | EXECUTABLE ctest 40 | DEPENDENCIES ${ARGN} 41 | ) 42 | 43 | setup_target_for_coverage_gcovr_html( 44 | NAME coverage-html 45 | EXECUTABLE ctest 46 | DEPENDENCIES ${ARGN} 47 | ) 48 | 49 | add_custom_target(coverage 50 | DEPENDS coverage-xml coverage-html) 51 | endif() 52 | endfunction() 53 | -------------------------------------------------------------------------------- /analysis/cppcheck.cmake: -------------------------------------------------------------------------------- 1 | ################### 2 | # CppCheck Module # 3 | ################### 4 | # 5 | # This module provides `cppcheck` and `cppcheck-xml` targets, as well as an option 6 | # to enable building the project with cppcheck analysis. 7 | # 8 | # To enable cppcheck output during the build process, set the `BUILD_WITH_CPPCHECK_ANALYSIS` 9 | # option to "ON" 10 | # 11 | # You can control the behavior of cppcheck by setting the following variables 12 | # before you include this module: 13 | # CPPCHECK_ENABLE_CHECKS, which is passed to the --enable= argument 14 | # By default, "style" is used 15 | # CPPCHECK_DIRS, which is a CMake list of directories to include in cppcheck analysis 16 | # By default, the src and test directories are included 17 | # CPPCHECK_INCLUDE_DIRS, which is a CMake list of directories to pass as include arguments 18 | # to CppCheck 19 | # By default, no include directories are specified. 20 | # CPPCHECK_EXCLUDES, which is a CMake list of directories or files to exclude from analysis. 21 | # By defaulut, no excludes are specified 22 | # 23 | # You can completely override the default vaules by setting the variables above. 24 | # If you wish to use the defaults for CPPCHECK_DIRS but add additional directories or files, 25 | # you can set the CPPCHECK_ADDITIONAL_DIRS variable. 26 | 27 | find_program(CPPCHECK cppcheck) 28 | 29 | if(CPPCHECK) 30 | 31 | if(NOT CPPCHECK_ENABLE_CHECKS) 32 | set(CPPCHECK_ENABLE_CHECKS style CACHE STRING "Value to pass to the CppCheck --enable= flag") 33 | endif() 34 | 35 | if(CPPCHECK_INCLUDE_DIRS) 36 | foreach(dir ${CPPCHECK_INCLUDE_DIRS}) 37 | list(APPEND CPPCHECK_INCLUDE_DIRS_ARG -I ${dir}) 38 | endforeach() 39 | endif() 40 | 41 | if(CPPCHECK_EXCLUDES) 42 | foreach(exclude ${CPPCHECK_EXCLUDES}) 43 | list(APPEND CPPCHECK_EXCLUDE_ARGS -i ${exclude}) 44 | endforeach() 45 | endif() 46 | 47 | ### Custom Rules 48 | if(NOT CPPCHECK_CUSTOM_RULES) 49 | set(CPPCHECK_CUSTOM_RULES 50 | ${CMAKE_CURRENT_LIST_DIR}/cppcheck-rules/BitwiseOperatorInConditional/rule.xml 51 | ${CMAKE_CURRENT_LIST_DIR}/cppcheck-rules/CollapsibleIfStatements/rule.xml 52 | ${CMAKE_CURRENT_LIST_DIR}/cppcheck-rules/EmptyElseBlock/rule.xml 53 | ${CMAKE_CURRENT_LIST_DIR}/cppcheck-rules/EmptyCatchStatement/rule.xml 54 | ${CMAKE_CURRENT_LIST_DIR}/cppcheck-rules/EmptyDoWhileStatement/rule.xml 55 | ${CMAKE_CURRENT_LIST_DIR}/cppcheck-rules/EmptyForStatement/rule.xml 56 | ${CMAKE_CURRENT_LIST_DIR}/cppcheck-rules/EmptyIfStatement/rule.xml 57 | ${CMAKE_CURRENT_LIST_DIR}/cppcheck-rules/EmptySwitchStatement/rule.xml 58 | ${CMAKE_CURRENT_LIST_DIR}/cppcheck-rules/ForLoopShouldBeWhileLoop/rule.xml 59 | ${CMAKE_CURRENT_LIST_DIR}/cppcheck-rules/InvertedLogic/rule.xml 60 | ${CMAKE_CURRENT_LIST_DIR}/cppcheck-rules/MultipleUnaryOperator/rule.xml 61 | ${CMAKE_CURRENT_LIST_DIR}/cppcheck-rules/RedundantConditionalOperator/rule.xml 62 | ${CMAKE_CURRENT_LIST_DIR}/cppcheck-rules/RedundantIfStatement/rule.xml 63 | ${CMAKE_CURRENT_LIST_DIR}/cppcheck-rules/UnnecessaryElseStatement/rule.xml 64 | ${CMAKE_CURRENT_LIST_DIR}/cppcheck-rules/UseStlAlgorithm/rule.xml 65 | CACHE STRING "Custom rule files to use with CppCheck") 66 | endif() 67 | 68 | if(CPPCHECK_ADDITIONAL_CUSTOM_RULES) 69 | list(APPEND CPPCHECK_CUSTOM_RULES ${CPPCHECK_ADDITIONAL_CUSTOM_RULES}) 70 | endif() 71 | 72 | foreach(rule ${CPPCHECK_CUSTOM_RULES}) 73 | list(APPEND CPPCHECK_CUSTOM_RULES_ARG --rule-file=${rule}) 74 | endforeach() 75 | 76 | # With CppCheck, default arguments are shared between the analysis-during-build 77 | # configuration and with the cppcheck build targets 78 | set(CPPCHECK_DEFAULT_ARGS 79 | ${CPPCHECK} --quiet --enable=${CPPCHECK_ENABLE_CHECKS} --force 80 | ${CPPCHECK_INCLUDE_DIRS_ARG} 81 | ${CPPCHECK_EXCLUDE_ARGS} 82 | ${CPPCHECK_CUSTOM_RULES_ARG} 83 | ) 84 | 85 | ### Static analysis build option 86 | option(BUILD_WITH_CPPCHECK_ANALYSIS 87 | "Compile the project with cppcheck support." 88 | OFF) 89 | 90 | if(BUILD_WITH_CPPCHECK_ANALYSIS) 91 | set(CMAKE_C_CPPCHECK ${CPPCHECK_DEFAULT_ARGS}) 92 | set(CMAKE_CXX_CPPCHECK ${CPPCHECK_DEFAULT_ARGS}) 93 | endif() 94 | 95 | ### Supply argument defaults for targets 96 | if(NOT CPPCHECK_DIRS) 97 | set(CPPCHECK_DIRS src test CACHE STRING "CMake list of directories to analyze with cppcheck.") 98 | endif() 99 | 100 | if(CPPCHECK_ADDITIONAL_DIRS) 101 | list(APPEND CPPCHECK_DIRS ${CPPCHECK_ADDITIONAL_DIRS}) 102 | endif() 103 | 104 | ### CppCheck Targets 105 | add_custom_target(cppcheck 106 | COMMAND ${CPPCHECK_DEFAULT_ARGS} 107 | ${CPPCHECK_DIRS} 108 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 109 | ) 110 | 111 | add_custom_target(cppcheck-xml 112 | COMMAND ${CPPCHECK_DEFAULT_ARGS} 113 | # enable XML output 114 | --xml --xml-version=2 115 | # Source directories 116 | ${CPPCHECK_DIRS} 117 | # Redirect to file 118 | 2>${CMAKE_BINARY_DIR}/cppcheck.xml 119 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 120 | ) 121 | else() 122 | message("[WARNING] CppCheck is not installed. CppCheck targets are disabled.") 123 | endif() 124 | -------------------------------------------------------------------------------- /analysis/sanitizers.cmake: -------------------------------------------------------------------------------- 1 | ######################### 2 | # Code Sanitizer Module # 3 | ######################### 4 | # This module provides a USE_SANITIZER option which can enable code sanitizers with GCC and Clang. 5 | # Include this module before you define your targets to ensure the proper arguments are added 6 | # to your targets. 7 | 8 | set(USE_SANITIZER 9 | "" CACHE STRING 10 | "Compile with a sanitizer. Options are: Address, Memory, Leak, Undefined, Thread, 'Address;Undefined'" 11 | ) 12 | 13 | if(USE_SANITIZER MATCHES "([Aa]ddress)") 14 | add_compile_options(-fsanitize=address) 15 | add_link_options(-fsanitize=address) 16 | elseif(USE_SANITIZER MATCHES "([Tt]hread)") 17 | add_compile_options(-fsanitize=thread) 18 | add_link_options(-fsanitize=thread) 19 | elseif(USE_SANITIZER MATCHES "([Uu]ndefined)") 20 | add_compile_options(-fsanitize=undefined) 21 | add_link_options(-fsanitize=undefined) 22 | elseif(USE_SANITIZER MATCHES "([Ll]eak)") 23 | add_compile_options(-fsanitize=leak) 24 | add_link_options(-fsanitize=leak) 25 | elseif(USE_SANITIZER MATCHES "([Mm]emory)") 26 | add_compile_options(-fsanitize=memory) 27 | add_link_options(-fsanitize=memory) 28 | elseif(USE_SANITIZER MATCHES "([Aa]ddress),([Uu]ndefined)") 29 | add_compile_options(-fsanitize=address,undefined) 30 | add_link_options(-fsanitize=address,undefined) 31 | elseif(NOT "${USE_SANITIZER}" STREQUAL "" AND NOT "${USE_SANITIZER}" STREQUAL "None") 32 | message(FATAL_ERROR "Unsupported value of USE_SANITIZER: ${USE_SANITIZER}") 33 | endif() 34 | 35 | # Set the possible values of build type for cmake-gui/ccmake 36 | set_property(CACHE USE_SANITIZER 37 | PROPERTY STRINGS "None" "Address" "Thread" "Undefined" "Leak" "Memory" "Address,Undefined" 38 | ) 39 | -------------------------------------------------------------------------------- /analysis/sloccount.cmake: -------------------------------------------------------------------------------- 1 | #################### 2 | # SLOCCount Module # 3 | #################### 4 | # 5 | # This module adds an sloccount target to your build system, which provides 6 | # line of code analysis and cost/effort estimates for your project. 7 | # 8 | # All variables used below can be overridden by the user. Simply define the variables 9 | # with your own values before invoking this module. 10 | # 11 | # The settings for this module default to "embedded" COCOMO mode: 12 | # Embedded: The project must operate within tight (hard-to-meet) constraints, 13 | # and requirements and interface specifications are often non-negotiable. 14 | # The software will be embedded in a complex environment that the software must deal with as-is. 15 | # 16 | # Values for the models are: 17 | # Organic: effort factor = 2.4, exponent = 1.05; schedule factor = 2.5, exponent = 0.38 18 | # Semidetached: effort factor = 3.0, exponent = 1.12; schedule factor = 2.5, exponent = 0.35 19 | # Embedded: effort factor = 3.6, exponent = 1.20; schedule factor = 2.5, exponent = 0.32 20 | # 21 | # For information on tuning the model, see: 22 | # https://dwheeler.com/sloccount/sloccount.html#cocomo 23 | 24 | find_program(SLOCCount sloccount) 25 | 26 | if(SLOCCount) 27 | # Set defaults 28 | if(NOT SLOCCOUNT_DIRS) 29 | set(SLOCCOUNT_DIRS 30 | src 31 | test 32 | CACHE STRING "Directories to analyze with SLOCCount.") 33 | endif() 34 | 35 | if(SLOCCOUNT_ADDITIONAL_DIRS) 36 | lists(APPEND SLOCCOUNT_DIRS ${SLOCCOUNT_ADDITIONAL_DIRS}) 37 | endif() 38 | 39 | # SLOCCOUNT_EFFORT is a pair of values [F, E], where F is the factor and E is the exponent. 40 | # this impacts calculated effort in person-months. 41 | if(NOT SLOCCOUNT_EFFORT) 42 | set(SLOCCOUNT_EFFORT 3.6;1.12 43 | CACHE STRING "A pair of values F;E where F is the factor and E is the exponent. This impacts calculated effort in person months.") 44 | endif() 45 | 46 | # SLOCCOUNT_SCHEDULE is a pair of values [F, E], where F is the factor and E is the exponent. 47 | # this impacts calculated effort in person-months. 48 | if(NOT SLOCCOUNT_SCHEDULE) 49 | set(SLOCCOUNT_SCHEDULE 2.5;0.32 50 | CACHE STRING "A pair of values F;E where F is the factor and E is the exponent. This impacts calculated effort in person months.") 51 | endif() 52 | 53 | # The average salary value to use in cost estimates 54 | if(NOT SLOCCOUNT_SALARY) 55 | set(SLOCCOUNT_SALARY 140000 56 | CACHE STRING "The average salary value to use in cost estimates.") 57 | endif() 58 | 59 | # The developer overhead value to use in cost estimates 60 | if(NOT SLOCCOUNT_OVERHEAD) 61 | set(SLOCCOUNT_OVERHEAD 2 62 | CACHE STRING "The developer overhead value to use in cost estimates.") 63 | endif() 64 | 65 | if(NOT SLOCCOUNT_DATA_DIR) 66 | set(SLOCCOUNT_DATA_DIR ${CMAKE_BINARY_DIR}/sloccount 67 | CACHE STRING "The location that sloccount will cache data.") 68 | endif() 69 | 70 | # Create the output directory 71 | file(MAKE_DIRECTORY ${SLOCCOUNT_DATA_DIR}) 72 | 73 | # You can override the default set by defining this variable 74 | if(NOT SLOCCOUNT_ARGS) 75 | set(SLOCCOUNT_ARGS 76 | # Follow symbolic links 77 | --follow 78 | # Store data in the output directory 79 | --datadir ${SLOCCOUNT_DATA_DIR} 80 | # Effort factor/exponent 81 | --effort ${SLOCCOUNT_EFFORT} 82 | # Schedule factor/exponent 83 | --schedule ${SLOCCOUNT_SCHEDULE} 84 | # Annual Salary Setting 85 | --personcost ${SLOCCOUNT_SALARY} 86 | # Overhad value 87 | --overhead ${SLOCCOUNT_OVERHEAD} 88 | CACHE STRING "Default arguments supplied to SLOCCount" 89 | ) 90 | endif() 91 | 92 | # Add arguments to the default set 93 | if(SLOCCOUNT_ADDITIONAL_ARGS) 94 | list(APPEND SLOCCOUNT_ARGS ${SLOCCOUNT_ADDITIONAL_ARGS}) 95 | endif() 96 | 97 | ################## 98 | # Custom Targets # 99 | ################## 100 | add_custom_target(sloccount 101 | COMMAND ${SLOCCount} ${SLOCCOUNT_ARGS} ${SLOCCOUNT_DIRS} 102 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 103 | ) 104 | 105 | add_custom_target(sloccount-full 106 | COMMAND ${SLOCCount} ${SLOCCOUNT_ARGS} 107 | # Show details for every source code file 108 | --details 109 | ${SLOCCOUNT_DIRS} 110 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 111 | ) 112 | 113 | add_custom_target(sloccount-report 114 | COMMAND ${SLOCCount} ${SLOCCOUNT_ARGS} 115 | # Show details for every source code file 116 | --details 117 | ${SLOCCOUNT_DIRS} 118 | # Redirect output to file 119 | > ${CMAKE_BINARY_DIR}/sloccount.sc 120 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 121 | ) 122 | set_target_properties(sloccount-report 123 | PROPERTIES ADDITIONAL_CLEAN_FILES ${CMAKE_BINARY_DIR}/sloccount.sc) 124 | 125 | add_custom_target(sloccount-report-full 126 | COMMAND ${SLOCCount} ${SLOCCOUNT_ARGS} 127 | # Show details for every source code file 128 | --details 129 | ${SLOCCOUNT_DIRS} 130 | # Redirect output to file 131 | > ${CMAKE_BINARY_DIR}/sloccount_detailed.sc 132 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 133 | ) 134 | set_target_properties(sloccount-report-full 135 | PROPERTIES ADDITIONAL_CLEAN_FILES ${CMAKE_BINARY_DIR}/sloccount_detailed.sc) 136 | 137 | endif() 138 | -------------------------------------------------------------------------------- /analysis/vale.cmake: -------------------------------------------------------------------------------- 1 | ################################ 2 | # Vale Document Linting Module # 3 | ################################ 4 | # This module adds a vale document linting target to your build system. 5 | # 6 | # All variables used below can be overridden by the user. Simply define the variables 7 | # with your own values before invoking this module. 8 | 9 | find_program(VALE vale) 10 | 11 | if(VALE) 12 | # Code goes here 13 | 14 | if(NOT VALE_FILES) 15 | set(VALE_FILES 16 | docs/ README.md 17 | CACHE STRING "List of folders and files for vale to lint.") 18 | endif() 19 | 20 | if(VALE_ADDITIONAL_FILES) 21 | list(APPEND VALE_FILES ${VALE_ADDITIONAL_FILES}) 22 | endif() 23 | 24 | if(NOT VALE_FILETYPES) 25 | set(VALE_FILETYPES "md,rst,txt" 26 | CACHE STRING "Comma-separated list of filetypes for vale to lint.") 27 | endif() 28 | 29 | if(VALE_ADDITIONAL_FILETYPES) 30 | list(APPEND VALE_FILETYPES ${VALE_ADDITIONAL_FILETYPES}) 31 | string(REPLACE ";" "," VALE_FILETYPES ${VALE_FILETYPES}) 32 | endif() 33 | 34 | # Supply a style file, which will use this file instead of the default .vale.ini 35 | if(NOT VALE_CONFIG_FILE) 36 | set(VALE_CONFIG_FILE 37 | ${CMAKE_CURRENT_LIST_DIR}/vale-styleguide/config/documentation.vale.ini 38 | CACHE STRING "Configuration file to use instead of the default .vale.ini" 39 | ) 40 | endif() 41 | 42 | if(NOT VALE_ARGS) 43 | set(VALE_ARGS 44 | --glob='*.{${VALE_FILETYPES}}' 45 | CACHE STRING "Arguments passed to Vale." 46 | ) 47 | 48 | if(NOT "${VALE_CONFIG_FILE}" STREQUAL "") 49 | list(APPEND VALE_ARGS --config ${VALE_CONFIG_FILE}) 50 | endif() 51 | endif() 52 | 53 | if(VALE_ADDITIONAL_ARGS) 54 | list(APPEND VALE_ARGS ${VALE_ADDITIONAL_ARGS}) 55 | endif() 56 | 57 | ################ 58 | # Vale Targets # 59 | ################ 60 | add_custom_target(vale 61 | COMMAND ${VALE} ${VALE_ARGS} ${VALE_FILES} 62 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 63 | ) 64 | 65 | endif() 66 | -------------------------------------------------------------------------------- /compiler/CheckAndApplyFlags.cmake: -------------------------------------------------------------------------------- 1 | 2 | # Read enabled languages to determine what functionality to provide 3 | get_property(enabled_languages GLOBAL PROPERTY ENABLED_LANGUAGES) 4 | 5 | ########################### 6 | # Common Macros/Functions # 7 | ########################### 8 | 9 | # Change - to _ to generate safe variable names 10 | macro(make_safe_varname input_string output_var) 11 | string(REPLACE - _ ${output_var} ${input_string}) 12 | endmacro() 13 | 14 | # Use this function to promote an input argument that represents a list 15 | # such that two layers of dereferencing aren't needed. 16 | # This enables supporting both string and variable inputs with a function arg. 17 | macro(check_and_double_deref input) 18 | if(NOT "${${${input}}}" STREQUAL "") 19 | set(${input} ${${${input}}}) 20 | endif() 21 | endmacro() 22 | 23 | ######################################### 24 | # C Language Support (For CMake < 3.18) # 25 | ######################################### 26 | 27 | if("C" IN_LIST enabled_languages) 28 | include(CheckCCompilerFlag) 29 | 30 | function(apply_supported_c_compiler_flags target scope flag_list) 31 | check_and_double_deref(flag_list) 32 | foreach(flag ${flag_list}) 33 | make_safe_varname(${flag} flag_var) 34 | check_c_compiler_flag(${flag} ${flag_var}) 35 | 36 | if(${flag_var}) 37 | target_compile_options(${target} ${scope} ${flag}) 38 | endif() 39 | endforeach() 40 | endfunction() 41 | 42 | function(check_c_linker_flag flag out_var) 43 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${flag}") 44 | check_c_compiler_flag("" ${out_var}) 45 | endfunction() 46 | 47 | function(apply_supported_c_linker_flags target scope flag_list) 48 | check_and_double_deref(flag_list) 49 | foreach(flag ${flag_list}) 50 | make_safe_varname(${flag} flag_var) 51 | check_c_linker_flag(${flag} ${flag_var}) 52 | 53 | if(${flag_var}) 54 | target_link_options(${target} ${scope} ${flag}) 55 | endif() 56 | endforeach() 57 | endfunction() 58 | 59 | # This function uses the compiler to check whether the linker flag is supported, 60 | # then adds it to the link options if it is. 61 | function(apply_supported_c_linker_flags_using_compiler target scope flag_list) 62 | check_and_double_deref(flag_list) 63 | foreach(flag ${flag_list}) 64 | make_safe_varname(${flag} flag_var) 65 | check_c_compiler_flag(${flag} ${flag_var}) 66 | 67 | if(${flag_var}) 68 | target_link_options(${target} ${scope} ${flag}) 69 | endif() 70 | endforeach() 71 | endfunction() 72 | endif() 73 | 74 | ########################################### 75 | # CXX Language Support (For CMake < 3.18) # 76 | ########################################### 77 | 78 | if("CXX" IN_LIST enabled_languages) 79 | include(CheckCXXCompilerFlag) 80 | 81 | function(apply_supported_cxx_compiler_flags target scope flag_list) 82 | check_and_double_deref(flag_list) 83 | foreach(flag ${flag_list}) 84 | make_safe_varname(${flag} flag_var) 85 | check_cxx_compiler_flag(${flag} ${flag_var}) 86 | 87 | if(${flag_var}) 88 | target_compile_options(${target} ${scope} ${flag}) 89 | endif() 90 | endforeach() 91 | endfunction() 92 | 93 | function(check_cxx_linker_flag flag out_var) 94 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${flag}") 95 | check_cxx_compiler_flag("" ${out_var}) 96 | endfunction() 97 | 98 | function(apply_supported_cxx_linker_flags target scope flag_list) 99 | check_and_double_deref(flag_list) 100 | foreach(flag ${flag_list}) 101 | make_safe_varname(${flag} flag_var) 102 | check_cxx_linker_flag(${flag} ${flag_var}) 103 | 104 | if(${flag_var}) 105 | target_link_options(${target} ${scope} ${flag}) 106 | endif() 107 | endforeach() 108 | endfunction() 109 | 110 | # This function uses the compiler to check whether the linker flag is supported, 111 | # then adds it to the link options if it is. 112 | function(apply_supported_cxx_linker_flags_using_compiler target scope flag_list) 113 | check_and_double_deref(flag_list) 114 | foreach(flag ${flag_list}) 115 | make_safe_varname(${flag} flag_var) 116 | check_cxx_compiler_flag(${flag} ${flag_var}) 117 | 118 | if(${flag_var}) 119 | target_link_options(${target} ${scope} ${flag}) 120 | endif() 121 | endforeach() 122 | endfunction() 123 | endif() 124 | 125 | ############################# 126 | # Common Compiler Interface # 127 | ############################# 128 | 129 | function(apply_supported_compiler_flags lang target scope flag_list) 130 | if(${CMAKE_VERSION} VERSION_LESS "3.18") 131 | if(${lang} STREQUAL C) 132 | apply_supported_c_compiler_flags(${target} ${scope} ${flag_list}) 133 | elseif(${lang} STREQUAL CXX) 134 | apply_supported_cxx_compiler_flags(${target} ${scope} ${flag_list}) 135 | else() 136 | message(FATAL_ERROR "Language ${lang} is not supported by this function.") 137 | endif() 138 | else() 139 | include(Check${lang}CompilerFlag) 140 | 141 | check_and_double_deref(flag_list) 142 | foreach(flag ${flag_list}) 143 | make_safe_varname(${flag} flag_var) 144 | cmake_language(CALL check_${lang}_compiler_flag ${flag} ${flag_var}) 145 | 146 | if(${flag_var}) 147 | target_compile_options(${target} ${scope} ${flag}) 148 | endif() 149 | endforeach() 150 | endif() 151 | endfunction() 152 | 153 | function(apply_supported_compiler_flags_globally lang flag_list) 154 | check_and_double_deref(flag_list) 155 | foreach(flag ${flag_list}) 156 | make_safe_varname(${flag} flag_var) 157 | if(${CMAKE_VERSION} VERSION_LESS "3.18") 158 | if(${LANG} STREQUAL C) 159 | check_c_compiler_flag(${flag} ${flag_var}) 160 | elseif(${lang} STREQUAL CXX) 161 | check_cxx_compiler_flag(${flag} ${flag_var}) 162 | else() 163 | message(FATAL_ERROR "Language ${lang} is not supported by this function.") 164 | endif() 165 | else() 166 | include(Check${lang}CompilerFlag) 167 | cmake_language(CALL check_${lang}_compiler_flag ${flag} ${flag_var}) 168 | endif() 169 | if(${flag_var}) 170 | add_compile_options($<$:${flag}>) 171 | endif() 172 | endforeach() 173 | endfunction() 174 | 175 | ########################### 176 | # Common Linker Interface # 177 | ########################### 178 | 179 | if(${CMAKE_VERSION} VERSION_LESS "3.18") 180 | function(apply_supported_linker_flags lang target scope flag_list) 181 | if(${lang} STREQUAL C) 182 | apply_supported_c_linker_flags(${target} ${scope} ${flag_list}) 183 | elseif(${lang} STREQUAL CXX) 184 | apply_supported_cxx_linker_flags(${target} ${scope} ${flag_list}) 185 | else() 186 | message(FATAL_ERROR "Language ${lang} is not supported by this function.") 187 | endif() 188 | endfunction() 189 | else() 190 | include(CheckLinkerFlag) 191 | 192 | function(apply_supported_linker_flags lang target scope flag_list) 193 | check_and_double_deref(flag_list) 194 | foreach(flag ${flag_list}) 195 | make_safe_varname(${flag} flag_var) 196 | check_linker_flag(${lang} ${flag} ${flag_var}) 197 | 198 | if(${flag_var}) 199 | target_link_options(${target} ${scope} ${flag}) 200 | endif() 201 | endforeach() 202 | endfunction() 203 | endif() 204 | 205 | if(${CMAKE_VERSION} VERSION_LESS "3.18") 206 | function(apply_supported_linker_flags_using_compiler lang target scope flag_list) 207 | if(${lang} STREQUAL C) 208 | apply_supported_c_linker_flags_using_compiler(${target} ${scope} ${flag_list}) 209 | elseif(${lang} STREQUAL CXX) 210 | apply_supported_cxx_linker_flags_using_compiler(${target} ${scope} ${flag_list}) 211 | else() 212 | message(FATAL_ERROR "Language ${lang} is not supported by this function.") 213 | endif() 214 | endfunction() 215 | else() 216 | function(apply_supported_linker_flags_using_compiler lang target scope flag_list) 217 | check_and_double_deref(flag_list) 218 | foreach(flag ${flag_list}) 219 | make_safe_varname(${flag} flag_var) 220 | cmake_language(CALL check_${lang}_compiler_flag ${flag} ${flag_var}) 221 | 222 | if(${flag_var}) 223 | target_link_options(${target} ${scope} ${flag}) 224 | endif() 225 | endforeach() 226 | endfunction() 227 | endif() 228 | 229 | function(apply_supported_linker_flags_globally lang flag_list) 230 | check_and_double_deref(flag_list) 231 | foreach(flag ${flag_list}) 232 | make_safe_varname(${flag} flag_var) 233 | if(${CMAKE_VERSION} VERSION_LESS "3.18") 234 | if(${LANG} STREQUAL C) 235 | check_c_linker_flag(${flag} ${flag_var}) 236 | elseif(${lang} STREQUAL CXX) 237 | check_cxx_linker_flag(${flag} ${flag_var}) 238 | else() 239 | message(FATAL_ERROR "Language ${lang} is not supported by this function.") 240 | endif() 241 | else() 242 | include(CheckLinkerFlag) 243 | check_linker_flag(${lang} ${flag} ${flag_var}) 244 | endif() 245 | if(${flag_var}) 246 | add_link_options($<$,${lang}>:${flag}>) 247 | endif() 248 | endforeach() 249 | endfunction() 250 | 251 | function(apply_supported_linker_flags_globally_using_compiler lang flag_list) 252 | check_and_double_deref(flag_list) 253 | foreach(flag ${flag_list}) 254 | make_safe_varname(${flag} flag_var) 255 | if(${CMAKE_VERSION} VERSION_LESS "3.18") 256 | if(${LANG} STREQUAL C) 257 | check_c_compiler_flag(${flag} ${flag_var}) 258 | elseif(${lang} STREQUAL CXX) 259 | check_cxx_compiler_flag(${flag} ${flag_var}) 260 | else() 261 | message(FATAL_ERROR "Language ${lang} is not supported by this function.") 262 | endif() 263 | else() 264 | include(CheckLinkerFlag) 265 | cmake_language(CALL check_${lang}_compiler_flag ${flag} ${flag_var}) 266 | endif() 267 | if(${flag_var}) 268 | add_link_options($<$,${lang}>:${flag}>) 269 | endif() 270 | endforeach() 271 | endfunction() 272 | -------------------------------------------------------------------------------- /compiler/DefaultCompilerSettings.cmake: -------------------------------------------------------------------------------- 1 | ############################# 2 | # Default Compiler Settings # 3 | ############################# 4 | # 5 | # This module applies compiler flags that are commonly used on Embedded Artistry projects. 6 | 7 | include(${CMAKE_CURRENT_LIST_DIR}/CheckAndApplyFlags.cmake) 8 | 9 | # These are warning/diagnostic settings that we want to apply on our projects 10 | set(desired_common_warning_flags 11 | # Base Warnings 12 | -Wall 13 | -Wextra 14 | # Diagnostics 15 | -fdiagnostics-show-option 16 | -fcolor-diagnostics 17 | # Disabled Warnings 18 | -Wno-padded 19 | # Desired Warnings 20 | -Wfloat-equal 21 | -Wconversion 22 | -Wlogical-op 23 | -Wundef 24 | -Wredundant-decls 25 | -Wshadow 26 | -Wstrict-overflow=2 27 | -Wwrite-strings 28 | -Wpointer-arith 29 | -Wcast-qual 30 | -Wformat=2 31 | -Wformat-truncation 32 | -Wmissing-include-dirs 33 | -Wcast-align 34 | -Wswitch-enum 35 | -Wsign-conversion 36 | -Wdisabled-optimization 37 | -Winline 38 | -Winvalid-pch 39 | -Wmissing-declarations 40 | -Wdouble-promotion 41 | -Wshadow 42 | -Wtrampolines 43 | -Wvector-operation-performance 44 | -Wshift-overflow=2 45 | -Wnull-dereference 46 | -Wduplicated-cond 47 | -Wshift-overflow=2 48 | -Wnull-dereference 49 | -Wduplicated-cond 50 | -Wcast-align=strict 51 | ) 52 | 53 | set(desired_cpp_warning_flags 54 | -Wold-style-cast 55 | -Wnon-virtual-dtor 56 | -Wctor-dtor-privacy 57 | -Woverloaded-virtual 58 | -Wnoexcept 59 | -Wstrict-null-sentinel 60 | -Wuseless-cast 61 | -Wzero-as-null-pointer-constant 62 | -Wextra-semi 63 | # Disabled Warnings 64 | -Wno-c++98-compat 65 | -Wno-c++98-compat-pedantic 66 | -Wno-exit-time-destructors # causes warnings if you use static values 67 | -Wno-global-constructors # causes warnings if you use static values 68 | -Wno-covered-switch-default 69 | ) 70 | 71 | set(desired_common_compiler_optimization_flags 72 | -ffunction-sections # Place each function in its own section (ELF Only) 73 | -fdata-sections # Place each data in its own section (ELF Only) 74 | -fdevirtualize # Attempt to convert calls to virtual functions to direct calls 75 | ) 76 | 77 | #################################################### 78 | # Settings enforced when we're the primary project # 79 | #################################################### 80 | 81 | if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) 82 | apply_supported_compiler_flags_globally(C desired_common_warning_flags) 83 | apply_supported_compiler_flags_globally(CXX desired_common_warning_flags) 84 | apply_supported_compiler_flags_globally(CXX desired_cpp_warning_flags) 85 | apply_supported_compiler_flags_globally(C desired_common_compiler_optimization_flags) 86 | apply_supported_compiler_flags_globally(CXX desired_common_compiler_optimization_flags) 87 | endif() 88 | 89 | #################################### 90 | # Settings enforced no matter what # 91 | #################################### 92 | 93 | apply_supported_compiler_flags_globally(C "-Wno-unknown-pragmas") 94 | apply_supported_compiler_flags_globally(CXX "-Wno-unknown-pragmas") 95 | -------------------------------------------------------------------------------- /documentation/doxygen.cmake: -------------------------------------------------------------------------------- 1 | ######################## 2 | # Doxygen Build Module # 3 | ######################## 4 | 5 | find_program(DOXYGEN doxygen) 6 | 7 | if(DOXYGEN) 8 | ## Set default values for variables 9 | if(NOT DOXYGEN_PROJECT_NAME) 10 | set(DOXYGEN_PROJECT_NAME ${PROJECT_NAME} CACHE STRING "Project name used for Doxygen documentation.") 11 | endif() 12 | 13 | if(NOT DOXYGEN_PROJECT_VERSION) 14 | set(DOXYGEN_PROJECT_VERSION ${CMAKE_PROJECT_VERSION} CACHE STRING "Project version reported in Doxygen documentation.") 15 | endif() 16 | 17 | if(NOT DOXYGEN_PROJECT_DESCRIPTION) 18 | set(DOXYGEN_PROJECT_DESCRIPTION ${CMAKE_PROJECT_DESCRIPTION} CACHE STRING "Short project description used for Doxygen documentation.") 19 | endif() 20 | 21 | if(NOT DOXYGEN_OUTPUT_DIR) 22 | set(DOXYGEN_OUTPUT_DIR ${CMAKE_BINARY_DIR}/docs CACHE STRING "Output directory for Doxygen documentation.") 23 | endif() 24 | 25 | if(NOT DOXYGEN_INPUT_DIRS) 26 | set(DOXYGEN_INPUT_DIRS src docs README.md CACHE STRING "List of input files and folders used to generate Doxygen documentation.") 27 | endif() 28 | 29 | if(DOXYGEN_ADDITIONAL_INPUT_DIRS) 30 | list(APPEND DOXYGEN_INPUT_DIRS "${DOXYGEN_ADDITIONAL_INPUT_DIRS}") 31 | endif() 32 | 33 | # Convert to expected Doxygen output 34 | string(REPLACE ";" " " DOXYGEN_INPUT_DIRS "${DOXYGEN_INPUT_DIRS}") 35 | 36 | if(NOT DOXYGEN_EXCLUDE_PATTERNS) 37 | set(DOXYGEN_EXCLUDE_PATTERNS catch.hpp CACHE STRING "Project name used for Doxygen documentation.") 38 | endif() 39 | 40 | if(DOXYGEN_ADDITIONAL_EXCLUDE_PATTERNS) 41 | list(APPEND DOXYGEN_EXCLUDE_PATTERNS "${DOXYGEN_ADDITIONAL_EXCLUDE_PATTERNS}") 42 | endif() 43 | 44 | if(NOT DOXYGEN_MD_AS_MAINPAGE) 45 | set(DOXYGEN_MD_AS_MAINPAGE README.md CACHE STRING "Project name used for Doxygen documentation.") 46 | endif() 47 | 48 | ## Process Doxyfile.in 49 | configure_file(${CMAKE_CURRENT_LIST_DIR}/Doxyfile.in ${DOXYGEN_OUTPUT_DIR}/Doxyfile @ONLY) 50 | 51 | ## Build Target 52 | add_custom_target(docs 53 | COMMAND ${DOXYGEN} ${DOXYGEN_OUTPUT_DIR}/Doxyfile 54 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 55 | ) 56 | # This property will remove a directory, while add_custom_target BYPRODUCTS will not. 57 | set_target_properties(docs PROPERTIES ADDITIONAL_CLEAN_FILES ${DOXYGEN_OUTPUT_DIR}) 58 | else() 59 | message("[WARNING] Doxygen not installed. Documentation targets are disabled.") 60 | endif() 61 | -------------------------------------------------------------------------------- /format/clang-format.cmake: -------------------------------------------------------------------------------- 1 | ####################### 2 | # Clang-format Module # 3 | ####################### 4 | # This module adds two targets to your build: format and format-patch 5 | # 6 | # You can control the behavior of clang-format by setting the following variables 7 | # before you include this module: 8 | # CLANG_FORMAT_EXCLUDE_DIRS, which is a CMake list of directories to exclude from formatting 9 | # By default, no directories are excluded 10 | # CLANG_FORMAT_DIRS, which is a CMake list of directories to include in formatting 11 | # By default, the src and test directories are included 12 | # CLANG_FORMAT_FILETYPES, which is a CMake list of file types to include in formatting, 13 | # specified as globs (e.g.: "*.c") 14 | # By default, we format *.c, *.h, *.cpp, *.hpp 15 | # 16 | # You can completely override the default vaules by setting the variables above. 17 | # If you wish to use the defaults for CLANG_FORMAT_DIRS and CLANG_FORMAT_FILETYPES 18 | # but add additional directories or files, you can set these variables: 19 | # CLANG_FORMAT_ADDITIONAL_DIRS 20 | # CLANG_FORMAT_ADDITIONAL_FILETYPES 21 | 22 | find_program(CLANG_FORMAT clang-format) 23 | if(CLANG_FORMAT) 24 | ### Supply argument defaults 25 | if(NOT CLANG_FORMAT_EXCLUDE_DIRS) 26 | set(CLANG_FORMAT_EXCLUDE_DIRS "" CACHE STRING "CMake list of directories to exclude form clang-format.") 27 | endif() 28 | 29 | if(NOT CLANG_FORMAT_DIRS) 30 | set(CLANG_FORMAT_DIRS src test CACHE STRING "CMake list of directories to format using clang-format.") 31 | endif() 32 | 33 | if(NOT CLANG_FORMAT_FILETYPES) 34 | set(CLANG_FORMAT_FILETYPES "*.c" "*.h" "*.cpp" "*.hpp" CACHE STRING "CMake list of file types to format using clang-format.") 35 | endif() 36 | 37 | if(CLANG_FORMAT_ADDITIONAL_DIRS) 38 | list(APPEND CLANG_FORMAT_DIRS ${CLANG_FORMAT_ADDITIONAL_DIRS}) 39 | endif() 40 | 41 | if(CLANG_FORMAT_ADDITIONAL_FILETYPES) 42 | list(APPEND CLANG_FORMAT_FILETYPES ${CLANG_FORMAT_ADDITIONAL_FILETYPES}) 43 | endif() 44 | 45 | ### Convert variables into script format 46 | if(CLANG_FORMAT_EXCLUDE_ARG) 47 | string(REPLACE ";" "," CLANG_FORMAT_EXCLUDE_ARG "${CLANG_FORMAT_EXCLUDE_DIRS}") 48 | set(CLANG_FORMAT_EXCLUDE_ARG "-e ${CLANG_FORMAT_EXCLUDE_ARG}") 49 | endif() 50 | string(REPLACE ";" "," CLANG_FORMAT_DIRS_ARG "${CLANG_FORMAT_DIRS}") 51 | string(REPLACE ";" "," CLANG_FORMAT_FILETYPES_ARG "${CLANG_FORMAT_FILETYPES}") 52 | 53 | set(clang_format_args 54 | ${CLANG_FORMAT_EXCLUDE_ARG} 55 | ${CLANG_FORMAT_DIRS_ARG} 56 | ${CLANG_FORMAT_FILETYPES_ARG} 57 | ) 58 | 59 | add_custom_target(format 60 | COMMAND ${CMAKE_CURRENT_LIST_DIR}/format.sh 61 | # common args 62 | ${clang_format_args} 63 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 64 | ) 65 | 66 | add_custom_target(format-patch 67 | COMMAND ${CMAKE_CURRENT_LIST_DIR}/format.sh 68 | # Enable patch file 69 | -p 70 | # Common args 71 | ${clang_format_args} 72 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 73 | ) 74 | else() 75 | message("[WARNING] Clang-format is not installed. Formatting targets are disabled.") 76 | endif() 77 | -------------------------------------------------------------------------------- /format/format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script invocation: 3 | # format.sh [options] DIRS TYPES 4 | # 5 | # Argument 1 is a list of directories to search for files in, delimited by commas 6 | # e.g., src,include 7 | # This is required 8 | # Argument 2 is a list of file types to search for, delimited by commas 9 | # e.g., *.c,*.h,*.cpp,*.hpp 10 | # This is required 11 | # 12 | # Optional arguments: 13 | # -e specifies is a comma-separated list of directories to exclude, delimited by commas 14 | # e.g.: -e src/gdtoa,src/external 15 | # -p generates a patch file using a `git` 16 | # 17 | ## NOTE: Due to getopts limitations, optional arguments must precede positional arguments 18 | # 19 | ## Processing strategy for separating CSVs is derived from: 20 | ## https://stackoverflow.com/questions/918886/how-do-i-split-a-string-on-a-delimiter-in-bash#tab-top 21 | 22 | EXCLUDES_ARGS= 23 | PATCH='0' 24 | 25 | while getopts "e:p" opt; do 26 | case $opt in 27 | e) EXCLUDES_ARGS="$OPTARG" 28 | ;; 29 | p) PATCH='1' 30 | ;; 31 | \?) echo "Invalid option -$OPTARG" >&2 32 | ;; 33 | esac 34 | done 35 | 36 | # Shift off the getopts args, leaving us with positional args 37 | shift $((OPTIND -1)) 38 | 39 | # Parse $1 into a list of directories 40 | IFS=',' read -ra DIRS <<< "$1" 41 | 42 | # Parse $2 into file-type arguments 43 | FILE_TYPES= 44 | IFS=',' read -ra ENTRIES <<< "$2" 45 | for entry in "${ENTRIES[@]}"; do 46 | FILE_TYPES="$FILE_TYPES -o -iname $entry" 47 | done 48 | 49 | # Parse $3 into the exclude arguments 50 | EXCLUDES= 51 | IFS=',' read -ra ENTRIES <<< "$EXCLUDES_ARGS" 52 | for entry in "${ENTRIES[@]}"; do 53 | EXCLUDES="$EXCLUDES -o -path $entry" 54 | done 55 | 56 | if [[ ! -z $EXCLUDES ]]; then 57 | # Remove the initial `-o` argument for a single/first directory 58 | EXCLUDES=${EXCLUDES:3:${#EXCLUDES}} 59 | 60 | # Create the final argument string 61 | EXCLUDES="-type d \( $EXCLUDES \) -prune" 62 | else 63 | # Remove the initial `-o` argument for the first file type if there are no excludes, 64 | # otherwise the rules will not be properly parsed 65 | FILE_TYPES=${FILE_TYPES:3:${#FILE_TYPES}} 66 | fi 67 | 68 | eval find $DIRS $EXCLUDES -type f $FILE_TYPES \ 69 | | xargs clang-format -style=file -i -fallback-style=none 70 | 71 | if [ "$PATCH" == '1' ]; then 72 | git diff > clang_format.patch 73 | 74 | # Delete if 0 size 75 | if [ ! -s clang_format.patch ] 76 | then 77 | rm clang_format.patch 78 | fi 79 | fi 80 | -------------------------------------------------------------------------------- /linker-scripts/stm/STM32F103VBIx_FLASH.ld: -------------------------------------------------------------------------------- 1 | /* Entry Point */ 2 | ENTRY(entry) 3 | 4 | /* Highest address of the user mode stack */ 5 | _estack = 0x20005000; /* end of RAM */ 6 | /* Generate a link error if heap and stack don't fit into RAM */ 7 | _Min_Heap_Size = 0x200; /* required amount of heap */ 8 | _Min_Stack_Size = 0x400; /* required amount of stack */ 9 | 10 | /* Specify the memory areas */ 11 | MEMORY 12 | { 13 | FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K 14 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K 15 | } 16 | 17 | /* Define output sections */ 18 | 19 | 20 | SECTIONS 21 | { 22 | /* The startup code goes first into FLASH */ 23 | .isr_vector : 24 | { 25 | . = ALIGN(4); 26 | KEEP(*(.isr_vector)) /* Startup code */ 27 | . = ALIGN(4); 28 | } >FLASH 29 | 30 | /* The program code and other data goes into FLASH */ 31 | .text : 32 | { 33 | . = ALIGN(4); 34 | *(.text) /* .text sections (code) */ 35 | *(.text*) /* .text* sections (code) */ 36 | *(.glue_7) /* glue arm to thumb code */ 37 | *(.glue_7t) /* glue thumb to arm code */ 38 | *(.eh_frame) 39 | 40 | KEEP (*(.init)) 41 | KEEP (*(.fini)) 42 | 43 | . = ALIGN(4); 44 | _etext = .; /* define a global symbols at end of code */ 45 | } >FLASH 46 | 47 | /* Constant data goes into FLASH */ 48 | .rodata : 49 | { 50 | . = ALIGN(4); 51 | *(.rodata) /* .rodata sections (constants, strings, etc.) */ 52 | *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ 53 | . = ALIGN(4); 54 | } >FLASH 55 | 56 | .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH 57 | .ARM : { 58 | __exidx_start = .; 59 | *(.ARM.exidx*) 60 | __exidx_end = .; 61 | } >FLASH 62 | 63 | .preinit_array : 64 | { 65 | PROVIDE_HIDDEN (__preinit_array_start = .); 66 | KEEP (*(.preinit_array*)) 67 | PROVIDE_HIDDEN (__preinit_array_end = .); 68 | } >FLASH 69 | .init_array : 70 | { 71 | PROVIDE_HIDDEN (__init_array_start = .); 72 | KEEP (*(SORT(.init_array.*))) 73 | KEEP (*(.init_array*)) 74 | PROVIDE_HIDDEN (__init_array_end = .); 75 | } >FLASH 76 | .fini_array : 77 | { 78 | PROVIDE_HIDDEN (__fini_array_start = .); 79 | KEEP (*(SORT(.fini_array.*))) 80 | KEEP (*(.fini_array*)) 81 | PROVIDE_HIDDEN (__fini_array_end = .); 82 | } >FLASH 83 | 84 | /* used by the startup to initialize data */ 85 | _sidata = LOADADDR(.data); 86 | 87 | /* Initialized data sections goes into RAM, load LMA copy after code */ 88 | .data : 89 | { 90 | . = ALIGN(4); 91 | _sdata = .; /* create a global symbol at data start */ 92 | *(.data) /* .data sections */ 93 | *(.data*) /* .data* sections */ 94 | 95 | . = ALIGN(4); 96 | _edata = .; /* define a global symbol at data end */ 97 | } >RAM AT> FLASH 98 | 99 | 100 | /* Uninitialized data section */ 101 | . = ALIGN(4); 102 | .bss : 103 | { 104 | /* This is used by the startup in order to initialize the .bss secion */ 105 | _sbss = .; /* define a global symbol at bss start */ 106 | __bss_start__ = _sbss; 107 | *(.bss) 108 | *(.bss*) 109 | *(COMMON) 110 | 111 | . = ALIGN(4); 112 | _ebss = .; /* define a global symbol at bss end */ 113 | __bss_end__ = _ebss; 114 | } >RAM 115 | 116 | /* User_heap_stack section, used to check that there is enough RAM left */ 117 | ._user_heap_stack : 118 | { 119 | . = ALIGN(8); 120 | PROVIDE ( end = . ); 121 | PROVIDE ( _end = . ); 122 | . = . + _Min_Heap_Size; 123 | . = . + _Min_Stack_Size; 124 | . = ALIGN(8); 125 | } >RAM 126 | 127 | /* Remove information from the standard libraries */ 128 | /DISCARD/ : 129 | { 130 | libc.a ( * ) 131 | libm.a ( * ) 132 | libgcc.a ( * ) 133 | } 134 | 135 | .ARM.attributes 0 : { *(.ARM.attributes) } 136 | } 137 | -------------------------------------------------------------------------------- /linker/AddExecutableWithLinkerScriptDep.cmake: -------------------------------------------------------------------------------- 1 | ############################################################# 2 | # Module: Automatically Register Linker Script Dependencies # 3 | ############################################################# 4 | # Redefine add_executable so that we specify a link-time dependency on the linker script 5 | # The `LINKER_SCRIPT_DEPENDENCIES` variable should be defined in a toolchain 6 | # file. It can contain a CMake list of all linker script dependencies. 7 | 8 | function(add_executable target) 9 | # Forward all arguments to the CMake add_executable 10 | _add_executable(${target} ${ARGN}) 11 | if(LINKER_SCRIPT_DEPENDENCIES) 12 | set_target_properties(${target} PROPERTIES 13 | LINK_DEPENDS "${LINKER_SCRIPT_DEPENDENCIES}") 14 | endif() 15 | endfunction() 16 | -------------------------------------------------------------------------------- /linker/DefaultLinkerSettings.cmake: -------------------------------------------------------------------------------- 1 | ########################### 2 | # Default Linker Settings # 3 | ########################### 4 | # 5 | # This module applies linker flags that are commonly used on Embedded Artistry projects. 6 | 7 | include(${CMAKE_CURRENT_LIST_DIR}/../compiler/CheckAndApplyFlags.cmake) 8 | 9 | set(desired_common_linker_optimization_flags 10 | # This seems to cause problems with GCC... 11 | #-Wl,-dead_strip # Strip dead symbols for OS X 12 | -Wl,--gc-sections # Strip dead sections for GCC 13 | ) 14 | 15 | #################################################### 16 | # Settings enforced when we're the primary project # 17 | #################################################### 18 | 19 | if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) 20 | apply_supported_linker_flags_globally(C desired_common_linker_optimization_flags) 21 | apply_supported_linker_flags_globally(CXX desired_common_linker_optimization_flags) 22 | endif() 23 | 24 | #################################### 25 | # Settings enforced no matter what # 26 | #################################### 27 | 28 | -------------------------------------------------------------------------------- /linker/map.cmake: -------------------------------------------------------------------------------- 1 | ###################################### 2 | # Module: Generate a Linker Map File # 3 | ###################################### 4 | # This module provides a target_linker_map() function that can be used to generate 5 | # a linker map file on an executable target in a compiler-agnostic way/ 6 | # This module currently supports GCC and Clang compiler families. 7 | 8 | include(CheckCCompilerFlag) 9 | 10 | function(target_linker_map target) 11 | check_c_linker_flag("-Wl,-Map,test.map" _Map1) 12 | if("${_Map1}") 13 | set(GEN_MAP_FILE "-Wl,-Map,") 14 | else() 15 | check_c_linker_flag("-Wl,-map,test.map" _Map2) 16 | if("${_Map2}") 17 | set(GEN_MAP_FILE "-Wl,-map,") 18 | else() 19 | message("[WARNING] ${CMAKE_C_COMPILER_ID} does not have a defined linker map argument.") 20 | return() 21 | endif() 22 | endif() 23 | 24 | get_target_property(map_dir ${target} BINARY_DIR) 25 | target_link_options(${target} PRIVATE ${GEN_MAP_FILE}${map_dir}/${target}.map) 26 | set_target_properties(${target} PROPERTIES 27 | ADDITIONAL_CLEAN_FILES ${target}.map) 28 | endfunction() 29 | -------------------------------------------------------------------------------- /test/catch2.cmake: -------------------------------------------------------------------------------- 1 | ################# 2 | # Catch2 Module # 3 | ################# 4 | # This module simplifies the process of adding Catch2 testing support to your build. 5 | # 6 | # You can link against the `catch2_dep` library to access the dependency 7 | # 8 | # By default, XML output will be placed in ${CMAKE_BINARY_DIR}/test. You can change this 9 | # by setting CATCH2_TEST_OUTPUT_DIR before you include this module. 10 | # 11 | # By default, you use the Catch2 built-in main. If you wish to define your own main file, 12 | # set CATCH2_MAIN_FILE with the path to your file. You'll link against Catch2 dep instead. 13 | # 14 | # We disable many options in the Catch2 dependency, such as building tests and installing docs/helpers. 15 | # You can pass your own settings to the dependency by setting the CATCH2_DEP_SETTINGS variable. 16 | # 17 | # Our default compile-time settings are provided in CATCH2_COMPILE_SETTINGS. You can override 18 | # our settings completely by defining this variable. You can also provide additional settings 19 | # using CATCH2_ADDITIONAL_COMPILE_SETTINGS. 20 | # 21 | # This module also provides a `register_catch2_test` function to simplify the registration of Catch2 22 | # test programs. Call this function with the desired test name and the build target for the test 23 | # program. This call can be used with multiple test programs. 24 | # 25 | # Example: 26 | # register_catch2_test(Printf.Test printf_tests) 27 | 28 | include(${CMAKE_CURRENT_LIST_DIR}/../CPM.cmake) 29 | 30 | ## Set Default Options 31 | if(NOT CATCH2_MAIN_FILE) 32 | set(CATCH2_MAIN_FILE "" CACHE STRING "The file that contains the definiton of a custom main for Catch2.") 33 | endif() 34 | 35 | if(NOT CATCH2_TEST_OUTPUT_DIR) 36 | set(CATCH2_TEST_OUTPUT_DIR ${CMAKE_BINARY_DIR}/test/ CACHE STRING "Location where Catch2 test results should live.") 37 | endif() 38 | 39 | if(NOT CATCH2_DEP_SETTINGS) 40 | set(CATCH2_DEP_SETTINGS 41 | "CATCH_BUILD_TESTING OFF" 42 | "CATCH_ENABLE_WERROR OFF" 43 | "CATCH_INSTALL_DOCS OFF" 44 | "CATCH_INSTALL_HELPERS OFF" 45 | CACHE STRING "Settings that control the Catch2 dependency build target.") 46 | endif() 47 | 48 | if(NOT CATCH2_COMPILE_SETTINGS) 49 | set(CATCH2_COMPILE_SETTINGS 50 | -DCATCH_CONFIG_FAST_COMPILE 51 | -DCATCH_CONFIG_NO_WCHAR 52 | -DCATCH_CONFIG_NO_POSIX_SIGNALS 53 | -DCATCH_CONFIG_DISABLE_MATCHERS 54 | -DMSE_DISABLE_IOSTREAM 55 | -DMSE_SCOPEPOINTER_DEBUG_RUNTIME_CHECKS_DISABLED 56 | -fexceptions 57 | -funwind-tables 58 | CACHE STRING "Settings that control Catch2 compilation.") 59 | endif() 60 | 61 | if(CATCH2_ADDITIONAL_COMPILE_SETTINGS) 62 | list(APPEND CATCH2_COMPILE_SETTINGS ${CATCH2_ADDITIONAL_COMPILE_SETTINGS}) 63 | endif() 64 | 65 | ###################### 66 | # Satisfy Dependency # 67 | ###################### 68 | 69 | # The library target is Catch2 70 | CPMAddPackage( 71 | NAME Catch2 72 | GITHUB_REPOSITORY catchorg/Catch2 73 | VERSION 3.8.0 74 | OPTIONS 75 | ${CATCH2_DEP_SETTINGS} 76 | ) 77 | 78 | # Define a build target that users can link against to build a Catch2 test program 79 | add_library(catch2_dep INTERFACE) 80 | if(CATCH2_MAIN_FILE) 81 | target_link_libraries(catch2_dep INTERFACE Catch2) 82 | target_sources(catch2_dep INTERFACE ${CATCH2_MAIN_FILE}) 83 | else() 84 | target_link_libraries(catch2_dep INTERFACE Catch2WithMain) 85 | endif() 86 | target_compile_options(catch2_dep INTERFACE ${CATCH2_COMPILE_SETTINGS}) 87 | 88 | ################################ 89 | # Register Catch2 test targets # 90 | ################################ 91 | 92 | function(register_catch2_test test_name target) 93 | set(JUNIT_XML_NAME ${CATCH2_TEST_OUTPUT_DIR}/${target}.xml) 94 | set(CATCH_XML_NAME ${CATCH2_TEST_OUTPUT_DIR}/${target}_catchxml.xml) 95 | 96 | add_test(NAME ${test_name} 97 | COMMAND ${target} 98 | ) 99 | 100 | # Add a command to run the tests with standard Catch output 101 | add_custom_target(test-${target} 102 | COMMAND ${target} 103 | ) 104 | 105 | # Generate JUnit XML output 106 | add_custom_target(${target}-xml 107 | COMMAND ${target} -s -r junit -o ${JUNIT_XML_NAME} 108 | ) 109 | set_target_properties(${target}-xml PROPERTIES ADDITIONAL_CLEAN_FILES ${JUNIT_XML_NAME}) 110 | 111 | # Generate Catch2 XML output 112 | add_custom_target(${target}-catch-xml 113 | COMMAND ${target} -s -r xml -o ${CATCH_XML_NAME} 114 | ) 115 | set_target_properties(${target}-catch-xml PROPERTIES ADDITIONAL_CLEAN_FILES ${CATCH_XML_NAME}) 116 | endfunction() 117 | -------------------------------------------------------------------------------- /test/cmocka.cmake: -------------------------------------------------------------------------------- 1 | ################# 2 | # CMocka Module # 3 | ################# 4 | # This module simplifies the process of adding CMocka testing support to your build. 5 | # 6 | # You can link against the `cmocka_dep` library to access the dependency in an agnostic manner. 7 | # 8 | # This module also provides a `test-clear-results` target which can be used to clear existing 9 | # XML files, since CMocka will not save XML data if the files already exist. 10 | # 11 | # By default, XML output will be placed in ${CMAKE_BINARY_DIR}/test. You can change this 12 | # by setting CMOCKA_TEST_OUTPUT_DIR before you include this module. 13 | # 14 | # This module also provides a `register_cmocka_test` function to simplify the registration of CMocka 15 | # test programs. Call this function with the desired test name and the build target for the test 16 | # program. This call can be used with multiple test programs. 17 | # 18 | # Example: 19 | # register_cmocka_test(Libc.Test libc_tests) 20 | 21 | include(${CMAKE_CURRENT_LIST_DIR}/../CPM.cmake) 22 | 23 | ## Set Default Options 24 | if(NOT CMOCKA_TEST_OUTPUT_DIR) 25 | set(CMOCKA_TEST_OUTPUT_DIR ${CMAKE_BINARY_DIR}/test/ CACHE STRING "Location where CMocka test results should live.") 26 | endif() 27 | 28 | ###################### 29 | # Satisfy Dependency # 30 | ###################### 31 | 32 | find_package(cmocka QUIET) 33 | 34 | if(NOT cmocka_FOUND) 35 | CPMAddPackage( 36 | NAME cmocka 37 | GIT_REPOSITORY https://git.cryptomilk.org/projects/cmocka.git/ 38 | VERSION 1.1.5 39 | GIT_TAG cmocka-1.1.5 40 | DOWNLOAD_ONLY YES 41 | ) 42 | 43 | # Convenience variable to shorten our code below 44 | set(CMOCKA_STATIC_FILENAME 45 | ${CMAKE_STATIC_LIBRARY_PREFIX}cmocka-static${CMAKE_STATIC_LIBRARY_SUFFIX} 46 | ) 47 | 48 | 49 | # We would normally use CPMAddPackage, but CMocka's build presents problems. 50 | # So we include it as an external project, which allows us to build the lib 51 | # separately and then link it in. 52 | include(ExternalProject) 53 | ExternalProject_Add(project_cmocka 54 | SOURCE_DIR ${cmocka_SOURCE_DIR} 55 | PREFIX ${CMAKE_CURRENT_BINARY_DIR}/cmocka 56 | BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/cmocka 57 | CMAKE_ARGS 58 | -DBUILD_STATIC_LIB=ON 59 | -DWITH_STATIC_LIB=ON # Without this, Cmocka will not install the static lib 60 | -DWITH_EXAMPLES=OFF 61 | -DCMAKE_BUILD_TYPE=Debug 62 | -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/cmocka 63 | # This is needed with Ninja generators to prevent it from failing due to 64 | # the library being missing before the build has been run 65 | BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/cmocka/lib/${CMOCKA_STATIC_FILENAME} 66 | ) 67 | 68 | # We need to get the CMocka installation directory to know where our 69 | # built libraries can be found 70 | ExternalProject_Get_Property(project_cmocka BINARY_DIR) 71 | 72 | # Now we define an IMPORTED library target and tell CMake where the library 73 | # files can be found. To ensure the build flows in the proper order, 74 | # we'll add a dependency on the ExternalProject target above, which will 75 | # ensure the library is built before we try to link it 76 | add_library(cmocka-static STATIC IMPORTED) 77 | set_target_properties(cmocka-static PROPERTIES 78 | IMPORTED_LOCATION ${BINARY_DIR}/lib/${CMOCKA_STATIC_FILENAME} 79 | ) 80 | add_dependencies(cmocka-static project_cmocka) 81 | 82 | # Maintain build compatibility between find_package and CMakeLists.txt variants. 83 | set(CMOCKA_LIBRARIES cmocka-static) 84 | set(CMOCKA_INCLUDE_DIR ${BINARY_DIR}/include) 85 | endif() 86 | 87 | add_library(cmocka_dep INTERFACE) 88 | target_include_directories(cmocka_dep INTERFACE SYSTEM ${CMOCKA_INCLUDE_DIR}) 89 | target_link_libraries(cmocka_dep INTERFACE ${CMOCKA_LIBRARIES}) 90 | 91 | ################## 92 | # Helper Targets # 93 | ################## 94 | 95 | add_custom_target(test-clear-results 96 | COMMAND ${CMAKE_COMMAND} -E rm -rf ${CMOCKA_TEST_OUTPUT_DIR}/*.xml 97 | COMMENT "Removing XML files in the test/ directory" 98 | ) 99 | 100 | ############################ 101 | # Register Test with CTest # 102 | ############################ 103 | 104 | add_test(NAME CMocka.ClearResults 105 | COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target test-clear-results 106 | ) 107 | 108 | ################################ 109 | # Register CMocka test targets # 110 | ################################ 111 | function(register_cmocka_test test_name target) 112 | add_custom_target(test-${target} 113 | COMMAND export CMOCKA_MESSAGE_OUTPUT=stdout 114 | COMMAND ${target} 115 | ) 116 | 117 | add_test(NAME ${test_name} 118 | COMMAND ${target} 119 | ) 120 | 121 | set_tests_properties(${test_name} 122 | PROPERTIES 123 | ENVIRONMENT CMOCKA_XML_FILE=${CMOCKA_TEST_OUTPUT_DIR}/%g.xml 124 | DEPENDS CMocka.ClearResults 125 | ) 126 | endfunction() 127 | -------------------------------------------------------------------------------- /test/unity.cmake: -------------------------------------------------------------------------------- 1 | ################# 2 | # Unity Module # 3 | ################# 4 | # This module simplifies the process of adding Unity testing support to your build. 5 | # 6 | # You can link against the `unity_dep` target to access the dependency and build a test program. 7 | # 8 | # We set our own default build settings for the unity framework, such as enabling extensions. 9 | # You can pass your own settings to the dependency by setting the UNITY_DEP_SETTINGS variable. 10 | # 11 | # Our default compile-time settings are provided in UNITY_COMPILE_SETTINGS. You can override 12 | # our settings completely by defining this variable. You can also provide additional settings 13 | # using UNITY_ADDITIONAL_COMPILE_SETTINGS. 14 | # 15 | # This module also provides a `register_unity_test` function to simplify the registration of Unity 16 | # test programs. Call this function with the desired test name and the build target for the test 17 | # program. This call can be used with multiple test programs. 18 | # 19 | # Example: 20 | # register_unity_test(Printf.Test printf_tests) 21 | 22 | ######## 23 | # TODO # 24 | ######## 25 | # By default, XML output will be placed in ${CMAKE_BINARY_DIR}/test. You can change this 26 | # by setting UNITY_TEST_OUTPUT_DIR before you include this module. 27 | 28 | # I understand your root question is that you would prefer to not use Ceedling 29 | # at all and would just like to get xml output from Unity. While unity itself 30 | # doesn't output xml output, it CAN be post-processed using a script. 31 | # There are some sample scripts in the unity/auto directory of the repo. 32 | # For example, stylize_as_junit.rb is a ruby script which will output 33 | # junit-compatible xml from unity's output files. 34 | 35 | # So. i did some digging and in my particular setup, i was just building my own 36 | # main, then running the executable that I created. Then I tee'd that output to 37 | # a .testResults file. Then after I had that, I was able to use the 38 | # style_as_junit.rb ruby script to generate the appropriate junit xml file. 39 | 40 | include(${CMAKE_CURRENT_LIST_DIR}/../CPM.cmake) 41 | 42 | if(NOT UNITY_TEST_OUTPUT_DIR) 43 | set(UNITY_TEST_OUTPUT_DIR ${CMAKE_BINARY_DIR}/test/ CACHE STRING "Location where Catch2 test results should live.") 44 | endif() 45 | 46 | if(NOT UNITY_DEP_SETTINGS) 47 | set(UNITY_DEP_SETTINGS 48 | "UNITY_EXTENSION_FIXTURE ON" 49 | "UNITY_EXTENSION_MEMORY ON" 50 | CACHE STRING "Settings that control the Unity dependency build target.") 51 | endif() 52 | 53 | if(NOT UNITY_COMPILE_SETTINGS) 54 | set(UNITY_COMPILE_SETTINGS 55 | # UNITY_EXCLUDE_FLOAT 56 | # UNITY_EXCLUDE_DOUBLE 57 | # UNITY_EXCLUDE_FLOAT_PRINT 58 | # UNITY_OUTPUT_COLOR 59 | CACHE STRING "Settings that control Unity compilation.") 60 | endif() 61 | 62 | if(UNITY_ADDITIONAL_COMPILE_SETTINGS) 63 | list(APPEND UNITY_COMPILE_SETTINGS ${UNITY_ADDITIONAL_COMPILE_SETTINGS}) 64 | endif() 65 | 66 | ###################### 67 | # Satisfy Dependency # 68 | ###################### 69 | 70 | # The library target is Unity 71 | CPMAddPackage( 72 | NAME Unity 73 | GITHUB_REPOSITORY ThrowTheSwitch/Unity 74 | VERSION 2.5.2 75 | OPTIONS 76 | ${UNITY_DEP_SETTINGS} 77 | ) 78 | 79 | # Define a build target that users can link against to build a Catch2 test program 80 | add_library(unity_dep INTERFACE) 81 | target_link_libraries(unity_dep INTERFACE unity) 82 | target_compile_options(unity_dep INTERFACE ${UNITY_COMPILE_SETTINGS}) 83 | 84 | ################################ 85 | # Register Unity test targets # 86 | ################################ 87 | 88 | function(register_unity_test test_name target) 89 | add_custom_target(test-${target} 90 | COMMAND ${target} 91 | ) 92 | 93 | add_test(NAME ${test_name} 94 | COMMAND ${target} 95 | ) 96 | endfunction() 97 | -------------------------------------------------------------------------------- /toolchains/OverrideBuildTypeSettings.cmake: -------------------------------------------------------------------------------- 1 | ##################################### 2 | # Default Build Configuration Flags # 3 | ##################################### 4 | # 5 | # Our ecosystem uses a different meaning for the default build types. 6 | # Common flags are set in this file. You can use these default settings 7 | # In your toolchain file by including this file. 8 | # 9 | # See the CMake Manual for CMAKE__FLAGS_INIT: 10 | # https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_FLAGS_CONFIG_INIT.html 11 | 12 | ### Debug Flags 13 | SET(CMAKE_C_FLAGS_DEBUG_INIT 14 | "-Og -ggdb3" 15 | CACHE 16 | INTERNAL "Default C compiler debug build flags.") 17 | SET(CMAKE_CXX_FLAGS_DEBUG_INIT 18 | "-Og -ggdb3" 19 | CACHE 20 | INTERNAL "Default C++ compiler debug build flags.") 21 | SET(CMAKE_ASM_FLAGS_DEBUG_INIT 22 | "-Og -ggdb3" 23 | CACHE 24 | INTERNAL "Default assembly compiler debug build flags") 25 | 26 | ### Release with Debug Info Flags 27 | SET(CMAKE_C_FLAGS_MINSIZEREL_INIT 28 | "-O2 -ggdb" 29 | CACHE 30 | INTERNAL "Default C compiler release + debug build flags.") 31 | SET(CMAKE_CXX_FLAGS_MINSIZEREL_INIT 32 | "-O2 -ggdb" 33 | CACHE 34 | INTERNAL "Default C++ compiler release + debug build flags.") 35 | SET(CMAKE_ASM_FLAGS_MINSIZEREL_INIT 36 | "-O2 -ggdb" 37 | CACHE 38 | INTERNAL "Default assembly compiler release + debug build flags") 39 | 40 | ### Release Flags 41 | SET(CMAKE_C_FLAGS_RELEASE_INIT 42 | "-O2" 43 | CACHE 44 | INTERNAL "Default C compiler release build flags.") 45 | SET(CMAKE_CXX_FLAGS_RELEASE_INIT 46 | "-O2" 47 | CACHE 48 | INTERNAL "Default C++ compiler release build flags.") 49 | SET(CMAKE_ASM_FLAGS_RELEASE_INIT 50 | "-O2" 51 | CACHE 52 | INTERNAL "Default asm compiler release build flags.") 53 | -------------------------------------------------------------------------------- /toolchains/cross/STM32F103VBIx.cmake: -------------------------------------------------------------------------------- 1 | ############################# 2 | # STM32F103VBIx (Cortex-M3) # 3 | ############################# 4 | 5 | # CMake includes the toolchain file multiple times when configuring the build, 6 | # which causes errors for some flags (e.g., --specs=nano.specs). 7 | # We prevent this with an include guard. 8 | if(STM32F103VBIx_TOOLCHAIN_INCLUDED) 9 | return() 10 | endif() 11 | 12 | set(STM32F103VBIx_TOOLCHAIN_INCLUDED true) 13 | 14 | set(CPU_NAME STM32F103VBIx) 15 | 16 | set(LINKER_SCRIPT_DIR ${CMAKE_SOURCE_DIR}/cmake/linker-scripts/stm) 17 | set(LINKER_SCRIPT_PRIMARY_FILE STM32F103VBIx_FLASH.ld) 18 | 19 | # These dependencies are applied to add_executable targets by the 20 | # AddExecutableWithLinkerScriptDep module 21 | list(APPEND LINKER_SCRIPT_DEPENDENCIES "${LINKER_SCRIPT_DIR}/${LINKER_SCRIPT_PRIMARY_FILE}") 22 | 23 | set(LD_FLAGS "-T${LINKER_SCRIPT_PRIMARY_FILE} -L${LINKER_SCRIPT_DIR}") 24 | 25 | include(${CMAKE_CURRENT_LIST_DIR}/cortex-m3.cmake) 26 | -------------------------------------------------------------------------------- /toolchains/cross/arm-none-eabi-gcc.cmake: -------------------------------------------------------------------------------- 1 | #################################### 2 | # arm-none-eabi-gcc Base Toolchain # 3 | #################################### 4 | # 5 | # To include this file as a base toolchain file, 6 | # include it at the bottom of the derived toolchain file. 7 | # 8 | # You can define CPU_FLAGS that will be passed to CMAKE_*_FLAGS to select the CPU 9 | # (and any other necessary CPU-specific flags) 10 | # You can define VFP_FLAGS to select the desired floating-point configuration 11 | # You can define LD_FLAGS to control linker flags for your target 12 | 13 | ################# 14 | # System Config # 15 | ################# 16 | 17 | set(CMAKE_SYSTEM_NAME Generic) 18 | set(CMAKE_SYSTEM_PROCESSOR arm) 19 | # Represents the name of the specific processor type, e.g. Cortex-M4 20 | if(NOT CPU_NAME) 21 | set(CPU_NAME generic) 22 | endif() 23 | 24 | #################### 25 | # Toolchain Config # 26 | #################### 27 | 28 | set(CMAKE_C_COMPILER arm-none-eabi-gcc) 29 | set(CMAKE_CXX_COMPILER arm-none-eabi-g++) 30 | set(AS arm-none-eabi-as) 31 | set(CMAKE_AR arm-none-eabi-gcc-ar) 32 | set(OBJCOPY arm-none-eabi-objcopy) 33 | set(OBJDUMP arm-none-eabi-objdump) 34 | set(SIZE arm-none-eabi-size) 35 | 36 | # If set to ONLY, then only the roots in CMAKE_FIND_ROOT_PATH (i.e., the host machine) 37 | # will be searched. If set to NEVER, then the roots in CMAKE_FIND_ROOT_PATH will 38 | # be ignored and only the build machine root will be used. 39 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 40 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 41 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 42 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 43 | 44 | ################ 45 | # Common Flags # 46 | ################ 47 | # Note that CPU_FLAGS, LD_FLAGS, and VFP_FLAGS are set by other Toolchain files 48 | # that include this file. 49 | # 50 | # See the CMake Manual for CMAKE__FLAGS_INIT: 51 | # https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_FLAGS_INIT.html 52 | 53 | set(CMAKE_C_FLAGS_INIT 54 | "${CPU_FLAGS} ${VFP_FLAGS} -fdata-sections -ffunction-sections" 55 | CACHE 56 | INTERNAL "Default C compiler flags.") 57 | set(CMAKE_CXX_FLAGS_INIT 58 | "${CPU_FLAGS} ${VFP_FLAGS} -fdata-sections -ffunction-sections" 59 | CACHE 60 | INTERNAL "Default C++ compiler flags.") 61 | set(CMAKE_ASM_FLAGS_INIT 62 | "${CPU_FLAGS} -x assembler-with-cpp" 63 | CACHE 64 | INTERNAL "Default ASM compiler flags.") 65 | set(CMAKE_EXE_LINKER_FLAGS_INIT 66 | "${LD_FLAGS} -Wl,--gc-sections" 67 | CACHE 68 | INTERNAL "Default linker flags.") 69 | -------------------------------------------------------------------------------- /toolchains/cross/cortex-m3.cmake: -------------------------------------------------------------------------------- 1 | ############# 2 | # Cortex-M3 # 3 | ############# 4 | 5 | # CMake includes the toolchain file multiple times when configuring the build, 6 | # which causes errors for some flags (e.g., --specs=nano.specs). 7 | # We prevent this with an include guard. 8 | if(ARM_CORTEX_M3_TOOLCHAIN_INCLUDED) 9 | return() 10 | endif() 11 | 12 | set(ARM_CORTEX_M3_TOOLCHAIN_INCLUDED true) 13 | 14 | if(NOT CPU_NAME) 15 | set(CPU_NAME cortex-m3) 16 | endif() 17 | 18 | set(CPU_FLAGS "-mcpu=cortex-m3 -mthumb --specs=nosys.specs ${CPU_FLAGS}") 19 | set(VFP_FLAGS "-mfloat-abi=soft ${VFP_FLAGS}") 20 | 21 | include(${CMAKE_CURRENT_LIST_DIR}/arm-none-eabi-gcc.cmake) 22 | -------------------------------------------------------------------------------- /toolchains/cross/cortex-m4_hardfloat.cmake: -------------------------------------------------------------------------------- 1 | ######################## 2 | # Cortex-M4 Hard-float # 3 | ######################## 4 | 5 | # CMake includes the toolchain file multiple times when configuring the build, 6 | # which causes errors for some flags (e.g., --specs=nano.specs). 7 | # We prevent this with an include guard. 8 | if(ARM_CORTEX_M4_HARDFLOAT_TOOLCHAIN_INCLUDED) 9 | return() 10 | endif() 11 | 12 | set(ARM_CORTEX_M4_HARDFLOAT_TOOLCHAIN_INCLUDED true) 13 | 14 | if(NOT CPU_NAME) 15 | set(CPU_NAME cortex-m4) 16 | endif() 17 | set(CPU_FLAGS "-mcpu=cortex-m4 -mthumb --specs=nosys.specs ${CPU_FLAGS}") 18 | set(VFP_FLAGS "-mfloat-abi=hard -mfpu=fpv4-sp-d16 ${VFP_FLAGS}") 19 | 20 | include(${CMAKE_CURRENT_LIST_DIR}/arm-none-eabi-gcc.cmake) 21 | -------------------------------------------------------------------------------- /toolchains/cross/cortex-m7_hardfloat.cmake: -------------------------------------------------------------------------------- 1 | ######################## 2 | # Cortex-M7 Hard-float # 3 | ######################## 4 | 5 | # CMake includes the toolchain file multiple times when configuring the build, 6 | # which causes errors for some flags (e.g., --specs=nano.specs). 7 | # We prevent this with an include guard. 8 | if(ARM_CORTEX_M7_HARDFLOAT_TOOLCHAIN_INCLUDED) 9 | return() 10 | endif() 11 | 12 | set(ARM_CORTEX_M7_HARDFLOAT_TOOLCHAIN_INCLUDED true) 13 | 14 | if(NOT CPU_NAME) 15 | set(CPU_NAME cortex-m7) 16 | endif() 17 | set(CPU_FLAGS "-mcpu=cortex-m7 -mthumb --specs=nosys.specs ${CPU_FLAGS}") 18 | set(VFP_FLAGS "-mfloat-abi=hard -mfpu=fpv4-sp-d16 ${VFP_FLAGS}") 19 | 20 | include(${CMAKE_CURRENT_LIST_DIR}/arm-none-eabi-gcc.cmake) 21 | -------------------------------------------------------------------------------- /toolchains/native/clang.cmake: -------------------------------------------------------------------------------- 1 | ################### 2 | # Clang Toolchain # 3 | ################### 4 | 5 | set(CMAKE_C_COMPILER clang) 6 | set(CMAKE_CXX_COMPILER clang++) 7 | set(CMAKE_AR llvm-ar) 8 | -------------------------------------------------------------------------------- /toolchains/native/gcc-10.cmake: -------------------------------------------------------------------------------- 1 | #################### 2 | # GCC-10 Toolchain # 3 | #################### 4 | 5 | set(CMAKE_C_COMPILER gcc-10) 6 | set(CMAKE_CXX_COMPILER g++-10) 7 | if(NOT (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin")) 8 | set(CMAKE_AR gcc-ar-10) 9 | endif() 10 | 11 | -------------------------------------------------------------------------------- /toolchains/native/gcc-11.cmake: -------------------------------------------------------------------------------- 1 | #################### 2 | # GCC-11 Toolchain # 3 | #################### 4 | 5 | set(CMAKE_C_COMPILER gcc-11) 6 | set(CMAKE_CXX_COMPILER g++-11) 7 | if(NOT (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin")) 8 | set(CMAKE_AR gcc-ar-11) 9 | endif() 10 | 11 | -------------------------------------------------------------------------------- /toolchains/native/gcc-12.cmake: -------------------------------------------------------------------------------- 1 | #################### 2 | # GCC-12 Toolchain # 3 | #################### 4 | 5 | set(CMAKE_C_COMPILER gcc-12) 6 | set(CMAKE_CXX_COMPILER g++-12) 7 | if(NOT (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin")) 8 | set(CMAKE_AR gcc-ar-12) 9 | endif() 10 | 11 | -------------------------------------------------------------------------------- /toolchains/native/gcc-13.cmake: -------------------------------------------------------------------------------- 1 | #################### 2 | # GCC-13 Toolchain # 3 | #################### 4 | 5 | set(CMAKE_C_COMPILER gcc-13) 6 | set(CMAKE_CXX_COMPILER g++-13) 7 | if(NOT (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin")) 8 | set(CMAKE_AR gcc-ar-13) 9 | endif() 10 | 11 | -------------------------------------------------------------------------------- /toolchains/native/gcc-7.cmake: -------------------------------------------------------------------------------- 1 | ################### 2 | # GCC-7 Toolchain # 3 | ################### 4 | 5 | set(CMAKE_C_COMPILER gcc-7) 6 | set(CMAKE_CXX_COMPILER g++-7) 7 | if(NOT (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin")) 8 | # gcc-ar-7 does not successfully find the linker plugin on Darwin 9 | set(CMAKE_AR gcc-ar-7) 10 | endif() 11 | -------------------------------------------------------------------------------- /toolchains/native/gcc-8.cmake: -------------------------------------------------------------------------------- 1 | ################### 2 | # GCC-8 Toolchain # 3 | ################### 4 | 5 | set(CMAKE_C_COMPILER gcc-8) 6 | set(CMAKE_CXX_COMPILER g++-8) 7 | if(NOT (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin")) 8 | # gcc-ar-8 does not successfully find the linker plugin on Darwin 9 | set(CMAKE_AR gcc-ar-7) 10 | endif() 11 | 12 | -------------------------------------------------------------------------------- /toolchains/native/gcc-9.cmake: -------------------------------------------------------------------------------- 1 | ################### 2 | # GCC-9 Toolchain # 3 | ################### 4 | 5 | set(CMAKE_C_COMPILER gcc-9) 6 | set(CMAKE_CXX_COMPILER g++-9) 7 | # You could also use 'ar' from binutils 8 | set(CMAKE_AR gcc-ar-9) 9 | -------------------------------------------------------------------------------- /toolchains/native/gcc.cmake: -------------------------------------------------------------------------------- 1 | ################# 2 | # GCC Toolchain # 3 | ################# 4 | 5 | set(CMAKE_C_COMPILER gcc) 6 | set(CMAKE_CXX_COMPILER g++) 7 | # You could also use 'ar' from binutils 8 | set(CMAKE_AR gcc-ar) 9 | -------------------------------------------------------------------------------- /toolchains/native/homebrew_clang.cmake: -------------------------------------------------------------------------------- 1 | ################################################## 2 | # Standard Clang Toolchain On OSX (via Homebrew) # 3 | ################################################## 4 | # This file is for using Mainline Clang on OS X 5 | # This is expected to be installed by Homebrew. 6 | 7 | set(CMAKE_C_COMPILER /usr/local/opt/llvm/bin/clang) 8 | set(CMAKE_CXX_COMPILER /usr/local/opt/llvm/bin/clang++) 9 | set(CMAKE_AR /usr/local/opt/llvm/bin/llvm-ar) 10 | 11 | if((${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin") AND (${CMAKE_HOST_SYSTEM_VERSION} VERSION_LESS "19")) 12 | # Resolve `-platform-version` error with macOS versions < Catalina and latest Clang 13 | set(CMAKE_EXE_LINKER_FLAGS_INIT 14 | "-mlinker-version=450" 15 | CACHE 16 | INTERNAL "Default linker flags.") 17 | endif() 18 | 19 | --------------------------------------------------------------------------------