feat: add Windows platform support (x64 and ARM64) to build workflow #39
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 - 'a call of a 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://raw.githubusercontent.com/nodejs/node/main/.configurations/configuration.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 * -requires Microsoft.Component.MSBuild -property installationPath | |
| Write-Host "Visual Studio installation found at: $vsPath" | |
| # Get VS product ID (Enterprise, Professional, or Community) | |
| $productId = & $vsWhere -latest -products * -property productId | |
| Write-Host "Product ID: $productId" | |
| # Use VS Installer to modify existing installation and add required components | |
| $installerPath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vs_installer.exe" | |
| Write-Host "Adding C++ workload and Clang components..." | |
| $processArgs = @( | |
| "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" | |
| ) | |
| $process = Start-Process -FilePath $installerPath -ArgumentList $processArgs -Wait -PassThru -NoNewWindow | |
| if ($process.ExitCode -eq 0) { | |
| Write-Host "Visual Studio components installed successfully!" | |
| } elseif ($process.ExitCode -eq 3010) { | |
| Write-Host "Visual Studio components installed successfully! (Reboot required but skipped)" | |
| } else { | |
| Write-Host "Warning: VS Installer returned exit code $($process.ExitCode)" | |
| Write-Host "This may indicate the components are already installed or partially installed." | |
| Write-Host "Continuing with build process..." | |
| } | |
| - 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 "`nVisual Studio:" | |
| vswhere -latest -property displayName | |
| vswhere -latest -property installationVersion | |
| Write-Host "`n==========================" | |
| Write-Host "All prerequisites verified!" | |
| - 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 |