From 316d0ec9d298ea59046b6cb91b1d5298d2141cb1 Mon Sep 17 00:00:00 2001 From: xjt <969211735@qq.com> Date: Thu, 8 Jan 2026 10:36:40 +0800 Subject: [PATCH] ci: Windows cross-compilation via clang-cl on linux This commit adds a new CI job `windows-clang-cl` that runs on Ubuntu runners but targets Windows x64. 1. Utilizes `clang-cl` and `lld-link` with `Ninja` for faster build speeds. 2. Uses `xwin` to fetch MSVC CRT and Windows SDK headers/libraries portably. 3. Implements a custom CMake toolchain file to handle cross-compilation flags and path mapping. 4. Enables `ccache` to significantly reduce recompilation time. --- .github/workflows/build.yml | 151 +++++++++++++++++- CMakeLists.txt | 10 +- cmake/toolchain-linux-clang-cl.cmake | 225 +++++++++++++++++++++++++++ 3 files changed, 383 insertions(+), 3 deletions(-) create mode 100644 cmake/toolchain-linux-clang-cl.cmake diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ce034a9b8..ee5d6b82b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,7 +51,6 @@ jobs: with: build-config: ${{ inputs.build-config || 'RelWithDebInfo' }} - windows: needs: meta runs-on: windows-latest @@ -163,6 +162,156 @@ jobs: name: MAA-win-${{ matrix.arch }} path: "install" + windows-clang-cl: + needs: meta + runs-on: ubuntu-latest + strategy: + matrix: + arch: [x86_64] + fail-fast: false + + env: + XWIN_CACHE_DIR: ${{ github.workspace }}/.xwin-cache + + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Install LLVM and build tools + run: | + sudo apt-get update + sudo apt-get install -y clang lld llvm ninja-build ccache + + # Create symlinks for clang-cl and llvm tools if they don't exist in standard path + sudo ln -sf $(which clang) /usr/bin/clang-cl + + if ! command -v llvm-rc &> /dev/null; then + sudo ln -sf $(find /usr/bin -name "llvm-rc*" | head -n 1) /usr/bin/llvm-rc + fi + if ! command -v llvm-lib &> /dev/null; then + sudo ln -sf $(find /usr/bin -name "llvm-ar*" | head -n 1) /usr/bin/llvm-lib + fi + if ! command -v llvm-mt &> /dev/null; then + sudo ln -sf $(find /usr/bin -name "llvm-mt*" | head -n 1) /usr/bin/llvm-mt + fi + if ! command -v lld-link &> /dev/null; then + sudo ln -sf $(find /usr/bin -name "lld-link*" | head -n 1) /usr/bin/lld-link + fi + + clang-cl --version + lld-link --version + ccache --version + + - name: Cache xwin SDK + id: cache-xwin + uses: actions/cache@v4 + with: + path: ${{ env.XWIN_CACHE_DIR }} + key: xwin-${{ runner.os }}-${{ matrix.arch }}-sdk-${{ env.MSVC_SDK_VERSION }} + + - name: Setup xwin (Download MSVC Headers/Libs) + if: steps.cache-xwin.outputs.cache-hit != 'true' + run: | + mkdir -p tools/xwin + curl -L https://github.com/Jake-Shadle/xwin/releases/download/0.6.5/xwin-0.6.5-x86_64-unknown-linux-musl.tar.gz | tar xz -C tools/xwin --strip-components=1 + + # Download SDK/CRT + ./tools/xwin/xwin --accept-license splat --output ${{ env.XWIN_CACHE_DIR }} + + - name: Update MaaUtils + if: github.event_name == 'repository_dispatch' && github.event.action == 'MaaUtilsUpdated' + run: | + git submodule update --remote source/MaaUtils + + - name: Setup ccache + uses: Chocobo1/setup-ccache-action@v1 + with: + remove_stale_cache: false + + - name: Cache MaaDeps + id: cache-maadeps + uses: actions/cache@v4 + with: + path: | + ./source/MaaUtils/MaaDeps + # Use 'windows' key to share cache logically + key: maadeps-windows-${{ matrix.arch }}-${{ hashFiles('./tools/maadeps-download.py') }} + + - name: Bootstrap MaaDeps (Force Windows) + if: steps.cache-maadeps.outputs.cache-hit != 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Force download windows dependencies on Linux + python3 tools/maadeps-download.py ${{ matrix.arch == 'x86_64' && 'x64' || 'arm64' }}-windows + + - uses: pnpm/action-setup@v4 + with: + version: latest + + - name: Use Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Prepare node_modules + run: | + cd source/binding/NodeJS + pnpm i + cd - + + - name: Build MAA (Cross Compile) + env: + CCACHE_COMPILERTYPE: clang-cl + run: | + cmake -G "Ninja Multi-Config" -B build \ + -DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}/cmake/toolchain-linux-clang-cl.cmake" \ + -DMAADEPS_TRIPLET="maa-${{ matrix.arch == 'x86_64' && 'x64' || 'arm64' }}-windows" \ + -DMAA_HASH_VERSION='${{ needs.meta.outputs.tag }}' \ + -DWITH_NODEJS_BINDING=ON \ + -DWITH_QUICKJS_BINDING=ON \ + -DXWIN_CACHE_DIR="${{ env.XWIN_CACHE_DIR }}" \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_FLAGS_RELWITHDEBINFO="/O1 /Zi /DNDEBUG" \ + -DCMAKE_C_FLAGS_RELWITHDEBINFO="/O1 /Zi /DNDEBUG" + + cmake --build build --config ${{ needs.meta.outputs.build-config }} -j $(nproc) + + - name: Install + shell: bash + run: | + cmake --install build --prefix install --config ${{ needs.meta.outputs.build-config }} + + rm -rf install/bin/msvc-debug + + cp -r docs install + cp README*.md install + cp -r sample install + cp -r LICENSE.md install + + - name: Download Plugin + uses: robinraju/release-downloader@v1 + with: + repository: MaaXYZ/MaaPluginDemo + tag: ${{ needs.meta.outputs.latest-plugin }} + fileName: "*win-${{ matrix.arch }}*" + out-file-path: "build/download_plugins" + extract: true + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install Plugin + shell: bash + run: | + cp -r build/download_plugins/bin install/bin/plugins + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: MAA-win-clangcl-${{ matrix.arch }} + path: "install" + ubuntu: needs: meta runs-on: ubuntu-latest diff --git a/CMakeLists.txt b/CMakeLists.txt index 2eec9b092..d81c09f01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,13 @@ cmake_minimum_required(VERSION 3.28) project(MaaFw) -set(CMAKE_CXX_SCAN_FOR_MODULES OFF) +set(CMAKE_CXX_SCAN_FOR_MODULES OFF) + +# Detect if we are cross-compiling to Windows from Linux +if(CMAKE_SYSTEM_NAME STREQUAL "Windows" AND CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + set(MAA_CROSS_COMPILE_WIN32 ON) + message(STATUS "Cross-compiling for Windows on Linux host") +endif() include(source/MaaUtils/MaaUtils.cmake) @@ -35,7 +41,7 @@ if(NOT WITH_DBG_CONTROLLER) endif() if(WITH_WIN32_CONTROLLER AND NOT WIN32) - message(STATUS "Not on Windows, disable WITH_WIN32_CONTROLLER") + message(STATUS "Not targeting Windows, disable WITH_WIN32_CONTROLLER") set(WITH_WIN32_CONTROLLER OFF) endif() diff --git a/cmake/toolchain-linux-clang-cl.cmake b/cmake/toolchain-linux-clang-cl.cmake new file mode 100644 index 000000000..64fba23f5 --- /dev/null +++ b/cmake/toolchain-linux-clang-cl.cmake @@ -0,0 +1,225 @@ +# cmake/toolchain-linux-clang-cl.cmake + +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR AMD64) + +# Specify the compiler +set(CMAKE_C_COMPILER clang-cl) +set(CMAKE_CXX_COMPILER clang-cl) +set(CMAKE_RC_COMPILER llvm-rc) +set(CMAKE_MT llvm-mt) +set(CMAKE_AR llvm-lib) +set(CMAKE_LINKER lld-link) + +# Where is the target environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# XWIN_CACHE_DIR setup +if(NOT DEFINED XWIN_CACHE_DIR) + if(DEFINED ENV{XWIN_CACHE_DIR}) + set(XWIN_CACHE_DIR $ENV{XWIN_CACHE_DIR}) + else() + message(FATAL_ERROR "XWIN_CACHE_DIR is not defined.") + endif() +endif() + +set(MSVC_CRT_DIR "${XWIN_CACHE_DIR}/crt") +set(MSVC_SDK_DIR "${XWIN_CACHE_DIR}/sdk") + +# ============================================================================= +# Fix: Case sensitivity issues (Symlinks) +# ============================================================================= +set(_MSVC_UM_DIR "${MSVC_SDK_DIR}/lib/um/x86_64") + +if(EXISTS "${_MSVC_UM_DIR}/cfgmgr32.lib" AND NOT EXISTS "${_MSVC_UM_DIR}/Cfgmgr32.lib") + message(STATUS "Toolchain: Creating symlink for Cfgmgr32.lib...") + file(CREATE_LINK "${_MSVC_UM_DIR}/cfgmgr32.lib" "${_MSVC_UM_DIR}/Cfgmgr32.lib" SYMBOLIC) +endif() + +set(_MSVC_CPPWINRT_DIR "${MSVC_SDK_DIR}/include/cppwinrt") + +if(EXISTS "${MSVC_SDK_DIR}/include/CppWinRT" AND NOT EXISTS "${_MSVC_CPPWINRT_DIR}") + file(CREATE_LINK "${MSVC_SDK_DIR}/include/CppWinRT" "${_MSVC_CPPWINRT_DIR}" SYMBOLIC) +endif() + +if(EXISTS "${_MSVC_CPPWINRT_DIR}/WinRT" AND NOT EXISTS "${_MSVC_CPPWINRT_DIR}/winrt") + file(CREATE_LINK "${_MSVC_CPPWINRT_DIR}/WinRT" "${_MSVC_CPPWINRT_DIR}/winrt" SYMBOLIC) +endif() + +set(_TARGET_HEADER_DIR "${_MSVC_CPPWINRT_DIR}/winrt") + +if(EXISTS "${_TARGET_HEADER_DIR}") + if(EXISTS "${_TARGET_HEADER_DIR}/windows.graphics.capture.h" AND NOT EXISTS "${_TARGET_HEADER_DIR}/Windows.Graphics.Capture.h") + file(CREATE_LINK "${_TARGET_HEADER_DIR}/windows.graphics.capture.h" "${_TARGET_HEADER_DIR}/Windows.Graphics.Capture.h" SYMBOLIC) + endif() + + if(EXISTS "${_TARGET_HEADER_DIR}/windows.graphics.directx.direct3d11.h" AND NOT EXISTS "${_TARGET_HEADER_DIR}/Windows.Graphics.DirectX.Direct3D11.h") + file(CREATE_LINK "${_TARGET_HEADER_DIR}/windows.graphics.directx.direct3d11.h" "${_TARGET_HEADER_DIR}/Windows.Graphics.DirectX.Direct3D11.h" SYMBOLIC) + endif() +endif() + +# ============================================================================= +# Compile Flags (Include Paths & Warnings) +# ============================================================================= +set(_MAA_SYS_INCLUDES + "/imsvc \"${MSVC_CRT_DIR}/include\"" + "/imsvc \"${MSVC_SDK_DIR}/include/ucrt\"" + "/imsvc \"${MSVC_SDK_DIR}/include/shared\"" + "/imsvc \"${MSVC_SDK_DIR}/include/um\"" + "/imsvc \"${MSVC_SDK_DIR}/include/winrt\"" + "/imsvc \"${MSVC_SDK_DIR}/include/cppwinrt\"" +) +string(REPLACE ";" " " _MAA_SYS_INCLUDES_STR "${_MAA_SYS_INCLUDES}") + +set(_MAA_SUPPRESS_FLAGS "\ +/clang:-Wno-everything \ +/clang:-Wno-unused-command-line-argument \ +/clang:-Wno-sign-compare \ +/clang:-Wno-error=unused-command-line-argument \ +/clang:-Wno-missing-field-initializers \ +/clang:-Wno-missing-braces \ +/clang:-Wno-#pragma-messages \ +/clang:-Wno-macro-redefined \ +/clang:-Wno-double-promotion \ +/clang:-Wno-float-equal \ +/clang:-Wno-cast-qual \ +/clang:-Wno-format-nonliteral \ +/clang:-Wno-declaration-after-statement \ +/clang:-Wno-implicit-int-float-conversion \ +/clang:-Wno-bad-function-cast \ +/clang:-Wno-shadow \ +/clang:-Wno-switch-default \ +/clang:-Wno-cast-align \ +/clang:-Wno-undef \ +/clang:-Wno-duplicate-enum \ +/clang:-Wno-unused-macros \ +/clang:-Wno-missing-prototypes \ +/clang:-Wno-implicit-float-conversion \ +/clang:-Wno-missing-variable-declarations \ +/clang:-Wno-conditional-uninitialized \ +/clang:-Wno-float-conversion \ +/clang:-Wno-covered-switch-default \ +/clang:-Wno-unreachable-code-break \ +/clang:-Wno-unreachable-code-return \ +/clang:-Wno-unreachable-code \ +/clang:-Wno-switch-enum \ +/clang:-Wno-unused-parameter \ +/clang:-Wno-unused-variable \ +/clang:-Wno-unused-function \ +/clang:-Wno-unused-label \ +/clang:-Wno-unused-value \ +/clang:-Wno-implicit-fallthrough \ +/clang:-Wno-strict-prototypes \ +/clang:-Wno-shorten-64-to-32 \ +/clang:-Wno-sign-conversion \ +/clang:-Wno-int-conversion \ +/clang:-Wno-incompatible-pointer-types \ +/clang:-Wno-newline-eof \ +/clang:-Wno-deprecated-declarations \ +/clang:-Wno-shift-op-parentheses \ +/clang:-Wno-bitwise-op-parentheses \ +/clang:-Wno-logical-op-parentheses \ +/clang:-Wno-parentheses-equality \ +/clang:-Wno-microsoft-enum-value \ +/clang:-Wno-microsoft-include \ +/clang:-Wno-unknown-pragmas \ +/clang:-Wno-used-but-marked-unused \ +/clang:-Wno-cast-function-type-strict \ +/clang:-Wno-comma \ +") + +# Set Initial Flags +set(CMAKE_C_FLAGS_INIT "${_MAA_SYS_INCLUDES_STR} ${_MAA_SUPPRESS_FLAGS}") +set(CMAKE_CXX_FLAGS_INIT "${_MAA_SYS_INCLUDES_STR} /EHsc /arch:AVX ${_MAA_SUPPRESS_FLAGS}") + +# ============================================================================= +# Linker Configuration +# ============================================================================= + +# 1. Force add library search paths +add_link_options( + "/libpath:${MSVC_CRT_DIR}/lib/x86_64" + "/libpath:${MSVC_SDK_DIR}/lib/ucrt/x86_64" + "/libpath:${MSVC_SDK_DIR}/lib/um/x86_64" +) + +# 2. Resolve -lpthreads issue +set(CMAKE_THREAD_LIBS_INIT "") +set(CMAKE_HAVE_LIBC_PTHREAD 1) +set(CMAKE_USE_WIN32_THREADS_INIT 1) + +# 3. Global link standard libraries +link_libraries( + kernel32 user32 gdi32 winspool shell32 ole32 oleaut32 uuid comdlg32 advapi32 + msvcrt oldnames msvcprt + delayimp +) + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +add_compile_definitions( + _AMD64_ + _ALLOW_COMPILER_AND_STL_VERSION_MISMATCH + WIN32_LEAN_AND_MEAN + NOMINMAX +) + +# 4. Ensure warnings are ignored during compilation +add_compile_options( + -Wno-unused-command-line-argument + -Wno-sign-compare + -Wno-error=unused-command-line-argument + -Wno-missing-field-initializers + -Wno-missing-braces + "-Wno-#pragma-messages" + -Wno-macro-redefined + -Wno-double-promotion + -Wno-float-equal + -Wno-cast-qual + -Wno-format-nonliteral + -Wno-declaration-after-statement + -Wno-implicit-int-float-conversion + -Wno-bad-function-cast + -Wno-shadow + -Wno-switch-default + -Wno-cast-align + -Wno-undef + -Wno-duplicate-enum + -Wno-unused-macros + -Wno-missing-prototypes + -Wno-implicit-float-conversion + -Wno-missing-variable-declarations + -Wno-conditional-uninitialized + -Wno-float-conversion + -Wno-covered-switch-default + -Wno-unreachable-code-break + -Wno-unreachable-code-return + -Wno-unreachable-code + -Wno-switch-enum + -Wno-unused-parameter + -Wno-unused-variable + -Wno-unused-function + -Wno-unused-label + -Wno-unused-value + -Wno-implicit-fallthrough + -Wno-strict-prototypes + -Wno-shorten-64-to-32 + -Wno-sign-conversion + -Wno-int-conversion + -Wno-incompatible-pointer-types + -Wno-newline-eof + -Wno-deprecated-declarations + -Wno-shift-op-parentheses + -Wno-bitwise-op-parentheses + -Wno-logical-op-parentheses + -Wno-parentheses-equality + -Wno-microsoft-enum-value + -Wno-microsoft-include + -Wno-unknown-pragmas + -Wno-used-but-marked-unused + -Wno-cast-function-type-strict + -Wno-comma +) \ No newline at end of file