Skip to content

Commit 775cbca

Browse files
ci: Add arm64x build support and a Windows on Arm arm64x test job
A shippable Windows on Arm loader is an arm64x binary that fuses the arm64 and arm64ec code, which takes two builds combined at link time (see https://learn.microsoft.com/en-us/windows/arm/arm64x-build). Add a BUILD_AS_ARM64X switch that drives the arm64 pass to emit a link repro and the arm64ec pass to graft those inputs in with /machine:arm64x, plus a windows_arm64x CI job that builds the fused binary and runs both the arm64ec and an x64 test suite against it. The arm64x build support and CI job come from charles-lunarg's add_arm64x_ci_test branch.
1 parent 26a2a29 commit 775cbca

2 files changed

Lines changed: 82 additions & 0 deletions

File tree

.github/workflows/build.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,49 @@ jobs:
297297
- run: cmake --install build --prefix build/install --config ${{matrix.config}}
298298
- run: ctest --parallel --output-on-failure -C ${{matrix.config}} --test-dir build/
299299

300+
windows_arm64x:
301+
# Native Windows on Arm: build a real arm64x binary and check the x64 test suite passes against it.
302+
# windows is 2x expensive to run on GitHub machines, so only run after the cheaper arm jobs pass.
303+
needs: windows_arm
304+
runs-on: windows-11-arm
305+
timeout-minutes: 30
306+
steps:
307+
- uses: actions/checkout@v6
308+
- uses: actions/setup-python@v5
309+
with:
310+
python-version: '3.x'
311+
- run: python scripts/update_deps.py --arch arm64 --api vulkan --dir external --clean-build --clean-install
312+
- run: |
313+
cmake -S. -B build_arm64x `
314+
-A arm64 `
315+
-D BUILD_AS_ARM64X=ARM64 `
316+
-D CMAKE_BUILD_TYPE=Release `
317+
-D BUILD_WERROR=ON `
318+
-C external/helper.cmake
319+
- run: cmake --build build_arm64x/ --config Release
320+
- run: |
321+
cmake -S. -B build_arm64xec `
322+
-A arm64ec `
323+
-D BUILD_AS_ARM64X=ARM64EC `
324+
-D BUILD_TESTS=ON `
325+
-D CMAKE_BUILD_TYPE=Release `
326+
-D BUILD_WERROR=ON `
327+
-C external/helper.cmake
328+
- run: cmake --build build_arm64xec/ --config Release
329+
- run: cmake --install build_arm64xec --prefix build/install --config Release
330+
- run: ctest --parallel --output-on-failure -C Release --test-dir build_arm64xec/
331+
- run: |
332+
cmake -S. -B build_x64 `
333+
-A x64 `
334+
-D BUILD_TESTS=ON `
335+
-D CMAKE_BUILD_TYPE=Release `
336+
-D BUILD_WERROR=ON `
337+
-C external/helper.cmake
338+
- run: cmake --build build_x64/ --config Release
339+
- run: ctest --parallel --output-on-failure -C Release --test-dir build_x64/
340+
env:
341+
VK_LOADER_TEST_LOADER_PATH: ${{ github.workspace }}/build_arm64xec/loader/Release/vulkan-1.dll
342+
300343
# Test both clang and clang-cl (Chromium project uses clang-cl)
301344
windows_clang:
302345
# windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well

CMakeLists.txt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,3 +324,42 @@ if (BUILD_TESTS)
324324
enable_testing()
325325
add_subdirectory(tests)
326326
endif()
327+
328+
# Building an arm64x binary takes two builds: one for the arm64 code and one for the arm64ec/x64 code,
329+
# combined at link time. See https://learn.microsoft.com/en-us/windows/arm/arm64x-build
330+
if(DEFINED BUILD_AS_ARM64X)
331+
set(ARM64X_TARGETS vulkan)
332+
333+
# Where the arm64 build drops the link.rsp file the arm64ec build reads back.
334+
set(arm64ReproDir "${CMAKE_CURRENT_SOURCE_DIR}/repros")
335+
336+
# Pull the arm64 objs, libs and def out of the rsp the arm64 build produced and feed them to the
337+
# arm64ec link with /machine:arm64x so the two halves fuse into one arm64x binary.
338+
function(set_arm64_dependencies n)
339+
set(REPRO_FILE "${arm64ReproDir}/${n}.rsp")
340+
file(STRINGS "${REPRO_FILE}" ARM64_OBJS REGEX obj\"$)
341+
file(STRINGS "${REPRO_FILE}" ARM64_DEF REGEX def\"$)
342+
file(STRINGS "${REPRO_FILE}" ARM64_LIBS REGEX lib\"$)
343+
string(REPLACE "\"" ";" ARM64_OBJS "${ARM64_OBJS}")
344+
string(REPLACE "\"" ";" ARM64_LIBS "${ARM64_LIBS}")
345+
string(REPLACE "\"" ";" ARM64_DEF "${ARM64_DEF}")
346+
string(REPLACE "/def:" "/defArm64Native:" ARM64_DEF "${ARM64_DEF}")
347+
348+
target_sources(${n} PRIVATE ${ARM64_OBJS})
349+
target_link_options(${n} PRIVATE /machine:arm64x "${ARM64_DEF}" "${ARM64_LIBS}")
350+
endfunction()
351+
352+
if("${BUILD_AS_ARM64X}" STREQUAL "ARM64")
353+
# arm64 pass: emit an rsp with absolute paths to every linker input.
354+
add_custom_target(mkdirs ALL COMMAND cmd /c (if not exist \"${arm64ReproDir}/\" mkdir \"${arm64ReproDir}\" ))
355+
foreach (n ${ARM64X_TARGETS})
356+
add_dependencies(${n} mkdirs)
357+
target_link_options(${n} PRIVATE "/LINKREPROFULLPATHRSP:${arm64ReproDir}/${n}.rsp")
358+
endforeach()
359+
elseif("${BUILD_AS_ARM64X}" STREQUAL "ARM64EC")
360+
# arm64ec pass: graft the arm64 inputs in to produce the arm64x binary.
361+
foreach (n ${ARM64X_TARGETS})
362+
set_arm64_dependencies(${n})
363+
endforeach()
364+
endif()
365+
endif()

0 commit comments

Comments
 (0)