From 69070a82d98cf7d7003a2f3a46b69b40b1a874b1 Mon Sep 17 00:00:00 2001 From: pedroo-seaiaa <138585043+pedroo-seaiaa@users.noreply.github.com> Date: Wed, 7 Feb 2024 08:13:18 +0900 Subject: [PATCH] Release 0.1.0 (#43) * Updates README * Adds basic project structure * Adds first version of configure batch file * Adds git hook scripts and configuration on setup (#6) * Changes configure batch file * Adds sample hooks * Adds simple git hooks * Fixes hooksPath * Adds configure shell script to linux (#8) * Adds configure for Linux * Triggers Git Hooks Activation * Changes file permissions of git hooks * Adds build dependencies to setup python script (#11) * Changes configure shell script to colorize output on git bash * First version of python setup * Refactors import_package from setup to utils * Refactors check_system_requirements into function * Updates references to python setup script * Removes vscode launch file * Adds visual studio code to gitignore * Refactors bash logger to its own file (#12) * Adds a simple cpp program (#14) * Updates Building doc * Adds formatting configuration files (#17) * Adds a sample program with library application and tests (#18) * Adds vcpkg to BuildTools * Adds solution for Windows Visual Studio * spdlog example working on vscode * Seems to work on VSCode and VS * Adds runTests script (#20) * Adds basic CI through GitHub Actions (#22) * Adds basic CI through GitHub Actions * Update GitHub workflow * Fixes CI jobs without setup cpp * Fixes target_include * Testing lefticus workflow * Fixes typo * setup CI llvm version * Fixes missing generator and build_type on CI * Updates CI * Experiments with string manipulation on matrix strategy * Removes MacOS * Adds run-tests to pre commit hooks and prompt installation on configure (#24) * Adds LCOV to configure shell script * Adds missing pre-commit related files * Adds a Dockerfile to test CI scripts (#31) * Adds basic dockerfile test * Need to work in WSL * WIP * Refactoring - variable renaming * Adds sugar stdout * WIP * Installing cmake automagically * Adds option to bypass hooks configuration * Add self-hosted windows workflow to GitHub Actions (#33) * Starts with greeting * Adds checkout step to windows-hosted CI workflow * Adds configuring to CI workflow * Adds bootstrapping vcpkg step to CI workflow * Adds dummy steps to windows workflow * Adds build step * Fixes building step in CI workflow * Fixes typos in building step of CI workflow * Adds test step to CI workflow * Adds matrix strategy to windows self-hosted * Checking PATH for ccache * Adds call to vcvarsall on windows CI workflow * Using cmd instead of call in windows CI workflow * Temporarily changes fail-fast to true * Fixes checkgin ccache on CI workflow * Testing echo PATH * Adds aminya setup cpp GitHub Action * Cleaning * using Mozilla sccache instead of ccache * Trying cppcache instead of sccacche * Reverts back to ccache * Removing ccache from windows CI workflow setup-cpp step * Echo new env var CCACHE_PATH * Adding debug info * Adding debug info * Adding debug info 3 * Adding debug info 4 * Cleaning * Adds Linux job to the CI workflow (#36) * Adds linux job to the CI workflow * Updates cmakeMinimumRequired in CMakePreset * Adds caching to CI workflow (#37) * Testing cloning once and only once * More changes * Adds GitHub Action cache v3 to CI workflow * Adds GitHub-Hosted-Matrix-Job * Adds CMAKE_TOOLCHAIN_FILE to GitHub-Hosted-Matrix-Jobs * Adds lcov to setup cpp on Matrix-Jobs CI workflow * Updating setup-cpp on CI workflow * dos2unix * Adds setup LCOV to CI workflow * Fixes typo * WIP * Adds setup LCOV to windows-hosted CI * All local configurations seem to be working * Fixes GitHub-Hosted windows-latest msvc * Removes echo and adds build_type to ctest * Fixes unexpected input to setup CPP on CI workflow * Removes lcov * Fixing windows-latest with msvc * Fixing windows-latest with msvc 2 * Fixing windows-latest with msvc 3 * Fixing windows-latest with msvc 4 * Fixing GitHub-Hosted-Matrix-Job with msvc * Try to cache setup-cpp installation dirs * Revert "Try to cache setup-cpp installation dirs" This reverts commit e8b71c73a13b2c65e763030f2b996ef090e4c667. * Adds support for clang-tidy (#39) * Adds antiphon clang-tidy * Disables clang-tidy * Adds PCH library * Clang-tidy working on windows-msvc-dev * Formatting * Using default clang-tidy config * clang-tidy working on all systems and targets * Adds CMAKE_BUILD_TYPE to CI workflow configure cmake step --- .clang-format | 127 ++ .clang-tidy | 38 + .cmake-format.yaml | 259 ++++ .github/workflows/cmake-multi-platform.yml | 185 +++ .gitignore | 22 + .gitmodules | 3 + .vscode/settings.json | 6 + CMakeLists.txt | 449 +++++++ CMakePresets.json | 402 ++++++ README.md | 53 + configure.bat | 78 ++ configure.sh | 137 ++ docs/BUILDING.md | 16 + include/Project/ProjectLib.hpp | 19 + src/ProjectApp/ProjectApp.cpp | 23 + src/ProjectLib/ProjectLib.cpp | 18 + src/ProjectPCH/StandardLibrary.cpp | 1 + src/ProjectPCH/StandardLibrary.hpp | 380 ++++++ tests/FuzzTests/FuzzTestsMain.cpp | 21 + tests/MainTests/MainTests.cpp | 34 + tests/PerfTests/PerfTestsMain.cpp | 37 + tests/SetupTests/docker/linux.Dockerfile | 8 + tests/SetupTests/scripts/testSetup.sh | 137 ++ tests/UnitTests/UnitTestsMain.cpp | 24 + .../cmake/Modules/BuildToolsMain.cmake | 95 ++ tools/BuildTools/cmake/Modules/CCache.cmake | 62 + .../cmake/Modules/CMakeProjectInclude.cmake | 1 + .../Modules/CMakeProjectIncludeBefore.cmake | 1 + tools/BuildTools/cmake/Modules/CPM.cmake | 1161 +++++++++++++++++ .../cmake/Modules/CodeCoverage.cmake | 743 +++++++++++ .../cmake/Modules/CompilerWarnings.cmake | 129 ++ tools/BuildTools/cmake/Modules/Doxygen.cmake | 52 + .../cmake/Modules/FetchAndInstall.cmake | 103 ++ .../BuildTools/cmake/Modules/FetchCatch.cmake | 35 + tools/BuildTools/cmake/Modules/FetchGSL.cmake | 27 + .../BuildTools/cmake/Modules/FetchGTest.cmake | 30 + .../BuildTools/cmake/Modules/Filesystem.cmake | 33 + .../cmake/Modules/HostSystemInformation.cmake | 92 ++ tools/BuildTools/cmake/Modules/Linker.cmake | 65 + tools/BuildTools/cmake/Modules/Logger.cmake | 107 ++ .../cmake/Modules/PreCompiledHeaders.cmake | 84 ++ .../cmake/Modules/PreventInSourceBuilds.cmake | 52 + .../BuildTools/cmake/Modules/Sanitizers.cmake | 96 ++ .../Modules/StandardProjectSettings.cmake | 76 ++ .../cmake/Modules/StaticAnalyzers.cmake | 77 ++ .../cmake/Templates/IncludeMeta.cmake | 11 + tools/BuildTools/cmake/Templates/Meta.h.in | 46 + tools/BuildTools/scripts/dependencies.py | 98 ++ tools/BuildTools/scripts/setup.py | 9 + tools/BuildTools/scripts/utils.py | 92 ++ tools/BuildTools/vcpkg | 1 + tools/GitTools/Hooks/applypatch-msg | 17 + tools/GitTools/Hooks/commit-msg | 26 + tools/GitTools/Hooks/fsmonitor-watchman | 176 +++ tools/GitTools/Hooks/post-update | 10 + tools/GitTools/Hooks/pre-applypatch | 16 + tools/GitTools/Hooks/pre-commit | 67 + tools/GitTools/Hooks/pre-merge-commit | 15 + tools/GitTools/Hooks/pre-push | 58 + tools/GitTools/Hooks/pre-rebase | 171 +++ tools/GitTools/Hooks/pre-receive | 26 + tools/GitTools/Hooks/prepare-commit-msg | 44 + tools/GitTools/Hooks/push-to-checkout | 80 ++ tools/GitTools/Hooks/sendemail-validate | 79 ++ tools/GitTools/Hooks/update | 130 ++ tools/GitTools/Utils/logger.sh | 138 ++ tools/GitTools/Utils/runTests.sh | 85 ++ tools/GitTools/Utils/utilities.sh | 12 + vcpkg.json | 11 + 69 files changed, 7016 insertions(+) create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 .cmake-format.yaml create mode 100644 .github/workflows/cmake-multi-platform.yml create mode 100644 .gitmodules create mode 100644 .vscode/settings.json create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json create mode 100644 configure.bat create mode 100755 configure.sh create mode 100644 docs/BUILDING.md create mode 100644 include/Project/ProjectLib.hpp create mode 100644 src/ProjectApp/ProjectApp.cpp create mode 100644 src/ProjectLib/ProjectLib.cpp create mode 100644 src/ProjectPCH/StandardLibrary.cpp create mode 100644 src/ProjectPCH/StandardLibrary.hpp create mode 100644 tests/FuzzTests/FuzzTestsMain.cpp create mode 100644 tests/MainTests/MainTests.cpp create mode 100644 tests/PerfTests/PerfTestsMain.cpp create mode 100644 tests/SetupTests/docker/linux.Dockerfile create mode 100644 tests/SetupTests/scripts/testSetup.sh create mode 100644 tests/UnitTests/UnitTestsMain.cpp create mode 100644 tools/BuildTools/cmake/Modules/BuildToolsMain.cmake create mode 100644 tools/BuildTools/cmake/Modules/CCache.cmake create mode 100644 tools/BuildTools/cmake/Modules/CMakeProjectInclude.cmake create mode 100644 tools/BuildTools/cmake/Modules/CMakeProjectIncludeBefore.cmake create mode 100644 tools/BuildTools/cmake/Modules/CPM.cmake create mode 100644 tools/BuildTools/cmake/Modules/CodeCoverage.cmake create mode 100644 tools/BuildTools/cmake/Modules/CompilerWarnings.cmake create mode 100644 tools/BuildTools/cmake/Modules/Doxygen.cmake create mode 100644 tools/BuildTools/cmake/Modules/FetchAndInstall.cmake create mode 100644 tools/BuildTools/cmake/Modules/FetchCatch.cmake create mode 100644 tools/BuildTools/cmake/Modules/FetchGSL.cmake create mode 100644 tools/BuildTools/cmake/Modules/FetchGTest.cmake create mode 100644 tools/BuildTools/cmake/Modules/Filesystem.cmake create mode 100644 tools/BuildTools/cmake/Modules/HostSystemInformation.cmake create mode 100644 tools/BuildTools/cmake/Modules/Linker.cmake create mode 100644 tools/BuildTools/cmake/Modules/Logger.cmake create mode 100644 tools/BuildTools/cmake/Modules/PreCompiledHeaders.cmake create mode 100644 tools/BuildTools/cmake/Modules/PreventInSourceBuilds.cmake create mode 100644 tools/BuildTools/cmake/Modules/Sanitizers.cmake create mode 100644 tools/BuildTools/cmake/Modules/StandardProjectSettings.cmake create mode 100644 tools/BuildTools/cmake/Modules/StaticAnalyzers.cmake create mode 100644 tools/BuildTools/cmake/Templates/IncludeMeta.cmake create mode 100644 tools/BuildTools/cmake/Templates/Meta.h.in create mode 100644 tools/BuildTools/scripts/dependencies.py create mode 100644 tools/BuildTools/scripts/setup.py create mode 100644 tools/BuildTools/scripts/utils.py create mode 160000 tools/BuildTools/vcpkg create mode 100755 tools/GitTools/Hooks/applypatch-msg create mode 100755 tools/GitTools/Hooks/commit-msg create mode 100755 tools/GitTools/Hooks/fsmonitor-watchman create mode 100755 tools/GitTools/Hooks/post-update create mode 100755 tools/GitTools/Hooks/pre-applypatch create mode 100755 tools/GitTools/Hooks/pre-commit create mode 100755 tools/GitTools/Hooks/pre-merge-commit create mode 100755 tools/GitTools/Hooks/pre-push create mode 100755 tools/GitTools/Hooks/pre-rebase create mode 100755 tools/GitTools/Hooks/pre-receive create mode 100755 tools/GitTools/Hooks/prepare-commit-msg create mode 100755 tools/GitTools/Hooks/push-to-checkout create mode 100755 tools/GitTools/Hooks/sendemail-validate create mode 100755 tools/GitTools/Hooks/update create mode 100644 tools/GitTools/Utils/logger.sh create mode 100755 tools/GitTools/Utils/runTests.sh create mode 100644 tools/GitTools/Utils/utilities.sh create mode 100644 vcpkg.json diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..ba3dd72 --- /dev/null +++ b/.clang-format @@ -0,0 +1,127 @@ +--- +Language: Cpp +# BasedOnStyle: Mozilla +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: false + AfterEnum: true + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: false + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeComma +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 160 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: false +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +... + diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..d9ac6df --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,38 @@ +--- +# Version: 15.y.z +Checks: 'clang-diagnostic-*,clang-analyzer-*' +WarningsAsErrors: '' +# NOTE(PO): Available on Version 17.y.z +# HeaderFileExtensions: +# - '' +# - h +# - hh +# - hpp +# - hxx +# NOTE(PO): Available on Version 17.y.z +# ImplementationFileExtensions: +# - c +# - cc +# - cpp +# - cxx +HeaderFilterRegex: '' +AnalyzeTemporaryDtors: false +FormatStyle: none +User: pedroo +CheckOptions: + cert-dcl16-c.NewSuffixes: 'L;LL;LU;LLU' + google-readability-namespace-comments.ShortNamespaceLines: '10' + cert-err33-c.CheckedFunctions: '::aligned_alloc;::asctime_s;::at_quick_exit;::atexit;::bsearch;::bsearch_s;::btowc;::c16rtomb;::c32rtomb;::calloc;::clock;::cnd_broadcast;::cnd_init;::cnd_signal;::cnd_timedwait;::cnd_wait;::ctime_s;::fclose;::fflush;::fgetc;::fgetpos;::fgets;::fgetwc;::fopen;::fopen_s;::fprintf;::fprintf_s;::fputc;::fputs;::fputwc;::fputws;::fread;::freopen;::freopen_s;::fscanf;::fscanf_s;::fseek;::fsetpos;::ftell;::fwprintf;::fwprintf_s;::fwrite;::fwscanf;::fwscanf_s;::getc;::getchar;::getenv;::getenv_s;::gets_s;::getwc;::getwchar;::gmtime;::gmtime_s;::localtime;::localtime_s;::malloc;::mbrtoc16;::mbrtoc32;::mbsrtowcs;::mbsrtowcs_s;::mbstowcs;::mbstowcs_s;::memchr;::mktime;::mtx_init;::mtx_lock;::mtx_timedlock;::mtx_trylock;::mtx_unlock;::printf_s;::putc;::putwc;::raise;::realloc;::remove;::rename;::scanf;::scanf_s;::setlocale;::setvbuf;::signal;::snprintf;::snprintf_s;::sprintf;::sprintf_s;::sscanf;::sscanf_s;::strchr;::strerror_s;::strftime;::strpbrk;::strrchr;::strstr;::strtod;::strtof;::strtoimax;::strtok;::strtok_s;::strtol;::strtold;::strtoll;::strtoul;::strtoull;::strtoumax;::strxfrm;::swprintf;::swprintf_s;::swscanf;::swscanf_s;::thrd_create;::thrd_detach;::thrd_join;::thrd_sleep;::time;::timespec_get;::tmpfile;::tmpfile_s;::tmpnam;::tmpnam_s;::tss_create;::tss_get;::tss_set;::ungetc;::ungetwc;::vfprintf;::vfprintf_s;::vfscanf;::vfscanf_s;::vfwprintf;::vfwprintf_s;::vfwscanf;::vfwscanf_s;::vprintf_s;::vscanf;::vscanf_s;::vsnprintf;::vsnprintf_s;::vsprintf;::vsprintf_s;::vsscanf;::vsscanf_s;::vswprintf;::vswprintf_s;::vswscanf;::vswscanf_s;::vwprintf_s;::vwscanf;::vwscanf_s;::wcrtomb;::wcschr;::wcsftime;::wcspbrk;::wcsrchr;::wcsrtombs;::wcsrtombs_s;::wcsstr;::wcstod;::wcstof;::wcstoimax;::wcstok;::wcstok_s;::wcstol;::wcstold;::wcstoll;::wcstombs;::wcstombs_s;::wcstoul;::wcstoull;::wcstoumax;::wcsxfrm;::wctob;::wctrans;::wctype;::wmemchr;::wprintf_s;::wscanf;::wscanf_s;' + llvm-else-after-return.WarnOnUnfixable: 'false' + cert-str34-c.DiagnoseSignedUnsignedCharComparisons: 'false' + google-readability-namespace-comments.SpacesBeforeComments: '2' + cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: 'true' + google-readability-braces-around-statements.ShortStatementLines: '1' + google-readability-function-size.StatementThreshold: '800' + llvm-qualified-auto.AddConstToQualified: 'false' + llvm-else-after-return.WarnOnConditionVariables: 'false' + cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField: 'false' +# NOTE(PO): Available on Version 17.y.z +# SystemHeaders: false +... + diff --git a/.cmake-format.yaml b/.cmake-format.yaml new file mode 100644 index 0000000..3033585 --- /dev/null +++ b/.cmake-format.yaml @@ -0,0 +1,259 @@ +_help_parse: Options affecting listfile parsing +parse: + _help_additional_commands: + - Specify structure for custom cmake functions + additional_commands: + setup_target_for_coverage_lcov: + flags: + - NO_DEMANGLE + - SONARQUBE + kwargs: + NAME: '*' + EXCLUDE: '*' + EXECUTABLE: '*' + EXECUTABLE_ARGS: '*' + DEPENDENCIES: '*' + BASE_DIRECTORY: '*' + LCOV_ARGS: '*' + GENHTML_ARGS: '*' + foo: + flags: + - BAR + - BAZ + kwargs: + HEADERS: '*' + SOURCES: '*' + DEPENDS: '*' + _help_override_spec: + - Override configurations per-command where available + override_spec: {} + _help_vartags: + - Specify variable tags. + vartags: [] + _help_proptags: + - Specify property tags. + proptags: [] +_help_format: Options affecting formatting. +format: + _help_disable: + - Disable formatting entirely, making cmake-format a no-op + disable: false + _help_line_width: + - How wide to allow formatted cmake files + line_width: 160 + _help_tab_size: + - How many spaces to tab for indent + tab_size: 2 + _help_use_tabchars: + - If true, lines are indented using tab characters (utf-8 + - 0x09) instead of space characters (utf-8 0x20). + - In cases where the layout would require a fractional tab + - character, the behavior of the fractional indentation is + - governed by + use_tabchars: false + _help_fractional_tab_policy: + - If is True, then the value of this variable + - indicates how fractional indentions are handled during + - whitespace replacement. If set to 'use-space', fractional + - indentation is left as spaces (utf-8 0x20). If set to + - '`round-up` fractional indentation is replaced with a single' + - tab character (utf-8 0x09) effectively shifting the column + - to the next tabstop + fractional_tab_policy: use-space + _help_max_subgroups_hwrap: + - If an argument group contains more than this many sub-groups + - (parg or kwarg groups) then force it to a vertical layout. + max_subgroups_hwrap: 2 + _help_max_pargs_hwrap: + - If a positional argument group contains more than this many + - arguments, then force it to a vertical layout. + max_pargs_hwrap: 3 + _help_max_rows_cmdline: + - If a cmdline positional group consumes more than this many + - lines without nesting, then invalidate the layout (and nest) + max_rows_cmdline: 2 + _help_separate_ctrl_name_with_space: + - If true, separate flow control names from their parentheses + - with a space + separate_ctrl_name_with_space: false + _help_separate_fn_name_with_space: + - If true, separate function names from parentheses with a + - space + separate_fn_name_with_space: false + _help_dangle_parens: + - If a statement is wrapped to more than one line, than dangle + - the closing parenthesis on its own line. + dangle_parens: true + _help_dangle_align: + - If the trailing parenthesis must be 'dangled' on its on + - 'line, then align it to this reference: `prefix`: the start' + - 'of the statement, `prefix-indent`: the start of the' + - 'statement, plus one indentation level, `child`: align to' + - the column of the arguments + dangle_align: prefix + _help_min_prefix_chars: + - If the statement spelling length (including space and + - parenthesis) is smaller than this amount, then force reject + - nested layouts. + min_prefix_chars: 4 + _help_max_prefix_chars: + - If the statement spelling length (including space and + - parenthesis) is larger than the tab width by more than this + - amount, then force reject un-nested layouts. + max_prefix_chars: 10 + _help_max_lines_hwrap: + - If a candidate layout is wrapped horizontally but it exceeds + - this many lines, then reject the layout. + max_lines_hwrap: 2 + _help_line_ending: + - What style line endings to use in the output. + line_ending: unix + _help_command_case: + - Format command names consistently as 'lower' or 'upper' case + command_case: canonical + _help_keyword_case: + - Format keywords consistently as 'lower' or 'upper' case + keyword_case: upper + _help_always_wrap: + - A list of command names which should always be wrapped + always_wrap: [] + _help_enable_sort: + - If true, the argument lists which are known to be sortable + - will be sorted lexicographicall + enable_sort: true + _help_autosort: + - If true, the parsers may infer whether or not an argument + - list is sortable (without annotation). + autosort: false + _help_require_valid_layout: + - By default, if cmake-format cannot successfully fit + - everything into the desired linewidth it will apply the + - last, most agressive attempt that it made. If this flag is + - True, however, cmake-format will print error, exit with non- + - zero status code, and write-out nothing + require_valid_layout: false + _help_layout_passes: + - A dictionary mapping layout nodes to a list of wrap + - decisions. See the documentation for more information. + layout_passes: {} +_help_markup: Options affecting comment reflow and formatting. +markup: + _help_bullet_char: + - What character to use for bulleted lists + bullet_char: '*' + _help_enum_char: + - What character to use as punctuation after numerals in an + - enumerated list + enum_char: . + _help_first_comment_is_literal: + - If comment markup is enabled, don't reflow the first comment + - block in each listfile. Use this to preserve formatting of + - your copyright/license statements. + first_comment_is_literal: false + _help_literal_comment_pattern: + - If comment markup is enabled, don't reflow any comment block + - which matches this (regex) pattern. Default is `None` + - (disabled). + literal_comment_pattern: .* # disabling all comments + _help_fence_pattern: + - Regular expression to match preformat fences in comments + - default= ``r'^\s*([`~]{3}[`~]*)(.*)$'`` + fence_pattern: ^\\s*([`~]{3}[`~]*)(.*)$ + _help_ruler_pattern: + - Regular expression to match rulers in comments default= + - '``r''^\s*[^\w\s]{3}.*[^\w\s]{3}$''``' + ruler_pattern: ^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$ + _help_explicit_trailing_pattern: + - If a comment line matches starts with this pattern then it + - is explicitly a trailing comment for the preceeding + - argument. Default is '#<' + explicit_trailing_pattern: '#<' + _help_hashruler_min_length: + - If a comment line starts with at least this many consecutive + - hash characters, then don't lstrip() them off. This allows + - for lazy hash rulers where the first hash char is not + - separated by space + hashruler_min_length: 10 + _help_canonicalize_hashrulers: + - If true, then insert a space between the first hash char and + - remaining hash chars in a hash ruler, and normalize its + - length to fill the column + canonicalize_hashrulers: true + _help_enable_markup: + - enable comment markup parsing and reflow + enable_markup: true +_help_lint: Options affecting the linter +lint: + _help_disabled_codes: + - a list of lint codes to disable + disabled_codes: + - C0113 + _help_function_pattern: + - regular expression pattern describing valid function names + function_pattern: '[0-9a-z_]+' + _help_macro_pattern: + - regular expression pattern describing valid macro names + macro_pattern: '[0-9A-Z_]+' + _help_global_var_pattern: + - regular expression pattern describing valid names for + - variables with global (cache) scope + global_var_pattern: '[A-Z][0-9A-Z_]+' + _help_internal_var_pattern: + - regular expression pattern describing valid names for + - variables with global scope (but internal semantic) + internal_var_pattern: _[A-Z][0-9A-Z_]+ + _help_local_var_pattern: + - regular expression pattern describing valid names for + - variables with local scope + local_var_pattern: '[a-z][a-z0-9_]+' + _help_private_var_pattern: + - regular expression pattern describing valid names for + - privatedirectory variables + private_var_pattern: _[0-9a-z_]+ + _help_public_var_pattern: + - regular expression pattern describing valid names for public + - directory variables + public_var_pattern: '[A-Z][0-9A-Z_]+' + _help_argument_var_pattern: + - regular expression pattern describing valid names for + - function/macro arguments and loop variables. + argument_var_pattern: '[a-z][a-z0-9_]+' + _help_keyword_pattern: + - regular expression pattern describing valid names for + - keywords used in functions or macros + keyword_pattern: '[A-Z][0-9A-Z_]+' + _help_max_conditionals_custom_parser: + - In the heuristic for C0201, how many conditionals to match + - within a loop in before considering the loop a parser. + max_conditionals_custom_parser: 2 + _help_min_statement_spacing: + - Require at least this many newlines between statements + min_statement_spacing: 1 + _help_max_statement_spacing: + - Require no more than this many newlines between statements + max_statement_spacing: 2 + max_returns: 6 + max_branches: 12 + max_arguments: 5 + max_localvars: 15 + max_statements: 50 +_help_encode: Options affecting file encoding +encode: + _help_emit_byteorder_mark: + - If true, emit the unicode byte-order mark (BOM) at the start + - of the file + emit_byteorder_mark: false + _help_input_encoding: + - Specify the encoding of the input file. Defaults to utf-8 + input_encoding: utf-8 + _help_output_encoding: + - Specify the encoding of the output file. Defaults to utf-8. + - Note that cmake only claims to support utf-8 so be careful + - when using anything else + output_encoding: utf-8 +_help_misc: Miscellaneous configurations options. +misc: + _help_per_command: + - A dictionary containing any per-command configuration + - overrides. Currently only `command_case` is supported. + per_command: {} diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml new file mode 100644 index 0000000..128ab15 --- /dev/null +++ b/.github/workflows/cmake-multi-platform.yml @@ -0,0 +1,185 @@ +name: CMake on multiple platforms + +on: + push: + branches: + - master + - main + - dev + pull_request: + branches: + - master + - main + - dev + +jobs: + # Cloning-Job: + # runs-on: [self-hosted, x64] + # steps: + # - name: Cloning + # uses: actions/checkout@v4 + # with: + # submodules: recursive + + # Self-Hosted-Windows-Job: + # runs-on: [self-hosted, Windows, x64] + # needs: Cloning-Job + # strategy: + # fail-fast: true + # matrix: + # compiler: + # - msvc + # - clang + # build_type: + # - debug + # - dev + # - release + # steps: + # - name: Cache + # uses: actions/cache@v3 + # with: + # path: | + # ~/vcpkg + # ./build/vcpkg_installed + # ${{ env.HOME }}/.cache/vcpkg/archives + # ${{ env.XDG_CACHE_HOME }}/vcpkg/archives + # ${{ env.LOCALAPPDATA }}\vcpkg\archives + # ${{ env.APPDATA }}\vcpkg\archives + # key: windows-${{ matrix.compiler }}-${{ matrix.build_type }}-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('./vcpkg.json')}} + # restore-keys: | + # windows-${{ matrix.build_type }}- + # - name: Setup Cpp + # uses: aminya/setup-cpp@v1 + # with: + # vcvarsall: true + # - name: Configuring + # run: | + # echo "[cmake-multi-platform][TRACE] Configuring" + # cmake -S ${{ github.workspace }} --preset windows-${{matrix.compiler}}-${{matrix.build_type}} + # - name: Building + # run: | + # echo "[cmake-multi-platform][TRACE] Building" + # cmake --build ${{ github.workspace }}/out/build/windows-${{matrix.compiler}}-${{matrix.build_type}} + # - name: Testing + # run: | + # echo "[cmake-multi-platform][TRACE] Testing" + # ctest --test-dir ${{ github.workspace }}/out/build/windows-${{matrix.compiler}}-${{matrix.build_type}} + # - name: Installing + # run: echo "[cmake-multi-platform][TRACE] Installing" + # - name: Packaging + # run: echo "[cmake-multi-platform][TRACE] Packaging" + + # Self-Hosted-Linux-Job: + # runs-on: [self-hosted, Linux, x64] + # needs: Cloning-Job + # strategy: + # fail-fast: true + # matrix: + # compiler: + # - gcc + # - clang + # build_type: + # - debug + # - dev + # - release + # steps: + # - name: Cache + # uses: actions/cache@v3 + # with: + # path: | + # ~/vcpkg + # ./build/vcpkg_installed + # ${{ env.HOME }}/.cache/vcpkg/archives + # ${{ env.XDG_CACHE_HOME }}/vcpkg/archives + # ${{ env.LOCALAPPDATA }}\vcpkg\archives + # ${{ env.APPDATA }}\vcpkg\archives + # key: linux-${{ matrix.compiler }}-${{ matrix.build_type }}-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('./vcpkg.json')}} + # restore-keys: | + # linux-${{ matrix.build_type }}- + # - name: Configuring + # run: | + # echo "[cmake-multi-platform][TRACE] Configuring" + # cmake -S ${{ github.workspace }} --preset linux-${{matrix.compiler}}-${{matrix.build_type}} + # - name: Building + # run: | + # echo "[cmake-multi-platform][TRACE] Building" + # cmake --build ${{ github.workspace }}/out/build/linux-${{matrix.compiler}}-${{matrix.build_type}} + # - name: Testing + # run: | + # echo "[cmake-multi-platform][TRACE] Testing" + # ctest --test-dir ${{ github.workspace }}/out/build/linux-${{matrix.compiler}}-${{matrix.build_type}} + # - name: Installing + # run: echo "[cmake-multi-platform][TRACE] Installing" + # - name: Packaging + # run: echo "[cmake-multi-platform][TRACE] Packaging" + + GitHub-Hosted-Matrix-Job: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: + - windows-latest + - ubuntu-latest + compiler: + - llvm-15 + - gcc-11 + - msvc + generator: + - "Ninja" + build_type: + - Release + - Debug + exclude: + - os: ubuntu-latest + compiler: msvc + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Cache + uses: actions/cache@v3 + with: + path: | + ~/vcpkg + ./build/vcpkg_installed + ${{ env.HOME }}/.cache/vcpkg/archives + ${{ env.XDG_CACHE_HOME }}/vcpkg/archives + ${{ env.LOCALAPPDATA }}\vcpkg\archives + ${{ env.APPDATA }}\vcpkg\archives + key: ${{ runner.os }}-${{ matrix.compiler }}-${{ env.BUILD_TYPE }}-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('./vcpkg.json')}} + restore-keys: | + ${{ runner.os }}-${{ env.BUILD_TYPE }}- + - name: Setup LCOV + uses: hrishikesh-kadam/setup-lcov@v1 + - name: Setup Cpp + uses: aminya/setup-cpp@v1 + with: + # compiler and analyzers (llvm, gcc, msvc, vcvarsall, cppcheck, clangtidy, clangformat) + compiler: ${{ matrix.compiler }} + vcvarsall: ${{ contains(matrix.os, 'windows') }} + cppcheck: true + clangtidy: 15 + clangformat: 15 + # build system (cmake, ninja, meson, make, task, bazel) + cmake: true + ninja: true + # package manager (vcpkg, conan, nala) + vcpkg: true + # cache (ccache, sccache) + ccache: true + # documentation (doxygen, graphviz) + # coverage (gcovr, opencppcoverage, kcov) + # other (python, powershell, sevenzip) + + - name: Configure CMake + run: | + cmake -S . -B ./build -DCMAKE_BUILD_TYPE:STRING=${{matrix.build_type}} -DCMAKE_TOOLCHAIN_FILE:STRING=./tools/BuildTools/vcpkg/scripts/buildsystems/vcpkg.cmake + + - name: Build + run: | + cmake --build ./build --config ${{matrix.build_type}} + + - name: Test + run: | + ctest --test-dir ./build --config ${{matrix.build_type}} diff --git a/.gitignore b/.gitignore index 0521543..813dcf9 100644 --- a/.gitignore +++ b/.gitignore @@ -699,4 +699,26 @@ xcuserdata/ /*.gcno **/xcshareddata/WorkspaceSettings.xcsettings +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + # End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,cmake,c++,vcpkg,vs,xcode,git,python,batch,powershell + +# Created by pedroo-seaiaa +!configure.bat diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c85fa21 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tools/BuildTools/vcpkg"] + path = tools/BuildTools/vcpkg + url = git@github.com:microsoft/vcpkg.git diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6a7fcd4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "xstring": "cpp" + }, + "C_Cpp.codeAnalysis.clangTidy.config": "${workspaceFolder}/.clang-tidy" +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..3cdec20 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,449 @@ +# ############################################################################ +# Copyright (C) 2023 Pedro Oliveira. All Rights Reserved. +# ############################################################################ + +# ############################################################################ +# Configuring +# ############################################################################ + +# ============================================================================ +# CMake General Settings +# ============================================================================ + +cmake_minimum_required(VERSION 3.21) + +set(COMPANY_NAME "MyCompany") +project( + MyProject + VERSION 1.0.0.0 + LANGUAGES CXX +) + +# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +# CMake Include Modules +# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +include(tools/BuildTools/cmake/Modules/BuildToolsMain.cmake) + +# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +# CMake Cache Variables & Options +# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +cmake_host_system_information(RESULT OS_NAME_RESULT QUERY OS_NAME) + +# short alias for top-level root folder +set(ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}) + +# VRVIU is not using the latest standard +set(CMAKE_CXX_STANDARD 20) + +# prevent std=gnu++20 or similar +set(CMAKE_CXX_EXTENSIONS OFF) + +# Surpress CTest Targets (Continuous, Experimental, Nightly) +set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) + +# Surpresses MSVC Target ZERO_CHECK +set(CMAKE_SUPPRESS_REGENERATION ON) + +set(CMAKE_VERBOSE_MAKEFILE OFF) + +set(CMAKE_DEBUG_POSTFIX d) + +# Check to see if this project is the root/top-level project +if(CMAKE_VERSION VERSION_LESS 3.21) + # This variable is set by project() in CMake 3.21+ + string( + COMPARE EQUAL + "${CMAKE_SOURCE_DIR}" + "${PROJECT_SOURCE_DIR}" + PROJECT_IS_TOP_LEVEL + ) +endif() + +# ############################################################################ +# Building +# ############################################################################ + +# ============================================================================ +# CMake Configured Headers +# ============================================================================ + +# configure project metadata header file based on CMake configuration options +include(tools/BuildTools/cmake/Templates/IncludeMeta.cmake) +configure_meta_header_file() + +# ============================================================================ +# global settings that propagates settings to linking targets +# ============================================================================ + +add_library(myproject_global_settings INTERFACE) +set_project_warnings(myproject_global_settings) + +# Include What You Use (from Windows) +if(OS_NAME_RESULT STREQUAL "Windows") + + target_compile_definitions( + myproject_global_settings # + INTERFACE WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0601 NOMINMAX + ) + + # target_link_libraries(myproject_global_settings INTERFACE ws2_32) +endif() + +# ============================================================================ +# MyProjectPCH: My Project pre-compiled headers +# NOTE(PO): clang-tidy fails to build cmake precompiled_headers if not CLANG +# so we create our own PCH library and link to it +# ============================================================================ + +option(USE_STANDARD_LIBRARY_PCH "Use Standard Library Precompile Headers" ON) +if(USE_STANDARD_LIBRARY_PCH) + message("[CORE][INFO] Including StandardLibraryPCH OBJECT library") + add_library(StandardLibraryPCH OBJECT) + target_sources(StandardLibraryPCH PRIVATE "src/ProjectPCH/StandardLibrary.cpp") + target_link_libraries(StandardLibraryPCH PUBLIC myproject_global_settings) + + if(MSVC) + target_compile_options(StandardLibraryPCH INTERFACE "/Zc:__cplusplus") + else() + cmake_host_system_information(RESULT OS_NAME_RESULT QUERY OS_NAME) + if(OS_NAME_RESULT STREQUAL "Ubuntu") + target_compile_options(StandardLibraryPCH INTERFACE "-fcoroutines") + elseif(OS_NAME_RESULT STREQUAL "Darwin") + target_compile_options(StandardLibraryPCH INTERFACE "-stdlib=libstdc++") + endif() + endif() +else() + add_library(StandardLibraryPCH INTERFACE) +endif() +add_dependencies(StandardLibraryPCH myproject_global_settings) + +# ============================================================================ +# MyProjectLib: My Project main library +# ============================================================================ + +add_library(myprojectlib) +add_dependencies(myprojectlib myproject_global_settings StandardLibraryPCH) +file(GLOB_RECURSE myprojectlib_SOURCE_FILES "src/ProjectLib/*.cpp") +file(GLOB_RECURSE myprojectlib_HEADER_FILES "src/ProjectLib/*.hpp") +file(GLOB_RECURSE myprojectlib_PUBLIC_HEADER_FILES "include/*.hpp") + +target_sources(myprojectlib PRIVATE ${myprojectlib_SOURCE_FILES} ${myprojectlib_HEADER_FILES} ${myprojectlib_PUBLIC_HEADER_FILES}) + +target_include_directories( + myprojectlib + PRIVATE ${ROOT_DIR}/src/ProjectPCH + PRIVATE ${ROOT_DIR}/src/ProjectLib + PUBLIC ${ROOT_DIR}/include +) + +find_package(spdlog CONFIG REQUIRED) +target_link_libraries(myprojectlib PUBLIC myproject_global_settings StandardLibraryPCH spdlog::spdlog) + +# set_target_precompiled_headers(myprojectlib PRIVATE) + +# ============================================================================ +# MyProjectApp: My Project main application +# ============================================================================ + +add_executable(myprojectapp) +add_dependencies(myprojectapp myprojectlib StandardLibraryPCH) + +file(GLOB_RECURSE myprojectapp_SOURCE_FILES "src/ProjectApp/*.cpp") +file(GLOB_RECURSE myprojectapp_HEADER_FILES "src/ProjectApp/*.hpp") + +target_sources(myprojectapp PRIVATE ${myprojectapp_SOURCE_FILES} ${myprojectapp_HEADER_FILES}) +target_include_directories(myprojectapp PRIVATE ${ROOT_DIR}/src/ProjectApp ${ROOT_DIR}/src/ProjectPCH) +target_link_libraries(myprojectapp PRIVATE StandardLibraryPCH myprojectlib) + +# set_target_precompiled_headers(myprojectapp PRIVATE) + +# ############################################################################ +# Testing +# ############################################################################ + +enable_testing() +include(CTest) + +# ============================================================================ +# Main Tests +# ============================================================================ + +option(MYPROJECT_BUILD_MAIN_TESTS "Build main test targets" ${PROJECT_IS_TOP_LEVEL}) +if(MYPROJECT_BUILD_MAIN_TESTS) + + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + # Dependencies + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + find_package(Catch2 CONFIG REQUIRED) + include(Catch) + + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + # TARGET: myproject_main_tests + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + add_executable(myproject_main_tests) + add_dependencies(myproject_main_tests StandardLibraryPCH myprojectlib) + + file(GLOB_RECURSE myproject_main_tests_SOURCE_FILES "tests/MainTests/*.cpp") + + target_sources(myproject_main_tests PRIVATE ${myproject_main_tests_SOURCE_FILES}) + target_include_directories(myproject_main_tests PRIVATE ${ROOT_DIR}/src/ProjectPCH ${ROOT_DIR}/tests/MainTests) + target_link_libraries(myproject_main_tests PRIVATE myprojectlib StandardLibraryPCH Catch2::Catch2WithMain) + + # set_target_precompiled_headers(myproject_main_tests PRIVATE) + + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + # Utilities + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + catch_discover_tests(myproject_main_tests TEST_PREFIX "myproject_main_test_") + +endif(MYPROJECT_BUILD_MAIN_TESTS) + +# ============================================================================ +# Unit Tests +# ============================================================================ + +option(MYPROJECT_BUILD_UNIT_TESTS "Build unit test targets" ${PROJECT_IS_TOP_LEVEL}) +if(MYPROJECT_BUILD_UNIT_TESTS) + + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + # Dependencies + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + set(gtest_EXTERNAL_DIR ${CMAKE_CURRENT_LIST_DIR}/external/googletest) + find_package(GTest CONFIG REQUIRED) + include(GoogleTest) + + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + # TARGET: myproject_unit_tests + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + add_executable(myproject_unit_tests) + add_dependencies(myproject_unit_tests StandardLibraryPCH myprojectlib) + + file(GLOB_RECURSE myproject_unit_tests_SOURCE_FILES "tests/UnitTests/*.cpp") + + target_sources(myproject_unit_tests PRIVATE ${myproject_unit_tests_SOURCE_FILES}) + target_include_directories(myproject_unit_tests PRIVATE ${ROOT_DIR}/src/ProjectPCH ${ROOT_DIR}/tests/UnitTests) + target_link_libraries( + myproject_unit_tests + PRIVATE myprojectlib + StandardLibraryPCH + GTest::gtest + GTest::gmock + ) + + # set_target_precompiled_headers(myproject_unit_tests PRIVATE) + + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + # Utilities + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + gtest_discover_tests(myproject_unit_tests TEST_PREFIX "myproject_unit_test_") + +endif(MYPROJECT_BUILD_UNIT_TESTS) + +# ============================================================================ +# Performance Tests +# ============================================================================ + +option(MYPROJECT_BUILD_PERF_TESTS "Build performance test target" ${PROJECT_IS_TOP_LEVEL}) +if(MYPROJECT_BUILD_PERF_TESTS AND CMAKE_BUILD_TYPE STREQUAL "Release") + + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + # Dependencies + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + find_package(benchmark CONFIG REQUIRED) + + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + # TARGET: myproject_perf_tests + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + add_executable(myproject_perf_tests) + add_dependencies(myproject_perf_tests StandardLibraryPCH myprojectlib) + + file(GLOB_RECURSE myproject_perf_tests_SOURCE_FILES "tests/PerfTests/*.cpp") + + target_sources(myproject_perf_tests PRIVATE ${myproject_perf_tests_SOURCE_FILES}) + target_include_directories(myproject_perf_tests PRIVATE ${ROOT_DIR}/tests/PerfTests) + target_link_libraries(myproject_perf_tests PRIVATE myprojectlib benchmark::benchmark) + + # set_target_precompiled_headers(myproject_perf_tests PRIVATE) + + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + # Utilities + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + # Allow short runs during automated testing to see if something new breaks + # set(PERF_RUNTIME 2 CACHE STRING "Number of seconds to run fuzz tests during ctest run" ) # Default of 2 seconds + # add_test(NAME perf_tester_run COMMAND myproject_perf_tests -max_total_time=${PERF_RUNTIME} ) +endif() + +# ============================================================================ +# Fuzzing Tests +# ============================================================================ + +option(MYPROJECT_BUILD_FUZZ_TESTS "Build fuzz test target" ${PROJECT_IS_TOP_LEVEL}) +if(MYPROJECT_BUILD_FUZZ_TESTS) + if(NOT (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR WIN32) + log_warn("Fuzz tests require Clang") + add_custom_target(myproject_fuzz_tests COMMAND echo -e "[ERROR] Fuzz tests require Clang") + else() + + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + # Dependencies + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + set(fmt_EXTERNAL_DIR ${CMAKE_CURRENT_LIST_DIR}/external/fmt) + find_package(fmt CONFIG REQUIRED) + + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + # TARGET: myproject_fuzz_tests + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + add_executable(myproject_fuzz_tests) + add_dependencies(myproject_fuzz_tests StandardLibraryPCH myprojectlib) + + file(GLOB_RECURSE myproject_fuzz_tests_SOURCE_FILES "tests/FuzzTests/*.cpp") + + target_sources(myproject_fuzz_tests PRIVATE ${myproject_fuzz_tests_SOURCE_FILES}) + target_include_directories(myproject_fuzz_tests PRIVATE ${ROOT_DIR}/tests/FuzzTests) + target_link_libraries( + myproject_fuzz_tests + PRIVATE myproject_global_settings + fmt::fmt + -coverage + -fsanitize=fuzzer + ) + target_compile_options(myproject_fuzz_tests PRIVATE -fsanitize=fuzzer) + target_compile_features(myproject_fuzz_tests PRIVATE cxx_std_20) + + # set_target_precompiled_headers(myproject_fuzz_tests PRIVATE) + + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + # Utilities + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + # Allow short runs during automated testing to see if something new breaks + # set(FUZZ_RUNTIME + # 10 + # CACHE STRING "Number of seconds to run fuzz tests during ctest run" ) # Default of 10 seconds + + # add_test(NAME fuzz_tester_run COMMAND myproject_fuzz_tests -max_total_time=${FUZZ_RUNTIME}) + + endif() +endif() + +# ============================================================================ +# Coverage Tests +# ============================================================================ + +option(MYPROJECT_BUILD_GCOV_TESTS "Build coverage test target" ${PROJECT_IS_TOP_LEVEL} # Problems with LLVM: + # undefined reference to llvm_* functions +) +if(MYPROJECT_BUILD_GCOV_TESTS + AND CMAKE_BUILD_TYPE STREQUAL "Debug" + AND NOT MSVC + AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU" +) + + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + # TARGET: myproject_covr_tests + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + add_executable(myproject_covr_tests) + add_dependencies(myproject_covr_tests StandardLibraryPCH myprojectlib) + + file(GLOB_RECURSE myproject_covr_tests_SOURCE_FILES "tests/UnitTests/*.cpp") + + target_sources( + myproject_covr_tests + PRIVATE ${myproject_SOURCE_FILES} + ${myproject_HEADER_FILES} + ${myproject_PUBLIC_HEADER_FILES} + ${myproject_covr_tests_SOURCE_FILES} + ) + target_include_directories(myproject_covr_tests PRIVATE ${ROOT_DIR}/tests/UnitTests) + target_link_libraries( + myproject_covr_tests + PRIVATE myprojectlib + GTest::gtest + GTest::gmock + gcov + ) + target_include_directories(myproject_covr_tests PRIVATE ${ROOT_DIR}/lib) + + # NOTE(PO): we need to remove clang-tidy on GNU gcc Debug, + # because, if we don't, we get the following error: + # error: unknown argument: '-fprofile-abs-path' [clang-diagnostic-error] + set_target_properties(myproject_covr_tests PROPERTIES CXX_CLANG_TIDY "") + + # set_target_precompiled_headers(myproject_covr_tests PRIVATE) + + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + # Utilities from CodeCoverage.cmake + # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + # 1. Append necessary compiler flags for all supported source files + append_coverage_compiler_flags_to_target(myproject_covr_tests) + + # 2. create a custom make target which runs your test executable and produces a code coverage report + setup_target_for_coverage_lcov( + NAME myproject_covr_tests_coverage + EXECUTABLE myproject_covr_tests + DEPENDENCIES myproject_covr_tests + BASE_DIRECTORY "${ROOT_DIR}/tests/CovrTests" + NO_DEMANGLE + ) + + add_custom_command( + TARGET myproject_covr_tests_coverage + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${ROOT_DIR}/logs/myproject_covr_tests_coverage + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/myproject_covr_tests_coverage ${ROOT_DIR}/logs/myproject_covr_tests_coverage + COMMENT "Copying report to ${ROOT_DIR}/logs/myproject_covr_tests_coverage" + ) +endif() + +# ============================================================================ +# Custom Targets +# ============================================================================ + +add_custom_target(BUILD_ALL_TESTS) +add_dependencies( + BUILD_ALL_TESTS + StandardLibraryPCH + myproject_unit_tests + myproject_main_tests +) + +add_custom_target( + RUN_ALL_TESTS + COMMAND myprojectapp + COMMAND ${CMAKE_COMMAND} -E echo "Running Unit Tests" + COMMAND ${CMAKE_COMMAND} -E echo "" + COMMAND myproject_unit_tests + COMMAND ${CMAKE_COMMAND} -E echo "" + COMMAND ${CMAKE_COMMAND} -E echo "Running Main Tests" + COMMAND ${CMAKE_COMMAND} -E echo "" + COMMAND myproject_main_tests + COMMAND ${CMAKE_COMMAND} -E echo $<$:""> + COMMAND ${CMAKE_COMMAND} -E echo $<$:"Running Perf Tests"> + COMMAND ${CMAKE_COMMAND} -E echo $<$:""> + COMMAND $<$:myproject_perf_tests> + COMMAND ${CMAKE_COMMAND} -E echo "" + COMMAND ${CMAKE_COMMAND} -E echo "Finished executing RUN_ALL_TARGETS" +) + +# ############################################################################ +# Installing +# ############################################################################ + +# ############################################################################ +# Packaging +# ############################################################################ diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..5291c44 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,402 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 21, + "patch": 0 + }, + "configurePresets": [ + { + "name": "conf-common", + "description": "General settings that apply to all configurations", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/tools/BuildTools/vcpkg/scripts/buildsystems/vcpkg.cmake", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "conf-windows-common", + "description": "Windows settings for MSBuild toolchain that apply to msvc and clang", + "hidden": true, + "inherits": "conf-common", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "architecture": { + "value": "x64", + "strategy": "external" + }, + "toolset": { + "value": "host=x64", + "strategy": "external" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "hostOS": [ + "Windows" + ] + } + } + }, + { + "name": "conf-windows-msvc-common", + "description": "Windows settings for MSBuild toolchain that apply to msvc", + "hidden": true, + "inherits": "conf-windows-common", + "cacheVariables": { + "CMAKE_CXX_COMPILER": "cl" + } + }, + { + "name": "windows-msvc-debug", + "displayName": "msvc Debug", + "description": "Target Windows with the msvc compiler, debug build type", + "inherits": "conf-windows-msvc-common", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "windows-msvc-dev", + "displayName": "msvc Dev", + "description": "Target Windows with the msvc compiler, development build type", + "inherits": "conf-windows-msvc-common", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "windows-msvc-release", + "displayName": "msvc Release", + "description": "Target Windows with the msvc compiler, release build type", + "inherits": "conf-windows-msvc-common", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "conf-windows-clang-common", + "description": "Windows settings for MSBuild toolchain that apply to clang", + "hidden": true, + "inherits": "conf-windows-common", + "cacheVariables": { + "CMAKE_CXX_COMPILER": "clang++" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "intelliSenseMode": "windows-clang-x64" + } + } + }, + { + "name": "windows-clang-debug", + "displayName": "clang Debug", + "description": "Target Windows with the clang compiler, debug build type", + "inherits": "conf-windows-clang-common", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "windows-clang-dev", + "displayName": "clang Dev", + "description": "Target Windows with the clang compiler, development build type", + "inherits": "conf-windows-clang-common", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "windows-clang-release", + "displayName": "clang Release", + "description": "Target Windows with the clang compiler, release build type", + "inherits": "conf-windows-clang-common", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "conf-linux-common", + "description": "Linux settings for gcc and clang toolchains", + "hidden": true, + "inherits": "conf-common", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + }, + "vendor": { + "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { + "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" + } + } + }, + { + "name": "conf-linux-gcc-common", + "description": "Linux settings for gcc toolchains", + "hidden": true, + "inherits": "conf-linux-common", + "cacheVariables": { + "CMAKE_CXX_COMPILER": "g++" + } + }, + { + "name": "linux-gcc-debug", + "displayName": "gcc Debug", + "description": "Target Linux with the gcc compiler, debug build type", + "inherits": "conf-linux-gcc-common", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "linux-gcc-dev", + "displayName": "gcc Dev", + "description": "Target Linux with the gcc compiler, dev build type", + "inherits": "conf-linux-gcc-common", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "linux-gcc-release", + "displayName": "gcc Release", + "description": "Target Linux with the gcc compiler, release build type", + "inherits": "conf-linux-gcc-common", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "conf-linux-clang-common", + "description": "Linux settings for clang toolchains", + "hidden": true, + "inherits": "conf-linux-common", + "cacheVariables": { + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "linux-clang-debug", + "displayName": "clang Debug", + "description": "Target Linux with the clang compiler, debug build type", + "inherits": "conf-linux-clang-common", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "linux-clang-dev", + "displayName": "clang Dev", + "description": "Target Linux with the clang compiler, dev build type", + "inherits": "conf-linux-clang-common", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "linux-clang-release", + "displayName": "clang Release", + "description": "Target Linux with the clang compiler, release build type", + "inherits": "conf-linux-clang-common", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + } + ], + "buildPresets": [ + { + "name": "build-common", + "description": "Test CMake settings that apply to all configurations", + "hidden": true + }, + { + "name": "build-windows-msvc-debug", + "displayName": "build-windows-msvc-debug", + "description": "Enable output and stop on failure", + "inherits": "build-common", + "configurePreset": "windows-msvc-debug" + }, + { + "name": "build-windows-msvc-dev", + "displayName": "build-windows-msvc-dev", + "description": "Enable output and stop on failure", + "inherits": "build-common", + "configurePreset": "windows-msvc-dev" + }, + { + "name": "build-windows-msvc-release", + "displayName": "build-windows-msvc-release", + "description": "Enable output and stop on failure", + "inherits": "build-common", + "configurePreset": "windows-msvc-release" + }, + { + "name": "build-windows-clang-debug", + "displayName": "build-windows-clang-debug", + "description": "Enable output and stop on failure", + "inherits": "build-common", + "configurePreset": "windows-clang-debug" + }, + { + "name": "build-windows-clang-dev", + "displayName": "build-windows-clang-dev", + "description": "Enable output and stop on failure", + "inherits": "build-common", + "configurePreset": "windows-clang-dev" + }, + { + "name": "build-windows-clang-release", + "displayName": "build-windows-clang-release", + "description": "Enable output and stop on failure", + "inherits": "build-common", + "configurePreset": "windows-clang-release" + }, + { + "name": "build-linux-gcc-debug", + "displayName": "build-linux-gcc-debug", + "description": "Enable output and stop on failure", + "inherits": "build-common", + "configurePreset": "linux-gcc-debug" + }, + { + "name": "build-linux-gcc-dev", + "displayName": "build-linux-gcc-dev", + "description": "Enable output and stop on failure", + "inherits": "build-common", + "configurePreset": "linux-gcc-dev" + }, + { + "name": "build-linux-gcc-release", + "displayName": "build-linux-gcc-release", + "description": "Enable output and stop on failure", + "inherits": "build-common", + "configurePreset": "linux-gcc-release" + }, + { + "name": "build-linux-clang-debug", + "displayName": "build-linux-clang-debug", + "description": "Enable output and stop on failure", + "inherits": "build-common", + "configurePreset": "linux-clang-debug" + }, + { + "name": "build-linux-clang-dev", + "displayName": "build-linux-clang-dev", + "description": "Enable output and stop on failure", + "inherits": "build-common", + "configurePreset": "linux-clang-dev" + }, + { + "name": "build-linux-clang-release", + "displayName": "build-linux-clang-release", + "description": "Enable output and stop on failure", + "inherits": "build-common", + "configurePreset": "linux-clang-release" + } + ], + "testPresets": [ + { + "name": "test-common", + "description": "Test CMake settings that apply to all configurations", + "hidden": true, + "output": { + "outputOnFailure": true + }, + "execution": { + "noTestsAction": "error", + "stopOnFailure": true + } + }, + { + "name": "test-windows-msvc-debug", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "windows-msvc-debug" + }, + { + "name": "test-windows-msvc-dev", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "windows-msvc-dev" + }, + { + "name": "test-windows-msvc-release", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "windows-msvc-release" + }, + { + "name": "test-windows-clang-debug", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "windows-clang-debug" + }, + { + "name": "test-windows-clang-dev", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "windows-clang-dev" + }, + { + "name": "test-windows-clang-release", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "windows-clang-release" + }, + { + "name": "test-linux-gcc-debug", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "linux-gcc-debug" + }, + { + "name": "test-linux-gcc-dev", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "linux-gcc-dev" + }, + { + "name": "test-linux-gcc-release", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "linux-gcc-release" + }, + { + "name": "test-linux-clang-debug", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "linux-clang-debug" + }, + { + "name": "test-linux-clang-dev", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "linux-clang-dev" + }, + { + "name": "test-linux-clang-release", + "displayName": "Strict", + "description": "Enable output and stop on failure", + "inherits": "test-common", + "configurePreset": "linux-clang-release" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index b040b8e..e5f8dfb 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,55 @@ # cpp-project-template A C++ Project Template + + + +## Description + +This is a simple project template, which main language is C++. Its main purpose is to demonstrate clean code and clean architecture. + +## Getting Started + +This project serves as a template so it will probably be missing the software requirements you need for your particular experiment/project. + +However, the sample code provided contains a list of software that is required to either build, test or lint the project. Unlike other README files, I decided to keep this information on a script file that will detect and prompt to install said requirements, which I think it is a much better approach to documenting system or software requirements. +Please refer to those documents. + + +### Windows + +Run the `configure.bat` file. + +### Linux/MacOS + +Run the `configure.sh` file. + +## Instalation + +This project is configured to install the "final" product on specific paths (which you can change). For more information, read the INSTALL files in the `docs\` folder. + +## Contributing + +You can contribute to this repository (the C++ Template Project) by following these steps: +1. Create a new issue on this repository's GitHub page () + +2. Create a branch from the new issue page, and in doing so, create a new branch. Please, make sure to branch from `dev`. The `main` and `release-*` branches will be protected and contain special workflows. +3. Make your contribution. If it is code, please, make sure it runs all tests (at least on your environment, e.g. Ubuntu 22.04 LTS). + +## Contributors + + + +## License + +This project is provided with a MIT License. +For more information read [this](LICENSE). + +## References + +The following URLs were used to create this README: +- [How to write a good README for your project - DEV Community](https://dev.to/kwing25/how-to-write-a-good-readme-for-your-project-1l10) +- [How to write a perfect README for your GiHub project - DEV Community](https://dev.to/mfts/how-to-write-a-perfect-readme-for-your-github-project-59f2) +- [A good example of a README file](https://gist.github.com/Lemmah/0c5ef5ce4586b0eeff1b2d1e90535927) +- [Make a README](https://www.makeareadme.com/) +- [How to Write a Good README File for Your GitHub Project](https://www.freecodecamp.org/news/how-to-write-a-good-readme-file/) + diff --git a/configure.bat b/configure.bat new file mode 100644 index 0000000..4babcba --- /dev/null +++ b/configure.bat @@ -0,0 +1,78 @@ +@echo off + +set ROOT_DIR=%~dp0 +set GIT_HOOKS_PATH=%ROOT_DIR%tools\GitTools\Hooks +set PYTHON_SETUP_SCRIPT_PATH=%ROOT_DIR%tools\BuildTools\setup.py + +call:main + +echo.&pause&goto:eof + +:: ########################################################################### +:: Main Entry Point +:: ########################################################################### + +:main +call:log " === Windows Configuration Batch File (version 0.1.0) ===" +call:setup-git-hooks +call:run-python-setup +goto:eof + + +:: ########################################################################### +:: "Functions" +:: ########################################################################### + +:setup-git-hooks +call:log "Configuring Git Hooks ..." +IF EXIST %GIT_HOOKS_PATH% ( + call:log "Installing hooks ..." + git config core.hooksPath %GIT_HOOKS_PATH% + call:log "Installing hooks --- SUCCESS" + call:log "Configuring Git Hooks --- SUCCESS" +) else ( + call:log "Installing hooks --- FAILED" + call:log "Configuring Git Hooks --- FAILED" +) +goto:eof + +:run-python-setup +call:log "Testing if python is installed or in PATH ..." +where python >nul 2>nul +if %errorlevel% neq 0 ( + call:log "Testing if python is installed or in PATH --- FAILED" +) else ( + call:log "Testing if python is installed or in PATH --- SUCCESS" + call:log "Checking python for version 3.12.0 ..." + python --version > temp.txt 2>&1 + findstr /C:"3.12.0" temp.txt >nul 2>nul + if %errorlevel% neq 0 ( + call:log "Checking python for version 3.12.0 --- FAILED" + call:log "This project was tested with Python 3.12.0. Other versions may not work as expected." + ) else ( + call:log "Checking python for version 3.12.0 --- SUCCESS" + ) + + del temp.txt + + call:log "Detecting setup.py ..." + IF EXIST %PYTHON_SETUP_SCRIPT_PATH% ( + call:log "Detecting setup.py --- SUCCESS" + call:log "Running setup.py" + python %PYTHON_SETUP_SCRIPT_PATH% + ) else ( + call:log "Detecting setup.py --- FAILED" + call:log "Aborting ..." + ) +) +goto:eof + +:: =========================================================================== +:: Utility Functions +:: =========================================================================== + +:log +:: if verbose is passed +::echo [configure.bat]%~1 +echo %~1 +goto:eof diff --git a/configure.sh b/configure.sh new file mode 100755 index 0000000..42a6c38 --- /dev/null +++ b/configure.sh @@ -0,0 +1,137 @@ +#!/bin/bash + +# ############################################################################ +# Entrypoint +# ############################################################################ + +# Get +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# +LOGGER_PATH=$ROOT_DIR/tools/GitTools/Utils/logger.sh + +# +GIT_HOOKS_PATH=$ROOT_DIR/tools/GitTools/Hooks + +# +PYTHON_SETUP_SCRIPT_PATH=$ROOT_DIR/tools/BuildTools/scripts/setup.py + +# Include/Import logger +source $LOGGER_PATH + +# TODO(PO): remove this +set_log_level $LOG_LEVEL_TRACE + +# Parse command line arguments +# TODO: add --install-missing or something like that + +verbose=0 # Default verbosity level +install_hooks=true + +while getopts ":vhn" opt; do + case $opt in + v) + ((verbose++)) + set_log_level LOG_LEVEL_TRACE + ;; + h) + echo "Usage: $0 [-v (verbose)] [-h (help)] [-n (no-install-hooks)]" + exit 0 + ;; + n) + install_hooks=false + ;; + \?) + echo "Invalid option: -$OPTARG" + exit 1 + ;; + :) + echo "Option -$OPTARG requires an argument." + exit 1 + ;; + esac +done + +# VERBOSE=0 +# while getopts 'v' flag; do +# case "${flag}" in +# v) +# VERBOSE=1 +# LOG_VERBOSITY=1 +# set_log_level LOG_LEVEL_TRACE +# ;; +# ?) +# echo "script usage: $(basename \$0) [-v]" >&2 +# exit 1 +# ;; +# esac +# done + +# ############################################################################ +# Helper Functions +# - setup_git_hooks +# - run_python_setup +# ############################################################################ + +function setup_git_hooks() { + log_trace "setup_git_hooks" + + if [ $install_hooks = true ]; then + read -p "Do you want to configure the git hooks? (yes/no): " UserInput + if [ "$UserInput" = "yes" ]; then + log_warn "Configuring Git Hooks ..." + if [ -d "$GIT_HOOKS_PATH" ]; then + log_warn "Installing hooks ..." + git config core.hooksPath "$GIT_HOOKS_PATH" + log_info "Installing hooks --- SUCCESS" + log_info "Configuring Git Hooks --- SUCCESS" + else + log_warn "Installing hooks --- FAILED" + log_warn "Configuring Git Hooks --- FAILED" + fi + else + log_info "Skipping git hooks configuration." + fi + fi +} + +DEFAULT_PYTHON_VERSION="3.10.12" +function run_python_setup() { + log_warn "Testing if python is installed or in PATH ..." + if command -v python >/dev/null 2>&1; then + log_info "Testing if python is installed or in PATH --- SUCCESS" + log_warn "Checking python for version ${DEFAULT_PYTHON_VERSION} ..." + if python --version | grep -q "Python ${DEFAULT_PYTHON_VERSION}"; then + log_info "Checking python for version ${DEFAULT_PYTHON_VERSION} --- SUCCESS" + else + log_error "Checking python for version ${DEFAULT_PYTHON_VERSION} --- FAILED" + log_warn "This project was tested with Python ${DEFAULT_PYTHON_VERSION}. Other versions may not work as expected." + fi + + else + log_fatal "Testing if python is installed or in PATH --- FAILED" + fi + + log_warn "Detecting setup.py ..." + if [ -e "$PYTHON_SETUP_SCRIPT_PATH" ]; then + log_warn "Detecting setup.py --- SUCCESS" + log_warn "Running setup.py" + PYTHON_VERSION=$(python --version) + log_debug "PYTHON_VERSION: $PYTHON_VERSION" + python $PYTHON_SETUP_SCRIPT_PATH + else + log_fatal "Detecting setup.py --- FAILED" + fi +} + +# ############################################################################ +# Main Function +# ############################################################################ + +main() { + log_message " === Linux Configuration Batch File (version 0.1.0) ===" + setup_git_hooks + run_python_setup +} + +main diff --git a/docs/BUILDING.md b/docs/BUILDING.md new file mode 100644 index 0000000..9cb7e8f --- /dev/null +++ b/docs/BUILDING.md @@ -0,0 +1,16 @@ +# Building + +This project comes with [CMakePresets.json](../CMakePresets.json) file that contains all cmake presets for building. +These presets contain all the options for configuration, building, testing and packaging the project. + +> Please change them as you see fit. + +The standard configurations include: +- Windows +- - MSVC +- - Clang +- Linux +- - GNU +- - Clang + +By default, the project uses `ninja` as the build system generator. diff --git a/include/Project/ProjectLib.hpp b/include/Project/ProjectLib.hpp new file mode 100644 index 0000000..6f20339 --- /dev/null +++ b/include/Project/ProjectLib.hpp @@ -0,0 +1,19 @@ +/// ########################################################################## +/// @copyright Copyright (c) 2023, AI & ARTS Alchemy. All rights reserved. +/// ########################################################################## +#ifndef PROJECT_LIB_H +#define PROJECT_LIB_H + +#pragma once +#include +#include + +namespace project::lib { + /// @brief + /// @param args + /// @return 0 if function behaves correctly + [[nodiscard]] int better_main(std::span args) noexcept; + +} // namespace project::lib + +#endif // PROJECT_LIB_H diff --git a/src/ProjectApp/ProjectApp.cpp b/src/ProjectApp/ProjectApp.cpp new file mode 100644 index 0000000..758baa9 --- /dev/null +++ b/src/ProjectApp/ProjectApp.cpp @@ -0,0 +1,23 @@ +/// ########################################################################## +/// @copyright Copyright (c) 2023, AI & ARTS Alchemy. All rights reserved. +/// ########################################################################## +#include "StandardLibrary.hpp" + +#include "Project/ProjectLib.hpp" + +int main(const int argc, char const* const* const argv) { + +#ifdef NO_HEAP_ALLOCATIONS + std::array args; + + std::size_t arg_count = std::min(args.size(), static_cast(argc)); + + for (std::size_t arg = 0; arg < arg_count; ++arg) { + args[arg] = std::string_view(*std::next(argv, static_cast(arg))); + } +#else + std::vector args(argv, std::next(argv, static_cast(argc))); +#endif + + return project::lib::better_main(args); +} diff --git a/src/ProjectLib/ProjectLib.cpp b/src/ProjectLib/ProjectLib.cpp new file mode 100644 index 0000000..01f6a38 --- /dev/null +++ b/src/ProjectLib/ProjectLib.cpp @@ -0,0 +1,18 @@ +/// ########################################################################## +/// @copyright Copyright (c) 2023, AI & ARTS Alchemy. All rights reserved. +/// ########################################################################## +#include "StandardLibrary.hpp" + +#include "Project/ProjectLib.hpp" + +namespace project::lib { + int better_main(std::span args) noexcept { + for (const auto& arg : args) { + std::cout << arg << '\n'; + } + std::cout << '\n'; + + return 0; + } + +} // namespace project::lib diff --git a/src/ProjectPCH/StandardLibrary.cpp b/src/ProjectPCH/StandardLibrary.cpp new file mode 100644 index 0000000..b998923 --- /dev/null +++ b/src/ProjectPCH/StandardLibrary.cpp @@ -0,0 +1 @@ +#include "StandardLibrary.hpp" \ No newline at end of file diff --git a/src/ProjectPCH/StandardLibrary.hpp b/src/ProjectPCH/StandardLibrary.hpp new file mode 100644 index 0000000..dd65389 --- /dev/null +++ b/src/ProjectPCH/StandardLibrary.hpp @@ -0,0 +1,380 @@ +#pragma once + +/// ########################################################################### +/// @section C++ 98/03 +/// ########################################################################### + +/// =========================================================================== +/// @subsection Utilities library +/// =========================================================================== + +#include // std::bitset class template +#include // Functions and macro constants for signal management +#include // Handling of variable length argument lists +#include // Function objects, Function invocations, Bind operations and Reference wrappers +#include // Runtime type information utilities +#include // Various utility components + +/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +/// @subsubsection Dynamic memory management +/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +#include // High-level memory management utilities +#include // Low-level memory management utilities + +/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +/// @subsubsection Numeric limits +/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +#include // Limits of integral types +#include // Uniform way to query properties of arithmetic types + +/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +/// @subsubsection Error handling +/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +#include // Conditionally compiled macro that compares its argument to zero +#include // Macro containing the last error number +#include // Exception handling utilities +#include // Standard exception objects + +/// =========================================================================== +/// @subsection Strings library +/// =========================================================================== + +#include // Various narrow character string handling functions +#include // std::basic_string class template + +/// =========================================================================== +/// @subsection Containers library +/// =========================================================================== + +#include // std::deque container +#include // std::list container +#include // std::map and std::multimap associative containers +#include // std::queue and std::priority_queue container adaptors +#include // std::set and std::multiset associative containers +#include // std:: stack container adaptor +#include // std::vector container + +/// =========================================================================== +/// @subsection Iterators library +/// =========================================================================== + +#include // Range iterators + +/// =========================================================================== +/// @subsection Algorithms library +/// =========================================================================== + +#include // Algorithms that operate on ranges + +/// =========================================================================== +/// @subsection Numerics library +/// =========================================================================== + +#include // Common mathematics functions +#include // Complex number type +#include // Numeric operations on values in ranges +#include // Class for representing and manipulating arrays of values + +/// =========================================================================== +/// @subsection Localization library +/// =========================================================================== + +#include // C localization utilities +#include // Localization utilities + +/// =========================================================================== +/// @subsection Input/Output library +/// =========================================================================== + +#include // C-style input-output functions +#include // std::basic_fstream, et. al class templates and typedefs +#include // Helper functions to contro the format of input and output +#include // Several standard stream objects (i.e. ...) +#include // std::basic_stringstream, et. al class templates and typedefs + +/// ########################################################################### +/// @section C++ 11 +/// ########################################################################### + +#if defined(__cplusplus) || defined(_MSVC_LANG) + +#if __cplusplus >= 201103L || _MSVC_LANG >= 201103L + +/// =========================================================================== +/// @subsection Utilities library +/// =========================================================================== + +#include // C++ time utilities +#include // std::initializer_list class template +#include // std::tuple class template +#include // Compile-time type information + +/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +/// @subsubsection Numeric limits +/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +#include // Fixed-width integer types and limits of other types + +/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +/// @subsubsection Error handling +/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +#include // Defines std::error_code, a platform-dependent error code + +/// =========================================================================== +/// @subsection Containers library +/// =========================================================================== + +#include // std::array container +#include // std::forward_list container +#include // std::unordered_map and std::unordered_multimap unordered associative containers +#include // std::unordered_set and std::unordered_multiset unordered associative containers + +/// =========================================================================== +/// @subsection Numerics library +/// =========================================================================== + +#include // Random number generators and distributions +#include // Compile-time rational arithmetic + +/// =========================================================================== +/// @subsection Regular Expressions library +/// =========================================================================== + +#include // Classes, algorithms and iterators to support regex processing + +/// =========================================================================== +/// @subsection Atomic Operations library +/// =========================================================================== + +#include // Atomic operations library + +/// =========================================================================== +/// @subsection Thread support library +/// =========================================================================== + +#include // Thread waiting conditions +#include // Primitives for asynchronous computations +#include // Mutual exclusion primitives +#include // std::thread class and supporting functions + +/// ########################################################################### +/// @section C++ 14 +/// ########################################################################### + +#if __cplusplus >= 201402L || _MSVC_LANG >= 201402L + +/// =========================================================================== +/// @subsection Thread support library +/// =========================================================================== + +#include // Shared mutual exclusion primitives + +/// ########################################################################### +/// @section C++ 17 +/// ########################################################################### + +#if __cplusplus >= 201703L || _MSVC_LANG >= 201703L + +/// =========================================================================== +/// @subsection Utilities library +/// =========================================================================== + +#include // std::any class +#include // std::optional class template +#include // std::variant class template + +/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +/// @subsubsection Dynamic memory management +/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +/// --------------------------------------------------------------------------- +/// +#if defined(__has_include) +#if __has_include() + +#include // Polymorphic allocators and memory resources + +#endif // __has_include() + +#if __has_include() + +#include // Polymorphic allocators and memory resources + +#endif // __has_include() + +#endif // defined (__has_include) +/// --------------------------------------------------------------------------- + +/// =========================================================================== +/// @subsection Strings library +/// =========================================================================== + +#include // std::to_chars and std::from_chars +#include // std::basic_string_view class template + +/// =========================================================================== +/// @subsection Algorithms library +/// =========================================================================== + +#include // Predefined execution policies for parallel versions of the algorithms + +/// =========================================================================== +/// @subsection Filesystem library +/// =========================================================================== + +#include // std::path class and supporting functions + +/// ########################################################################### +/// @section C++ 20 +/// ########################################################################### + +#if __cplusplus >= 202002L || _MSVC_LANG >= 202002L + +/// =========================================================================== +/// @subsection Concepts library +/// =========================================================================== + +/// --------------------------------------------------------------------------- +/// +#if defined(__has_include) +#if __has_include() + +#include // Fundamental library concepts + +#endif // __has_include() +#endif // defined (__has_include) +/// --------------------------------------------------------------------------- + +/// =========================================================================== +/// @subsection Coroutines library +/// =========================================================================== + +/// --------------------------------------------------------------------------- +/// +#if defined(__has_include) +#if __has_include() + +#include // Coroutine support library + +#endif // __has_include() +#endif // defined (__has_include) +/// --------------------------------------------------------------------------- + +/// =========================================================================== +/// @subsection Utilities library +/// =========================================================================== + +#include // Three-way comparison operator support + +/// --------------------------------------------------------------------------- +/// +#if defined(__has_include) +#if __has_include() + +#include // Supplies means to obtain source code location + +#endif // __has_include() +#endif // defined (__has_include) +/// --------------------------------------------------------------------------- + +#include // Supplies implementation-dependent library information + +/// =========================================================================== +/// @subsection Strings library +/// =========================================================================== + +/// --------------------------------------------------------------------------- +/// +#if defined(__has_include) +#if __has_include() + +#include // Formatting library including std::format + +#endif // __has_include() +#endif // defined (__has_include) +/// --------------------------------------------------------------------------- + +/// =========================================================================== +/// @subsection Containers library +/// =========================================================================== + +#include // std::span view + +/// =========================================================================== +/// @subsection Ranges library +/// =========================================================================== + +#include // Range access, primitives, requirements, utilities and adaptors + +/// =========================================================================== +/// @subsection Numerics library +/// =========================================================================== + +#include // Bit manipulation functions +#include // Math constants + +/// =========================================================================== +/// @subsection Thread support library +/// =========================================================================== + +/// --------------------------------------------------------------------------- +/// +#if defined(__has_include) +#if __has_include() + +#include // Barriers + +#endif // __has_include() +#endif // defined (__has_include) +/// --------------------------------------------------------------------------- + +/// --------------------------------------------------------------------------- +/// +#if defined(__has_include) +#if __has_include() + +#include // Latches + +#endif // __has_include() +#endif // defined (__has_include) +/// --------------------------------------------------------------------------- + +/// --------------------------------------------------------------------------- +/// +#if defined(__has_include) +#if __has_include() + +#include // Semaphores + +#endif // __has_include() +#endif // defined (__has_include) +/// --------------------------------------------------------------------------- + +/// --------------------------------------------------------------------------- +/// +#if defined(__has_include) +#if __has_include() + +#include // Stop tokens for std::jthread + +#endif // __has_include() + +#if __has_include() + +#include // Stop tokens for std::jthread + +#endif // __has_include() + +#endif // defined (__has_include) +/// --------------------------------------------------------------------------- + +#endif // __cplusplus >= 201103L || _MSVC_LANG >= 201103L +#endif // __cplusplus >= 201402L || _MSVC_LANG >= 201402L +#endif // __cplusplus >= 201703L || _MSVC_LANG >= 201703L +#endif // __cplusplus >= 202002L || _MSVC_LANG >= 202002L + +#endif // defined(__cplusplus) || defined(_MSVC_LANG) diff --git a/tests/FuzzTests/FuzzTestsMain.cpp b/tests/FuzzTests/FuzzTestsMain.cpp new file mode 100644 index 0000000..d0fb66e --- /dev/null +++ b/tests/FuzzTests/FuzzTestsMain.cpp @@ -0,0 +1,21 @@ +#include +#include +#include + +[[nodiscard]] auto sum_values(const uint8_t* data, size_t size) { + constexpr auto scale = 1000; + + int value = 0; + for (std::size_t offset = 0; offset < size; ++offset) { + value += + static_cast(*std::next(data, static_cast(offset))) * scale; + } + return value; +} + +// Fuzzer that attempts to invoke undefined behavior for signed integer overflow +// cppcheck-suppress unusedFunction symbolName=LLVMFuzzerTestOneInput +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + fmt::print("Value sum: {}, len{}\n", sum_values(data, size), size); + return 0; +} diff --git a/tests/MainTests/MainTests.cpp b/tests/MainTests/MainTests.cpp new file mode 100644 index 0000000..4174da5 --- /dev/null +++ b/tests/MainTests/MainTests.cpp @@ -0,0 +1,34 @@ +/// ########################################################################## +/// @copyright Copyright (c) 2023, AI & ARTS Alchemy. All rights reserved. +/// ########################################################################## +#include "StandardLibrary.hpp" + +#include "Project/ProjectLib.hpp" + +#include + +/// ========================================================================== +/// @section Tests +/// ========================================================================== + +SCENARIO("Project") { + GIVEN("The executable, 'Hello' and 'World'") { + std::vector args; + args.emplace_back("UnitTestsMain.exe"); + args.emplace_back("Hello"); + args.emplace_back("World"); + + WHEN("better_main is called with those arguments") { + std::stringstream output; + auto* cout_buff = std::cout.rdbuf(); // save pointer to std::cout buffer + std::cout.rdbuf(output.rdbuf()); // substitute internal std::cout buffer with buffer of output + + THEN("better_main should return 0 AND STDOUT should be 'UnitTestsMain.exe\nHello\nWorld\n\n'") { + CHECK(0 == project::lib::better_main(args)); + CHECK("UnitTestsMain.exe\nHello\nWorld\n\n" == output.str()); + } + + std::cout.rdbuf(cout_buff); // restore std::cout buffer + } + } +} diff --git a/tests/PerfTests/PerfTestsMain.cpp b/tests/PerfTests/PerfTestsMain.cpp new file mode 100644 index 0000000..7d8ad07 --- /dev/null +++ b/tests/PerfTests/PerfTestsMain.cpp @@ -0,0 +1,37 @@ +// Microbenchmarks for string comparison using Google benchmark +#include +#include +#include + +#include "benchmark/benchmark.h" + +using std::unique_ptr; + +bool compare_int(const char* s1, const char* s2) { + char c1, c2; + for (int i1 = 0, i2 = 0;; ++i1, ++i2) { + c1 = s1[i1]; + c2 = s2[i2]; + if (c1 != c2) + return c1 > c2; + } +} + +void BM_loop_int(benchmark::State& state) { + const auto N = static_cast(state.range(0)); + unique_ptr s(new char[2 * N]); + ::memset(s.get(), 'a', 2 * N * sizeof(char)); + s[2 * N - 1] = 0; + const char *s1 = s.get(), *s2 = s1 + N; + for (auto _ : state) { + (void)_; + benchmark::DoNotOptimize(compare_int(s1, s2)); + } + state.SetItemsProcessed(N * state.iterations()); +} + +#define ARGS ->Arg(1 << 20) + +BENCHMARK(BM_loop_int) ARGS; + +BENCHMARK_MAIN(); diff --git a/tests/SetupTests/docker/linux.Dockerfile b/tests/SetupTests/docker/linux.Dockerfile new file mode 100644 index 0000000..70b6d56 --- /dev/null +++ b/tests/SetupTests/docker/linux.Dockerfile @@ -0,0 +1,8 @@ +# Use the official Ubuntu 22.04 LTS image as a parent image +FROM ubuntu:22.04 + +# Update the system and install any packages you need +RUN apt-get update && apt-get install -y git python-is-python3 + +# Run your application when the container launches +CMD ["ls", "/antiphon"] diff --git a/tests/SetupTests/scripts/testSetup.sh b/tests/SetupTests/scripts/testSetup.sh new file mode 100644 index 0000000..db3ca22 --- /dev/null +++ b/tests/SetupTests/scripts/testSetup.sh @@ -0,0 +1,137 @@ +#!/bin/bash + +reset + +# ============================================================================ +# Set PROJECT_ROOT_DIR & REPO_NAME +# ============================================================================ + +# navigate to project root directory (this works as long as not in a submodule) +git_dir=$(git rev-parse --show-toplevel) + +# import git utilities +GIT_UTILITIES_SCRIPT_PATH=$git_dir/tools/GitTools//Utils/utilities.sh +test -f $GIT_UTILITIES_SCRIPT_PATH || (echo "File not found: $GIT_UTILITIES_SCRIPT_PATH" && exit 1) +source $GIT_UTILITIES_SCRIPT_PATH || (echo "Failed to source $GIT_UTILITIES_SCRIPT_PATH" && exit 1) + +# detect if this tools folder is a submodule or not +IS_SUBMODULE=$( + git_is_submodule + echo $? +) + +# getting the root level directory of the project +PROJECT_ROOT_DIR=$(git rev-parse --show-toplevel) +if [[ $IS_SUBMODULE == 1 ]]; then + cd "${PROJECT_ROOT_DIR}/.." + PROJECT_ROOT_DIR=$(git rev-parse --show-toplevel) +fi + +GIT_REMOTE_URL=$(git remote get-url origin) +REPO_NAME=$(basename $GIT_REMOTE_URL | sed 's/\.git$//') + +# ============================================================================ +# Include logger +# ============================================================================ + +# import logger +source ${PROJECT_ROOT_DIR}/tools/GitTools/Utils/logger.sh +# TODO(PO): remove this +set_log_level $LOG_LEVEL_TRACE + +# ============================================================================ +# Test Initial Project Setup through Docker +# ============================================================================ + +log_info " === Running Initial Project Setup (through Docker) === " + +LINUX_DOCKERFILE_PATH=$PROJECT_ROOT_DIR/tests/SetupTests/docker/ +log_debug "LINUX_DOCKERFILE_PATH $LINUX_DOCKERFILE_PATH" + +# ---------------------------------------------------------------------------- +# Detect Docker Installation +# ---------------------------------------------------------------------------- + +log_warn "Detecting docker installation ..." +if command -v docker &>/dev/null; then + log_warn "Detecting docker installation --- SUCCESS" +else + log_fatal "Detecting docker installation --- FAILURE" +fi + +# ---------------------------------------------------------------------------- +# Detect Docker Image +# ---------------------------------------------------------------------------- + +DOCKER_IMAGE_TAG="docker-image-${REPO_NAME}" +log_warn "Detecting docker image tagged with ${DOCKER_IMAGE_TAG} ..." +DOCKER_IMAGE_ID=$(docker images -q ${DOCKER_IMAGE_TAG}) +if [ "$DOCKER_IMAGE_ID" = "" ]; then + log_error "Detecting docker image tagged with ${DOCKER_IMAGE_TAG} --- FAILURE" + + log_warn "Building ${DOCKER_IMAGE_TAG} from Dockerfile ..." + docker build -f ${LINUX_DOCKERFILE_PATH}/linux.Dockerfile ${LINUX_DOCKERFILE_PATH} --tag ${DOCKER_IMAGE_TAG} + DOCKER_IMAGE_ID=$(docker images -q ${DOCKER_IMAGE_TAG}) + if [ "$DOCKER_IMAGE_ID" = "" ]; then + log_fatal "Building ${DOCKER_IMAGE_TAG} from Dockerfile --- FAILURE" + else + log_info "Building ${DOCKER_IMAGE_TAG} from Dockerfile --- SUCCESS" + fi +else + log_info "Detecting docker image tagged with ${DOCKER_IMAGE_TAG} --- SUCCESS" +fi + +# ---------------------------------------------------------------------------- +# Run Docker Image with this repository as Volume +# ---------------------------------------------------------------------------- + +DOCKER_VOLUME_NAME="docker-volume-${REPO_NAME}" +log_warn "Running project setup through ${DOCKER_IMAGE_TAG} ..." +cd ${PROJECT_ROOT_DIR}/.. + +sleep 1 + +DOCKER_ENTRYPOINT_CMD="echo '[${DOCKER_IMAGE_TAG}] hello-pedro' && cd ${DOCKER_VOLUME_NAME} && ./configure.sh -n && exit 0" + +# docker run --rm -it --mount type=bind,source=${PROJECT_ROOT_DIR},target=/${DOCKER_VOLUME_NAME} ${DOCKER_IMAGE_TAG} bash +docker run --rm -it --mount type=bind,source=${PROJECT_ROOT_DIR},target=/${DOCKER_VOLUME_NAME} ${DOCKER_IMAGE_TAG} bash -c "${DOCKER_ENTRYPOINT_CMD}" + +sleep 1 + +if [ "$?" != 0 ]; then + log_error "Running project setup through ${DOCKER_IMAGE_TAG} --- FAILURE" +else + log_info "Running project setup through ${DOCKER_IMAGE_TAG} --- SUCCESS" +fi + +# # debug +log_debug "" +log_debug " === before === " +log_debug "containers:" +docker container ls -a +log_debug "images:" +docker images + +# # clean-up + +# # debug +log_debug "" +log_debug " === after === " +log_debug "containers:" +docker container ls -a +log_debug "images:" +docker images + +# # TIPS(PO): command to remove all docker containers +# #docker ps -aq | xargs docker rm + +echo "" +echo -ne "Clearing screen in ..." +sleep 1 +echo -ne "\r" +echo -ne "\rClearing screen in 2 " +sleep 1 +echo -ne "\rClearing screen in 1 " +sleep 1 + +# reset diff --git a/tests/UnitTests/UnitTestsMain.cpp b/tests/UnitTests/UnitTestsMain.cpp new file mode 100644 index 0000000..10d40ec --- /dev/null +++ b/tests/UnitTests/UnitTestsMain.cpp @@ -0,0 +1,24 @@ +#include "Project/ProjectLib.hpp" + +#include + +TEST(MainTestSuite, better_main) { + std::vector args; + args.emplace_back("UnitTestsMain.exe"); + args.emplace_back("Hello"); + args.emplace_back("World"); + + std::stringstream output; + auto* cout_buff = std::cout.rdbuf(); // save pointer to std::cout buffer + std::cout.rdbuf(output.rdbuf()); // substitute internal std::cout buffer with buffer of output + + EXPECT_EQ(0, project::lib::better_main(args)); + EXPECT_EQ("UnitTestsMain.exe\nHello\nWorld\n\n", output.str()); + + std::cout.rdbuf(cout_buff); // restore std::cout buffer +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tools/BuildTools/cmake/Modules/BuildToolsMain.cmake b/tools/BuildTools/cmake/Modules/BuildToolsMain.cmake new file mode 100644 index 0000000..29fafb0 --- /dev/null +++ b/tools/BuildTools/cmake/Modules/BuildToolsMain.cmake @@ -0,0 +1,95 @@ +include_guard(GLOBAL) + +set(THIS_DIR ${CMAKE_CURRENT_LIST_DIR}) +list(APPEND CMAKE_MODULE_PATH ${THIS_DIR}) + +# Check to see if this project is the root/top-level project +if(CMAKE_VERSION VERSION_LESS 3.21) + # This variable is set by project() in CMake 3.21+ + string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}" PROJECT_IS_TOP_LEVEL) +endif() + +if(NOT PROJECT_IS_TOP_LEVEL) + set(CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + PARENT_SCOPE + ) +endif() + +include(Logger) + +log_trace("Including Filesystem...") +include(Filesystem) +log_trace("Including Filesystem -- DONE") + +log_trace("Including HostSystemInformation...") +include(HostSystemInformation) +log_trace("Including HostSystemInformation -- DONE") + +log_trace("Including StandardProjectSettings...") +include(StandardProjectSettings) +log_trace("Including StandardProjectSettings -- DONE") + +log_trace("Including PreventInSourceBuilds...") +include(PreventInSourceBuilds) +log_trace("Including PreventInSourceBuilds -- DONE") + +log_trace("Including CCache...") +include(CCache) +log_trace("Including CCache -- DONE") + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_BUILD_TYPE STREQUAL "Debug") + log_trace("Including CodeCoverage...") + include(CodeCoverage) + log_trace("Including CodeCoverage -- DONE") +endif() + +log_trace("Including StaticAnalyzers...") +include(StaticAnalyzers) +log_trace("Including StaticAnalyzers -- DONE") + +log_trace("Including CompilerWarnings...") +include(CompilerWarnings) +log_trace("Including CompilerWarnings -- DONE") + +log_trace("Including Sanitizers...") +include(Sanitizers) +log_trace("Including Sanitizers -- DONE") + +log_trace("Including Doxygen...") +include(Doxygen) +log_trace("Including Doxygen -- DONE") + +log_trace("Including CPM...") +set(CMAKE_MESSAGE_LOG_LEVEL ERROR) +include(CPM) +set(CMAKE_MESSAGE_LOG_LEVEL STATUS) +log_trace("Including CPM -- DONE") + +log_trace("Including FetchAndInstall...") +include(FetchAndInstall) +log_trace("Including FetchAndInstall -- DONE") + +log_trace("Including FetchGTest...") +include(FetchGTest) +log_trace("Including FetchGTest -- DONE") + +log_trace("Including FetchCatch...") +include(FetchCatch) +log_trace("Including FetchCatch -- DONE") + +log_trace("Including FetchGSL...") +include(FetchGSL) +log_trace("Including FetchGSL -- DONE") + +log_trace("Setting CMAKE_PROJECT_INCLUDE...") +set(CMAKE_PROJECT_INCLUDE ${THIS_DIR}/CMakeProjectInclude.cmake) +log_trace("Setting CMAKE_PROJECT_INCLUDE -- DONE") + +log_trace("Setting CMAKE_PROJECT_INCLUDE_BEFORE...") +set(CMAKE_PROJECT_INCLUDE_BEFORE ${THIS_DIR}/CMakeProjectIncludeBefore.cmake) +log_trace("Setting CMAKE_PROJECT_INCLUDE_BEFORE -- DONE") + +log_trace("Setting CMAKE_PROJECT_INCLUDE_BEFORE...") +include(PreCompiledHeaders) +log_trace("Setting CMAKE_PROJECT_INCLUDE_BEFORE -- DONE") diff --git a/tools/BuildTools/cmake/Modules/CCache.cmake b/tools/BuildTools/cmake/Modules/CCache.cmake new file mode 100644 index 0000000..a8e0090 --- /dev/null +++ b/tools/BuildTools/cmake/Modules/CCache.cmake @@ -0,0 +1,62 @@ +# cmake-format: off +# ============================================================================= +# Copyright (C) lefticus +# +# See Also: https://github.com/lefticus +# See Also: https://github.com/cpp-best-practices/cpp_starter_project +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to +# ============================================================================= +# cmake-format: on +include_guard(GLOBAL) + +# +option(ENABLE_CACHE "Enable cache if available" ON) +if(NOT ENABLE_CACHE) + return() +endif() + +set(CACHE_OPTION + "ccache" + CACHE STRING "Compiler cache to be used" +) +set(CACHE_OPTION_VALUES "ccache" "sccache") +set_property(CACHE CACHE_OPTION PROPERTY STRINGS ${CACHE_OPTION_VALUES}) +list(FIND CACHE_OPTION_VALUES ${CACHE_OPTION} CACHE_OPTION_INDEX) + +if(${CACHE_OPTION_INDEX} EQUAL -1) + log_info( + "Using custom compiler cache system: '${CACHE_OPTION}', explicitly supported entries are ${CACHE_OPTION_VALUES}" + ) +endif() + +find_program(CACHE_BINARY ${CACHE_OPTION}) +if(CACHE_BINARY) + log_info("${CACHE_OPTION} found and enabled") + set(CMAKE_CXX_COMPILER_LAUNCHER ${CACHE_BINARY}) +else() + log_warn("${CACHE_OPTION} is enabled but was not found. Not using it") +endif() diff --git a/tools/BuildTools/cmake/Modules/CMakeProjectInclude.cmake b/tools/BuildTools/cmake/Modules/CMakeProjectInclude.cmake new file mode 100644 index 0000000..695a509 --- /dev/null +++ b/tools/BuildTools/cmake/Modules/CMakeProjectInclude.cmake @@ -0,0 +1 @@ +log_trace("[${PROJECT_NAME}] CMakeProjectInclude") diff --git a/tools/BuildTools/cmake/Modules/CMakeProjectIncludeBefore.cmake b/tools/BuildTools/cmake/Modules/CMakeProjectIncludeBefore.cmake new file mode 100644 index 0000000..013001b --- /dev/null +++ b/tools/BuildTools/cmake/Modules/CMakeProjectIncludeBefore.cmake @@ -0,0 +1 @@ +log_trace("[${PROJECT_NAME}] CMakeProjectIncludeBefore") diff --git a/tools/BuildTools/cmake/Modules/CPM.cmake b/tools/BuildTools/cmake/Modules/CPM.cmake new file mode 100644 index 0000000..786e969 --- /dev/null +++ b/tools/BuildTools/cmake/Modules/CPM.cmake @@ -0,0 +1,1161 @@ +# CPM.cmake - CMake's missing package manager +# =========================================== +# See https://github.com/cpm-cmake/CPM.cmake for usage and update instructions. +# +# MIT License +# ----------- +#[[ + Copyright (c) 2019-2023 Lars Melchior and contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +]] + +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +# Initialize logging prefix +if(NOT CPM_INDENT) + set(CPM_INDENT + "CPM:" + CACHE INTERNAL "" + ) +endif() + +if(NOT COMMAND cpm_message) + function(cpm_message) + message(${ARGV}) + endfunction() +endif() + +set(CURRENT_CPM_VERSION 1.0.0-development-version) + +get_filename_component(CPM_CURRENT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" REALPATH) +if(CPM_DIRECTORY) + if(NOT CPM_DIRECTORY STREQUAL CPM_CURRENT_DIRECTORY) + if(CPM_VERSION VERSION_LESS CURRENT_CPM_VERSION) + message( + AUTHOR_WARNING + "${CPM_INDENT} \ +A dependency is using a more recent CPM version (${CURRENT_CPM_VERSION}) than the current project (${CPM_VERSION}). \ +It is recommended to upgrade CPM to the most recent version. \ +See https://github.com/cpm-cmake/CPM.cmake for more information." + ) + endif() + if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + include(FetchContent) + endif() + return() + endif() + + get_property( + CPM_INITIALIZED GLOBAL "" + PROPERTY CPM_INITIALIZED + SET + ) + if(CPM_INITIALIZED) + return() + endif() +endif() + +if(CURRENT_CPM_VERSION MATCHES "development-version") + message( + WARNING "${CPM_INDENT} Your project is using an unstable development version of CPM.cmake. \ +Please update to a recent release if possible. \ +See https://github.com/cpm-cmake/CPM.cmake for details." + ) +endif() + +set_property(GLOBAL PROPERTY CPM_INITIALIZED true) + +macro(cpm_set_policies) + # the policy allows us to change options without caching + cmake_policy(SET CMP0077 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + + # the policy allows us to change set(CACHE) without caching + if(POLICY CMP0126) + cmake_policy(SET CMP0126 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0126 NEW) + endif() + + # The policy uses the download time for timestamp, instead of the timestamp in the archive. This + # allows for proper rebuilds when a projects url changes + if(POLICY CMP0135) + cmake_policy(SET CMP0135 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0135 NEW) + endif() + + # treat relative git repository paths as being relative to the parent project's remote + if(POLICY CMP0150) + cmake_policy(SET CMP0150 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0150 NEW) + endif() +endmacro() +cpm_set_policies() + +option(CPM_USE_LOCAL_PACKAGES "Always try to use `find_package` to get dependencies" + $ENV{CPM_USE_LOCAL_PACKAGES} +) +option(CPM_LOCAL_PACKAGES_ONLY "Only use `find_package` to get dependencies" + $ENV{CPM_LOCAL_PACKAGES_ONLY} +) +option(CPM_DOWNLOAD_ALL "Always download dependencies from source" $ENV{CPM_DOWNLOAD_ALL}) +option(CPM_DONT_UPDATE_MODULE_PATH "Don't update the module path to allow using find_package" + $ENV{CPM_DONT_UPDATE_MODULE_PATH} +) +option(CPM_DONT_CREATE_PACKAGE_LOCK "Don't create a package lock file in the binary path" + $ENV{CPM_DONT_CREATE_PACKAGE_LOCK} +) +option(CPM_INCLUDE_ALL_IN_PACKAGE_LOCK + "Add all packages added through CPM.cmake to the package lock" + $ENV{CPM_INCLUDE_ALL_IN_PACKAGE_LOCK} +) +option(CPM_USE_NAMED_CACHE_DIRECTORIES + "Use additional directory of package name in cache on the most nested level." + $ENV{CPM_USE_NAMED_CACHE_DIRECTORIES} +) + +set(CPM_VERSION + ${CURRENT_CPM_VERSION} + CACHE INTERNAL "" +) +set(CPM_DIRECTORY + ${CPM_CURRENT_DIRECTORY} + CACHE INTERNAL "" +) +set(CPM_FILE + ${CMAKE_CURRENT_LIST_FILE} + CACHE INTERNAL "" +) +set(CPM_PACKAGES + "" + CACHE INTERNAL "" +) +set(CPM_DRY_RUN + OFF + CACHE INTERNAL "Don't download or configure dependencies (for testing)" +) + +if(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_SOURCE_CACHE_DEFAULT $ENV{CPM_SOURCE_CACHE}) +else() + set(CPM_SOURCE_CACHE_DEFAULT OFF) +endif() + +set(CPM_SOURCE_CACHE + ${CPM_SOURCE_CACHE_DEFAULT} + CACHE PATH "Directory to download CPM dependencies" +) + +if(NOT CPM_DONT_UPDATE_MODULE_PATH) + set(CPM_MODULE_PATH + "${CMAKE_BINARY_DIR}/CPM_modules" + CACHE INTERNAL "" + ) + # remove old modules + file(REMOVE_RECURSE ${CPM_MODULE_PATH}) + file(MAKE_DIRECTORY ${CPM_MODULE_PATH}) + # locally added CPM modules should override global packages + set(CMAKE_MODULE_PATH "${CPM_MODULE_PATH};${CMAKE_MODULE_PATH}") +endif() + +if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) + set(CPM_PACKAGE_LOCK_FILE + "${CMAKE_BINARY_DIR}/cpm-package-lock.cmake" + CACHE INTERNAL "" + ) + file(WRITE ${CPM_PACKAGE_LOCK_FILE} + "# CPM Package Lock\n# This file should be committed to version control\n\n" + ) +endif() + +include(FetchContent) + +# Try to infer package name from git repository uri (path or url) +function(cpm_package_name_from_git_uri URI RESULT) + if("${URI}" MATCHES "([^/:]+)/?.git/?$") + set(${RESULT} + ${CMAKE_MATCH_1} + PARENT_SCOPE + ) + else() + unset(${RESULT} PARENT_SCOPE) + endif() +endfunction() + +# Try to infer package name and version from a url +function(cpm_package_name_and_ver_from_url url outName outVer) + if(url MATCHES "[/\\?]([a-zA-Z0-9_\\.-]+)\\.(tar|tar\\.gz|tar\\.bz2|zip|ZIP)(\\?|/|$)") + # We matched an archive + set(filename "${CMAKE_MATCH_1}") + + if(filename MATCHES "([a-zA-Z0-9_\\.-]+)[_-]v?(([0-9]+\\.)*[0-9]+[a-zA-Z0-9]*)") + # We matched - (ie foo-1.2.3) + set(${outName} + "${CMAKE_MATCH_1}" + PARENT_SCOPE + ) + set(${outVer} + "${CMAKE_MATCH_2}" + PARENT_SCOPE + ) + elseif(filename MATCHES "(([0-9]+\\.)+[0-9]+[a-zA-Z0-9]*)") + # We couldn't find a name, but we found a version + # + # In many cases (which we don't handle here) the url would look something like + # `irrelevant/ACTUAL_PACKAGE_NAME/irrelevant/1.2.3.zip`. In such a case we can't possibly + # distinguish the package name from the irrelevant bits. Moreover if we try to match the + # package name from the filename, we'd get bogus at best. + unset(${outName} PARENT_SCOPE) + set(${outVer} + "${CMAKE_MATCH_1}" + PARENT_SCOPE + ) + else() + # Boldly assume that the file name is the package name. + # + # Yes, something like `irrelevant/ACTUAL_NAME/irrelevant/download.zip` will ruin our day, but + # such cases should be quite rare. No popular service does this... we think. + set(${outName} + "${filename}" + PARENT_SCOPE + ) + unset(${outVer} PARENT_SCOPE) + endif() + else() + # No ideas yet what to do with non-archives + unset(${outName} PARENT_SCOPE) + unset(${outVer} PARENT_SCOPE) + endif() +endfunction() + +function(cpm_find_package NAME VERSION) + string(REPLACE " " ";" EXTRA_ARGS "${ARGN}") + find_package(${NAME} ${VERSION} ${EXTRA_ARGS} QUIET) + if(${CPM_ARGS_NAME}_FOUND) + if(DEFINED ${CPM_ARGS_NAME}_VERSION) + set(VERSION ${${CPM_ARGS_NAME}_VERSION}) + endif() + cpm_message(STATUS "${CPM_INDENT} Using local package ${CPM_ARGS_NAME}@${VERSION}") + CPMRegisterPackage(${CPM_ARGS_NAME} "${VERSION}") + set(CPM_PACKAGE_FOUND + YES + PARENT_SCOPE + ) + else() + set(CPM_PACKAGE_FOUND + NO + PARENT_SCOPE + ) + endif() +endfunction() + +# Create a custom FindXXX.cmake module for a CPM package This prevents `find_package(NAME)` from +# finding the system library +function(cpm_create_module_file Name) + if(NOT CPM_DONT_UPDATE_MODULE_PATH) + # erase any previous modules + file(WRITE ${CPM_MODULE_PATH}/Find${Name}.cmake + "include(\"${CPM_FILE}\")\n${ARGN}\nset(${Name}_FOUND TRUE)" + ) + endif() +endfunction() + +# Find a package locally or fallback to CPMAddPackage +function(CPMFindPackage) + set(oneValueArgs NAME VERSION GIT_TAG FIND_PACKAGE_ARGUMENTS) + + cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "" ${ARGN}) + + if(NOT DEFINED CPM_ARGS_VERSION) + if(DEFINED CPM_ARGS_GIT_TAG) + cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION) + endif() + endif() + + set(downloadPackage ${CPM_DOWNLOAD_ALL}) + if(DEFINED CPM_DOWNLOAD_${CPM_ARGS_NAME}) + set(downloadPackage ${CPM_DOWNLOAD_${CPM_ARGS_NAME}}) + elseif(DEFINED ENV{CPM_DOWNLOAD_${CPM_ARGS_NAME}}) + set(downloadPackage $ENV{CPM_DOWNLOAD_${CPM_ARGS_NAME}}) + endif() + if(downloadPackage) + CPMAddPackage(${ARGN}) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}") + if(CPM_PACKAGE_ALREADY_ADDED) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS}) + + if(NOT CPM_PACKAGE_FOUND) + CPMAddPackage(${ARGN}) + cpm_export_variables(${CPM_ARGS_NAME}) + endif() + +endfunction() + +# checks if a package has been added before +function(cpm_check_if_package_already_added CPM_ARGS_NAME CPM_ARGS_VERSION) + if("${CPM_ARGS_NAME}" IN_LIST CPM_PACKAGES) + CPMGetPackageVersion(${CPM_ARGS_NAME} CPM_PACKAGE_VERSION) + if("${CPM_PACKAGE_VERSION}" VERSION_LESS "${CPM_ARGS_VERSION}") + message( + WARNING + "${CPM_INDENT} Requires a newer version of ${CPM_ARGS_NAME} (${CPM_ARGS_VERSION}) than currently included (${CPM_PACKAGE_VERSION})." + ) + endif() + cpm_get_fetch_properties(${CPM_ARGS_NAME}) + set(${CPM_ARGS_NAME}_ADDED NO) + set(CPM_PACKAGE_ALREADY_ADDED + YES + PARENT_SCOPE + ) + cpm_export_variables(${CPM_ARGS_NAME}) + else() + set(CPM_PACKAGE_ALREADY_ADDED + NO + PARENT_SCOPE + ) + endif() +endfunction() + +# Parse the argument of CPMAddPackage in case a single one was provided and convert it to a list of +# arguments which can then be parsed idiomatically. For example gh:foo/bar@1.2.3 will be converted +# to: GITHUB_REPOSITORY;foo/bar;VERSION;1.2.3 +function(cpm_parse_add_package_single_arg arg outArgs) + # Look for a scheme + if("${arg}" MATCHES "^([a-zA-Z]+):(.+)$") + string(TOLOWER "${CMAKE_MATCH_1}" scheme) + set(uri "${CMAKE_MATCH_2}") + + # Check for CPM-specific schemes + if(scheme STREQUAL "gh") + set(out "GITHUB_REPOSITORY;${uri}") + set(packageType "git") + elseif(scheme STREQUAL "gl") + set(out "GITLAB_REPOSITORY;${uri}") + set(packageType "git") + elseif(scheme STREQUAL "bb") + set(out "BITBUCKET_REPOSITORY;${uri}") + set(packageType "git") + # A CPM-specific scheme was not found. Looks like this is a generic URL so try to determine + # type + elseif(arg MATCHES ".git/?(@|#|$)") + set(out "GIT_REPOSITORY;${arg}") + set(packageType "git") + else() + # Fall back to a URL + set(out "URL;${arg}") + set(packageType "archive") + + # We could also check for SVN since FetchContent supports it, but SVN is so rare these days. + # We just won't bother with the additional complexity it will induce in this function. SVN is + # done by multi-arg + endif() + else() + if(arg MATCHES ".git/?(@|#|$)") + set(out "GIT_REPOSITORY;${arg}") + set(packageType "git") + else() + # Give up + message(FATAL_ERROR "${CPM_INDENT} Can't determine package type of '${arg}'") + endif() + endif() + + # For all packages we interpret @... as version. Only replace the last occurrence. Thus URIs + # containing '@' can be used + string(REGEX REPLACE "@([^@]+)$" ";VERSION;\\1" out "${out}") + + # Parse the rest according to package type + if(packageType STREQUAL "git") + # For git repos we interpret #... as a tag or branch or commit hash + string(REGEX REPLACE "#([^#]+)$" ";GIT_TAG;\\1" out "${out}") + elseif(packageType STREQUAL "archive") + # For archives we interpret #... as a URL hash. + string(REGEX REPLACE "#([^#]+)$" ";URL_HASH;\\1" out "${out}") + # We don't try to parse the version if it's not provided explicitly. cpm_get_version_from_url + # should do this at a later point + else() + # We should never get here. This is an assertion and hitting it means there's a bug in the code + # above. A packageType was set, but not handled by this if-else. + message(FATAL_ERROR "${CPM_INDENT} Unsupported package type '${packageType}' of '${arg}'") + endif() + + set(${outArgs} + ${out} + PARENT_SCOPE + ) +endfunction() + +# Check that the working directory for a git repo is clean +function(cpm_check_git_working_dir_is_clean repoPath gitTag isClean) + + find_package(Git REQUIRED) + + if(NOT GIT_EXECUTABLE) + # No git executable, assume directory is clean + set(${isClean} + TRUE + PARENT_SCOPE + ) + return() + endif() + + # check for uncommitted changes + execute_process( + COMMAND ${GIT_EXECUTABLE} status --porcelain + RESULT_VARIABLE resultGitStatus + OUTPUT_VARIABLE repoStatus + OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET + WORKING_DIRECTORY ${repoPath} + ) + if(resultGitStatus) + # not supposed to happen, assume clean anyway + message(WARNING "${CPM_INDENT} Calling git status on folder ${repoPath} failed") + set(${isClean} + TRUE + PARENT_SCOPE + ) + return() + endif() + + if(NOT "${repoStatus}" STREQUAL "") + set(${isClean} + FALSE + PARENT_SCOPE + ) + return() + endif() + + # check for committed changes + execute_process( + COMMAND ${GIT_EXECUTABLE} diff -s --exit-code ${gitTag} + RESULT_VARIABLE resultGitDiff + OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_QUIET + WORKING_DIRECTORY ${repoPath} + ) + + if(${resultGitDiff} EQUAL 0) + set(${isClean} + TRUE + PARENT_SCOPE + ) + else() + set(${isClean} + FALSE + PARENT_SCOPE + ) + endif() + +endfunction() + +# method to overwrite internal FetchContent properties, to allow using CPM.cmake to overload +# FetchContent calls. As these are internal cmake properties, this method should be used carefully +# and may need modification in future CMake versions. Source: +# https://github.com/Kitware/CMake/blob/dc3d0b5a0a7d26d43d6cfeb511e224533b5d188f/Modules/FetchContent.cmake#L1152 +function(cpm_override_fetchcontent contentName) + cmake_parse_arguments(PARSE_ARGV 1 arg "" "SOURCE_DIR;BINARY_DIR" "") + if(NOT "${arg_UNPARSED_ARGUMENTS}" STREQUAL "") + message(FATAL_ERROR "${CPM_INDENT} Unsupported arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + + string(TOLOWER ${contentName} contentNameLower) + set(prefix "_FetchContent_${contentNameLower}") + + set(propertyName "${prefix}_sourceDir") + define_property( + GLOBAL + PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} "${arg_SOURCE_DIR}") + + set(propertyName "${prefix}_binaryDir") + define_property( + GLOBAL + PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} "${arg_BINARY_DIR}") + + set(propertyName "${prefix}_populated") + define_property( + GLOBAL + PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} TRUE) +endfunction() + +# Download and add a package from source +function(CPMAddPackage) + cpm_set_policies() + + list(LENGTH ARGN argnLength) + if(argnLength EQUAL 1) + cpm_parse_add_package_single_arg("${ARGN}" ARGN) + + # The shorthand syntax implies EXCLUDE_FROM_ALL and SYSTEM + set(ARGN "${ARGN};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;") + endif() + + set(oneValueArgs + NAME + FORCE + VERSION + GIT_TAG + DOWNLOAD_ONLY + GITHUB_REPOSITORY + GITLAB_REPOSITORY + BITBUCKET_REPOSITORY + GIT_REPOSITORY + SOURCE_DIR + FIND_PACKAGE_ARGUMENTS + NO_CACHE + SYSTEM + GIT_SHALLOW + EXCLUDE_FROM_ALL + SOURCE_SUBDIR + ) + + set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND) + + cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}") + + # Set default values for arguments + + if(NOT DEFINED CPM_ARGS_VERSION) + if(DEFINED CPM_ARGS_GIT_TAG) + cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION) + endif() + endif() + + if(CPM_ARGS_DOWNLOAD_ONLY) + set(DOWNLOAD_ONLY ${CPM_ARGS_DOWNLOAD_ONLY}) + else() + set(DOWNLOAD_ONLY NO) + endif() + + if(DEFINED CPM_ARGS_GITHUB_REPOSITORY) + set(CPM_ARGS_GIT_REPOSITORY "https://github.com/${CPM_ARGS_GITHUB_REPOSITORY}.git") + elseif(DEFINED CPM_ARGS_GITLAB_REPOSITORY) + set(CPM_ARGS_GIT_REPOSITORY "https://gitlab.com/${CPM_ARGS_GITLAB_REPOSITORY}.git") + elseif(DEFINED CPM_ARGS_BITBUCKET_REPOSITORY) + set(CPM_ARGS_GIT_REPOSITORY "https://bitbucket.org/${CPM_ARGS_BITBUCKET_REPOSITORY}.git") + endif() + + if(DEFINED CPM_ARGS_GIT_REPOSITORY) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_REPOSITORY ${CPM_ARGS_GIT_REPOSITORY}) + if(NOT DEFINED CPM_ARGS_GIT_TAG) + set(CPM_ARGS_GIT_TAG v${CPM_ARGS_VERSION}) + endif() + + # If a name wasn't provided, try to infer it from the git repo + if(NOT DEFINED CPM_ARGS_NAME) + cpm_package_name_from_git_uri(${CPM_ARGS_GIT_REPOSITORY} CPM_ARGS_NAME) + endif() + endif() + + set(CPM_SKIP_FETCH FALSE) + + if(DEFINED CPM_ARGS_GIT_TAG) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_TAG ${CPM_ARGS_GIT_TAG}) + # If GIT_SHALLOW is explicitly specified, honor the value. + if(DEFINED CPM_ARGS_GIT_SHALLOW) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_SHALLOW ${CPM_ARGS_GIT_SHALLOW}) + endif() + endif() + + if(DEFINED CPM_ARGS_URL) + # If a name or version aren't provided, try to infer them from the URL + list(GET CPM_ARGS_URL 0 firstUrl) + cpm_package_name_and_ver_from_url(${firstUrl} nameFromUrl verFromUrl) + # If we fail to obtain name and version from the first URL, we could try other URLs if any. + # However multiple URLs are expected to be quite rare, so for now we won't bother. + + # If the caller provided their own name and version, they trump the inferred ones. + if(NOT DEFINED CPM_ARGS_NAME) + set(CPM_ARGS_NAME ${nameFromUrl}) + endif() + if(NOT DEFINED CPM_ARGS_VERSION) + set(CPM_ARGS_VERSION ${verFromUrl}) + endif() + + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS URL "${CPM_ARGS_URL}") + endif() + + # Check for required arguments + + if(NOT DEFINED CPM_ARGS_NAME) + message( + FATAL_ERROR + "${CPM_INDENT} 'NAME' was not provided and couldn't be automatically inferred for package added with arguments: '${ARGN}'" + ) + endif() + + # Check if package has been added before + cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}") + if(CPM_PACKAGE_ALREADY_ADDED) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + # Check for manual overrides + if(NOT CPM_ARGS_FORCE AND NOT "${CPM_${CPM_ARGS_NAME}_SOURCE}" STREQUAL "") + set(PACKAGE_SOURCE ${CPM_${CPM_ARGS_NAME}_SOURCE}) + set(CPM_${CPM_ARGS_NAME}_SOURCE "") + CPMAddPackage( + NAME "${CPM_ARGS_NAME}" + SOURCE_DIR "${PACKAGE_SOURCE}" + EXCLUDE_FROM_ALL "${CPM_ARGS_EXCLUDE_FROM_ALL}" + SYSTEM "${CPM_ARGS_SYSTEM}" + OPTIONS "${CPM_ARGS_OPTIONS}" + SOURCE_SUBDIR "${CPM_ARGS_SOURCE_SUBDIR}" + DOWNLOAD_ONLY "${DOWNLOAD_ONLY}" + FORCE True + ) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + # Check for available declaration + if(NOT CPM_ARGS_FORCE AND NOT "${CPM_DECLARATION_${CPM_ARGS_NAME}}" STREQUAL "") + set(declaration ${CPM_DECLARATION_${CPM_ARGS_NAME}}) + set(CPM_DECLARATION_${CPM_ARGS_NAME} "") + CPMAddPackage(${declaration}) + cpm_export_variables(${CPM_ARGS_NAME}) + # checking again to ensure version and option compatibility + cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}") + return() + endif() + + if(NOT CPM_ARGS_FORCE) + if(CPM_USE_LOCAL_PACKAGES OR CPM_LOCAL_PACKAGES_ONLY) + cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS}) + + if(CPM_PACKAGE_FOUND) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + if(CPM_LOCAL_PACKAGES_ONLY) + message( + SEND_ERROR + "${CPM_INDENT} ${CPM_ARGS_NAME} not found via find_package(${CPM_ARGS_NAME} ${CPM_ARGS_VERSION})" + ) + endif() + endif() + endif() + + CPMRegisterPackage("${CPM_ARGS_NAME}" "${CPM_ARGS_VERSION}") + + if(DEFINED CPM_ARGS_GIT_TAG) + set(PACKAGE_INFO "${CPM_ARGS_GIT_TAG}") + elseif(DEFINED CPM_ARGS_SOURCE_DIR) + set(PACKAGE_INFO "${CPM_ARGS_SOURCE_DIR}") + else() + set(PACKAGE_INFO "${CPM_ARGS_VERSION}") + endif() + + if(DEFINED FETCHCONTENT_BASE_DIR) + # respect user's FETCHCONTENT_BASE_DIR if set + set(CPM_FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR}) + else() + set(CPM_FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/_deps) + endif() + + if(DEFINED CPM_ARGS_DOWNLOAD_COMMAND) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS DOWNLOAD_COMMAND ${CPM_ARGS_DOWNLOAD_COMMAND}) + elseif(DEFINED CPM_ARGS_SOURCE_DIR) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${CPM_ARGS_SOURCE_DIR}) + if(NOT IS_ABSOLUTE ${CPM_ARGS_SOURCE_DIR}) + # Expand `CPM_ARGS_SOURCE_DIR` relative path. This is important because EXISTS doesn't work + # for relative paths. + get_filename_component( + source_directory ${CPM_ARGS_SOURCE_DIR} REALPATH BASE_DIR ${CMAKE_CURRENT_BINARY_DIR} + ) + else() + set(source_directory ${CPM_ARGS_SOURCE_DIR}) + endif() + if(NOT EXISTS ${source_directory}) + string(TOLOWER ${CPM_ARGS_NAME} lower_case_name) + # remove timestamps so CMake will re-download the dependency + file(REMOVE_RECURSE "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild") + endif() + elseif(CPM_SOURCE_CACHE AND NOT CPM_ARGS_NO_CACHE) + string(TOLOWER ${CPM_ARGS_NAME} lower_case_name) + set(origin_parameters ${CPM_ARGS_UNPARSED_ARGUMENTS}) + list(SORT origin_parameters) + if(CPM_USE_NAMED_CACHE_DIRECTORIES) + string(SHA1 origin_hash "${origin_parameters};NEW_CACHE_STRUCTURE_TAG") + set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}/${CPM_ARGS_NAME}) + else() + string(SHA1 origin_hash "${origin_parameters}") + set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}) + endif() + # Expand `download_directory` relative path. This is important because EXISTS doesn't work for + # relative paths. + get_filename_component(download_directory ${download_directory} ABSOLUTE) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${download_directory}) + + if(CPM_SOURCE_CACHE) + file(LOCK ${download_directory}/../cmake.lock) + endif() + + if(EXISTS ${download_directory}) + if(CPM_SOURCE_CACHE) + file(LOCK ${download_directory}/../cmake.lock RELEASE) + endif() + + cpm_store_fetch_properties( + ${CPM_ARGS_NAME} "${download_directory}" + "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-build" + ) + cpm_get_fetch_properties("${CPM_ARGS_NAME}") + + if(DEFINED CPM_ARGS_GIT_TAG AND NOT (PATCH_COMMAND IN_LIST CPM_ARGS_UNPARSED_ARGUMENTS)) + # warn if cache has been changed since checkout + cpm_check_git_working_dir_is_clean(${download_directory} ${CPM_ARGS_GIT_TAG} IS_CLEAN) + if(NOT ${IS_CLEAN}) + message( + WARNING "${CPM_INDENT} Cache for ${CPM_ARGS_NAME} (${download_directory}) is dirty" + ) + endif() + endif() + + cpm_add_subdirectory( + "${CPM_ARGS_NAME}" + "${DOWNLOAD_ONLY}" + "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" + "${${CPM_ARGS_NAME}_BINARY_DIR}" + "${CPM_ARGS_EXCLUDE_FROM_ALL}" + "${CPM_ARGS_SYSTEM}" + "${CPM_ARGS_OPTIONS}" + ) + set(PACKAGE_INFO "${PACKAGE_INFO} at ${download_directory}") + + # As the source dir is already cached/populated, we override the call to FetchContent. + set(CPM_SKIP_FETCH TRUE) + cpm_override_fetchcontent( + "${lower_case_name}" SOURCE_DIR "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" + BINARY_DIR "${${CPM_ARGS_NAME}_BINARY_DIR}" + ) + + else() + # Enable shallow clone when GIT_TAG is not a commit hash. Our guess may not be accurate, but + # it should guarantee no commit hash get mis-detected. + if(NOT DEFINED CPM_ARGS_GIT_SHALLOW) + cpm_is_git_tag_commit_hash("${CPM_ARGS_GIT_TAG}" IS_HASH) + if(NOT ${IS_HASH}) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_SHALLOW TRUE) + endif() + endif() + + # remove timestamps so CMake will re-download the dependency + file(REMOVE_RECURSE ${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild) + set(PACKAGE_INFO "${PACKAGE_INFO} to ${download_directory}") + endif() + endif() + + cpm_create_module_file(${CPM_ARGS_NAME} "CPMAddPackage(\"${ARGN}\")") + + if(CPM_PACKAGE_LOCK_ENABLED) + if((CPM_ARGS_VERSION AND NOT CPM_ARGS_SOURCE_DIR) OR CPM_INCLUDE_ALL_IN_PACKAGE_LOCK) + cpm_add_to_package_lock(${CPM_ARGS_NAME} "${ARGN}") + elseif(CPM_ARGS_SOURCE_DIR) + cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "local directory") + else() + cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "${ARGN}") + endif() + endif() + + cpm_message( + STATUS "${CPM_INDENT} Adding package ${CPM_ARGS_NAME}@${CPM_ARGS_VERSION} (${PACKAGE_INFO})" + ) + + if(NOT CPM_SKIP_FETCH) + cpm_declare_fetch( + "${CPM_ARGS_NAME}" "${CPM_ARGS_VERSION}" "${PACKAGE_INFO}" "${CPM_ARGS_UNPARSED_ARGUMENTS}" + ) + cpm_fetch_package("${CPM_ARGS_NAME}" populated) + if(CPM_SOURCE_CACHE AND download_directory) + file(LOCK ${download_directory}/../cmake.lock RELEASE) + endif() + if(${populated}) + cpm_add_subdirectory( + "${CPM_ARGS_NAME}" + "${DOWNLOAD_ONLY}" + "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" + "${${CPM_ARGS_NAME}_BINARY_DIR}" + "${CPM_ARGS_EXCLUDE_FROM_ALL}" + "${CPM_ARGS_SYSTEM}" + "${CPM_ARGS_OPTIONS}" + ) + endif() + cpm_get_fetch_properties("${CPM_ARGS_NAME}") + endif() + + set(${CPM_ARGS_NAME}_ADDED YES) + cpm_export_variables("${CPM_ARGS_NAME}") +endfunction() + +# Fetch a previously declared package +macro(CPMGetPackage Name) + if(DEFINED "CPM_DECLARATION_${Name}") + CPMAddPackage(NAME ${Name}) + else() + message(SEND_ERROR "${CPM_INDENT} Cannot retrieve package ${Name}: no declaration available") + endif() +endmacro() + +# export variables available to the caller to the parent scope expects ${CPM_ARGS_NAME} to be set +macro(cpm_export_variables name) + set(${name}_SOURCE_DIR + "${${name}_SOURCE_DIR}" + PARENT_SCOPE + ) + set(${name}_BINARY_DIR + "${${name}_BINARY_DIR}" + PARENT_SCOPE + ) + set(${name}_ADDED + "${${name}_ADDED}" + PARENT_SCOPE + ) + set(CPM_LAST_PACKAGE_NAME + "${name}" + PARENT_SCOPE + ) +endmacro() + +# declares a package, so that any call to CPMAddPackage for the package name will use these +# arguments instead. Previous declarations will not be overridden. +macro(CPMDeclarePackage Name) + if(NOT DEFINED "CPM_DECLARATION_${Name}") + set("CPM_DECLARATION_${Name}" "${ARGN}") + endif() +endmacro() + +function(cpm_add_to_package_lock Name) + if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) + cpm_prettify_package_arguments(PRETTY_ARGN false ${ARGN}) + file(APPEND ${CPM_PACKAGE_LOCK_FILE} "# ${Name}\nCPMDeclarePackage(${Name}\n${PRETTY_ARGN})\n") + endif() +endfunction() + +function(cpm_add_comment_to_package_lock Name) + if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) + cpm_prettify_package_arguments(PRETTY_ARGN true ${ARGN}) + file(APPEND ${CPM_PACKAGE_LOCK_FILE} + "# ${Name} (unversioned)\n# CPMDeclarePackage(${Name}\n${PRETTY_ARGN}#)\n" + ) + endif() +endfunction() + +# includes the package lock file if it exists and creates a target `cpm-update-package-lock` to +# update it +macro(CPMUsePackageLock file) + if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) + get_filename_component(CPM_ABSOLUTE_PACKAGE_LOCK_PATH ${file} ABSOLUTE) + if(EXISTS ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH}) + include(${CPM_ABSOLUTE_PACKAGE_LOCK_PATH}) + endif() + if(NOT TARGET cpm-update-package-lock) + add_custom_target( + cpm-update-package-lock COMMAND ${CMAKE_COMMAND} -E copy ${CPM_PACKAGE_LOCK_FILE} + ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH} + ) + endif() + set(CPM_PACKAGE_LOCK_ENABLED true) + endif() +endmacro() + +# registers a package that has been added to CPM +function(CPMRegisterPackage PACKAGE VERSION) + list(APPEND CPM_PACKAGES ${PACKAGE}) + set(CPM_PACKAGES + ${CPM_PACKAGES} + CACHE INTERNAL "" + ) + set("CPM_PACKAGE_${PACKAGE}_VERSION" + ${VERSION} + CACHE INTERNAL "" + ) +endfunction() + +# retrieve the current version of the package to ${OUTPUT} +function(CPMGetPackageVersion PACKAGE OUTPUT) + set(${OUTPUT} + "${CPM_PACKAGE_${PACKAGE}_VERSION}" + PARENT_SCOPE + ) +endfunction() + +# declares a package in FetchContent_Declare +function(cpm_declare_fetch PACKAGE VERSION INFO) + if(${CPM_DRY_RUN}) + cpm_message(STATUS "${CPM_INDENT} Package not declared (dry run)") + return() + endif() + + FetchContent_Declare(${PACKAGE} ${ARGN}) +endfunction() + +# returns properties for a package previously defined by cpm_declare_fetch +function(cpm_get_fetch_properties PACKAGE) + if(${CPM_DRY_RUN}) + return() + endif() + + set(${PACKAGE}_SOURCE_DIR + "${CPM_PACKAGE_${PACKAGE}_SOURCE_DIR}" + PARENT_SCOPE + ) + set(${PACKAGE}_BINARY_DIR + "${CPM_PACKAGE_${PACKAGE}_BINARY_DIR}" + PARENT_SCOPE + ) +endfunction() + +function(cpm_store_fetch_properties PACKAGE source_dir binary_dir) + if(${CPM_DRY_RUN}) + return() + endif() + + set(CPM_PACKAGE_${PACKAGE}_SOURCE_DIR + "${source_dir}" + CACHE INTERNAL "" + ) + set(CPM_PACKAGE_${PACKAGE}_BINARY_DIR + "${binary_dir}" + CACHE INTERNAL "" + ) +endfunction() + +# adds a package as a subdirectory if viable, according to provided options +function( + cpm_add_subdirectory + PACKAGE + DOWNLOAD_ONLY + SOURCE_DIR + BINARY_DIR + EXCLUDE + SYSTEM + OPTIONS +) + + if(NOT DOWNLOAD_ONLY AND EXISTS ${SOURCE_DIR}/CMakeLists.txt) + set(addSubdirectoryExtraArgs "") + if(EXCLUDE) + list(APPEND addSubdirectoryExtraArgs EXCLUDE_FROM_ALL) + endif() + if("${SYSTEM}" AND "${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.25") + # https://cmake.org/cmake/help/latest/prop_dir/SYSTEM.html#prop_dir:SYSTEM + list(APPEND addSubdirectoryExtraArgs SYSTEM) + endif() + if(OPTIONS) + foreach(OPTION ${OPTIONS}) + cpm_parse_option("${OPTION}") + set(${OPTION_KEY} "${OPTION_VALUE}") + endforeach() + endif() + set(CPM_OLD_INDENT "${CPM_INDENT}") + set(CPM_INDENT "${CPM_INDENT} ${PACKAGE}:") + add_subdirectory(${SOURCE_DIR} ${BINARY_DIR} ${addSubdirectoryExtraArgs}) + set(CPM_INDENT "${CPM_OLD_INDENT}") + endif() +endfunction() + +# downloads a previously declared package via FetchContent and exports the variables +# `${PACKAGE}_SOURCE_DIR` and `${PACKAGE}_BINARY_DIR` to the parent scope +function(cpm_fetch_package PACKAGE populated) + set(${populated} + FALSE + PARENT_SCOPE + ) + if(${CPM_DRY_RUN}) + cpm_message(STATUS "${CPM_INDENT} Package ${PACKAGE} not fetched (dry run)") + return() + endif() + + FetchContent_GetProperties(${PACKAGE}) + + string(TOLOWER "${PACKAGE}" lower_case_name) + + if(NOT ${lower_case_name}_POPULATED) + FetchContent_Populate(${PACKAGE}) + set(${populated} + TRUE + PARENT_SCOPE + ) + endif() + + cpm_store_fetch_properties( + ${CPM_ARGS_NAME} ${${lower_case_name}_SOURCE_DIR} ${${lower_case_name}_BINARY_DIR} + ) + + set(${PACKAGE}_SOURCE_DIR + ${${lower_case_name}_SOURCE_DIR} + PARENT_SCOPE + ) + set(${PACKAGE}_BINARY_DIR + ${${lower_case_name}_BINARY_DIR} + PARENT_SCOPE + ) +endfunction() + +# splits a package option +function(cpm_parse_option OPTION) + string(REGEX MATCH "^[^ ]+" OPTION_KEY "${OPTION}") + string(LENGTH "${OPTION}" OPTION_LENGTH) + string(LENGTH "${OPTION_KEY}" OPTION_KEY_LENGTH) + if(OPTION_KEY_LENGTH STREQUAL OPTION_LENGTH) + # no value for key provided, assume user wants to set option to "ON" + set(OPTION_VALUE "ON") + else() + math(EXPR OPTION_KEY_LENGTH "${OPTION_KEY_LENGTH}+1") + string(SUBSTRING "${OPTION}" "${OPTION_KEY_LENGTH}" "-1" OPTION_VALUE) + endif() + set(OPTION_KEY + "${OPTION_KEY}" + PARENT_SCOPE + ) + set(OPTION_VALUE + "${OPTION_VALUE}" + PARENT_SCOPE + ) +endfunction() + +# guesses the package version from a git tag +function(cpm_get_version_from_git_tag GIT_TAG RESULT) + string(LENGTH ${GIT_TAG} length) + if(length EQUAL 40) + # GIT_TAG is probably a git hash + set(${RESULT} + 0 + PARENT_SCOPE + ) + else() + string(REGEX MATCH "v?([0123456789.]*).*" _ ${GIT_TAG}) + set(${RESULT} + ${CMAKE_MATCH_1} + PARENT_SCOPE + ) + endif() +endfunction() + +# guesses if the git tag is a commit hash or an actual tag or a branch name. +function(cpm_is_git_tag_commit_hash GIT_TAG RESULT) + string(LENGTH "${GIT_TAG}" length) + # full hash has 40 characters, and short hash has at least 7 characters. + if(length LESS 7 OR length GREATER 40) + set(${RESULT} + 0 + PARENT_SCOPE + ) + else() + if(${GIT_TAG} MATCHES "^[a-fA-F0-9]+$") + set(${RESULT} + 1 + PARENT_SCOPE + ) + else() + set(${RESULT} + 0 + PARENT_SCOPE + ) + endif() + endif() +endfunction() + +function(cpm_prettify_package_arguments OUT_VAR IS_IN_COMMENT) + set(oneValueArgs + NAME + FORCE + VERSION + GIT_TAG + DOWNLOAD_ONLY + GITHUB_REPOSITORY + GITLAB_REPOSITORY + BITBUCKET_REPOSITORY + GIT_REPOSITORY + SOURCE_DIR + FIND_PACKAGE_ARGUMENTS + NO_CACHE + SYSTEM + GIT_SHALLOW + EXCLUDE_FROM_ALL + SOURCE_SUBDIR + ) + set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND) + cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + foreach(oneArgName ${oneValueArgs}) + if(DEFINED CPM_ARGS_${oneArgName}) + if(${IS_IN_COMMENT}) + string(APPEND PRETTY_OUT_VAR "#") + endif() + if(${oneArgName} STREQUAL "SOURCE_DIR") + string(REPLACE ${CMAKE_SOURCE_DIR} "\${CMAKE_SOURCE_DIR}" CPM_ARGS_${oneArgName} + ${CPM_ARGS_${oneArgName}} + ) + endif() + string(APPEND PRETTY_OUT_VAR " ${oneArgName} ${CPM_ARGS_${oneArgName}}\n") + endif() + endforeach() + foreach(multiArgName ${multiValueArgs}) + if(DEFINED CPM_ARGS_${multiArgName}) + if(${IS_IN_COMMENT}) + string(APPEND PRETTY_OUT_VAR "#") + endif() + string(APPEND PRETTY_OUT_VAR " ${multiArgName}\n") + foreach(singleOption ${CPM_ARGS_${multiArgName}}) + if(${IS_IN_COMMENT}) + string(APPEND PRETTY_OUT_VAR "#") + endif() + string(APPEND PRETTY_OUT_VAR " \"${singleOption}\"\n") + endforeach() + endif() + endforeach() + + if(NOT "${CPM_ARGS_UNPARSED_ARGUMENTS}" STREQUAL "") + if(${IS_IN_COMMENT}) + string(APPEND PRETTY_OUT_VAR "#") + endif() + string(APPEND PRETTY_OUT_VAR " ") + foreach(CPM_ARGS_UNPARSED_ARGUMENT ${CPM_ARGS_UNPARSED_ARGUMENTS}) + string(APPEND PRETTY_OUT_VAR " ${CPM_ARGS_UNPARSED_ARGUMENT}") + endforeach() + string(APPEND PRETTY_OUT_VAR "\n") + endif() + + set(${OUT_VAR} + ${PRETTY_OUT_VAR} + PARENT_SCOPE + ) + +endfunction() \ No newline at end of file diff --git a/tools/BuildTools/cmake/Modules/CodeCoverage.cmake b/tools/BuildTools/cmake/Modules/CodeCoverage.cmake new file mode 100644 index 0000000..8844c89 --- /dev/null +++ b/tools/BuildTools/cmake/Modules/CodeCoverage.cmake @@ -0,0 +1,743 @@ +# Copyright (c) 2012 - 2017, Lars Bilke +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# CHANGES: +# +# 2012-01-31, Lars Bilke +# - Enable Code Coverage +# +# 2013-09-17, Joakim Söderberg +# - Added support for Clang. +# - Some additional usage instructions. +# +# 2016-02-03, Lars Bilke +# - Refactored functions to use named parameters +# +# 2017-06-02, Lars Bilke +# - Merged with modified version from github.com/ufz/ogs +# +# 2019-05-06, Anatolii Kurotych +# - Remove unnecessary --coverage flag +# +# 2019-12-13, FeRD (Frank Dana) +# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor +# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments. +# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY +# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list +# - Set lcov basedir with -b argument +# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be +# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().) +# - Delete output dir, .info file on 'make clean' +# - Remove Python detection, since version mismatches will break gcovr +# - Minor cleanup (lowercase function names, update examples...) +# +# 2019-12-19, FeRD (Frank Dana) +# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets +# +# 2020-01-19, Bob Apthorpe +# - Added gfortran support +# +# 2020-02-17, FeRD (Frank Dana) +# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters +# in EXCLUDEs, and remove manual escaping from gcovr targets +# +# 2021-01-19, Robin Mueller +# - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run +# - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional +# flags to the gcovr command +# +# 2020-05-04, Mihchael Davis +# - Add -fprofile-abs-path to make gcno files contain absolute paths +# - Fix BASE_DIRECTORY not working when defined +# - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines +# +# 2021-05-10, Martin Stump +# - Check if the generator is multi-config before warning about non-Debug builds +# +# 2022-02-22, Marko Wehle +# - Change gcovr output from -o for --xml and --html output respectively. +# This will allow for Multiple Output Formats at the same time by making use of GCOVR_ADDITIONAL_ARGS, e.g. GCOVR_ADDITIONAL_ARGS "--txt". +# +# 2022-09-28, Sebastian Mueller +# - fix append_coverage_compiler_flags_to_target to correctly add flags +# - replace "-fprofile-arcs -ftest-coverage" with "--coverage" (equivalent) +# +# USAGE: +# +# 1. Copy this file into your cmake modules path. +# +# 2. Add the following line to your CMakeLists.txt (best inside an if-condition +# using a CMake option() to enable it just optionally): +# include(CodeCoverage) +# +# 3. Append necessary compiler flags for all supported source files: +# append_coverage_compiler_flags() +# Or for specific target: +# append_coverage_compiler_flags_to_target(YOUR_TARGET_NAME) +# +# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og +# +# 4. If you need to exclude additional directories from the report, specify them +# using full paths in the COVERAGE_EXCLUDES variable before calling +# setup_target_for_coverage_*(). +# Example: +# set(COVERAGE_EXCLUDES +# '${PROJECT_SOURCE_DIR}/src/dir1/*' +# '/path/to/my/src/dir2/*') +# Or, use the EXCLUDE argument to setup_target_for_coverage_*(). +# Example: +# setup_target_for_coverage_lcov( +# NAME coverage +# EXECUTABLE testrunner +# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*") +# +# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set +# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR) +# Example: +# set(COVERAGE_EXCLUDES "dir1/*") +# setup_target_for_coverage_gcovr_html( +# NAME coverage +# EXECUTABLE testrunner +# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src" +# EXCLUDE "dir2/*") +# +# 5. Use the functions described below to create a custom make target which +# runs your test executable and produces a code coverage report. +# +# 6. Build a Debug build: +# cmake -DCMAKE_BUILD_TYPE=Debug .. +# make +# make my_coverage_target +# +include_guard(GLOBAL) + +include(CMakeParseArguments) + +option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE) + +# Check prereqs +find_program( GCOV_PATH gcov ) +find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl) +find_program( FASTCOV_PATH NAMES fastcov fastcov.py ) +find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat ) +find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) +find_program( CPPFILT_PATH NAMES c++filt ) + +if(NOT GCOV_PATH) + message(FATAL_ERROR "gcov not found! Aborting...") +endif() # NOT GCOV_PATH + +# Check supported compiler (Clang, GNU and Flang) +get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) +foreach(LANG ${LANGUAGES}) + if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") + if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3) + message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") + endif() + elseif(NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "GNU" + AND NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(LLVM)?[Ff]lang") + message(FATAL_ERROR "Compiler is not GNU or Flang! Aborting...") + endif() +endforeach() + +set(COVERAGE_COMPILER_FLAGS "-g --coverage" + CACHE INTERNAL "") +if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path) + if(HAVE_fprofile_abs_path) + set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path") + endif() +endif() + +set(CMAKE_Fortran_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the Fortran compiler during coverage builds." + FORCE ) +set(CMAKE_CXX_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C++ compiler during coverage builds." + FORCE ) +set(CMAKE_C_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C compiler during coverage builds." + FORCE ) +set(CMAKE_EXE_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used for linking binaries during coverage builds." + FORCE ) +set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used by the shared libraries linker during coverage builds." + FORCE ) +mark_as_advanced( + CMAKE_Fortran_FLAGS_COVERAGE + CMAKE_CXX_FLAGS_COVERAGE + CMAKE_C_FLAGS_COVERAGE + CMAKE_EXE_LINKER_FLAGS_COVERAGE + CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) + +get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)) + message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") +endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG) + +if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + link_libraries(gcov) +endif() + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_lcov( +# NAME testrunner_coverage # New target name +# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES testrunner # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# NO_DEMANGLE # Don't demangle C++ symbols +# # even if c++filt is found +# ) +function(setup_target_for_coverage_lcov) + + set(options NO_DEMANGLE SONARQUBE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT LCOV_PATH) + message(FATAL_ERROR "lcov not found! Aborting...") + endif() # NOT LCOV_PATH + + if(NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif() # NOT GENHTML_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(DEFINED Coverage_BASE_DIRECTORY) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(LCOV_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND LCOV_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES LCOV_EXCLUDES) + + # Conditional arguments + if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) + set(GENHTML_EXTRA_ARGS "--demangle-cpp") + endif() + + # Setting up commands which will be run to generate coverage data. + # Cleanup lcov + set(LCOV_CLEAN_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory . + -b ${BASEDIR} --zerocounters + ) + # Create baseline to make sure untouched files show up in the report + set(LCOV_BASELINE_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b + ${BASEDIR} -o ${Coverage_NAME}.base + ) + # Run tests + set(LCOV_EXEC_TESTS_CMD + ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + ) + # Capturing lcov counters and generating report + set(LCOV_CAPTURE_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b + ${BASEDIR} --capture --output-file ${Coverage_NAME}.capture + ) + # add baseline counters + set(LCOV_BASELINE_COUNT_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base + -a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total + ) + # filter collected data to final coverage report + set(LCOV_FILTER_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove + ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info + ) + # Generate HTML output + set(LCOV_GEN_HTML_CMD + ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o + ${Coverage_NAME} ${Coverage_NAME}.info + ) + if(${Coverage_SONARQUBE}) + # Generate SonarQube output + set(GCOVR_XML_CMD + ${GCOVR_PATH} --sonarqube ${Coverage_NAME}_sonarqube.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} + ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} + ) + set(GCOVR_XML_CMD_COMMAND + COMMAND ${GCOVR_XML_CMD} + ) + set(GCOVR_XML_CMD_BYPRODUCTS ${Coverage_NAME}_sonarqube.xml) + set(GCOVR_XML_CMD_COMMENT COMMENT "SonarQube code coverage info report saved in ${Coverage_NAME}_sonarqube.xml.") + endif() + + + if(CODE_COVERAGE_VERBOSE) + message(STATUS "Executed command report") + message(STATUS "Command to clean up lcov: ") + string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}") + message(STATUS "${LCOV_CLEAN_CMD_SPACED}") + + message(STATUS "Command to create baseline: ") + string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}") + message(STATUS "${LCOV_BASELINE_CMD_SPACED}") + + message(STATUS "Command to run the tests: ") + string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}") + message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}") + + message(STATUS "Command to capture counters and generate report: ") + string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}") + message(STATUS "${LCOV_CAPTURE_CMD_SPACED}") + + message(STATUS "Command to add baseline counters: ") + string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}") + message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}") + + message(STATUS "Command to filter collected data: ") + string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}") + message(STATUS "${LCOV_FILTER_CMD_SPACED}") + + message(STATUS "Command to generate lcov HTML output: ") + string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}") + message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}") + + if(${Coverage_SONARQUBE}) + message(STATUS "Command to generate SonarQube XML output: ") + string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}") + message(STATUS "${GCOVR_XML_CMD_SPACED}") + endif() + endif() + + # Setup target + add_custom_target(${Coverage_NAME} + COMMAND ${LCOV_CLEAN_CMD} + COMMAND ${LCOV_BASELINE_CMD} + COMMAND ${LCOV_EXEC_TESTS_CMD} + COMMAND ${LCOV_CAPTURE_CMD} + COMMAND ${LCOV_BASELINE_COUNT_CMD} + COMMAND ${LCOV_FILTER_CMD} + COMMAND ${LCOV_GEN_HTML_CMD} + ${GCOVR_XML_CMD_COMMAND} + + # Set output files as GENERATED (will be removed on 'make clean') + BYPRODUCTS + ${Coverage_NAME}.base + ${Coverage_NAME}.capture + ${Coverage_NAME}.total + ${Coverage_NAME}.info + ${GCOVR_XML_CMD_BYPRODUCTS} + ${Coverage_NAME}/index.html + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." + ) + + # Show where to find the lcov info report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info." + ${GCOVR_XML_CMD_COMMENT} + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # setup_target_for_coverage_lcov + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_gcovr_xml( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# ) +# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the +# GCVOR command. +function(setup_target_for_coverage_gcovr_xml) + + set(options NONE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(DEFINED Coverage_BASE_DIRECTORY) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(GCOVR_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES GCOVR_EXCLUDES) + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDE_ARGS "") + foreach(EXCLUDE ${GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDE_ARGS "-e") + list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") + endforeach() + + # Set up commands which will be run to generate coverage data + # Run tests + set(GCOVR_XML_EXEC_TESTS_CMD + ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + ) + # Running gcovr + set(GCOVR_XML_CMD + ${GCOVR_PATH} --xml ${Coverage_NAME}.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} + ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} + ) + + if(CODE_COVERAGE_VERBOSE) + message(STATUS "Executed command report") + + message(STATUS "Command to run tests: ") + string(REPLACE ";" " " GCOVR_XML_EXEC_TESTS_CMD_SPACED "${GCOVR_XML_EXEC_TESTS_CMD}") + message(STATUS "${GCOVR_XML_EXEC_TESTS_CMD_SPACED}") + + message(STATUS "Command to generate gcovr XML coverage data: ") + string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}") + message(STATUS "${GCOVR_XML_CMD_SPACED}") + endif() + + add_custom_target(${Coverage_NAME} + COMMAND ${GCOVR_XML_EXEC_TESTS_CMD} + COMMAND ${GCOVR_XML_CMD} + + BYPRODUCTS ${Coverage_NAME}.xml + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Running gcovr to produce Cobertura code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml." + ) +endfunction() # setup_target_for_coverage_gcovr_xml + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_gcovr_html( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# ) +# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the +# GCVOR command. +function(setup_target_for_coverage_gcovr_html) + + set(options NONE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(DEFINED Coverage_BASE_DIRECTORY) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(GCOVR_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES GCOVR_EXCLUDES) + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDE_ARGS "") + foreach(EXCLUDE ${GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDE_ARGS "-e") + list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") + endforeach() + + # Set up commands which will be run to generate coverage data + # Run tests + set(GCOVR_HTML_EXEC_TESTS_CMD + ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + ) + # Create folder + set(GCOVR_HTML_FOLDER_CMD + ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME} + ) + # Running gcovr + set(GCOVR_HTML_CMD + ${GCOVR_PATH} --html ${Coverage_NAME}/index.html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} + ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} + ) + + if(CODE_COVERAGE_VERBOSE) + message(STATUS "Executed command report") + + message(STATUS "Command to run tests: ") + string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}") + message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}") + + message(STATUS "Command to create a folder: ") + string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}") + message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}") + + message(STATUS "Command to generate gcovr HTML coverage data: ") + string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}") + message(STATUS "${GCOVR_HTML_CMD_SPACED}") + endif() + + add_custom_target(${Coverage_NAME} + COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD} + COMMAND ${GCOVR_HTML_FOLDER_CMD} + COMMAND ${GCOVR_HTML_CMD} + + BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Running gcovr to produce HTML code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # setup_target_for_coverage_gcovr_html + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_fastcov( +# NAME testrunner_coverage # New target name +# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES testrunner # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude. +# NO_DEMANGLE # Don't demangle C++ symbols +# # even if c++filt is found +# SKIP_HTML # Don't create html report +# POST_CMD perl -i -pe s!${PROJECT_SOURCE_DIR}/!!g ctest_coverage.json # E.g. for stripping source dir from file paths +# ) +function(setup_target_for_coverage_fastcov) + + set(options NO_DEMANGLE SKIP_HTML) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS POST_CMD) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT FASTCOV_PATH) + message(FATAL_ERROR "fastcov not found! Aborting...") + endif() + + if(NOT Coverage_SKIP_HTML AND NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif() + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(Coverage_BASE_DIRECTORY) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (Patterns, not paths, for fastcov) + set(FASTCOV_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES}) + list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES FASTCOV_EXCLUDES) + + # Conditional arguments + if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) + set(GENHTML_EXTRA_ARGS "--demangle-cpp") + endif() + + # Set up commands which will be run to generate coverage data + set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}) + + set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH} + --search-directory ${BASEDIR} + --process-gcno + --output ${Coverage_NAME}.json + --exclude ${FASTCOV_EXCLUDES} + ) + + set(FASTCOV_CONVERT_CMD ${FASTCOV_PATH} + -C ${Coverage_NAME}.json --lcov --output ${Coverage_NAME}.info + ) + + if(Coverage_SKIP_HTML) + set(FASTCOV_HTML_CMD ";") + else() + set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} + -o ${Coverage_NAME} ${Coverage_NAME}.info + ) + endif() + + set(FASTCOV_POST_CMD ";") + if(Coverage_POST_CMD) + set(FASTCOV_POST_CMD ${Coverage_POST_CMD}) + endif() + + if(CODE_COVERAGE_VERBOSE) + message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):") + + message(" Running tests:") + string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}") + message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}") + + message(" Capturing fastcov counters and generating report:") + string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}") + message(" ${FASTCOV_CAPTURE_CMD_SPACED}") + + message(" Converting fastcov .json to lcov .info:") + string(REPLACE ";" " " FASTCOV_CONVERT_CMD_SPACED "${FASTCOV_CONVERT_CMD}") + message(" ${FASTCOV_CONVERT_CMD_SPACED}") + + if(NOT Coverage_SKIP_HTML) + message(" Generating HTML report: ") + string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}") + message(" ${FASTCOV_HTML_CMD_SPACED}") + endif() + if(Coverage_POST_CMD) + message(" Running post command: ") + string(REPLACE ";" " " FASTCOV_POST_CMD_SPACED "${FASTCOV_POST_CMD}") + message(" ${FASTCOV_POST_CMD_SPACED}") + endif() + endif() + + # Setup target + add_custom_target(${Coverage_NAME} + + # Cleanup fastcov + COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH} + --search-directory ${BASEDIR} + --zerocounters + + COMMAND ${FASTCOV_EXEC_TESTS_CMD} + COMMAND ${FASTCOV_CAPTURE_CMD} + COMMAND ${FASTCOV_CONVERT_CMD} + COMMAND ${FASTCOV_HTML_CMD} + COMMAND ${FASTCOV_POST_CMD} + + # Set output files as GENERATED (will be removed on 'make clean') + BYPRODUCTS + ${Coverage_NAME}.info + ${Coverage_NAME}.json + ${Coverage_NAME}/index.html # report directory + + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report." + ) + + set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info and ${Coverage_NAME}.json.") + if(NOT Coverage_SKIP_HTML) + string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.") + endif() + # Show where to find the fastcov info report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG} + ) + +endfunction() # setup_target_for_coverage_fastcov + +function(append_coverage_compiler_flags) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") +endfunction() # append_coverage_compiler_flags + +# Setup coverage for specific library +function(append_coverage_compiler_flags_to_target name) + separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}") + target_compile_options(${name} PRIVATE ${_flag_list}) + if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + target_link_libraries(${name} PRIVATE gcov) + endif() +endfunction() \ No newline at end of file diff --git a/tools/BuildTools/cmake/Modules/CompilerWarnings.cmake b/tools/BuildTools/cmake/Modules/CompilerWarnings.cmake new file mode 100644 index 0000000..e4b717b --- /dev/null +++ b/tools/BuildTools/cmake/Modules/CompilerWarnings.cmake @@ -0,0 +1,129 @@ +# cmake-format: off +# ============================================================================= +# Copyright (C) lefticus +# +# See Also: https://github.com/lefticus +# See Also: https://github.com/cpp-best-practices/cpp_starter_project +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to +# ----------------------------------------------------------------------------- +# See Also: +# github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md +# ============================================================================= +# cmake-format: on +include_guard(GLOBAL) + +function(set_project_warnings project_name) + option(WARNINGS_AS_ERRORS "Treat compiler warnings as errors" ON) + + set(MSVC_WARNINGS + /W4 # Baseline reasonable warnings + /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss + # of data + /w14254 # 'operator': conversion from 'type1:field_bits' to + # 'type2:field_bits', possible loss of data + /w14263 # 'function': member function does not override any base class + # virtual member function + /w14265 # 'classname': class has virtual functions, but destructor is not + # virtual instances of this class may not be destructed correctly + /w14287 # 'operator': unsigned/negative constant mismatch + /we4289 # nonstandard extension used: 'variable': loop control variable + # declared in the for-loop is used outside the for-loop scope + /w14296 # 'operator': expression is always 'boolean_value' + /w14311 # 'variable': pointer truncation from 'type1' to 'type2' + /w14545 # expression before comma evaluates to a function which is missing + # an argument list + /w14546 # function call before comma missing argument list + /w14547 # 'operator': operator before comma has no effect; expected + # operator with side-effect + /w14549 # 'operator': operator before comma has no effect; did you intend + # 'operator'? + /w14555 # expression has no effect; expected expression with side- effect + /w14619 # pragma warning: there is no warning number 'number' + /w14640 # Enable warning on thread un-safe static member initialization + /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may + # cause unexpected runtime behavior. + /w14905 # wide string literal cast to 'LPSTR' + /w14906 # string literal cast to 'LPWSTR' + /w14928 # illegal copy-initialization; more than one user-defined + # conversion has been implicitly applied + /permissive- # standards conformance mode for MSVC compiler. + ) + + set(CLANG_WARNINGS + -Wall + -Wextra # reasonable and standard + -Wshadow # warn the user if a variable declaration shadows one from a + # parent context + -Wnon-virtual-dtor # warn the user if a class with virtual functions has a + # non-virtual destructor. This helps catch hard to + # track down memory errors + -Wold-style-cast # warn for c-style casts + -Wcast-align # warn for potential performance problem casts + -Wunused # warn on anything being unused + -Woverloaded-virtual # warn if you overload (not override) a virtual + # function + -Wpedantic # warn if non-standard C++ is used + -Wconversion # warn on type conversions that may lose data + -Wsign-conversion # warn on sign conversions + -Wnull-dereference # warn if a null dereference is detected + -Wdouble-promotion # warn if float is implicit promoted to double + -Wformat=2 # warn on security issues around functions that format output + # (ie printf) + -Wimplicit-fallthrough # warn on statements that fallthrough without an + # explicit annotation + ) + + if(WARNINGS_AS_ERRORS) + set(CLANG_WARNINGS ${CLANG_WARNINGS} -Werror) + set(MSVC_WARNINGS ${MSVC_WARNINGS} /WX) + endif() + + set(GCC_WARNINGS + ${CLANG_WARNINGS} + -Wmisleading-indentation # warn if indentation implies blocks where blocks + # do not exist + -Wduplicated-cond # warn if if / else chain has duplicated conditions + -Wduplicated-branches # warn if if / else branches have duplicated code + -Wlogical-op # warn about logical operations being used where bitwise were + # probably wanted + -Wuseless-cast # warn if you perform a cast to the same type + ) + + if(MSVC) + set(PROJECT_WARNINGS ${MSVC_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(PROJECT_WARNINGS ${CLANG_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(PROJECT_WARNINGS ${GCC_WARNINGS}) + else() + log_warn( + "No compiler warnings set for '${CMAKE_CXX_COMPILER_ID}' compiler.") + endif() + + target_compile_options(${project_name} INTERFACE ${PROJECT_WARNINGS}) + +endfunction() diff --git a/tools/BuildTools/cmake/Modules/Doxygen.cmake b/tools/BuildTools/cmake/Modules/Doxygen.cmake new file mode 100644 index 0000000..8fa87d1 --- /dev/null +++ b/tools/BuildTools/cmake/Modules/Doxygen.cmake @@ -0,0 +1,52 @@ +# cmake-format: off +# ============================================================================= +# Copyright (C) lefticus +# +# See Also: https://github.com/lefticus +# See Also: https://github.com/cpp-best-practices/cpp_starter_project +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to +# ============================================================================= +# cmake-format: on +include_guard(GLOBAL) + +function(enable_doxygen) + option(ENABLE_DOXYGEN "Enable doxygen doc builds of source" ON) + if(ENABLE_DOXYGEN) + set(DOXYGEN_CALLER_GRAPH YES) + set(DOXYGEN_CALL_GRAPH YES) + set(DOXYGEN_EXTRACT_ALL YES) + set(DOXYGEN_SOURCE_BROWSER YES) + set(DOXYGEN_INLINE_SOURCSE YES) + set(DOXYGEN_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/www/docs) + find_package(Doxygen REQUIRED dot OPTIONAL_COMPONENTS mscgen dia) + doxygen_add_docs( + doxygen-docs ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/lib + ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests + ) + + endif() +endfunction() diff --git a/tools/BuildTools/cmake/Modules/FetchAndInstall.cmake b/tools/BuildTools/cmake/Modules/FetchAndInstall.cmake new file mode 100644 index 0000000..c07bc4e --- /dev/null +++ b/tools/BuildTools/cmake/Modules/FetchAndInstall.cmake @@ -0,0 +1,103 @@ +include_guard(GLOBAL) + +include(FetchContent) +function(fetch_and_install) + + set(options SYSTEM_INSTALL_) + set(oneValueArgs NAME_ REPO_ TAG_ INSTALL_FOLDER_) + set(multiValueArgs OPTIONS_) + + cmake_parse_arguments(FETCH_AND_INSTALL "${options}" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}") + + if(NOT FETCH_AND_INSTALL_NAME_) + log_fatal("Missing dependency name") + endif() + if(NOT FETCH_AND_INSTALL_REPO_) + log_fatal("Missing dependency repository") + endif() + if(NOT FETCH_AND_INSTALL_TAG_) + log_fatal("Missing dependency tag") + endif() + if(NOT FETCH_AND_INSTALL_INSTALL_FOLDER_) + log_fatal("Missing dependency installation folder") + endif() + + log_info("Declaring ${FETCH_AND_INSTALL_NAME_} ...") + FetchContent_Declare( + ${FETCH_AND_INSTALL_NAME_} + GIT_REPOSITORY ${FETCH_AND_INSTALL_REPO_} + GIT_TAG ${FETCH_AND_INSTALL_TAG_} + ) + log_info("Declaring ${FETCH_AND_INSTALL_NAME_} -- DONE") + + log_info("Populating ${FETCH_AND_INSTALL_NAME_} ...") + FetchContent_Populate(${FETCH_AND_INSTALL_NAME_}) + set(OLD_PROJECT_SOURCE_DIR ${PROJECT_SOURCE_DIR}) + set(PROJECT_SOURCE_DIR ${PROJECT_BINARY_DIR}/_deps/${FETCH_AND_INSTALL_NAME_}-src) + log_info("Populating ${FETCH_AND_INSTALL_NAME_} -- DONE") + + if(MSVC) + log_info("Configuring ${FETCH_AND_INSTALL_NAME_} ...") + execute_process( + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${CMAKE_COMMAND} -S . -B ${PROJECT_SOURCE_DIR}/build/Windows -A + x64 -DBUILD_SHARED_LIBS:BOOL=ON ${FETCH_AND_INSTALL_OPTIONS_} + ) + log_info("Configuring ${FETCH_AND_INSTALL_NAME_} -- DONE") + log_info("Building ${FETCH_AND_INSTALL_NAME_} [DEBUG] ...") + execute_process( + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${CMAKE_COMMAND} --build ${PROJECT_SOURCE_DIR}/build/Windows + --config Debug + ) + log_info("Building ${FETCH_AND_INSTALL_NAME_} [DEBUG] -- DONE") + log_info("Installing ${FETCH_AND_INSTALL_NAME_} [DEBUG] ...") + execute_process( + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} --install ${PROJECT_SOURCE_DIR}/build/Windows --config Debug --prefix + ${FETCH_AND_INSTALL_INSTALL_FOLDER_}/Debug + ) + log_info("Installing ${FETCH_AND_INSTALL_NAME_} [DEBUG] -- DONE") + log_info("Building ${FETCH_AND_INSTALL_NAME_} [RELEASE] ...") + execute_process( + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${CMAKE_COMMAND} --build ${PROJECT_SOURCE_DIR}/build/Windows + --config Release + ) + log_info("Building ${FETCH_AND_INSTALL_NAME_} [RELEASE] -- DONE") + log_info("Installing ${FETCH_AND_INSTALL_NAME_} [RELEASE] ...") + execute_process( + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} --install ${PROJECT_SOURCE_DIR}/build/Windows --config Release --prefix + ${FETCH_AND_INSTALL_INSTALL_FOLDER_}/Release + ) + log_info("Installing ${FETCH_AND_INSTALL_NAME_} [RELEASE] -- DONE") + else() + log_info("Configuring ${FETCH_AND_INSTALL_NAME_} [DEBUG] ...") + execute_process( + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND + ${CMAKE_COMMAND} -S . -B ${PROJECT_SOURCE_DIR}/build/Linux/Debug -A x64 -DCMAKE_BUILD_TYPE:STRING=Debug + -DBUILD_SHARED_LIBS:BOOL=ON -DCMAKE_INSTALL_PREFIX:STRING=${FETCH_AND_INSTALL_INSTALL_FOLDER_}/Debug + ${FETCH_AND_INSTALL_OPTIONS_} + ) + execute_process( + log_info ("Configuring ${FETCH_AND_INSTALL_NAME_} [DEBUG] -- DONE") log_info + ("Configuring ${FETCH_AND_INSTALL_NAME_} [RELEASE] ...") + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND + ${CMAKE_COMMAND} -S . -B ${PROJECT_SOURCE_DIR}/build/Linux/Release -A x64 -DCMAKE_BUILD_TYPE:STRING=Release + -DBUILD_SHARED_LIBS:BOOL=ON -DCMAKE_INSTALL_PREFIX:STRING=${FETCH_AND_INSTALL_INSTALL_FOLDER_}/Release + ${FETCH_AND_INSTALL_OPTIONS_} + ) + log_info("Configuring ${FETCH_AND_INSTALL_NAME_} [RELEASE] -- DONE") + log_info("Installing ${FETCH_AND_INSTALL_NAME_} ...") + execute_process( + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${CMAKE_COMMAND} --build ${PROJECT_SOURCE_DIR}/build/Linux/Debug + --target install --config Debug + ) + execute_process( + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} --build ${PROJECT_SOURCE_DIR}/build/Linux/Release --target install --config Release + ) + endif() + log_info("Installing ${FETCH_AND_INSTALL_NAME_} -- DONE") + set(PROJECT_SOURCE_DIR ${OLD_PROJECT_SOURCE_DIR}) +endfunction() diff --git a/tools/BuildTools/cmake/Modules/FetchCatch.cmake b/tools/BuildTools/cmake/Modules/FetchCatch.cmake new file mode 100644 index 0000000..94b20ed --- /dev/null +++ b/tools/BuildTools/cmake/Modules/FetchCatch.cmake @@ -0,0 +1,35 @@ +include_guard(GLOBAL) + +function(include_catch) + log_info("Configuring Catch") + find_package(Catch2 3 QUIET) + if(NOT Catch2_FOUND) + if(NOT EXISTS ${CMAKE_BINARY_DIR}/_deps/Catch2-src) + log_warn("Configuring Catch2 -- NOT FOUND") + log_info("Downloading into ${CMAKE_BINARY_DIR}/_deps") + FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG ab6c7375be9a8e71ee84c6f8537113f9f47daf99 # v3.2.1 + ) + FetchContent_MakeAvailable(Catch2) + else() + log_warn("Configuring Catch -- FOUND in ${CMAKE_BINARY_DIR}/_deps") + add_subdirectory(${CMAKE_BINARY_DIR}/_deps/catch2-src) + endif() + log_warn("Appending Catch2 modules to CMAKE_MODULE_PATH") + # Only necessary if using FetchContent + list(APPEND CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}/_deps/catch2-src/extras) + set(CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + PARENT_SCOPE + ) + log_info("You might want to try installing on your system") + add_library(KKSK::Catch2 ALIAS Catch2) + add_library(KKSK::Catch2WithMain ALIAS Catch2WithMain) + else() + add_library(KKSK::Catch2 ALIAS Catch2) + add_library(KKSK::Catch2WithMain ALIAS Catch2WithMain) + endif(NOT Catch2_FOUND) + log_info("Configuring Catch -- done") +endfunction() diff --git a/tools/BuildTools/cmake/Modules/FetchGSL.cmake b/tools/BuildTools/cmake/Modules/FetchGSL.cmake new file mode 100644 index 0000000..304f482 --- /dev/null +++ b/tools/BuildTools/cmake/Modules/FetchGSL.cmake @@ -0,0 +1,27 @@ +include_guard(GLOBAL) + +function(include_gsl) + log_info("Configuring GSL") + find_package(Microsoft.GSL CONFIG QUIET) + if(NOT Microsoft.GSL_FOUND) + if(NOT EXISTS ${CMAKE_BINARY_DIR}/_deps/GSL-src) + log_warn("Configuring GSL -- NOT FOUND") + log_info("Downloading into ${CMAKE_BINARY_DIR}/_deps") + FetchContent_Declare( + GSL + GIT_REPOSITORY "https://github.com/microsoft/GSL" + GIT_TAG "v4.0.0" + GIT_SHALLOW ON + ) + FetchContent_MakeAvailable(GSL) + else() + log_warn("Configuring GSL -- FOUND in ${CMAKE_BINARY_DIR}/_deps") + add_subdirectory(${CMAKE_BINARY_DIR}/_deps/GSL-src) + endif() + log_info("You might want to try installing on your system") + add_library(KKSK::GSL ALIAS GSL) + else() + add_library(KKSK::GSL ALIAS GSL) + endif() + log_info("Configuring GSL -- done") +endfunction() diff --git a/tools/BuildTools/cmake/Modules/FetchGTest.cmake b/tools/BuildTools/cmake/Modules/FetchGTest.cmake new file mode 100644 index 0000000..da55e27 --- /dev/null +++ b/tools/BuildTools/cmake/Modules/FetchGTest.cmake @@ -0,0 +1,30 @@ +include_guard(GLOBAL) + +function(include_gtest) + log_info("Configuring Google Test") + find_package(Threads REQUIRED) + find_package(GTest) + if(NOT GTest_FOUND) + if(NOT EXISTS ${CMAKE_BINARY_DIR}/_deps/googletest-src) + log_warn("Configuring Google Test -- NOT FOUND") + log_info("Downloading into ${CMAKE_BINARY_DIR}/_deps") + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG 58d77fa8070e8cec2dc1ed015d66b454c8d78850 # release-1.12.1 + ) + FetchContent_MakeAvailable(googletest) + else() + log_warn("Configuring Google Test -- FOUND in ${CMAKE_BINARY_DIR}/_deps") + add_subdirectory(${CMAKE_BINARY_DIR}/_deps/googletest-src) + endif() + log_info("You might want to try installing on your system") + add_library(KKSK::GTest ALIAS gtest) + add_library(KKSK::GMock ALIAS gmock) + else() + add_library(KKSK::GTest ALIAS GTest::gtest) + add_library(KKSK::GMock ALIAS GTest::gmock) + endif(NOT GTest_FOUND) + include(GoogleTest) + log_info("Configuring Google Test -- done") +endfunction() diff --git a/tools/BuildTools/cmake/Modules/Filesystem.cmake b/tools/BuildTools/cmake/Modules/Filesystem.cmake new file mode 100644 index 0000000..47f877b --- /dev/null +++ b/tools/BuildTools/cmake/Modules/Filesystem.cmake @@ -0,0 +1,33 @@ +include_guard(GLOBAL) + +function(get_all_objects_in directory return_value) + log_trace("get_all_objects_in") + if(IS_DIRECTORY ${directory}) + log_trace("${directory} is a directory") + file(GLOB objects ${directory}/*) + log_debug("objects: ${objects}") + set(${return_value} + ${objects} + PARENT_SCOPE + ) + else() + log_error("${directory} is not a directory") + endif() +endfunction() + +function(get_all_subdirectories_in directory return_value) + log_trace("get_all_subdirectories_in") + get_all_objects_in(${directory} objects) + log_debug("objects: ${objects}") + set(subdirs "") + foreach(object ${objects}) + if(IS_DIRECTORY ${object}) + list(APPEND subdirs ${object}) + endif() + endforeach() + log_debug("subdirs: ${subdirs}") + set(${return_value} + ${subdirs} + PARENT_SCOPE + ) +endfunction() diff --git a/tools/BuildTools/cmake/Modules/HostSystemInformation.cmake b/tools/BuildTools/cmake/Modules/HostSystemInformation.cmake new file mode 100644 index 0000000..45351f6 --- /dev/null +++ b/tools/BuildTools/cmake/Modules/HostSystemInformation.cmake @@ -0,0 +1,92 @@ +# Provides an include guard for the file currently being processed by CMake. +include_guard(GLOBAL) + +# +cmake_host_system_information(RESULT _NUMBER_OF_LOGICAL_CORES_RESULT QUERY NUMBER_OF_LOGICAL_CORES) +cmake_host_system_information(RESULT _NUMBER_OF_PHYSICAL_CORES_RESULT QUERY NUMBER_OF_PHYSICAL_CORES) +cmake_host_system_information(RESULT _HOSTNAME_RESULT QUERY HOSTNAME) +cmake_host_system_information(RESULT _FQDN_RESULT QUERY FQDN) +cmake_host_system_information(RESULT _TOTAL_VIRTUAL_MEMORY_RESULT QUERY TOTAL_VIRTUAL_MEMORY) +cmake_host_system_information(RESULT _AVAILABLE_VIRTUAL_MEMORY_RESULT QUERY AVAILABLE_VIRTUAL_MEMORY) +cmake_host_system_information(RESULT _TOTAL_PHYSICAL_MEMORY_RESULT QUERY TOTAL_PHYSICAL_MEMORY) +cmake_host_system_information(RESULT _AVAILABLE_PHYSICAL_MEMORY_RESULT QUERY AVAILABLE_PHYSICAL_MEMORY) +cmake_host_system_information(RESULT _IS_64BIT_RESULT QUERY IS_64BIT) +cmake_host_system_information(RESULT _HAS_FPU_RESULT QUERY HAS_FPU) +cmake_host_system_information(RESULT _HAS_MMX_RESULT QUERY HAS_MMX) +cmake_host_system_information(RESULT _HAS_MMX_PLUS_RESULT QUERY HAS_MMX_PLUS) +cmake_host_system_information(RESULT _HAS_SSE_RESULT QUERY HAS_SSE) +cmake_host_system_information(RESULT _HAS_SSE2_RESULT QUERY HAS_SSE2) +cmake_host_system_information(RESULT _HAS_SSE_FP_RESULT QUERY HAS_SSE_FP) +cmake_host_system_information(RESULT _HAS_SSE_MMX_RESULT QUERY HAS_SSE_MMX) +cmake_host_system_information(RESULT _HAS_AMD_3DNOW_RESULT QUERY HAS_AMD_3DNOW) +cmake_host_system_information(RESULT _HAS_AMD_3DNOW_PLUS_RESULT QUERY HAS_AMD_3DNOW_PLUS) +cmake_host_system_information(RESULT _HAS_IA64_RESULT QUERY HAS_IA64) +cmake_host_system_information(RESULT _HAS_SERIAL_NUMBER_RESULT QUERY HAS_SERIAL_NUMBER) +cmake_host_system_information(RESULT _PROCESSOR_SERIAL_NUMBER_RESULT QUERY PROCESSOR_SERIAL_NUMBER) +cmake_host_system_information(RESULT _PROCESSOR_NAME_RESULT QUERY PROCESSOR_NAME) +cmake_host_system_information(RESULT _PROCESSOR_DESCRIPTION_RESULT QUERY PROCESSOR_DESCRIPTION) +cmake_host_system_information(RESULT _OS_NAME_RESULT QUERY OS_NAME) +cmake_host_system_information(RESULT _OS_RELEASE_RESULT QUERY OS_RELEASE) +cmake_host_system_information(RESULT _OS_VERSION_RESULT QUERY OS_VERSION) +cmake_host_system_information(RESULT _OS_PLATFORM_RESULT QUERY OS_PLATFORM) + +if(NOT PROJECT_IS_TOP_LEVEL) + set(NUMBER_OF_LOGICAL_CORES_RESULT ${_NUMBER_OF_LOGICAL_CORES_RESULT} PARENT_SCOPE) + set(NUMBER_OF_PHYSICAL_CORES_RESULT ${_NUMBER_OF_PHYSICAL_CORES_RESULT} PARENT_SCOPE) + set(HOSTNAME_RESULT ${_HOSTNAME_RESULT} PARENT_SCOPE) + set(FQDN_RESULT ${_FQDN_RESULT} PARENT_SCOPE) + set(TOTAL_VIRTUAL_MEMORY_RESULT ${_TOTAL_VIRTUAL_MEMORY_RESULT} PARENT_SCOPE) + set(AVAILABLE_VIRTUAL_MEMORY_RESULT ${_AVAILABLE_VIRTUAL_MEMORY_RESULT} PARENT_SCOPE) + set(TOTAL_PHYSICAL_MEMORY_RESULT ${_TOTAL_PHYSICAL_MEMORY_RESULT} PARENT_SCOPE) + set(AVAILABLE_PHYSICAL_MEMORY_RESULT ${_AVAILABLE_PHYSICAL_MEMORY_RESULT} PARENT_SCOPE) + set(IS_64BIT_RESULT ${_IS_64BIT_RESULT} PARENT_SCOPE) + set(HAS_FPU_RESULT ${_HAS_FPU_RESULT} PARENT_SCOPE) + set(HAS_MMX_RESULT ${_HAS_MMX_RESULT} PARENT_SCOPE) + set(HAS_MMX_PLUS_RESULT ${_HAS_MMX_PLUS_RESULT} PARENT_SCOPE) + set(HAS_SSE_RESULT ${_HAS_SSE_RESULT} PARENT_SCOPE) + set(HAS_SSE2_RESULT ${_HAS_SSE2_RESULT} PARENT_SCOPE) + set(HAS_SSE_FP_RESULT ${_HAS_SSE_FP_RESULT} PARENT_SCOPE) + set(HAS_SSE_MMX_RESULT ${_HAS_SSE_MMX_RESULT} PARENT_SCOPE) + set(HAS_AMD_3DNOW_RESULT ${_HAS_AMD_3DNOW_RESULT} PARENT_SCOPE) + set(HAS_AMD_3DNOW_PLUS_RESULT ${_HAS_AMD_3DNOW_PLUS_RESULT} PARENT_SCOPE) + set(HAS_IA64_RESULT ${_HAS_IA64_RESULT} PARENT_SCOPE) + set(HAS_SERIAL_NUMBER_RESULT ${_HAS_SERIAL_NUMBER_RESULT} PARENT_SCOPE) + set(PROCESSOR_SERIAL_NUMBER_RESULT ${_PROCESSOR_SERIAL_NUMBER_RESULT} PARENT_SCOPE) + set(PROCESSOR_NAME_RESULT ${_PROCESSOR_NAME_RESULT} PARENT_SCOPE) + set(PROCESSOR_DESCRIPTION_RESULT ${_PROCESSOR_DESCRIPTION_RESULT} PARENT_SCOPE) + set(OS_NAME_RESULT ${_OS_NAME_RESULT} PARENT_SCOPE) + set(OS_RELEASE_RESULT ${_OS_RELEASE_RESULT} PARENT_SCOPE) + set(OS_VERSION_RESULT ${_OS_VERSION_RESULT} PARENT_SCOPE) + set(OS_PLATFORM_RESULT ${_OS_PLATFORM_RESULT} PARENT_SCOPE) +endif() + +if(${CMAKE_CURRENT_LOG_LEVEL} LESS CMAKE_LOG_LEVEL_INFO) + include(CMakePrintHelpers) + cmake_print_variables(_NUMBER_OF_LOGICAL_CORES_RESULT) + cmake_print_variables(_NUMBER_OF_PHYSICAL_CORES_RESULT) + cmake_print_variables(_HOSTNAME_RESULT) + cmake_print_variables(_FQDN_RESULT) + cmake_print_variables(_TOTAL_VIRTUAL_MEMORY_RESULT) + cmake_print_variables(_AVAILABLE_VIRTUAL_MEMORY_RESULT) + cmake_print_variables(_TOTAL_PHYSICAL_MEMORY_RESULT) + cmake_print_variables(_AVAILABLE_PHYSICAL_MEMORY_RESULT) + cmake_print_variables(_IS_64BIT_RESULT) + cmake_print_variables(_HAS_FPU_RESULT) + cmake_print_variables(_HAS_MMX_RESULT) + cmake_print_variables(_HAS_MMX_PLUS_RESULT) + cmake_print_variables(_HAS_SSE_RESULT) + cmake_print_variables(_HAS_SSE2_RESULT) + cmake_print_variables(_HAS_SSE_FP_RESULT) + cmake_print_variables(_HAS_SSE_MMX_RESULT) + cmake_print_variables(_HAS_AMD_3DNOW_RESULT) + cmake_print_variables(_HAS_AMD_3DNOW_PLUS_RESULT) + cmake_print_variables(_HAS_IA64_RESULT) + cmake_print_variables(_HAS_SERIAL_NUMBER_RESULT) + cmake_print_variables(_PROCESSOR_SERIAL_NUMBER_RESULT) + cmake_print_variables(_PROCESSOR_NAME_RESULT) + cmake_print_variables(_PROCESSOR_DESCRIPTION_RESULT) + cmake_print_variables(_OS_NAME_RESULT) + cmake_print_variables(_OS_RELEASE_RESULT) + cmake_print_variables(_OS_VERSION_RESULT) + cmake_print_variables(_OS_PLATFORM_RESULT) +endif() diff --git a/tools/BuildTools/cmake/Modules/Linker.cmake b/tools/BuildTools/cmake/Modules/Linker.cmake new file mode 100644 index 0000000..095aca3 --- /dev/null +++ b/tools/BuildTools/cmake/Modules/Linker.cmake @@ -0,0 +1,65 @@ +# cmake-format: off +# ============================================================================= +# Copyright (C) lefticus +# +# See Also: https://github.com/lefticus +# See Also: https://github.com/cpp-best-practices/cpp_starter_project +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to +# ============================================================================= +# cmake-format: on +include_guard(GLOBAL) + +option(ENABLE_USER_LINKER "Enable a specific linker if available" OFF) + +include(CheckCXXCompilerFlag) + +set(USER_LINKER_OPTION + "lld" + CACHE STRING "Linker to be used" +) +set(USER_LINKER_OPTION_VALUES "lld" "gold" "bfd") +set_property(CACHE USER_LINKER_OPTION PROPERTY STRINGS ${USER_LINKER_OPTION_VALUES}) +list(FIND USER_LINKER_OPTION_VALUES ${USER_LINKER_OPTION} USER_LINKER_OPTION_INDEX) + +if(${USER_LINKER_OPTION_INDEX} EQUAL -1) + log_info( + "Using custom linker: '${USER_LINKER_OPTION}', explicitly supported entries are ${USER_LINKER_OPTION_VALUES}" + ) +endif() + +function(configure_linker project_name) + if(NOT ENABLE_USER_LINKER) + return() + endif() + + set(LINKER_FLAG "-fuse-ld=${USER_LINKER_OPTION}") + + check_cxx_compiler_flag(${LINKER_FLAG} CXX_SUPPORTS_USER_LINKER) + if(CXX_SUPPORTS_USER_LINKER) + target_compile_options(${project_name} INTERFACE ${LINKER_FLAG}) + endif() +endfunction() diff --git a/tools/BuildTools/cmake/Modules/Logger.cmake b/tools/BuildTools/cmake/Modules/Logger.cmake new file mode 100644 index 0000000..909799d --- /dev/null +++ b/tools/BuildTools/cmake/Modules/Logger.cmake @@ -0,0 +1,107 @@ +include_guard(GLOBAL) + +# if($ENV{CLION_IDE}) # TODO: Do the same for CLion + +# else() +# if(NOT WIN32) +# string(ASCII 27 Esc) +# set(ColourReset "${Esc}[m") +# set(ColourBold "${Esc}[1m") +# set(Red "${Esc}[31m") +# set(Green "${Esc}[32m") +# set(Yellow "${Esc}[33m") +# set(Blue "${Esc}[34m") +# set(Magenta "${Esc}[35m") +# set(Cyan "${Esc}[36m") +# set(White "${Esc}[37m") +# set(BoldRed "${Esc}[1;31m") +# set(BoldGreen "${Esc}[1;32m") +# set(BoldYellow "${Esc}[1;33m") +# set(BoldBlue "${Esc}[1;34m") +# set(BoldMagenta "${Esc}[1;35m") +# set(BoldCyan "${Esc}[1;36m") +# set(BoldWhite "${Esc}[1;37m") +# endif() +# endif() + +set(CMAKE_LOG_LEVEL_TRACE 2) +set(CMAKE_LOG_LEVEL_DEBUG 3) +set(CMAKE_LOG_LEVEL_INFO 4) +set(CMAKE_LOG_LEVEL_WARNING 5) +set(CMAKE_LOG_LEVEL_ERROR 6) +set(CMAKE_LOG_LEVEL_FATAL 7) + +if(NOT PROJECT_IS_TOP_LEVEL) + set(CMAKE_LOG_LEVEL_TRACE + 2 + PARENT_SCOPE + ) + set(CMAKE_LOG_LEVEL_DEBUG + 3 + PARENT_SCOPE + ) + set(CMAKE_LOG_LEVEL_INFO + 4 + PARENT_SCOPE + ) + set(CMAKE_LOG_LEVEL_WARNING + 5 + PARENT_SCOPE + ) + set(CMAKE_LOG_LEVEL_ERROR + 6 + PARENT_SCOPE + ) + set(CMAKE_LOG_LEVEL_FATAL + 7 + PARENT_SCOPE + ) +endif() + +# Sets the CMAKE_CURRENT_LOG_LEVEL to the default or command line option +option(CMAKE_CURRENT_LOG_LEVEL "Set CMake current log level" ${CMAKE_LOG_LEVEL_INFO}) + +if(CMAKE_CURRENT_LOG_LEVEL STREQUAL "OFF") + message("[CORE][WARNING] CMAKE_CURRENT_LOG_LEVEL not set") + message("[CORE][INFO] Defaulting to CMAKE_LOG_LEVEL_INFO") + set(CMAKE_CURRENT_LOG_LEVEL ${CMAKE_LOG_LEVEL_INFO}) + if(NOT PROJECT_IS_TOP_LEVEL) + set(CMAKE_CURRENT_LOG_LEVEL + ${CMAKE_LOG_LEVEL_INFO} + PARENT_SCOPE + ) + endif() +endif() + +function(log_message msg log_level) + if(NOT ${log_level} LESS CMAKE_CURRENT_LOG_LEVEL) + message(${msg}) + endif() +endfunction() + +function(log_trace msg) + log_message("${ColourBold}[TRACE]${ColourReset} ${msg}" ${CMAKE_LOG_LEVEL_TRACE}) +endfunction(log_trace) + +function(log_debug msg) + log_message("${Cyan}[DEBUG]${ColourReset} ${msg}" ${CMAKE_LOG_LEVEL_DEBUG}) +endfunction(log_debug) + +function(log_info msg) + log_message("${Green}[INFO]${ColourReset} ${msg}" ${CMAKE_LOG_LEVEL_INFO}) +endfunction(log_info) + +function(log_warn msg) + log_message("${BoldYellow}[WARNING]${ColourReset} ${msg}" ${CMAKE_LOG_LEVEL_WARNING}) +endfunction(log_warn) + +function(log_error msg) + message(SEND_ERROR "${BoldRed}[ERROR]${ColourReset} ${msg}") +endfunction(log_error) + +function(log_fatal msg) + message(FATAL_ERROR "${BoldRed}[FATAL]${ColourReset} ${msg} ") +endfunction(log_fatal) + +log_info("Logger -- Initialized successfull") +log_info("CMake Version: ${CMAKE_VERSION}") diff --git a/tools/BuildTools/cmake/Modules/PreCompiledHeaders.cmake b/tools/BuildTools/cmake/Modules/PreCompiledHeaders.cmake new file mode 100644 index 0000000..ff3cb0f --- /dev/null +++ b/tools/BuildTools/cmake/Modules/PreCompiledHeaders.cmake @@ -0,0 +1,84 @@ +include_guard() + +# C Standard Library pre-compiled headers +set(c_standard_library_pch + + + + + + + +) + +# C++ Standard Library pre-compiled headers +set(cpp_standard_library_pch + + + + + + + + + + + +) +# Win32 API pre-compiled headers +set(windows_api_library_pch # + # +) + +# POSIX API pre-compiled headers +set(linux_api_library_pch + # + # + # + # + # + # + # + # + # +) + +function(set_target_precompiled_headers target VISIBILITY) + cmake_host_system_information(RESULT OS_NAME_RESULT QUERY OS_NAME) + if(OS_NAME_RESULT STREQUAL "Windows") + target_precompile_headers( + ${target} + ${VISIBILITY} + # C Standard Library pre-compiled headers + ${c_standard_library_pch} + # C++ Standard Library pre-compiled headers + ${cpp_standard_library_pch} + # Win32 API pre-compiled headers + ${windows_api_library_pch} + ) + elseif(OS_NAME_RESULT STREQUAL "Linux") + target_precompile_headers( + ${target} + ${VISIBILITY} + # C Standard Library pre-compiled headers + ${c_standard_library_pch} + # C++ Standard Library pre-compiled headers + ${cpp_standard_library_pch} + # POSIX API pre-compiled headers + ${linux_api_library_pch} + ) + elseif(OS_NAME_RESULT STREQUAL "macOS") + target_precompile_headers( + ${target} + ${VISIBILITY} + # C Standard Library pre-compiled headers + ${c_standard_library_pch} + # C++ Standard Library pre-compiled headers + ${cpp_standard_library_pch} + # POSIX API pre-compiled headers + ${linux_api_library_pch} + ) + else() + message(FATAL_ERROR "[BUG] OS_NAME_RESULT: ${OS_NAME_RESULT}") + endif() +endfunction() \ No newline at end of file diff --git a/tools/BuildTools/cmake/Modules/PreventInSourceBuilds.cmake b/tools/BuildTools/cmake/Modules/PreventInSourceBuilds.cmake new file mode 100644 index 0000000..eb25dbc --- /dev/null +++ b/tools/BuildTools/cmake/Modules/PreventInSourceBuilds.cmake @@ -0,0 +1,52 @@ +# cmake-format: off +# ============================================================================= +# Copyright (C) lefticus +# +# See Also: https://github.com/lefticus +# See Also: https://github.com/cpp-best-practices/cpp_starter_project +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to +# ============================================================================= +# cmake-format: on +include_guard(GLOBAL) + +# This function will prevent in-source builds +function(AssureOutOfSourceBuilds) + # make sure the user doesn't play dirty with symlinks + get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH) + get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH) + + # disallow in-source builds + if("${srcdir}" STREQUAL "${bindir}") + message("######################################################") + message("Warning: in-source builds are disabled") + message("Please create a separate build directory and run cmake from there") + message("######################################################") + message(FATAL_ERROR "Quitting configuration") + endif() +endfunction() + +assureoutofsourcebuilds() diff --git a/tools/BuildTools/cmake/Modules/Sanitizers.cmake b/tools/BuildTools/cmake/Modules/Sanitizers.cmake new file mode 100644 index 0000000..d2f29bc --- /dev/null +++ b/tools/BuildTools/cmake/Modules/Sanitizers.cmake @@ -0,0 +1,96 @@ +# cmake-format: off +# ============================================================================= +# Copyright (C) lefticus +# +# See Also: https://github.com/lefticus +# See Also: https://github.com/cpp-best-practices/cpp_starter_project +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to +# ============================================================================= +# cmake-format: on +include_guard(GLOBAL) + +function(enable_sanitizers project_name) + + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + + option(ENABLE_COVERAGE "Enable coverage reporting for gcc/clang" FALSE) + + if(ENABLE_COVERAGE) + target_compile_options(${project_name} INTERFACE --coverage -O0 -g) + target_link_libraries(${project_name} INTERFACE --coverage) + endif() + + set(SANITIZERS "") + + option(ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" FALSE) + if(ENABLE_SANITIZER_ADDRESS) + list(APPEND SANITIZERS "address") + endif() + + option(ENABLE_SANITIZER_LEAK "Enable leak sanitizer" FALSE) + if(ENABLE_SANITIZER_LEAK) + list(APPEND SANITIZERS "leak") + endif() + + option(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR "Enable undefined behavior sanitizer" FALSE) + if(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR) + list(APPEND SANITIZERS "undefined") + endif() + + option(ENABLE_SANITIZER_THREAD "Enable thread sanitizer" FALSE) + if(ENABLE_SANITIZER_THREAD) + if("address" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS) + log_warn("Thread sanitizer does not work with Address and Leak sanitizer enabled") + else() + list(APPEND SANITIZERS "thread") + endif() + endif() + + option(ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" FALSE) + if(ENABLE_SANITIZER_MEMORY AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + if("address" IN_LIST SANITIZERS + OR "thread" IN_LIST SANITIZERS + OR "leak" IN_LIST SANITIZERS + ) + log_warn("Memory sanitizer does not work with Address, Thread and Leak sanitizer enabled") + else() + list(APPEND SANITIZERS "memory") + endif() + endif() + + list(JOIN SANITIZERS "," LIST_OF_SANITIZERS) + + endif() + + if(LIST_OF_SANITIZERS) + if(NOT "${LIST_OF_SANITIZERS}" STREQUAL "") + target_compile_options(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS}) + target_link_libraries(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS}) + endif() + endif() + +endfunction() diff --git a/tools/BuildTools/cmake/Modules/StandardProjectSettings.cmake b/tools/BuildTools/cmake/Modules/StandardProjectSettings.cmake new file mode 100644 index 0000000..3180bfb --- /dev/null +++ b/tools/BuildTools/cmake/Modules/StandardProjectSettings.cmake @@ -0,0 +1,76 @@ +# cmake-format: off +# ============================================================================= +# Copyright (C) lefticus +# +# See Also: https://github.com/lefticus +# See Also: https://github.com/cpp-best-practices/cpp_starter_project +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to +# ============================================================================= +# cmake-format: on +include_guard(GLOBAL) + +# ============================================================================= +# Set a default build type if none was specified +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + log_info("Setting build type to 'RelWithDebInfo' as none was specified.") + set(CMAKE_BUILD_TYPE + RelWithDebInfo + CACHE STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui, ccmake + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" + "MinSizeRel" "RelWithDebInfo") +endif() + +# ============================================================================= +# Generate compile_commands.json to ease the work from clang-based tools +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# ============================================================================= +# Configure Interprocedural Optimization, aka Link Time Optimization (LTO) +option(ENABLE_IPO + "Enable Interprocedural Optimization, aka Link Time Optimization (LTO)" + OFF) + +if(ENABLE_IPO) + include(CheckIPOSupported) + check_ipo_supported(RESULT result OUTPUT output) + if(result) + log_info("IPO is supported") + else() + log_error("IPO is not supported: '${output}'") + endif() +endif() + +if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + add_compile_options(-fcolor-diagnostics) +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + add_compile_options(-fdiagnostics-color=always) +else() + log_info( + "No colored compiler diagnostic set for '${CMAKE_CXX_COMPILER_ID}' compiler" + ) +endif() diff --git a/tools/BuildTools/cmake/Modules/StaticAnalyzers.cmake b/tools/BuildTools/cmake/Modules/StaticAnalyzers.cmake new file mode 100644 index 0000000..878a22d --- /dev/null +++ b/tools/BuildTools/cmake/Modules/StaticAnalyzers.cmake @@ -0,0 +1,77 @@ +# cmake-format: off +# ============================================================================= +# Copyright (C) lefticus +# +# See Also: https://github.com/lefticus +# See Also: https://github.com/cpp-best-practices/cpp_starter_project +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to +# ============================================================================= +# cmake-format: on +include_guard(GLOBAL) + +# ============================================================================= +option(ENABLE_CPPCHECK "Enable static analysis with cppcheck" OFF) +if(ENABLE_CPPCHECK) + find_program(CPPCHECK cppcheck) + if(CPPCHECK) + set(CMAKE_CXX_CPPCHECK + ${CPPCHECK} + --suppress=missingInclude + --enable=all + --inline-suppr + --inconclusive + -i + ${CMAKE_SOURCE_DIR}/imgui/lib) + else() + log_error("cppcheck requested but executable not found") + endif() +endif() + +# ============================================================================= +option(ENABLE_CLANG_TIDY "Enable static analysis with clang-tidy" ON) +if(ENABLE_CLANG_TIDY) + find_program(CLANGTIDY clang-tidy) + if(CLANGTIDY) + log_info("clang-tidy found and enabled") + set(CMAKE_CXX_CLANG_TIDY ${CLANGTIDY} + -extra-arg=-Wno-unknown-warning-option) + else() + log_error("clang-tidy requested but executable not found") + endif() +endif() + +# ============================================================================= +option(ENABLE_INCLUDE_WHAT_YOU_USE + "Enable static analysis with include-what-you-use" OFF) +if(ENABLE_INCLUDE_WHAT_YOU_USE) + find_program(INCLUDE_WHAT_YOU_USE include-what-you-use) + if(INCLUDE_WHAT_YOU_USE) + set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${INCLUDE_WHAT_YOU_USE}) + else() + log_error("include-what-you-use requested but executable not found") + endif() +endif() diff --git a/tools/BuildTools/cmake/Templates/IncludeMeta.cmake b/tools/BuildTools/cmake/Templates/IncludeMeta.cmake new file mode 100644 index 0000000..cc95d4c --- /dev/null +++ b/tools/BuildTools/cmake/Templates/IncludeMeta.cmake @@ -0,0 +1,11 @@ +include_guard(GLOBAL) + +set(THIS_DIR ${CMAKE_CURRENT_LIST_DIR}) +function(configure_meta_header_file) + string(TOLOWER COMPANY_NAME COMPANY_NAME_LOWER_CASE) + string(TOLOWER PROJECT_NAME PROJECT_NAME_LOWER_CASE) + configure_file( + "${THIS_DIR}/Meta.h.in" "${CMAKE_BINARY_DIR}/${COMPANY_NAME}/${PROJECT_NAME}/Meta.hpp" + ESCAPE_QUOTES + ) +endfunction() diff --git a/tools/BuildTools/cmake/Templates/Meta.h.in b/tools/BuildTools/cmake/Templates/Meta.h.in new file mode 100644 index 0000000..f3b3025 --- /dev/null +++ b/tools/BuildTools/cmake/Templates/Meta.h.in @@ -0,0 +1,46 @@ +/// ============================================================================= +/// Copyright (C) lefticus +/// +/// See Also: https://github.com/lefticus +/// See Also: https://github.com/cpp-best-practices/cpp_starter_project +/// +/// This is free and unencumbered software released into the public domain. +/// +/// Anyone is free to copy, modify, publish, use, compile, sell, or +/// distribute this software, either in source code form or as a compiled +/// binary, for any purpose, commercial or non-commercial, and by any +/// means. +/// +/// In jurisdictions that recognize copyright laws, the author or authors +/// of this software dedicate any and all copyright interest in the +/// software to the public domain. We make this dedication for the benefit +/// of the public at large and to the detriment of our heirs and +/// successors. We intend this dedication to be an overt act of +/// relinquishment in perpetuity of all present and future rights to this +/// software under copyright law. +/// +/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +/// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +/// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +/// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +/// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +/// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +/// OTHER DEALINGS IN THE SOFTWARE. +/// +/// For more information, please refer to +/// ============================================================================= + +#pragma once + +namespace @COMPANY_NAME_LOWER_CASE@::@PROJECT_NAME_LOWER_CASE@::meta { + + static constexpr std::string_view cx_CompanyName = "@COMPANY_NAME@"; + static constexpr std::string_view cx_ProjectName = "@PROJECT_NAME@"; + static constexpr std::string_view cx_ProjectVersion = "@PROJECT_VERSION@"; + static constexpr int cx_ProjectVersionMajor { @PROJECT_VERSION_MAJOR@ }; + static constexpr int cx_ProjectVersionMinor { @PROJECT_VERSION_MINOR@ }; + static constexpr int cx_ProjectVersionPatch { @PROJECT_VERSION_PATCH@ }; + static constexpr int cx_ProjectVersionTweak { @PROJECT_VERSION_TWEAK@ }; + static constexpr std::string_view cx_GitSha = "@GIT_SHA@"; + +} // namespace myproject::meta diff --git a/tools/BuildTools/scripts/dependencies.py b/tools/BuildTools/scripts/dependencies.py new file mode 100644 index 0000000..18fcc74 --- /dev/null +++ b/tools/BuildTools/scripts/dependencies.py @@ -0,0 +1,98 @@ +from utils import check_software_version +import sys +import os +import platform +import subprocess +print("[dependencies.py] traced ") + + +SHOULD_ABORT = False + + +def is_running_as_sudo(): + return os.geteuid() == 0 + + +def defaultOnSuccess(software: str, version: str): + print(f"{software} {version} or higher is installed") + + +def defaultOnMissing(software: str, version: str): + global SHOULD_ABORT + print(f"{software} {version} is missing! Please create a Pull Request with the installation steps!") + SHOULD_ABORT = True + + +class SystemRequirement: + def __init__(self, software, version, onMissing=None, onSuccess=None): + self.software = software + self.version = version + self.onMissing = onMissing if onMissing is not None else lambda: defaultOnMissing( + self.software, self.version) + self.onSuccess = onSuccess if onSuccess is not None else lambda: defaultOnSuccess( + self.software, self.version) + + def onSuccess(self): + return self.onSuccess() + + def onMissing(self): + return self.onMissing() + + +def install_cmake(): + print("Installing cmake...") + if platform.system().lower() == "linux": + # Install pip using apt-get on Ubuntu + if is_running_as_sudo(): + subprocess.run(["apt-get", "update"], check=True) + subprocess.run( + ["apt-get", "install", "cmake", "-y", "--no-install-recommends"], check=True) + else: + subprocess.run(["sudo", "apt-get", "update"], check=True) + subprocess.run(["sudo", "apt-get", "install", + "cmake", "-y", "--no-install-recommends"], check=True) + else: + print("Installing cmake with chocolatey") + print("pip has been successfully installed.") + + +def install_clang(): + print("Installing clang...") + if platform.system().lower() == "linux": + # Install pip using apt-get on Ubuntu + if is_running_as_sudo(): + subprocess.run(["apt-get", "update"], check=True) + subprocess.run( + ["apt-get", "install", "-y", "--no-install-recommends", "clang-15", "clang-tidy-15", "clang-format-15"], check=True) + else: + subprocess.run(["sudo", "apt-get", "update"], check=True) + subprocess.run(["sudo", "apt-get", "install", "-y", "--no-install-recommends", + "clang-15", "clang-tidy-15", "clang-format-15"], check=True) + else: + print("Installing llvm with chocolatey...") + print("pip has been successfully installed.") + + +# ############################################################################ +# System Requirements +# ############################################################################ +system_requirements = [ + SystemRequirement("cmake", "3.21.0", install_cmake), + SystemRequirement("clang", "15.0.0", install_clang), +] + + +def check_system_requirements(): + print("[TRACE] check_system_requirements <<<<<<<<<<<<<<<<<<<<<< ") + for system_requirement in system_requirements: + if (check_software_version(system_requirement.software, system_requirement.version)): + print(f"{system_requirement.software} found successfully") + system_requirement.onSuccess() + else: + print(f"{system_requirement.software} missing!") + system_requirement.onMissing() + + if SHOULD_ABORT: + return False + + return True diff --git a/tools/BuildTools/scripts/setup.py b/tools/BuildTools/scripts/setup.py new file mode 100644 index 0000000..ee2f75c --- /dev/null +++ b/tools/BuildTools/scripts/setup.py @@ -0,0 +1,9 @@ +print("[setup.py] === Initial Python Setup === ") + +from dependencies import check_system_requirements + +print("Checking the system requirements ...") +if check_system_requirements(): + print("Checking the system requirements --- SUCCESS") +else: + print("Checking the system requirements --- FAILURE") diff --git a/tools/BuildTools/scripts/utils.py b/tools/BuildTools/scripts/utils.py new file mode 100644 index 0000000..8bf08e0 --- /dev/null +++ b/tools/BuildTools/scripts/utils.py @@ -0,0 +1,92 @@ +import subprocess +import re +import sys +import platform +import os + + +def is_running_as_sudo(): + return os.geteuid() == 0 + + +def install_pip(): + try: + __import__("pip") + except ModuleNotFoundError: + try: + print("Attempting to install pip...") + if platform.system().lower() == "linux": + # Install pip using apt-get on Ubuntu + if is_running_as_sudo(): + subprocess.run(["apt-get", "update"], check=True) + subprocess.run( + ["apt-get", "install", "python3-pip", "-y", "--no-install-recommends"], check=True) + else: + subprocess.run(["sudo", "apt-get", "update"], check=True) + subprocess.run(["sudo", "apt-get", "install", + "python3-pip", "-y", "--no-install-recommends"], check=True) + else: + subprocess.run( + [sys.executable, "-m", "ensurepip", "--default-pip"], check=True) + print("pip has been successfully installed.") + except subprocess.CalledProcessError: + print("Failed to install pip. Please install it manually.") + sys.exit(1) + + +# Check and install pip +print("================ INSTALLING PIP ================ ") +install_pip() +print("================ PIP INSTALLED ================ ") + +def import_package(package_name: str) -> None: + try: + __import__(package_name) + # print(f"{package_name} module is installed.") + except ModuleNotFoundError: + print(f"{package_name} module is not installed.") + + user_response = input("Do you want to install it? (yes/no): ").lower() + + if user_response == "yes": + try: + # Attempt to install the module + subprocess.run(["pip", "install", package_name], check=True) + print(f"{package_name} module has been successfully installed.") + # Now try importing it again + __import__(package_name) + print(f"Now the {package_name} module is available.") + except subprocess.CalledProcessError: + print(f"Failed to install the {package_name} module.") + else: + print( + f"Installation aborted. The script cannot proceed without the {package_name} module.") + sys.exit(1) # Exit with a non-zero code to indicate an error + + +import_package('packaging') +from packaging import version + +def check_software_version(software: str, required_version: str): + print(f"[TRACE] check_software_version({software}, {required_version})") + try: + # Run the command to get the version + result = subprocess.run([software, "--version"], check=True, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + # Extract version number using a regular expression + match = re.search(r"version (\d+\.\d+\.\d+)", result.stdout) + if match: + installed_version_str = match.group(1) + installed_version = version.parse(installed_version_str) + # print(f"Installed {software} version: {installed_version}") + # Compare with the required version + return installed_version >= version.parse(required_version) + else: + print(f"Failed to extract {software} version.") + return False + except subprocess.CalledProcessError: + print(f"[CalledProcessError] Failed to check {software} version") + return False + except FileNotFoundError: + print(f"[FileNotFoundError] Failed to check {software} version") + return False diff --git a/tools/BuildTools/vcpkg b/tools/BuildTools/vcpkg new file mode 160000 index 0000000..c869686 --- /dev/null +++ b/tools/BuildTools/vcpkg @@ -0,0 +1 @@ +Subproject commit c8696863d371ab7f46e213d8f5ca923c4aef2a00 diff --git a/tools/GitTools/Hooks/applypatch-msg b/tools/GitTools/Hooks/applypatch-msg new file mode 100755 index 0000000..d784726 --- /dev/null +++ b/tools/GitTools/Hooks/applypatch-msg @@ -0,0 +1,17 @@ +#!/bin/sh + +echo "=== Running applypatch-msg git-hooks ===" + +# # An example hook script to check the commit log message taken by +# # applypatch from an e-mail message. +# # +# # The hook should exit with non-zero status after issuing an +# # appropriate message if it wants to stop the commit. The hook is +# # allowed to edit the commit message file. +# # +# # To enable this hook, rename this file to "applypatch-msg". + +# # . git-sh-setup +# # commitmsg="$(git rev-parse --git-path hooks/commit-msg)" +# # test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} +# # : diff --git a/tools/GitTools/Hooks/commit-msg b/tools/GitTools/Hooks/commit-msg new file mode 100755 index 0000000..f00c5df --- /dev/null +++ b/tools/GitTools/Hooks/commit-msg @@ -0,0 +1,26 @@ +#!/bin/sh + +echo "=== Running commit-msg git-hooks ===" + +# # An example hook script to check the commit log message. +# # Called by "git commit" with one argument, the name of the file +# # that has the commit message. The hook should exit with non-zero +# # status after issuing an appropriate message if it wants to stop the +# # commit. The hook is allowed to edit the commit message file. +# # +# # To enable this hook, rename this file to "commit-msg". + +# # Uncomment the below to add a Signed-off-by line to the message. +# # Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# # hook is more suited to it. +# # +# # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# # This example catches duplicate Signed-off-by lines. + +# test "" = "$(grep '^Signed-off-by: ' "$1" | +# sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { +# echo >&2 Duplicate Signed-off-by lines. +# exit 1 +# } diff --git a/tools/GitTools/Hooks/fsmonitor-watchman b/tools/GitTools/Hooks/fsmonitor-watchman new file mode 100755 index 0000000..cc38fad --- /dev/null +++ b/tools/GitTools/Hooks/fsmonitor-watchman @@ -0,0 +1,176 @@ +#!/usr/bin/perl + +print "=== Running fsmonitor-watchman git-hooks ===" + +use strict; +use warnings; +use IPC::Open2; + +# # An example hook script to integrate Watchman +# # (https://facebook.github.io/watchman/) with git to speed up detecting +# # new and modified files. +# # +# # The hook is passed a version (currently 2) and last update token +# # formatted as a string and outputs to stdout a new update token and +# # all files that have been modified since the update token. Paths must +# # be relative to the root of the working tree and separated by a single NUL. +# # +# # To enable this hook, rename this file to "query-watchman" and set +# # 'git config core.fsmonitor .git/hooks/query-watchman' +# # +# my ($version, $last_update_token) = @ARGV; + +# # Uncomment for debugging +# # print STDERR "$0 $version $last_update_token\n"; + +# # Check the hook interface version +# if ($version ne 2) { +# die "Unsupported query-fsmonitor hook version '$version'.\n" . +# "Falling back to scanning...\n"; +# } + +# my $git_work_tree = get_working_dir(); + +# my $retry = 1; + +# my $json_pkg; +# eval { +# require JSON::XS; +# $json_pkg = "JSON::XS"; +# 1; +# } or do { +# require JSON::PP; +# $json_pkg = "JSON::PP"; +# }; + +# launch_watchman(); + +# sub launch_watchman { +# my $o = watchman_query(); +# if (is_work_tree_watched($o)) { +# output_result($o->{clock}, @{$o->{files}}); +# } +# } + +# sub output_result { +# my ($clockid, @files) = @_; + +# # Uncomment for debugging watchman output +# # open (my $fh, ">", ".git/watchman-output.out"); +# # binmode $fh, ":utf8"; +# # print $fh "$clockid\n@files\n"; +# # close $fh; + +# binmode STDOUT, ":utf8"; +# print $clockid; +# print "\0"; +# local $, = "\0"; +# print @files; +# } + +# sub watchman_clock { +# my $response = qx/watchman clock "$git_work_tree"/; +# die "Failed to get clock id on '$git_work_tree'.\n" . +# "Falling back to scanning...\n" if $? != 0; + +# return $json_pkg->new->utf8->decode($response); +# } + +# sub watchman_query { +# my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty') +# or die "open2() failed: $!\n" . +# "Falling back to scanning...\n"; + +# # In the query expression below we're asking for names of files that +# # changed since $last_update_token but not from the .git folder. +# # +# # To accomplish this, we're using the "since" generator to use the +# # recency index to select candidate nodes and "fields" to limit the +# # output to file names only. Then we're using the "expression" term to +# # further constrain the results. +# my $last_update_line = ""; +# if (substr($last_update_token, 0, 1) eq "c") { +# $last_update_token = "\"$last_update_token\""; +# $last_update_line = qq[\n"since": $last_update_token,]; +# } +# my $query = <<" END"; +# ["query", "$git_work_tree", {$last_update_line +# "fields": ["name"], +# "expression": ["not", ["dirname", ".git"]] +# }] +# END + +# # Uncomment for debugging the watchman query +# # open (my $fh, ">", ".git/watchman-query.json"); +# # print $fh $query; +# # close $fh; + +# print CHLD_IN $query; +# close CHLD_IN; +# my $response = do {local $/; }; + +# # Uncomment for debugging the watch response +# # open ($fh, ">", ".git/watchman-response.json"); +# # print $fh $response; +# # close $fh; + +# die "Watchman: command returned no output.\n" . +# "Falling back to scanning...\n" if $response eq ""; +# die "Watchman: command returned invalid output: $response\n" . +# "Falling back to scanning...\n" unless $response =~ /^\{/; + +# return $json_pkg->new->utf8->decode($response); +# } + +# sub is_work_tree_watched { +# my ($output) = @_; +# my $error = $output->{error}; +# if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) { +# $retry--; +# my $response = qx/watchman watch "$git_work_tree"/; +# die "Failed to make watchman watch '$git_work_tree'.\n" . +# "Falling back to scanning...\n" if $? != 0; +# $output = $json_pkg->new->utf8->decode($response); +# $error = $output->{error}; +# die "Watchman: $error.\n" . +# "Falling back to scanning...\n" if $error; + +# # Uncomment for debugging watchman output +# # open (my $fh, ">", ".git/watchman-output.out"); +# # close $fh; + +# # Watchman will always return all files on the first query so +# # return the fast "everything is dirty" flag to git and do the +# # Watchman query just to get it over with now so we won't pay +# # the cost in git to look up each individual file. +# my $o = watchman_clock(); +# $error = $output->{error}; + +# die "Watchman: $error.\n" . +# "Falling back to scanning...\n" if $error; + +# output_result($o->{clock}, ("/")); +# $last_update_token = $o->{clock}; + +# eval { launch_watchman() }; +# return 0; +# } + +# die "Watchman: $error.\n" . +# "Falling back to scanning...\n" if $error; + +# return 1; +# } + +# sub get_working_dir { +# my $working_dir; +# if ($^O =~ 'msys' || $^O =~ 'cygwin') { +# $working_dir = Win32::GetCwd(); +# $working_dir =~ tr/\\/\//; +# } else { +# require Cwd; +# $working_dir = Cwd::cwd(); +# } + +# return $working_dir; +# } diff --git a/tools/GitTools/Hooks/post-update b/tools/GitTools/Hooks/post-update new file mode 100755 index 0000000..9fb4192 --- /dev/null +++ b/tools/GitTools/Hooks/post-update @@ -0,0 +1,10 @@ +#!/bin/sh + +echo "=== Running post-update git-hooks ===" + +# # An example hook script to prepare a packed repository for use over +# # dumb transports. +# # +# # To enable this hook, rename this file to "post-update". + +# # exec git update-server-info diff --git a/tools/GitTools/Hooks/pre-applypatch b/tools/GitTools/Hooks/pre-applypatch new file mode 100755 index 0000000..69ac0fa --- /dev/null +++ b/tools/GitTools/Hooks/pre-applypatch @@ -0,0 +1,16 @@ +#!/bin/sh + +echo "=== Running pre-applypatch git-hooks ===" + +# # An example hook script to verify what is about to be committed +# # by applypatch from an e-mail message. +# # +# # The hook should exit with non-zero status after issuing an +# # appropriate message if it wants to stop the commit. +# # +# # To enable this hook, rename this file to "pre-applypatch". + +# # . git-sh-setup +# # precommit="$(git rev-parse --git-path hooks/pre-commit)" +# # test -x "$precommit" && exec "$precommit" ${1+"$@"} +# # : diff --git a/tools/GitTools/Hooks/pre-commit b/tools/GitTools/Hooks/pre-commit new file mode 100755 index 0000000..6cf4996 --- /dev/null +++ b/tools/GitTools/Hooks/pre-commit @@ -0,0 +1,67 @@ +#!/bin/bash + +# ============================================================================= +# Fun Fact: we can use the following shebang line for cross-compiling: +# /* #!/bin/bash; C:/Program\ Files/path/to/Git/usr/bin/sh.exe */ +# according to https://www.youtube.com/watch?v=fMYv6-SZsSo +# ============================================================================= + +# ============================================================================ +# Script setup +# ============================================================================ + +# get the directory where the script is located +THIS_SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" +# import git utilities +GIT_UTILITIES_SCRIPT_PATH=$THIS_SCRIPT_DIR/../Utils/utilities.sh +test -f $GIT_UTILITIES_SCRIPT_PATH || (echo "File not found: $GIT_UTILITIES_SCRIPT_PATH" && exit 1) +source $GIT_UTILITIES_SCRIPT_PATH || (echo "Failed to source $GIT_UTILITIES_SCRIPT_PATH" && exit 1) + +# detect if this tools folder is a submodule or not +IS_SUBMODULE=$( + git_is_submodule + echo $? +) + +# getting the root level directory of the project +PROJECT_ROOT_DIR=$(git rev-parse --show-toplevel) +if [[ $IS_SUBMODULE == 1 ]]; then + cd "${PROJECT_ROOT_DIR}/.." + PROJECT_ROOT_DIR=$(git rev-parse --show-toplevel) +fi + +# import logger +source ${PROJECT_ROOT_DIR}/tools/GitTools/Utils/logger.sh + +CMAKE_CURRENT_LOG_LEVEL=$CMAKE_LOG_LEVEL_TRACE + +log_info "=== Running pre-commit git-hooks ===" + +# # An example hook script to verify what is about to be committed. +# # Called by "git commit" with no arguments. The hook should +# # exit with non-zero status after issuing an appropriate message if +# # it wants to stop the commit. +# # +# # To enable this hook, rename this file to "pre-commit". + +log_warn "TODO(PO): Validate email address" +log_warn "TODO(PO): Get a list of all changed files." +log_warn "TODO(PO): Format all Added or Modified files (c++, cmake)" +log_warn "TODO(PO): Set REQUIRES_CLEAN_BUILD if Added or Deleted files" + +# ============================================================================= +# Run All Tests +# ============================================================================= + +if [[ $CLEAN_BUILD != 0 ]]; then + ${PROJECT_ROOT_DIR}/tools/GitTools/Utils/runTests.sh -f +else + ${PROJECT_ROOT_DIR}/tools/GitTools/Utils/runTests.sh +fi + +if [[ "$?" != 0 ]]; then + log_error "pre-commit tests - FAILED" + exit 1 +else + log_info "pre-commit tests - DONE" +fi diff --git a/tools/GitTools/Hooks/pre-merge-commit b/tools/GitTools/Hooks/pre-merge-commit new file mode 100755 index 0000000..e851237 --- /dev/null +++ b/tools/GitTools/Hooks/pre-merge-commit @@ -0,0 +1,15 @@ +#!/bin/sh + +echo "=== Running pre-merge-commit git-hooks ===" + +# # An example hook script to verify what is about to be committed. +# # Called by "git merge" with no arguments. The hook should +# # exit with non-zero status after issuing an appropriate message to +# # stderr if it wants to stop the merge commit. +# # +# # To enable this hook, rename this file to "pre-merge-commit". + +# # . git-sh-setup +# # test -x "$GIT_DIR/hooks/pre-commit" && +# # exec "$GIT_DIR/hooks/pre-commit" +# # : diff --git a/tools/GitTools/Hooks/pre-push b/tools/GitTools/Hooks/pre-push new file mode 100755 index 0000000..b4092a2 --- /dev/null +++ b/tools/GitTools/Hooks/pre-push @@ -0,0 +1,58 @@ +#!/bin/sh + +echo "=== Running pre-push git-hooks ===" + +# # An example hook script to verify what is about to be pushed. Called by "git +# # push" after it has checked the remote status, but before anything has been +# # pushed. If this script exits with a non-zero status nothing will be pushed. +# # +# # This hook is called with the following parameters: +# # +# # $1 -- Name of the remote to which the push is being done +# # $2 -- URL to which the push is being done +# # +# # If pushing without using a named remote those arguments will be equal. +# # +# # Information about the commits which are being pushed is supplied as lines to +# # the standard input in the form: +# # +# # +# # +# # This sample shows how to prevent push of commits where the log message starts +# # with "WIP" (work in progress). + +# remote="$1" +# url="$2" + +# # Assuming that pre-commit has formatted everything correctly +echo "TODO(PO): Run All Tests (Fuzz, Main, Perf, Unit)" + +# # zero=$(git hash-object --stdin &2 "Found WIP commit in $local_ref, not pushing" +# # exit 1 +# # fi +# # fi +# # done + +# exit 0 diff --git a/tools/GitTools/Hooks/pre-rebase b/tools/GitTools/Hooks/pre-rebase new file mode 100755 index 0000000..1acd1b2 --- /dev/null +++ b/tools/GitTools/Hooks/pre-rebase @@ -0,0 +1,171 @@ +#!/bin/sh + +echo "=== Running pre-rebase git-hooks ===" + +# # Copyright (c) 2006, 2008 Junio C Hamano +# # +# # The "pre-rebase" hook is run just before "git rebase" starts doing +# # its job, and can prevent the command from running by exiting with +# # non-zero status. +# # +# # The hook is called with the following parameters: +# # +# # $1 -- the upstream the series was forked from. +# # $2 -- the branch being rebased (or empty when rebasing the current branch). +# # +# # This sample shows how to prevent topic branches that are already +# # merged to 'next' branch from getting rebased, because allowing it +# # would result in rebasing already published history. + +# # publish=next +# # basebranch="$1" +# # if test "$#" = 2 +# # then +# # topic="refs/heads/$2" +# # else +# # topic=`git symbolic-ref HEAD` || +# # exit 0 ;# we do not interrupt rebasing detached HEAD +# # fi + +# # case "$topic" in +# # refs/heads/??/*) +# # ;; +# # *) +# # exit 0 ;# we do not interrupt others. +# # ;; +# # esac + +# # # Now we are dealing with a topic branch being rebased +# # # on top of master. Is it OK to rebase it? + +# # # Does the topic really exist? +# # git show-ref -q "$topic" || { +# # echo >&2 "No such branch $topic" +# # exit 1 +# # } + +# # # Is topic fully merged to master? +# # not_in_master=`git rev-list --pretty=oneline ^master "$topic"` +# # if test -z "$not_in_master" +# # then +# # echo >&2 "$topic is fully merged to master; better remove it." +# # exit 1 ;# we could allow it, but there is no point. +# # fi + +# # # Is topic ever merged to next? If so you should not be rebasing it. +# # only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` +# # only_next_2=`git rev-list ^master ${publish} | sort` +# # if test "$only_next_1" = "$only_next_2" +# # then +# # not_in_topic=`git rev-list "^$topic" master` +# # if test -z "$not_in_topic" +# # then +# # echo >&2 "$topic is already up to date with master" +# # exit 1 ;# we could allow it, but there is no point. +# # else +# # exit 0 +# # fi +# # else +# # not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` +# # /usr/bin/perl -e ' +# # my $topic = $ARGV[0]; +# # my $msg = "* $topic has commits already merged to public branch:\n"; +# # my (%not_in_next) = map { +# # /^([0-9a-f]+) /; +# # ($1 => 1); +# # } split(/\n/, $ARGV[1]); +# # for my $elem (map { +# # /^([0-9a-f]+) (.*)$/; +# # [$1 => $2]; +# # } split(/\n/, $ARGV[2])) { +# # if (!exists $not_in_next{$elem->[0]}) { +# # if ($msg) { +# # print STDERR $msg; +# # undef $msg; +# # } +# # print STDERR " $elem->[1]\n"; +# # } +# # } +# # ' "$topic" "$not_in_next" "$not_in_master" +# # exit 1 +# # fi + +# # <<\DOC_END + +# # This sample hook safeguards topic branches that have been +# # published from being rewound. + +# # The workflow assumed here is: + +# # * Once a topic branch forks from "master", "master" is never +# # merged into it again (either directly or indirectly). + +# # * Once a topic branch is fully cooked and merged into "master", +# # it is deleted. If you need to build on top of it to correct +# # earlier mistakes, a new topic branch is created by forking at +# # the tip of the "master". This is not strictly necessary, but +# # it makes it easier to keep your history simple. + +# # * Whenever you need to test or publish your changes to topic +# # branches, merge them into "next" branch. + +# # The script, being an example, hardcodes the publish branch name +# # to be "next", but it is trivial to make it configurable via +# # $GIT_DIR/config mechanism. + +# # With this workflow, you would want to know: + +# # (1) ... if a topic branch has ever been merged to "next". Young +# # topic branches can have stupid mistakes you would rather +# # clean up before publishing, and things that have not been +# # merged into other branches can be easily rebased without +# # affecting other people. But once it is published, you would +# # not want to rewind it. + +# # (2) ... if a topic branch has been fully merged to "master". +# # Then you can delete it. More importantly, you should not +# # build on top of it -- other people may already want to +# # change things related to the topic as patches against your +# # "master", so if you need further changes, it is better to +# # fork the topic (perhaps with the same name) afresh from the +# # tip of "master". + +# # Let's look at this example: + +# # o---o---o---o---o---o---o---o---o---o "next" +# # / / / / +# # / a---a---b A / / +# # / / / / +# # / / c---c---c---c B / +# # / / / \ / +# # / / / b---b C \ / +# # / / / / \ / +# # ---o---o---o---o---o---o---o---o---o---o---o "master" + + +# # A, B and C are topic branches. + +# # * A has one fix since it was merged up to "next". + +# # * B has finished. It has been fully merged up to "master" and "next", +# # and is ready to be deleted. + +# # * C has not merged to "next" at all. + +# # We would want to allow C to be rebased, refuse A, and encourage +# # B to be deleted. + +# # To compute (1): + +# # git rev-list ^master ^topic next +# # git rev-list ^master next + +# # if these match, topic has not merged in next at all. + +# # To compute (2): + +# # git rev-list master..topic + +# # if this is empty, it is fully merged to "master". + +# # DOC_END diff --git a/tools/GitTools/Hooks/pre-receive b/tools/GitTools/Hooks/pre-receive new file mode 100755 index 0000000..c3e7d4f --- /dev/null +++ b/tools/GitTools/Hooks/pre-receive @@ -0,0 +1,26 @@ +#!/bin/sh + +echo "=== Running pre-receive git-hooks ===" + +# # An example hook script to make use of push options. +# # The example simply echoes all push options that start with 'echoback=' +# # and rejects all pushes when the "reject" push option is used. +# # +# # To enable this hook, rename this file to "pre-receive". + +# # if test -n "$GIT_PUSH_OPTION_COUNT" +# # then +# # i=0 +# # while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" +# # do +# # eval "value=\$GIT_PUSH_OPTION_$i" +# # case "$value" in +# # echoback=*) +# # echo "echo from the pre-receive-hook: ${value#*=}" >&2 +# # ;; +# # reject) +# # exit 1 +# # esac +# # i=$((i + 1)) +# # done +# # fi diff --git a/tools/GitTools/Hooks/prepare-commit-msg b/tools/GitTools/Hooks/prepare-commit-msg new file mode 100755 index 0000000..1193803 --- /dev/null +++ b/tools/GitTools/Hooks/prepare-commit-msg @@ -0,0 +1,44 @@ +#!/bin/sh + +echo "=== Running prepare-commit-msg git-hooks ===" + +# # An example hook script to prepare the commit log message. +# # Called by "git commit" with the name of the file that has the +# # commit message, followed by the description of the commit +# # message's source. The hook's purpose is to edit the commit +# # message file. If the hook fails with a non-zero status, +# # the commit is aborted. +# # +# # To enable this hook, rename this file to "prepare-commit-msg". + +# # # This hook includes three examples. +# # # The first one removes the "# Please enter the commit message..." help message. + +# COMMIT_MSG_FILE=$1 +# COMMIT_SOURCE=$2 +# SHA1=$3 + +# # /usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE" + +# # # The second includes the output of "git diff --name-status -r" +# # # into the message, just before the "git status" output. It is +# # # commented because it doesn't cope with --amend or with squashed +# # # commits. + +# # case "$COMMIT_SOURCE,$SHA1" in +# # ,|template,) +# # /usr/bin/perl -i.bak -pe ' +# # print "\n" . `git diff --cached --name-status -r` +# # if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;; +# # *) ;; +# # esac + +# # # The third example adds a Signed-off-by line to the message, that can +# # # still be edited. This is rarely a good idea. + +# # SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# # git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE" +# # if test -z "$COMMIT_SOURCE" +# # then +# # /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE" +# # fi diff --git a/tools/GitTools/Hooks/push-to-checkout b/tools/GitTools/Hooks/push-to-checkout new file mode 100755 index 0000000..79182b9 --- /dev/null +++ b/tools/GitTools/Hooks/push-to-checkout @@ -0,0 +1,80 @@ +#!/bin/sh + +echo "=== Running push-to-checkout git-hooks ===" + +# # An example hook script to update a checked-out tree on a git push. +# # +# # This hook is invoked by git-receive-pack(1) when it reacts to git +# # push and updates reference(s) in its repository, and when the push +# # tries to update the branch that is currently checked out and the +# # receive.denyCurrentBranch configuration variable is set to +# # updateInstead. +# # +# # By default, such a push is refused if the working tree and the index +# # of the remote repository has any difference from the currently +# # checked out commit; when both the working tree and the index match +# # the current commit, they are updated to match the newly pushed tip +# # of the branch. This hook is to be used to override the default +# # behaviour; however the code below reimplements the default behaviour +# # as a starting point for convenient modification. +# # +# # The hook receives the commit with which the tip of the current +# # branch is going to be updated: +# commit=$1 + +# # It can exit with a non-zero status to refuse the push (when it does +# # so, it must not modify the index or the working tree). +# die () { +# echo >&2 "$*" +# exit 1 +# } + +# # Or it can make any necessary changes to the working tree and to the +# # index to bring them to the desired state when the tip of the current +# # branch is updated to the new commit, and exit with a zero status. +# # +# # For example, the hook can simply run git read-tree -u -m HEAD "$1" +# # in order to emulate git fetch that is run in the reverse direction +# # with git push, as the two-tree form of git read-tree -u -m is +# # essentially the same as git switch or git checkout that switches +# # branches while keeping the local changes in the working tree that do +# # not interfere with the difference between the branches. + +# # The below is a more-or-less exact translation to shell of the C code +# # for the default behaviour for git's push-to-checkout hook defined in +# # the push_to_deploy() function in builtin/receive-pack.c. +# # +# # Note that the hook will be executed from the repository directory, +# # not from the working tree, so if you want to perform operations on +# # the working tree, you will have to adapt your code accordingly, e.g. +# # by adding "cd .." or using relative paths. + +# if ! git update-index -q --ignore-submodules --refresh +# then +# die "Up-to-date check failed" +# fi + +# if ! git diff-files --quiet --ignore-submodules -- +# then +# die "Working directory has unstaged changes" +# fi + +# # This is a rough translation of: +# # +# # head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX +# if git cat-file -e HEAD 2>/dev/null +# then +# head=HEAD +# else +# head=$(git hash-object -t tree --stdin &2 +# exit 1 +# } + +# unset GIT_DIR GIT_WORK_TREE +# cd "$worktree" && + +# if grep -q "^diff --git " "$1" +# then +# validate_patch "$1" +# else +# validate_cover_letter "$1" +# fi && + +# if test "$GIT_SENDEMAIL_FILE_COUNTER" = "$GIT_SENDEMAIL_FILE_TOTAL" +# then +# git config --unset-all sendemail.validateWorktree && +# trap 'git worktree remove -ff "$worktree"' EXIT && +# validate_series +# fi diff --git a/tools/GitTools/Hooks/update b/tools/GitTools/Hooks/update new file mode 100755 index 0000000..8065dc5 --- /dev/null +++ b/tools/GitTools/Hooks/update @@ -0,0 +1,130 @@ +#!/bin/sh + +echo "=== Running update git-hooks ===" + +# # An example hook script to block unannotated tags from entering. +# # Called by "git receive-pack" with arguments: refname sha1-old sha1-new +# # +# # To enable this hook, rename this file to "update". +# # +# # Config +# # ------ +# # hooks.allowunannotated +# # This boolean sets whether unannotated tags will be allowed into the +# # repository. By default they won't be. +# # hooks.allowdeletetag +# # This boolean sets whether deleting tags will be allowed in the +# # repository. By default they won't be. +# # hooks.allowmodifytag +# # This boolean sets whether a tag may be modified after creation. By default +# # it won't be. +# # hooks.allowdeletebranch +# # This boolean sets whether deleting branches will be allowed in the +# # repository. By default they won't be. +# # hooks.denycreatebranch +# # This boolean sets whether remotely creating branches will be denied +# # in the repository. By default this is allowed. +# # + +# # --- Command line +# refname="$1" +# oldrev="$2" +# newrev="$3" + +# # --- Safety check +# if [ -z "$GIT_DIR" ]; then +# echo "Don't run this script from the command line." >&2 +# echo " (if you want, you could supply GIT_DIR then run" >&2 +# echo " $0 )" >&2 +# exit 1 +# fi + +# if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then +# echo "usage: $0 " >&2 +# exit 1 +# fi + +# # --- Config +# allowunannotated=$(git config --type=bool hooks.allowunannotated) +# allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch) +# denycreatebranch=$(git config --type=bool hooks.denycreatebranch) +# allowdeletetag=$(git config --type=bool hooks.allowdeletetag) +# allowmodifytag=$(git config --type=bool hooks.allowmodifytag) + +# # check for no description +# projectdesc=$(sed -e '1q' "$GIT_DIR/description") +# case "$projectdesc" in +# "Unnamed repository"* | "") +# echo "*** Project description file hasn't been set" >&2 +# exit 1 +# ;; +# esac + +# # --- Check types +# # if $newrev is 0000...0000, it's a commit to delete a ref. +# zero=$(git hash-object --stdin &2 +# echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 +# exit 1 +# fi +# ;; +# refs/tags/*,delete) +# # delete tag +# if [ "$allowdeletetag" != "true" ]; then +# echo "*** Deleting a tag is not allowed in this repository" >&2 +# exit 1 +# fi +# ;; +# refs/tags/*,tag) +# # annotated tag +# if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 +# then +# echo "*** Tag '$refname' already exists." >&2 +# echo "*** Modifying a tag is not allowed in this repository." >&2 +# exit 1 +# fi +# ;; +# refs/heads/*,commit) +# # branch +# if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then +# echo "*** Creating a branch is not allowed in this repository" >&2 +# exit 1 +# fi +# ;; +# refs/heads/*,delete) +# # delete branch +# if [ "$allowdeletebranch" != "true" ]; then +# echo "*** Deleting a branch is not allowed in this repository" >&2 +# exit 1 +# fi +# ;; +# refs/remotes/*,commit) +# # tracking branch +# ;; +# refs/remotes/*,delete) +# # delete tracking branch +# if [ "$allowdeletebranch" != "true" ]; then +# echo "*** Deleting a tracking branch is not allowed in this repository" >&2 +# exit 1 +# fi +# ;; +# *) +# # Anything else (is there anything else?) +# echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 +# exit 1 +# ;; +# esac + +# # --- Finished +# exit 0 diff --git a/tools/GitTools/Utils/logger.sh b/tools/GitTools/Utils/logger.sh new file mode 100644 index 0000000..212445e --- /dev/null +++ b/tools/GitTools/Utils/logger.sh @@ -0,0 +1,138 @@ +#!/bin/bash + +# ============================================================================= +# Color Definitions +# see also: https://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux +# ============================================================================= + +# Reset +Color_Off='\033[0m' # Text Reset + +# Regular Colors +Black='\033[0;30m' # Black +Red='\033[0;31m' # Red +Green='\033[0;32m' # Green +Yellow='\033[0;33m' # Yellow +Blue='\033[0;34m' # Blue +Purple='\033[0;35m' # Purple +Cyan='\033[0;36m' # Cyan +White='\033[0;37m' # White + +# Bold +BBlack='\033[1;30m' # Black +BRed='\033[1;31m' # Red +BGreen='\033[1;32m' # Green +BYellow='\033[1;33m' # Yellow +BBlue='\033[1;34m' # Blue +BPurple='\033[1;35m' # Purple +BCyan='\033[1;36m' # Cyan +BWhite='\033[1;37m' # White + +# Underline +UBlack='\033[4;30m' # Black +URed='\033[4;31m' # Red +UGreen='\033[4;32m' # Green +UYellow='\033[4;33m' # Yellow +UBlue='\033[4;34m' # Blue +UPurple='\033[4;35m' # Purple +UCyan='\033[4;36m' # Cyan +UWhite='\033[4;37m' # White + +# Background +On_Black='\033[40m' # Black +On_Red='\033[41m' # Red +On_Green='\033[42m' # Green +On_Yellow='\033[43m' # Yellow +On_Blue='\033[44m' # Blue +On_Purple='\033[45m' # Purple +On_Cyan='\033[46m' # Cyan +On_White='\033[47m' # White + +# ============================================================================= +# Log Level Definitions +# ============================================================================= + +export LOG_LEVEL_TRACE=2 +export LOG_LEVEL_DEBUG=3 +export LOG_LEVEL_INFO=4 +export LOG_LEVEL_WARN=5 +export LOG_LEVEL_ERROR=6 + +export LOG_VERBOSITY=0 + +CURRENT_LOG_LEVEL=$LOG_LEVEL_INFO +if [[ $LOG_VERBOSITY = 1 ]]; then + echo "changing logger verbosity" + CURRENT_LOG_LEVEL=$LOG_LEVEL_TRACE +fi + +# ============================================================================= +# Functions Definitions +# ============================================================================= + +function set_log_level() { + CURRENT_LOG_LEVEL=$1 +} + +function log_message() { + echo -e "$1" +} + +function log_trace() { + if [[ $CURRENT_LOG_LEVEL -le $LOG_LEVEL_TRACE ]]; then + if [[ $LOG_VERBOSITY = 1 ]]; then + THIS_SCRIPT="[${0##*/}]" + else + THIS_SCRIPT="" + fi + log_message "$BWhite${THIS_SCRIPT}[T] $1 $Color_Off" + fi +} + +function log_debug() { + if [[ $CURRENT_LOG_LEVEL -le $LOG_LEVEL_DEBUG ]]; then + if [[ $LOG_VERBOSITY = 1 ]]; then + THIS_SCRIPT="[${0##*/}]" + else + THIS_SCRIPT="" + fi + log_message "$Cyan${THIS_SCRIPT}[D] $1 $Color_Off" + fi +} + +function log_info() { + if [[ $LOG_VERBOSITY = 1 ]]; then + THIS_SCRIPT="[${0##*/}]" + else + THIS_SCRIPT="" + fi + log_message "$Green${THIS_SCRIPT}[I] $1 $Color_Off" +} + +function log_warn() { + if [[ $LOG_VERBOSITY = 1 ]]; then + THIS_SCRIPT="[${0##*/}]" + else + THIS_SCRIPT="" + fi + log_message "$Yellow${THIS_SCRIPT}[W] $1 $Color_Off" +} + +function log_error() { + if [[ $LOG_VERBOSITY = 1 ]]; then + THIS_SCRIPT="[${0##*/}]" + else + THIS_SCRIPT="" + fi + log_message "$Red${THIS_SCRIPT}[E] $1 $Color_Off" +} + +function log_fatal() { + if [[ $LOG_VERBOSITY = 1 ]]; then + THIS_SCRIPT="[${0##*/}]" + else + THIS_SCRIPT="" + fi + log_message "$On_Red${THIS_SCRIPT}[F] $1$Color_Off" + exit 1 +} diff --git a/tools/GitTools/Utils/runTests.sh b/tools/GitTools/Utils/runTests.sh new file mode 100755 index 0000000..0a6f6f8 --- /dev/null +++ b/tools/GitTools/Utils/runTests.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# ============================================================================ +# Script setup +# ============================================================================ + +# navigate to project root directory (this works as long as not in a submodule) +git_dir=$(git rev-parse --show-toplevel) +cd $git_dir + +# add logger to the scope +source $(pwd)/tools/GitTools/Utils/logger.sh +CLEAN_BUILD=0 + +while getopts 'f' flag; do + case "${flag}" in + f) + CLEAN_BUILD=1 + ;; + ?) + echo "script usage: $(basename \$0) [-f]" >&2 + exit 1 + ;; + esac +done + +CMAKE_TOOLCHAIN_FILE_DIR=$(pwd)/tools/BuildTools/vcpkg/scripts/buildsystems +CMAKE_BINARY_DIR=out/build/local-CI/ +# TODO:(PO): Should we do all presets or let that for CI ? +CMAKE_BUILD_TYPE=Release + +if [[ $CLEAN_BUILD != 0 ]]; then + log_warn "Removing previous CI build ..." + rm -rf $CMAKE_BINARY_DIR + log_warn "Removing previous CI build --- SUCCESS" +fi + +# ============================================================================ +# CMake Generating, building and testing stages +# ============================================================================ + +# ---------------------------------------------------------------------------- +# Generating +# ---------------------------------------------------------------------------- + +CMAKE_CTestTestfile=$CMAKE_BINARY_DIR/CTestTestfile.cmake +if [[ ! -f "$CMAKE_CTestTestfile" ]]; then + log_info "Generating build system ..." + cmake -S . -B $CMAKE_BINARY_DIR -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$CMAKE_TOOLCHAIN_FILE_DIR/vcpkg.cmake -DCMAKE_CXX_COMPILER="clang++" + if [[ "$?" != 0 ]]; then + log_error "Generating build system --- FAILURE" + exit 1 + fi + log_info "Generating build system --- SUCCESS" +fi + +# ----------------------------------------------------------------------------- +# Building +# ----------------------------------------------------------------------------- +log_info "Building ..." +cmake --build $CMAKE_BINARY_DIR --config $CMAKE_BUILD_TYPE +if [[ "$?" != 0 ]]; then + log_error "Building --- FAILURE" + exit 1 +fi +log_info "Building --- SUCCESS" + +# ----------------------------------------------------------------------------- +# Testing +# ----------------------------------------------------------------------------- + +log_info "Testing ..." +ctest --test-dir $CMAKE_BINARY_DIR -C $CMAKE_BUILD_TYPE --output-on-failure --timeout 10 +if [[ "$?" != 0 ]]; then + log_error "Testing --- FAILURE" + exit 1 +fi +log_info "Testing --- SUCCESS" + +# ============================================================================= +# Clean up +# ============================================================================= + +# return to previous cwd +cd - diff --git a/tools/GitTools/Utils/utilities.sh b/tools/GitTools/Utils/utilities.sh new file mode 100644 index 0000000..6d7c81f --- /dev/null +++ b/tools/GitTools/Utils/utilities.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +function git_is_submodule() { + GIT_TOP_LEVEL_DIR=$(git rev-parse --show-toplevel) + cd $GIT_TOP_LEVEL_DIR + IS_SUBMODULE=$(test -d .git; echo $?) + if [[ $IS_SUBMODULE == 1 ]]; then + return 1 + else + return 0 + fi +} diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..fb7684a --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,11 @@ +{ + "name": "aiaa-solutions", + "version-string": "0.1.0", + "dependencies": [ + "spdlog", + "catch", + "gtest", + "benchmark" + ] + } + \ No newline at end of file