diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix
index 559f59b5bf29..b19700606e67 100644
--- a/maintainers/maintainer-list.nix
+++ b/maintainers/maintainer-list.nix
@@ -24738,10 +24738,10 @@
name = "Travis Whitton";
};
tmplt = {
- email = "tmplt@dragons.rocks";
+ email = "v@tmplt.dev";
github = "tmplt";
githubId = 6118602;
- name = "Viktor";
+ name = "Viktor Sonesten";
};
tmssngr = {
email = "nixpkgs@syntevo.com";
diff --git a/pkgs/by-name/fm/fmi-reference-fmus/package.nix b/pkgs/by-name/fm/fmi-reference-fmus/package.nix
new file mode 100644
index 000000000000..e41e1223d8b9
--- /dev/null
+++ b/pkgs/by-name/fm/fmi-reference-fmus/package.nix
@@ -0,0 +1,47 @@
+{
+ lib,
+ stdenv,
+ fetchFromGitHub,
+ cmake,
+
+ # Build the FMUs following the latest FMI standard
+ FMIVersion ? 3,
+}:
+
+# C.f.
+assert lib.asserts.assertMsg (
+ FMIVersion >= 1 && FMIVersion <= 3
+) "FMIVersion must be a valid FMI specification standard: 1, 2, or 3; not ${toString FMIVersion}";
+
+# NB: this derivation does not package the fmusim executables, only
+# the FMUs.
+stdenv.mkDerivation (finalAttrs: {
+ pname = "reference-fmus";
+ version = "0.0.38";
+ src = fetchFromGitHub {
+ owner = "modelica";
+ repo = finalAttrs.pname;
+ rev = "v${finalAttrs.version}";
+ hash = "sha256-FeDKYcm9K670q1FGqy41Tp2Ag8p2JidH4z78zpHOngw=";
+ };
+
+ nativeBuildInputs = [ cmake ];
+
+ cmakeFlags = [
+ "-DFMI_VERSION=${toString FMIVersion}"
+ (lib.cmakeBool "WITH_FMUSIM" false)
+ ];
+ CFLAGS = lib.optionalString (FMIVersion == 3) "-Wno-stringop-truncation";
+
+ meta = {
+ # CMakeLists.txt explicitly states support for aarch64-darwin, but
+ # the build fails in a Nix environment. C.f.
+ # .
+ broken = with stdenv.hostPlatform; isAarch64 && isDarwin;
+ description = "Functional Mock-up Units for development, testing and debugging";
+ homepage = "https://github.com/modelica/Reference-FMUs";
+ license = lib.licenses.bsd2;
+ maintainers = with lib.maintainers; [ tmplt ];
+ platforms = lib.platforms.all;
+ };
+})
diff --git a/pkgs/by-name/rp/rpclib/package.nix b/pkgs/by-name/rp/rpclib/package.nix
new file mode 100644
index 000000000000..d055c060cce9
--- /dev/null
+++ b/pkgs/by-name/rp/rpclib/package.nix
@@ -0,0 +1,26 @@
+{
+ lib,
+ stdenv,
+ fetchFromGitHub,
+ cmake,
+}:
+stdenv.mkDerivation (finalAttrs: {
+ pname = "rpclib";
+ version = "2.3.0";
+
+ src = fetchFromGitHub {
+ owner = "rpclib";
+ repo = "rpclib";
+ rev = "v${finalAttrs.version}";
+ sha256 = "0dlbkl47zd2fkxwbn93w51wmvfr8ssp4zribn5wi4cpiky44a4g9";
+ };
+
+ nativeBuildInputs = [ cmake ];
+
+ meta = {
+ description = "RPC library for C++, providing both a client and server implementation";
+ homepage = "https://github.com/rpclib/rpclib/";
+ license = lib.licenses.mit;
+ maintainers = with lib.maintainers; [ tmplt ];
+ };
+})
diff --git a/pkgs/development/python-modules/fmpy/0001-gui-override-Qt6-libexec-path.patch b/pkgs/development/python-modules/fmpy/0001-gui-override-Qt6-libexec-path.patch
new file mode 100644
index 000000000000..80c4933df6ad
--- /dev/null
+++ b/pkgs/development/python-modules/fmpy/0001-gui-override-Qt6-libexec-path.patch
@@ -0,0 +1,10 @@
+--- a/src/fmpy/gui/__init__.py
++++ b/src/fmpy/gui/__init__.py
+@@ -15,6 +15,7 @@ def compile_resources():
+
+ if os.name == 'posix':
+ pyside_dir = pyside_dir / 'Qt' / 'libexec'
++ pyside_dir = Path("@qt6libexec@")
+
+ gui_dir = Path(__file__).parent
+ forms_dir = gui_dir / 'forms'
diff --git a/pkgs/development/python-modules/fmpy/0002-sundials-override-shared-object-paths.patch b/pkgs/development/python-modules/fmpy/0002-sundials-override-shared-object-paths.patch
new file mode 100644
index 000000000000..c4364763d09c
--- /dev/null
+++ b/pkgs/development/python-modules/fmpy/0002-sundials-override-shared-object-paths.patch
@@ -0,0 +1,10 @@
+--- a/src/fmpy/sundials/libraries.py
++++ b/src/fmpy/sundials/libraries.py
+@@ -9,6 +9,7 @@ if platform_tuple == 'aarch64_darwin':
+ library_dir = library_dir / 'x86_64-darwin'
+ else:
+ library_dir = library_dir / platform_tuple
++library_dir = Path("@cvode@") / 'lib'
+
+ # load SUNDIALS shared libraries
+ sundials_nvecserial = cdll.LoadLibrary(str(library_dir / f'sundials_nvecserial{sharedLibraryExtension}'))
diff --git a/pkgs/development/python-modules/fmpy/0003-remoting-client-create-lockfile-with-explicit-r-w.patch b/pkgs/development/python-modules/fmpy/0003-remoting-client-create-lockfile-with-explicit-r-w.patch
new file mode 100644
index 000000000000..35c96a25a72d
--- /dev/null
+++ b/pkgs/development/python-modules/fmpy/0003-remoting-client-create-lockfile-with-explicit-r-w.patch
@@ -0,0 +1,44 @@
+From bf0f536776d2554a99af3fbcc08fa4c43bba1c7e Mon Sep 17 00:00:00 2001
+From: Viktor Sonesten
+Date: Mon, 5 May 2025 17:40:10 +0200
+Subject: [PATCH] remoting/client: create lockfile with explicit r+w
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This fixes the compilation error below, which is triggered in hardened
+build environments (e.g. NixOS):
+
+ > In file included from /nix/store/aaaaaaaa-glibc-2.40-66-dev/include/fcntl.h:341,
+ > from /build/source/native/remoting/client_tcp.cpp:12:
+ > In function ‘int open(const char*, int, ...)’,
+ > inlined from ‘void* fmi2Instantiate(fmi2String, fmi2Type, fmi2String, fmi2String, const fmi2CallbackFunctions*, fmi2Boolean, fmi2Boolean)’ at /build/source/native/remoting/client_tcp.cpp:217:28:
+ > /nix/store/aaaaaaaa-glibc-2.40-66-dev/include/bits/fcntl2.h:52:31: error: call to ‘__open_missing_mode’ declared with attribute error: open with O_CREAT or O_TMPFILE in second argument needs 3 arguments
+ > 52 | __open_missing_mode ();
+ > | ~~~~~~~~~~~~~~~~~~~~^~
+
+In short: O_CREAT without explicit mode params may generate a file
+with suid/sgid. Error triggered by _FORTIFY_SOURCE. This commit
+explicitly creates the file with read/write permissions for the file
+owner.
+
+Adopted from https://github.com/facebook/hhvm/issues/168
+---
+ native/remoting/client_tcp.cpp | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/native/remoting/client_tcp.cpp b/native/remoting/client_tcp.cpp
+index 708c895..7acbc80 100644
+--- a/native/remoting/client_tcp.cpp
++++ b/native/remoting/client_tcp.cpp
+@@ -214,7 +214,7 @@ fmi2Component fmi2Instantiate(fmi2String instanceName, fmi2Type fmuType, fmi2Str
+ // create lock file
+ const char *lockFilePath = tempnam(NULL, "");
+
+- int lockFile = open(lockFilePath, O_CREAT | O_EXCL);
++ int lockFile = open(lockFilePath, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+
+ if (lockFile == -1) {
+ s_logger(s_componentEnvironment, instanceName, fmi2Error, "error", "Failed to create lock file %s.", lockFilePath);
+--
+2.47.1
diff --git a/pkgs/development/python-modules/fmpy/default.nix b/pkgs/development/python-modules/fmpy/default.nix
new file mode 100644
index 000000000000..fd66b5f34bd2
--- /dev/null
+++ b/pkgs/development/python-modules/fmpy/default.nix
@@ -0,0 +1,232 @@
+{
+ lib,
+ stdenv,
+ fetchurl,
+ buildPythonPackage,
+ fetchFromGitHub,
+ pythonOlder,
+ cmake,
+ sundials,
+ lapack,
+ attrs,
+ lark,
+ lxml,
+ rpclib,
+ msgpack,
+ numpy,
+ scipy,
+ pytz,
+ dask,
+ requests,
+ matplotlib,
+ pyqtgraph,
+ notebook,
+ plotly,
+ hatchling,
+ pyside6,
+ jinja2,
+ flask,
+ dash,
+ dash-bootstrap-components,
+ qt6,
+ fmpy,
+ runCommand,
+ enableRemoting ? true,
+ versionCheckHook,
+ fmi-reference-fmus,
+ replaceVars,
+}:
+buildPythonPackage rec {
+ pname = "fmpy";
+ version = "0.3.23";
+ disabled = pythonOlder "3.10";
+ pyproject = true;
+
+ # Bumping version? Make sure to look through the commit history for
+ # bumped native/build_cvode.py pins (see above).
+ src = fetchFromGitHub {
+ owner = "CATIA-Systems";
+ repo = "FMPy";
+ rev = "v${version}";
+ fetchSubmodules = true;
+ hash = "sha256-4jBYOymurGCbjT0WQjIRNsztwOXBYVTGLdc4kNSTOZw=";
+ };
+
+ nativeBuildInputs = [
+ cmake
+ ];
+
+ build-system = [
+ hatchling
+ ];
+
+ dependencies = [
+ pyqtgraph
+ pyside6
+ attrs
+ lark
+ lxml
+ msgpack
+ numpy
+ scipy
+ pytz
+ dask
+ requests
+ matplotlib
+ pyqtgraph
+ notebook
+ plotly
+ rpclib
+ fmpy.passthru.cvode
+ pyside6
+ jinja2
+ flask
+ dash
+ dash-bootstrap-components
+ ];
+
+ dontUseCmakeConfigure = true;
+ dontUseCmakeBuildDir = true;
+
+ patches = [
+ (replaceVars ./0001-gui-override-Qt6-libexec-path.patch {
+ qt6libexec = "${qt6.qtbase}/libexec/";
+ })
+ (replaceVars ./0002-sundials-override-shared-object-paths.patch {
+ inherit (fmpy.passthru) cvode;
+ })
+ # Upstream does not accept pull requests for unknown legal
+ # reasons, see:
+ # .
+ # Thus a fix is vendored here until patched by maintainer. C.f.
+ #
+ ./0003-remoting-client-create-lockfile-with-explicit-r-w.patch
+ ];
+
+ # Make forced includes of other systems' artifacts optional in order
+ # to pass build (otherwise vendored upstream from CI)
+ postPatch = ''
+ sed --in-place 's/force-include/source/g' pyproject.toml
+ '';
+
+ # Don't run upstream build scripts as they are too specialized.
+ # cvode is already built, so we only need to build native binaries.
+ # We run these cmake builds and then run the standard
+ # buildPythonPackage phases.
+ preBuild =
+ ''
+ cmakeFlags="-S native/src -B native/src/build -D CVODE_INSTALL_DIR=${passthru.cvode}"
+ cmakeConfigurePhase
+ cmake --build native/src/build --config Release
+ ''
+ + lib.optionalString (enableRemoting && stdenv.isLinux) ''
+ # reimplementation of native/build_remoting.py
+ cmakeFlags="-S native/remoting -B remoting/linux64 -D RPCLIB=${rpclib}"
+ cmakeConfigurePhase
+ cmake --build remoting/linux64 --config Release
+ ''
+ # C.f. upstream build-wheel CI job
+ + ''
+ python native/copy_sources.py
+
+ # reimplementation of native/compile_resources.py
+ pushd src/
+ python -c "from fmpy.gui import compile_resources; compile_resources()"
+ popd
+ '';
+
+ pythonImportsCheck = [
+ "fmpy"
+ "fmpy.cross_check"
+ "fmpy.cswrapper"
+ "fmpy.examples"
+ "fmpy.fmucontainer"
+ "fmpy.logging"
+ "fmpy.gui"
+ "fmpy.gui.generated"
+ "fmpy.ssp"
+ "fmpy.sundials"
+ ];
+
+ nativeInstallCheckInputs = [
+ versionCheckHook
+ ];
+
+ passthru = {
+ # From sundials, build only the CVODE solver. C.f.
+ # src/native/build_cvode.py
+ cvode =
+ (sundials.overrideAttrs (prev: {
+ # hash copied from native/build_cvode.py
+ version = "5.3.0";
+ src = fetchurl {
+ url = "https://github.com/LLNL/sundials/releases/download/v5.3.0/sundials-5.3.0.tar.gz";
+ sha256 = "88dff7e11a366853d8afd5de05bf197a8129a804d9d4461fb64297f1ef89bca7";
+ };
+
+ cmakeFlags =
+ prev.cmakeFlags
+ ++ lib.mapAttrsToList (option: enable: lib.cmakeBool option enable) {
+ # only build the CVODE solver
+ BUILD_CVODE = true;
+
+ BUILD_CVODES = false;
+ BUILD_ARKODE = false;
+ BUILD_IDA = false;
+ BUILD_IDAS = false;
+ BUILD_KINSOL = false;
+ };
+
+ # FMPy searches for sundials without the "lib"-prefix; strip it
+ # and symlink the so-files into existance.
+ postFixup = ''
+ pushd $out/lib
+ for so in *.so; do
+ ln --verbose --symbolic $so ''${so#lib}
+ done
+ popd
+ '';
+ })).override
+ {
+ lapackSupport = false;
+ lapack.isILP64 = stdenv.hostPlatform.is64bit;
+ blas = lapack;
+ kluSupport = false;
+ };
+
+ # Simulate reference FMUs from
+ # .
+ #
+ # Just check that the execution passes; don't verify any numerical
+ # results, but save them in case of future or manual check use.
+ tests.simulate-reference-fmus = runCommand "${pname}-simulate-reference-fmus" { } ''
+ mkdir $out
+ # NB(find ! -name): Clocks.fmu is marked TODO upstream and is of a
+ # FMI type that FMPy doesn't support currently (ModelExchange)
+ for fmu in $(find ${fmi-reference-fmus}/*.fmu ! -name "Clocks.fmu"); do
+ name=$(basename $fmu)
+ echo "--- START $name ---"
+ ${fmpy}/bin/fmpy simulate $fmu \
+ --fmi-logging \
+ --output-file $out/$name.csv \
+ | tee $out/$name.out
+ done
+ '';
+ };
+
+ meta = {
+ # A logging.dylib is built but is not packaged correctly so as to
+ # be found. C.f.
+ #
+ broken = stdenv.hostPlatform.isDarwin;
+ description = "Simulate Functional Mockup Units (FMUs) in Python";
+ homepage = "https://github.com/CATIA-Systems/FMPy";
+ license = lib.licenses.bsd2;
+ maintainers = with lib.maintainers; [ tmplt ];
+ # Supported platforms are not exhaustively and explicitly stated,
+ # but inferred from the artifacts that are vendored in upstream CI
+ # builds. C.f.
+ #
+ platforms = lib.platforms.x86_64 ++ [ "i686-windows" ];
+ };
+}
diff --git a/pkgs/top-level/python-packages.nix b/pkgs/top-level/python-packages.nix
index 332fde0a1867..c0eb34fc8f6f 100644
--- a/pkgs/top-level/python-packages.nix
+++ b/pkgs/top-level/python-packages.nix
@@ -5171,6 +5171,8 @@ self: super: with self; {
flynt = callPackage ../development/python-modules/flynt { };
+ fmpy = callPackage ../development/python-modules/fmpy { };
+
fnllm = callPackage ../development/python-modules/fnllm { };
fnv-hash-fast = callPackage ../development/python-modules/fnv-hash-fast { };