diff --git a/.github/workflows/cpp.yml b/.github/workflows/cpp.yml index 90b6c90..0db0c1d 100644 --- a/.github/workflows/cpp.yml +++ b/.github/workflows/cpp.yml @@ -24,14 +24,14 @@ jobs: test: needs: formatting-check - name: "Test (${{ matrix.toolchain.os }}/${{ matrix.toolchain.compiler }}, ${{ matrix.build-type }})" + name: "Test (${{ matrix.toolchain.os }}/${{ matrix.toolchain.compiler-id }}, ${{ matrix.build-type }})" strategy: fail-fast: false matrix: toolchain: - - { os: Linux, compiler: GCC, runner-label: gcc-14 } - - { os: Linux, compiler: Clang, runner-label: clang-19 } + - { os: Linux, compiler-id: GCC, runner-label: gcc-14 } + - { os: Linux, compiler-id: Clang, runner-label: clang-19 } build-type: - Release - Debug @@ -42,7 +42,7 @@ jobs: runs-on: [self-hosted, ubuntu, base, "${{ matrix.toolchain.runner-label }}"] env: - CT_CMAKE_PRESET: "CI-${{ matrix.toolchain.compiler }}-${{ matrix.build-type }}" + CT_CMAKE_PRESET: "CI-${{ matrix.toolchain.compiler-id }}-${{ matrix.build-type }}" defaults: run: diff --git a/CMakeLists.txt b/CMakeLists.txt index 8131fc3..a14dab2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.21...3.31) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") -include(SetupToolchain) include(ConfigureTarget) +include(ConfigureVcpkg) include(CompilerWarnings) project(template LANGUAGES CXX) @@ -12,6 +12,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +ct_configure_vcpkg() + # Setup a 'solution' target file(GLOB SOLUTION_SRC CONFIGURE_DEPENDS src/*.cpp src/*.h) add_library(solution ${SOLUTION_SRC}) diff --git a/CMakePresets.json b/CMakePresets.json index b2eefd4..0a813d1 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -10,7 +10,11 @@ "name": "Base", "description": "General preset that applies to all configurations", "hidden": true, - "binaryDir": "${sourceDir}/build/${presetName}" + "binaryDir": "${sourceDir}/build/${presetName}", + "cacheVariables": { + "CT_TOOLCHAIN_TEMPLATE": "${sourceDir}/cmake/templates/toolchain.cmake.in", + "CT_TRIPLET_TEMPLATE": "${sourceDir}/cmake/templates/vcpkg-triplet.cmake.in" + } }, { "name": "Default-Release", @@ -57,11 +61,7 @@ "name": "CI-Linux", "description": "Base preset for CI builds on Linux", "hidden": true, - "inherits": "Base", - "cacheVariables": { - "CT_TRIPLET_TEMPLATE": "${sourceDir}/cmake/vcpkg-triplet-templates/ct-x64-linux.cmake.in", - "CT_TOOLCHAIN_TEMPLATE": "${sourceDir}/cmake/toolchain-templates/ci.cmake.in" - } + "inherits": "Base" }, { "name": "CI-GCC", @@ -69,9 +69,7 @@ "hidden": true, "inherits": "CI-Linux", "cacheVariables": { - "CT_C_EXE": "gcc-14", - "CT_CXX_EXE": "gcc-14", - "CT_COMPILER_ID": "GNU" + "CMAKE_CXX_COMPILER": "g++-14" } }, { @@ -80,9 +78,7 @@ "hidden": true, "inherits": "CI-Linux", "cacheVariables": { - "CT_C_EXE": "clang-19", - "CT_CXX_EXE": "clang++-19", - "CT_COMPILER_ID": "Clang" + "CMAKE_CXX_COMPILER": "clang++-19" } }, { diff --git a/cmake/ConfigureCompiler.cmake b/cmake/ConfigureCompiler.cmake index 84723c0..fd54831 100644 --- a/cmake/ConfigureCompiler.cmake +++ b/cmake/ConfigureCompiler.cmake @@ -1,35 +1,26 @@ -function(ct_set_compiler C_EXE CXX_EXE) - find_program(C_EXE_PATH "${C_EXE}") - find_program(CXX_EXE_PATH "${CXX_EXE}") - - if(NOT C_EXE_PATH OR NOT CXX_EXE_PATH) - message(FATAL_ERROR "Could not find a requested compiler (C_EXE=${C_EXE}, CXX_EXE=${CXX_EXE})") - endif() - - set(CMAKE_C_COMPILER "${C_EXE_PATH}") - set(CMAKE_CXX_COMPILER "${CXX_EXE_PATH}") -endfunction() - -function(ct_configure_compiler COMPILER_ID HARDENED SANITIZED STABLE_ABI) +function(ct_configure_compiler COMPILER_ID HARDENED SANITIZED) set(IS_GCC OFF) set(IS_CLANG OFF) + set(IS_MSVC OFF) if(COMPILER_ID STREQUAL "GNU") set(IS_GCC ON) elseif(COMPILER_ID MATCHES "Clang") set(IS_CLANG ON) + elseif(COMPILER_ID MATCHES "MSVC") + set(IS_MSVC ON) endif() set(CT_COMPILER_FLAGS "") set(CT_LINKER_FLAGS "") - if(IS_CLANG AND NOT STABLE_ABI) + if(IS_CLANG) set(CT_COMPILER_FLAGS "${CT_COMPILER_FLAGS} -stdlib=libc++") set(CT_LINKER_FLAGS "${CT_LINKER_FLAGS} -stdlib=libc++") message(STATUS "Using libc++ as a standard library") endif() - if(HARDENED AND NOT STABLE_ABI) + if(HARDENED) if(IS_GCC) set(CT_COMPILER_FLAGS "${CT_COMPILER_FLAGS} -D_GLIBCXX_DEBUG") message(STATUS "Enabled debug mode for libstdc++") @@ -37,7 +28,7 @@ function(ct_configure_compiler COMPILER_ID HARDENED SANITIZED STABLE_ABI) set(CT_COMPILER_FLAGS "${CT_COMPILER_FLAGS} -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG") message(STATUS "Enabled hardening mode for libc++") else() - message(WARNING "Hardening is not supported for CXX compiler: '${COMPILER_ID}'") + message(STATUS "Hardening is not supported for CXX compiler: '${COMPILER_ID}'") endif() endif() @@ -52,6 +43,9 @@ function(ct_configure_compiler COMPILER_ID HARDENED SANITIZED STABLE_ABI) "-fno-omit-frame-pointer" ) message(STATUS "Enabled UBSan and ASan") + elseif(IS_MSVC) + set(CT_COMPILER_FLAGS "${CT_COMPILER_FLAGS} /fsanitize=address") + message(STATUS "Enabled ASan") else() message(WARNING "Sanitized builds are not supported for CXX compiler: '${COMPILER_ID}'") endif() diff --git a/cmake/ConfigureTarget.cmake b/cmake/ConfigureTarget.cmake index 874b1af..7309ad2 100644 --- a/cmake/ConfigureTarget.cmake +++ b/cmake/ConfigureTarget.cmake @@ -1,11 +1,6 @@ function(ct_configure_target TARGET) - if(CT_COMPILER_FLAGS OR CT_LINKER_FLAGS) - message(STATUS "No need to configure '${TARGET}': Using global toolchain options") - return() - endif() - include(ConfigureCompiler) - ct_configure_compiler("${CMAKE_CXX_COMPILER_ID}" "${CT_HARDENED}" "${CT_SANITIZED}" ON) + ct_configure_compiler("${CMAKE_CXX_COMPILER_ID}" "${CT_HARDENED}" "${CT_SANITIZED}") separate_arguments(CT_COMPILER_FLAGS) separate_arguments(CT_LINKER_FLAGS) diff --git a/cmake/ConfigureVcpkg.cmake b/cmake/ConfigureVcpkg.cmake new file mode 100644 index 0000000..fd7bf08 --- /dev/null +++ b/cmake/ConfigureVcpkg.cmake @@ -0,0 +1,94 @@ +include(FetchContent) + +function(_ct_fetch_vcpkg) + if (DEFINED ENV{CT_VCPKG_ROOT_DIR}) + message(STATUS "Detected CT_VCPKG_ROOT_DIR environment variable") + set(CT_VCPKG_ROOT_DIR "$ENV{CT_VCPKG_ROOT_DIR}" CACHE PATH "") + endif() + + if(DEFINED CT_VCPKG_ROOT_DIR) + message(STATUS "Using vcpkg at ${CT_VCPKG_ROOT_DIR}") + else() + message(STATUS "Fetching vcpkg...") + FetchContent_Declare( + vcpkg + URL https://github.com/microsoft/vcpkg/archive/refs/tags/2025.02.14.tar.gz + ) + FetchContent_MakeAvailable(vcpkg) + + set(CT_VCPKG_ROOT_DIR "${vcpkg_SOURCE_DIR}" CACHE PATH "") + endif() + + if(NOT DEFINED CT_VCPKG_TOOLCHAIN_FILE) + set(CT_VCPKG_TOOLCHAIN_FILE "${CT_VCPKG_ROOT_DIR}/scripts/buildsystems/vcpkg.cmake" CACHE FILEPATH "") + endif() +endfunction() + +function(_ct_bootstrap_vcpkg) + if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + set(CT_VCPKG_EXECUTABLE "${CT_VCPKG_ROOT_DIR}/vcpkg.exe") + set(CT_VCPKG_BOOTSTRAP_SCRIPT "${CT_VCPKG_ROOT_DIR}/bootstrap-vcpkg.bat") + else() + set(CT_VCPKG_EXECUTABLE "${CT_VCPKG_ROOT_DIR}/vcpkg") + set(CT_VCPKG_BOOTSTRAP_SCRIPT "${CT_VCPKG_ROOT_DIR}/bootstrap-vcpkg.sh") + endif() + + if(NOT EXISTS "${CT_VCPKG_EXECUTABLE}") + message(STATUS "Bootstrapping vcpkg before install") + + set(CT_VCPKG_BOOTSTRAP_LOG "${CMAKE_BINARY_DIR}/vcpkg-bootstrap.log") + file(TO_NATIVE_PATH "${CT_VCPKG_BOOTSTRAP_LOG}" CT_NATIVE_VCPKG_BOOTSTRAP_LOG) + execute_process( + COMMAND "${CT_VCPKG_BOOTSTRAP_SCRIPT}" -disableMetrics + OUTPUT_FILE "${CT_VCPKG_BOOTSTRAP_LOG}" + ERROR_FILE "${CT_VCPKG_BOOTSTRAP_LOG}" + RESULT_VARIABLE CT_VCPKG_BOOTSTRAP_RESULT) + + if(CT_VCPKG_BOOTSTRAP_RESULT EQUAL "0") + message(STATUS "Bootstrapping vcpkg before install - done") + else() + message(STATUS "Bootstrapping vcpkg before install - failed") + message(FATAL_ERROR "vcpkg install failed. See logs for more information: ${CT_NATIVE_VCPKG_BOOTSTRAP_LOG}") + endif() + endif() +endfunction() + +option(CT_HARDENED "Should the standard library be hardened" OFF) +option(CT_SANITIZED "Should the build be sanitized" OFF) + +function(_ct_generate_toolchain_and_triplet) + cmake_path(GET CT_TOOLCHAIN_TEMPLATE STEM CT_TOOLCHAIN_NAME) + set(CT_GENERATED_TOOLCHAIN_FILE "${CMAKE_BINARY_DIR}/gen/toolchains/${CT_TOOLCHAIN_NAME}.cmake") + + configure_file("${CT_TOOLCHAIN_TEMPLATE}" "${CT_GENERATED_TOOLCHAIN_FILE}" @ONLY) + message(STATUS "Generated a toolchain '${CT_TOOLCHAIN_NAME}' from template") + + if(DEFINED CT_TRIPLET_TEMPLATE) + message(STATUS "Running ${CT_VCPKG_ROOT_DIR}/vcpkg z-print-config") + execute_process( + COMMAND "${CT_VCPKG_ROOT_DIR}/vcpkg" z-print-config + COMMAND_ERROR_IS_FATAL ANY + OUTPUT_VARIABLE CT_VCPKG_CONFIG_OUTPUT + ) + string(JSON CT_DEFAULT_TRIPLET_NAME GET "${CT_VCPKG_CONFIG_OUTPUT}" "default-triplet") + set(CT_DEFAULT_TRIPLET_FILE "${CT_VCPKG_ROOT_DIR}/triplets/${CT_DEFAULT_TRIPLET_NAME}.cmake") + + cmake_path(GET CT_TRIPLET_TEMPLATE STEM CT_TRIPLET_TEMPLATE_NAME) + set(CT_GENERATED_TRIPLETS_DIR "${CMAKE_BINARY_DIR}/gen/vcpkg-triplets") + set(CT_GENERATED_TRIPLET_NAME "${CT_TRIPLET_TEMPLATE_NAME}-${CT_DEFAULT_TRIPLET_NAME}") + set(CT_GENERATED_TRIPLET_FILE "${CT_GENERATED_TRIPLETS_DIR}/${CT_GENERATED_TRIPLET_NAME}.cmake") + + configure_file("${CT_TRIPLET_TEMPLATE}" "${CT_GENERATED_TRIPLET_FILE}" @ONLY) + message(STATUS "Generated a vcpkg triplet '${CT_GENERATED_TRIPLET_NAME}' from template") + + set(VCPKG_TARGET_TRIPLET "${CT_GENERATED_TRIPLET_NAME}" CACHE STRING "") + set(VCPKG_OVERLAY_TRIPLETS "${CT_GENERATED_TRIPLETS_DIR}" CACHE PATH "") + endif() +endfunction() + +macro(ct_configure_vcpkg) + _ct_fetch_vcpkg() + _ct_bootstrap_vcpkg() + _ct_generate_toolchain_and_triplet() + include("${CT_VCPKG_TOOLCHAIN_FILE}") +endmacro() diff --git a/cmake/FetchVcpkg.cmake b/cmake/FetchVcpkg.cmake deleted file mode 100644 index d523eaa..0000000 --- a/cmake/FetchVcpkg.cmake +++ /dev/null @@ -1,48 +0,0 @@ -include(FetchContent) - -option(CT_FETCH_VCPKG "Should the vcpkg be fetched via FetchContent" ON) - -function(ct_fetch_vcpkg) - if(PROJECT_NAME) - message(FATAL_ERROR "ct_fetch_vcpkg must be run before the first project directive") - endif() - - if(NOT CT_FETCH_VCPKG) - message(STATUS "Skipped fetching vcpkg because CT_FETCH_VCPKG=OFF") - return() - endif() - - if(CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg.cmake$") - message(STATUS "Skipped fetching vcpkg because CMAKE_TOOLCHAIN_FILE is already set to vcpkg.cmake") - return() - endif() - - message(STATUS "Fetching vcpkg...") - FetchContent_Declare( - vcpkg - URL https://github.com/microsoft/vcpkg/archive/refs/tags/2025.02.14.tar.gz - ) - FetchContent_MakeAvailable(vcpkg) - - set(VCPKG_BOOTSTRAP_OPTIONS "-disableMetrics" CACHE STRING "") - - if(CMAKE_TOOLCHAIN_FILE) - set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" CACHE FILEPATH "") - endif() - set(CMAKE_TOOLCHAIN_FILE "${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake" - CACHE FILEPATH "Vcpkg toolchain file" FORCE) - - if(CT_TRIPLET_TEMPLATE) - cmake_path(GET CT_TRIPLET_TEMPLATE STEM CT_TRIPLET_NAME) - - configure_file( - "${CT_TRIPLET_TEMPLATE}" - "${CMAKE_BINARY_DIR}/vcpkg-triplets/${CT_TRIPLET_NAME}.cmake" - @ONLY - ) - message(STATUS "Generated a vcpkg triplet '${CT_TRIPLET_NAME}' from template") - - set(VCPKG_TARGET_TRIPLET "${CT_TRIPLET_NAME}" CACHE FILEPATH "") - set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_BINARY_DIR}/vcpkg-triplets" CACHE FILEPATH "") - endif() -endfunction() diff --git a/cmake/GenerateToolchain.cmake b/cmake/GenerateToolchain.cmake deleted file mode 100644 index 67c52a5..0000000 --- a/cmake/GenerateToolchain.cmake +++ /dev/null @@ -1,25 +0,0 @@ -option(CT_HARDENED "Should the standard library be hardened" OFF) -option(CT_SANITIZED "Should the build be sanitized" OFF) - -function(ct_gen_toolchain) - if(PROJECT_NAME) - message(FATAL_ERROR "ct_gen_toolchain must be run before the first project directive") - endif() - - if(NOT CT_TOOLCHAIN_TEMPLATE) - return() - endif() - - if(CMAKE_TOOLCHAIN_FILE) - message(STATUS "Skipped generating toolchain file because CMAKE_TOOLCHAIN_FILE is already set") - return() - endif() - - cmake_path(GET CT_TOOLCHAIN_TEMPLATE STEM CT_TOOLCHAIN_NAME) - set(CT_GENERATED_TOOLCHAIN "${CMAKE_BINARY_DIR}/toolchains/${CT_TOOLCHAIN_NAME}.cmake") - - configure_file("${CT_TOOLCHAIN_TEMPLATE}" "${CT_GENERATED_TOOLCHAIN}" @ONLY) - message(STATUS "Generated a toolchain '${CT_TOOLCHAIN_NAME}' from template") - - set(CMAKE_TOOLCHAIN_FILE "${CT_GENERATED_TOOLCHAIN}" CACHE FILEPATH "") -endfunction() diff --git a/cmake/SetupToolchain.cmake b/cmake/SetupToolchain.cmake deleted file mode 100644 index e668b23..0000000 --- a/cmake/SetupToolchain.cmake +++ /dev/null @@ -1,5 +0,0 @@ -include(GenerateToolchain) -include(FetchVcpkg) - -ct_gen_toolchain() -ct_fetch_vcpkg() diff --git a/cmake/templates/toolchain.cmake.in b/cmake/templates/toolchain.cmake.in new file mode 100644 index 0000000..c61c565 --- /dev/null +++ b/cmake/templates/toolchain.cmake.in @@ -0,0 +1,11 @@ +include("@CMAKE_SOURCE_DIR@/cmake/ConfigureCompiler.cmake") + +ct_configure_compiler("@CMAKE_CXX_COMPILER_ID@" @CT_HARDENED@ @CT_SANITIZED@) + +set(CMAKE_C_FLAGS_INIT "${CT_COMPILER_FLAGS}") +set(CMAKE_CXX_FLAGS_INIT "${CT_COMPILER_FLAGS}") + +foreach(linker IN ITEMS "SHARED" "MODULE" "EXE") + set(CMAKE_${linker}_LINKER_FLAGS_INIT "${CT_LINKER_FLAGS}") + set(CMAKE_${linker}_LINKER_FLAGS "${CT_LINKER_FLAGS}") +endforeach() diff --git a/cmake/templates/vcpkg-triplet.cmake.in b/cmake/templates/vcpkg-triplet.cmake.in new file mode 100644 index 0000000..1540477 --- /dev/null +++ b/cmake/templates/vcpkg-triplet.cmake.in @@ -0,0 +1,3 @@ +include("@CT_DEFAULT_TRIPLET_FILE@") + +set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "@CT_GENERATED_TOOLCHAIN_FILE@") diff --git a/cmake/toolchain-templates/ci.cmake.in b/cmake/toolchain-templates/ci.cmake.in deleted file mode 100644 index b82ee19..0000000 --- a/cmake/toolchain-templates/ci.cmake.in +++ /dev/null @@ -1,12 +0,0 @@ -include("@CMAKE_SOURCE_DIR@/cmake/ConfigureCompiler.cmake") - -ct_set_compiler("@CT_C_EXE@" "@CT_CXX_EXE@") -ct_configure_compiler("@CT_COMPILER_ID@" "@CT_HARDENED" "@CT_SANITIZED@" OFF) - -set(CMAKE_C_FLAGS "${CT_COMPILER_FLAGS}" CACHE STRING "") -set(CMAKE_CXX_FLAGS "${CT_COMPILER_FLAGS}" CACHE STRING "") - -foreach(linker IN ITEMS "SHARED" "MODULE" "EXE") - set(CMAKE_${linker}_LINKER_FLAGS_INIT "${CT_LINKER_FLAGS}" CACHE STRING "") - set(CMAKE_${linker}_LINKER_FLAGS "${CT_LINKER_FLAGS}" CACHE STRING "") -endforeach() diff --git a/cmake/vcpkg-triplet-templates/ct-x64-linux.cmake.in b/cmake/vcpkg-triplet-templates/ct-x64-linux.cmake.in deleted file mode 100644 index 764062d..0000000 --- a/cmake/vcpkg-triplet-templates/ct-x64-linux.cmake.in +++ /dev/null @@ -1,7 +0,0 @@ -set(VCPKG_TARGET_ARCHITECTURE x64) -set(VCPKG_CRT_LINKAGE dynamic) -set(VCPKG_LIBRARY_LINKAGE static) - -set(VCPKG_CMAKE_SYSTEM_NAME Linux) - -set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "@VCPKG_CHAINLOAD_TOOLCHAIN_FILE@")