diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..bbb7ed5 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,79 @@ +--- +Checks: > + , clang-diagnostic-* + , clang-analyzer-* + , bugprone-* + , -bugprone-easily-swappable-parameters + , -bugprone-implicit-widening-of-multiplication-result + , -bugprone-narrowing-conversions + , -bugprone-reserved-identifier + , cert-* + , -cert-dcl37-c + , -cert-dcl51-cpp + , -cert-env33-c + , concurrency-* + , cppcoreguidelines-* + , -cppcoreguidelines-avoid-c-arrays + , -cppcoreguidelines-avoid-magic-numbers + , -cppcoreguidelines-narrowing-conversions + , -cppcoreguidelines-non-private-member-variables-in-classes + , -cppcoreguidelines-owning-memory + , -cppcoreguidelines-pro-bounds-array-to-pointer-decay + , -cppcoreguidelines-pro-bounds-constant-array-index + , -cppcoreguidelines-pro-type-union-access + , -cppcoreguidelines-pro-type-vararg + , google-* + , -google-readability-todo + , misc-* + , -misc-non-private-member-variables-in-classes + , modernize-* + , -modernize-avoid-c-arrays + , -modernize-use-override + , -modernize-use-trailing-return-type + , performance-* + , portability-* + , readability-* + , -readability-avoid-const-params-in-decls + , -readability-implicit-bool-conversion + , -readability-magic-numbers + , -readability-suspicious-call-argument +WarningsAsErrors: '' +HeaderFilterRegex: 'acp_hub_[^/]*\.(h|hpp|hxx)$' +FormatStyle: file +CheckOptions: + - key: cert-dcl16-c.NewSuffixes + value: 'L;LL;LU;LLU' + - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField + value: 'false' + - key: cert-str34-c.DiagnoseSignedUnsignedCharComparisons + value: 'false' + - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors + value: 'true' + - key: google-readability-braces-around-statements.ShortStatementLines + value: '1' + - key: google-readability-function-size.StatementThreshold + value: '800' + - key: google-readability-namespace-comments.SpacesBeforeComments + value: '2' + - key: google-readability-namespace-comments.ShortNamespaceLines + value: '10' + - key: llvm-else-after-return.WarnOnConditionVariables + value: 'false' + - key: llvm-else-after-return.WarnOnUnfixable + value: 'false' + - key: llvm-qualified-auto.AddConstToQualified + value: 'false' + - key: modernize-loop-convert.MinConfidence + value: reasonable + - key: modernize-loop-convert.MaxCopySize + value: '16' + - key: modernize-loop-convert.NamingStyle + value: CamelCase + - key: modernize-replace-auto-ptr.IncludeStyle + value: llvm + - key: modernize-pass-by-value.IncludeStyle + value: llvm + - key: modernize-use-nullptr.NullMacros + value: 'NULL' + +... diff --git a/.gitignore b/.gitignore index 95b5676..327adfb 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ tstcrc bin/prc.exe bin/prc *.o +build +compile_commands.json +.vscode \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8f7eee7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,208 @@ +# Requirements to the cmake ---------------------------------------------------- +cmake_minimum_required(VERSION 3.12) + +# Project Declaration ---------------------------------------------------------- + +project(crc + VERSION 2.1.0 + DESCRIPTION "Multi platform MIT licensed CRC library in C" + HOMEPAGE_URL "https://github.com/lammertb/libcrc" + LANGUAGES C +) + +# CMake extensions ------------------------------------------------------------- +add_subdirectory(cmake) + +# Includes --------------------------------------------------------------------- +## cmake native modules +include(CheckCCompilerFlag) +include(CMakePackageConfigHelpers) +include(FeatureSummary) +include(GNUInstallDirs) + +## local modules +include(CommonOptions) + +if(WITH_INCLUDE_WHAT_YOU_USE) + include(UseIncludeWhatYouUse) +endif() + +if(WITH_CLANG_TIDY) + include(UseClangTidy) +endif() + +if(WITH_COVERAGE) + include(CodeCoverage) +endif() + +# Package setting ------------------------------------------------------------- +set(PACKAGE_NAME ${PROJECT_NAME}) +set(PACKAGE_NAMESPACE ${PROJECT_NAME}) +set(PACKAGE_INSTALL_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PACKAGE_NAME}) +set(PACKAGE_INSTALL_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR}/${PACKAGE_NAME}) +set(PACKAGE_INSTALL_LIBRARY_DIR ${CMAKE_INSTALL_LIBDIR}) +if(WIN32) + set(PACKAGE_INSTALL_CMAKE_DIR cmake) + set(PACKAGE_INSTALL_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR}) +endif() + + +# Target Declaration ----------------------------------------------------------- +add_library(${PROJECT_NAME} STATIC) +add_library(${PACKAGE_NAMESPACE}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) + +# Compilation flags ------------------------------------------------------------ + +add_library(crc_compile_flags INTERFACE) +add_library(${PACKAGE_NAMESPACE}::compile_flags ALIAS crc_compile_flags) + +set(compiler_flags -Wall) +if(WIN32) + list(APPEND compiler_flags -Ox -Ot -MT -GT -J -sdl -WX + -volatile:iso -nologo + -wd4464 -wd4668 -wd4710 -wd4711 -wd4201 -wd4820 + ) +else() + list(APPEND compiler_flags -Wextra -Wstrict-prototypes -Wshadow + -Wpointer-arith -Wcast-qual -Wcast-align + -Wwrite-strings -Wredundant-decls -Wnested-externs + -funsigned-char + ) +endif() + +foreach(compiler_flag IN LISTS compiler_flags) + check_c_compiler_flag(${compiler_flag} supports${compiler_flag}) + if(supports${compiler_flag}) + target_compile_options(crc_compile_flags + INTERFACE + $ + ) + endif() +endforeach() + +# Dependency resolving --------------------------------------------------------- + +add_subdirectory(include) +add_subdirectory(precalc) + + +# File generation -------------------------------------------------------------- +foreach(arch 32 64) + add_custom_command(OUTPUT tab/gentab${arch}.inc + COMMAND ${CMAKE_COMMAND} -E make_directory tab + COMMAND prc --crc${arch} tab/gentab${arch}.inc + COMMENT "precalc: generate tab/gentab${arch}.inc" + VERBATIM + ) +endforeach() + + +add_library(crc_gentab INTERFACE) +add_library(${PACKAGE_NAMESPACE}::gentab ALIAS crc_gentab) +target_sources(crc_gentab + INTERFACE + $ + $ +) +target_include_directories(crc_gentab + INTERFACE + $ +) + +# Target Definition ------------------------------------------------------------ + +target_sources(${PROJECT_NAME} + PRIVATE + src/crc8.c + src/crc16.c + src/crc32.c + src/crc64.c + src/crcccitt.c + src/crcdnp.c + src/crckrmit.c + src/crcsick.c + src/nmea-chk.c +) + +target_link_libraries(${PROJECT_NAME} + PUBLIC + crc::common + PRIVATE + $ + $ +) + +# Package configuration -------------------------------------------------------- +set(PACKAGE_EXPORTS common crc) +configure_package_config_file( + cmake/templates/${PACKAGE_NAME}Config.cmake.in + ${PACKAGE_NAME}Config.cmake + INSTALL_DESTINATION ${PACKAGE_INSTALL_CMAKE_DIR} + NO_SET_AND_CHECK_MACRO +) + +write_basic_package_version_file(${PROJECT_NAME}ConfigVersion.cmake + COMPATIBILITY AnyNewerVersion +) + +# pkgconfig configuration ------------------------------------------------------ +configure_file("cmake/templates/${PACKAGE_NAME}.pc.in" + "${PACKAGE_NAME}.pc" + @ONLY +) + +# Static Code analysis --------------------------------------------------------- +if(WITH_CLANG_TIDY) + target_setup_clang_tidy(${PROJECT_NAME}) +endif() + +# Testing ---------------------------------------------------------------------- +if(WITH_UNIT_TEST) + + if(WITH_COVERAGE) + target_setup_coverage(${PROJECT_NAME}) + endif() + + include(CTest) + enable_testing() + add_subdirectory(test) +endif() + +# Examples --------------------------------------------------------------------- +if(WITH_EXAMPLE) + add_subdirectory(examples) +endif() + +# Feature summary -------------------------------------------------------------- +feature_summary(WHAT ALL + DESCRIPTION "-- [${PROJECT_NAME} summary] ---------------------------------" +) + +# Installations ---------------------------------------------------------------- + +## Targets installation +install(TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + ARCHIVE DESTINATION ${PACKAGE_INSTALL_LIBRARY_DIR} + LIBRARY DESTINATION ${PACKAGE_INSTALL_LIBRARY_DIR} + PUBLIC_HEADER DESTINATION ${PACKAGE_INSTALL_INCLUDE_DIR} + COMPONENT ${PROJECT_NAME} +) + +## CMake's configurations +install(EXPORT ${PROJECT_NAME}Targets + NAMESPACE ${PACKAGE_NAMESPACE}:: + DESTINATION ${PACKAGE_INSTALL_CMAKE_DIR} +) + + +## Package configurations +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}Config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}ConfigVersion.cmake + DESTINATION ${PACKAGE_INSTALL_CMAKE_DIR} +) + +## pkgconfig configurations ---------------------------------------------------- +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}.pc" + DESTINATION ${PACKAGE_INSTALL_LIBRARY_DIR}/pkgconfig +) \ No newline at end of file diff --git a/INSTALL b/INSTALL index e45dea2..3481973 100644 --- a/INSTALL +++ b/INSTALL @@ -148,4 +148,130 @@ make clean cleans up the object files, library file and testall executable +CMake Toolchain +=============== + +**libcrc** could be built and installed with CMake, which gives cross-platform +compatibility for all those platform where CMake can work. +Configure process accepts all the standard arguments [cmake(1)](https://cmake.org/cmake/help/latest/manual/cmake.1.html) supports. + +CMake version `>= 3.12` is required. + +Compilers +--------- + +One can test build with different compilers providing them to the configuration step +of CMake in one of the several ways: +- [CMAKE_\_COMPILER](https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER.html) + provided + +``` sh +cmake .. -DCMAKE_C_COMPILER=clang +``` + +- toolchain files [CMAKE_TOOLCHAIN_FILE](https://cmake.org/cmake/help/latest/variable/CMAKE_TOOLCHAIN_FILE.html#variable:CMAKE_TOOLCHAIN_FILE) + +```sh +cmake .. --toolchain= +``` + +Generators +---------- + +CMake provides verity of different generators. Underlying generator has to be installed +and available in `$PATH` + +```sh +# for Linux/Unix +cmake .. -G "Unix Makefiles" + +# for Windows +cmake .. -G "Visual Studio 15 2017" +``` + +Options +------- +| Option | Default | Note | +| ------------------------- | ------- | ---------------------------------------------------------- | +| WITH_INCLUDE_WHAT_YOU_USE | OFF | Dev: Validate correctness of `#include` directives [^1] | +| WITH_CLANG_TIDY | OFF | Dev: Run `clang-tidy` static code analysis [^2] | +| WITH_UNIT_TEST | OFF | Build unit tests | +| WITH_COVERAGE | OFF | Dev: Collects coverage metrics during tests execution [^3] | +| WITH_EXAMPLE | OFF | Build examples | + + +Building ``libcrc`` +------------------- + +```sh +mkdir build +cd build +cmake .. +# optionally generator and build type can be specified +# cmake .. -G "ninja" -DCMAKE_BUILD_TYPE=Debug +# build +cmake --build . --config Debug --target all -j 6 +``` + +Building and running tests +-------------------------- + +```sh +mkdir build +cd build +# configure with unit tests +cmake .. -DWITH_UNIT_TEST=On +# build +cmake --build . --config Debug --target all -j 6 +# run tests +cmake -VV +``` + +Installing +---------- + +```sh +mkdir build +cd build +# configure, optionally provide install destination +cmake .. -DCMAKE_INSTALL_PREFIX= +# build and install +cmake --build . --config Debug --target install -j 6 +``` + +Consuming ``libcrc`` in CMake project +------------------------------------- + +If ``libcrc`` is built and installed via CMake toolchain it can be easily discovered +and used within any CMake project + +```cmake +# discover libcrc +find_package(crc REQUIRED) + +add_executable(my_crc_consumer main.c) + +# my_crc_consumer will be linked against libcrc +# and compiled with includes paths from libcrc installation +target_link_libraries(my_crc_consumer + PRIVATE + crc::crc +) +``` + +Consuming via ``pkg-config`` +---------------------------- + +Using CMake toolchain one then can use ``libcrc`` with ``pkg-config`` + +```sh +cc `pkg-config --cflags --libs crc` -o main.c my_crc_consumer +``` + Lammert Bies + +[^1]: requires ``include-what-you-use`` + +[^2]: requires ``clang-tidy`` + +[^3]: requires ``gcov`` or ``llvm-cov`` and ``gcovr`` \ No newline at end of file diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt new file mode 100644 index 0000000..affbb54 --- /dev/null +++ b/cmake/CMakeLists.txt @@ -0,0 +1,3 @@ +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/modules) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/finders) +set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" PARENT_SCOPE) diff --git a/cmake/finders/FindClangTidy.cmake b/cmake/finders/FindClangTidy.cmake new file mode 100644 index 0000000..361634a --- /dev/null +++ b/cmake/finders/FindClangTidy.cmake @@ -0,0 +1,221 @@ +# Author: Vitalii Shylienkov +# License: MIT +# Copyright: (c) 2018-2022 Vitalii Shylienkov + +#[=============================================================================[.rst: +FindClangTidy +------------- + +Locates `clang-tidy `_, +a clang-based C++ “linter” tool. + + +Components +^^^^^^^^^^ + +This module doesn't support components + +Result variables +^^^^^^^^^^^^^^^^ +This module will set the following variables in your project: + + :cmake:variable:`ClangTidy_FOUND`, + :cmake:variable:`CLANGTIDY_FOUND` + + Found ``ClangTidy`` package + + :cmake:variable:`ClangTidy_EXECUTABLE` + + Path to the ``clang-tidy`` executable + + :cmake:variable:`ClangTidy_COMMAND` + + String that has to be used to invoke ``clang-tidy`` + + :cmake:variable:`ClangTidy_VERSION_STRING` + + ``clang-tidy`` full version string + + :cmake:variable:`ClangTidy_VERSION_MAJOR` + + ``clang-tidy`` major version + + :cmake:variable:`ClangTidy_VERSION_MINOR` + + ``clang-tidy`` minor version + + :cmake:variable:`ClangTidy_VERSION_PATCH` + + ``clang-tidy`` patch version + +Hints +^^^^^ + +The following variables may be set to provide hints to this module: + + :cmake:variable:`ClangTidy_DIR`, + :cmake:variable:`CLANGTIDY_DIR` + + Path to the installation root of ``clang-tidy`` + +Environment variables with the same names will be also checked: + + :cmake:envvar:`ClangTidy_DIR`, + :cmake:envvar:`CLANGTIDY_DIR` + +Example usage +^^^^^^^^^^^^^ + +.. code-block:: cmake + + find_package(ClangTidy REQUIRED) + + set(CMAKE_CXX_CLANG_TIDY "${ClangTidy_COMMAND}") + +#]=============================================================================] + +# includes --------------------------------------------------------------------- +include(FeatureSummary) +include(FindPackageHandleStandardArgs) + +# Internal variables ----------------------------------------------------------- +set(cfp_NAME "${CMAKE_FIND_PACKAGE_NAME}") +string(TOUPPER "${cfp_NAME}" CFP_NAME) +set(_ClangTidy_log_prefix "${_cf_log_prefix}${cfp_NAME}:" CACHE INTERNAL "FindClangTidy Log prefix") + +# Declare package properties --------------------------------------------------- +set_package_properties(${cfp_NAME} + PROPERTIES + URL "https://clang.llvm.org/extra/clang-tidy/index.html" + DESCRIPTION "A clang-based C++ “linter” tool" +) + +# Validate find_package() arguments -------------------------------------------- +# No components supported +if(${cfp_NAME}_FIND_COMPONENTS AND NOT ${cfp_NAME}_FIND_QUIETLY) + message(WARNING "${_ClangTidy_log_prefix} components not supported") +endif() + +# Build list of names ---------------------------------------------------------- +set(${cfp_NAME}_names "clang-tidy") + +set(known_file_suffixes 3.9 4.0 5.0 6.0) +set(known_file_suffixes_major 7 8 9 10 13 14 15) + +if(DEFINED ${cfp_NAME}_FIND_VERSION) # if specific version was requested + if(${cfp_NAME}_FIND_VERSION_EXACT) # if exact this version has to be found + if(${cfp_NAME}_FIND_VERSION VERSION_LESS 7) # we are looking for two components + set(suffix ${${cfp_NAME}_FIND_VERSION_MAJOR}) + + if(${cfp_NAME}_FIND_VERSION_COUNT EQUAL 1) # if only one component provided + string(APPEND suffix ".0") # we explicitly append .0 + else() + string(APPEND suffix ".${${cfp_NAME}_FIND_VERSION_MINOR}") # otherwise we take only two components + endif() + + if(suffix IN_LIST known_file_suffixes) # if we know this version + list(APPEND ${cfp_NAME}_names "clang-tidy-${suffix}") # add to the list of searched names + endif() + else() # one component suffix + if( (${cfp_NAME}_FIND_VERSION_COUNT EQUAL 1) # if requested one component + OR ( (${cfp_NAME}_FIND_VERSION_COUNT EQUAL 2) # or two with the second equals 0 + AND (${cfp_NAME}_FIND_VERSION_MINOR EQUAL 0))) + + if(${cfp_NAME}_FIND_VERSION_MAJOR IN_LIST known_file_suffixes_major) + list(APPEND ${cfp_NAME}_names "clang-tidy-${${cfp_NAME}_FIND_VERSION_MAJOR}") + endif() + endif() + endif() + else() # not exact version requested + foreach(suffix IN LISTS known_file_suffixes known_file_suffixes_major) + if(NOT suffix VERSION_LESS ${cfp_NAME}_FIND_VERSION) + list(APPEND ${cfp_NAME}_names "clang-tidy-${suffix}") + endif() + endforeach() + endif() +else() # no specific version requested + foreach(suffix IN LISTS known_file_suffixes known_file_suffixes_major) + list(APPEND ${cfp_NAME}_names "clang-tidy-${suffix}") + endforeach() +endif() + +list(REVERSE ${cfp_NAME}_names) + +unset(suffix) +unset(known_file_suffixes) +unset(known_file_suffixes_major) + +# build list of hints +set(${cfp_NAME}_hints "") +foreach(dir ${cfp_NAME}_DIR ${CFP_NAME}_DIR) + if(DEFINED ${dir}) + list(APPEND ${cfp_NAME}_hints "${${dir}}") + endif() +endforeach() +unset(dir) + +# Find binary ------------------------------------------------------------------ + +find_program(${cfp_NAME}_EXECUTABLE + NAMES ${${cfp_NAME}_names} + HINTS ${${cfp_NAME}_hints} + ENV ${cfp_NAME}_DIR + ENV ${CFP_NAME}_DIR + PATH_SUFFIXES bin + DOC "The ${cfp_NAME} executable" +) + +unset(${cfp_NAME}_names) +unset(${cfp_NAME}_hints) + +# Figure out the version ------------------------------------------------------- + +if(${cfp_NAME}_EXECUTABLE) + set(${cfp_NAME}_COMMAND "${${cfp_NAME}_EXECUTABLE}" CACHE STRING "") + mark_as_advanced(${cfp_NAME}_EXECUTABLE ${cfp_NAME}_COMMAND) + + execute_process( + COMMAND ${${cfp_NAME}_COMMAND} --version + RESULT_VARIABLE ${cfp_NAME}_version_result + OUTPUT_VARIABLE ${cfp_NAME}_version_output + ERROR_VARIABLE ${cfp_NAME}_version_error + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + + if(${${cfp_NAME}_version_result} EQUAL 0) + if(${cfp_NAME}_version_output MATCHES "([0-9]+\.[0-9]+\.[0-9]+)") + set(${cfp_NAME}_VERSION_STRING "${CMAKE_MATCH_1}") + + string(REGEX REPLACE "([0-9]+)\\.[0-9]+\\.[0-9]+" "\\1" + ${cfp_NAME}_VERSION_MAJOR "${${cfp_NAME}_VERSION_STRING}" + ) + string(REGEX REPLACE "[0-9]+\\.([0-9]+)\\.[0-9]+" "\\1" + ${cfp_NAME}_VERSION_MINOR "${${cfp_NAME}_VERSION_STRING}" + ) + string(REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+)" "\\1" + ${cfp_NAME}_VERSION_PATCH "${${cfp_NAME}_VERSION_STRING}" + ) + endif() + else() + if(NOT ${cfp_NAME}_FIND_QUIETLY) + message(WARNING "${_ClangTidy_log_prefix}: version query failed: ${${cfp_NAME}_version_error}") + endif() + endif() + + unset(${cfp_NAME}_version_result) + unset(${cfp_NAME}_version_output) + unset(${cfp_NAME}_version_error) +endif() + +# handling --------------------------------------------------------------------- +find_package_handle_standard_args(${cfp_NAME} + REQUIRED_VARS ${cfp_NAME}_EXECUTABLE + ${cfp_NAME}_COMMAND + VERSION_VAR ${cfp_NAME}_VERSION_STRING + FAIL_MESSAGE "Installation: https://apt.llvm.org/" +) + +# clean-up --------------------------------------------------------------------- +unset(CFP_NAME) +unset(cfp_NAME) \ No newline at end of file diff --git a/cmake/finders/FindGcov.cmake b/cmake/finders/FindGcov.cmake new file mode 100644 index 0000000..8d1c276 --- /dev/null +++ b/cmake/finders/FindGcov.cmake @@ -0,0 +1,354 @@ +# Author: Vitalii Shylienkov +# License: MIT +# Copyright: (c) 2018-2022 Vitalii Shylienkov + +#[=============================================================================[.rst: +FindGcov +-------- + +Locate ``gcov``-compatible program: + +- ``gcov`` in case of GNU environment +- ``llvm-cov`` for LLVM environment (can emulate ``gcov``) + +Requirements +^^^^^^^^^^^^ + +- At least one of languages ``C`` ``CXX`` should be enabled for the project + +Components +^^^^^^^^^^ + +This module doesn't support modules + +Result variables +^^^^^^^^^^^^^^^^ +This module will set the following variables in your project: + + :cmake:variable:`Gcov_FOUND`, + :cmake:variable:`GCOV_FOUND` + + Found ``gcov`` package + + :cmake:variable:`Gcov_EXECUTABLE` + + Path to the ``gcov``/``llvm-cov`` executable + + :cmake:variable:`Gcov_COMMAND` + + String that has to be used to call ``gcov`` + + :cmake:variable:`Gcov_VERSION_STRING` + + ``gcov`` full version string + + :cmake:variable:`Gcov_VERSION_MAJOR` + + ``gcov`` major version + + :cmake:variable:`Gcov_VERSION_MINOR` + + ``gcov`` minor version + + :cmake:variable:`Gcov_VERSION_PATCH` + + ``gcov`` version patch + +Hints +^^^^^ + +The following variables may be set to provide hints to this module: + + :cmake:variable:`Gcov_DIR`, + :cmake:variable:`GCOV_DIR` + + Path to the installation root of gcov + +Environment variables with the same names will be also checked: + + :cmake:envvar:`Gcov_DIR`, + :cmake:envvar:`GCOV_DIR` + +Example usage +^^^^^^^^^^^^^ + +.. code-block:: cmake + + find_package(Gcov REQUIRED) + + execute_process( + COMMAND ${Gcov_COMMAND} -version + OUTPUT_VARIABLE Gcov_VERSION_RAW + ERROR_VARIABLE Gcov_VERSION_RAW + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + +#]=============================================================================] + +# includes --------------------------------------------------------------------- +include(FeatureSummary) +include(FindPackageHandleStandardArgs) + +# Internal variables ----------------------------------------------------------- +set(cfp_NAME "${CMAKE_FIND_PACKAGE_NAME}") +string(TOUPPER "${cfp_NAME}" CFP_NAME) +set(_Gcov_log_prefix "${_cf_log_prefix}${cfp_NAME}:" CACHE INTERNAL "FindGcov Log prefix") + +# Declare package properties --------------------------------------------------- +set_package_properties(${cfp_NAME} + PROPERTIES + URL "https://gcc.gnu.org/onlinedocs/gcc/Gcov.html" + DESCRIPTION "Tool to test code coverage" +) + +# Validate find_package() arguments -------------------------------------------- +if(${cfp_NAME}_FIND_COMPONENTS AND NOT ${cfp_NAME}_FIND_QUIETLY) + message(WARNING "${_${cfp_NAME}_log_prefix} components not supported") +endif() + +# Helper functions ------------------------------------------------------------- + +#[==[ + Search gcov for GNU environment + + Names to search for (x - major version of GCC): + + ${CROSS_COMPILE}gcov-x # if crosscompilation is enabled and + ${CROSS_COMPILE}gcov # an environment variable CROSS_COMPILE is + # defined + gcov-x + gcov + + Cache Variables + + ``Gcov_DIR`` # may also be set as an environment variable + ``GCOV_DIR`` # may also be set as an environment variable + ``COMPILER_PATH`` + + Result variables + + ``Gcov_EXECUTABLE`` # path to gcov executable + ``Gcov_COMMAND`` # same +#]==] +macro(__gcov_find_gnu_program) + + # get compiler major version + string(REGEX MATCH "^[0-9]+" GCC_VERSION_MAJOR + "${CMAKE_${LANG}_COMPILER_VERSION}" + ) + + # build list of potential names + set(${cfp_NAME}_names gcov gcov-${GCC_VERSION_MAJOR}) + + if(CMAKE_CROSSCOMPILING AND DEFINED ENV{CROSS_COMPILE}) + list(APPEND ${cfp_NAME}_names + $ENV{CROSS_COMPILE}gcov + $ENV{CROSS_COMPILE}gcov-${GCC_VERSION_MAJOR} + ) + endif() + list(REMOVE_DUPLICATES ${cfp_NAME}_names) + list(REVERSE ${cfp_NAME}_names) + + # build list of hints + # compiler path provided by call site + set(${cfp_NAME}_hints "${COMPILER_PATH}") + list(APPEND ${cfp_NAME}_hints ${__gcc_hints}) + foreach(dir ${cfp_NAME}_DIR ${CFP_NAME}_DIR) + if(DEFINED dir) + list(APPEND ${cfp_NAME}_hints "${dir}") + endif() + endforeach() + list(REMOVE_DUPLICATES ${cfp_NAME}_hints) + unset(dir) + + # look for executable + find_program(${cfp_NAME}_EXECUTABLE + NAMES ${${cfp_NAME}_names} + HINTS ${${cfp_NAME}_hints} + ENV ${cfp_NAME}_DIR + ENV ${CFP_NAME}_DIR + ) + + # clean-up + unset(${cfp_NAME}_hints) + unset(${cfp_NAME}_names) + unset(GCC_VERSION_MAJOR) + + # store in cache + if(${cfp_NAME}_EXECUTABLE) + set(${cfp_NAME}_COMMAND "${${cfp_NAME}_EXECUTABLE}" CACHE STRING "") + mark_as_advanced(${cfp_NAME}_EXECUTABLE ${cfp_NAME}_COMMAND) + endif() +endmacro() + +#[==[ + Search gcov for LLVM environment + + Names to search for (x - major version of LLVM, y - minor version): + + ${CROSS_COMPILE}llvm-cov-x.y # if crosscompilation is enabled and + ${CROSS_COMPILE}llvm-cov-x # an environment variable CROSS_COMPILE is + ${CROSS_COMPILE}llvm-cov # defined + llvm-cov-x.y + llvm-cov-x + llvm-cov + + Cache Variables + + ``Gcov_DIR`` # may also be set as an environment variable + ``GCOV_DIR`` # may also be set as an environment variable + ``COMPILER_PATH`` + + Result variables + + ``Gcov_EXECUTABLE`` # path to gcov executable + ``Gcov_COMMAND`` # path to script wrapping call to gcov +#]==] +macro(__gcov_find_llvm_program) + # get compiler major and minor versions + string(REGEX MATCH "^[0-9]+.[0-9]+" LLVM_VERSION_STRING + "${CMAKE_${LANG}_COMPILER_VERSION}" + ) + + # llvm-cov version < 3.5 not supported + if(LLVM_VERSION_STRING VERSION_GREATER 3.4) + string(REGEX REPLACE "^([0-9]+).[0-9]+" "\\1" LLVM_VERSION_MAJOR + "${LLVM_VERSION_STRING}" + ) + + # build list of potential names + set(${cfp_NAME}_names llvm-cov + llvm-cov-${LLVM_VERSION_MAJOR} + llvm-cov-${LLVM_VERSION_STRING} + ) + + if(CMAKE_CROSSCOMPILING AND DEFINED ENV{CROSS_COMPILE}) + list(APPEND ${cfp_NAME}_names $ENV{CROSS_COMPILE}llvm-cov + $ENV{CROSS_COMPILE}llvm-cov-${LLVM_VERSION_MAJOR} + $ENV{CROSS_COMPILE}llvm-cov-${LLVM_VERSION_STRING} + ) + endif() + list(REMOVE_DUPLICATES ${cfp_NAME}_names) + list(REVERSE ${cfp_NAME}_names) + + # build list of hints + # compiler path provided by call site + set(${cfp_NAME}_hints "${COMPILER_PATH}") + list(APPEND ${cfp_NAME}_hints ${__clang_hints}) + foreach(dir ${cfp_NAME}_DIR ${CFP_NAME}_DIR) + if(DEFINED dir) + list(APPEND ${cfp_NAME}_hints "${dir}") + endif() + endforeach() + list(REMOVE_DUPLICATES ${cfp_NAME}_hints) + unset(dir) + + # look for executable + find_program(${cfp_NAME}_EXECUTABLE + NAMES ${${cfp_NAME}_names} + HINTS ${${cfp_NAME}_hints} + ENV ${cfp_NAME}_DIR + ENV ${CFP_NAME}_DIR + ) + + # clean-up + unset(${cfp_NAME}_hints) + unset(${cfp_NAME}_names) + unset(LLVM_VERSION_MAJOR) + unset(LLVM_VERSION_STRING) + + # store in cache + if(${cfp_NAME}_EXECUTABLE) + # create shell script for gcov command + # llvm-cov gcov - emulates gcov + file(WRITE ${CMAKE_BINARY_DIR}/CMakeFiles/gcov + "#!/bin/bash\nexec ${${cfp_NAME}_EXECUTABLE} gcov \"$@\"" + ) + file(COPY ${CMAKE_BINARY_DIR}/CMakeFiles/gcov + DESTINATION ${CMAKE_BINARY_DIR} + FILE_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ + GROUP_EXECUTE GROUP_READ + WORLD_EXECUTE WORLD_READ + ) + set(${cfp_NAME}_COMMAND "${CMAKE_BINARY_DIR}/gcov" CACHE STRING "") + mark_as_advanced(${cfp_NAME}_EXECUTABLE ${cfp_NAME}_COMMAND) + endif() + endif() +endmacro() + +#TODO:VSH proper version handling + +# for each enabled language try to find bonary +get_property(ppt_enabled_languages GLOBAL PROPERTY ENABLED_LANGUAGES) + +foreach(LANG IN LISTS ppt_enabled_languages) + # no language + if(LANG STREQUAL "NONE") + continue() + endif() + + # if Gcov was already found - skip search + if(${cfp_NAME}_${CMAKE_${LANG}_COMPILER_ID}_EXECUTABLE) + continue() + endif() + + # gcov usually placed near to the compiler + get_filename_component(COMPILER_PATH "${CMAKE_${LANG}_COMPILER}" PATH) + + # if we are in llvm environment + if(CMAKE_${LANG}_COMPILER_ID MATCHES "^(Apple)?Clang$") + __gcov_find_llvm_program() + endif() + + # we are in GNU environment or + # llvm-cov was not found - fallback to GNU + if(NOT ${cfp_NAME}_EXECUTABLE) + __gcov_find_gnu_program() + endif() + + if(${cfp_NAME}_EXECUTABLE) + # do not repeat search for this compiler anymore + set(${cfp_NAME}_${CMAKE_${LANG}_COMPILER_ID}_EXECUTABLE "${${cfp_NAME}_EXECUTABLE}" + CACHE FILEPATH "${LANG} gcov binary." + ) + endif() + +endforeach() +unset(ppt_enabled_languages) +unset(LANG) + +# Figuring out gcov version +if(${cfp_NAME}_EXECUTABLE) + execute_process( + COMMAND ${${cfp_NAME}_COMMAND} -version + OUTPUT_VARIABLE ${cfp_NAME}_VERSION_RAW + ERROR_VARIABLE ${cfp_NAME}_VERSION_RAW + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + if(${cfp_NAME}_VERSION_RAW MATCHES "([0-9]+\.[0-9]+\.[0-9]+)") + set(${cfp_NAME}_VERSION_STRING "${CMAKE_MATCH_1}") + string(REGEX REPLACE "([0-9]+)\\.[0-9]+\\.[0-9]+" "\\1" + ${cfp_NAME}_VERSION_MAJOR ${${cfp_NAME}_VERSION_STRING} + ) + string(REGEX REPLACE "[0-9]+\\.([0-9]+)\\.[0-9]+" "\\1" + ${cfp_NAME}_VERSION_MINOR ${${cfp_NAME}_VERSION_STRING} + ) + string(REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+)" "\\1" + ${cfp_NAME}_VERSION_PATCH ${${cfp_NAME}_VERSION_STRING} + ) + endif() +endif() +unset(${cfp_NAME}_VERSION_RAW) + +# FPHSA to cover flags and version +find_package_handle_standard_args(${cfp_NAME} + REQUIRED_VARS ${cfp_NAME}_EXECUTABLE + ${cfp_NAME}_COMMAND + VERSION_VAR ${cfp_NAME}_VERSION_STRING + FAIL_MESSAGE "Install: `sudo apt install gcov`." +) + +# clean-up +unset(cfp_NAME) +unset(CFP_NAME) diff --git a/cmake/finders/FindGcovr.cmake b/cmake/finders/FindGcovr.cmake new file mode 100644 index 0000000..64e4004 --- /dev/null +++ b/cmake/finders/FindGcovr.cmake @@ -0,0 +1,253 @@ +# Author: Vitalii Shylienkov +# License: MIT +# Copyright: (c) 2018-2022 Vitalii Shylienkov + +#[=============================================================================[.rst: +FindGcovr +--------- + +Locates `Gcovr `_, an utility for managing the use +of the ``GNU`` ``gcov`` utility and generating summarized code coverage results. + + +Components +^^^^^^^^^^ + +This module doesn't support components + +Result variables +^^^^^^^^^^^^^^^^ +This module will set the following variables in your project: + + :cmake:variable:`Gcovr_FOUND`, + :cmake:variable:`GCOVR_FOUND` + + Found ``gcovr`` package + + :cmake:variable:`Gcovr_EXECUTABLE` + + Path to the ``gcovr`` executable + + :cmake:variable:`Gcovr_COMMAND` + + String that has to be used to invoke ``gcovr`` + + :cmake:variable:`Gcovr_DIR` + + Root directory of ``gcovr`` installation + + :cmake:variable:`Gcovr_VERSION_STRING` + + ``gcovr`` full version string + + :cmake:variable:`Gcovr_VERSION_MAJOR` + + ``gcovr`` major version + + :cmake:variable:`Gcovr_VERSION_MINOR` + + ``gcovr`` minor version + +Hints +^^^^^ + +The following variables may be set to provide hints to this module: + + :cmake:variable:`Gcovr_DIR`, + :cmake:variable:`GCOVR_DIR` + + Path to the installation root of ``gcovr`` + +Environment variables with the same names will be also checked: + + :cmake:envvar:`Gcovr_DIR`, + :cmake:envvar:`GCOVR_DIR` + +Example usage +^^^^^^^^^^^^^ + +.. code-block:: cmake + + find_package(Gcovr REQUIRED) + + execute_process( + COMMAND ${Gcovr_COMMAND} + ${CMAKE_BINARY_DIR}/coverage + --root ${CMAKE_SOURCE_DIR} + --gcov-executable ${Gcov_COMMAND} + --output coverage.txt + ) + +#]=============================================================================] + +# includes --------------------------------------------------------------------- +include(FeatureSummary) +include(FindPackageHandleStandardArgs) + +# Internal variables ----------------------------------------------------------- +set(cfp_NAME "${CMAKE_FIND_PACKAGE_NAME}") +string(TOUPPER "${cfp_NAME}" CFP_NAME) +set(_Gcovr_log_prefix "${_cf_log_prefix}${cfp_NAME}:" CACHE INTERNAL "FindGcovr Log prefix") + +# Declare package properties --------------------------------------------------- +set_package_properties(${cfp_NAME} + PROPERTIES + URL "https://gcovr.com/en/stable/" + DESCRIPTION "An utility for managing the use of the GNU gcov utility and generating summarized code coverage results" +) + +# Validate find_package() arguments -------------------------------------------- +# No components supported +if(${cfp_NAME}_FIND_COMPONENTS AND NOT ${cfp_NAME}_FIND_QUIETLY) + message(WARNING "${_${cfp_NAME}_log_prefix} components not supported") +endif() + +# Helper functions ------------------------------------------------------------- +macro(__gcovr_parse_shebang _line) + set(_OPTIONS "") + # exclude heading or trailing whitespaces + string(REGEX REPLACE "^ +| +$" "" _line "${_line}") + + if(_line MATCHES "^#!([^ ]*/python.*)") + set(_COMMAND "${CMAKE_MATCH_1}") + if(_COMMAND MATCHES "([^ ]+) (.*)") + set(_COMMAND "${CMAKE_MATCH_1}") + + # options transformed to cmake ;-list + string (REGEX REPLACE " +" ";" _OPTIONS "${CMAKE_MATCH_2}") + endif () + elseif(_line MATCHES "^#!(.*env python.*)$") + set(_COMMAND "${CMAKE_MATCH_1}") + if(_COMMAND MATCHES "([^ ]+) (python[23]?)(.*)") + # command + set(_COMMAND "${CMAKE_MATCH_2}") + + # options transformed to cmake ;-list + string (REGEX REPLACE " +" ";" _OPTIONS "${CMAKE_MATCH_3}") + endif () + else() + set(_COMMAND NOTFOUND) + endif() + unset(_line) +endmacro() + + +# build list of hints +set(${cfp_NAME}_hints "") +foreach(dir ${cfp_NAME}_DIR ${CFP_NAME}_DIR) + if(DEFINED ${dir}) + list(APPEND ${cfp_NAME}_hints "${${dir}}") + endif() +endforeach() +unset(dir) + +# Gcovr can be installed into Python user script directory: +# Unix: ~/.local/bin +# Windows: %APPDATA%/Python/Scripts +# https://www.python.org/dev/peps/pep-0370 +find_package(Python QUIET COMPONENTS Interpreter) + +if(Python_Interpreter_FOUND) + execute_process( + COMMAND ${Python_EXECUTABLE} -m site --user-base + RESULT_VARIABLE USER_BASE_DIR_RESULT + OUTPUT_VARIABLE USER_BASE_DIR_OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(USER_BASE_DIR_RESULT EQUAL 0) + list(APPEND ${cfp_NAME}_hints "${USER_BASE_DIR_OUTPUT}") + endif() + unset(USER_BASE_DIR_RESULT) + unset(USER_BASE_DIR_OUTPUT) +endif() +list(REMOVE_DUPLICATES ${cfp_NAME}_hints) + +# look for executable +find_program(${cfp_NAME}_EXECUTABLE + NAMES gcovr + gcovr.py + HINTS ${cfp_NAME}_hints + ENV ${cfp_NAME}_DIR + ENV ${CFP_NAME}_DIR + PATH_SUFFIXES bin Scripts + DOC "The ${cfp_NAME} executable" +) +unset(${cfp_NAME}_hints) + +# build up command +if(${cfp_NAME}_EXECUTABLE) + mark_as_advanced(${cfp_NAME}_EXECUTABLE) + + file(STRINGS "${${cfp_NAME}_EXECUTABLE}" SHEBANG LIMIT_COUNT 1) + + __gcovr_parse_shebang(${SHEBANG}) + unset(SHEBANG) + + if(_COMMAND) + # escape any special charecter + string(REGEX REPLACE "([.+*?^$])" "\\\\\\1" + _Pythong_EXECUTABLE_ESCEPED "${Python_EXECUTABLE}" + ) + + list(FIND _OPTIONS -E INDEX) + if((INDEX EQUAL -1) AND + (NOT _COMMAND MATCHES "^${_Pythong_EXECUTABLE_ESCEPED}$")) + list(INSERT _OPTIONS 0 -E) + endif() + + set(${cfp_NAME}_COMMAND "${_COMMAND};${_OPTIONS};${${cfp_NAME}_EXECUTABLE}" CACHE STRING "") + + unset(_Pythong_EXECUTABLE_ESCEPED) + unset(INDEX) + unset(_COMMAND) + unset(_OPTIONS) + else() + set(${cfp_NAME}_COMMAND "${${cfp_NAME}_EXECUTABLE}" CACHE STRING "") + endif() + mark_as_advanced(${cfp_NAME}_COMMAND) +endif() + + +# Figuring out gcovr version +if(${cfp_NAME}_COMMAND) + execute_process( + COMMAND ${${cfp_NAME}_COMMAND} --version + OUTPUT_VARIABLE ${cfp_NAME}_VERSION_RAW + ERROR_VARIABLE ${cfp_NAME}_VERSION_RAW + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE + ) + + if(${cfp_NAME}_VERSION_RAW MATCHES "gcovr ([.0-9]+)") + set(${cfp_NAME}_VERSION_STRING "${CMAKE_MATCH_1}") + + string(REGEX REPLACE "([0-9]+)\\.[0-9]+" "\\1" + ${cfp_NAME}_VERSION_MAJOR ${${cfp_NAME}_VERSION_STRING} + ) + string(REGEX REPLACE "[0-9]+\\.([0-9]+)" "\\1" + ${cfp_NAME}_VERSION_MINOR ${${cfp_NAME}_VERSION_STRING} + ) + endif() +endif() +unset(${cfp_NAME}_VERSION_RAW) + +# Get gcovr installation dir +if(${cfp_NAME}_EXECUTABLE) + get_filename_component(${cfp_NAME}_DIR "${${cfp_NAME}_EXECUTABLE}" PATH) + string(REGEX REPLACE "/bin/?$" "" ${cfp_NAME}_DIR "${${cfp_NAME}_DIR}") + # cache it + set(${cfp_NAME}_DIR "${${cfp_NAME}_DIR}" CACHE PATH "${cfp_NAME} installation path") +endif() + +# handle components, version, quiet, required and other flags +find_package_handle_standard_args(${cfp_NAME} + REQUIRED_VARS ${cfp_NAME}_EXECUTABLE + ${cfp_NAME}_COMMAND + ${cfp_NAME}_DIR + VERSION_VAR ${cfp_NAME}_VERSION_STRING + FAIL_MESSAGE "Install: `python3 -m pip install -U gcovr`.\nRemove default: `sudo apt remove gcovr`\n" +) + +# clean-up +unset(cfp_NAME) +unset(CFP_NAME) diff --git a/cmake/finders/FindIncludeWhatYouUse.cmake b/cmake/finders/FindIncludeWhatYouUse.cmake new file mode 100644 index 0000000..576aefd --- /dev/null +++ b/cmake/finders/FindIncludeWhatYouUse.cmake @@ -0,0 +1,165 @@ +# Author: Vitalii Shylienkov +# License: MIT +# Copyright: (c) 2018-2022 Vitalii Shylienkov + +#[=============================================================================[.rst: +FindIncludeWhatYouUse +--------------------- + +Locates `include-what-you-use `_, +a tool for use with clang to analyze #includes in C and C++ source files. + + +Components +^^^^^^^^^^ + +This module doesn't support components + +Result variables +^^^^^^^^^^^^^^^^ +This module will set the following variables in your project: + + :cmake:variable:`IncludeWhatYouUse_FOUND`, + :cmake:variable:`INCLUDEWHATYOUUSE_FOUND` + + Found ``IncludeWhatYouUse`` package + + :cmake:variable:`IncludeWhatYouUse_EXECUTABLE` + + Path to the ``include-what-you-use`` executable + + :cmake:variable:`IncludeWhatYouUse_COMMAND` + + String that has to be used to invoke ``include-what-you-use`` + + :cmake:variable:`IncludeWhatYouUse_VERSION_STRING` + + ``include-what-you-use`` full version string + + :cmake:variable:`IncludeWhatYouUse_VERSION_MAJOR` + + ``include-what-you-use`` major version + + :cmake:variable:`IncludeWhatYouUse_VERSION_MINOR` + + ``include-what-you-use`` minor version + +Hints +^^^^^ + +The following variables may be set to provide hints to this module: + + :cmake:variable:`IncludeWhatYouUse_DIR`, + :cmake:variable:`INCLUDEWHATYOUUSE_DIR` + + Path to the installation root of ``include-what-you-use`` + +Environment variables with the same names will be also checked: + + :cmake:envvar:`IncludeWhatYouUse_DIR`, + :cmake:envvar:`INCLUDEWHATYOUUSE_DIR` + +Example usage +^^^^^^^^^^^^^ + +.. code-block:: cmake + + find_package(IncludeWhatYouUse REQUIRED) + + set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "${IncludeWhatYouUse_COMMAND}") + +#]=============================================================================] + +# includes --------------------------------------------------------------------- +include(FeatureSummary) +include(FindPackageHandleStandardArgs) + +# Internal variables ----------------------------------------------------------- +set(cfp_NAME "${CMAKE_FIND_PACKAGE_NAME}") +string(TOUPPER "${cfp_NAME}" CFP_NAME) +set(_IncludeWhatYouUse_log_prefix "${_cf_log_prefix}${cfp_NAME}:" + CACHE INTERNAL "FindIncludeWhatYouUse Log prefix" +) + +# Declare package properties --------------------------------------------------- +set_package_properties(${cfp_NAME} + PROPERTIES + URL "https://include-what-you-use.org/" + DESCRIPTION "A tool for use with clang to analyze #includes in C and C++ source files" +) + +# Validate find_package() arguments -------------------------------------------- +# No components supported +if(${cfp_NAME}_FIND_COMPONENTS AND NOT ${cfp_NAME}_FIND_QUIETLY) + message(WARNING "${_IncludeWhatYouUse_log_prefix} components not supported") +endif() + +# build list of hints +set(${cfp_NAME}_hints "") +foreach(dir ${cfp_NAME}_DIR ${CFP_NAME}_DIR) + if(DEFINED ${dir}) + list(APPEND ${cfp_NAME}_hints "${${dir}}") + endif() +endforeach() +unset(dir) + + +# Find binary ------------------------------------------------------------------ + +find_program(${cfp_NAME}_EXECUTABLE + NAMES include-what-you-use + HINTS ${${cfp_NAME}_hints} + ENV ${cfp_NAME}_DIR + ENV ${CFP_NAME}_DIR + PATH_SUFFIXES bin + DOC "The ${cfp_NAME} executable" +) +unset(${cfp_NAME}_hints) + +# Figure out the version ------------------------------------------------------- + +if(${cfp_NAME}_EXECUTABLE) + set(${cfp_NAME}_COMMAND "${${cfp_NAME}_EXECUTABLE}" CACHE STRING "") + mark_as_advanced(${cfp_NAME}_EXECUTABLE ${cfp_NAME}_COMMAND) + + execute_process( + COMMAND ${${cfp_NAME}_COMMAND} --version + RESULT_VARIABLE ${cfp_NAME}_version_result + OUTPUT_VARIABLE ${cfp_NAME}_version_output + ERROR_VARIABLE ${cfp_NAME}_version_error + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + if(${${cfp_NAME}_version_result} EQUAL 0) + if(${cfp_NAME}_version_output MATCHES "include-what-you-use ([0-9]+\.[0-9]+)") + set(${cfp_NAME}_VERSION_STRING "${CMAKE_MATCH_1}") + + string(REGEX REPLACE "([0-9]+)\\.[0-9]+" "\\1" + ${cfp_NAME}_VERSION_MAJOR "${${cfp_NAME}_VERSION_STRING}" + ) + string(REGEX REPLACE "[0-9]+\\.([0-9]+)" "\\1" + ${cfp_NAME}_VERSION_MINOR "${${cfp_NAME}_VERSION_STRING}" + ) + endif() + else() + if(NOT ${cfp_NAME}_FIND_QUIETLY) + message(WARNING "${_IncludeWhatYouUse_log_prefix}: version query failed: ${${cfp_NAME}_version_error}") + endif() + endif() + + unset(${cfp_NAME}_version_result) + unset(${cfp_NAME}_version_output) + unset(${cfp_NAME}_version_error) +endif() + +# handling +find_package_handle_standard_args(${cfp_NAME} + REQUIRED_VARS ${cfp_NAME}_EXECUTABLE + ${cfp_NAME}_COMMAND + VERSION_VAR ${cfp_NAME}_VERSION_STRING + FAIL_MESSAGE "Installation: https://github.com/include-what-you-use/include-what-you-use#how-to-install" +) + +# clean-up --------------------------------------------------------------------- +unset(CFP_NAME) +unset(cfp_NAME) \ No newline at end of file diff --git a/cmake/modules/CodeCoverage.cmake b/cmake/modules/CodeCoverage.cmake new file mode 100644 index 0000000..1d4d44b --- /dev/null +++ b/cmake/modules/CodeCoverage.cmake @@ -0,0 +1,274 @@ +# Author: Vitalii Shylienkov +# License: MIT +# Copyright: (c) 2018-2022 Vitalii Shylienkov + +#[=============================================================================[.rst: +CodeCoverage +------------ + +This file adds functions and utility targets to collect code coverage statistics +in ``txt`` and ``html`` formats. + +Synopsis +^^^^^^^^ + +One can mark all targets coverage of which is required. To mark target see +:cmake:command:`target_setup_coverage`. After the build step first group of +auxiliary files will be created. Then invoke the test binaries to generate the +second group of auxiliary files. And after use utility target ``coverage-report-notest`` +to generate coverage report. + +#]=============================================================================] + +# include guard ---------------------------------------------------------------- +include_guard(GLOBAL) + +# coverage may be set up from another CodeCoverage.cmake file +# include_guard doesn't work here +# use our own guard mechanism +if(TARGET coverage) + return() +endif() + +get_filename_component(_module_NAME ${CMAKE_CURRENT_LIST_FILE} NAME_WE) +set(_CodeCoverage_log_prefix "${_cf_log_prefix}${_module_NAME}:" + CACHE INTERNAL "CodeCoverage Log prefix" +) + +message(STATUS "${_CodeCoverage_log_prefix} included") + +#[=============================================================================[.rst: +Options +^^^^^^^ + +This file introduce several options, all of them ``OFF`` by default. + +- :cmake:option:`COVERAGE_SORT_LINES` + + Sort coverage report by lines covered + +- :cmake:option:`COVERAGE_SORT_PERCENTAGE` + + Sort coverage report by percent of coverage + +.. note:: + + Options :cmake:option:`COVERAGE_SORT_LINES` and :cmake:option:`COVERAGE_SORT_PERCENTAGE` + are exclusive and shouldn't be both set at time. +#]=============================================================================] +include(FeatureSummary) + +option(COVERAGE_SORT_LINES "Sort coverage report by lines covered" OFF) +add_feature_info([GLOBAL].CoverageSortedLines + COVERAGE_SORT_LINES + "Sort entries by increasing number of uncovered lines." +) + +option(COVERAGE_SORT_PERCENTAGE + "Sort coverage report by percent of coverage" + OFF +) +add_feature_info([GLOBAL].CoverageSortedPercents + COVERAGE_SORT_PERCENTAGE + "Sort entries by increasing percentage of uncovered lines." +) + +#[=============================================================================[.rst: +Requirements +^^^^^^^^^^^^ + +Requires: + +- ``gcov`` + + Uses :cmake:module:`FindGcov` to obtain ``gcov`` command. + +- ``gcovr`` + + Uses :cmake:module:`FindGcovr` to obtain ``gcovr`` command. + +#]=============================================================================] +# Environment validation ------------------------------------------------------- +find_package(Gcov REQUIRED) +find_package(Gcovr 4.3 REQUIRED) + + +#[=============================================================================[.rst: +Variables +^^^^^^^^^ + +Client of the module can define next variables to control the behavior: + +- :cmake:variable:`COVERAGE_PRODUCTS_DIR` + + Path to the directory where auxiliary files will be stored + +- :cmake:variable:`COVERAGE_REPORT_DIR` + + Path to the directory where report files will be stored + +- :cmake:variable:`COVERAGE_REPORT_TXT_FILE` + + Path to the coverage report in ``txt`` format + +- :cmake:variable:`COVERAGE_REPORT_HTML_FILE` + + Path to the coverage report in ``html`` format + +.. note:: + + Some default values will be set if nothing provided. + +#]=============================================================================] +# Variables -------------------------------------------------------------------- +if(NOT DEFINED COVERAGE_PRODUCTS_DIR) + set(COVERAGE_PRODUCTS_DIR ${CMAKE_BINARY_DIR}/coverage) +endif() +if(NOT DEFINED COVERAGE_REPORT_DIR) + set(COVERAGE_REPORT_DIR ${CMAKE_BINARY_DIR}/coverage_results) +endif() +if(NOT DEFINED COVERAGE_REPORT_TXT_FILE) + set(COVERAGE_REPORT_TXT_FILE ${COVERAGE_REPORT_DIR}/coverage_results.txt) +endif() +if(NOT DEFINED COVERAGE_REPORT_HTML_FILE) + set(COVERAGE_REPORT_HTML_FILE ${COVERAGE_REPORT_DIR}/index.html) +endif() + +set(Gcovr_PARAMS ${COVERAGE_PRODUCTS_DIR} + --root ${CMAKE_SOURCE_DIR} + --gcov-executable ${Gcov_COMMAND} + $<$:--sort-uncovered> + $<$:--sort-percentage> +) + +#[=============================================================================[.rst: +Utility Targets +^^^^^^^^^^^^^^^ + +Introduces targets: + +- ``coverage-clean`` + + Run clean-up to wipe the results of the coverage analysis, including auxiliary + files. + +- ``coverage-report-notest`` + + Collect code coverage counters without running all the tests. + +- ``coverage-report`` + + Collect code coverage counters, running all the tests beforehand. + +.. note:: + + To run tests utility target ``test`` will be used. In case it is defined. + +- ``coverage`` + + Alias to ``coverage-report`` + +#]=============================================================================] + +# Targets ---------------------------------------------------------------------- +add_custom_target(coverage-clean + COMMAND + ${CMAKE_COMMAND} -E remove -f ${COVERAGE_PRODUCTS_DIR}/*.gcda + COMMAND + ${CMAKE_COMMAND} -E remove -f ${COVERAGE_PRODUCTS_DIR}/*.gcno + COMMAND + ${CMAKE_COMMAND} -E remove -f ${COVERAGE_REPORT_DIR}/*.html + COMMAND + ${CMAKE_COMMAND} -E remove -f ${COVERAGE_REPORT_TXT_FILE} + COMMENT + "${_CodeCoverage_log_prefix} cleaning: code coverage counters and reports" +) + +add_custom_target(coverage-report-notest + COMMAND + ${CMAKE_COMMAND} -E make_directory ${COVERAGE_REPORT_DIR} + COMMAND + ${CMAKE_COMMAND} -E make_directory ${COVERAGE_PRODUCTS_DIR} + COMMAND + find ${CMAKE_BINARY_DIR}/ -type f \"\(\" -name \"*.gcda\" -o -name \"*.gcno\" \"\)\" + -exec cp -u -t ${COVERAGE_PRODUCTS_DIR}/ {} + + COMMAND + ${Gcovr_EXECUTABLE} ${Gcovr_PARAMS} + $<$>:$> + --output ${COVERAGE_REPORT_TXT_FILE} + COMMAND + ${Gcovr_EXECUTABLE} ${Gcovr_PARAMS} --html-details --print-summary + $<$>:$> + --output ${COVERAGE_REPORT_HTML_FILE} + COMMENT + "${_CodeCoverage_log_prefix} processing: code coverage counters and generating report" + DEPENDS + coverage-clean + COMMAND_EXPAND_LISTS +) + +add_custom_target(coverage-report DEPENDS coverage-report-notest) +add_custom_target(coverage DEPENDS coverage-report) + +if(TARGET test) + add_dependencies(coverage-report test) +endif() + +#[=============================================================================[.rst: +Commands +^^^^^^^^ + +.. cmake:command:: target_setup_coverage + +Marks target to be analyzed with code coverage + +.. code-block:: cmake + + target_setup_coverage( [EXCLUDE [ ...]) + +This command set needed compilation and linkage flags to the target ````. + + ``EXCLUDE`` + + Allows to set regular expressions to exclude directories or files from + coverage analysis. + +#]=============================================================================] +# Functions -------------------------------------------------------------------- +function(target_setup_coverage target) + + # Parameters verification -------------------------------------------------- + if(NOT TARGET ${target}) + message(FATAL_ERROR "${_CodeCoverage_log_prefix} target not found: ${target}") + endif() + + # Find out un-aliased name of the target + get_target_property(TARGET_ALIASED_TARGET ${target} ALIASED_TARGET) + if(TARGET_ALIASED_TARGET) + set(target ${TARGET_ALIASED_TARGET}) + endif() + + # parse EXCLUDE option + cmake_parse_arguments(_PARAM "" "" "EXCLUDE" ${ARGN}) + if(_PARAM_EXCLUDE) + list(TRANSFORM _PARAM_EXCLUDE PREPEND "\"") + list(TRANSFORM _PARAM_EXCLUDE APPEND "\"") + list(TRANSFORM _PARAM_EXCLUDE PREPEND "--exclude;") + + set_property(TARGET coverage-report-notest + APPEND + PROPERTY + EXCLUDE ${_PARAM_EXCLUDE} + ) + endif() + + + message(STATUS "${_CodeCoverage_log_prefix} setup coverage for target: ${target}") + + # Target settings ---------------------------------------------------------- + target_compile_options(${target} PRIVATE --coverage) + target_link_libraries(${target} PRIVATE --coverage) +endfunction() + +# clean-up --------------------------------------------------------------------- +unset(_module_NAME) diff --git a/cmake/modules/CommonOptions.cmake b/cmake/modules/CommonOptions.cmake new file mode 100644 index 0000000..72fae08 --- /dev/null +++ b/cmake/modules/CommonOptions.cmake @@ -0,0 +1,146 @@ +# Author: Vitalii Shylienkov +# License: MIT +# Copyright: (c) 2018-2022 Vitalii Shylienkov + +#[=============================================================================[.rst: +CommonOptions +------------- + +This file introduces set of commonly used options which project may utilize. + +.. note:: + + All options are ``Off`` by default. + +Options +^^^^^^^ +#]=============================================================================] + +# include guard ---------------------------------------------------------------- +include_guard(GLOBAL) + +get_filename_component(_module_NAME ${CMAKE_CURRENT_LIST_FILE} NAME_WE) +set(_CommonOptions_log_prefix "${_cf_log_prefix}${_module_NAME}:" + CACHE INTERNAL "CommonOptions Log prefix" +) + +message(STATUS "${_CommonOptions_log_prefix} included") + +include(FeatureSummary) + +#[=============================================================================[.rst: + +- :cmake:variable:`WITH_DOCUMENTING` + + Generate documentation for all projects + +#]=============================================================================] + +option(WITH_DOCUMENTING "Generate documentation for all projects" OFF) +add_feature_info([ALL].Documenting WITH_DOCUMENTING + "Generate documentation for all projects" +) + +#[=============================================================================[.rst: + +- :cmake:variable:`WITH_UNIT_TEST` + + Build unit tests for all projects + +#]=============================================================================] + +option(WITH_UNIT_TEST "Build unit tests for all projects" OFF) +add_feature_info([ALL].Unit-tests WITH_UNIT_TEST + "Build unit tests for all projects" +) + +#[=============================================================================[.rst: + +- :cmake:variable:`WITH_COMPONENT_TEST` + + Build component tests for all projects + +#]=============================================================================] + +option(WITH_COMPONENT_TEST "Build component tests for all projects" OFF) +add_feature_info([ALL].Component-tests WITH_COMPONENT_TEST + "Build component tests for all projects" +) + +#[=============================================================================[.rst: + +- :cmake:variable:`WITH_SW_ELEMENT_TEST` + + Build software element tests for all projects + +#]=============================================================================] + +option(WITH_SW_ELEMENT_TEST "Build software element tests for all projects" OFF) +add_feature_info([ALL].Software-Element-tests WITH_SW_ELEMENT_TEST + "Build software element tests for all projects" +) + +#[=============================================================================[.rst: + +- :cmake:variable:`WITH_COVERAGE` + + Generate code coverage counters for all projects + +#]=============================================================================] + +option(WITH_COVERAGE "Generate code coverage counters for all projects" OFF) +add_feature_info([ALL].Coverage WITH_COVERAGE + "Generate code coverage counters for all projects" +) + +#[=============================================================================[.rst: + +- :cmake:variable:`WITH_EXAMPLE` + + Build examples for all projects + +#]=============================================================================] + +option(WITH_EXAMPLE "Build examples for all projects" OFF) +add_feature_info([ALL].Example WITH_EXAMPLE + "Build examples for all projects" +) + +#[=============================================================================[.rst: + +- :cmake:variable:`ENGINEERING_BUILD` + + Build all projects as engineering variant + +#]=============================================================================] + +option(ENGINEERING_BUILD "Build all projects as engineering variant" OFF) +add_feature_info([ALL].Engineering-build ENGINEERING_BUILD + "Build all projects as engineering variant" +) + +#[=============================================================================[.rst: + +- :cmake:variable:`WITH_INCLUDE_WHAT_YOU_USE` + + Build with include-what-you-use globally enabled + +#]=============================================================================] + +option(WITH_INCLUDE_WHAT_YOU_USE "Build with include-what-you-use globally enabled" OFF) +add_feature_info([GLOBAL].include-what-you-use WITH_INCLUDE_WHAT_YOU_USE + "Build with include-what-you-use globally enabled" +) + +#[=============================================================================[.rst: + +- :cmake:variable:`WITH_CLANG_TIDY` + + Build with clang-tidy globally enabled + +#]=============================================================================] + +option(WITH_CLANG_TIDY "Build with clang-tidy globally enabled" OFF) +add_feature_info([GLOBAL].clang-tidy WITH_CLANG_TIDY + "Build with clang-tidy globally enabled" +) diff --git a/cmake/modules/UseClangTidy.cmake b/cmake/modules/UseClangTidy.cmake new file mode 100644 index 0000000..8198284 --- /dev/null +++ b/cmake/modules/UseClangTidy.cmake @@ -0,0 +1,167 @@ +# Author: Vitalii Shylienkov +# License: MIT +# Copyright: (c) 2018-2022 Vitalii Shylienkov + +#[=============================================================================[.rst: +UseClangTidy +------------ + +This file provides support for `clang-tidy `_. +See `CMAKE__CLANG_TIDY `_ +for details. + +Synopsis +^^^^^^^^ + +Two main ways to generate static code analysis: + +- in-build generation + + During build step of a project clang-tidy check every C/C++ source file. + +.. warning:: + + CMake will compile source code with ``clang`` which emulates the behavior + of the compiler CMake detects by default or provided via toolchain files or + configuration step parameters. + +- out-of-build generation + + Using command :cmake:command:`target_setup_clang_tidy` to mark those target for which + ``clang-tidy`` has to run and then use custom target ``run-clang-tidy``. + +.. warning:: + + Files generated in build time will not exist and may lead to analysis errors. + +#]=============================================================================] + + +# include guard ---------------------------------------------------------------- +include_guard(GLOBAL) + +get_filename_component(_module_NAME ${CMAKE_CURRENT_LIST_FILE} NAME_WE) +set(_UseClangTidy_log_prefix "${_cf_log_prefix}${_module_NAME}:" + CACHE INTERNAL "UseClangTidy Log prefix" +) + +message(STATUS "${_UseClangTidy_log_prefix} included") + + +#[=============================================================================[.rst: +Requirements +^^^^^^^^^^^^ + +Requires: + +- ``clang-tidy`` + + Uses :cmake:module:`FindClangTidy` to obtain ``clang-tidy`` command. +#]=============================================================================] +find_package(ClangTidy REQUIRED) + +get_property(enabled_languages GLOBAL PROPERTY ENABLED_LANGUAGES) + +foreach(language C CXX) + if((language IN_LIST enabled_languages) AND (NOT DEFINED CMAKE_${language}_CLANG_TIDY)) + set(CMAKE_${language}_CLANG_TIDY "${ClangTidy_COMMAND}") + endif() +endforeach() + +unset(language) +unset(enabled_languages) + +# produce compilation database +#[=============================================================================[.rst: +Compilation database +^^^^^^^^^^^^^^^^^^^^ +.. note:: + + For producing static code analysis diagnostics in out-of-build variant compilation + database will be generated. + +#]=============================================================================] +set(CMAKE_EXPORT_COMPILE_COMMANDS true) + +#[=============================================================================[.rst: +Custom Targets +^^^^^^^^^^^^^^ + +Introduces targets: + +- ``run-clang-tidy`` + + Global target to run ``clang-tidy`` in out-of-build variant. + +.. code-block:: shell + + # configure step + cmake .. -DWITH_CLANG_TIDY=ON + cmake --build . --target run-clang-tidy + +#]=============================================================================] +add_custom_target(run-clang-tidy + COMMENT "Static Code Analysis: clang-tidy" +) + +#[=============================================================================[.rst: +Commands +^^^^^^^^ + +.. cmake:command:: target_setup_clang_tidy + +Marks target to be analyzed by ``clang-tidy`` in out-of-build variant:: + + target_setup_clang_tidy() + +This command collects sources for the target ```` and create custom +target ``clang-tidy-`` in order to run ``clang-tidy`` for this source +files. This target will be run in scope of execution ``run-clang-tidy`` target. + +#]=============================================================================] +# Functions -------------------------------------------------------------------- +function(target_setup_clang_tidy target) + + # Parameters verification -------------------------------------------------- + if(NOT TARGET ${target}) + message(FATAL_ERROR "${_UseClangTidy_log_prefix} target not found: ${target}") + endif() + + # Find out un-aliased name of the target + get_target_property(target_aliased_target ${target} ALIASED_TARGET) + if(target_aliased_target) + set(target ${target_aliased_target}) + endif() + unset(target_aliased_target) + + # Prevent duplication of the target names + set(clang_tidy_target "clang-tidy-${target}") + if(NOT TARGET clang_tidy_target) + message(STATUS "${_UseClangTidy_log_prefix} setup clang tidy for target: ${target}") + + # collect target sources + get_target_property(target_sources ${target} SOURCES) + list(FILTER target_sources EXCLUDE REGEX ".*\.hpp") + + # create custom target to analyze sources + add_custom_target(${clang_tidy_target} + COMMAND + "${ClangTidy_COMMAND}" + --quiet + -p "${CMAKE_BINARY_DIR}" + ${target_sources} + DEPENDS ${CMAKE_BINARY_DIR}/compile_commands.json ${target_sources} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + VERBATIM + COMMAND_EXPAND_LISTS + COMMENT "Static Code Analysis: clang-tidy: ${target}" + ) + unset(target_sources) + endif() + + add_dependencies(run-clang-tidy ${clang_tidy_target}) + unset(clang_tidy_target) +endfunction() + +# clean-up --------------------------------------------------------------------- +unset(_module_NAME) diff --git a/cmake/modules/UseIncludeWhatYouUse.cmake b/cmake/modules/UseIncludeWhatYouUse.cmake new file mode 100644 index 0000000..793caae --- /dev/null +++ b/cmake/modules/UseIncludeWhatYouUse.cmake @@ -0,0 +1,55 @@ +# Author: Vitalii Shylienkov +# License: MIT +# Copyright: (c) 2018-2022 Vitalii Shylienkov + +#[=============================================================================[.rst: +UseIncludeWhatYouUse +-------------------- + +This file provides support for `include-what-you-use `_. +See `CMAKE__INCLUDE_WHAT_YOU_USE +`_ +for details. + +Synopsis +^^^^^^^^ + +Pre-compile step will be added to each compilation run to validate the set of +included files + +#]=============================================================================] + +# include guard ---------------------------------------------------------------- +include_guard(GLOBAL) + +get_filename_component(_module_NAME ${CMAKE_CURRENT_LIST_FILE} NAME_WE) +set(_UseIncludeWhatYouUse_log_prefix "${_cf_log_prefix}${_module_NAME}:" + CACHE INTERNAL "UseIncludeWhatYouUse Log prefix" +) + +message(STATUS "${_UseIncludeWhatYouUse_log_prefix} included") + +#[=============================================================================[.rst: +Requirements +^^^^^^^^^^^^ + +Requires: + +- ``include-what-you-use`` + + Uses :cmake:module:`FindIncludeWhatYouUse` to obtain ``include-what-you-use`` + command. +#]=============================================================================] +find_package(IncludeWhatYouUse REQUIRED) + +get_property(enabled_languages GLOBAL PROPERTY ENABLED_LANGUAGES) + +foreach(language C CXX) + if((language IN_LIST enabled_languages) AND (NOT DEFINED CMAKE_${language}_INCLUDE_WHAT_YOU_USE)) + set(CMAKE_${language}_INCLUDE_WHAT_YOU_USE "${IncludeWhatYouUse_COMMAND}") + endif() +endforeach() + +# clean-up --------------------------------------------------------------------- +unset(language) +unset(enabled_languages) \ No newline at end of file diff --git a/cmake/templates/crc.pc.in b/cmake/templates/crc.pc.in new file mode 100644 index 0000000..701936d --- /dev/null +++ b/cmake/templates/crc.pc.in @@ -0,0 +1,9 @@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ + +Name: @PROJECT_NAME@ +Description: @PROJECT_DESCRIPTION@ +Version: @PROJECT_VERSION@ +URL: @PROJECT_HOMEPAGE_URL@ +Libs: -L${libdir} -l@PROJECT_NAME@ +Cflags: -I${includedir} \ No newline at end of file diff --git a/cmake/templates/crcConfig.cmake.in b/cmake/templates/crcConfig.cmake.in new file mode 100644 index 0000000..abe2d9a --- /dev/null +++ b/cmake/templates/crcConfig.cmake.in @@ -0,0 +1,7 @@ +@PACKAGE_INIT@ + +foreach(package_export @PACKAGE_EXPORTS@) + include(${CMAKE_CURRENT_LIST_DIR}/${package_export}Targets.cmake) +endforeach() + +check_required_components(@PROJECT_NAME@) \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..cdd9e16 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,21 @@ +# Requirements to the cmake ---------------------------------------------------- +cmake_minimum_required(VERSION 3.12) + +# Dependency resolving --------------------------------------------------------- +if(NOT TARGET crc::crc) + find_package(crc REQUIRED) +endif() + +# Target Declaration ----------------------------------------------------------- +add_executable(tstcrc) + +# Target Definition ------------------------------------------------------------ +target_sources(tstcrc + PRIVATE + tstcrc.c +) + +target_link_libraries(tstcrc + PRIVATE + crc::crc +) diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 0000000..63b6c98 --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,34 @@ +# Requirements to the cmake ---------------------------------------------------- +cmake_minimum_required(VERSION 3.12) + +# Target Declaration ----------------------------------------------------------- +add_library(crc_common INTERFACE) +add_library(${PACKAGE_NAMESPACE}::common ALIAS crc_common) + +set_target_properties(crc_common + PROPERTIES + EXPORT_NAME common +) + +# Target Definition ------------------------------------------------------------ +target_include_directories(crc_common + INTERFACE + $ + $ +) + +# Installations ---------------------------------------------------------------- +## Targets installation +install(TARGETS crc_common EXPORT commonTargets) + +## CMake's configurations +install(EXPORT commonTargets + NAMESPACE ${PACKAGE_NAMESPACE}:: + DESTINATION ${PACKAGE_INSTALL_CMAKE_DIR} +) + +## Install files +install(FILES checksum.h + DESTINATION ${PACKAGE_INSTALL_INCLUDE_DIR} + COMPONENT library +) diff --git a/precalc/CMakeLists.txt b/precalc/CMakeLists.txt new file mode 100644 index 0000000..e842835 --- /dev/null +++ b/precalc/CMakeLists.txt @@ -0,0 +1,27 @@ +# Requirements to the cmake ---------------------------------------------------- +cmake_minimum_required(VERSION 3.12) + +# Target Declaration ----------------------------------------------------------- +add_executable(prc) + +# Target Definition ------------------------------------------------------------ + +target_sources(prc + PRIVATE + crc32_table.c + crc64_table.c + precalc.c +) + +target_include_directories(prc + PRIVATE + $ +) + +target_link_libraries(prc + PRIVATE + crc::common + crc::compile_flags +) + + diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..fca6034 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,57 @@ +cmake_minimum_required(VERSION 3.12) + +# build all project test utility target ---------------------------------------- +if(NOT TARGET all_unit_tests) + add_custom_target(all_unit_tests + COMMENT "Build all unit tests" + ) +endif() + +add_custom_target(${PROJECT_NAME}_all_unit_tests + COMMENT "Build all ${PROJECT_NAME} unit tests" +) + +add_dependencies(all_unit_tests ${PROJECT_NAME}_all_unit_tests) + +# Target Declaration ----------------------------------------------------------- +add_executable(testall) + +# Target Definition ------------------------------------------------------------ +target_sources(testall + PRIVATE + testnmea.c + testcrc.c + testall.c +) + +target_include_directories(testall + PRIVATE + $ +) + +target_link_libraries(testall + PRIVATE + crc::crc +) + +add_test( + NAME ${PROJECT_NAME}_tesall + COMMAND $ +) + +add_dependencies(${PROJECT_NAME}_all_unit_tests testall) + +# run all project test utility target ------------------------------------------ +if(NOT TARGET run_unit_tests) + add_custom_target(run_unit_tests + COMMENT "Run all unit tests" + ) +endif() + +add_custom_target(run_${PROJECT_NAME}_unit_tests + COMMAND ${CMAKE_CTEST_COMMAND} -j $(nproc) --tests-regex \"${PROJECT_NAME}_*\" --output-on-failure + COMMENT "Run all ${PROJECT_NAME} unit tests" + VERBATIM +) + +add_dependencies(run_unit_tests run_${PROJECT_NAME}_unit_tests) \ No newline at end of file