diff --git a/.drone.yml b/.drone.yml index 283384d..159dc65 100644 --- a/.drone.yml +++ b/.drone.yml @@ -25,13 +25,13 @@ trigger: steps: - name: unit tests - image: unit_tests:latest + image: amd64_gcc_unit_tests:latest pull: if-not-exists commands: - ./tools/ci/amd64/gcc/unit_tests.sh - name: valgrind - image: valgrind:latest + image: amd64_gcc_valgrind:latest pull: if-not-exists commands: - ./tools/ci/amd64/gcc/valgrind.sh @@ -45,14 +45,23 @@ trigger: - main steps: +- name: code coverage + image: amd64_clang_coverage:latest + pull: if-not-exists + environment: + CODECOV_TOKEN: + from_secret: CODECOV_TOKEN + commands: + - ./tools/ci/amd64/clang/coverage.sh + - name: leak sanitizer - image: leak_sanitizer:latest + image: amd64_clang_lsan:latest pull: if-not-exists commands: - ./tools/ci/amd64/clang/lsan.sh - name: memory sanitizer - image: memory_sanitizer:latest + image: amd64_clang_msan:latest pull: if-not-exists commands: - ./tools/ci/amd64/clang/msan.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a88bf5..7cc9934 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,16 +1,42 @@ cmake_minimum_required(VERSION 3.14) project(Light) set(CMAKE_CXX_STANDARD 23) +set(CMAKE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake) -include(${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake/functions.cmake) -include(${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake/definitions.cmake) -include(${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake/dependencies.cmake) +include(CheckCXXSourceCompiles) +include(${CMAKE_DIR}/functions.cmake) +include(${CMAKE_DIR}/definitions.cmake) +include(${CMAKE_DIR}/dependencies.cmake) -add_option(ENABLE_STATIC_ANALYSIS "Performs static analysis via clang-tidy and fails build on failing checks") +add_option(ENABLE_TESTS "Enables the building of the test modules") + +add_option(ENABLE_STATIC_ANALYSIS "Makes clang-tidy checks mandatory for compilation") if (ENABLE_STATIC_ANALYSIS) set(CMAKE_CXX_CLANG_TIDY "clang-tidy;--warnings-as-errors=*;--allow-no-checks") endif () -add_option(ENABLE_TESTS "Enables the building of the test modules") +add_option(ENABLE_LLVM_COVERAGE "Enables the code coverage instrumentation for clang") +if(ENABLE_LLVM_COVERAGE) + if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + message(FATAL_ERROR "ENABLE_LLVM_COVERAGE only supports the clang compiler") + endif () + + # Check for libc++ + check_cxx_source_compiles(" + #include + #ifdef _LIBCPP_VERSION + int main() { return 0; } + #else + #error Not using libc++ + #endif + " USING_LIBCXX) + if(NOT USING_LIBCXX) + message(FATAL_ERROR "ENABLE_LLVM_COVERAGE requires libc++, please compile with -stdlib=libc++") + endif() + + add_compile_options(-fprofile-instr-generate -fcoverage-mapping) + add_link_options(-fprofile-instr-generate -fcoverage-mapping) +endif () + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/modules) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/glad) diff --git a/conanfile.py b/conanfile.py index 47348bc..18e21bc 100644 --- a/conanfile.py +++ b/conanfile.py @@ -12,6 +12,7 @@ class LightRecipe(ConanFile): options = { "enable_tests": [True, False], + "enable_llvm_coverage": [True, False], "enable_static_analysis": [True, False], "use_mold": [True, False], "export_compile_commands": [True, False], @@ -19,6 +20,7 @@ class LightRecipe(ConanFile): default_options = { "enable_tests": True, + "enable_llvm_coverage": False, "enable_static_analysis": False, "use_mold": False, "export_compile_commands": True, @@ -44,8 +46,9 @@ class LightRecipe(ConanFile): tc.cache_variables["CMAKE_LINKER_TYPE"] = "MOLD" tc.cache_variables["CMAKE_EXPORT_COMPILE_COMMANDS"] = self.options.export_compile_commands - tc.cache_variables["ENABLE_STATIC_ANALYSIS"] = self.options.enable_static_analysis tc.cache_variables["ENABLE_TESTS"] = self.options.enable_tests + tc.cache_variables["ENABLE_LLVM_COVERAGE"] = self.options.enable_llvm_coverage + tc.cache_variables["ENABLE_STATIC_ANALYSIS"] = self.options.enable_static_analysis repo = git.Repo(search_parent_directories=True) tc.cache_variables["GIT_HASH"] = repo.head.object.hexsha diff --git a/tools/ci/amd64/clang/coverage.dockerfile b/tools/ci/amd64/clang/coverage.dockerfile new file mode 100644 index 0000000..bf54cdf --- /dev/null +++ b/tools/ci/amd64/clang/coverage.dockerfile @@ -0,0 +1,48 @@ +FROM archlinux:latest + +RUN pacman -Syu --noconfirm && \ + pacman -S --noconfirm \ + bash \ + base-devel \ + git \ + cmake \ + ninja \ + python \ + python-pip \ + clang \ + llvm \ + mesa \ + mold \ + ninja \ + curl \ + wget \ + zlib + +RUN pip install --no-cache-dir --break-system-packages conan gitpython \ + && conan profile detect + +RUN clang --version \ + && pip --version \ + && conan --version \ + && cmake --version \ + && g++ --version \ + && clang --version \ + && ninja --version \ + && mold --version \ + && llvm-profdata --version \ + && llvm-cov --version + +RUN git clone 'https://git.light7734.com/light7734/light.git' \ + && cd light; git checkout 'ci/code_cov' \ + && conan build . \ + -c tools.system.package_manager:mode=install \ + -c tools.cmake.cmaketoolchain:generator=Ninja \ + -c tools.build:compiler_executables='{"c": "clang", "cpp": "clang++"}' \ + -s build_type=Release \ + -s compiler=clang \ + -s compiler.version=20 \ + -s compiler.libcxx=libc++ \ + -o use_mold=True \ + -o enable_lcov=True \ + --build=missing \ + && rm -r ../light/ diff --git a/tools/ci/amd64/clang/coverage.sh b/tools/ci/amd64/clang/coverage.sh index 6a03ffe..4935ad3 100755 --- a/tools/ci/amd64/clang/coverage.sh +++ b/tools/ci/amd64/clang/coverage.sh @@ -7,16 +7,13 @@ rm -rf ./build conan build . \ -c tools.system.package_manager:mode=install \ -c tools.cmake.cmaketoolchain:generator=Ninja \ - -c tools.build:cxxflags='["-fprofile-instr-generate", "-fcoverage-mapping"]' \ - -c tools.build:sharedlinkflags='["-fprofile-instr-generate", "-fcoverage-mapping"]' \ - -c tools.build:exelinkflags='["-fprofile-instr-generate", "-fcoverage-mapping"]' \ - -c tools.info.package_id:confs='["tools.build:cxxflags","tools.build:sharedlinkflags","tools.build:exelinkflags"]' \ -c tools.build:compiler_executables='{"c": "clang", "cpp": "clang++"}' \ -s build_type=Release \ -s compiler=clang \ -s compiler.version=20 \ -s compiler.libcxx=libc++ \ -o use_mold=True \ + -o enable_llvm_coverage=True \ --build=missing for test in $(find ./build -type f -name '*_tests' -executable); do @@ -24,3 +21,15 @@ for test in $(find ./build -type f -name '*_tests' -executable); do "$test" done +mkdir -p ./build/coverage/ +for test in $(find ./build -type f -name '*_tests' -executable); do + export LLVM_PROFILE_FILE="./build/coverage/$(basename "$(dirname "{}")").profraw"; + echo ${LLVM_PROFILE_FILE} >> ./build/coverage/list; + "$test" +done + +llvm-profdata merge --input-files './build/coverage/list' -o "./build/coverage/merged.profdata" +find ./modules -type f -name "*.profraw" -exec rm -fv {} + +LLVM_COV_SHOW=$(llvm-cov show -instr-profile='./build/coverage/merged.profdata' $(find ./build -type f -name '*_tests' -executable -exec printf -- '-object %s ' {} \;) -ignore-filename-regex='./modules/[^/]+/*_tests') +echo "${LLVM_COV_SHOW}" > './build/coverage/coverage.txt' +wget -qO- "https://codecov.io/bash" | bash diff --git a/tools/scripts/build_ci_images.sh b/tools/scripts/build_ci_images.sh index adb718e..4919748 100755 --- a/tools/scripts/build_ci_images.sh +++ b/tools/scripts/build_ci_images.sh @@ -15,6 +15,9 @@ docker build -t amd64_gcc_unit_tests -f $CI_DIR/amd64/gcc/unit_tests.dockerfile echo "==> Building image: amd64_gcc_valgrind" docker build -t amd64_gcc_valgrind -f $CI_DIR/amd64/gcc/valgrind.dockerfile . +echo "==> Building image: amd64_clang_coverage" +docker build -t amd64_clang_coverage -f $CI_DIR/amd64/clang/coverage.dockerfile . + echo "==> Building image: amd64_clang_lsan" docker build -t amd64_clang_lsan -f $CI_DIR/amd64/clang/lsan.dockerfile .