From 8e01eff876dfee6962055f64cc916c3b83edb157 Mon Sep 17 00:00:00 2001 From: Jess Date: Sat, 23 May 2026 09:40:30 -0700 Subject: [PATCH] windows --- .gitignore | 1 + crates/femm-app/Cargo.toml | 3 + crates/femm-app/build.rs | 19 +++ crates/femm-app/src/main.rs | 31 ++-- crates/femm-app/src/svg_io.rs | 62 +++----- crates/femm-sys/build.rs | 36 ++++- liblua/llimits.h | 5 +- scripts/windows/_toolchain.ps1 | 119 +++++++++++++++ scripts/windows/build.ps1 | 74 ++++++++++ scripts/windows/build_ffi.ps1 | 227 +++++++++++++++++++++++++++++ scripts/windows/build_triangle.ps1 | 32 ++++ scripts/windows/debug.ps1 | 26 ++++ scripts/windows/ffi.ps1 | 4 + scripts/windows/install.ps1 | 29 ++++ triangle/triangle.c | 121 +++++++-------- xtask/src/main.rs | 2 +- 16 files changed, 666 insertions(+), 125 deletions(-) create mode 100644 crates/femm-app/build.rs create mode 100644 scripts/windows/_toolchain.ps1 create mode 100644 scripts/windows/build.ps1 create mode 100644 scripts/windows/build_ffi.ps1 create mode 100644 scripts/windows/build_triangle.ps1 create mode 100644 scripts/windows/debug.ps1 create mode 100644 scripts/windows/ffi.ps1 create mode 100644 scripts/windows/install.ps1 diff --git a/.gitignore b/.gitignore index 344db9c..9052def 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ build/ffi/ *.o wavs/ *.a +build/ target/ examples/vids/ assets/old/ diff --git a/crates/femm-app/Cargo.toml b/crates/femm-app/Cargo.toml index 4482732..5f9b726 100644 --- a/crates/femm-app/Cargo.toml +++ b/crates/femm-app/Cargo.toml @@ -19,3 +19,6 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread"] } libc = "0.2" tiny-skia = "0.11" roxmltree = "0.20" + +[target.'cfg(windows)'.build-dependencies] +winresource = "0.1" diff --git a/crates/femm-app/build.rs b/crates/femm-app/build.rs new file mode 100644 index 0000000..31d4e2d --- /dev/null +++ b/crates/femm-app/build.rs @@ -0,0 +1,19 @@ +fn main() { + #[cfg(target_os = "windows")] + embed_windows_icon(); +} + +#[cfg(target_os = "windows")] +fn embed_windows_icon() { + let manifest = std::path::PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()); + let repo_root = manifest.parent().unwrap().parent().unwrap(); + let icon = repo_root.join("build").join("assets").join("femm.ico"); + println!("cargo:rerun-if-changed={}", icon.display()); + if !icon.is_file() { + println!("cargo:warning=femm.ico not found at {} - skipping icon embed (run scripts/windows/build.ps1 to generate)", icon.display()); + return; + } + let mut res = winresource::WindowsResource::new(); + res.set_icon(icon.to_str().expect("non-utf8 icon path")); + res.compile().expect("winresource compile failed"); +} diff --git a/crates/femm-app/src/main.rs b/crates/femm-app/src/main.rs index c24bf97..c96cefb 100644 --- a/crates/femm-app/src/main.rs +++ b/crates/femm-app/src/main.rs @@ -1,5 +1,7 @@ //! iced shell entry point for the FEMM 4.2 port. +#![cfg_attr(all(target_os = "windows", not(debug_assertions)), windows_subsystem = "windows")] + mod doc_canvas; mod export; mod kinematic; @@ -380,7 +382,7 @@ impl App { .pick_file(); let Some(path) = picked else { return Task::none(); }; match svg_io::import_svg(&path) { - Ok(doc) => { + Ok((doc, warnings)) => { self.doc = doc; self.mesh = None; self.solution = None; @@ -388,8 +390,9 @@ impl App { self.simulation = None; self.source_label = path.file_name().and_then(|s| s.to_str()).unwrap_or("?").to_string(); self.source_path = Some(path.clone()); + let suffix = if warnings.is_empty() { String::new() } else { format!(" ({})", warnings.join("; ")) }; self.status = format!( - "imported {}: {} nodes, {} segs, {} arcs, {} labels", + "imported {}: {} nodes, {} segs, {} arcs, {} labels{suffix}", path.display(), self.doc.nodes.len(), self.doc.segments.len(), @@ -2438,7 +2441,7 @@ fn run_mesh(doc: &FemmDoc) -> Result { let triangle = locate_triangle().ok_or_else(|| ErrorReport { title: String::from("triangle binary missing"), - body: String::from("expected triangle next to femm.app/Contents/MacOS/triangle, or under build/triangle/triangle, or at $FEMM_TRIANGLE - run scripts/macos/build_triangle.sh"), + body: String::from("run `cargo xtask install` (or set $FEMM_TRIANGLE to the triangle executable)"), })?; let stem_str = stem.to_str().ok_or_else(|| ErrorReport { @@ -2483,7 +2486,7 @@ fn run_solve(stem: &Path) -> Result<(), ErrorReport> { let helper = locate_mag_solver().ok_or_else(|| ErrorReport { title: String::from("femm-mag-solve binary missing"), - body: String::from("expected femm-mag-solve next to femm.app/Contents/MacOS/, or under target/release/, or at $FEMM_MAG_SOLVE - run `cargo xtask install`"), + body: String::from("run `cargo xtask install` (or set $FEMM_MAG_SOLVE to the femm-mag-solve executable)"), })?; let output = std::process::Command::new(&helper) @@ -2544,16 +2547,13 @@ fn unix_signal(status: &std::process::ExitStatus) -> Option { #[cfg(not(unix))] fn unix_signal(_status: &std::process::ExitStatus) -> Option { None } -/// resolves the femm-mag-solve helper path from the bundled .app, the dev target dir, or $FEMM_MAG_SOLVE. +/// resolves the femm-mag-solve helper path from the bundled app, the dev target dir, or $FEMM_MAG_SOLVE. fn locate_mag_solver() -> Option { + let exe_name = format!("femm-mag-solve{}", std::env::consts::EXE_SUFFIX); if let Ok(exe) = std::env::current_exe() { if let Some(parent) = exe.parent() { - let bundled = parent.join("femm-mag-solve"); + let bundled = parent.join(&exe_name); if bundled.is_file() { return Some(bundled); } - let dev = parent.join("femm-mag-solve"); - if let Ok(canon) = dev.canonicalize() { - if canon.is_file() { return Some(canon); } - } } } if let Ok(env_path) = std::env::var("FEMM_MAG_SOLVE") { @@ -2574,15 +2574,16 @@ fn panic_payload_text(payload: &Box) -> String { String::from("panic payload was not a string") } -/// resolves the Triangle binary path from the bundled .app, the dev build dir, or $FEMM_TRIANGLE. +/// resolves the Triangle binary path from the bundled app, the dev build dir, or $FEMM_TRIANGLE. fn locate_triangle() -> Option { + let exe_name = format!("triangle{}", std::env::consts::EXE_SUFFIX); if let Ok(exe) = std::env::current_exe() { if let Some(parent) = exe.parent() { - let bundled = parent.join("triangle"); + let bundled = parent.join(&exe_name); if bundled.is_file() { return Some(bundled); } - let dev = parent.join("../../build/triangle/triangle"); - if let Ok(canon) = dev.canonicalize() { - if canon.is_file() { return Some(canon); } + for ancestor in parent.ancestors().take(6) { + let dev = ancestor.join("build").join("triangle").join(&exe_name); + if dev.is_file() { return Some(dev); } } } } diff --git a/crates/femm-app/src/svg_io.rs b/crates/femm-app/src/svg_io.rs index e3ad028..99c811d 100644 --- a/crates/femm-app/src/svg_io.rs +++ b/crates/femm-app/src/svg_io.rs @@ -9,58 +9,42 @@ use femm_doc_mag::{ const NODE_MERGE_TOL: f64 = 1e-3; -/// produces a fresh FemmDoc from the SVG at path, prompting (via rfd) to convert any non-circular ellipse into a circle on ambiguous radii. -pub fn import_svg(path: &Path) -> Result { +/// produces a fresh FemmDoc from the SVG at path, returning any non-fatal warnings (bezier approximation, missing materials, etc.) alongside the doc. +pub fn import_svg(path: &Path) -> Result<(FemmDoc, Vec), String> { let raw = std::fs::read_to_string(path).map_err(|e| format!("read svg: {e}"))?; - parse_svg_text(&raw, true) + parse_svg_text_with_warnings(&raw) } -/// parses an SVG text buffer into a FemmDoc; if prompt_for_ellipse is true and any non-circular ellipse is found, asks the user (rfd dialog) whether to convert. -pub fn parse_svg_text(src: &str, prompt_for_ellipse: bool) -> Result { +/// parses an SVG text buffer into a FemmDoc; non-circular ellipses are silently converted to circles (average radius). the `_prompt_for_ellipse` argument is accepted for backwards compatibility but ignored. +pub fn parse_svg_text(src: &str, _prompt_for_ellipse: bool) -> Result { + let (doc, _) = parse_svg_text_with_warnings(src)?; + Ok(doc) +} + +/// like parse_svg_text but also returns a list of human-readable warnings the caller can surface (bezier approximation, shapes missing material, etc.). +pub fn parse_svg_text_with_warnings(src: &str) -> Result<(FemmDoc, Vec), String> { let xml = Document::parse(src).map_err(|e| format!("svg parse: {e}"))?; let mut builder = Builder::new(); let mut warnings = Warnings::default(); walk(xml.root_element(), Mat3::identity(), &mut builder, &mut warnings)?; - let convert_ellipses = if warnings.has_ellipses() && prompt_for_ellipse { - let r = rfd::MessageDialog::new() - .set_title("Non-circular ellipses detected") - .set_description(&format!( - "Found {} non-circular ellipse(s). Converting to circles (average radius) keeps the mesh clean and avoids the polygon-approximation penalty. Convert?", - warnings.ellipse_count - )) - .set_buttons(rfd::MessageButtons::YesNo) - .show(); - matches!(r, rfd::MessageDialogResult::Yes) - } else { - true - }; - + let mut messages = Vec::new(); + if warnings.has_ellipses() { + messages.push(format!("converted {} non-circular ellipse(s) to circles", warnings.ellipse_count)); + } if !warnings.bezier_paths.is_empty() { - rfd::MessageDialog::new() - .set_title("Bezier curves approximated") - .set_description(&format!( - "{} bezier curve(s) approximated with straight-line polylines. For smooth curves, replace with circles or arc paths.", - warnings.bezier_paths.len(), - )) - .set_buttons(rfd::MessageButtons::Ok) - .show(); + messages.push(format!("approximated {} bezier curve(s) with polylines", warnings.bezier_paths.len())); } - if !warnings.no_material_shapes.is_empty() { - let ids = warnings.no_material_shapes.join(", "); - rfd::MessageDialog::new() - .set_title("Shapes missing material") - .set_description(&format!( - "{} shape(s) have a recognized class (Block/Segment/Toroid/Disc) but no material name. Add a child like <title>Steel so the importer can label the region. Affected: {ids}", - warnings.no_material_shapes.len(), - )) - .set_buttons(rfd::MessageButtons::Ok) - .show(); + messages.push(format!( + "{} shape(s) missing material (add MaterialName): {}", + warnings.no_material_shapes.len(), + warnings.no_material_shapes.join(", "), + )); } - let doc = builder.finalize(convert_ellipses); - Ok(doc) + let doc = builder.finalize(true); + Ok((doc, messages)) } /// emits an SVG containing every node (as ``), segment (as ``), arc (as ``), and block label (as ``); writes y-flipped so it renders right-side-up in any viewer. diff --git a/crates/femm-sys/build.rs b/crates/femm-sys/build.rs index 1ce31b3..2f5364d 100644 --- a/crates/femm-sys/build.rs +++ b/crates/femm-sys/build.rs @@ -13,20 +13,34 @@ fn main() { // re-run on changes to any FFI header, build script, or solver source dir. println!("cargo:rerun-if-changed={}", ffi_dir.display()); println!("cargo:rerun-if-changed={}", repo_root.join("scripts/macos/build_ffi.sh").display()); + println!("cargo:rerun-if-changed={}", repo_root.join("scripts/windows/build_ffi.ps1").display()); + println!("cargo:rerun-if-changed={}", repo_root.join("scripts/windows/_toolchain.ps1").display()); for engine in ["fkn", "belasolv", "csolv", "hsolv", "liblua", "compat"] { println!("cargo:rerun-if-changed={}", repo_root.join(engine).display()); } let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); - let script = match target_os.as_str() { - "macos" => repo_root.join("scripts/macos/build_ffi.sh"), + let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default(); + let status = match target_os.as_str() { + "macos" => Command::new("bash") + .arg(repo_root.join("scripts/macos/build_ffi.sh")) + .status() + .expect("failed to invoke bash for engine build script"), + "windows" => { + let script = repo_root.join("scripts/windows/build_ffi.ps1"); + Command::new("pwsh") + .args([ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + ]) + .arg(&script) + .status() + .expect("failed to invoke pwsh for engine build script") + } other => panic!("femm-sys: no engine build script wired up for target_os={other}"), }; - - let status = Command::new("bash") - .arg(&script) - .status() - .expect("failed to invoke bash for engine build script"); if !status.success() { panic!("engine build script exited with {status}"); } @@ -35,7 +49,13 @@ fn main() { for lib in ["femm_mag", "femm_elec", "femm_heat", "femm_curr"] { println!("cargo:rustc-link-lib=static={lib}"); } - println!("cargo:rustc-link-lib=dylib=c++"); + match (target_os.as_str(), target_env.as_str()) { + ("macos", _) => println!("cargo:rustc-link-lib=dylib=c++"), + ("windows", "gnu") => println!("cargo:rustc-link-lib=dylib=stdc++"), + ("windows", "gnullvm") => println!("cargo:rustc-link-lib=dylib=c++"), + ("windows", "msvc") => { /* MSVC links the C++ runtime automatically */ } + _ => println!("cargo:rustc-link-lib=dylib=stdc++"), + } let bindings = bindgen::Builder::default() .header(manifest.join("wrapper.h").to_str().unwrap()) diff --git a/liblua/llimits.h b/liblua/llimits.h index 3a46909..be30c02 100644 --- a/liblua/llimits.h +++ b/liblua/llimits.h @@ -10,6 +10,7 @@ #include #include +#include @@ -45,7 +46,7 @@ typedef CComplex Number; #define NUMBER_FMT "%.16g" /* LUA_NUMBER */ void lua_number2str(char *s,Number n); - + typedef unsigned long lint32; /* unsigned int with at least 32 bits */ @@ -58,7 +59,7 @@ typedef unsigned long lint32; /* unsigned int with at least 32 bits */ ** conversion of pointer to int (for hashing only) ** (the shift removes bits that are usually 0 because of alignment) */ -#define IntPoint(p) (((unsigned long)(p)) >> 3) +#define IntPoint(p) (((uintptr_t)(p)) >> 3) diff --git a/scripts/windows/_toolchain.ps1 b/scripts/windows/_toolchain.ps1 new file mode 100644 index 0000000..e4bbe4f --- /dev/null +++ b/scripts/windows/_toolchain.ps1 @@ -0,0 +1,119 @@ +# Toolchain detection. Picks a backend appropriate for the host arch: +# x64 host -> MSVC (x64) or MinGW (x86_64), Rust target = host default or x86_64-pc-windows-gnu +# ARM64 host -> CLANGARM64 (msys2) or MinGW (x86_64, emulated, warned), Rust target = aarch64-pc-windows-gnullvm +# Dot-source this file: . "$PSScriptRoot\_toolchain.ps1" + +$ErrorActionPreference = 'Stop' + +function Get-HostArch { + if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64' -or $env:PROCESSOR_ARCHITEW6432 -eq 'ARM64') { return 'arm64' } + return 'x64' +} + +function Initialize-VsEnv { + param([string]$VcVars) + $tmp = New-TemporaryFile + try { + cmd /c "`"$VcVars`" x64 >NUL && set" | Out-File -FilePath $tmp -Encoding ascii + foreach ($line in Get-Content $tmp) { + if ($line -match '^([^=]+)=(.*)$') { + Set-Item -Path "env:$($Matches[1])" -Value $Matches[2] + } + } + } finally { + Remove-Item $tmp -Force -ErrorAction SilentlyContinue + } +} + +function Find-VsInstall { + $vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe' + if (-not (Test-Path $vswhere)) { + $vswhere = Join-Path $env:ProgramFiles 'Microsoft Visual Studio\Installer\vswhere.exe' + } + if (-not (Test-Path $vswhere)) { return $null } + + $install = & $vswhere -latest -products * ` + -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 ` + -property installationPath 2>$null + if (-not $install) { return $null } + + $vcvars = Join-Path $install 'VC\Auxiliary\Build\vcvars64.bat' + if (Test-Path $vcvars) { return $vcvars } + return $null +} + +function Find-MingwBin { + if ($env:MINGW_PREFIX -and (Test-Path (Join-Path $env:MINGW_PREFIX 'g++.exe'))) { + return $env:MINGW_PREFIX + } + $gxx = Get-Command g++.exe -ErrorAction SilentlyContinue + if ($gxx) { return (Split-Path $gxx.Source -Parent) } + foreach ($p in @( + 'C:\msys64\mingw64\bin', + 'C:\msys64\ucrt64\bin', + 'C:\mingw64\bin', + 'C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin' + )) { + if (Test-Path (Join-Path $p 'g++.exe')) { return $p } + } + return $null +} + +function Find-ClangArm64Bin { + if ($env:CLANGARM64_PREFIX -and (Test-Path (Join-Path $env:CLANGARM64_PREFIX 'clang++.exe'))) { + return $env:CLANGARM64_PREFIX + } + foreach ($p in @('C:\msys64\clangarm64\bin')) { + if (Test-Path (Join-Path $p 'clang++.exe')) { return $p } + } + return $null +} + +function Get-Toolchain { + $hostArch = Get-HostArch + + if ($hostArch -eq 'arm64') { + $clangBin = Find-ClangArm64Bin + if ($clangBin) { + return [pscustomobject]@{ + Kind = 'clangarm64' + Ready = $true + Bin = $clangBin + CargoTarget = 'aarch64-pc-windows-gnullvm' + } + } + $mingw = Find-MingwBin + if ($mingw) { + Write-Warning "ARM64 host but only x86_64 MinGW found; resulting binaries will run under emulation and may crash at startup. Install msys2 CLANGARM64 (pacman -S mingw-w64-clang-aarch64-toolchain)." + return [pscustomobject]@{ Kind = 'mingw'; Ready = $true; MingwBin = $mingw; CargoTarget = 'x86_64-pc-windows-gnu' } + } + throw "No ARM64 C++ toolchain found. Install msys2 CLANGARM64 (pacman -S mingw-w64-clang-aarch64-toolchain) or Visual Studio ARM64 C++ tools." + } + + if (Get-Command cl.exe -ErrorAction SilentlyContinue) { + return [pscustomobject]@{ Kind = 'msvc'; Ready = $true; VcVars = $null; CargoTarget = '' } + } + $vcvars = Find-VsInstall + if ($vcvars) { + return [pscustomobject]@{ Kind = 'msvc'; Ready = $false; VcVars = $vcvars; CargoTarget = '' } + } + $mingw = Find-MingwBin + if ($mingw) { + return [pscustomobject]@{ Kind = 'mingw'; Ready = $true; MingwBin = $mingw; CargoTarget = 'x86_64-pc-windows-gnu' } + } + throw "No C++ toolchain found. Install Visual Studio Build Tools (with the C++ workload) or MinGW-w64." +} + +function Initialize-Toolchain { + param([pscustomobject]$Toolchain) + if ($Toolchain.Kind -eq 'msvc' -and -not $Toolchain.Ready) { + Write-Host "Importing MSVC environment from $($Toolchain.VcVars)" + Initialize-VsEnv -VcVars $Toolchain.VcVars + } + if ($Toolchain.Kind -eq 'mingw' -and $Toolchain.MingwBin -and ($env:PATH -notlike "*$($Toolchain.MingwBin)*")) { + $env:PATH = "$($Toolchain.MingwBin);$env:PATH" + } + if ($Toolchain.Kind -eq 'clangarm64' -and $Toolchain.Bin -and ($env:PATH -notlike "*$($Toolchain.Bin)*")) { + $env:PATH = "$($Toolchain.Bin);$env:PATH" + } +} diff --git a/scripts/windows/build.ps1 b/scripts/windows/build.ps1 new file mode 100644 index 0000000..8053590 --- /dev/null +++ b/scripts/windows/build.ps1 @@ -0,0 +1,74 @@ +#requires -Version 7 +# Top-level Windows release build. Mirrors scripts/macos/build.sh. + +$ErrorActionPreference = 'Stop' + +$Root = Resolve-Path (Join-Path $PSScriptRoot '..\..') +Set-Location $Root + +if (-not $IsWindows) { + Write-Error "wrong platform: $($PSVersionTable.OS) - use cargo xtask build" + exit 1 +} + +. (Join-Path $PSScriptRoot '_toolchain.ps1') +$tc = Get-Toolchain +Initialize-Toolchain $tc +Write-Host "toolchain: $($tc.Kind)" + +$env:PROFILE = 'release' + +$Build = Join-Path $Root 'build' +$BinDir = Join-Path $Build 'bin\femm' + +Write-Host 'Building Triangle...' +& (Join-Path $PSScriptRoot 'build_triangle.ps1') + +$tri = Join-Path $Root 'build\triangle\triangle.exe' +if (-not (Test-Path $tri)) { Write-Error "triangle binary missing at $tri"; exit 1 } + +# femm-app/build.rs embeds this into femm.exe, so it must exist before cargo build. +$svg = Join-Path $Root 'assets\femm.svg' +$icoOut = Join-Path $Build 'assets\femm.ico' +New-Item -ItemType Directory -Force -Path (Split-Path $icoOut) | Out-Null +if (Test-Path $svg) { + $magick = Get-Command magick.exe -ErrorAction SilentlyContinue + if ($magick) { + & $magick.Source -background none $svg -define icon:auto-resize=16,32,48,64,128,256 $icoOut + if ($LASTEXITCODE -ne 0) { Write-Warning "magick failed to build $icoOut ($LASTEXITCODE)" } + } else { + Write-Warning 'magick not found - femm.exe will be built without an embedded icon' + } +} + +Write-Host 'Building Rust workspace (release)...' +$cargoArgs = @('build','--release','-p','femm-app','-p','femm-mag-solve') +if ($tc.CargoTarget) { + $cargoArgs += @('--target',$tc.CargoTarget) +} +& cargo @cargoArgs +if ($LASTEXITCODE -ne 0) { throw "cargo build failed ($LASTEXITCODE)" } + +$targetDir = if ($tc.CargoTarget) { + Join-Path $Root "target\$($tc.CargoTarget)\release" +} else { + Join-Path $Root 'target\release' +} +$bin = Join-Path $targetDir 'femm.exe' +$solve = Join-Path $targetDir 'femm-mag-solve.exe' +foreach ($p in @($bin, $solve)) { + if (-not (Test-Path $p)) { Write-Error "missing build output: $p"; exit 1 } +} + +if (Test-Path $BinDir) { Remove-Item $BinDir -Recurse -Force } +New-Item -ItemType Directory -Force -Path $BinDir | Out-Null + +Copy-Item $bin (Join-Path $BinDir 'femm.exe') +Copy-Item $solve (Join-Path $BinDir 'femm-mag-solve.exe') +Copy-Item $tri (Join-Path $BinDir 'triangle.exe') + +if (Test-Path $icoOut) { + Copy-Item $icoOut (Join-Path $BinDir 'femm.ico') +} + +Write-Host "Built: $BinDir" diff --git a/scripts/windows/build_ffi.ps1 b/scripts/windows/build_ffi.ps1 new file mode 100644 index 0000000..fa8bcae --- /dev/null +++ b/scripts/windows/build_ffi.ps1 @@ -0,0 +1,227 @@ +#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)" } +} diff --git a/scripts/windows/build_triangle.ps1 b/scripts/windows/build_triangle.ps1 new file mode 100644 index 0000000..70f383d --- /dev/null +++ b/scripts/windows/build_triangle.ps1 @@ -0,0 +1,32 @@ +#requires -Version 7 +# Builds the Triangle mesher into build\triangle\triangle.exe. + +$ErrorActionPreference = 'Stop' + +$Root = Resolve-Path (Join-Path $PSScriptRoot '..\..') +$Build = if ($env:BUILD) { $env:BUILD } else { Join-Path $Root 'build\triangle' } +New-Item -ItemType Directory -Force -Path $Build | Out-Null + +. (Join-Path $PSScriptRoot '_toolchain.ps1') +$tc = Get-Toolchain +Initialize-Toolchain $tc + +$buildProfile = if ($env:PROFILE) { $env:PROFILE } else { 'release' } +$src = Join-Path $Root 'triangle\triangle.c' +$out = Join-Path $Build 'triangle.exe' + +if ($tc.Kind -eq 'msvc') { + $cflags = if ($buildProfile -eq 'release') { @('/nologo','/O2','/DNDEBUG','/W0') } else { @('/nologo','/Od','/Zi','/W0') } + & cl.exe @cflags $src "/Fe:$out" "/Fo:$Build\" /link /SUBSYSTEM:CONSOLE + if ($LASTEXITCODE -ne 0) { throw "cl.exe failed building triangle ($LASTEXITCODE)" } +} elseif ($tc.Kind -eq 'clangarm64') { + $cflags = if ($buildProfile -eq 'release') { @('-std=gnu89','-O3','-DNDEBUG','-w') } else { @('-std=gnu89','-O0','-g','-w') } + & (Join-Path $tc.Bin 'clang.exe') @cflags -o $out $src -lm + if ($LASTEXITCODE -ne 0) { throw "clang failed building triangle ($LASTEXITCODE)" } +} else { + $cflags = if ($buildProfile -eq 'release') { @('-std=gnu89','-O3','-DNDEBUG','-w') } else { @('-std=gnu89','-O0','-g','-w') } + & gcc.exe @cflags -o $out $src -lm + if ($LASTEXITCODE -ne 0) { throw "gcc failed building triangle ($LASTEXITCODE)" } +} + +Write-Host "built: $out" diff --git a/scripts/windows/debug.ps1 b/scripts/windows/debug.ps1 new file mode 100644 index 0000000..ac2b1ba --- /dev/null +++ b/scripts/windows/debug.ps1 @@ -0,0 +1,26 @@ +#requires -Version 7 +$ErrorActionPreference = 'Stop' + +$Root = Resolve-Path (Join-Path $PSScriptRoot '..\..') +Set-Location $Root + +if (-not $IsWindows) { + Write-Error "wrong platform: $($PSVersionTable.OS) - use cargo xtask debug" + exit 1 +} + +. (Join-Path $PSScriptRoot '_toolchain.ps1') +$tc = Get-Toolchain +Initialize-Toolchain $tc + +$env:RUST_BACKTRACE = '1' + +$cargoArgs = @('run','-p','femm-app') +if ($tc.CargoTarget) { + $cargoArgs += @('--target',$tc.CargoTarget) +} +$cargoArgs += '--' +$cargoArgs += $args + +& cargo @cargoArgs +exit $LASTEXITCODE diff --git a/scripts/windows/ffi.ps1 b/scripts/windows/ffi.ps1 new file mode 100644 index 0000000..a642f96 --- /dev/null +++ b/scripts/windows/ffi.ps1 @@ -0,0 +1,4 @@ +#requires -Version 7 +$ErrorActionPreference = 'Stop' +& (Join-Path $PSScriptRoot 'build_ffi.ps1') @args +exit $LASTEXITCODE diff --git a/scripts/windows/install.ps1 b/scripts/windows/install.ps1 new file mode 100644 index 0000000..3900fbd --- /dev/null +++ b/scripts/windows/install.ps1 @@ -0,0 +1,29 @@ +#requires -Version 7 +$ErrorActionPreference = 'Stop' + +$Root = Resolve-Path (Join-Path $PSScriptRoot '..\..') +Set-Location $Root + +if (-not $IsWindows) { + Write-Error "wrong platform: $($PSVersionTable.OS) - use cargo xtask install" + exit 1 +} + +& (Join-Path $PSScriptRoot 'build.ps1') +if ($LASTEXITCODE -ne 0) { throw "build.ps1 failed ($LASTEXITCODE)" } + +$Dest = if ($env:FEMM_INSTALL_DIR) { $env:FEMM_INSTALL_DIR } else { Join-Path $env:LOCALAPPDATA 'Programs\femm' } +$Src = Join-Path $Root 'build\bin\femm' + +# Stop any running instance so the copy doesn't trip on a file lock. +Get-Process -Name 'femm' -ErrorAction SilentlyContinue | ForEach-Object { + Write-Host "Stopping running femm.exe (pid $($_.Id))..." + try { $_ | Stop-Process -Force } catch {} +} +Start-Sleep -Milliseconds 200 + +if (Test-Path $Dest) { Remove-Item $Dest -Recurse -Force } +New-Item -ItemType Directory -Force -Path $Dest | Out-Null +Copy-Item -Path (Join-Path $Src '*') -Destination $Dest -Recurse -Force + +Write-Host "Installed: $Dest" diff --git a/triangle/triangle.c b/triangle/triangle.c index fd0a1c6..ecfeab0 100644 --- a/triangle/triangle.c +++ b/triangle/triangle.c @@ -346,6 +346,7 @@ #include #include +#include #include #include #ifndef NO_TIMER @@ -831,7 +832,7 @@ struct behavior { /* extracting an orientation (in the range 0 to 2) and a pointer to the */ /* beginning of a triangle. The encode() routine compresses a pointer to a */ /* triangle and an orientation into a single pointer. My assumptions that */ -/* triangles are four-byte-aligned and that the `unsigned long' type is */ +/* triangles are four-byte-aligned and that the `uintptr_t' type is */ /* long enough to hold a pointer are two of the few kludges in this program.*/ /* */ /* Subsegments are manipulated similarly. A pointer to a subsegment */ @@ -942,16 +943,16 @@ int minus1mod3[3] = {2, 0, 1}; /* extracted from the two least significant bits of the pointer. */ #define decode(ptr, otri) \ - (otri).orient = (int) ((unsigned long) (ptr) & (unsigned long) 3l); \ + (otri).orient = (int) ((uintptr_t) (ptr) & (uintptr_t) 3l); \ (otri).tri = (triangle *) \ - ((unsigned long) (ptr) ^ (unsigned long) (otri).orient) + ((uintptr_t) (ptr) ^ (uintptr_t) (otri).orient) /* encode() compresses an oriented triangle into a single pointer. It */ /* relies on the assumption that all triangles are aligned to four-byte */ /* boundaries, so the two least significant bits of (otri).tri are zero. */ #define encode(otri) \ - (triangle) ((unsigned long) (otri).tri | (unsigned long) (otri).orient) + (triangle) ((uintptr_t) (otri).tri | (uintptr_t) (otri).orient) /* The following handle manipulation primitives are all described by Guibas */ /* and Stolfi. However, Guibas and Stolfi use an edge-based data */ @@ -1115,16 +1116,16 @@ int minus1mod3[3] = {2, 0, 1}; #define infect(otri) \ (otri).tri[6] = (triangle) \ - ((unsigned long) (otri).tri[6] | (unsigned long) 2l) + ((uintptr_t) (otri).tri[6] | (uintptr_t) 2l) #define uninfect(otri) \ (otri).tri[6] = (triangle) \ - ((unsigned long) (otri).tri[6] & ~ (unsigned long) 2l) + ((uintptr_t) (otri).tri[6] & ~ (uintptr_t) 2l) /* Test a triangle for viral infection. */ #define infected(otri) \ - (((unsigned long) (otri).tri[6] & (unsigned long) 2l) != 0l) + (((uintptr_t) (otri).tri[6] & (uintptr_t) 2l) != 0l) /* Check or set a triangle's attributes. */ @@ -1162,16 +1163,16 @@ int minus1mod3[3] = {2, 0, 1}; /* are masked out to produce the real pointer. */ #define sdecode(sptr, osub) \ - (osub).ssorient = (int) ((unsigned long) (sptr) & (unsigned long) 1l); \ + (osub).ssorient = (int) ((uintptr_t) (sptr) & (uintptr_t) 1l); \ (osub).ss = (subseg *) \ - ((unsigned long) (sptr) & ~ (unsigned long) 3l) + ((uintptr_t) (sptr) & ~ (uintptr_t) 3l) /* sencode() compresses an oriented subsegment into a single pointer. It */ /* relies on the assumption that all subsegments are aligned to two-byte */ /* boundaries, so the least significant bit of (osub).ss is zero. */ #define sencode(osub) \ - (subseg) ((unsigned long) (osub).ss | (unsigned long) (osub).ssorient) + (subseg) ((uintptr_t) (osub).ss | (uintptr_t) (osub).ssorient) /* ssym() toggles the orientation of a subsegment. */ @@ -1908,27 +1909,27 @@ struct otri *t; struct osub printsh; vertex printvertex; - printf("triangle x%lx with orientation %d:\n", (unsigned long) t->tri, + printf("triangle x%lx with orientation %d:\n", (uintptr_t) t->tri, t->orient); decode(t->tri[0], printtri); if (printtri.tri == m->dummytri) { printf(" [0] = Outer space\n"); } else { - printf(" [0] = x%lx %d\n", (unsigned long) printtri.tri, + printf(" [0] = x%lx %d\n", (uintptr_t) printtri.tri, printtri.orient); } decode(t->tri[1], printtri); if (printtri.tri == m->dummytri) { printf(" [1] = Outer space\n"); } else { - printf(" [1] = x%lx %d\n", (unsigned long) printtri.tri, + printf(" [1] = x%lx %d\n", (uintptr_t) printtri.tri, printtri.orient); } decode(t->tri[2], printtri); if (printtri.tri == m->dummytri) { printf(" [2] = Outer space\n"); } else { - printf(" [2] = x%lx %d\n", (unsigned long) printtri.tri, + printf(" [2] = x%lx %d\n", (uintptr_t) printtri.tri, printtri.orient); } @@ -1937,37 +1938,37 @@ struct otri *t; printf(" Origin[%d] = NULL\n", (t->orient + 1) % 3 + 3); else printf(" Origin[%d] = x%lx (%.12g, %.12g)\n", - (t->orient + 1) % 3 + 3, (unsigned long) printvertex, + (t->orient + 1) % 3 + 3, (uintptr_t) printvertex, printvertex[0], printvertex[1]); dest(*t, printvertex); if (printvertex == (vertex) NULL) printf(" Dest [%d] = NULL\n", (t->orient + 2) % 3 + 3); else printf(" Dest [%d] = x%lx (%.12g, %.12g)\n", - (t->orient + 2) % 3 + 3, (unsigned long) printvertex, + (t->orient + 2) % 3 + 3, (uintptr_t) printvertex, printvertex[0], printvertex[1]); apex(*t, printvertex); if (printvertex == (vertex) NULL) printf(" Apex [%d] = NULL\n", t->orient + 3); else printf(" Apex [%d] = x%lx (%.12g, %.12g)\n", - t->orient + 3, (unsigned long) printvertex, + t->orient + 3, (uintptr_t) printvertex, printvertex[0], printvertex[1]); if (b->usesegments) { sdecode(t->tri[6], printsh); if (printsh.ss != m->dummysub) { - printf(" [6] = x%lx %d\n", (unsigned long) printsh.ss, + printf(" [6] = x%lx %d\n", (uintptr_t) printsh.ss, printsh.ssorient); } sdecode(t->tri[7], printsh); if (printsh.ss != m->dummysub) { - printf(" [7] = x%lx %d\n", (unsigned long) printsh.ss, + printf(" [7] = x%lx %d\n", (uintptr_t) printsh.ss, printsh.ssorient); } sdecode(t->tri[8], printsh); if (printsh.ss != m->dummysub) { - printf(" [8] = x%lx %d\n", (unsigned long) printsh.ss, + printf(" [8] = x%lx %d\n", (uintptr_t) printsh.ss, printsh.ssorient); } } @@ -2003,19 +2004,19 @@ struct osub *s; vertex printvertex; printf("subsegment x%lx with orientation %d and mark %d:\n", - (unsigned long) s->ss, s->ssorient, mark(*s)); + (uintptr_t) s->ss, s->ssorient, mark(*s)); sdecode(s->ss[0], printsh); if (printsh.ss == m->dummysub) { printf(" [0] = No subsegment\n"); } else { - printf(" [0] = x%lx %d\n", (unsigned long) printsh.ss, + printf(" [0] = x%lx %d\n", (uintptr_t) printsh.ss, printsh.ssorient); } sdecode(s->ss[1], printsh); if (printsh.ss == m->dummysub) { printf(" [1] = No subsegment\n"); } else { - printf(" [1] = x%lx %d\n", (unsigned long) printsh.ss, + printf(" [1] = x%lx %d\n", (uintptr_t) printsh.ss, printsh.ssorient); } @@ -2024,28 +2025,28 @@ struct osub *s; printf(" Origin[%d] = NULL\n", 2 + s->ssorient); else printf(" Origin[%d] = x%lx (%.12g, %.12g)\n", - 2 + s->ssorient, (unsigned long) printvertex, + 2 + s->ssorient, (uintptr_t) printvertex, printvertex[0], printvertex[1]); sdest(*s, printvertex); if (printvertex == (vertex) NULL) printf(" Dest [%d] = NULL\n", 3 - s->ssorient); else printf(" Dest [%d] = x%lx (%.12g, %.12g)\n", - 3 - s->ssorient, (unsigned long) printvertex, + 3 - s->ssorient, (uintptr_t) printvertex, printvertex[0], printvertex[1]); decode(s->ss[6], printtri); if (printtri.tri == m->dummytri) { printf(" [6] = Outer space\n"); } else { - printf(" [6] = x%lx %d\n", (unsigned long) printtri.tri, + printf(" [6] = x%lx %d\n", (uintptr_t) printtri.tri, printtri.orient); } decode(s->ss[7], printtri); if (printtri.tri == m->dummytri) { printf(" [7] = Outer space\n"); } else { - printf(" [7] = x%lx %d\n", (unsigned long) printtri.tri, + printf(" [7] = x%lx %d\n", (uintptr_t) printtri.tri, printtri.orient); } @@ -2054,14 +2055,14 @@ struct osub *s; printf(" Segment origin[%d] = NULL\n", 4 + s->ssorient); else printf(" Segment origin[%d] = x%lx (%.12g, %.12g)\n", - 4 + s->ssorient, (unsigned long) printvertex, + 4 + s->ssorient, (uintptr_t) printvertex, printvertex[0], printvertex[1]); segdest(*s, printvertex); if (printvertex == (vertex) NULL) printf(" Segment dest [%d] = NULL\n", 5 - s->ssorient); else printf(" Segment dest [%d] = x%lx (%.12g, %.12g)\n", - 5 - s->ssorient, (unsigned long) printvertex, + 5 - s->ssorient, (uintptr_t) printvertex, printvertex[0], printvertex[1]); } @@ -2124,7 +2125,7 @@ struct memorypool *pool; #endif /* not ANSI_DECLARATORS */ { - unsigned long alignptr; + uintptr_t alignptr; pool->items = 0; pool->maxitems = 0; @@ -2132,11 +2133,11 @@ struct memorypool *pool; /* Set the currently active block. */ pool->nowblock = pool->firstblock; /* Find the first item in the pool. Increment by the size of (VOID *). */ - alignptr = (unsigned long) (pool->nowblock + 1); + alignptr = (uintptr_t) (pool->nowblock + 1); /* Align the item on an `alignbytes'-byte boundary. */ pool->nextitem = (VOID *) - (alignptr + (unsigned long) pool->alignbytes - - (alignptr % (unsigned long) pool->alignbytes)); + (alignptr + (uintptr_t) pool->alignbytes - + (alignptr % (uintptr_t) pool->alignbytes)); /* There are lots of unallocated items left in this block. */ pool->unallocateditems = pool->itemsfirstblock; /* The stack of deallocated items is empty. */ @@ -2241,7 +2242,7 @@ struct memorypool *pool; { VOID *newitem; VOID **newblock; - unsigned long alignptr; + uintptr_t alignptr; /* First check the linked list of dead items. If the list is not */ /* empty, allocate an item from the list rather than a fresh one. */ @@ -2266,11 +2267,11 @@ struct memorypool *pool; pool->nowblock = (VOID **) *(pool->nowblock); /* Find the first item in the block. */ /* Increment by the size of (VOID *). */ - alignptr = (unsigned long) (pool->nowblock + 1); + alignptr = (uintptr_t) (pool->nowblock + 1); /* Align the item on an `alignbytes'-byte boundary. */ pool->nextitem = (VOID *) - (alignptr + (unsigned long) pool->alignbytes - - (alignptr % (unsigned long) pool->alignbytes)); + (alignptr + (uintptr_t) pool->alignbytes - + (alignptr % (uintptr_t) pool->alignbytes)); /* There are lots of unallocated items left in this block. */ pool->unallocateditems = pool->itemsperblock; } @@ -2325,16 +2326,16 @@ struct memorypool *pool; #endif /* not ANSI_DECLARATORS */ { - unsigned long alignptr; + uintptr_t alignptr; /* Begin the traversal in the first block. */ pool->pathblock = pool->firstblock; /* Find the first item in the block. Increment by the size of (VOID *). */ - alignptr = (unsigned long) (pool->pathblock + 1); + alignptr = (uintptr_t) (pool->pathblock + 1); /* Align with item on an `alignbytes'-byte boundary. */ pool->pathitem = (VOID *) - (alignptr + (unsigned long) pool->alignbytes - - (alignptr % (unsigned long) pool->alignbytes)); + (alignptr + (uintptr_t) pool->alignbytes - + (alignptr % (uintptr_t) pool->alignbytes)); /* Set the number of items left in the current block. */ pool->pathitemsleft = pool->itemsfirstblock; } @@ -2362,7 +2363,7 @@ struct memorypool *pool; { VOID *newitem; - unsigned long alignptr; + uintptr_t alignptr; /* Stop upon exhausting the list of items. */ if (pool->pathitem == pool->nextitem) { @@ -2374,11 +2375,11 @@ struct memorypool *pool; /* Find the next block. */ pool->pathblock = (VOID **) *(pool->pathblock); /* Find the first item in the block. Increment by the size of (VOID *). */ - alignptr = (unsigned long) (pool->pathblock + 1); + alignptr = (uintptr_t) (pool->pathblock + 1); /* Align with item on an `alignbytes'-byte boundary. */ pool->pathitem = (VOID *) - (alignptr + (unsigned long) pool->alignbytes - - (alignptr % (unsigned long) pool->alignbytes)); + (alignptr + (uintptr_t) pool->alignbytes - + (alignptr % (uintptr_t) pool->alignbytes)); /* Set the number of items left in the current block. */ pool->pathitemsleft = pool->itemsperblock; } @@ -2430,16 +2431,16 @@ int subsegbytes; #endif /* not ANSI_DECLARATORS */ { - unsigned long alignptr; + uintptr_t alignptr; /* Set up `dummytri', the `triangle' that occupies "outer space." */ m->dummytribase = (triangle *) trimalloc(trianglebytes + m->triangles.alignbytes); /* Align `dummytri' on a `triangles.alignbytes'-byte boundary. */ - alignptr = (unsigned long) m->dummytribase; + alignptr = (uintptr_t) m->dummytribase; m->dummytri = (triangle *) - (alignptr + (unsigned long) m->triangles.alignbytes - - (alignptr % (unsigned long) m->triangles.alignbytes)); + (alignptr + (uintptr_t) m->triangles.alignbytes - + (alignptr % (uintptr_t) m->triangles.alignbytes)); /* Initialize the three adjoining triangles to be "outer space." These */ /* will eventually be changed by various bonding operations, but their */ /* values don't really matter, as long as they can legally be */ @@ -2459,10 +2460,10 @@ int subsegbytes; m->dummysubbase = (subseg *) trimalloc(subsegbytes + m->subsegs.alignbytes); /* Align `dummysub' on a `subsegs.alignbytes'-byte boundary. */ - alignptr = (unsigned long) m->dummysubbase; + alignptr = (uintptr_t) m->dummysubbase; m->dummysub = (subseg *) - (alignptr + (unsigned long) m->subsegs.alignbytes - - (alignptr % (unsigned long) m->subsegs.alignbytes)); + (alignptr + (uintptr_t) m->subsegs.alignbytes - + (alignptr % (uintptr_t) m->subsegs.alignbytes)); /* Initialize the two adjoining subsegments to be the omnipresent */ /* subsegment. These will eventually be changed by various bonding */ /* operations, but their values don't really matter, as long as they */ @@ -2819,7 +2820,7 @@ int number; { VOID **getblock; char *foundvertex; - unsigned long alignptr; + uintptr_t alignptr; int current; getblock = m->vertices.firstblock; @@ -2836,9 +2837,9 @@ int number; } /* Now find the right vertex. */ - alignptr = (unsigned long) (getblock + 1); - foundvertex = (char *) (alignptr + (unsigned long) m->vertices.alignbytes - - (alignptr % (unsigned long) m->vertices.alignbytes)); + alignptr = (uintptr_t) (getblock + 1); + foundvertex = (char *) (alignptr + (uintptr_t) m->vertices.alignbytes - + (alignptr % (uintptr_t) m->vertices.alignbytes)); return (vertex) (foundvertex + m->vertices.itembytes * (number - current)); } @@ -5882,7 +5883,7 @@ struct otri *searchtri; char *firsttri; struct otri sampletri; vertex torg, tdest; - unsigned long alignptr; + uintptr_t alignptr; REAL searchdist, dist; REAL ahead; long samplesperblock, totalsamplesleft, samplesleft; @@ -5954,11 +5955,11 @@ struct otri *searchtri; population = totalpopulation; } /* Find a pointer to the first triangle in the block. */ - alignptr = (unsigned long) (sampleblock + 1); + alignptr = (uintptr_t) (sampleblock + 1); firsttri = (char *) (alignptr + - (unsigned long) m->triangles.alignbytes - + (uintptr_t) m->triangles.alignbytes - (alignptr % - (unsigned long) m->triangles.alignbytes)); + (uintptr_t) m->triangles.alignbytes)); /* Choose `samplesleft' randomly sampled triangles in this block. */ do { diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 3833458..58b89b6 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -26,7 +26,7 @@ fn main() -> ExitCode { "windows" => ( repo_root.join(format!("scripts/windows/{action}.ps1")), vec![ - "powershell", + "pwsh", "-NoProfile", "-ExecutionPolicy", "Bypass",