@@ -354,14 +354,103 @@ jobs:
354354 python --version
355355 vcbuild.bat dll x64
356356
357+ - name : Build node.exe that links to libnode.dll (Windows)
358+ if : matrix.platform == 'win'
359+ shell : powershell
360+ run : |
361+ Write-Host "========== Building node.exe that uses libnode.dll =========="
362+ Write-Host ""
363+ Write-Host "The vcbuild.bat dll option creates libnode.dll but the default node.exe"
364+ Write-Host "is statically linked. We need to build a thin node.exe that dynamically"
365+ Write-Host "links to libnode.dll (which contains V8 and Node.js core)."
366+ Write-Host ""
367+
368+ # Create a minimal C++ launcher that calls node::Start() from libnode.dll
369+ # This is based on src/node_main.cc but simplified for external linking
370+ $launcherCode = @'
371+ // Minimal Node.js launcher that links to libnode.dll
372+ // This executable does NOT contain V8 - it uses V8 from libnode.dll
373+
374+ #include <windows.h>
375+
376+ namespace node {
377+ int Start(int argc, char** argv);
378+ }
379+
380+ #ifdef _UNICODE
381+ int wmain(int argc, wchar_t* wargv[]) {
382+ // Convert wide args to UTF-8
383+ char** argv = new char*[argc + 1];
384+ for (int i = 0; i < argc; i++) {
385+ int size = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, nullptr, 0, nullptr, nullptr);
386+ argv[i] = new char[size];
387+ WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], size, nullptr, nullptr);
388+ }
389+ argv[argc] = nullptr;
390+
391+ int result = node::Start(argc, argv);
392+
393+ for (int i = 0; i < argc; i++) {
394+ delete[] argv[i];
395+ }
396+ delete[] argv;
397+
398+ return result;
399+ }
400+ #else
401+ int main(int argc, char** argv) {
402+ return node::Start(argc, argv);
403+ }
404+ #endif
405+ '@
406+
407+ Set-Location Release
408+
409+ # Write the launcher source file
410+ $launcherCode | Out-File -FilePath "node_launcher.cc" -Encoding UTF8
411+ Write-Host "Created node_launcher.cc"
412+
413+ # Compile the launcher and link against libnode.lib
414+ Write-Host ""
415+ Write-Host "Compiling node.exe (thin launcher linked to libnode.dll)..."
416+
417+ # Use cl.exe to compile
418+ $compileResult = & cl.exe /nologo /O2 /MD /EHsc /DUNICODE /D_UNICODE `
419+ node_launcher.cc `
420+ /link /OUT:node.exe `
421+ libnode.lib `
422+ /SUBSYSTEM:CONSOLE 2>&1
423+
424+ Write-Host $compileResult
425+
426+ if (Test-Path "node.exe") {
427+ Write-Host ""
428+ Write-Host "[OK] node.exe built successfully!"
429+ Write-Host ""
430+ Write-Host "Verifying node.exe dependencies:"
431+ & dumpbin /dependents node.exe | Select-String -Pattern "libnode|KERNEL32|VCRUNTIME|api-ms"
432+
433+ Write-Host ""
434+ Write-Host "File sizes comparison:"
435+ $nodeSize = (Get-Item "node.exe").Length / 1KB
436+ $libSize = (Get-Item "libnode.dll").Length / 1MB
437+ Write-Host " node.exe: $([math]::Round($nodeSize, 2)) KB (thin launcher, no V8)"
438+ Write-Host " libnode.dll: $([math]::Round($libSize, 2)) MB (contains V8 and Node.js core)"
439+ } else {
440+ Write-Host ""
441+ Write-Host "[ERROR] Failed to build node.exe"
442+ exit 1
443+ }
444+
357445 - name : Package assets
358446 if : startsWith(github.ref, 'refs/tags/')
359447 run : |
360448 if [ "${{ matrix.platform }}" = "win" ]; then
361449 cd Release
362450
363- # Package shared libraries into zip archive
364- 7z a node-shared-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.compiler }}.zip ${{ matrix.lib_name }} libnode.lib node.lib
451+ # Package shared libraries and node.exe into zip archive
452+ # node.exe is now dynamically linked to libnode.dll (does not contain V8)
453+ 7z a node-shared-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.compiler }}.zip ${{ matrix.lib_name }} libnode.lib node.lib node.exe
365454 else
366455 cd out/Release
367456
0 commit comments