Skip to content

Build Windows ARM64 (experimental) #7

Build Windows ARM64 (experimental)

Build Windows ARM64 (experimental) #7

name: Build Windows ARM64 (experimental)
# Experimental native Windows ARM64 build.
# - Runner: windows-11-arm (GitHub-hosted, public preview).
# - Harbour libs and Scintilla DLLs are built from source for ARM64.
# - Trigger is manual only until the pipeline stabilises; it is NOT
# wired to "release: published" to avoid breaking the release flow.
on:
workflow_dispatch:
inputs:
upload_to_release:
description: 'Upload artifact to a release tag (leave blank to skip)'
required: false
default: ''
permissions:
contents: write
env:
ASSET: HbBuilder-1.0.0-windows-arm64.zip
jobs:
build:
name: Build (arm64, experimental)
runs-on: windows-11-arm
steps:
- uses: actions/checkout@v4
- name: Set up MSVC (arm64)
uses: ilammy/msvc-dev-cmd@v1
with:
arch: arm64
- name: Show toolchain
shell: cmd
run: |
where cl.exe
where link.exe
where nmake.exe
cl.exe 2>&1 | findstr /C:"Version" /C:"ARM"
- name: Cache Harbour (ARM64)
id: cache-harbour
uses: actions/cache@v4
with:
path: C:\harbour
key: harbour-win-arm64-v2-contribs
save-always: true
- name: Locate GNU make on runner
id: findmake
shell: cmd
run: |
where mingw32-make 2>nul && (echo MAKE=mingw32-make>>%GITHUB_ENV%& goto :done)
where make 2>nul && (echo MAKE=make>>%GITHUB_ENV%& goto :done)
if exist "C:\Program Files\Git\usr\bin\make.exe" (
echo MAKE="C:\Program Files\Git\usr\bin\make.exe">>%GITHUB_ENV%
goto :done
)
if exist "C:\msys64\usr\bin\make.exe" (
echo MAKE=C:\msys64\usr\bin\make.exe>>%GITHUB_ENV%
goto :done
)
echo ERROR: no GNU make found on runner
exit /b 1
:done
echo MAKE found
- name: Build Harbour (ARM64) if cache miss
if: steps.cache-harbour.outputs.cache-hit != 'true'
shell: cmd
run: |
git clone --depth 1 https://github.com/harbour/core C:\harbour-src
cd /d C:\harbour-src
set HB_COMPILER=msvcarm64
set HB_PLATFORM=win
set HB_INSTALL_PREFIX=C:\harbour
set HB_BUILD_SHARED=no
REM Enable contribs — hbct, hbsqlit3 are linked by HbBuilder.
REM Contribs add ~3-5 min but provide hbct.lib / hbsqlit3.lib.
echo Using make: %MAKE%
REM Serialise (-j1) so harbour.exe in src/main is fully linked
REM before any sub-tree (src/vm, src/rtl, ...) tries to invoke it
REM to process .prg sources.
%MAKE% -j1 install
if errorlevel 1 (
echo Harbour build failed
exit /b 1
)
REM Diagnostic listings — do NOT let their exit code fail the step
echo === C:\harbour\lib\win\msvcarm64 ===
dir C:\harbour\lib\win\msvcarm64 1>nul 2>nul
dir C:\harbour\lib\win\msvcarm64 2>&1 & rem
echo === C:\harbour\bin ===
dir C:\harbour\bin /S /B 2>&1 & rem
exit /b 0
- name: Build Scintilla + Lexilla (ARM64)
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path resources\scintilla_src | Out-Null
Push-Location resources\scintilla_src
if (-not (Test-Path scintilla\cocoa\ScintillaView.h)) {
curl.exe -L -o scintilla556.tgz https://www.scintilla.org/scintilla556.tgz
curl.exe -L -o lexilla520.tgz https://www.scintilla.org/lexilla520.tgz
tar xzf scintilla556.tgz
tar xzf lexilla520.tgz
Remove-Item scintilla556.tgz, lexilla520.tgz
}
# /CETCOMPAT is x86/x64-only (Intel CET) — strip from .mak files
# before nmake. Scintilla/Lexilla hardcode it in LDFLAGS.
$maks = @('lexilla\src\lexilla.mak', 'scintilla\win32\scintilla.mak')
foreach ($m in $maks) {
# Match the flag plus only horizontal whitespace (no newlines)
# so !IF / !ENDIF block structure is preserved.
(Get-Content $m -Raw) -replace '[ \t]*-CETCOMPAT', '' | Set-Content $m -NoNewline
Select-String -Path $m -Pattern 'CETCOMPAT' | ForEach-Object { Write-Host "STILL PRESENT: $_" }
}
# Lexilla
Push-Location lexilla\src
nmake -f lexilla.mak DIR_O=..\bin DIR_BIN=..\bin
if ($LASTEXITCODE -ne 0) { throw "Lexilla build failed" }
Pop-Location
# Scintilla
Push-Location scintilla\win32
nmake -f scintilla.mak DIR_O=..\bin DIR_BIN=..\bin
if ($LASTEXITCODE -ne 0) { throw "Scintilla build failed" }
Pop-Location
Get-ChildItem scintilla\bin
Get-ChildItem lexilla\bin
# Stage DLLs where the loader expects them
New-Item -ItemType Directory -Force -Path ..\arm64 | Out-Null
$sci = Get-ChildItem -Recurse -Path scintilla\bin -Include 'Scintilla.dll','SciLexer.dll' | Select-Object -First 1
$lex = Get-ChildItem -Recurse -Path lexilla\bin -Include 'Lexilla.dll','lexilla.dll' | Select-Object -First 1
Copy-Item $sci.FullName ..\arm64\Scintilla.dll
Copy-Item $lex.FullName ..\arm64\Lexilla.dll
Pop-Location
- name: Build HbBuilder (ARM64)
shell: cmd
run: |
set HBDIR=C:\harbour
set HBBIN=%HBDIR%\bin\win\msvcarm64
if not exist "%HBBIN%\harbour.exe" set HBBIN=%HBDIR%\bin
set HBLIB=%HBDIR%\lib\win\msvcarm64
set HBINC=%HBDIR%\include
set SRCDIR=%CD%\source
set CPPDIR=%CD%\source\cpp
set INCDIR=%CD%\include
set OUTDIR=%CD%\bin
if not exist "%OUTDIR%" mkdir "%OUTDIR%"
echo === Step 1: Harbour PRG -> C ===
cd /d "%SRCDIR%"
"%HBBIN%\harbour.exe" hbbuilder_win.prg -n -w -es2 -q -I%HBINC% -I%INCDIR%
if errorlevel 1 ( echo HARBOUR FAILED & exit /b 1 )
echo === Step 2: cl.exe (arm64 host already set up by msvc-dev-cmd) ===
set CL_BASE=/c /O2 /W0 /EHsc /I"%HBINC%" /I"%INCDIR%"
cl.exe %CL_BASE% hbbuilder_win.c /Fohbbuilder_win.obj
if not exist hbbuilder_win.obj ( echo CL FAILED hbbuilder_win.c & exit /b 1 )
for %%f in (tform hbbridge tcontrol tcontrols hb_db_real) do (
if exist "%CPPDIR%\%%f.cpp" (
cl.exe %CL_BASE% "%CPPDIR%\%%f.cpp" /Fo%%f.obj
if not exist %%f.obj ( echo CL FAILED %%f.cpp & exit /b 1 )
)
)
echo === Step 2b: Resources ===
rc.exe /nologo /fohbbuilder_win.res hbbuilder_win.rc
set RES_OBJ=
if exist hbbuilder_win.res set RES_OBJ=hbbuilder_win.res
echo === Step 3: Link (arm64) ===
set OBJS=hbbuilder_win.obj tform.obj hbbridge.obj tcontrol.obj tcontrols.obj hb_db_real.obj %RES_OBJ%
link.exe /NOLOGO /SUBSYSTEM:WINDOWS /MACHINE:ARM64 /NODEFAULTLIB:LIBCMT ^
/OUT:"%OUTDIR%\hbbuilder_win_arm64.exe" ^
/LIBPATH:"%HBLIB%" ^
%OBJS% ^
hbrtl.lib hbvm.lib hbcpage.lib hblang.lib hbrdd.lib hbmacro.lib hbpp.lib ^
hbcommon.lib hbcplr.lib hbct.lib hbhsx.lib hbsix.lib hbusrrdd.lib ^
rddntx.lib rddnsx.lib rddcdx.lib rddfpt.lib hbdebug.lib hbpcre.lib ^
hbzlib.lib hbsqlit3.lib sqlite3.lib ^
gtwin.lib gtwvt.lib gtgui.lib ^
user32.lib gdi32.lib comctl32.lib comdlg32.lib shell32.lib ole32.lib ^
oleaut32.lib advapi32.lib ws2_32.lib winmm.lib msimg32.lib gdiplus.lib ^
winspool.lib ucrt.lib vcruntime.lib msvcrt.lib
if errorlevel 1 ( echo LINK FAILED & exit /b 1 )
dir "%OUTDIR%\hbbuilder_win_arm64.exe"
- name: Package
shell: pwsh
run: |
$stage = "HbBuilder-1.0.0-windows-arm64"
New-Item -ItemType Directory -Force -Path "$stage\bin","$stage\resources\arm64" | Out-Null
Copy-Item "bin\hbbuilder_win_arm64.exe" "$stage\bin\"
Copy-Item "resources\arm64\Scintilla.dll" "$stage\resources\arm64\"
Copy-Item "resources\arm64\Lexilla.dll" "$stage\resources\arm64\"
if (Test-Path "resources\icons") { Copy-Item -Recurse "resources\icons" "$stage\resources\" }
if (Test-Path "resources\harbour_logo.png") { Copy-Item "resources\harbour_logo.png" "$stage\resources\" }
@"
HbBuilder v1.0.0 — Windows ARM64 (experimental)
===============================================
Native Windows 11 ARM64 build. Requires an ARM64 PC
(Surface Pro X / 9 / 11, Snapdragon X, etc).
Run: bin\hbbuilder_win_arm64.exe
DLLs in resources\arm64\ must remain alongside the exe.
https://github.com/FiveTechSoft/HarbourBuilder
"@ | Set-Content "$stage\README.txt"
Compress-Archive -Path $stage -DestinationPath $env:ASSET -Force
Get-Item $env:ASSET | Select-Object Name,Length
- uses: actions/upload-artifact@v4
with:
name: ${{ env.ASSET }}
path: ${{ env.ASSET }}
- name: Upload to release
if: github.event_name == 'workflow_dispatch' && inputs.upload_to_release != ''
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ inputs.upload_to_release }}
files: ${{ env.ASSET }}