Merge pull request #753 from webadderallorg/ci/windows-vs2026-native-build

fix(ci): support Visual Studio 2026 native builds
This commit is contained in:
PTMH-NMH
2026-07-11 15:17:09 +07:00
committed by GitHub
10 changed files with 375 additions and 943 deletions
+3
View File
@@ -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
+66
View File
@@ -0,0 +1,66 @@
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).toHaveBeenCalledWith("Visual Studio 18 2026", "v143");
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, toolset) => {
attempted.push({ name: generator, toolset });
if (generator !== "Visual Studio 16 2019") {
throw new Error(`${generator} unavailable`);
}
},
});
expect(attempted).toEqual(
WINDOWS_CMAKE_GENERATORS.map(({ name, toolset }) => ({ name, toolset })),
);
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);
});
});
+150 -807
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -54,7 +54,7 @@
},
"devDependencies": {
"@biomejs/biome": "2.3.13",
"@electron/rebuild": "^4.0.3",
"@electron/rebuild": "^4.2.0",
"@fix-webm-duration/fix": "^1.0.1",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-dialog": "^1.1.15",
+17 -19
View File
@@ -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, toolset) =>
execSync(`${cmake} .. -G "${generator}" -A x64${toolset ? ` -T ${toolset}` : ""}`, {
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...");
+21 -26
View File
@@ -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, toolset) =>
execSync(
`${cmake} .. -G "${generator}" -A ${generatorArch}${toolset ? ` -T ${toolset}` : ""} -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...");
+47 -52
View File
@@ -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, toolset) {
const args = [
"-S",
sourceDir,
@@ -281,6 +283,8 @@ function getConfigureArgs(sourceDir, target) {
"-DWHISPER_BUILD_TESTS=OFF",
"-DWHISPER_BUILD_SERVER=OFF",
"-DBUILD_SHARED_LIBS=OFF",
...(generator ? ["-G", generator] : []),
...(toolset ? ["-T", toolset] : []),
...target.configureArgs,
];
@@ -424,36 +428,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, toolset) =>
execFileSync(cmake, getConfigureArgs(sourceDir, target, generator, toolset), {
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(
+17 -19
View File
@@ -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, toolset) =>
execSync(`${cmake} .. -G "${generator}" -A ${generatorArch}${toolset ? ` -T ${toolset}` : ""}`, {
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...");
+17 -19
View File
@@ -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, toolset) =>
execSync(`${cmake} .. -G "${generator}" -A ${generatorArch}${toolset ? ` -T ${toolset}` : ""}`, {
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...");
+36
View File
@@ -0,0 +1,36 @@
export const WINDOWS_CMAKE_GENERATORS = Object.freeze([
{ name: "Visual Studio 18 2026", label: "VS 2026", toolset: "v143" },
{ 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, generator.toolset);
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;
}