|
FhSim
3.1.0
Marine systems simulation
|
| ID | 0221 |
| Class | BUG |
| Severity | 2 |
| Status | ready |
Models: —
Progress: step 1 done in 53bd9ba0 (ThisExePath() macOS branch; ResolveFhSimPaths() calls it). DllWrapperCommon.h builds .dylib names (d0a7e559). Still open: steps 3-5, the other .so spellings (LicenseManager.cpp:266,287, FhsimSlaveCoreLifecycle.cpp:204, ResourceBundler.cpp:183-184, tests/fmu/FMITestCommon.h:163, tests/cmake/fmu_export.cmake:298, tests/CMakeLists.txt:486,511,585), and the macOS FMU test platform folders (darwin64, aarch64-darwin; moved here from FHSIM-0206).
Found: 2026-10-26 portability audit of da1b45b3
Rule: doc/agents/portability.md ("Code"): OS headers, direct OS calls and inline platform #ifdef belong in one helper .cpp that implements all platforms. About 144 scan hits in 35 files fall outside such a helper. The two confirmed defects on macOS first.
Confirmed (BUG): the executable path has no macOS branch.
src/simobject/utilities/SystemUtils.cpp:87-106 ThisExePath() has a _WIN32 and a __linux__ branch, and #else is #error ThisExePath() is not implemented for the current platform yet. The file is listed unconditionally in src/simobject/utilities/CMakeLists.txt:7, so the simobject library does not compile with Apple Clang.src/fmu/fhFMUexport/ResourceBundler.cpp:36-47 ResolveFhSimPaths() is a second, private copy: #ifdef WIN32 uses GetModuleFileName, the #else branch calls readlink("/proc/self/exe", ...). /proc/self/exe does not exist on macOS, so the call fails, it prints "Unable to resolve executable path" and returns an empty FhSimPaths. The FMU exporter (src/fmu/fhFMUexport/fhFMUExport.cpp:59) cannot find its install root there. It also includes <windows.h>/<unistd.h> itself (ResourceBundler.cpp:22-25).Duplicate or inline platform code (ARCHITECTURE), by file (hits in brackets):
dlopen/LoadLibrary/GetProcAddress), each with its own <windows.h>/<dlfcn.h> includes: src/engine/io/PluginStateObserver.cpp (11), src/engine/license/LicenseManager.cpp (14), include/fhsim/license/LicenseManager.h (4), src/fmu/core/FhsimDllBridge.cpp (6) and FhsimDllBridge.h (4; FhsimDllBridge.h:20 defines LoadLibrary(path) as dlopen), tests/dll/DllAbi_test.cpp (8), tests/fmu/FMITestCommon.h (13), tests/io/install_consumer/ConsumerHost.cpp (10).<windows.h>, <unistd.h> or <dlfcn.h>: include/fhsim/DllLib.h (4), include/fhsim/dll/DllEntry.h (5), include/fhsim/dll/DllWrapper.h (3), include/fhsim/visual/dll/VisDllEntry.h (5), include/fhsim/visual/dll/FhVisDllWrapper.h (2), include/fhsim/PrintDuringExec.h (2), include/fhsim/NFhSim.h:45 (PATH_DELIMITER), include/fhsim/IStateObserverPlugin.h:86.#ifdef _WIN32 around CRT or POSIX calls: src/simobject/utilities/PrintDuringExec.cpp (7), src/simobject/utilities/InputReader.cpp (4, incl. <sys/stat.h>), src/engine/core/Parse.cpp (2), src/engine/diagnostics/DiagnosticSink.cpp (3, isatty/_isatty), src/engine/integrator/FhIntegrator.cpp (2), src/engine/integrator/IntegratorData.cpp (3, getenv_s, fopen_s), src/engine/core/SimulationBuilder.cpp:41 (GetSystemInfo/sysconf), src/fmu/fhFMUexport/FmuGenerationTimestamp.cpp:13 (gmtime_s/gmtime_r), src/fmu/core/FhsimSlaveCoreLifecycle.cpp (1), src/visual/renderer/FhVisualization.cpp:105-109 and src/visual/ui/FhVisualizationSDL.cpp:386-390 (setenv, guarded by #ifndef _WIN32).tests/fmu/FMITestHelper.h (5), tests/fmu/FMI3TestHelper.h (6; setenv/unsetenv of LD_LIBRARY_PATH), tests/fmu/partial_export/PartialExportLibrary.cpp:9 (export macro), tests/model/AddressSpaceLimit.h (2; see FHSIM-0222), tests/model/DenseJacobianLimit_test.cpp:145 and tests/core/fhUtilities/BatchPool_test.cpp:231 (guarded, tests skip or are absent elsewhere), tests/fmu/FmuArchiveWriter_test.cpp:15 (<unistd.h>; built under if(UNIX) only).simobject stops at the #error; if it were fixed alone, the FMU exporter still fails at run time.ThisExePath must each be fixed for a new platform or a Windows loader rule (ADR 0012 search path).ThisExePath() (_NSGetExecutablePath, or std::filesystem::canonical of the result) in SystemUtils.cpp, and make ResourceBundler::ResolveFhSimPaths() call ThisExePath().portability-ok: <reason> (acceptable, not defects; they implement all platforms in one place): src/simobject/utilities/SystemUtils.cpp, src/engine/dll/common/DllWrapperCommon.h and src/engine/dll/wrapper/DllWrapper.cpp (15+1 hits), src/visual/dll/wrapper/FhVisDll.cpp (calls that helper), src/apps/fhPlot/BrowserLauncher.cpp (Windows, macOS and Linux/WSL branches), src/fmu/fhFMUexport/FmuBinaryLayout.cpp (Windows, Apple, other), src/visual/renderer/WindowSystem.cpp (all three), include/fhsim/testtools/NullLogFile.h. Compiler-only guards (_MSC_VER in src/engine/core/BatchPool.cpp, OgreDebugGuard.h, IModelVisualizationHooks.h) are not OS code.PluginStateObserver.cpp, LicenseManager.cpp, FhsimDllBridge.cpp and the test loaders through the one loader (DllWrapperCommon.h, or fhlib DynamicLib)._WIN32 blocks in the lists above into one PlatformUtils.cpp (env, isatty, CPU count, gmtime, set-env) behind small functions, or replace them with standard calls (std::getenv, std::fopen, std::thread::hardware_concurrency).<windows.h> out of installed headers where the include is only needed by an inline function.Step 1 is the fix for the BUG; steps 3-5 are the architecture work and can be split.
ThisExePath() equals std::filesystem::canonical(argv[0]) for the test binary (runs on all platforms).ResourceBundler::ResolveFhSimPaths().valid is true (tests/fmu/ResourceBundler_test.cpp:106 already calls it).tools/portability_scan.py <worktree> <base> --all with no unmarked hit outside the helper files.Step 1 is low. Steps 3-5 touch the plugin and licence loaders and public headers (contract C-list: header names other repos use), so each needs a Windows (MSVC) build before merge.