diff --git a/.vscode/launch.json b/.vscode/launch.json index d13d7c6..ecbd67f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -3,33 +3,45 @@ "configurations": [ { "name": "FluidSim desktop", - "type": "node-terminal", + "type": "cppdbg", "request": "launch", - "command": "./build-desktop/fluidsim_desktop", + "program": "${workspaceFolder}/build-desktop/fluidsim_desktop", + "args": [], + "MIMode": "gdb", + "miDebuggerPath": "gdb", "windows": { - "command": ".\\build\\windows\\Release\\fluidsim_desktop.exe" + "program": "${workspaceFolder}/out/build/windows-gcc/fluidsim_desktop.exe", + "miDebuggerPath": "${env:LOCALAPPDATA}/FluidSimTools/mingw64/bin/gdb.exe" }, "cwd": "${workspaceFolder}", "preLaunchTask": "FluidSim: build desktop" }, { "name": "FluidSim two-stroke", - "type": "node-terminal", + "type": "cppdbg", "request": "launch", - "command": "./build-desktop/fluidsim_desktop --scenario two-stroke", + "program": "${workspaceFolder}/build-desktop/fluidsim_desktop", + "args": ["--scenario", "two-stroke"], + "MIMode": "gdb", + "miDebuggerPath": "gdb", "windows": { - "command": ".\\build\\windows\\Release\\fluidsim_desktop.exe --scenario two-stroke" + "program": "${workspaceFolder}/out/build/windows-gcc/fluidsim_desktop.exe", + "miDebuggerPath": "${env:LOCALAPPDATA}/FluidSimTools/mingw64/bin/gdb.exe" }, "cwd": "${workspaceFolder}", "preLaunchTask": "FluidSim: build desktop" }, { "name": "FluidSim Saab B205", - "type": "node-terminal", + "type": "cppdbg", "request": "launch", - "command": "./build-desktop/fluidsim_desktop --scenario b205", + "program": "${workspaceFolder}/build-desktop/fluidsim_desktop", + "args": ["--scenario", "b205"], + "MIMode": "gdb", + "miDebuggerPath": "gdb", "windows": { - "command": ".\\build\\windows\\Release\\fluidsim_desktop.exe --scenario b205" + "program": "${workspaceFolder}/out/build/windows-gcc/fluidsim_desktop.exe", + "miDebuggerPath": "${env:LOCALAPPDATA}/FluidSimTools/mingw64/bin/gdb.exe" }, "cwd": "${workspaceFolder}", "preLaunchTask": "FluidSim: build desktop" diff --git a/.vscode/settings.json b/.vscode/settings.json index 5ee88a7..f611f89 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { - "C_Cpp.debugShortcut": false, + "C_Cpp.debugShortcut": true, "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", "cmake.useCMakePresets": "always", + "cmake.configureOnOpen": true, "cmake.buildBeforeRun": true } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index fb941cd..72f166e 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -50,10 +50,7 @@ "kind": "build", "isDefault": true }, - "problemMatcher": [ - "$gcc", - "$msCompile" - ] + "problemMatcher": "$gcc" } ] } diff --git a/CMakeLists.txt b/CMakeLists.txt index e28e46b..cdc3ba7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,3 +157,5 @@ if(BUILD_TESTING) target_link_libraries(fluidsim_turbo_twin_regression PRIVATE fluidsim_core) add_test(NAME fluidsim_turbo_twin_regression COMMAND fluidsim_turbo_twin_regression) endif() + +include(cmake/MinGWRuntime.cmake) diff --git a/CMakePresets.json b/CMakePresets.json index 7ac3aa7..e14605b 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -9,33 +9,64 @@ { "name": "linux", "displayName": "Linux (Make, desktop)", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + }, "generator": "Unix Makefiles", "binaryDir": "${sourceDir}/build-desktop", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release", - "FLUIDSIM_BUILD_DESKTOP": "ON" + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "FLUIDSIM_BUILD_DESKTOP": "ON", + "BUILD_TESTING": "ON" } }, { "name": "windows", - "displayName": "Windows (Visual Studio 2022, desktop)", - "generator": "Visual Studio 17 2022", - "architecture": "x64", - "binaryDir": "${sourceDir}/build/windows", + "displayName": "Windows x64 (standalone GCC, desktop)", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/windows-gcc", + "environment": { + "PATH": "$penv{LOCALAPPDATA}/FluidSimTools/mingw64/bin;$penv{PATH}" + }, "cacheVariables": { - "FLUIDSIM_BUILD_DESKTOP": "ON" + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_C_COMPILER": "$penv{LOCALAPPDATA}/FluidSimTools/mingw64/bin/gcc.exe", + "CMAKE_CXX_COMPILER": "$penv{LOCALAPPDATA}/FluidSimTools/mingw64/bin/g++.exe", + "CMAKE_MAKE_PROGRAM": "$penv{LOCALAPPDATA}/FluidSimTools/mingw64/bin/ninja.exe", + "FLUIDSIM_BUILD_DESKTOP": "ON", + "BUILD_TESTING": "ON" } } ], "buildPresets": [ { "name": "linux", - "configurePreset": "linux" + "configurePreset": "linux", + "jobs": 8 }, { "name": "windows", "configurePreset": "windows", - "configuration": "Release" + "jobs": 8 + } + ], + "testPresets": [ + { + "name": "linux", + "configurePreset": "linux", + "output": { "outputOnFailure": true } + }, + { + "name": "windows", + "configurePreset": "windows", + "output": { "outputOnFailure": true } } ] } diff --git a/README.md b/README.md index 8f326e5..633cffa 100644 --- a/README.md +++ b/README.md @@ -17,48 +17,71 @@ a claim about all current models. Validation must include conservation of mass, momentum and energy, analytic acoustic modes, boundary reflection and radiation, thermodynamics and convergence under time/space refinement. -## Build (Windows / Visual Studio) +## Build and run (Windows and Linux) -Open a Visual Studio Developer PowerShell (or initialize the MSVC environment -with `VsDevCmd.bat`), then run from this directory: +Both platforms use CMake 3.28 or newer, Git, and a C++20 compiler. The presets +build with optimizations and debugging symbols (`RelWithDebInfo`). The first +desktop configure fetches SFML 3.0.2 if it is not installed and needs network +access. Visual Studio is not required. + +### Windows + +Install CMake and Git and ensure both are on PATH. Then run from this folder +in a regular PowerShell terminal: ```powershell -cmake -S . -B build -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -cmake --build build -ctest --test-dir build --output-on-failure +powershell -NoProfile -ExecutionPolicy Bypass -File .\tools\Setup-Windows.ps1 +cmake --preset windows +cmake --build --preset windows +ctest --preset windows +.\out\build\windows-gcc\fluidsim_desktop.exe ``` -## Build (Linux / Windows presets) +The setup script installs the portable [WinLibs](https://winlibs.com/) x64 UCRT +GCC bundle, including GDB and Ninja, in +`%LOCALAPPDATA%/FluidSimTools/mingw64`. It verifies the download's SHA-256 +checksum, removes the downloaded archive, and reuses an existing installation. +It needs no administrator access and does not change your system PATH. +Each built executable gets its GCC runtime DLLs copied beside it so it also +runs directly from a terminal or Explorer. -The repository includes CMake presets and VS Code tasks for the desktop app. -Install CMake 3.28 or newer, a C++20 compiler, and Make on Linux. On Windows, -install Visual Studio 2022 with the C++ desktop workload and CMake. Configure -and build from the project root with: +### Linux + +Install CMake 3.28 or newer, GCC with C++20 support, Make, GDB, and the SFML +development dependencies. For Ubuntu 24.04 / Debian with a suitable CMake: ```sh +sudo apt-get update +sudo apt-get install build-essential cmake git gdb pkg-config \ + libx11-dev libxrandr-dev libxcursor-dev libxi-dev libudev-dev \ + libgl1-mesa-dev libfreetype6-dev libflac-dev libogg-dev libvorbis-dev \ + fonts-dejavu-core cmake --preset linux -cmake --build --preset linux --target fluidsim_desktop +cmake --build --preset linux +ctest --preset linux +./build-desktop/fluidsim_desktop ``` -On Windows, use the `windows` preset in place of `linux`. CMake fetches SFML -3.0.2 if it is not installed, so the first desktop configure needs network -access. In VS Code, choose **FluidSim desktop** in Run and Debug; the -same configuration selects the Windows executable on Windows. The `.vscode` -build task configures and builds the desktop target before launching it. +Other distributions need equivalent packages; see +[SFML's source-build dependencies](https://www.sfml-dev.org/tutorials/3.0/getting-started/build-from-source/). +Running the desktop app requires a graphical session and an audio device. -The UI uses the optional Fira Code Nerd Font file from `../fonts` when present. -Without it, the simulation still runs but text labels are hidden. To bundle a -font from another location, configure with `-DFLUIDSIM_FONT_FILE=/path/to/font.ttf`. +### VS Code -The first configure fetches SFML 3.0.2 with CMake FetchContent for the desktop -window and audio stream. `-DFLUIDSIM_BUILD_DESKTOP=OFF` builds only the headless -runner and physics tests. +Install the recommended **C/C++** and **CMake Tools** extensions and open this +folder. Select the `windows` or `linux` CMake preset for your platform. Choose +**FluidSim desktop**, **FluidSim two-stroke**, or **FluidSim Saab B205** in +**Run and Debug**, then press F5 or the green Run button. The launch +configurations select the platform's executable and GDB automatically; the +build task configures and builds the desktop target before launch. +Ctrl+F5 runs without debugging; Ctrl+Shift+B builds the desktop target only. +The command-line builds above also build the CLI, benchmarks, and tests. -Run the interactive simulator with: - -```powershell -.\build\fluidsim_desktop.exe -``` +The UI uses `../fonts/FiraCodeNerdFont-Medium.ttf` when available, then tries +installed Windows or Linux system fonts. To bundle another font, add +`-DFLUIDSIM_FONT_FILE=/path/to/font.ttf` to the configure command. For a +headless build, add `-DFLUIDSIM_BUILD_DESKTOP=OFF`; this skips SFML and the +desktop's graphics/audio dependencies. Controls: hold `W` for throttle, `C` for clutch, `A` / `D` to shift, `Z` for neutral, up/down to adjust throttle target, left/right to adjust engine load, @@ -68,12 +91,14 @@ speed, and Space toggles real time. The command-line runner is useful for repeatable checks: ```powershell -.\build\fluidsim_cpp.exe --seconds 1 --throttle 0.35 -.\build\fluidsim_cpp.exe --scenario two-stroke --seconds 1 --throttle 0.3 -.\build\fluidsim_cpp.exe --scenario helmholtz --seconds 1 -.\build\fluidsim_cpp.exe --scenario pulse --seconds 1 +.\out\build\windows-gcc\fluidsim_cpp.exe --seconds 1 --throttle 0.35 +.\out\build\windows-gcc\fluidsim_cpp.exe --scenario two-stroke --seconds 1 --throttle 0.3 +.\out\build\windows-gcc\fluidsim_cpp.exe --scenario helmholtz --seconds 1 +.\out\build\windows-gcc\fluidsim_cpp.exe --scenario pulse --seconds 1 ``` +On Linux, use `./build-desktop/fluidsim_cpp` with the same arguments. + To compare deterministic checkpoints from the active C# twin scenario against the C++ port (energy variation is disabled for repeatability), build the C++ trace runner and execute the PowerShell comparison script: diff --git a/cmake/MinGWRuntime.cmake b/cmake/MinGWRuntime.cmake new file mode 100644 index 0000000..2f97cd6 --- /dev/null +++ b/cmake/MinGWRuntime.cmake @@ -0,0 +1,33 @@ +# Keep standalone GCC executables runnable without modifying the user's PATH. +if(NOT MINGW OR NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + return() +endif() + +get_filename_component(compiler_directory "${CMAKE_CXX_COMPILER}" DIRECTORY) +set(runtime_dlls) +foreach(runtime_names + "libgcc_s_seh-1.dll|libgcc_s_dw2-1.dll|libgcc_s_sjlj-1.dll" + "libstdc++-6.dll" + "libwinpthread-1.dll") + string(REPLACE "|" ";" runtime_names "${runtime_names}") + find_file(runtime_dll NAMES ${runtime_names} + PATHS "${compiler_directory}" NO_DEFAULT_PATH NO_CACHE) + if(NOT runtime_dll) + message(FATAL_ERROR "Cannot find MinGW runtime ${runtime_names} in ${compiler_directory}") + endif() + list(APPEND runtime_dlls "${runtime_dll}") + unset(runtime_dll) +endforeach() + +# This file is included after all project executables have been declared. +get_property(project_targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS) +foreach(project_target IN LISTS project_targets) + get_target_property(target_type "${project_target}" TYPE) + if(target_type STREQUAL "EXECUTABLE") + add_custom_command(TARGET "${project_target}" POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + ${runtime_dlls} "$" + COMMENT "Copying MinGW runtime DLLs for ${project_target}" + VERBATIM) + endif() +endforeach() diff --git a/tools/Setup-Windows.ps1 b/tools/Setup-Windows.ps1 new file mode 100644 index 0000000..9a0c481 --- /dev/null +++ b/tools/Setup-Windows.ps1 @@ -0,0 +1,49 @@ +# Install a portable GCC/GDB/Ninja toolchain for the Windows CMake preset. +# Visual Studio and administrator access are not required. +[CmdletBinding()] +param() + +$ErrorActionPreference = 'Stop' +$ProgressPreference = 'SilentlyContinue' +$toolRoot = Join-Path $env:LOCALAPPDATA 'FluidSimTools' +$compilerBin = Join-Path $toolRoot 'mingw64/bin' +$requiredTools = @('gcc.exe', 'g++.exe', 'gdb.exe', 'ninja.exe') +$missingTools = @($requiredTools | Where-Object { -not (Test-Path (Join-Path $compilerBin $_)) }) +if ($missingTools.Count -eq 0) { + Write-Output "Toolchain is already installed in $compilerBin" + & (Join-Path $compilerBin 'g++.exe') --version + exit $LASTEXITCODE +} + +# Use the GCC-only x64 UCRT build published by WinLibs, excluding the larger LLVM bundle. +$release = Invoke-RestMethod 'https://api.github.com/repos/brechtsanders/winlibs_mingw/releases/latest' +$archive = $release.assets | Where-Object { + $_.name -match '^winlibs-x86_64-posix-seh-gcc-.*ucrt.*\.zip$' -and $_.name -notmatch 'llvm' +} | Select-Object -First 1 +if (-not $archive) { throw 'The latest WinLibs release has no GCC-only x64 UCRT ZIP.' } +$checksumAsset = $release.assets | Where-Object { $_.name -eq "$($archive.name).sha256" } | Select-Object -First 1 +if (-not $checksumAsset) { throw 'The WinLibs archive has no SHA-256 checksum.' } + +New-Item -ItemType Directory -Force $toolRoot | Out-Null +$downloadPath = Join-Path ([IO.Path]::GetTempPath()) ("fluidsim-toolchain-" + [guid]::NewGuid() + '.zip') +try { + Write-Output "Downloading $($archive.name) ..." + Invoke-WebRequest -UseBasicParsing $archive.browser_download_url -OutFile $downloadPath + $checksumText = (Invoke-WebRequest -UseBasicParsing $checksumAsset.browser_download_url).Content + if ($checksumText -is [byte[]]) { $checksumText = [Text.Encoding]::UTF8.GetString($checksumText) } + $expectedHash = [regex]::Match([string]$checksumText, '(?i)\b[0-9a-f]{64}\b').Value + if (-not $expectedHash -or (Get-FileHash $downloadPath -Algorithm SHA256).Hash -ne $expectedHash) { + throw 'The toolchain archive failed SHA-256 verification.' + } + Write-Output "Extracting to $toolRoot ..." + & tar.exe -xf $downloadPath -C $toolRoot + if ($LASTEXITCODE -ne 0) { throw 'Toolchain extraction failed.' } + foreach ($tool in $requiredTools) { + if (-not (Test-Path (Join-Path $compilerBin $tool))) { throw "Missing tool after extraction: $tool" } + } + & (Join-Path $compilerBin 'g++.exe') --version + if ($LASTEXITCODE -ne 0) { throw 'The installed compiler could not start.' } + Write-Output 'Ready. In VS Code, select FluidSim desktop in Run and Debug, then press F5.' +} finally { + if (Test-Path -LiteralPath $downloadPath) { Remove-Item -LiteralPath $downloadPath } +}