#requires -Version 7 # Per-engine static archives for mag/elec/heat/curr. # MSVC: cl /c + lib /OUT (no inter-engine symbol localization yet). # MinGW: g++ -c + ld -r + objcopy --keep-global-symbols + ar — mirrors scripts/macos/build_ffi.sh. $ErrorActionPreference = 'Stop' $Root = Resolve-Path (Join-Path $PSScriptRoot '..\..') $Build = if ($env:BUILD) { $env:BUILD } else { Join-Path $Root 'build\ffi' } . (Join-Path $PSScriptRoot '_toolchain.ps1') $tc = Get-Toolchain Initialize-Toolchain $tc $buildProfile = if ($env:PROFILE) { $env:PROFILE } else { 'release' } foreach ($d in @('fkn','liblua','belasolv','csolv','hsolv','ffi','exports')) { New-Item -ItemType Directory -Force -Path (Join-Path $Build $d) | Out-Null } function Compile-Many { param( [string[]]$Includes, [string]$OutDir, [string[]]$Sources ) foreach ($src in $Sources) { $base = [IO.Path]::GetFileNameWithoutExtension($src) if ($tc.Kind -eq 'msvc') { $obj = Join-Path $OutDir "$base.obj" $cxxflags = if ($buildProfile -eq 'release') { @('/nologo','/std:c++17','/EHs-c-','/GR-','/O2','/DNDEBUG','/W0','/D_CRT_SECURE_NO_WARNINGS') } else { @('/nologo','/std:c++17','/EHs-c-','/GR-','/Od','/Zi','/W0','/D_CRT_SECURE_NO_WARNINGS') } $incFlags = $Includes | ForEach-Object { "/I$_" } & cl.exe @cxxflags @incFlags /c $src "/Fo:$obj" | Out-Null if ($LASTEXITCODE -ne 0) { throw "cl.exe failed on $src ($LASTEXITCODE)" } } else { $obj = Join-Path $OutDir "$base.o" $cxxflags = if ($buildProfile -eq 'release') { @('-std=c++17','-fno-exceptions','-fno-rtti','-fpermissive','-O3','-DNDEBUG','-w') } else { @('-std=c++17','-fno-exceptions','-fno-rtti','-fpermissive','-O0','-g','-w') } $incFlags = $Includes | ForEach-Object { "-I$_" } $cxx = if ($tc.Kind -eq 'clangarm64') { Join-Path $tc.Bin 'clang++.exe' } else { 'g++.exe' } & $cxx @cxxflags @incFlags -c $src -o $obj if ($LASTEXITCODE -ne 0) { throw "$cxx failed on $src ($LASTEXITCODE)" } } } } # liblua — compiled against fkn's complex.h Compile-Many ` -Includes @((Join-Path $Root 'fkn'), (Join-Path $Root 'liblua'), (Join-Path $Root 'compat')) ` -OutDir (Join-Path $Build 'liblua') ` -Sources @( (Join-Path $Root 'liblua\lapi.cpp'), (Join-Path $Root 'liblua\lauxlib.cpp'), (Join-Path $Root 'liblua\lbaselib.cpp'), (Join-Path $Root 'liblua\lcode.cpp'), (Join-Path $Root 'liblua\ldblib.cpp'), (Join-Path $Root 'liblua\ldebug.cpp'), (Join-Path $Root 'liblua\ldo.cpp'), (Join-Path $Root 'liblua\lfunc.cpp'), (Join-Path $Root 'liblua\lgc.cpp'), (Join-Path $Root 'liblua\liolib.cpp'), (Join-Path $Root 'liblua\llex.cpp'), (Join-Path $Root 'liblua\lmathlib.cpp'), (Join-Path $Root 'liblua\lmem.cpp'), (Join-Path $Root 'liblua\lobject.cpp'), (Join-Path $Root 'liblua\lparser.cpp'), (Join-Path $Root 'liblua\lstate.cpp'), (Join-Path $Root 'liblua\lstring.cpp'), (Join-Path $Root 'liblua\lstrlib.cpp'), (Join-Path $Root 'liblua\ltable.cpp'), (Join-Path $Root 'liblua\ltests.cpp'), (Join-Path $Root 'liblua\ltm.cpp'), (Join-Path $Root 'liblua\lundump.cpp'), (Join-Path $Root 'liblua\lvm.cpp'), (Join-Path $Root 'liblua\lzio.cpp') ) Compile-Many ` -Includes @((Join-Path $Root 'fkn'), (Join-Path $Root 'compat')) ` -OutDir (Join-Path $Build 'fkn') ` -Sources @( (Join-Path $Root 'fkn\complex.cpp'), (Join-Path $Root 'fkn\cspars.cpp'), (Join-Path $Root 'fkn\cuthill.cpp'), (Join-Path $Root 'fkn\femmedoccore.cpp'), (Join-Path $Root 'fkn\fullmatrix.cpp'), (Join-Path $Root 'fkn\matprop.cpp'), (Join-Path $Root 'fkn\prob1big.cpp'), (Join-Path $Root 'fkn\prob2big.cpp'), (Join-Path $Root 'fkn\prob3big.cpp'), (Join-Path $Root 'fkn\prob4big.cpp'), (Join-Path $Root 'fkn\spars.cpp') ) Compile-Many ` -Includes @((Join-Path $Root 'belasolv'), (Join-Path $Root 'compat')) ` -OutDir (Join-Path $Build 'belasolv') ` -Sources @( (Join-Path $Root 'belasolv\cuthill.cpp'), (Join-Path $Root 'belasolv\femmedoccore.cpp'), (Join-Path $Root 'belasolv\prob1big.cpp'), (Join-Path $Root 'belasolv\spars.cpp') ) Compile-Many ` -Includes @((Join-Path $Root 'csolv'), (Join-Path $Root 'compat')) ` -OutDir (Join-Path $Build 'csolv') ` -Sources @( (Join-Path $Root 'csolv\complex.cpp'), (Join-Path $Root 'csolv\cspars.cpp'), (Join-Path $Root 'csolv\CUTHILL.CPP'), (Join-Path $Root 'csolv\femmedoccore.cpp'), (Join-Path $Root 'csolv\PROB1BIG.CPP') ) Compile-Many ` -Includes @((Join-Path $Root 'hsolv'), (Join-Path $Root 'compat')) ` -OutDir (Join-Path $Build 'hsolv') ` -Sources @( (Join-Path $Root 'hsolv\complex.cpp'), (Join-Path $Root 'hsolv\CUTHILL.CPP'), (Join-Path $Root 'hsolv\hsolvdoc.cpp'), (Join-Path $Root 'hsolv\prob1big.cpp'), (Join-Path $Root 'hsolv\SPARS.CPP') ) # ffi shims — one TU per engine, plus the liblua complex-op shim. Compile-Many -Includes @((Join-Path $Root 'fkn'), (Join-Path $Root 'compat')) -OutDir (Join-Path $Build 'ffi') -Sources @((Join-Path $Root 'ffi\femm_mag.cpp')) Compile-Many -Includes @((Join-Path $Root 'belasolv'), (Join-Path $Root 'compat')) -OutDir (Join-Path $Build 'ffi') -Sources @((Join-Path $Root 'ffi\femm_elec.cpp')) Compile-Many -Includes @((Join-Path $Root 'hsolv'), (Join-Path $Root 'compat')) -OutDir (Join-Path $Build 'ffi') -Sources @((Join-Path $Root 'ffi\femm_heat.cpp')) Compile-Many -Includes @((Join-Path $Root 'csolv'), (Join-Path $Root 'compat')) -OutDir (Join-Path $Build 'ffi') -Sources @((Join-Path $Root 'ffi\femm_curr.cpp')) Compile-Many -Includes @((Join-Path $Root 'liblua'), (Join-Path $Root 'compat')) -OutDir (Join-Path $Build 'ffi') -Sources @((Join-Path $Root 'ffi\femm_lua_complex_ops.cpp')) if ($tc.Kind -eq 'msvc') { # MSVC: bundle objects per engine via lib.exe. Note: inter-engine internal-symbol collisions # (cuthill / femmedoccore / prob1big / spars all repeat across engines) are not localized here — # if the Rust link step trips LNK2005, we'll need to merge to DLLs or namespace the C++ sources. function Pack-Lib { param([string]$Name, [string[]]$Objs) $out = Join-Path $Build "$Name.lib" if (Test-Path $out) { Remove-Item $out -Force } & lib.exe /NOLOGO "/OUT:$out" @Objs | Out-Null if ($LASTEXITCODE -ne 0) { throw "lib.exe failed for $Name ($LASTEXITCODE)" } } Pack-Lib 'femm_mag' ((Get-ChildItem (Join-Path $Build 'fkn\*.obj') | ForEach-Object FullName) + (Get-ChildItem (Join-Path $Build 'liblua\*.obj') | ForEach-Object FullName) + @((Join-Path $Build 'ffi\femm_mag.obj'), (Join-Path $Build 'ffi\femm_lua_complex_ops.obj'))) Pack-Lib 'femm_elec' ((Get-ChildItem (Join-Path $Build 'belasolv\*.obj') | ForEach-Object FullName) + @((Join-Path $Build 'ffi\femm_elec.obj'))) Pack-Lib 'femm_heat' ((Get-ChildItem (Join-Path $Build 'hsolv\*.obj') | ForEach-Object FullName) + @((Join-Path $Build 'ffi\femm_heat.obj'))) Pack-Lib 'femm_curr' ((Get-ChildItem (Join-Path $Build 'csolv\*.obj') | ForEach-Object FullName) + @((Join-Path $Build 'ffi\femm_curr.obj'))) Write-Host 'built:' Get-ChildItem (Join-Path $Build 'femm_*.lib') | ForEach-Object { Write-Host " $($_.FullName) ($([math]::Round($_.Length/1KB)) KB)" } } else { $binDir = if ($tc.Kind -eq 'clangarm64') { $tc.Bin } else { $tc.MingwBin } $nmExe = Join-Path $binDir 'nm.exe' $arExe = Join-Path $binDir 'ar.exe' $objcopyExe = if ($tc.Kind -eq 'clangarm64') { Join-Path $binDir 'llvm-objcopy.exe' } else { Join-Path $binDir 'objcopy.exe' } foreach ($p in @($nmExe, $arExe, $objcopyExe)) { if (-not (Test-Path $p)) { throw "missing binutil: $p" } } function Pack-Engine { param([string]$Prefix, [string[]]$Objs) $publicRx = "^femm_${Prefix}_" $renameSet = [System.Collections.Generic.HashSet[string]]::new() foreach ($obj in $Objs) { $lines = & $nmExe --defined-only -g $obj if ($LASTEXITCODE -ne 0) { throw "nm failed on $obj" } foreach ($line in $lines) { if ($line -match '^\s*(?:[0-9a-fA-F]+\s+)?([A-Z])\s+(\S+)\s*$') { $sym = $Matches[2] if ($sym -notmatch $publicRx) { $null = $renameSet.Add($sym) } } } } $renameFile = Join-Path $Build "exports\${Prefix}_rename.txt" (($renameSet | Sort-Object | ForEach-Object { "$_ __femm_${Prefix}__$_" }) -join "`n") | Set-Content -Path $renameFile -Encoding ascii $engineDir = Join-Path $Build "engines\$Prefix" if (Test-Path $engineDir) { Remove-Item $engineDir -Recurse -Force } New-Item -ItemType Directory -Force -Path $engineDir | Out-Null $renamedObjs = foreach ($obj in $Objs) { $base = [IO.Path]::GetFileName($obj) $newPath = Join-Path $engineDir "${Prefix}_${base}" Copy-Item $obj $newPath -Force & $objcopyExe "--redefine-syms=$renameFile" $newPath $newPath if ($LASTEXITCODE -ne 0) { throw "objcopy failed on $newPath ($LASTEXITCODE)" } $newPath } $archive = Join-Path $Build "libfemm_${Prefix}.a" if (Test-Path $archive) { Remove-Item $archive -Force } & $arExe rcs $archive @renamedObjs if ($LASTEXITCODE -ne 0) { throw "ar failed for $Prefix ($LASTEXITCODE)" } } Pack-Engine 'mag' ((Get-ChildItem (Join-Path $Build 'fkn\*.o') | ForEach-Object FullName) + (Get-ChildItem (Join-Path $Build 'liblua\*.o') | ForEach-Object FullName) + @((Join-Path $Build 'ffi\femm_mag.o'), (Join-Path $Build 'ffi\femm_lua_complex_ops.o'))) Pack-Engine 'elec' ((Get-ChildItem (Join-Path $Build 'belasolv\*.o') | ForEach-Object FullName) + @((Join-Path $Build 'ffi\femm_elec.o'))) Pack-Engine 'heat' ((Get-ChildItem (Join-Path $Build 'hsolv\*.o') | ForEach-Object FullName) + @((Join-Path $Build 'ffi\femm_heat.o'))) Pack-Engine 'curr' ((Get-ChildItem (Join-Path $Build 'csolv\*.o') | ForEach-Object FullName) + @((Join-Path $Build 'ffi\femm_curr.o'))) Write-Host 'built:' Get-ChildItem (Join-Path $Build 'libfemm_*.a') | ForEach-Object { Write-Host " $($_.FullName) ($([math]::Round($_.Length/1KB)) KB)" } }