Skip to content

feat: add Windows platform support (x64 and ARM64) to build workflow #40

feat: add Windows platform support (x64 and ARM64) to build workflow

feat: add Windows platform support (x64 and ARM64) to build workflow #40

name: Build Node.js Shared Library
on:
release:
types: [created]
pull_request:
types: [opened, synchronize, reopened]
paths:
- '.github/workflows/build_node_shared.yml'
jobs:
build:
timeout-minutes: 720 # 12 hours (QEMU ARM64 builds are slow)
strategy:
matrix:
include:
# Linux x64 builds
- os: ubuntu-latest
platform: linux
arch: x64
compiler: gcc
container: ghcr.io/ten-framework/ten_building_ubuntu2204
lib_name: libnode.so.127
nproc_cmd: nproc
- os: ubuntu-latest
platform: linux
arch: x64
compiler: clang
container: ghcr.io/ten-framework/ten_building_ubuntu2204
lib_name: libnode.so.127
nproc_cmd: nproc
# Linux ARM64 builds (using cross-compilation)
- os: ubuntu-latest
platform: linux
arch: arm64
compiler: gcc
container: ghcr.io/ten-framework/ten_building_ubuntu2204
lib_name: libnode.so.127
nproc_cmd: nproc
- os: ubuntu-latest
platform: linux
arch: arm64
compiler: clang
container: ghcr.io/ten-framework/ten_building_ubuntu2204
lib_name: libnode.so.127
nproc_cmd: nproc
# macOS x64 builds
# Note: macOS should use Clang (Apple's official toolchain), GCC has compatibility issues
- os: macos-15-intel
platform: mac
arch: x64
compiler: clang
container: ""
lib_name: libnode.127.dylib
nproc_cmd: sysctl -n hw.ncpu
# macOS ARM64 builds
# Note: Apple Silicon only supports Clang
- os: macos-latest
platform: mac
arch: arm64
compiler: clang
container: ""
lib_name: libnode.127.dylib
nproc_cmd: sysctl -n hw.ncpu
# Windows x64 builds
- os: windows-latest
platform: win
arch: x64
compiler: msvc
container: ""
lib_name: libnode.dll
nproc_cmd: $env:NUMBER_OF_PROCESSORS
# Windows ARM64 builds (cross-compilation)
- os: windows-latest
platform: win
arch: arm64
compiler: msvc
container: ""
lib_name: libnode.dll
nproc_cmd: $env:NUMBER_OF_PROCESSORS
runs-on: ${{ matrix.os }}
container: ${{ matrix.container != '' && matrix.container || null }}
steps:
- name: Checkout Node.js
uses: actions/checkout@v4
with:
repository: nodejs/node
ref: v22.12.0
# Ref: https://github.com/nodejs/help/issues/5070
# https://forum.qt.io/topic/162305/possible-marking-state.h-chromium-bug-when-compiling-qtwebengine-from-source/2
- name: Patch V8 for MSVC compatibility (Windows)
if: matrix.platform == 'win'
shell: powershell
run: |
Write-Host "========== Applying V8 MSVC Compatibility Patch =========="
Write-Host "Issue: MSVC C2352 error - non-static member function requires an object"
Write-Host "Affected file: deps\v8\src\heap\cppgc\marking-state.h"
Write-Host "Reference: https://forum.qt.io/topic/162305"
Write-Host ""
$file = "deps\v8\src\heap\cppgc\marking-state.h"
if (!(Test-Path $file)) {
Write-Host "ERROR: $file not found!"
exit 1
}
Write-Host "Reading file..."
$lines = Get-Content $file
$modified = $false
$lineNumber = 0
$newLines = foreach ($line in $lines) {
$lineNumber++
$originalLine = $line
# Fix: MutatorMarkingState::BasicMarkingState::MarkNoPush(header)
# -> BasicMarkingState::MarkNoPush(header)
if ($line -match 'MutatorMarkingState::BasicMarkingState::MarkNoPush\(') {
$line = $line -replace 'MutatorMarkingState::BasicMarkingState::MarkNoPush\(', 'BasicMarkingState::MarkNoPush('
Write-Host " Line $lineNumber : Fixed qualified scope call"
$modified = $true
}
# Fix: return MarkingStateBase::MarkNoPush(
# -> return this->MarkNoPush(
if ($line -match '\s+return\s+MarkingStateBase::MarkNoPush\(') {
$line = $line -replace 'return\s+MarkingStateBase::MarkNoPush\(', 'return this->MarkNoPush('
Write-Host " Line $lineNumber : Fixed base class call"
$modified = $true
}
$line
}
if ($modified) {
Set-Content $file -Value $newLines
Write-Host ""
Write-Host "Successfully patched $file"
} else {
Write-Host ""
Write-Host "No matching patterns found (file may already be patched or have different code)"
Write-Host "Build will continue, but may still fail with C2352 error"
}
Write-Host "========== Patch Complete =========="
- name: Setup cross-compilation toolchain (Linux ARM64)
if: matrix.platform == 'linux' && matrix.arch == 'arm64'
run: |
apt-get update
apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
if [ "${{ matrix.compiler }}" = "clang" ]; then
apt-get install -y clang llvm
fi
- name: Setup Python (macOS)
if: matrix.platform == 'mac'
uses: actions/setup-python@v5
with:
python-version: '3.11'
# Reference: https://github.com/nodejs/node/blob/v22.12.0/BUILDING.md#option-2-automated-install-with-winget
# https://github.com/nodejs/node/blob/v22.12.0/.configurations/configuration.vsEnterprise.dsc.yaml
# Using direct winget install commands instead of DSC configuration due to compatibility issues in CI environment
- name: Setup Node.js prerequisites with WinGet (Windows)
if: matrix.platform == 'win'
shell: powershell
run: |
Write-Host "Installing Node.js build prerequisites with WinGet..."
# Install Python 3.12
Write-Host "`n[1/3] Installing Python 3.12..."
winget install --id Python.Python.3.12 --source winget --silent --accept-package-agreements --accept-source-agreements
# Install Git
Write-Host "`n[2/3] Installing Git..."
winget install --id Git.Git --source winget --silent --accept-package-agreements --accept-source-agreements
# Install NASM (NetWide Assembler) for OpenSSL
Write-Host "`n[3/3] Installing NASM..."
winget install --id Nasm.Nasm --source winget --silent --accept-package-agreements --accept-source-agreements
Write-Host "`nAll WinGet packages installed!"
- name: Install Visual Studio Components (Windows)
if: matrix.platform == 'win'
shell: powershell
run: |
Write-Host "Installing required Visual Studio components..."
Write-Host "Components: C++ Desktop Development, Clang, ClangToolset"
# Find existing VS installation (GitHub Actions has VS 2022 Enterprise pre-installed)
$vsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
$vsPath = & $vsWhere -latest -products Microsoft.VisualStudio.Product.Enterprise -property installationPath
Write-Host "Visual Studio installation found at: $vsPath"
# Get VS product ID
$productId = & $vsWhere -path "$vsPath" -property productId
Write-Host "Product ID: $productId"
# Use VS Installer to add required components
# Note: Using --passive instead of --quiet to see progress, and allow warnings
$installerPath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vs_installer.exe"
Write-Host "`nAdding required components (this may take 10-20 minutes)..."
& $installerPath modify `
--installPath "$vsPath" `
--add Microsoft.VisualStudio.Workload.NativeDesktop `
--add Microsoft.VisualStudio.Component.VC.Llvm.Clang `
--add Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset `
--includeRecommended `
--quiet `
--norestart `
--wait
$exitCode = $LASTEXITCODE
if ($exitCode -eq 0) {
Write-Host "`n✓ Visual Studio components installed successfully!"
} elseif ($exitCode -eq 3010) {
Write-Host "`n✓ Visual Studio components installed successfully! (Reboot requested but skipped)"
} elseif ($exitCode -eq 1641) {
Write-Host "`n✓ Visual Studio components installed successfully! (Restart initiated)"
} else {
Write-Host "`n⚠ VS Installer returned exit code: $exitCode"
if ($exitCode -eq 87) {
Write-Host "Exit code 87 usually means components are already installed or parameters are incorrect."
Write-Host "Continuing with build process..."
} else {
Write-Host "Warning: Unexpected exit code. Continuing anyway..."
}
}
- name: Verify installations (Windows)
if: matrix.platform == 'win'
shell: powershell
run: |
Write-Host "Verifying installed tools:"
Write-Host "=========================="
# Refresh PATH
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
Write-Host "`nPython:"
python --version
Write-Host "`nGit:"
git --version
Write-Host "`nNASM:"
nasm -v
Write-Host "`n--- Visual Studio Information ---"
$vsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
Write-Host "`nVisual Studio 2022 Enterprise:"
$vsPath = & $vsWhere -latest -products Microsoft.VisualStudio.Product.Enterprise -property installationPath
if ([string]::IsNullOrEmpty($vsPath)) {
Write-Host "WARNING: VS 2022 Enterprise not found! Looking for any VS installation..."
$vsPath = & $vsWhere -latest -products * -property installationPath
}
Write-Host "Installation path: $vsPath"
$displayName = & $vsWhere -path "$vsPath" -property displayName
$version = & $vsWhere -path "$vsPath" -property installationVersion
$productId = & $vsWhere -path "$vsPath" -property productId
Write-Host "Product: $displayName"
Write-Host "Version: $version"
Write-Host "Product ID: $productId"
Write-Host "`nSearching for MSVC and Clang compilers:"
$clPath = & $vsWhere -path "$vsPath" -find "**/Hostx64/x64/cl.exe" | Select-Object -First 1
$clangPath = & $vsWhere -path "$vsPath" -find "**/bin/Hostx64/clang.exe" | Select-Object -First 1
if ($clPath) {
Write-Host " ✓ MSVC cl.exe: $clPath"
} else {
Write-Host " ✗ MSVC cl.exe: NOT FOUND"
}
if ($clangPath) {
Write-Host " ✓ Clang: $clangPath"
} else {
Write-Host " ✗ Clang: NOT FOUND (May need to install C++ workload)"
}
Write-Host "`n=========================================="
Write-Host "Prerequisites verification completed!"
- name: Setup MSVC (Windows)
if: matrix.platform == 'win'
uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{ matrix.arch }}
- name: Configure and Build (Linux ARM64 with cross-compilation)
if: matrix.platform == 'linux' && matrix.arch == 'arm64'
run: |
if [ "${{ matrix.compiler }}" = "gcc" ]; then
export CC=aarch64-linux-gnu-gcc
export CXX=aarch64-linux-gnu-g++
export CC_host=gcc
export CXX_host=g++
else
export CC="clang --target=aarch64-linux-gnu"
export CXX="clang++ --target=aarch64-linux-gnu"
export CC_host=clang
export CXX_host=clang++
fi
./configure --shared \
--dest-cpu=arm64 \
--cross-compiling \
--without-npm \
--without-corepack
make -j$(nproc)
- name: Configure and Build (Linux x64 and macOS)
if: matrix.platform != 'win' && (matrix.platform != 'linux' || matrix.arch != 'arm64')
run: |
if [ "${{ matrix.compiler }}" = "gcc" ]; then
export CC=gcc
export CXX=g++
else
export CC=clang
export CXX=clang++
fi
./configure --shared
make -j$(${{ matrix.nproc_cmd }})
- name: Configure and Build (Windows)
if: matrix.platform == 'win'
shell: cmd
run: |
if "${{ matrix.arch }}" == "arm64" (
vcbuild.bat dll arm64
) else (
vcbuild.bat dll x64
)
- name: Package assets
if: startsWith(github.ref, 'refs/tags/')
run: |
if [ "${{ matrix.platform }}" = "win" ]; then
cd Release
# Package shared libraries into zip archive
7z a node-shared-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.compiler }}.zip ${{ matrix.lib_name }} libnode.lib node.lib
else
cd out/Release
# Package shared libraries into zip archive
zip node-shared-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.compiler }}.zip ${{ matrix.lib_name }}
fi
shell: bash
- name: Publish to release assets
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
out/Release/node-shared-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.compiler }}.zip
Release/node-shared-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.compiler }}.zip