From 0a7ecf82a9f5130de27a9d02edac9637a51b7219 Mon Sep 17 00:00:00 2001 From: bicarus <202771338+bicarus-dev@users.noreply.github.com> Date: Mon, 8 Jun 2026 17:42:41 -0700 Subject: [PATCH] build: produce shared objects for spice.exe / spice_laa.exe, disable 64-bit winxp builds (#744) to speed up clean builds. GitHub CI time went from 20 minutes -> 10 minutes. --- src/spice2x/CMakeLists.txt | 36 ++++++++++++------- src/spice2x/build_all.sh | 58 ++++++++++++++++++++++--------- src/spice2x/launcher/launcher.cpp | 11 ++++-- src/spice2x/util/sysutils.cpp | 18 ++++++++++ src/spice2x/util/sysutils.h | 4 +++ 5 files changed, 95 insertions(+), 32 deletions(-) diff --git a/src/spice2x/CMakeLists.txt b/src/spice2x/CMakeLists.txt index d11d602..76b5ed8 100644 --- a/src/spice2x/CMakeLists.txt +++ b/src/spice2x/CMakeLists.txt @@ -670,37 +670,47 @@ set(SOURCE_FILES ${SOURCE_FILES} source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "Source Files" FILES ${SOURCE_FILES}) +# spice.exe / spice_laa.exe shared objects +########################################### +# spice.exe and spice_laa.exe are compiled identically; the only difference is +# the large-address-aware bit, which is set at link time. Compile the sources +# once into a shared OBJECT library so spice_laa.exe only costs an extra link +# instead of a full recompile. + +add_library(spicetools_spice_objs OBJECT ${SOURCE_FILES}) +target_link_libraries(spicetools_spice_objs + PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp + PRIVATE fmt::fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features) +target_link_libraries(spicetools_spice_objs PUBLIC winscard) + +if(NOT MSVC) + set_target_properties(spicetools_spice_objs PROPERTIES COMPILE_FLAGS "-m32") +endif() + # spice.exe ########### set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc) -add_executable(spicetools_spice ${SOURCE_FILES} ${RESOURCE_FILES}) -target_link_libraries(spicetools_spice - PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp - PRIVATE fmt::fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features) -target_link_libraries(spicetools_spice PUBLIC winscard) +add_executable(spicetools_spice ${RESOURCE_FILES}) +target_link_libraries(spicetools_spice PRIVATE spicetools_spice_objs) set_target_properties(spicetools_spice PROPERTIES PREFIX "") set_target_properties(spicetools_spice PROPERTIES OUTPUT_NAME "spice") IF(NOT MSVC) - set_target_properties(spicetools_spice PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") + set_target_properties(spicetools_spice PROPERTIES LINK_FLAGS "-m32") endif() # spice_laa.exe ########### set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc) -add_executable(spicetools_spice_laa ${SOURCE_FILES} ${RESOURCE_FILES}) -target_link_libraries(spicetools_spice_laa - PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp - PRIVATE fmt::fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features) -target_link_libraries(spicetools_spice_laa PUBLIC winscard) +add_executable(spicetools_spice_laa ${RESOURCE_FILES}) +target_link_libraries(spicetools_spice_laa PRIVATE spicetools_spice_objs) set_target_properties(spicetools_spice_laa PROPERTIES PREFIX "") set_target_properties(spicetools_spice_laa PROPERTIES OUTPUT_NAME "spice_laa") -target_compile_definitions(spicetools_spice_laa PRIVATE SPICE32_LARGE_ADDRESS_AWARE=1) IF(NOT MSVC) - set_target_properties(spicetools_spice_laa PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32 -Wl,--large-address-aware") + set_target_properties(spicetools_spice_laa PROPERTIES LINK_FLAGS "-m32 -Wl,--large-address-aware") endif() # spice_linux.exe diff --git a/src/spice2x/build_all.sh b/src/spice2x/build_all.sh index 3030ebf..26f2b64 100755 --- a/src/spice2x/build_all.sh +++ b/src/spice2x/build_all.sh @@ -91,14 +91,20 @@ fi # is the XP-compatible toolchain installed? XP_MUST_BUILD=0 -BUILD_XP=0 -if [ -f "$TOOLCHAIN_WINXP_32" ] && [ -f "$TOOLCHAIN_WINXP_64" ]; then - BUILD_XP=1; +# 64-bit WinXP builds are disabled by default; set to 1 to opt in +BUILD_XP_64_ENABLE=0 +BUILD_XP_32=0 +BUILD_XP_64=0 +if [ -f "$TOOLCHAIN_WINXP_32" ]; then + BUILD_XP_32=1; elif ((XP_MUST_BUILD > 0)) then - echo "WinXP toolchain not available, aborting" + echo "WinXP 32bit toolchain not available, aborting" exit 1 fi +if ((BUILD_XP_64_ENABLE > 0)) && [ -f "$TOOLCHAIN_WINXP_64" ]; then + BUILD_XP_64=1; +fi # determine number of cores CORES=$(nproc) @@ -113,12 +119,17 @@ echo "Git Branch: $GIT_BRANCH" echo "Git Head: $GIT_HEAD" echo "Toolchain for 32bit targets: $TOOLCHAIN_32" echo "Toolchain for 64bit targets: $TOOLCHAIN_64" -if ((BUILD_XP > 0)) +if ((BUILD_XP_32 > 0)) then echo "Toolchain for WinXP 32bit targets: $TOOLCHAIN_WINXP_32" +else + echo "WinXP 32bit toolchain not available, skipping WinXP 32bit builds" +fi +if ((BUILD_XP_64 > 0)) +then echo "Toolchain for WinXP 64bit targets: $TOOLCHAIN_WINXP_64" else - echo "WinXP toolchain not available, skipping WinXP builds" + echo "WinXP 64bit builds disabled, skipping WinXP 64bit builds" fi echo "Distribution Name: $DIST_NAME" echo "Build Type: $BUILD_TYPE" @@ -160,7 +171,7 @@ time ( cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} "$OLDPWD" && ninja ${TARGETS_64} popd > /dev/null - if ((BUILD_XP > 0)) + if ((BUILD_XP_32 > 0)) then # 32 bit Windows XP echo "" @@ -174,7 +185,13 @@ time ( pushd ${BUILDDIR_WINXP_32} > /dev/null cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_WINXP_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DSPICE_XP=ON "$OLDPWD" && ninja ${TARGETS_XP32} popd > /dev/null + else + echo "" + echo "Skipping WinXP 32bit builds, toolchain not specified" + fi + if ((BUILD_XP_64 > 0)) + then # 64 bit Windows XP echo "" echo "Building 64bit targets (WinXP toolchain)..." @@ -189,7 +206,7 @@ time ( popd > /dev/null else echo "" - echo "Skipping WinXP builds, toolchain not specified" + echo "Skipping WinXP 64bit builds" fi echo "" @@ -197,7 +214,7 @@ time ( echo "===========================" ) -if ((BUILD_XP > 0)) +if ((BUILD_XP_32 > 0)) || ((BUILD_XP_64 > 0)) then echo "" echo "Checking XP compatibility..." @@ -205,11 +222,17 @@ then if ! command -v windows_dll_compat_checker &> /dev/null; then echo "WARNING: windows_dll_compat_checker not found, skipping XP compatibility check" else - windows_dll_compat_checker -s PREMADE/winxp_x86_64.ini \ - ${BUILDDIR_WINXP_64}/spicetools/64/spice64.exe - windows_dll_compat_checker -s PREMADE/winxp_x86_64_32bit_dlls.ini \ - ${BUILDDIR_WINXP_32}/spicetools/spicecfg.exe \ - ${BUILDDIR_WINXP_32}/spicetools/32/spice.exe + if ((BUILD_XP_64 > 0)) + then + windows_dll_compat_checker -s PREMADE/winxp_x86_64.ini \ + ${BUILDDIR_WINXP_64}/spicetools/64/spice64.exe + fi + if ((BUILD_XP_32 > 0)) + then + windows_dll_compat_checker -s PREMADE/winxp_x86_64_32bit_dlls.ini \ + ${BUILDDIR_WINXP_32}/spicetools/spicecfg.exe \ + ${BUILDDIR_WINXP_32}/spicetools/32/spice.exe + fi fi fi @@ -271,7 +294,7 @@ mkdir -p ${OUTDIR_EXTRAS}/largeaddressaware mkdir -p ${OUTDIR_EXTRAS}/linux mkdir -p ${OUTDIR_EXTRAS}/sdk/samples/32 mkdir -p ${OUTDIR_EXTRAS}/sdk/samples/64 -if ((BUILD_XP > 0)) +if ((BUILD_XP_32 > 0)) || ((BUILD_XP_64 > 0)) then mkdir -p ${OUTDIR_EXTRAS}/winxp fi @@ -301,10 +324,13 @@ else cp ${BUILDDIR_32}/spicetools/32/sdk_sample_v0_flat_c.dll ${OUTDIR_EXTRAS}/sdk/samples/32/v0_flat_c.dll 2>/dev/null cp ${BUILDDIR_64}/spicetools/64/sdk_sample_v0_flat_c.dll ${OUTDIR_EXTRAS}/sdk/samples/64/v0_flat_c.dll 2>/dev/null cp ${BUILDDIR_64}/spicetools/64/sdk_sample_v0_cpp.dll ${OUTDIR_EXTRAS}/sdk/samples/64/v0_cpp.dll 2>/dev/null - if ((BUILD_XP > 0)) + if ((BUILD_XP_32 > 0)) then cp ${BUILDDIR_WINXP_32}/spicetools/spicecfg.exe ${OUTDIR_EXTRAS}/winxp 2>/dev/null cp ${BUILDDIR_WINXP_32}/spicetools/32/spice.exe ${OUTDIR_EXTRAS}/winxp 2>/dev/null + fi + if ((BUILD_XP_64 > 0)) + then cp ${BUILDDIR_WINXP_64}/spicetools/64/spice64.exe ${OUTDIR_EXTRAS}/winxp 2>/dev/null fi fi diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp index e5f9bce..b6f3f50 100644 --- a/src/spice2x/launcher/launcher.cpp +++ b/src/spice2x/launcher/launcher.cpp @@ -1485,10 +1485,15 @@ int main_implementation(int argc, char *argv[]) { #else #ifdef SPICE64 log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x)"); - #elif SPICE32_LARGE_ADDRESS_AWARE - log_info("launcher", "SpiceTools Bootstrap (x32 - Large Address Aware) (spice2x)"); #else - log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x)"); + // spice.exe and spice_laa.exe share the same compiled objects; the only + // difference is the large-address-aware bit set at link time. detect it + // at runtime so the log line stays accurate without a separate compile. + if (sysutils::is_large_address_aware()) { + log_info("launcher", "SpiceTools Bootstrap (x32 - Large Address Aware) (spice2x)"); + } else { + log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x)"); + } #endif #endif diff --git a/src/spice2x/util/sysutils.cpp b/src/spice2x/util/sysutils.cpp index e053d45..4aa4e4f 100644 --- a/src/spice2x/util/sysutils.cpp +++ b/src/spice2x/util/sysutils.cpp @@ -589,6 +589,24 @@ namespace sysutils { (void**)&EnumDisplayDevicesA_orig); } + bool is_large_address_aware() { + auto image_base = reinterpret_cast(GetModuleHandleW(nullptr)); + if (image_base == nullptr) { + return false; + } + auto dos_header = reinterpret_cast(image_base); + if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) { + return false; + } + auto nt_headers = reinterpret_cast( + image_base + dos_header->e_lfanew); + if (nt_headers->Signature != IMAGE_NT_SIGNATURE) { + return false; + } + return (nt_headers->FileHeader.Characteristics + & IMAGE_FILE_LARGE_ADDRESS_AWARE) != 0; + } + #if !SPICE_XP bool is_running_as_admin() { diff --git a/src/spice2x/util/sysutils.h b/src/spice2x/util/sysutils.h index 44d32fa..a291092 100644 --- a/src/spice2x/util/sysutils.h +++ b/src/spice2x/util/sysutils.h @@ -20,6 +20,10 @@ namespace sysutils { const std::vector &enumerate_monitors(); + // reads the running module's PE header to determine whether the + // IMAGE_FILE_LARGE_ADDRESS_AWARE bit is set (i.e. spice_laa.exe) + bool is_large_address_aware(); + extern std::string SECOND_MONITOR_OVERRIDE; void hook_EnumDisplayDevicesA();