diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa0c6338..1482a4f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,77 +21,35 @@ env: GTEST_COLOR: 1 jobs: - build-windows: - name: Windows Host Build + build-in-devcontainer: + name: Devcontainer Build runs-on: [ubuntu-latest] container: ghcr.io/philips-software/amp-devcontainer-cpp:v6.8.2@sha256:8d6d0b49bef9b1f572793dee8dcc05edcbe4f44f108f075640dda284ff3e2d4e # v6.8.2 + strategy: + matrix: + target: [Host, Windows] + fail-fast: false steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + - if: ${{ matrix.target == 'Windows' }} + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 id: cache-winsdk with: path: /winsdk key: cache-winsdk-10.0.26100-14.43.17.13 - - if: ${{ steps.cache-winsdk.outputs.cache-hit != 'true' }} + - if: ${{ matrix.target == 'Windows' && steps.cache-winsdk.outputs.cache-hit != 'true' }} run: ./get-winsdk.sh - uses: hendrikmuhs/ccache-action@1bbbcda0748b3e340dee71a314fa68ffcbd6df79 # v1.2.21 with: - key: ${{ github.job }} - max-size: 2G - - uses: lukka/run-cmake@af1be47fd7c933593f687731bc6fdbee024d3ff4 # v10.8 - with: - configurePreset: "Windows" - buildPreset: "Windows-Release" - configurePresetAdditionalArgs: "['-DCMAKE_C_COMPILER_LAUNCHER=ccache', '-DCMAKE_CXX_COMPILER_LAUNCHER=ccache']" - - build-linux: - name: Linux Host Build - runs-on: [ubuntu-24.04] - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: hendrikmuhs/ccache-action@1bbbcda0748b3e340dee71a314fa68ffcbd6df79 # v1.2.21 - with: - key: ${{ github.job }} + key: ${{ github.job }}-${{ matrix.target }} max-size: 2G - - uses: seanmiddleditch/gha-setup-ninja@3b1f8f94a2f8254bd26914c4ab9474d4f0015f67 # v6 - uses: lukka/run-cmake@af1be47fd7c933593f687731bc6fdbee024d3ff4 # v10.8 with: - configurePreset: "Host" - buildPreset: "Host-Release" - testPreset: "Host-Release" - configurePresetAdditionalArgs: "['-DCMAKE_C_COMPILER_LAUNCHER=ccache', '-DCMAKE_CXX_COMPILER_LAUNCHER=ccache']" - - name: Upload test logs - if: ${{ failure() }} - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: test-logs - path: build/Host/Testing/Temporary/ - - build-linux-devcontainer: - name: Linux Host Build in Devcontainer - runs-on: [ubuntu-latest] - container: ghcr.io/philips-software/amp-devcontainer-cpp:v6.8.2@sha256:8d6d0b49bef9b1f572793dee8dcc05edcbe4f44f108f075640dda284ff3e2d4e # v6.8.2 - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: hendrikmuhs/ccache-action@1bbbcda0748b3e340dee71a314fa68ffcbd6df79 # v1.2.21 - with: - key: ${{ github.job }} - max-size: 2G - - uses: lukka/run-cmake@af1be47fd7c933593f687731bc6fdbee024d3ff4 # v10.8 - with: - configurePreset: "Host" - buildPreset: "Host-Release" - testPreset: "Host-Release" - configurePresetAdditionalArgs: "['-DCMAKE_C_COMPILER_LAUNCHER=ccache', '-DCMAKE_CXX_COMPILER_LAUNCHER=ccache']" - - name: Upload test logs - if: ${{ failure() }} - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: test-logs - path: build/Host/Testing/Temporary/ + configurePreset: "${{ matrix.target}}" + buildPreset: "${{ matrix.target}}-Release" test-linux: - name: Linux Host Test + name: Acceptance Test on Linux runs-on: [ubuntu-latest] permissions: contents: read @@ -109,7 +67,6 @@ jobs: with: configurePreset: "Host" buildPreset: "Host-Debug" - configurePresetAdditionalArgs: "['-DCMAKE_C_COMPILER_LAUNCHER=ccache', '-DCMAKE_CXX_COMPILER_LAUNCHER=ccache']" - run: | bats --formatter junit cucumber_cpp/acceptance_test/test.bats | tee test-report.xml - uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2.23.0 @@ -119,28 +76,27 @@ jobs: host_build_test: name: Host Build & Test - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.os }}-${{ matrix.version}} strategy: matrix: - os: [macos-latest, windows-latest] + os: [macos, windows, ubuntu] + version: [latest] + type: [synchronous] + fail-fast: false steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: hendrikmuhs/ccache-action@1bbbcda0748b3e340dee71a314fa68ffcbd6df79 # v1.2.21 with: - key: ${{ github.job }}-${{ matrix.os }} + key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.type }} max-size: 2G - variant: sccache - uses: lukka/run-cmake@af1be47fd7c933593f687731bc6fdbee024d3ff4 # v10.8 with: - configurePreset: "host-single-Debug" - buildPreset: "host-single-Debug" - testPreset: "host-single-Debug" - configurePresetAdditionalArgs: "['-DCMAKE_C_COMPILER_LAUNCHER=sccache', '-DCMAKE_CXX_COMPILER_LAUNCHER=sccache']" + workflowPreset: "${{ matrix.os }}-Debug-${{ matrix.type }}" - name: Upload test logs if: ${{ failure() }} uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: test-logs - path: build/host-single-Dbebug/Testing/Temporary/ + path: .build/${{ matrix.os }}-Debug-${{ matrix.type }}/Testing/Temporary/ diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 7b2f6eea..c5a01a62 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -38,7 +38,6 @@ jobs: configurePreset: "Coverage" buildPreset: "Coverage" testPreset: "Coverage" - configurePresetAdditionalArgs: "['-DCMAKE_C_COMPILER_LAUNCHER=ccache', '-DCMAKE_CXX_COMPILER_LAUNCHER=ccache']" env: GTEST_OUTPUT: "xml:${{ github.workspace }}/testresults/" @@ -84,5 +83,4 @@ jobs: with: configurePreset: "Host" buildPreset: "Host-Debug" - configurePresetAdditionalArgs: "['-DCMAKE_C_COMPILER_LAUNCHER=ccache', '-DCMAKE_CXX_COMPILER_LAUNCHER=ccache']" - uses: github/codeql-action/analyze@b1bff81932f5cdfc8695c7752dcee935dcd061c8 # v4.33.0 diff --git a/.mega-linter.yml b/.mega-linter.yml index e4d2f646..3cac3b79 100644 --- a/.mega-linter.yml +++ b/.mega-linter.yml @@ -3,7 +3,6 @@ ENABLE: - ACTION - CPP - DOCKERFILE - - JSON - MARKDOWN - REPOSITORY - SPELL diff --git a/CMakePresets.json b/CMakePresets.json index c4b7b6c3..76343f1c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -33,17 +33,35 @@ "generator": "Ninja Multi-Config" }, { - "name": "host-single-Debug", - "displayName": "Configuration for Host Tooling and Tests, Single Config Generator, Debug", + "name": "host-single-Debug-synchronous", + "hidden": true, "inherits": "defaults", + "generator": "Ninja", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } }, + { + "name": "ubuntu-Debug-synchronous", + "displayName": "Configuration for Linux Debug and Tests, Single Config Generator, synchronous", + "inherits": "host-single-Debug-synchronous" + }, + { + "name": "windows-Debug-synchronous", + "displayName": "Configuration for Windows Debug and Tests, Single Config Generator, synchronous", + "inherits": "host-single-Debug-synchronous", + "toolchainFile": "${sourceDir}/cmake/Windows.MSVC.toolchain.cmake" + }, + { + "name": "macos-Debug-synchronous", + "displayName": "Configuration for macOS Debug and Tests, Single Config Generator, synchronous", + "inherits": "host-single-Debug-synchronous" + }, { "name": "host-single-time-profile", "displayName": "Configuration time profiling", "inherits": "defaults", + "generator": "Ninja", "cacheVariables": { "CMAKE_C_COMPILER": "clang", "CMAKE_CXX_COMPILER": "clang++", @@ -92,9 +110,19 @@ "configurePreset": "Host-Iwyu" }, { - "name": "host-single-Debug", + "name": "ubuntu-Debug-synchronous", + "configuration": "Debug", + "configurePreset": "ubuntu-Debug-synchronous" + }, + { + "name": "windows-Debug-synchronous", "configuration": "Debug", - "configurePreset": "host-single-Debug" + "configurePreset": "windows-Debug-synchronous" + }, + { + "name": "macos-Debug-synchronous", + "configuration": "Debug", + "configurePreset": "macos-Debug-synchronous" }, { "name": "host-single-time-profile", @@ -148,10 +176,75 @@ "inherits": "defaults" }, { - "name": "host-single-Debug", - "configurePreset": "host-single-Debug", + "name": "ubuntu-Debug-synchronous", + "configurePreset": "ubuntu-Debug-synchronous", + "configuration": "Debug", + "inherits": "defaults" + }, + { + "name": "windows-Debug-synchronous", + "configurePreset": "windows-Debug-synchronous", "configuration": "Debug", "inherits": "defaults" + }, + { + "name": "macos-Debug-synchronous", + "configurePreset": "macos-Debug-synchronous", + "configuration": "Debug", + "inherits": "defaults" + } + ], + "workflowPresets": [ + { + "name": "ubuntu-Debug-synchronous", + "steps": [ + { + "type": "configure", + "name": "ubuntu-Debug-synchronous" + }, + { + "type": "build", + "name": "ubuntu-Debug-synchronous" + }, + { + "type": "test", + "name": "ubuntu-Debug-synchronous" + } + ] + }, + { + "name": "windows-Debug-synchronous", + "steps": [ + { + "type": "configure", + "name": "windows-Debug-synchronous" + }, + { + "type": "build", + "name": "windows-Debug-synchronous" + }, + { + "type": "test", + "name": "windows-Debug-synchronous" + } + ] + }, + { + "name": "macos-Debug-synchronous", + "steps": [ + { + "type": "configure", + "name": "macos-Debug-synchronous" + }, + { + "type": "build", + "name": "macos-Debug-synchronous" + }, + { + "type": "test", + "name": "macos-Debug-synchronous" + } + ] } ] } diff --git a/cmake/VSWhere.cmake b/cmake/VSWhere.cmake new file mode 100644 index 00000000..4cbf54a0 --- /dev/null +++ b/cmake/VSWhere.cmake @@ -0,0 +1,143 @@ +#---------------------------------------------------------------------------------------------------------------------- +# MIT License +# +# Copyright (c) 2026 Mark Schofield +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#---------------------------------------------------------------------------------------------------------------------- +include_guard() + +include("${CMAKE_CURRENT_LIST_DIR}/WSL.cmake") + +#[[==================================================================================================================== + toolchain_validate_vs_files + --------------------------- + + Note: Not supported for consumption outside of the toolchain files. + + Validates the the specified folder exists and contains the specified files. + + toolchain_validate_vs_files( + > + > + ...> + ) + + If the folder or files are missing, then a FATAL_ERROR is reported. +====================================================================================================================]]# +function(toolchain_validate_vs_files) + set(OPTIONS) + set(ONE_VALUE_KEYWORDS FOLDER DESCRIPTION) + set(MULTI_VALUE_KEYWORDS FILES) + + cmake_parse_arguments(PARSE_ARGV 0 VS "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") + + if(NOT EXISTS ${VS_FOLDER}) + message(FATAL_ERROR "Folder not present - ${VS_FOLDER} - ensure that the ${VS_DESCRIPTION} are installed with Visual Studio.") + endif() + + foreach(FILE ${VS_FILES}) + if(NOT EXISTS "${VS_FOLDER}/${FILE}") + message(FATAL_ERROR "File not present - ${VS_FOLDER}/${FILE} - ensure that the ${VS_DESCRIPTION} are installed with Visual Studio.") + endif() + endforeach() +endfunction() + +#[[==================================================================================================================== + findVisualStudio + ---------------- + + Finds a Visual Studio instance, and sets CMake variables based on properties of the found instance. + + findVisualStudio( + [VERSION ] + [PRERELEASE ] + [PRODUCTS ] + [REQUIRES ...] + PROPERTIES + < > + ) +====================================================================================================================]]# +function(findVisualStudio) + set(OPTIONS) + set(ONE_VALUE_KEYWORDS VERSION PRERELEASE PRODUCTS) + set(MULTI_VALUE_KEYWORDS REQUIRES PROPERTIES) + + cmake_parse_arguments(PARSE_ARGV 0 FIND_VS "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") + + set(VSWHERE_HINT_PATH "$ENV{ProgramFiles\(x86\)}/Microsoft Visual Studio/Installer") + + # Accommodate WSL + if((CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") AND (EXISTS "/usr/bin/wslpath")) + toolchain_read_reg_string("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion" "ProgramFilesDir (x86)" PROGRAM_FILES_X86_PATH) + set(VSWHERE_HINT_PATH "${PROGRAM_FILES_X86_PATH}\\Microsoft Visual Studio\\Installer") + toolchain_to_wsl_path("${VSWHERE_HINT_PATH}" VSWHERE_HINT_PATH) + endif() + + find_program(VSWHERE_PATH + NAMES vswhere vswhere.exe + HINTS ${VSWHERE_HINT_PATH} + ) + + if(NOT VSWHERE_PATH) + message(FATAL_ERROR "'vswhere' isn't found.") + endif() + + set(VSWHERE_COMMAND ${VSWHERE_PATH} -latest) + + if(FIND_VS_PRERELEASE) + list(APPEND VSWHERE_COMMAND -prerelease) + endif() + + if(FIND_VS_PRODUCTS) + list(APPEND VSWHERE_COMMAND -products ${FIND_VS_PRODUCTS}) + endif() + + if(FIND_VS_REQUIRES) + list(APPEND VSWHERE_COMMAND -requires ${FIND_VS_REQUIRES}) + endif() + + if(FIND_VS_VERSION) + list(APPEND VSWHERE_COMMAND -version "${FIND_VS_VERSION}") + endif() + + message(VERBOSE "findVisualStudio: VSWHERE_COMMAND = ${VSWHERE_COMMAND}") + + execute_process( + COMMAND ${VSWHERE_COMMAND} + OUTPUT_VARIABLE VSWHERE_OUTPUT + ) + + message(VERBOSE "findVisualStudio: VSWHERE_OUTPUT = ${VSWHERE_OUTPUT}") + + # Matches `VSWHERE_PROPERTY` in the `VSWHERE_OUTPUT` text in the format written by vswhere. + # The matched value is assigned to the variable `VARIABLE_NAME` in the parent scope. + function(getVSWhereProperty VSWHERE_OUTPUT VSWHERE_PROPERTY VARIABLE_NAME) + string(REGEX MATCH "${VSWHERE_PROPERTY}: [^\r\n]*" VSWHERE_VALUE "${VSWHERE_OUTPUT}") + string(REPLACE "${VSWHERE_PROPERTY}: " "" VSWHERE_VALUE "${VSWHERE_VALUE}") + set(${VARIABLE_NAME} "${VSWHERE_VALUE}" PARENT_SCOPE) + endfunction() + + while(FIND_VS_PROPERTIES) + list(POP_FRONT FIND_VS_PROPERTIES VSWHERE_PROPERTY) + list(POP_FRONT FIND_VS_PROPERTIES VSWHERE_CMAKE_VARIABLE) + getVSWhereProperty("${VSWHERE_OUTPUT}" ${VSWHERE_PROPERTY} VSWHERE_VALUE) + set(${VSWHERE_CMAKE_VARIABLE} ${VSWHERE_VALUE} PARENT_SCOPE) + endwhile() +endfunction() diff --git a/cmake/WSL.cmake b/cmake/WSL.cmake new file mode 100644 index 00000000..f74db0d6 --- /dev/null +++ b/cmake/WSL.cmake @@ -0,0 +1,87 @@ +#---------------------------------------------------------------------------------------------------------------------- +# MIT License +# +# Copyright (c) 2026 Mark Schofield +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#---------------------------------------------------------------------------------------------------------------------- +include_guard() + +#[[==================================================================================================================== + toolchain_read_reg_string + ------------------------- + + Reads a string value from the Windows registry using reg.exe via WSL. + + toolchain_read_reg_string( + + + + ) + + Note: Not supported for consumption outside of the toolchain files. +====================================================================================================================]]# +function(toolchain_read_reg_string REG_KEY REG_VALUE OUTPUT_VARIABLE) + if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + message(FATAL_ERROR "toolchain_read_reg_string should not be used on Windows platforms.") + endif() + + find_program(REG_EXE_PATH NAMES reg.exe) + if(NOT REG_EXE_PATH) + message(FATAL_ERROR "reg.exe not found - cannot read Windows registry from WSL.") + endif() + + execute_process( + COMMAND ${REG_EXE_PATH} QUERY ${REG_KEY} /v ${REG_VALUE} + OUTPUT_VARIABLE REG_OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(REGEX REPLACE ".*REG_SZ[ \t]+(.*)$" "\\1" REG_OUTPUT "${REG_OUTPUT}") + message(VERBOSE "toolchain_read_reg_string: VALUE = ${REG_OUTPUT}") + set(${OUTPUT_VARIABLE} "${REG_OUTPUT}" PARENT_SCOPE) +endfunction() + +#[[==================================================================================================================== + toolchain_to_wsl_path + --------------------- + + Converts a Windows path to a WSL path. + + toolchain_to_wsl_path( + + + ) + + Note: Not supported for consumption outside of the toolchain files. +====================================================================================================================]]# +function(toolchain_to_wsl_path INPUT_PATH OUTPUT_VARIABLE) + if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + message(FATAL_ERROR "toolchain_to_wsl_path should not be used on Windows platforms.") + endif() + + if(NOT (EXISTS "/usr/bin/wslpath")) + message(FATAL_ERROR "wslpath not found - cannot convert Windows path to WSL path.") + endif() + + execute_process(COMMAND /usr/bin/wslpath -u ${INPUT_PATH} + OUTPUT_VARIABLE WSL_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + set(${OUTPUT_VARIABLE} "${WSL_PATH}" PARENT_SCOPE) +endfunction() diff --git a/cmake/Windows.Kits.cmake b/cmake/Windows.Kits.cmake new file mode 100644 index 00000000..25041062 --- /dev/null +++ b/cmake/Windows.Kits.cmake @@ -0,0 +1,206 @@ +#---------------------------------------------------------------------------------------------------------------------- +# MIT License +# +# Copyright (c) 2026 Mark Schofield +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#---------------------------------------------------------------------------------------------------------------------- +# +# | CMake Variable | Description | +# |-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------| +# | CMAKE_SYSTEM_VERSION | The version of the operating system for which CMake is to build. Defaults to the host version. | +# | CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE | The architecture of the tooling to use. Defaults to 'arm64' on ARM64 systems, otherwise 'x64'. | +# | CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION | The version of the Windows SDK to use. Defaults to the highest installed, that is no higher than CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM | +# | CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM | The maximum version of the Windows SDK to use, for example '10.0.19041.0'. Defaults to nothing | +# | CMAKE_WINDOWS_KITS_10_DIR | The location of the root of the Windows Kits 10 directory. | +# +# The following variables will be set: +# +# | CMake Variable | Description | +# |---------------------------------------------|-------------------------------------------------------------------------------------------------------| +# | CMAKE_MT | The path to the 'mt' tool. | +# | CMAKE_RC_COMPILER | The path to the 'rc' tool. | +# | CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION | The version of the Windows SDK to be used. | +# | MDMERGE_TOOL | The path to the 'mdmerge' tool. | +# | MIDL_COMPILER | The path to the 'midl' compiler. | +# | WINDOWS_KITS_BIN_PATH | The path to the folder containing the Windows Kits binaries. | +# | WINDOWS_KITS_INCLUDE_PATH | The path to the folder containing the Windows Kits include files. | +# | WINDOWS_KITS_LIB_PATH | The path to the folder containing the Windows Kits library files. | +# | WINDOWS_KITS_REFERENCES_PATH | The path to the folder containing the Windows Kits references. | +# +include_guard() + +include("${CMAKE_CURRENT_LIST_DIR}/WSL.cmake") + +if(NOT CMAKE_SYSTEM_VERSION) + set(CMAKE_SYSTEM_VERSION ${CMAKE_HOST_SYSTEM_VERSION}) +endif() + +if(NOT CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE) + if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL ARM64) + set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE arm64) + else() + set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE x64) + endif() +endif() + +if(NOT CMAKE_WINDOWS_KITS_10_DIR) + if((CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") AND (EXISTS "/usr/bin/wslpath")) + toolchain_read_reg_string("HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0" "InstallationFolder" CMAKE_WINDOWS_KITS_10_DIR) + message(VERBOSE "Windows.Kits: CMAKE_WINDOWS_KITS_10_DIR (WSL) = ${CMAKE_WINDOWS_KITS_10_DIR}") + toolchain_to_wsl_path("${CMAKE_WINDOWS_KITS_10_DIR}" CMAKE_WINDOWS_KITS_10_DIR) + else() + get_filename_component(CMAKE_WINDOWS_KITS_10_DIR "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0;InstallationFolder]" ABSOLUTE CACHE) + if ("${CMAKE_WINDOWS_KITS_10_DIR}" STREQUAL "/registry") + unset(CMAKE_WINDOWS_KITS_10_DIR) + endif() + endif() +endif() + +message(VERBOSE "Windows.Kits: CMAKE_WINDOWS_KITS_10_DIR = ${CMAKE_WINDOWS_KITS_10_DIR}") +if(NOT CMAKE_WINDOWS_KITS_10_DIR) + message(FATAL_ERROR "Unable to find an installed Windows SDK, and one wasn't specified.") +endif() + +# If a CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION wasn't specified, find the highest installed version that is no higher +# than the host version +if(NOT CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) + file(GLOB WINDOWS_KITS_VERSIONS RELATIVE "${CMAKE_WINDOWS_KITS_10_DIR}/lib" "${CMAKE_WINDOWS_KITS_10_DIR}/lib/*") + list(FILTER WINDOWS_KITS_VERSIONS INCLUDE REGEX "10\\.0\\.") + list(SORT WINDOWS_KITS_VERSIONS COMPARE NATURAL ORDER DESCENDING) + while(WINDOWS_KITS_VERSIONS) + list(POP_FRONT WINDOWS_KITS_VERSIONS CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) + if(NOT CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM) + message(VERBOSE "Windows.Kits: Defaulting version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") + break() + endif() + + if(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION VERSION_LESS_EQUAL CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM) + message(VERBOSE "Windows.Kits: Choosing version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") + break() + endif() + + message(VERBOSE "Windows.Kits: Not suitable: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") + set(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) + endwhile() +endif() + +if(NOT CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) + message(FATAL_ERROR "A Windows SDK could not be found.") +endif() + +set(WINDOWS_KITS_BIN_PATH "${CMAKE_WINDOWS_KITS_10_DIR}/bin/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}" CACHE PATH "" FORCE) +set(WINDOWS_KITS_INCLUDE_PATH "${CMAKE_WINDOWS_KITS_10_DIR}/include/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}" CACHE PATH "" FORCE) +set(WINDOWS_KITS_LIB_PATH "${CMAKE_WINDOWS_KITS_10_DIR}/lib/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}" CACHE PATH "" FORCE) +set(WINDOWS_KITS_REFERENCES_PATH "${CMAKE_WINDOWS_KITS_10_DIR}/References" CACHE PATH "" FORCE) +set(WINDOWS_KITS_PLATFORM_PATH "${CMAKE_WINDOWS_KITS_10_DIR}/Platforms/UAP/${CMAKE_SYSTEM_VERSION}/Platform.xml" CACHE PATH "" FORCE) + +if(NOT EXISTS ${WINDOWS_KITS_BIN_PATH}) + message(FATAL_ERROR "Windows SDK ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION} cannot be found: Folder '${WINDOWS_KITS_BIN_PATH}' does not exist.") +endif() + +if(NOT EXISTS ${WINDOWS_KITS_INCLUDE_PATH}) + message(FATAL_ERROR "Windows SDK ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION} cannot be found: Folder '${WINDOWS_KITS_INCLUDE_PATH}' does not exist.") +endif() + +if(NOT EXISTS ${WINDOWS_KITS_LIB_PATH}) + message(FATAL_ERROR "Windows SDK ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION} cannot be found: Folder '${WINDOWS_KITS_LIB_PATH}' does not exist.") +endif() + +if(NOT CMAKE_MT) + set(CMAKE_MT "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/mt.exe") +endif() + +if((NOT CMAKE_RC_COMPILER) AND (NOT CMAKE_RC_COMPILER_INIT)) + set(CMAKE_RC_COMPILER_INIT "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/rc.exe") + set(CMAKE_RC_FLAGS_INIT "/nologo") +endif() + +set(MIDL_COMPILER "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/midl.exe") +set(MDMERGE_TOOL "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/mdmerge.exe") + +# Windows SDK +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + if(CMAKE_SYSTEM_PROCESSOR STREQUAL AMD64) + set(WINDOWS_KITS_TARGET_ARCHITECTURE x64) + elseif((CMAKE_SYSTEM_PROCESSOR STREQUAL ARM) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL ARM64) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL X86)) + set(WINDOWS_KITS_TARGET_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) + endif() +elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64) + set(WINDOWS_KITS_TARGET_ARCHITECTURE ARM64) + elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64) + set(WINDOWS_KITS_TARGET_ARCHITECTURE x64) + endif() +endif() + +if(NOT WINDOWS_KITS_TARGET_ARCHITECTURE) + message(FATAL_ERROR "Unable identify Windows Kits architecture for CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}") +endif() + +foreach(LANG C CXX RC ASM_MASM) + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${WINDOWS_KITS_INCLUDE_PATH}/ucrt") + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${WINDOWS_KITS_INCLUDE_PATH}/shared") + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${WINDOWS_KITS_INCLUDE_PATH}/um") + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${WINDOWS_KITS_INCLUDE_PATH}/winrt") + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${WINDOWS_KITS_INCLUDE_PATH}/cppwinrt") +endforeach() + +link_directories("${WINDOWS_KITS_LIB_PATH}/ucrt/${WINDOWS_KITS_TARGET_ARCHITECTURE}") +link_directories("${WINDOWS_KITS_LIB_PATH}/um/${WINDOWS_KITS_TARGET_ARCHITECTURE}") +link_directories("${WINDOWS_KITS_REFERENCES_PATH}/${WINDOWS_KITS_TARGET_ARCHITECTURE}") + +# With a FASTBuild generator the path to the WindowsKits binaries should be added to the path so that +# compiler tools - like link.exe - can find dependent tools - like mt.exe. +# +if(CMAKE_GENERATOR MATCHES "^FASTBuild") + + # Updates the CMAKE_FASTBUILD_ENV_OVERRIDES to prepend the given value to the Path environment variable. + # + # If: + # * CMAKE_FASTBUILD_ENV_OVERRIDES is not defined, it will be set to Path=${VALUE}\;$ENV{Path} + # * CMAKE_FASTBUILD_ENV_OVERRIDES is defined but does not contain a Path entry, a `Path=${VALUE}\;$ENV{Path}` + # entry will be added. + # * CMAKE_FASTBUILD_ENV_OVERRIDES is defined and contains a Path entry, the value will be prepended to the + # existing Path entry. + function(toolchain_prepend_fastbuild_path VALUE) + string(REPLACE ";" "\;" CURRENT_PATH "$ENV{Path}") + set(FOUND FALSE) + set(RESULT) + foreach(ENTRY IN LISTS CMAKE_FASTBUILD_ENV_OVERRIDES) + if(ENTRY MATCHES "^Path=(.*)$") + set(FOUND TRUE) + string(REPLACE ";" "\;" CURRENT_PATH "${CMAKE_MATCH_1}") + list(APPEND RESULT "Path=${VALUE}\;${CURRENT_PATH}") + else() + list(APPEND RESULT ${ENTRY}) + endif() + endforeach() + + if(NOT FOUND) + list(APPEND RESULT "Path=${VALUE}\;${CURRENT_PATH}") + endif() + + set(CMAKE_FASTBUILD_ENV_OVERRIDES "${RESULT}" PARENT_SCOPE) + endfunction() + + toolchain_prepend_fastbuild_path("${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}") +endif() diff --git a/cmake/Windows.MSVC.toolchain.cmake b/cmake/Windows.MSVC.toolchain.cmake new file mode 100644 index 00000000..d464b283 --- /dev/null +++ b/cmake/Windows.MSVC.toolchain.cmake @@ -0,0 +1,291 @@ +#---------------------------------------------------------------------------------------------------------------------- +# MIT License +# +# Copyright (c) 2026 Mark Schofield +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#---------------------------------------------------------------------------------------------------------------------- +# +# This CMake toolchain file configures a CMake, non-'Visual Studio Generator' build to use +# the MSVC compilers and tools. +# +# The following variables can be used to configure the behavior of this toolchain file: +# +# | CMake Variable | Description | +# |---------------------------------------------|--------------------------------------------------------------------------------------------------------------------------| +# | CMAKE_SYSTEM_PROCESSOR | The processor to compiler for. One of 'X86', 'AMD64', 'ARM', 'ARM64'. Defaults to ${CMAKE_HOST_SYSTEM_PROCESSOR}. | +# | CMAKE_SYSTEM_VERSION | The version of the operating system for which CMake is to build. Defaults to the host version. | +# | CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE | The architecture of the tooling to use. Defaults to 'arm64' on ARM64 systems, otherwise 'x64'. | +# | CMAKE_VS_PRODUCTS | One or more Visual Studio Product IDs to consider. Defaults to '*' | +# | CMAKE_VS_VERSION_PRERELEASE | Whether 'prerelease' versions of Visual Studio should be considered. Defaults to 'OFF' | +# | CMAKE_VS_VERSION_RANGE | A verson range for VS instances to find. For example, '[16.0,17.0)' will find versions '16.*'. Defaults to '[16.0,17.0)' | +# | CMAKE_WINDOWS_KITS_10_DIR | The location of the root of the Windows Kits 10 directory. | +# | TOOLCHAIN_ADD_VS_NINJA_PATH | Whether the toolchain should add the path to the VS Ninja to the CMAKE_SYSTEM_PROGRAM_PATH. Defaults to 'ON'. | +# | TOOLCHAIN_UPDATE_PROGRAM_PATH | Whether the toolchain should update CMAKE_PROGRAM_PATH. Defaults to 'ON'. | +# | VS_EXPERIMENTAL_MODULE | Whether experimental module support should be enabled. | +# | VS_INSTALLATION_PATH | The location of the root of the Visual Studio installation. If not specified VSWhere will be used to search for one. | +# | VS_PLATFORM_TOOLSET_VERSION | The version of the MSVC toolset to use. For example, 14.29.30133. Defaults to the highest available. | +# | VS_USE_SPECTRE_MITIGATION_ATLMFC_RUNTIME | Whether the compiler should link with the ATLMFC runtime that uses 'Spectre' mitigations. Defaults to 'OFF'. | +# | VS_USE_SPECTRE_MITIGATION_RUNTIME | Whether the compiler should link with a runtime that uses 'Spectre' mitigations. Defaults to 'OFF'. | +# +# The toolchain file will set the following variables: +# +# | CMake Variable | Description | +# |---------------------------------------------|-------------------------------------------------------------------------------------------------------| +# | CMAKE_C_COMPILER | The path to the C compiler to use. | +# | CMAKE_CXX_COMPILER | The path to the C++ compiler to use. | +# | CMAKE_MT | The path to the 'mt.exe' tool to use. | +# | CMAKE_RC_COMPILER | The path tp the 'rc.exe' tool to use. | +# | CMAKE_SYSTEM_NAME | "Windows", when cross-compiling | +# | CMAKE_VS_PLATFORM_TOOLSET_VERSION | The version of the MSVC toolset being used - e.g. 14.29.30133. | +# | MSVC | 1 | +# | MSVC_VERSION | The '' version of the C++ compiler being used. For example, '1929' | +# | VS_INSTALLATION_PATH | The location of the root of the Visual Studio installation. | +# | WIN32 | 1 | +# +# Other configuration: +# +# * If the 'CMAKE_CUDA_COMPILER' is set, and 'CMAKE_CUDA_HOST_COMPILER' is not set, and ENV{CUDAHOSTCXX} not defined +# then 'CMAKE_CUDA_HOST_COMPILER' is set to the value of 'CMAKE_CXX_COMPILER'. +# +# Resources: +# +# +cmake_minimum_required(VERSION 3.20) + +include_guard() + +# If `CMAKE_HOST_SYSTEM_NAME` is not 'Windows', there's nothing to do. +if(NOT (CMAKE_HOST_SYSTEM_NAME STREQUAL Windows)) + return() +endif() + +option(TOOLCHAIN_UPDATE_PROGRAM_PATH "Whether the toolchain should update CMAKE_PROGRAM_PATH." ON) +option(TOOLCHAIN_ADD_VS_NINJA_PATH "Whether the toolchain should add the path to the VS Ninja to the CMAKE_SYSTEM_PROGRAM_PATH." ON) + +set(UNUSED ${CMAKE_TOOLCHAIN_FILE}) # Note: only to prevent cmake unused variable warninig +list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES + CMAKE_C_COMPILER + CMAKE_CXX_COMPILER + CMAKE_MT + CMAKE_RC_COMPILER + CMAKE_SYSTEM_PROCESSOR + CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE + CMAKE_VS_PRODUCTS + CMAKE_VS_VERSION_PRERELEASE + CMAKE_VS_VERSION_RANGE + CMAKE_WINDOWS_KITS_10_DIR + VS_INSTALLATION_PATH + VS_INSTALLATION_VERSION + VS_PLATFORM_TOOLSET_VERSION +) +set(WIN32 1) +set(MSVC 1) + +include("${CMAKE_CURRENT_LIST_DIR}/VSWhere.cmake") + +# If `CMAKE_SYSTEM_PROCESSOR` isn't set, default to `CMAKE_HOST_SYSTEM_PROCESSOR` +if(NOT CMAKE_SYSTEM_PROCESSOR) + set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_HOST_SYSTEM_PROCESSOR}) +endif() + +# If `CMAKE_SYSTEM_PROCESSOR` is not equal to `CMAKE_HOST_SYSTEM_PROCESSOR`, this is cross-compilation. +# CMake expects `CMAKE_SYSTEM_NAME` to be set to reflect cross-compilation. +if(NOT (CMAKE_SYSTEM_PROCESSOR STREQUAL ${CMAKE_HOST_SYSTEM_PROCESSOR})) + set(CMAKE_SYSTEM_NAME Windows) +endif() + +if(NOT CMAKE_VS_PRODUCTS) + set(CMAKE_VS_PRODUCTS "*") +endif() + +if(NOT CMAKE_VS_VERSION_PRERELEASE) + set(CMAKE_VS_VERSION_PRERELEASE OFF) +endif() + +if(NOT CMAKE_VS_VERSION_RANGE) + set(CMAKE_VS_VERSION_RANGE "[16.0,)") +endif() + +if(NOT CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE) + if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL ARM64) + set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE arm64) + else() + set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE x64) + endif() +endif() + +if(NOT VS_USE_SPECTRE_MITIGATION_RUNTIME) + set(VS_USE_SPECTRE_MITIGATION_RUNTIME OFF) +endif() + +# Find Visual Studio +# +if(NOT VS_INSTALLATION_PATH) + findVisualStudio( + VERSION ${CMAKE_VS_VERSION_RANGE} + PRERELEASE ${CMAKE_VS_VERSION_PRERELEASE} + PRODUCTS ${CMAKE_VS_PRODUCTS} + PROPERTIES + installationVersion VS_INSTALLATION_VERSION + installationPath VS_INSTALLATION_PATH + ) +endif() + +message(VERBOSE "VS_INSTALLATION_VERSION = ${VS_INSTALLATION_VERSION}") +message(VERBOSE "VS_INSTALLATION_PATH = ${VS_INSTALLATION_PATH}") + +if(NOT VS_INSTALLATION_PATH) + message(FATAL_ERROR "Unable to find Visual Studio") +endif() + +cmake_path(NORMAL_PATH VS_INSTALLATION_PATH) + +set(VS_MSVC_PATH "${VS_INSTALLATION_PATH}/VC/Tools/MSVC") + +# Use 'VS_PLATFORM_TOOLSET_VERSION' to resolve 'CMAKE_VS_PLATFORM_TOOLSET_VERSION' +# +if(NOT VS_PLATFORM_TOOLSET_VERSION) + file(GLOB VS_PLATFORM_TOOLSET_VERSIONS RELATIVE ${VS_MSVC_PATH} ${VS_MSVC_PATH}/*) + list(SORT VS_PLATFORM_TOOLSET_VERSIONS COMPARE NATURAL ORDER DESCENDING) + list(POP_FRONT VS_PLATFORM_TOOLSET_VERSIONS VS_PLATFORM_TOOLSET_VERSION) + unset(VS_PLATFORM_TOOLSET_VERSIONS) +endif() + +set(CMAKE_VS_PLATFORM_TOOLSET_VERSION ${VS_PLATFORM_TOOLSET_VERSION}) +set(VS_TOOLSET_PATH "${VS_INSTALLATION_PATH}/VC/Tools/MSVC/${CMAKE_VS_PLATFORM_TOOLSET_VERSION}") + +# Set the tooling variables, include_directories and link_directories +# + +# Map CMAKE_SYSTEM_PROCESSOR values to CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE that identifies the tools that should +# be used to produce code for the CMAKE_SYSTEM_PROCESSOR. +if(CMAKE_SYSTEM_PROCESSOR STREQUAL AMD64) + set(CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE x64) +elseif((CMAKE_SYSTEM_PROCESSOR STREQUAL ARM) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL ARM64) + OR (CMAKE_SYSTEM_PROCESSOR STREQUAL X86)) + set(CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) +else() + message(FATAL_ERROR "Unable identify compiler architecture for CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}") +endif() + +set(CMAKE_CXX_COMPILER "${VS_TOOLSET_PATH}/bin/Host${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}/cl.exe") +set(CMAKE_C_COMPILER "${VS_TOOLSET_PATH}/bin/Host${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}/cl.exe") + +if(CMAKE_SYSTEM_PROCESSOR STREQUAL ARM) + set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /EHsc") +endif() + +# Compiler +foreach(LANG C CXX RC) + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${VS_TOOLSET_PATH}/ATLMFC/include") + list(APPEND CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES "${VS_TOOLSET_PATH}/include") +endforeach() + +foreach(LANG C CXX) + # Add '/X': Do not add %INCLUDE% to include search path + set(CMAKE_${LANG}_FLAGS_INIT "${CMAKE_${LANG}_FLAGS_INIT} /X") +endforeach() + +if(VS_USE_SPECTRE_MITIGATION_ATLMFC_RUNTIME) + # Ensure that the necessary folder and files are present before adding the 'link_directories' + toolchain_validate_vs_files( + DESCRIPTION "ATLMFC Spectre libraries" + FOLDER "${VS_TOOLSET_PATH}/ATLMFC/lib/spectre/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}" + FILES + atls.lib + ) + link_directories("${VS_TOOLSET_PATH}/ATLMFC/lib/spectre/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") +else() + link_directories("${VS_TOOLSET_PATH}/ATLMFC/lib/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") +endif() + +if(VS_USE_SPECTRE_MITIGATION_RUNTIME) + # Ensure that the necessary folder and files are present before adding the 'link_directories' + toolchain_validate_vs_files( + DESCRIPTION "Spectre libraries" + FOLDER "${VS_TOOLSET_PATH}/lib/spectre/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}" + FILES + msvcrt.lib vcruntime.lib vcruntimed.lib + ) + link_directories("${VS_TOOLSET_PATH}/lib/spectre/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") +else() + link_directories("${VS_TOOLSET_PATH}/lib/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") +endif() + +link_directories("${VS_TOOLSET_PATH}/lib/x86/store/references") + +# Module support +if(VS_EXPERIMENTAL_MODULE) + set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /experimental:module") + set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /stdIfcDir \"${VS_TOOLSET_PATH}/ifc/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}\"") +endif() + +# Windows Kits +include("${CMAKE_CURRENT_LIST_DIR}/Windows.Kits.cmake") + +# CUDA support +# +# If a CUDA compiler is specified, and a host compiler wasn't specified, set 'CMAKE_CXX_COMPILER' +# as the host compiler. +if(CMAKE_CUDA_COMPILER) + if((NOT CMAKE_CUDA_HOST_COMPILER) AND (NOT DEFINED ENV{CUDAHOSTCXX})) + set(CMAKE_CUDA_HOST_COMPILER "${CMAKE_CXX_COMPILER}") + endif() +endif() + +# If 'TOOLCHAIN_UPDATE_PROGRAM_PATH' is selected, update CMAKE_PROGRAM_PATH. +# +if(TOOLCHAIN_UPDATE_PROGRAM_PATH) + list(APPEND CMAKE_PROGRAM_PATH "${VS_TOOLSET_PATH}/bin/Host${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/${CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE}") + list(APPEND CMAKE_PROGRAM_PATH "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}") +endif() + +# If the CMAKE_GENERATOR is Ninja-based, and the path to the Visual Studio-installed Ninja is present, add it to +# the CMAKE_SYSTEM_PROGRAM_PATH. 'find_program' searches CMAKE_SYSTEM_PROGRAM_PATH after the environment path, so +# an installed Ninja would be preferred. +# +if( (CMAKE_GENERATOR MATCHES "^Ninja") AND + (EXISTS "${VS_INSTALLATION_PATH}/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja") AND + (TOOLCHAIN_ADD_VS_NINJA_PATH)) + list(APPEND CMAKE_SYSTEM_PROGRAM_PATH "${VS_INSTALLATION_PATH}/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja") +endif() + +# Set 'CMAKE__COMPILER_PREDEFINES_COMMAND' to allow consumers - like automoc - to obtain the compiler predefines. +# +set(CMAKE_CXX_COMPILER_PREDEFINES_COMMAND + ${CMAKE_CXX_COMPILER} + /nologo + /Zc:preprocessor + /PD + /c + /Fonul. + ${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp +) + +set(CMAKE_C_COMPILER_PREDEFINES_COMMAND + ${CMAKE_C_COMPILER} + /nologo + /Zc:preprocessor + /PD + /c + /Fonul. + ${CMAKE_ROOT}/Modules/CMakeCCompilerABI.c +) diff --git a/cmake/WindowsToolchainFilesProvidedBy.md b/cmake/WindowsToolchainFilesProvidedBy.md new file mode 100644 index 00000000..652c1180 --- /dev/null +++ b/cmake/WindowsToolchainFilesProvidedBy.md @@ -0,0 +1 @@ +https://github.com/MarkSchofield/WindowsToolchain