From d26d6135edeeadc0f4e98ad2008af73cecbc4ff6 Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Sun, 1 Feb 2026 12:06:04 -0800 Subject: [PATCH] Support Windows.Clang.toolchain.cmake and Windows.Kits.cmake consumption from Linux --- VSWhere.cmake | 15 +++- WSL.cmake | 87 +++++++++++++++++++ Windows.Clang.toolchain.cmake | 41 ++++++--- Windows.Kits.cmake | 39 ++++++--- example/CMakePresets.json | 49 +++++++++++ example/CommandLineAsm/CMakeLists.txt | 3 +- .../WindowsApplication/WindowsApplication.rc | 11 +-- 7 files changed, 210 insertions(+), 35 deletions(-) create mode 100644 WSL.cmake diff --git a/VSWhere.cmake b/VSWhere.cmake index de77c7b..4cbf54a 100644 --- a/VSWhere.cmake +++ b/VSWhere.cmake @@ -23,6 +23,8 @@ #---------------------------------------------------------------------------------------------------------------------- include_guard() +include("${CMAKE_CURRENT_LIST_DIR}/WSL.cmake") + #[[==================================================================================================================== toolchain_validate_vs_files --------------------------- @@ -79,12 +81,21 @@ function(findVisualStudio) 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 "$ENV{ProgramFiles\(x86\)}/Microsoft Visual Studio/Installer" + HINTS ${VSWHERE_HINT_PATH} ) - if(VSWHERE_PATH STREQUAL "VSWHERE_PATH-NOTFOUND") + if(NOT VSWHERE_PATH) message(FATAL_ERROR "'vswhere' isn't found.") endif() diff --git a/WSL.cmake b/WSL.cmake new file mode 100644 index 0000000..f74db0d --- /dev/null +++ b/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/Windows.Clang.toolchain.cmake b/Windows.Clang.toolchain.cmake index dc0b7b9..ac2c559 100644 --- a/Windows.Clang.toolchain.cmake +++ b/Windows.Clang.toolchain.cmake @@ -62,11 +62,6 @@ 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) @@ -147,6 +142,11 @@ if(NOT VS_INSTALLATION_PATH) installationPath VS_INSTALLATION_PATH ) endif() + + if((CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") AND (EXISTS "/usr/bin/wslpath")) + # Path properties returned by VSWhere are Windows-style paths. Convert to WSL-style paths on WSL. + toolchain_to_wsl_path("${VS_INSTALLATION_PATH}" VS_INSTALLATION_PATH) + endif() endif() message(VERBOSE "VS_INSTALLATION_VERSION = ${VS_INSTALLATION_VERSION}") @@ -177,13 +177,23 @@ set(VS_TOOLSET_PATH "${VS_INSTALLATION_PATH}/VC/Tools/MSVC/${CMAKE_VS_PLATFORM_T # 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() +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + 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}) + endif() +elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64) + set(CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE ARM64) + elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64) + set(CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE x64) + endif() +endif() + +if(NOT CMAKE_VS_PLATFORM_TOOLSET_ARCHITECTURE) message(FATAL_ERROR "Unable identify compiler architecture for CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}") endif() @@ -234,6 +244,13 @@ foreach(LANG C CXX) endif() endforeach() +# If not compiling on Windows, set the clang compiler target to Windows. +if(NOT (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")) + foreach(LANG C CXX) + set(CMAKE_${LANG}_COMPILER_TARGET "${CMAKE_SYSTEM_PROCESSOR}-windows-msvc") + endforeach() +endif() + 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( diff --git a/Windows.Kits.cmake b/Windows.Kits.cmake index 2a6a58b..2504106 100644 --- a/Windows.Kits.cmake +++ b/Windows.Kits.cmake @@ -46,6 +46,8 @@ # include_guard() +include("${CMAKE_CURRENT_LIST_DIR}/WSL.cmake") + if(NOT CMAKE_SYSTEM_VERSION) set(CMAKE_SYSTEM_VERSION ${CMAKE_HOST_SYSTEM_VERSION}) endif() @@ -59,12 +61,19 @@ if(NOT CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE) endif() if(NOT CMAKE_WINDOWS_KITS_10_DIR) - 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) + 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() @@ -127,13 +136,23 @@ set(MIDL_COMPILER "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARC set(MDMERGE_TOOL "${WINDOWS_KITS_BIN_PATH}/${CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE}/mdmerge.exe") # Windows SDK -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}) -else() +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() diff --git a/example/CMakePresets.json b/example/CMakePresets.json index 978f4ac..d50f157 100644 --- a/example/CMakePresets.json +++ b/example/CMakePresets.json @@ -22,6 +22,7 @@ { "name": "windows", "inherits": ["default"], + "condition": { "lhs": "${hostSystemName}", "type": "equals", "rhs": "Windows" }, "hidden": true }, { @@ -167,6 +168,46 @@ "CMAKE_GENERATOR_PLATFORM": "ARM64,version=10.0.26100.0", "CMAKE_SYSTEM_PROCESSOR": "ARM64" } + }, + { + "name": "linux-windows", + "inherits": ["default"], + "description": "Cross-compiling to Windows from Linux", + "condition": { "lhs": "${hostSystemName}", "type": "equals", "rhs": "Linux" }, + "hidden": true, + "cacheVariables": { + "CMAKE_SYSTEM_NAME": "Windows" + } + }, + { + "name": "linux-windows-ninja-clang", + "inherits": ["linux-windows"], + "generator": "Ninja Multi-Config", + "hidden": true, + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "../Windows.Clang.toolchain.cmake", + "CMAKE_C_COMPILER": "clang-20", + "CMAKE_CXX_COMPILER": "clang++-20", + "CMAKE_LINKER_TYPE": "lld", + "CMAKE_MT": "llvm-mt-20", + "CMAKE_RC_COMPILER": "llvm-rc-20", + "CMAKE_C_USING_LINKER_lld": "-fuse-ld=lld", + "CMAKE_CXX_USING_LINKER_lld": "-fuse-ld=lld" + } + }, + { + "name": "linux-windows-ninja-clang-arm64", + "inherits": ["linux-windows-ninja-clang"], + "cacheVariables": { + "CMAKE_SYSTEM_PROCESSOR": "aarch64" + } + }, + { + "name": "linux-windows-ninja-clang-x64", + "inherits": ["linux-windows-ninja-clang"], + "cacheVariables": { + "CMAKE_SYSTEM_PROCESSOR": "x86_64" + } } ], "buildPresets": [ @@ -213,6 +254,14 @@ { "name": "windows-vs2022-arm64", "configurePreset": "windows-vs2022-arm64" + }, + { + "name": "linux-windows-ninja-clang-arm64", + "configurePreset": "linux-windows-ninja-clang-arm64" + }, + { + "name": "linux-windows-ninja-clang-x64", + "configurePreset": "linux-windows-ninja-clang-x64" } ] } diff --git a/example/CommandLineAsm/CMakeLists.txt b/example/CommandLineAsm/CMakeLists.txt index 8885b51..58c0885 100644 --- a/example/CommandLineAsm/CMakeLists.txt +++ b/example/CommandLineAsm/CMakeLists.txt @@ -8,7 +8,8 @@ if((CMAKE_SYSTEM_PROCESSOR STREQUAL AMD64) OR (CMAKE_SYSTEM_PROCESSOR STREQUAL X elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL ARM64) enable_language(ASM_MARMASM) else() - message(FATAL_ERROR "Unknown CMAKE_SYSTEM_PROCESSOR for ASM language.") + message(STATUS "Unknown CMAKE_SYSTEM_PROCESSOR for ASM language.") + return() endif() add_executable(CommandLineAsm diff --git a/example/WindowsApplication/WindowsApplication.rc b/example/WindowsApplication/WindowsApplication.rc index 1b0ca25..9e005af 100644 --- a/example/WindowsApplication/WindowsApplication.rc +++ b/example/WindowsApplication/WindowsApplication.rc @@ -1,7 +1,7 @@ //--------------------------------------------------------------------------------------------------------------------- // //--------------------------------------------------------------------------------------------------------------------- -#include "resource.h" +#include "Resource.h" #define APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_INVOKED @@ -38,15 +38,6 @@ BEGIN END END -//--------------------------------------------------------------------------------------------------------------------- -// Accelerator -//--------------------------------------------------------------------------------------------------------------------- -IDC_WINDOWSAPPLICATION ACCELERATORS -BEGIN - "?", IDM_ABOUT, ASCII, ALT - "/", IDM_ABOUT, ASCII, ALT -END - //--------------------------------------------------------------------------------------------------------------------- // Dialog //---------------------------------------------------------------------------------------------------------------------