From df49a4afd7243439a7dfc6c5dae23717a9b673a3 Mon Sep 17 00:00:00 2001 From: franneck94 Date: Sun, 10 Mar 2024 09:31:46 +0100 Subject: [PATCH] up --- CMakeLists.txt | 24 +- README.md | 31 +-- README_cmake.md | 162 ++++++++++++ README_install.md | 69 +++++ README_tools.md | 10 + app/CMakeLists.txt | 29 ++- app/main.c | 1 - cmake/CodeCoverage.cmake | 235 +++++++++--------- cmake/Sanitizer.cmake | 48 +++- cmake/Tools.cmake | 10 +- cmake/toolchains/arm32-cross-toolchain.cmake | 12 + cmake/toolchains/arm32-native-toolchain.cmake | 12 + cmake/toolchains/x86-64-mingw-toolchain.cmake | 14 ++ .../toolchains/x86-64-native-toolchain.cmake | 10 + src/CMakeLists.txt | 3 +- src/bar/CMakeLists.txt | 28 +++ src/bar/bar.c | 11 + src/bar/bar.h | 8 + src/foo/CMakeLists.txt | 28 +++ src/{my_lib/my_lib.c => foo/foo.c} | 2 +- src/{my_lib/my_lib.h => foo/foo.h} | 6 +- src/my_lib/CMakeLists.txt | 30 --- tests/CMakeLists.txt | 78 +++--- tests/test_bar.c | 28 +++ tests/{main.c => test_foo.c} | 2 +- 25 files changed, 655 insertions(+), 236 deletions(-) create mode 100644 README_cmake.md create mode 100644 README_install.md create mode 100644 README_tools.md create mode 100644 cmake/toolchains/arm32-cross-toolchain.cmake create mode 100644 cmake/toolchains/arm32-native-toolchain.cmake create mode 100644 cmake/toolchains/x86-64-mingw-toolchain.cmake create mode 100644 cmake/toolchains/x86-64-native-toolchain.cmake create mode 100644 src/bar/CMakeLists.txt create mode 100644 src/bar/bar.c create mode 100644 src/bar/bar.h create mode 100644 src/foo/CMakeLists.txt rename src/{my_lib/my_lib.c => foo/foo.c} (91%) rename src/{my_lib/my_lib.h => foo/foo.h} (81%) delete mode 100644 src/my_lib/CMakeLists.txt create mode 100644 tests/test_bar.c rename tests/{main.c => test_foo.c} (96%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0fdd3ca..1a22d42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.22) project( "CTemplate" @@ -12,6 +12,10 @@ set(CMAKE_C_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Options +option(USE_CONAN "Whether to use Conan." OFF) +option(USE_VCPKG "Whether to use VCPKG." OFF) +option(USE_CPM "Whether to use CPM." ON) + option(ENABLE_WARNINGS "Enable to add warnings to a target." ON) option(ENABLE_WARNINGS_AS_ERRORS "Enable to treat warnings as errors." OFF) @@ -25,15 +29,12 @@ option(ENABLE_SANITIZE_UNDEF "Enable undefined sanitize." OFF) option(ENABLE_SANITIZE_LEAK "Enable leak sanitize (Gcc/Clang only)." OFF) option(ENABLE_SANITIZE_THREAD "Enable thread sanitize (Gcc/Clang only)." OFF) -option(ENABLE_CLANG_FORMAT "Enable to add clang-format." ON) -option(ENABLE_CMAKE_FORMAT "Enable to add cmake-format." ON) +option(ENABLE_CLANG_FORMAT "Enable to add clang-format." OFF) +option(ENABLE_CMAKE_FORMAT "Enable to add cmake-format." OFF) option(ENABLE_LTO "Enable to add Link Time Optimization." ON) # Project/Library Names -set(LIBRARY_NAME "lib") -set(UNIT_TEST_NAME "unit_tests") -set(EXECUTABLE_NAME "main") # CMAKE MODULES set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/) @@ -75,18 +76,21 @@ add_subdirectory(configured) add_subdirectory(external) add_subdirectory(src) add_subdirectory(app) -add_subdirectory(tests) +if(ENABLE_TESTING) + include(CTest) + enable_testing() + add_subdirectory(tests) +endif() # INSTALL TARGETS install( - TARGETS ${EXECUTABLE_NAME} - EXPORT ${LIBRARY_NAME} + TARGETS "main" ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin) install( - TARGETS ${LIBRARY_NAME} + TARGETS "LibFoo" "LibBar" ARCHIVE DESTINATION lib LIBRARY DESTINATION lib) diff --git a/README.md b/README.md index 69ca6bf..dbb6abf 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ # Template For C Projects -![C](https://img.shields.io/badge/C-89%2F99%2F11%2F17-blue) +![C++](https://img.shields.io/badge/C%2B%2B-11%2F14%2F17%2F20%2F23-blue) ![License](https://camo.githubusercontent.com/890acbdcb87868b382af9a4b1fac507b9659d9bf/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667) -![Build CI Test](https://github.com/franneck94/CProjectTemplate/workflows/Ubuntu%20CI%20Test/badge.svg) -[![codecov](https://codecov.io/gh/franneck94/CProjectTemplate/branch/master/graph/badge.svg)](https://codecov.io/gh/franneck94/CProjectTemplate) +![Linux Build](https://github.com/franneck94/CppProjectTemplate/workflows/Ubuntu%20CI%20Test/badge.svg) -This is a template for C projects. What you get: +This is a template for C++ projects. What you get: - Library, executable and test code separated in distinct folders. - Use of modern CMake for building and compiling. @@ -14,9 +13,9 @@ This is a template for C projects. What you get: - General purpose libraries: - [log](https://github.com/rxi/log.c) - [argparse](https://github.com/cofyc/argparse) -- Continuous integration testing with Github Actions. -- Code coverage reports, including automatic upload to [Codecov](https://codecov.io). +- Continuous integration testing and coverage reports with Github Actions. - Code documentation with [Doxygen](http://www.stack.nl/~dimitri/doxygen/). +- Tooling: Clang-Format, Cmake-Format, Clang-tidy, Sanitizers ## Structure @@ -24,7 +23,7 @@ This is a template for C projects. What you get: ├── CMakeLists.txt ├── app │ ├── CMakesLists.txt -│ └── main.c +│ └── main.cc ├── cmake │ └── cmake modules ├── docs @@ -33,10 +32,10 @@ This is a template for C projects. What you get: ├── external │ ├── CMakesLists.txt │ ├── ... -├── src/my_lib +├── src │ ├── CMakesLists.txt -│ ├── my_lib.h -│ └── my_lib.c +│ ├── foo/ +│ └── bar/ └── tests ├── CMakeLists.txt └── main.c @@ -46,18 +45,20 @@ Library code goes into [src/](src/), main program code in [app/](app) and tests ## Software Requirements -- CMake 3.16+ +- CMake 3.21+ - GNU Makefile - Doxygen -- MSVC 2017 (or higher), GCC 9 (or higher), Clang 9 (or higher) -- Code Coverage (only on GNU|Clang): lcov, gcovr +- Conan or VCPKG +- MSVC 2017 (or higher), G++9 (or higher), Clang++9 (or higher) +- Optional: Code Coverage (only on GNU|Clang): gcovr +- Optional: Makefile, Doxygen, Conan, VCPKG ## Building First, clone this repo and do the preliminary work: ```shell -git clone --recursive https://github.com/franneck94/CProjectTemplate +git clone --recursive https://github.com/franneck94/CppProjectTemplate make prepare ``` @@ -97,4 +98,4 @@ cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=ON .. cmake --build . --config Debug --target coverage ``` -For more info about CMake see [here](./CMakeGuide.md). +For more info about CMake see [here](./README_cmake.md). diff --git a/README_cmake.md b/README_cmake.md new file mode 100644 index 0000000..a53663a --- /dev/null +++ b/README_cmake.md @@ -0,0 +1,162 @@ +# CMake Tutorial + +## Generating a Project + +```bash +cmake [] -S -B +``` + +Assuming that a CMakeLists.txt is in the root directory, you can generate a project like the following. + +```bash +mkdir build +cd build +cmake -S .. -B . # Option 1 +cmake .. # Option 2 +``` + +Assuming that you have already built the CMake project, you can update the generated project. + +```bash +cd build +cmake . +``` + +## Generator for GCC and Clang + +```bash +cd build +cmake -S .. -B . -G "Unix Makefiles" # Option 1 +cmake .. -G "Unix Makefiles" # Option 2 +``` + +## Generator for MSVC + +```bash +cd build +cmake -S .. -B . -G "Visual Studio 16 2019" # Option 1 +cmake .. -G "Visual Studio 16 2019" # Option 2 +``` + +## Specify the Build Type + +Per default, the standard type is in most cases the debug type. +If you want to generate the project, for example, in release mode you have to set the build type. + +```bash +cd build +cmake -DCMAKE_BUILD_TYPE=Release .. +``` + +## Passing Options + +If you have set some options in the CMakeLists, you can pass values in the command line. + +```bash +cd build +cmake -DMY_OPTION=[ON|OFF] .. +``` + +## Specify the Build Target (Option 1) + +The standard build command would build all created targets within the CMakeLists. +If you want to build a specific target, you can do so. + +```bash +cd build +cmake --build . --target ExternalLibraries_Executable +``` + +The target *ExternalLibraries_Executable* is just an example of a possible target name. +Note: All dependent targets will be built beforehand. + +## Specify the Build Target (Option 2) + +Besides setting the target within the cmake build command, you could also run the previously generated Makefile (from the generating step). +If you want to build the *ExternalLibraries_Executable*, you could do the following. + +```bash +cd build +make ExternalLibraries_Executable +``` + +## Run the Executable + +After generating the project and building a specific target you might want to run the executable. +In the default case, the executable is stored in *build/5_ExternalLibraries/app/ExternalLibraries_Executable*, assuming that you are building the project *5_ExternalLibraries* and the main file of the executable is in the *app* dir. + +```bash +cd build +./bin/ExternalLibraries_Executable +``` + +## Different Linking Types + +```cmake +target_link_libraries(A PUBLIC fmt) +target_link_libraries(B PRIVATE spdlog) +``` + +```cmake +target_link_libraries(C PUBLIC/PRIVATE A) +target_link_libraries(C PUBLIC/PRIVATE B) +``` + +### PUBLIC + +When A links fmt as *PUBLIC*, it says that A uses fmt in its implementation, and fmt is also used in A's public API. +Hence, C can use fmt since it is part of the public API of A. + +### PRIVATE + +When B links spdlog as *PRIVATE*, it is saying that B uses spdlog in its +implementation, but spdlog is not used in any part of B's public API. + +### INTERFACE + +```cmake +add_library(D INTERFACE) +target_include_directories(D INTERFACE {CMAKE_CURRENT_SOURCE_DIR}/include) +``` + +In general, used for header-only libraries. + +## Different Library Types + +### Shared + +Shared libraries reduce the amount of code that is duplicated in each program that makes use of the library, keeping the binaries small. It also allows you to replace the shared object with one that is functionally equivalent, without needing to recompile the program that makes use of it. Shared libraries will however have a small additional cost for the execution. + +### Static + +Static libraries increase the overall size of the binary, but it means that you don't need to carry along a copy of the library that is being used. As the code is connected at compile time there are not any additional run-time loading costs. The code is simply there. + +## Cross Compilation with Toolchain Files + +## ARM 32 Cross + +```shell +cmake -B build_arm32 -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/arm32-cross-toolchain.cmake +cmake --build build_arm32 -j8 +``` + +## ARM 32 Native + +```shell +cmake -B build -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/arm32-native-toolchain.cmake +cmake --build build -j8 +``` + +## x86 64 MingW + +```shell +cmake -B build_mingw -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/x86-64-mingw-toolchain.cmake +cmake --build build_mingw -j8 +``` + +## x86 64 Native + +```shell +cmake -B build -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/x86-64-native-toolchain.cmake +cmake --build build -j8 +``` diff --git a/README_install.md b/README_install.md new file mode 100644 index 0000000..ef976e3 --- /dev/null +++ b/README_install.md @@ -0,0 +1,69 @@ +# Software Installation + +## How to install VCPKG + +Official Link: + +```cmd +cd external +git clone https://github.com/Microsoft/vcpkg.git +.\vcpkg\bootstrap-vcpkg.bat # windows +./vcpkg/bootstrap-vcpkg.sh # Unix +``` + +## How to install the Conan Package Manager + +Official installation guide is [here](https://docs.conan.io/2/). + +The conan database is [here](https://conan.io/center/). + +### Installation Steps + +1. Install Python (3.7+) +2. Type ``pip install --user -U conan`` into the terminal + 1. Unix: Append conan to the PATH by: ``source ~/.profile`` +3. Run the conan command: ``conan`` +4. conan profile detect --force +5. conan profile path default + +## Formatter and Static Analyzer + +### Tooling + +Clang-Format: Formatting tool for your C/C++ code: + +- Documentation for Clang-Format: [Link](https://clang.llvm.org/docs/ClangFormat.html) + +Clang-Tidy: Static linting tool for your C/C++ code: + +- Documentation for Clang-Tidy: [Link](https://clang.llvm.org/extra/clang-tidy/) + +Cmake-Format: + +```bash +pip install cmake-format # python 3.7+ +``` + +### Install Clang Tools + +It's included in the LLVM toolchain, but also installable by apt, brew, winget etc. + +https://github.com/llvm/llvm-project/releases/tag/llvmorg-16.0.0 + +## Cross Compiler as an Example + +### Install ARM Compiler on x86 64 Ubuntu + +```shell +sudo apt update +sudo apt install libc6-armel-cross libc6-dev-armel-cross binutils-arm-linux-gnueabi libncurses5-dev build-essential bison flex libssl-dev bc + +sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf +sudo apt install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi +``` + +### Install MingW Cross Compiler on x86 64 Ubuntu + +```shell +sudo apt-get install mingw-w64 +``` diff --git a/README_tools.md b/README_tools.md new file mode 100644 index 0000000..20e3c0e --- /dev/null +++ b/README_tools.md @@ -0,0 +1,10 @@ +# Tools + +To sum up all the tools we use: + +- Compiler warnings: fast checks while compiling the code, for the all target. +- Clang-tidy, CppCheck: linters, can be manually run at any time after their specific targets are built. +- Sanitizers: shows memory leaks in runtime. Built with the all target. +- LTO: applies linking optimization in release mode. Automatically works at compile/linking time for all target +- Doxygen: generates HTML documentation. It can be run apart after build its specific target. +- Clang-format and Cmake-format: allows automatically format the code and CMake files. They can be run apart after build their specific targets. diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index b0a5e7c..a1887d3 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -1,27 +1,34 @@ set(APP_SOURCES "main.c") +set(LIBRARY_INCLUDES "./" "${CMAKE_BINARY_DIR}/configured_files/include") -add_executable(${EXECUTABLE_NAME} ${APP_SOURCES}) +add_executable("main" ${APP_SOURCES}) +target_include_directories("main" PUBLIC ${LIBRARY_INCLUDES}) -target_link_libraries(${EXECUTABLE_NAME} PUBLIC ${LIBRARY_NAME} log argparse) +target_link_libraries( + "main" + PUBLIC "LibFoo" + "LibBar" + log + argparse) -if (${ENABLE_WARNINGS}) +if(${ENABLE_WARNINGS}) target_set_warnings( - TARGET - ${EXECUTABLE_NAME} - ENABLE - ${ENABLE_WARNINGS} - AS_ERRORS - ${ENABLE_WARNINGS_AS_ERRORS}) + TARGET + "main" + ENABLE + ${ENABLE_WARNINGS} + AS_ERRORS + ${ENABLE_WARNINGS_AS_ERRORS}) endif() if(${ENABLE_LTO}) target_enable_lto( TARGET - ${EXECUTABLE_NAME} + "main" ENABLE ON) endif() if(${ENABLE_CLANG_TIDY}) - add_clang_tidy_to_target(${EXECUTABLE_NAME}) + add_clang_tidy_to_target("main") endif() diff --git a/app/main.c b/app/main.c index 6f33041..c035067 100644 --- a/app/main.c +++ b/app/main.c @@ -5,7 +5,6 @@ #include #include "config.h" -#include "my_lib.h" static const char *const usages[] = { "basic [options] [[--] args]", diff --git a/cmake/CodeCoverage.cmake b/cmake/CodeCoverage.cmake index b35b329..bff5c02 100644 --- a/cmake/CodeCoverage.cmake +++ b/cmake/CodeCoverage.cmake @@ -1,4 +1,5 @@ # Copyright (c) 2012 - 2017, Lars Bilke All rights reserved. +# Edited by Jan Schaffranek (2024). # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: @@ -28,77 +29,61 @@ include(CMakeParseArguments) -if(ENABLE_COVERAGE) - # Check prereqs - find_program(GCOV_PATH gcov) - find_program( - LCOV_PATH - NAMES lcov - lcov.bat - lcov.exe - lcov.perl) - 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 - - if(CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES - "Clang") - set(IS_CLANG TRUE) - else() - set(IS_CLANG FALSE) - endif() - if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") - set(IS_GCC TRUE) - else() - set(IS_GCC FALSE) - endif() +if(CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(IS_CLANG TRUE) +else() + set(IS_CLANG FALSE) +endif() +if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") + set(IS_GCC TRUE) +else() + set(IS_GCC FALSE) +endif() - if(NOT ${IS_CLANG} AND NOT ${IS_GCC}) - message(FATAL_ERROR "Compiler is not gcc/clang! Aborting...") - endif() +if(NOT ${IS_CLANG} AND NOT ${IS_GCC}) + message(FATAL_ERROR "Compiler is not gcc/clang! Aborting...") +endif() - set(COVERAGE_COMPILER_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") - set(CMAKE_CXX_FLAGS_COVERAGE ${COVERAGE_COMPILER_FLAGS} FORCE) - set(CMAKE_C_FLAGS_COVERAGE ${COVERAGE_COMPILER_FLAGS} FORCE) - set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "-lgcov" FORCE) - set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "" FORCE) - mark_as_advanced( - CMAKE_CXX_FLAGS_COVERAGE - CMAKE_C_FLAGS_COVERAGE - CMAKE_EXE_LINKER_FLAGS_COVERAGE - CMAKE_SHARED_LINKER_FLAGS_COVERAGE) - - if(NOT - CMAKE_BUILD_TYPE - STREQUAL - "Debug") - message(WARNING "Cov results with non-Debug build may be misleading") - endif() +find_program(GCOVR_PATH gcovr) + +set(COVERAGE_COMPILER_FLAGS "-g3 -O0 --coverage") +set(CMAKE_CXX_FLAGS_COVERAGE ${COVERAGE_COMPILER_FLAGS} FORCE) +set(CMAKE_C_FLAGS_COVERAGE ${COVERAGE_COMPILER_FLAGS} FORCE) +set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "-lgcov" FORCE) +set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "" FORCE) +mark_as_advanced( + CMAKE_CXX_FLAGS_COVERAGE + CMAKE_C_FLAGS_COVERAGE + CMAKE_EXE_LINKER_FLAGS_COVERAGE + CMAKE_SHARED_LINKER_FLAGS_COVERAGE) + +if(NOT + CMAKE_BUILD_TYPE + STREQUAL + "Debug") + message(WARNING "Cov results with non-Debug build may be misleading") +endif() - if(${IS_GCC}) - link_libraries(gcov) - endif() +if(${IS_GCC}) + 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. -# -function(setup_target_for_coverage_lcov) - set(options NO_DEMANGLE) +function(setup_target_for_coverage_gcovr_html) + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH + + set(options NONE) set(oneValueArgs BASE_DIRECTORY NAME) set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS - DEPENDENCIES - LCOV_ARGS - GENHTML_ARGS) + DEPENDENCIES) cmake_parse_arguments( Coverage "${options}" @@ -106,24 +91,17 @@ function(setup_target_for_coverage_lcov) "${multiValueArgs}" ${ARGN}) - if(NOT LCOV_PATH) - message(FATAL_ERROR "lcov not found! Aborting...") - endif() - if(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}) + 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 "") + set(GCOVR_EXCLUDES "") foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} - ${COVERAGE_LCOV_EXCLUDES}) + ${COVERAGE_GCOVR_EXCLUDES}) if(CMAKE_VERSION VERSION_GREATER 3.4) get_filename_component( EXCLUDE @@ -132,67 +110,96 @@ function(setup_target_for_coverage_lcov) BASE_DIR ${BASEDIR}) endif() - list(APPEND LCOV_EXCLUDES "${EXCLUDE}") + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") endforeach() - list(REMOVE_DUPLICATES LCOV_EXCLUDES) + list(REMOVE_DUPLICATES GCOVR_EXCLUDES) - # Conditional arguments - if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) - set(GENHTML_EXTRA_ARGS "--demangle-cpp") + # 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_EXTRA_FLAGS + --json-summary + --json-summary-pretty + --html-them + blue) + set(GCOVR_HTML_CMD + ${GCOVR_PATH} + ${GCOVR_EXTRA_FLAGS} + --html + ${Coverage_NAME}/index.html + --html-details + --json-summary + ${Coverage_NAME}/summary.json + --json-summary-pretty + -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() - # Setup target add_custom_target( ${Coverage_NAME} - # Cleanup lcov - COMMAND ${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 - COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c - -i -d . -b ${BASEDIR} -o ${Coverage_NAME}.base - # Run tests - COMMAND ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} - # Capturing lcov counters and generating report - COMMAND - ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} - --directory . -b ${BASEDIR} --capture --output-file - ${Coverage_NAME}.capture - # add baseline counters - COMMAND - ${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 - COMMAND - ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove - ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file - ${Coverage_NAME}.info - # Generate HTML output - COMMAND ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} - -o ${Coverage_NAME} ${Coverage_NAME}.info - # Set output files as GENERATED (will be removed on 'make clean') - BYPRODUCTS ${Coverage_NAME}.base - ${Coverage_NAME}.capture - ${Coverage_NAME}.total - ${Coverage_NAME}.info - ${Coverage_NAME} # report directory + 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 - ) - - # Show where to find the lcov info report - add_custom_command( - TARGET ${Coverage_NAME} - POST_BUILD - COMMAND ;) + 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 ;) -endfunction() + COMMAND ; + COMMENT + "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) +endfunction() # setup_target_for_coverage_gcovr_html function(append_coverage_compiler_flags) set(CMAKE_C_FLAGS diff --git a/cmake/Sanitizer.cmake b/cmake/Sanitizer.cmake index 60d193c..0be7ed0 100644 --- a/cmake/Sanitizer.cmake +++ b/cmake/Sanitizer.cmake @@ -1,31 +1,57 @@ -function(add_sanitizer_flags enable_sanitize_addr enable_sanitize_undef) - if (NOT enable_sanitize_addr AND NOT enable_sanitize_undef) - message(STATUS "Sanitizers deactivated.") +function(add_sanitizer_flags) + if(NOT ENABLE_SANITIZE_ADDR AND NOT ENABLE_SANITIZE_UNDEF) return() endif() - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") + if(CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES + "GNU") add_compile_options("-fno-omit-frame-pointer") add_link_options("-fno-omit-frame-pointer") - if(enable_sanitize_addr) + if(ENABLE_SANITIZE_ADDR) + message(STATUS "Activating Address Sanitizer") add_compile_options("-fsanitize=address") add_link_options("-fsanitize=address") endif() - if(enable_sanitize_undef) + if(ENABLE_SANITIZE_UNDEF) + message(STATUS "Activating Undefined Sanitizer") add_compile_options("-fsanitize=undefined") add_link_options("-fsanitize=undefined") endif() - elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - if(enable_sanitize_addr) + + if(ENABLE_SANITIZE_LEAK) + add_compile_options("-fsanitize=leak") + add_link_options("-fsanitize=leak") + endif() + + if(ENABLE_SANITIZE_THREAD) + if(ENABLE_SANITIZE_ADDR OR ENABLE_SANITIZE_LEAK) + message(WARNING "thread does not work with: address and leak") + endif() + message(STATUS "Activating Thread Sanitizer") + add_compile_options("-fsanitize=thread") + add_link_options("-fsanitize=thread") + endif() + elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC") + if(ENABLE_SANITIZE_ADDR) + message(STATUS "Activating Address Sanitizer") add_compile_options("/fsanitize=address") endif() - if(enable_sanitize_undef) - message(STATUS "Undefined sanitizer not impl. for MSVC!") + if(ENABLE_SANITIZE_UNDEF) + message(STATUS "sanitize=undefined not avail. for MSVC") + endif() + + if(ENABLE_SANITIZE_LEAK) + message(STATUS "sanitize=leak not avail. for MSVC") + endif() + + if(ENABLE_SANITIZE_THREAD) + message(STATUS "sanitize=thread not avail. for MSVC") endif() else() - message(STATUS "Sanitizer not supported in this environment!") + message(WARNING "This sanitizer not supported in this environment") + return() endif() endfunction(add_sanitizer_flags) diff --git a/cmake/Tools.cmake b/cmake/Tools.cmake index 7f5e8cd..800d718 100644 --- a/cmake/Tools.cmake +++ b/cmake/Tools.cmake @@ -44,15 +44,11 @@ function(add_clang_format_target) if(NOT ${Python_FOUND}) return() endif() - file(GLOB_RECURSE CMAKE_FILES_CC "*/*.cc") - file(GLOB_RECURSE CMAKE_FILES_CPP "*/*.cpp") + file(GLOB_RECURSE CMAKE_FILES_C "*/*.c") file(GLOB_RECURSE CMAKE_FILES_H "*/*.h") - file(GLOB_RECURSE CMAKE_FILES_HPP "*/*.hpp") set(CPP_FILES - ${CMAKE_FILES_CC} - ${CMAKE_FILES_CPP} - ${CMAKE_FILES_H} - ${CMAKE_FILES_HPP}) + ${CMAKE_FILES_C} + ${CMAKE_FILES_H}) list( FILTER CPP_FILES diff --git a/cmake/toolchains/arm32-cross-toolchain.cmake b/cmake/toolchains/arm32-cross-toolchain.cmake new file mode 100644 index 0000000..f18f49d --- /dev/null +++ b/cmake/toolchains/arm32-cross-toolchain.cmake @@ -0,0 +1,12 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR arm) + +set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) +set(CMAKE_C_FLAGS -static) +set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) +set(CMAKE_CXX_FLAGS -static) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/cmake/toolchains/arm32-native-toolchain.cmake b/cmake/toolchains/arm32-native-toolchain.cmake new file mode 100644 index 0000000..29887e8 --- /dev/null +++ b/cmake/toolchains/arm32-native-toolchain.cmake @@ -0,0 +1,12 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR arm) + +set(CMAKE_C_COMPILER gcc) +set(CMAKE_C_FLAGS -static) +set(CMAKE_CXX_COMPILER g++) +set(CMAKE_CXX_FLAGS -static) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/cmake/toolchains/x86-64-mingw-toolchain.cmake b/cmake/toolchains/x86-64-mingw-toolchain.cmake new file mode 100644 index 0000000..f47acad --- /dev/null +++ b/cmake/toolchains/x86-64-mingw-toolchain.cmake @@ -0,0 +1,14 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR x86_64) + +set(TOOLCHAIN_PREFIX x86_64-w64-mingw32) + +set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc-posix) +set(CMAKE_C_FLAGS "-static") +set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++-posix) +set(CMAKE_CXX_FLAGS "-static") + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/cmake/toolchains/x86-64-native-toolchain.cmake b/cmake/toolchains/x86-64-native-toolchain.cmake new file mode 100644 index 0000000..05c64de --- /dev/null +++ b/cmake/toolchains/x86-64-native-toolchain.cmake @@ -0,0 +1,10 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR x86_64) + +set(CMAKE_C_COMPILER gcc) +set(CMAKE_CXX_COMPILER g++) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 97171db..97b3359 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1 +1,2 @@ -add_subdirectory(my_lib) +add_subdirectory(foo) +add_subdirectory(bar) diff --git a/src/bar/CMakeLists.txt b/src/bar/CMakeLists.txt new file mode 100644 index 0000000..fcf8d07 --- /dev/null +++ b/src/bar/CMakeLists.txt @@ -0,0 +1,28 @@ +set(LIBRARY_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/bar.c") +set(LIBRARY_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/bar.h") +set(LIBRARY_INCLUDES "./" "${CMAKE_BINARY_DIR}/configured_files/include") + +add_library("LibBar" STATIC ${LIBRARY_SOURCES} ${LIBRARY_HEADERS}) +target_include_directories("LibBar" PUBLIC ${LIBRARY_INCLUDES}) + +if(${ENABLE_WARNINGS}) + target_set_warnings( + TARGET + "LibBar" + ENABLE + ${ENABLE_WARNINGS} + AS_ERRORS + ${ENABLE_WARNINGS_AS_ERRORS}) +endif() + +if(${ENABLE_LTO}) + target_enable_lto( + TARGET + "LibBar" + ENABLE + ON) +endif() + +if(${ENABLE_CLANG_TIDY}) + add_clang_tidy_to_target("LibBar") +endif() diff --git a/src/bar/bar.c b/src/bar/bar.c new file mode 100644 index 0000000..5f422ce --- /dev/null +++ b/src/bar/bar.c @@ -0,0 +1,11 @@ +#include + +#include "bar.h" + +int fn_branch(bool do_branch1, bool do_branch2) +{ + if (do_branch1 || do_branch2) + return 0; + + return 1; +} diff --git a/src/bar/bar.h b/src/bar/bar.h new file mode 100644 index 0000000..3a097ab --- /dev/null +++ b/src/bar/bar.h @@ -0,0 +1,8 @@ +#ifndef BAR_H +#define BAR_H + +#include + +int fn_branch(bool do_branch1, bool do_branch2); + +#endif // BAR_H diff --git a/src/foo/CMakeLists.txt b/src/foo/CMakeLists.txt new file mode 100644 index 0000000..ca1b893 --- /dev/null +++ b/src/foo/CMakeLists.txt @@ -0,0 +1,28 @@ +set(LIBRARY_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/foo.c") +set(LIBRARY_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/foo.h") +set(LIBRARY_INCLUDES "./" "${CMAKE_BINARY_DIR}/configured_files/include") + +add_library("LibFoo" STATIC ${LIBRARY_SOURCES} ${LIBRARY_HEADERS}) +target_include_directories("LibFoo" PUBLIC ${LIBRARY_INCLUDES}) + +if(${ENABLE_WARNINGS}) + target_set_warnings( + TARGET + "LibFoo" + ENABLE + ${ENABLE_WARNINGS} + AS_ERRORS + ${ENABLE_WARNINGS_AS_ERRORS}) +endif() + +if(${ENABLE_LTO}) + target_enable_lto( + TARGET + "LibFoo" + ENABLE + ON) +endif() + +if(${ENABLE_CLANG_TIDY}) + add_clang_tidy_to_target("LibFoo") +endif() diff --git a/src/my_lib/my_lib.c b/src/foo/foo.c similarity index 91% rename from src/my_lib/my_lib.c rename to src/foo/foo.c index 88e8892..8c6b274 100644 --- a/src/my_lib/my_lib.c +++ b/src/foo/foo.c @@ -1,6 +1,6 @@ #include -#include "my_lib.h" +#include "foo.h" int print_hello_world() { diff --git a/src/my_lib/my_lib.h b/src/foo/foo.h similarity index 81% rename from src/my_lib/my_lib.h rename to src/foo/foo.h index 572e8f4..5243f43 100644 --- a/src/my_lib/my_lib.h +++ b/src/foo/foo.h @@ -1,5 +1,5 @@ -#ifndef MY_LIB_H -#define MY_LIB_H +#ifndef FOO_H +#define FOO_H /** * @brief Prints hello world the console. @@ -15,4 +15,4 @@ int print_hello_world(); */ unsigned int factorial(unsigned int number); -#endif // MY_LIB_H +#endif // FOO_H diff --git a/src/my_lib/CMakeLists.txt b/src/my_lib/CMakeLists.txt deleted file mode 100644 index ac6d248..0000000 --- a/src/my_lib/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -# Sources and Headers -set(LIBRARY_SOURCES "my_lib.c") -set(LIBRARY_HEADERS "my_lib.h") -set(LIBRARY_INCLUDES "./" "${CMAKE_BINARY_DIR}/configured_files/include") - -# MyLib Library -add_library(${LIBRARY_NAME} STATIC ${LIBRARY_SOURCES} ${LIBRARY_HEADERS}) -target_include_directories(${LIBRARY_NAME} PUBLIC ${LIBRARY_INCLUDES}) - -if (${ENABLE_WARNINGS}) - target_set_warnings( - TARGET - ${LIBRARY_NAME} - ENABLE - ${ENABLE_WARNINGS} - AS_ERRORS - ${ENABLE_WARNINGS_AS_ERRORS}) -endif() - -if(${ENABLE_LTO}) - target_enable_lto( - TARGET - ${LIBRARY_NAME} - ENABLE - ON) -endif() - -if(${ENABLE_CLANG_TIDY}) - add_clang_tidy_to_target(${LIBRARY_NAME}) -endif() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 709e095..5c7460a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,35 +1,51 @@ -if(ENABLE_TESTING) - set(TEST_SOURCES "main.c") +add_executable("UnitTestFoo" "test_foo.c") +target_link_libraries("UnitTestFoo" PUBLIC "LibFoo") +target_link_libraries("UnitTestFoo" PRIVATE unity) - add_executable(${UNIT_TEST_NAME} ${TEST_SOURCES}) +add_executable("UnitTestBar" "test_bar.c") +target_link_libraries("UnitTestBar" PUBLIC "LibBar") +target_link_libraries("UnitTestBar" PRIVATE unity) - target_link_libraries(${UNIT_TEST_NAME} PUBLIC ${LIBRARY_NAME}) - target_link_libraries(${UNIT_TEST_NAME} PRIVATE unity) - if(${ENABLE_WARNINGS}) - target_set_warnings( - TARGET - ${UNIT_TEST_NAME} - ENABLE - ON - AS_ERRORS - OFF) - endif() +add_test(NAME "RunUnitTestFoo" COMMAND "UnitTestFoo") +add_test(NAME "RunUnitTestBar" COMMAND "UnitTestBar") - if(ENABLE_COVERAGE) - set(COVERAGE_MAIN "coverage") - set(COVERAGE_EXCLUDES - "${PROJECT_SOURCE_DIR}/app/*" - "${PROJECT_SOURCE_DIR}/cmake/*" - "${PROJECT_SOURCE_DIR}/docs/*" - "${PROJECT_SOURCE_DIR}/external/*" - "${PROJECT_SOURCE_DIR}/tests/*" - "/usr/include/*") - setup_target_for_coverage_lcov( - NAME - ${COVERAGE_MAIN} - EXECUTABLE - ${UNIT_TEST_NAME} - DEPENDENCIES - ${UNIT_TEST_NAME}) - endif() +if(${ENABLE_WARNINGS}) + target_set_warnings( + TARGET + "UnitTestFoo" + ENABLE + ${ENABLE_WARNINGS} + AS_ERRORS + ${ENABLE_WARNINGS_AS_ERRORS}) + target_set_warnings( + TARGET + "UnitTestBar" + ENABLE + ${ENABLE_WARNINGS} + AS_ERRORS + ${ENABLE_WARNINGS_AS_ERRORS}) +endif() + +if(ENABLE_COVERAGE) + set(COVERAGE_MAIN "coverage") + set(COVERAGE_EXCLUDES + "${PROJECT_SOURCE_DIR}/app/*" + "${PROJECT_SOURCE_DIR}/cmake/*" + "${PROJECT_SOURCE_DIR}/docs/*" + "${PROJECT_SOURCE_DIR}/external/*" + "${PROJECT_SOURCE_DIR}/tests/*" + "${PROJECT_SOURCE_DIR}/build/*" + "/usr/include/*") + set(COVERAGE_EXTRA_FLAGS) + set(COVERAGE_DEPENDENCIES "UnitTestFoo" "UnitTestBar") + + setup_target_for_coverage_gcovr_html( + NAME + ${COVERAGE_MAIN} + EXECUTABLE + ctest + DEPENDENCIES + ${COVERAGE_DEPENDENCIES} + BASE_DIRECTORY + ${CMAKE_SOURCE_DIR}) endif() diff --git a/tests/test_bar.c b/tests/test_bar.c new file mode 100644 index 0000000..1d1b6b6 --- /dev/null +++ b/tests/test_bar.c @@ -0,0 +1,28 @@ +#include "unity.h" + +#include "bar.h" + +void setUp(void) +{ + // set stuff up here +} + +void tearDown(void) +{ + // clean stuff up here +} + +void test_branch1(void) +{ + TEST_ASSERT_EQUAL(fn_branch(true, false), 0); + TEST_ASSERT_EQUAL(fn_branch(true, true), 0); + TEST_ASSERT_EQUAL(fn_branch(false, true), 0); + TEST_ASSERT_EQUAL(fn_branch(false, false), 1); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_branch1); + return UNITY_END(); +} diff --git a/tests/main.c b/tests/test_foo.c similarity index 96% rename from tests/main.c rename to tests/test_foo.c index 95f8b4b..b216569 100644 --- a/tests/main.c +++ b/tests/test_foo.c @@ -1,6 +1,6 @@ #include "unity.h" -#include "my_lib.h" +#include "foo.h" void setUp(void) {