From 0d614bcce95adbc338ddb655f0fb138f79bcbe3f Mon Sep 17 00:00:00 2001 From: wiiiii123 Date: Sat, 11 Jul 2026 14:47:25 +0700 Subject: [PATCH] fix(ci): support Visual Studio 2026 native builds --- .github/workflows/build.yml | 3 + electron/windowsCmakeGenerators.test.mjs | 64 ++++++++++++++++ scripts/build-cursor-monitor.mjs | 36 ++++----- scripts/build-nvidia-cuda-compositor.mjs | 47 +++++------- scripts/build-whisper-runtime.mjs | 98 +++++++++++------------- scripts/build-windows-capture.mjs | 36 ++++----- scripts/build-windows-gpu-export.mjs | 36 ++++----- scripts/windows-cmake-generators.mjs | 36 +++++++++ 8 files changed, 221 insertions(+), 135 deletions(-) create mode 100644 electron/windowsCmakeGenerators.test.mjs create mode 100644 scripts/windows-cmake-generators.mjs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4225b839..78f1ddae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,8 +39,11 @@ jobs: shell: pwsh run: | npm run build:platform-native-helpers + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } npx tsc + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } npx vite build + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } npx electron-builder --win dir nsis --x64 --publish never - name: Smoke test packaged Windows paths diff --git a/electron/windowsCmakeGenerators.test.mjs b/electron/windowsCmakeGenerators.test.mjs new file mode 100644 index 00000000..cfded966 --- /dev/null +++ b/electron/windowsCmakeGenerators.test.mjs @@ -0,0 +1,64 @@ +import { describe, expect, it, vi } from "vitest"; + +import { + configureWithWindowsCmakeGenerator, + WINDOWS_CMAKE_GENERATORS, + WINDOWS_VISUAL_STUDIO_INSTALL_DIRS, +} from "../scripts/windows-cmake-generators.mjs"; + +describe("configureWithWindowsCmakeGenerator", () => { + it("uses Visual Studio 2026 first", () => { + const configure = vi.fn(); + const clearCache = vi.fn(); + + const selected = configureWithWindowsCmakeGenerator({ + prefix: "test", + configure, + clearCache, + }); + + expect(selected).toBe("Visual Studio 18 2026"); + expect(WINDOWS_VISUAL_STUDIO_INSTALL_DIRS[0]).toBe("18"); + expect(configure).toHaveBeenCalledTimes(1); + expect(clearCache).toHaveBeenCalledTimes(1); + }); + + it("falls back in supported-version order and clears stale CMake state", () => { + const attempted = []; + const clearCache = vi.fn(); + const log = vi.fn(); + + const selected = configureWithWindowsCmakeGenerator({ + prefix: "test", + clearCache, + log, + configure: (generator) => { + attempted.push(generator); + if (generator !== "Visual Studio 16 2019") { + throw new Error(`${generator} unavailable`); + } + }, + }); + + expect(attempted).toEqual(WINDOWS_CMAKE_GENERATORS.map(({ name }) => name)); + expect(clearCache).toHaveBeenCalledTimes(3); + expect(log).toHaveBeenCalledTimes(2); + expect(selected).toBe("Visual Studio 16 2019"); + }); + + it("rethrows the final configure error when no supported generator exists", () => { + const finalError = new Error("VS 2019 unavailable"); + + expect(() => + configureWithWindowsCmakeGenerator({ + prefix: "test", + clearCache: vi.fn(), + log: vi.fn(), + configure: (generator) => { + if (generator === "Visual Studio 16 2019") throw finalError; + throw new Error(`${generator} unavailable`); + }, + }), + ).toThrow(finalError); + }); +}); diff --git a/scripts/build-cursor-monitor.mjs b/scripts/build-cursor-monitor.mjs index 36917ddf..f051adab 100644 --- a/scripts/build-cursor-monitor.mjs +++ b/scripts/build-cursor-monitor.mjs @@ -7,6 +7,10 @@ import { updateNativeHelperManifest, verifyNativeHelperManifest, } from "./native-helper-manifest.mjs"; +import { + configureWithWindowsCmakeGenerator, + WINDOWS_VISUAL_STUDIO_INSTALL_DIRS, +} from "./windows-cmake-generators.mjs"; const projectRoot = process.cwd(); const sourceDir = path.join(projectRoot, "electron", "native", "cursor-monitor"); @@ -56,7 +60,7 @@ function findCmake() { path.join("C:", "Program Files (x86)", "Microsoft Visual Studio"), ]; const vsEditions = ["Community", "Professional", "Enterprise", "BuildTools"]; - const vsVersions = ["2022", "2019"]; + const vsVersions = WINDOWS_VISUAL_STUDIO_INSTALL_DIRS; for (const root of vsRoots) { for (const version of vsVersions) { for (const edition of vsEditions) { @@ -117,25 +121,19 @@ function clearCmakeCache() { console.log("[build-cursor-monitor] Configuring CMake..."); try { - clearCmakeCache(); - execSync(`${cmake} .. -G "Visual Studio 17 2022" -A x64`, { - cwd: buildDir, - stdio: "inherit", - timeout: 120000, + configureWithWindowsCmakeGenerator({ + prefix: "build-cursor-monitor", + clearCache: clearCmakeCache, + configure: (generator) => + execSync(`${cmake} .. -G "${generator}" -A x64`, { + cwd: buildDir, + stdio: "inherit", + timeout: 120000, + }), }); -} catch { - console.log("[build-cursor-monitor] VS 2022 generator not found, trying VS 2019..."); - try { - clearCmakeCache(); - execSync(`${cmake} .. -G "Visual Studio 16 2019" -A x64`, { - cwd: buildDir, - stdio: "inherit", - timeout: 120000, - }); - } catch (innerError) { - console.error("[build-cursor-monitor] CMake configure failed:", innerError.message); - process.exit(1); - } +} catch (error) { + console.error("[build-cursor-monitor] CMake configure failed:", error.message); + process.exit(1); } console.log("[build-cursor-monitor] Building..."); diff --git a/scripts/build-nvidia-cuda-compositor.mjs b/scripts/build-nvidia-cuda-compositor.mjs index e2959022..080a7526 100644 --- a/scripts/build-nvidia-cuda-compositor.mjs +++ b/scripts/build-nvidia-cuda-compositor.mjs @@ -7,6 +7,10 @@ import { updateNativeHelperManifest, verifyNativeHelperManifest, } from "./native-helper-manifest.mjs"; +import { + configureWithWindowsCmakeGenerator, + WINDOWS_VISUAL_STUDIO_INSTALL_DIRS, +} from "./windows-cmake-generators.mjs"; const projectRoot = process.cwd(); const sourceDir = path.join(projectRoot, "electron", "native", "nvidia-cuda-compositor"); @@ -84,7 +88,7 @@ function findCmake() { path.join("C:", "Program Files (x86)", "Microsoft Visual Studio"), ]; const vsEditions = ["Preview", "Community", "Professional", "Enterprise", "BuildTools"]; - const vsVersions = ["2022", "2019"]; + const vsVersions = WINDOWS_VISUAL_STUDIO_INSTALL_DIRS; for (const root of vsRoots) { for (const version of vsVersions) { for (const edition of vsEditions) { @@ -237,32 +241,23 @@ function clearCmakeCache() { console.log("[build-nvidia-cuda-compositor] Configuring CMake..."); try { - clearCmakeCache(); - execSync( - `${cmake} .. -G "Visual Studio 17 2022" -A ${generatorArch} -DRECORDLY_NVIDIA_VIDEO_CODEC_SDK_ROOT="${videoCodecSdkRoot}"`, - { - cwd: buildDir, - stdio: "inherit", - timeout: 120000, - }, + configureWithWindowsCmakeGenerator({ + prefix: "build-nvidia-cuda-compositor", + clearCache: clearCmakeCache, + configure: (generator) => + execSync( + `${cmake} .. -G "${generator}" -A ${generatorArch} -DRECORDLY_NVIDIA_VIDEO_CODEC_SDK_ROOT="${videoCodecSdkRoot}"`, + { + cwd: buildDir, + stdio: "inherit", + timeout: 120000, + }, + ), + }); +} catch (error) { + fallbackToBundledHelperOrExit( + `CMake configure failed: ${error instanceof Error ? error.message : String(error)}`, ); -} catch { - console.log("[build-nvidia-cuda-compositor] VS 2022 generator not found, trying VS 2019..."); - try { - clearCmakeCache(); - execSync( - `${cmake} .. -G "Visual Studio 16 2019" -A ${generatorArch} -DRECORDLY_NVIDIA_VIDEO_CODEC_SDK_ROOT="${videoCodecSdkRoot}"`, - { - cwd: buildDir, - stdio: "inherit", - timeout: 120000, - }, - ); - } catch (error) { - fallbackToBundledHelperOrExit( - `CMake configure failed: ${error instanceof Error ? error.message : String(error)}`, - ); - } } console.log("[build-nvidia-cuda-compositor] Building NVIDIA CUDA compositor..."); diff --git a/scripts/build-whisper-runtime.mjs b/scripts/build-whisper-runtime.mjs index 193e5bc2..7bd08e39 100644 --- a/scripts/build-whisper-runtime.mjs +++ b/scripts/build-whisper-runtime.mjs @@ -1,9 +1,14 @@ import { execFileSync, execSync } from "node:child_process"; -import { createWriteStream, existsSync } from "node:fs"; +import { createWriteStream, existsSync, rmSync } from "node:fs"; import { chmod, cp, mkdir, readdir, readFile, rm, stat, writeFile } from "node:fs/promises"; import { get as httpsGet } from "node:https"; import path from "node:path"; +import { + configureWithWindowsCmakeGenerator, + WINDOWS_VISUAL_STUDIO_INSTALL_DIRS, +} from "./windows-cmake-generators.mjs"; + const projectRoot = process.cwd(); const whisperVersion = "v1.8.4"; const nativeRoot = path.join(projectRoot, "electron", "native"); @@ -101,12 +106,7 @@ function getTargetConfigs() { archTag, buildRoot: path.join(cacheRoot, `build-${archTag}`), outputDir: path.join(nativeRoot, "bin", archTag), - configureArgs: [ - "-G", - "Visual Studio 17 2022", - "-A", - arch === "arm64" ? "ARM64" : "x64", - ], + configureArgs: ["-A", arch === "arm64" ? "ARM64" : "x64"], }, ]; } @@ -143,24 +143,26 @@ function findCmake() { if (process.platform === "win32") { const vsEditions = ["Community", "Professional", "Enterprise", "BuildTools"]; - for (const edition of vsEditions) { - const cmakePath = path.join( - "C:", - "Program Files", - "Microsoft Visual Studio", - "2022", - edition, - "Common7", - "IDE", - "CommonExtensions", - "Microsoft", - "CMake", - "CMake", - "bin", - "cmake.exe", - ); - if (existsSync(cmakePath)) { - return cmakePath; + for (const version of WINDOWS_VISUAL_STUDIO_INSTALL_DIRS) { + for (const edition of vsEditions) { + const cmakePath = path.join( + "C:", + "Program Files", + "Microsoft Visual Studio", + version, + edition, + "Common7", + "IDE", + "CommonExtensions", + "Microsoft", + "CMake", + "CMake", + "bin", + "cmake.exe", + ); + if (existsSync(cmakePath)) { + return cmakePath; + } } } } @@ -272,7 +274,7 @@ async function shouldSkipBuild(target) { } } -function getConfigureArgs(sourceDir, target) { +function getConfigureArgs(sourceDir, target, generator) { const args = [ "-S", sourceDir, @@ -281,6 +283,7 @@ function getConfigureArgs(sourceDir, target) { "-DWHISPER_BUILD_TESTS=OFF", "-DWHISPER_BUILD_SERVER=OFF", "-DBUILD_SHARED_LIBS=OFF", + ...(generator ? ["-G", generator] : []), ...target.configureArgs, ]; @@ -424,36 +427,27 @@ async function main() { console.log( `[build-whisper-runtime] Configuring whisper.cpp ${whisperVersion} for ${target.archTag}...`, ); - try { + if (target.platform === "win32") { + configureWithWindowsCmakeGenerator({ + prefix: "build-whisper-runtime", + clearCache: () => { + rmSync(path.join(target.buildRoot, "CMakeCache.txt"), { force: true }); + rmSync(path.join(target.buildRoot, "CMakeFiles"), { + recursive: true, + force: true, + }); + }, + configure: (generator) => + execFileSync(cmake, getConfigureArgs(sourceDir, target, generator), { + stdio: "inherit", + timeout: 300000, + }), + }); + } else { execFileSync(cmake, getConfigureArgs(sourceDir, target), { stdio: "inherit", timeout: 300000, }); - } catch (error) { - if (target.platform === "win32" && target.arch !== "arm64") { - console.log( - "[build-whisper-runtime] VS 2022 generator unavailable, retrying with VS 2019...", - ); - execFileSync( - cmake, - [ - "-S", - sourceDir, - "-B", - target.buildRoot, - "-G", - "Visual Studio 16 2019", - "-A", - "x64", - "-DWHISPER_BUILD_TESTS=OFF", - "-DWHISPER_BUILD_SERVER=OFF", - "-DBUILD_SHARED_LIBS=OFF", - ], - { stdio: "inherit", timeout: 300000 }, - ); - } else { - throw error; - } } console.log( diff --git a/scripts/build-windows-capture.mjs b/scripts/build-windows-capture.mjs index 76f7e477..06aacdf8 100644 --- a/scripts/build-windows-capture.mjs +++ b/scripts/build-windows-capture.mjs @@ -7,6 +7,10 @@ import { updateNativeHelperManifest, verifyNativeHelperManifest, } from "./native-helper-manifest.mjs"; +import { + configureWithWindowsCmakeGenerator, + WINDOWS_VISUAL_STUDIO_INSTALL_DIRS, +} from "./windows-cmake-generators.mjs"; const projectRoot = process.cwd(); const sourceDir = path.join(projectRoot, "electron", "native", "wgc-capture"); @@ -59,7 +63,7 @@ function findCmake() { path.join("C:", "Program Files (x86)", "Microsoft Visual Studio"), ]; const vsEditions = ["Community", "Professional", "Enterprise", "BuildTools"]; - const vsVersions = ["2022", "2019"]; + const vsVersions = WINDOWS_VISUAL_STUDIO_INSTALL_DIRS; for (const root of vsRoots) { for (const version of vsVersions) { for (const edition of vsEditions) { @@ -120,25 +124,19 @@ function clearCmakeCache() { console.log("[build-windows-capture] Configuring CMake..."); try { - clearCmakeCache(); - execSync(`${cmake} .. -G "Visual Studio 17 2022" -A ${generatorArch}`, { - cwd: buildDir, - stdio: "inherit", - timeout: 120000, + configureWithWindowsCmakeGenerator({ + prefix: "build-windows-capture", + clearCache: clearCmakeCache, + configure: (generator) => + execSync(`${cmake} .. -G "${generator}" -A ${generatorArch}`, { + cwd: buildDir, + stdio: "inherit", + timeout: 120000, + }), }); -} catch { - console.log("[build-windows-capture] VS 2022 generator not found, trying VS 2019..."); - try { - clearCmakeCache(); - execSync(`${cmake} .. -G "Visual Studio 16 2019" -A ${generatorArch}`, { - cwd: buildDir, - stdio: "inherit", - timeout: 120000, - }); - } catch (innerError) { - console.error("[build-windows-capture] CMake configure failed:", innerError.message); - process.exit(1); - } +} catch (error) { + console.error("[build-windows-capture] CMake configure failed:", error.message); + process.exit(1); } console.log("[build-windows-capture] Building native Windows capture helper..."); diff --git a/scripts/build-windows-gpu-export.mjs b/scripts/build-windows-gpu-export.mjs index 2bc64830..4530e892 100644 --- a/scripts/build-windows-gpu-export.mjs +++ b/scripts/build-windows-gpu-export.mjs @@ -7,6 +7,10 @@ import { updateNativeHelperManifest, verifyNativeHelperManifest, } from "./native-helper-manifest.mjs"; +import { + configureWithWindowsCmakeGenerator, + WINDOWS_VISUAL_STUDIO_INSTALL_DIRS, +} from "./windows-cmake-generators.mjs"; const projectRoot = process.cwd(); const sourceDir = path.join(projectRoot, "electron", "native", "gpu-export-probe"); @@ -55,7 +59,7 @@ function findCmake() { path.join("C:", "Program Files (x86)", "Microsoft Visual Studio"), ]; const vsEditions = ["Community", "Professional", "Enterprise", "BuildTools"]; - const vsVersions = ["2022", "2019"]; + const vsVersions = WINDOWS_VISUAL_STUDIO_INSTALL_DIRS; for (const root of vsRoots) { for (const version of vsVersions) { for (const edition of vsEditions) { @@ -115,25 +119,19 @@ function clearCmakeCache() { console.log("[build-windows-gpu-export] Configuring CMake..."); try { - clearCmakeCache(); - execSync(`${cmake} .. -G "Visual Studio 17 2022" -A ${generatorArch}`, { - cwd: buildDir, - stdio: "inherit", - timeout: 120000, + configureWithWindowsCmakeGenerator({ + prefix: "build-windows-gpu-export", + clearCache: clearCmakeCache, + configure: (generator) => + execSync(`${cmake} .. -G "${generator}" -A ${generatorArch}`, { + cwd: buildDir, + stdio: "inherit", + timeout: 120000, + }), }); -} catch { - console.log("[build-windows-gpu-export] VS 2022 generator not found, trying VS 2019..."); - try { - clearCmakeCache(); - execSync(`${cmake} .. -G "Visual Studio 16 2019" -A ${generatorArch}`, { - cwd: buildDir, - stdio: "inherit", - timeout: 120000, - }); - } catch (error) { - console.error("[build-windows-gpu-export] CMake configure failed:", error.message); - process.exit(1); - } +} catch (error) { + console.error("[build-windows-gpu-export] CMake configure failed:", error.message); + process.exit(1); } console.log("[build-windows-gpu-export] Building Windows GPU export helper..."); diff --git a/scripts/windows-cmake-generators.mjs b/scripts/windows-cmake-generators.mjs new file mode 100644 index 00000000..d832f570 --- /dev/null +++ b/scripts/windows-cmake-generators.mjs @@ -0,0 +1,36 @@ +export const WINDOWS_CMAKE_GENERATORS = Object.freeze([ + { name: "Visual Studio 18 2026", label: "VS 2026" }, + { name: "Visual Studio 17 2022", label: "VS 2022" }, + { name: "Visual Studio 16 2019", label: "VS 2019" }, +]); + +export const WINDOWS_VISUAL_STUDIO_INSTALL_DIRS = Object.freeze(["18", "2022", "2019"]); + +export function configureWithWindowsCmakeGenerator({ + prefix, + configure, + clearCache, + log = console.log, +}) { + let lastError; + + for (let index = 0; index < WINDOWS_CMAKE_GENERATORS.length; index += 1) { + const generator = WINDOWS_CMAKE_GENERATORS[index]; + clearCache(); + + try { + configure(generator.name); + return generator.name; + } catch (error) { + lastError = error; + const nextGenerator = WINDOWS_CMAKE_GENERATORS[index + 1]; + if (nextGenerator) { + log( + `[${prefix}] ${generator.label} generator unavailable, trying ${nextGenerator.label}...`, + ); + } + } + } + + throw lastError; +}