From 7223f1b99b273b35f6ad8dc29fda2837f946d9b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Sun, 10 Dec 2023 01:15:20 +0100 Subject: [PATCH] feat(CTemplate): Move to CMake --- C_Tempate/.gitignore | 74 ++++++++++++++++++++++++ C_Tempate/.vimspector.json | 26 --------- C_Tempate/CMakeLists.txt | 52 +++++++++++++++++ C_Tempate/Makefile | 91 ------------------------------ C_Tempate/include/CMakeLists.txt | 16 ++++++ C_Tempate/src/CMakeLists.txt | 17 ++++++ C_Tempate/src/main.c | 25 ++++++-- C_Tempate/tests/CMakeLists.txt | 80 ++++++++++++++++++++++++++ C_Tempate/tests/src/CMakeLists.txt | 17 ++++++ C_Tempate/tests/src/test.cpp | 31 ++++++++++ 10 files changed, 308 insertions(+), 121 deletions(-) create mode 100644 C_Tempate/.gitignore delete mode 100644 C_Tempate/.vimspector.json create mode 100644 C_Tempate/CMakeLists.txt delete mode 100644 C_Tempate/Makefile create mode 100644 C_Tempate/include/CMakeLists.txt create mode 100644 C_Tempate/src/CMakeLists.txt create mode 100644 C_Tempate/tests/CMakeLists.txt create mode 100644 C_Tempate/tests/src/CMakeLists.txt create mode 100644 C_Tempate/tests/src/test.cpp diff --git a/C_Tempate/.gitignore b/C_Tempate/.gitignore new file mode 100644 index 0000000..bd461b5 --- /dev/null +++ b/C_Tempate/.gitignore @@ -0,0 +1,74 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# Vscode +.vscode/ + +# CMake +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +.cache/ +CTestTestfile.cmake +_deps +build/ +lib/ +bin/ +*.swp + diff --git a/C_Tempate/.vimspector.json b/C_Tempate/.vimspector.json deleted file mode 100644 index d24574a..0000000 --- a/C_Tempate/.vimspector.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "configurations": { - "Launch": { - "adapter": "vscode-cpptools", - "filetypes": [ "cpp", "c", "objc", "rust" ], // optional - "configuration": { - "request": "launch", - "program": "${workspaceRoot}/output/main", - //"args": [ "", "" ], - "cwd": "${workspaceRoot}/output", - //"environment": [ ... ], - "externalConsole": true, - "MIMode": "gdb" - } - }, - "Attach": { - "adapter": "vscode-cpptools", - "filetypes": [ "cpp", "c", "objc", "rust" ], // optional - "configuration": { - "request": "attach", - "program": "${workspaceRoot}/output/main", - "MIMode": "gdb" - } - } - } -} diff --git a/C_Tempate/CMakeLists.txt b/C_Tempate/CMakeLists.txt new file mode 100644 index 0000000..e64334e --- /dev/null +++ b/C_Tempate/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.14) +# Generate compile_commands.json +set(PROJECT_NAME cTemplate) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Turn on testing by default +option(BUILD_TESTING "Build tests" ON) +# Turn off coverage by default +option(ENABLE_COVERAGE "Enable test coverage" ON) + +# Set C standard to C99 +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") + +# Set the project name and version number. This allows for a user of your +project(${PROJECT_NAME} VERSION 0.1) +set(${PROJECT_NAME} 0.1) + +# Function to prepend the subdirectory to source files in subdirectories +FUNCTION(PREPEND var ) + SET(listVar "") + FOREACH(f ${${var}}) + LIST(APPEND listVar "${CMAKE_CURRENT_SOURCE_DIR}/${f}") + ENDFOREACH(f) + SET(${var} "${listVar}" PARENT_SCOPE) +ENDFUNCTION(PREPEND) + +# After a normal build, we can specify the location of various outputs of the +# build. We put executables and static libraries outside the build directory in +# bin/ and lib/, respectively. +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib") + +# Include source code and headers. This calls the CMakeLists.txt in each +# subdirectory. These can define their own libraries, executables, etc. as targets, +# but here we define all exportable targets in the root CMakeLists.txt. +add_subdirectory(src) +add_subdirectory(include) + +# enable unit testing via "make test" once the code has been compiled. +# TODO: Google Test + +if(BUILD_TESTING) + message("Testing enabled") + enable_testing() + add_subdirectory(tests) + target_include_directories(tests PRIVATE include) +endif() + +# Add PROJECT_NAME as an executable target. +add_executable(${PROJECT_NAME} ${SRC} ${INC}) +target_include_directories(${PROJECT_NAME} PRIVATE include) + diff --git a/C_Tempate/Makefile b/C_Tempate/Makefile deleted file mode 100644 index 3f27a58..0000000 --- a/C_Tempate/Makefile +++ /dev/null @@ -1,91 +0,0 @@ -# -# 'make' build executable file 'main' -# 'make clean' removes all .o and executable files -# - -# define the C compiler to use CC = gcc - -# define any compile-time flags -# CFLAGS := -std=c99 -Wall -Wextra -g -lm -CFLAGS := -std=c99 -Wall -Wextra -g - -# define library paths in addition to /usr/lib -# if I wanted to include libraries not in /usr/lib I'd specify -# their path using -Lpath, something like: -LFLAGS = - -# define output directory -OUTPUT := output - -# define source directory -SRC := src - -# define include directory -INCLUDE := include - -# define lib directory -LIB := lib - -ifeq ($(OS),Windows_NT) -MAIN := main.exe -SOURCEDIRS := $(SRC) -INCLUDEDIRS := $(INCLUDE) -LIBDIRS := $(LIB) -FIXPATH = $(subst /,\,$1) -RM := del /q /f -MD := mkdir -else -MAIN := main -SOURCEDIRS := $(shell find $(SRC) -type d) -INCLUDEDIRS := $(shell find $(INCLUDE) -type d) -LIBDIRS := $(shell find $(LIB) -type d) -FIXPATH = $1 -RM = rm -f -MD := mkdir -p -endif - -# define any directories containing header files other than /usr/include -INCLUDES := $(patsubst %,-I%, $(INCLUDEDIRS:%/=%)) - -# define the C libs -LIBS := $(patsubst %,-L%, $(LIBDIRS:%/=%)) - -# define the C source files -SOURCES := $(wildcard $(patsubst %,%/*.c, $(SOURCEDIRS))) - -# define the C object files -OBJECTS := $(SOURCES:.c=.o) - -# -# The following part of the makefile is generic; it can be used to -# build any executable just by changing the definitions above and by -# deleting dependencies appended to the file from 'make depend' -# - -OUTPUTMAIN := $(call FIXPATH,$(OUTPUT)/$(MAIN)) - -all: $(OUTPUT) $(MAIN) - @echo Executing 'all' complete! - -$(OUTPUT): - $(MD) $(OUTPUT) - -$(MAIN): $(OBJECTS) - $(CC) $(INCLUDES) $(OBJECTS) $(LFLAGS) $(LIBS) -o $(OUTPUTMAIN) $(CFLAGS) - -# this is a suffix replacement rule for building .o's from .c's -# it uses automatic variables $<: the name of the prerequisite of -# the rule(a .c file) and $@: the name of the target of the rule (a .o file) -# (see the gnu make manual section about automatic variables) -.c.o: - $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ - -.PHONY: clean -clean: - $(RM) $(OUTPUTMAIN) - $(RM) $(call FIXPATH,$(OBJECTS)) - @echo Cleanup complete! - -run: all - ./$(OUTPUTMAIN) - @echo Executing 'run: all' complete! diff --git a/C_Tempate/include/CMakeLists.txt b/C_Tempate/include/CMakeLists.txt new file mode 100644 index 0000000..15f3e7a --- /dev/null +++ b/C_Tempate/include/CMakeLists.txt @@ -0,0 +1,16 @@ + +# Make an explicit list of all source files in IFJ23_INC. This is important +# because CMake is not a build system: it is a build system generator. Suppose +# you add a file foo.cpp to src/ after running cmake .. . If you set +# IFJ23_INC with `file(GLOB ... )`, this is not passed to the makefile; it +# doesn't know that foo.cpp exists and will not re-run cmake. Your +# collaborator's builds will fail and it will be unclear why. Whether you use +# file(GLOB ...) or not, you will need to re-run cmake, but with an explicit +# file list, you know beforehand why your code isn't compiling. +set(INC + ) + +# Form the full path to the source files... +PREPEND(INC) +# ... and pass the variable to the parent scope. +set(INC ${INC} PARENT_SCOPE) diff --git a/C_Tempate/src/CMakeLists.txt b/C_Tempate/src/CMakeLists.txt new file mode 100644 index 0000000..b91d51d --- /dev/null +++ b/C_Tempate/src/CMakeLists.txt @@ -0,0 +1,17 @@ + +# Make an explicit list of all source files in `CMakeDemo_SRC`. This is important +# because CMake is not a build system: it is a build system generator. Suppose +# you add a file foo.cpp to src/ after running cmake .. . If you set +# `CMakeDemo_SRC` with `file(GLOB ... )`, this is not passed to the makefile; +# the makefile doesn't know that foo.cpp exists and will not re-run cmake. Your +# collaborator's builds will fail and it will be unclear why. Whether you use +# file(GLOB ...) or not, you will need to re-run cmake, but with an explicit +# file list, you know beforehand why your code isn't compiling. +set(SRC + main.c + ) + +# Form the full path to the source files... +PREPEND(SRC) +# ... and pass the variable to the parent scope. +set(SRC ${SRC} PARENT_SCOPE) diff --git a/C_Tempate/src/main.c b/C_Tempate/src/main.c index e8178a6..70ba1e7 100644 --- a/C_Tempate/src/main.c +++ b/C_Tempate/src/main.c @@ -1,8 +1,25 @@ -#include -#include +/** + * Copyright [2023] Jiří Štefka + * Project: AdventOfCode + * @file main.c + * @brief Main entry point + * @author jiriks74 + */ +#include + +/** + * @brief Main entry point + * @param argc Number of command-line arguments. + * @param argv Array of command-line arguments. + */ +#ifndef TESTING int main(int argc, char *argv[]) +#endif +#ifdef TESTING + int main_test(int argc, char *argv[]) +#endif { - printf("Hello world!\n"); - return 0; + printf("Hello world!\n"); + return 0; } diff --git a/C_Tempate/tests/CMakeLists.txt b/C_Tempate/tests/CMakeLists.txt new file mode 100644 index 0000000..f2a2124 --- /dev/null +++ b/C_Tempate/tests/CMakeLists.txt @@ -0,0 +1,80 @@ +set(PROJECT_NAME ${PROJECT_NAME} PARENT_SCOPE ) +# GoogleTest requires at least C++14 +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Get GoogleTest +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +add_subdirectory(src) +add_executable(tests ${TESTS}) + +# Link test executable against gtest & gtest_main +target_link_libraries( + tests + GTest::gtest_main +) + +target_include_directories(tests PRIVATE + ${CMAKE_SOURCE_DIR}/src + ) + +# Discover tests +include(GoogleTest) +gtest_discover_tests(tests) +add_dependencies(tests ${PROJECT_NAME}) + +# The following section is inspired by https://github.com/cmake-modules/lcov +if(ENABLE_COVERAGE) + message("Test coverage enabled") + # set(exclude_dir "*/tests/* */_deps/* /usr/include/c++/11/**/* /usr/include/c++/**/*") + # set(exclude_dir "*/tests/* */_deps/* /usr/include/c++/11/tuple /usr/include/c++/11/**/*") + + # Check for lcov, gcov and genhtml + find_program(GCOV gcov) + if (NOT GCOV) + message(WARNING "gcov not found") + endif() + find_program(LCOV lcov) + if (NOT LCOV) + message(WARNING "lcov not found") + endif() + find_program(GENHTML genhtml) + if (NOT GENHTML) + message(WARNING "genhtml not found") + endif() + + if (GCOV AND LCOV AND GENHTML) + # Set C compiler flags + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage -fprofile-arcs -ftest-coverage") + # Set C++ compiler flags + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage -fprofile-arcs -ftest-coverage") + + set(covname cov.info) + add_custom_target(coverage DEPENDS ${covname}) + add_dependencies(coverage tests ${PROJECT_NAME}) + add_custom_command( + OUTPUT ${covname} + COMMAND ${LCOV} -c -o ${covname} -d ${CMAKE_BINARY_DIR}/tests/CMakeFiles/tests.dir/ -b . --gcov-tool ${GCOV} + COMMAND ${LCOV} -r ${covname} -o ${covname} "*/tests/*" "*/_deps/**/*" "/usr/include/c++/**/*" "/usr/include/c++/11/**/*" + COMMAND ${LCOV} -l ${covname} + COMMAND ${GENHTML} ${covname} -output coverage + COMMAND ${LCOV} -l ${covname} 2>/dev/null | grep Total | sed 's/|//g' | sed 's/Total://g' | awk '{print $1}' | sed s/%//g > coverage/total + ) + set_directory_properties(PROPERTIES + ADDITIONAL_CLEAN_FILES ${covname} + ) + set_directory_properties(PROPERTIES + ADDITIONAL_CLEAN_FILES coverage/ + ) + else() + message(WARNING "Cannot enable coverage. Missing the required tools") + endif() +endif() diff --git a/C_Tempate/tests/src/CMakeLists.txt b/C_Tempate/tests/src/CMakeLists.txt new file mode 100644 index 0000000..227c709 --- /dev/null +++ b/C_Tempate/tests/src/CMakeLists.txt @@ -0,0 +1,17 @@ + +# Make an explicit list of all source files in `IFJ23_TESTS`. This is important +# because CMake is not a build system: it is a build system generator. Suppose +# you add a file foo.cpp to src/ after running cmake .. . If you set +# `IFJ23_TESTS` with `file(GLOB ... )`, this is not passed to the makefile; +# the makefile doesn't know that foo.cpp exists and will not re-run cmake. Your +# collaborator's builds will fail and it will be unclear why. Whether you use +# file(GLOB ...) or not, you will need to re-run cmake, but with an explicit +# file list, you know beforehand why your code isn't compiling. +set(TESTS + test.cpp + ) + +# Form the full path to the source files... +PREPEND(TESTS) +# ... and pass the variable to the parent scope. +set(TESTS ${TESTS} PARENT_SCOPE) diff --git a/C_Tempate/tests/src/test.cpp b/C_Tempate/tests/src/test.cpp new file mode 100644 index 0000000..37477b0 --- /dev/null +++ b/C_Tempate/tests/src/test.cpp @@ -0,0 +1,31 @@ +#include + +#define TESTING + +// Include the source file(s) to be tested. +#include "main.c" + +// Create a test fixture class template - this will be like a "conlection" of +// tests. the : public ::testing::Test part is important! Add it to your fixture +// class. +class HelloTest : public ::testing::Test { + HelloTest() {} + + ~HelloTest() {} + + void SetUp() {} + + void TearDown() {} +}; + +// Add tests to the test fixture class. +// @param fixture_class_name The name of the test fixture class. +// @param test_name The name of the test. +TEST(HelloTest, BasicAssertions) { + // Execute the code to be tested. + // Expect two strings not to be equal. + EXPECT_STRNE("hello", "world"); + // Expect equality. + EXPECT_EQ(7 * 6, 42); +} +