From 03fc1da756d26944d66cff8997d712521b1fde2c Mon Sep 17 00:00:00 2001 From: SulfurNitride Date: Wed, 11 Mar 2026 07:11:59 -0500 Subject: Remove FUSE passthrough, flake.nix, and stop archiving build output FUSE passthrough caused Wine DLL loading failures (ERROR_NOACCESS 0x3E6) for all file types, not just executables. Root cause is a kernel-level incompatibility between FUSE passthrough I/O and Wine's file access patterns. Removed all passthrough code, the passthrough helper binary, libcap dependency, and the settings UI toggle. Also removed flake.nix (unused with Docker build system) and stopped tar.gz'ing the build output since GitHub zips release assets automatically. Co-Authored-By: Claude Opus 4.6 --- flake.nix | 578 -------------------------------------------------------------- 1 file changed, 578 deletions(-) delete mode 100644 flake.nix (limited to 'flake.nix') diff --git a/flake.nix b/flake.nix deleted file mode 100644 index dc3cc86..0000000 --- a/flake.nix +++ /dev/null @@ -1,578 +0,0 @@ -{ - description = "Fluorine Manager — Mod Organizer 2 for Linux"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - }; - - outputs = { self, nixpkgs }: - let - system = "x86_64-linux"; - pkgs = import nixpkgs { inherit system; }; - - version = "0.1.0"; - pythonVersion = "3.13"; - portablePythonVersion = "3.13.9"; - - # ── libloot (not in nixpkgs — build from source) ── - libloot = pkgs.stdenv.mkDerivation { - pname = "libloot"; - version = "0.29.0"; - - src = pkgs.fetchFromGitHub { - owner = "loot"; - repo = "libloot"; - rev = "master"; - hash = "sha256-ay8ZA5ANXc5ylKOzr66rjQc1/LclW2+0CObtjprn0ms="; - fetchSubmodules = true; - }; - - nativeBuildInputs = with pkgs; [ cmake ninja ]; - - cmakeDir = "../cpp"; - cmakeFlags = [ - "-DBUILD_SHARED_LIBS=ON" - "-DLIBLOOT_INSTALL_DOCS=OFF" - "-DBUILD_TESTING=OFF" - ]; - - postInstall = '' - mkdir -p $out/lib/pkgconfig - cat > $out/lib/pkgconfig/libloot.pc </dev/null || true - - # ── Boost (from nix store) ── - for boost_lib in ${pkgs.boost}/lib/libboost_program_options.so* \ - ${pkgs.boost}/lib/libboost_thread.so*; do - [ -f "$boost_lib" ] && cp -Lf "$boost_lib" $out/opt/fluorine-manager/lib/ - done - - # ── Qt6 plugins ── - QT6_PLUGIN_DIR="${pkgs.qt6.qtbase}/lib/qt-6/plugins" - for plugin_type in platforms tls networkinformation styles \ - imageformats iconengines xcbglintegrations \ - egldeviceintegrations; do - [ -d "$QT6_PLUGIN_DIR/$plugin_type" ] && \ - cp -a "$QT6_PLUGIN_DIR/$plugin_type" $out/opt/fluorine-manager/qt6plugins/ - done - # Wayland plugins. - WAYLAND_PLUGIN_DIR="${pkgs.qt6.qtwayland}/lib/qt-6/plugins" - for plugin_type in wayland-shell-integration \ - wayland-decoration-client wayland-graphics-integration-client; do - [ -d "$WAYLAND_PLUGIN_DIR/$plugin_type" ] && \ - cp -a "$WAYLAND_PLUGIN_DIR/$plugin_type" $out/opt/fluorine-manager/qt6plugins/ - done - # SVG plugin. - SVG_PLUGIN_DIR="${pkgs.qt6.qtsvg}/lib/qt-6/plugins" - [ -d "$SVG_PLUGIN_DIR/imageformats" ] && \ - cp -a "$SVG_PLUGIN_DIR/imageformats"/. $out/opt/fluorine-manager/qt6plugins/imageformats/ - - # ── Bundle shared library dependencies ── - SKIP_PATTERN="linux-vdso|ld-linux|libc\.so|libm\.so|libdl\.so|librt\.so|libpthread|libresolv|libnss|libgcc_s|libstdc\+\+" - SKIP_PATTERN="$SKIP_PATTERN|libGL\.so|libEGL|libGLX|libGLdispatch|libdrm|libvulkan|libX11|libxcb|libwayland|libxkbcommon" - SKIP_PATTERN="$SKIP_PATTERN|libpython" - - collect_and_bundle() { - ldd "$1" 2>/dev/null | grep "=>" | awk '{print $3}' | grep "^/" | while read -r dep; do - dep_name="$(basename "$dep")" - echo "$dep_name" | grep -qE "$SKIP_PATTERN" && continue - [ -f "$out/opt/fluorine-manager/lib/$dep_name" ] && continue - cp -Lf "$dep" "$out/opt/fluorine-manager/lib/" 2>/dev/null || true - done - } - collect_and_bundle "$out/opt/fluorine-manager/ModOrganizer-core" - find "$out/opt/fluorine-manager/plugins" -name "*.so" -exec bash -c 'ldd "$1" 2>/dev/null | grep "=>" | awk "{print \$3}" | grep "^/" | while read dep; do - dep_name="$(basename "$dep")" - echo "$dep_name" | grep -qE "'"$SKIP_PATTERN"'" && continue - [ -f "'"$out"'/opt/fluorine-manager/lib/$dep_name" ] && continue - cp -Lf "$dep" "'"$out"'/opt/fluorine-manager/lib/" 2>/dev/null || true - done' _ {} \; - find "$out/opt/fluorine-manager/lib" -name "*.so*" -exec bash -c 'ldd "$1" 2>/dev/null | grep "=>" | awk "{print \$3}" | grep "^/" | while read dep; do - dep_name="$(basename "$dep")" - echo "$dep_name" | grep -qE "'"$SKIP_PATTERN"'" && continue - [ -f "'"$out"'/opt/fluorine-manager/lib/$dep_name" ] && continue - cp -Lf "$dep" "'"$out"'/opt/fluorine-manager/lib/" 2>/dev/null || true - done' _ {} \; - [ -f "$out/opt/fluorine-manager/lootcli" ] && collect_and_bundle "$out/opt/fluorine-manager/lootcli" - rm -f "$out/opt/fluorine-manager/lib"/libpython*.so* 2>/dev/null || true - - # ── Portable Python runtime ── - PP_SRC=$(echo ${portable-python}/python-headless-*) - if [ ! -d "$PP_SRC" ]; then - PP_SRC="${portable-python}" - fi - cp -a "$PP_SRC" $out/opt/fluorine-manager/python - chmod -R u+w $out/opt/fluorine-manager/python - - # Trim portable Python. - PP_STDLIB="$out/opt/fluorine-manager/python/lib/python$PY_MM" - rm -rf "$PP_STDLIB/test" "$PP_STDLIB/idlelib" "$PP_STDLIB/tkinter" \ - "$PP_STDLIB/turtledemo" "$out/opt/fluorine-manager/python/include" \ - "$out/opt/fluorine-manager/python/share" 2>/dev/null || true - find "$out/opt/fluorine-manager/python" -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true - - # Patch portable Python SONAME. - PP_LIBPY="$out/opt/fluorine-manager/python/lib/libpython$PY_MM.so" - if [ -f "$PP_LIBPY" ]; then - CURRENT_SONAME="$(readelf -d "$PP_LIBPY" 2>/dev/null | grep SONAME | sed 's/.*\[//' | sed 's/\]//')" - if [ "$CURRENT_SONAME" = "libpython$PY_MM.so" ]; then - patchelf --set-soname "libpython$PY_MM.so.1.0" "$PP_LIBPY" - mv "$PP_LIBPY" "$PP_LIBPY.1.0" - ln -sf "libpython$PY_MM.so.1.0" "$PP_LIBPY" - fi - [ ! -e "$PP_LIBPY.1.0" ] && ln -sf "libpython$PY_MM.so" "$PP_LIBPY.1.0" - fi - - # Bundle PyQt6 into portable Python site-packages. - PYSITE="$out/opt/fluorine-manager/python/lib/python$PY_MM/site-packages" - mkdir -p "$PYSITE" - for search_dir in ${pkgs.python313Packages.pyqt6}/lib/python${pythonVersion}/site-packages; do - if [ -d "$search_dir/PyQt6" ]; then - cp -a "$search_dir/PyQt6" "$PYSITE/" - [ -d "$search_dir/PyQt6_sip" ] && cp -a "$search_dir/PyQt6_sip" "$PYSITE/" - [ -d "$search_dir/sip" ] && cp -a "$search_dir/sip" "$PYSITE/" - break - fi - done - - # Build-tree Python plugin payload. - [ -d src/src/python ] && cp -a src/src/python/. $out/opt/fluorine-manager/python/ - - # ── icoutils (for .exe icon extraction) ── - cp -f ${pkgs.icoutils}/bin/wrestool $out/opt/fluorine-manager/ 2>/dev/null || true - cp -f ${pkgs.icoutils}/bin/icotool $out/opt/fluorine-manager/ 2>/dev/null || true - - # ── Strip binaries ── - strip --strip-unneeded $out/opt/fluorine-manager/ModOrganizer-core 2>/dev/null || true - find $out/opt/fluorine-manager/plugins -name "*.so" -exec strip --strip-unneeded {} \; 2>/dev/null || true - find $out/opt/fluorine-manager/lib -name "*.so" -exec strip --strip-unneeded {} \; 2>/dev/null || true - [ -f "$out/opt/fluorine-manager/lootcli" ] && strip --strip-unneeded "$out/opt/fluorine-manager/lootcli" 2>/dev/null || true - - # ── Patch RPATH ── - patchelf --force-rpath --set-rpath '$ORIGIN/lib:$ORIGIN/python/lib' \ - $out/opt/fluorine-manager/ModOrganizer-core - [ -f "$out/opt/fluorine-manager/lootcli" ] && \ - patchelf --force-rpath --set-rpath '$ORIGIN/lib' $out/opt/fluorine-manager/lootcli - find $out/opt/fluorine-manager/plugins -name "*.so" \ - -exec patchelf --force-rpath --set-rpath '$ORIGIN/../lib:$ORIGIN/../python/lib' {} \; 2>/dev/null || true - find $out/opt/fluorine-manager/lib -name "*.so" \ - -exec patchelf --force-rpath --set-rpath '$ORIGIN:$ORIGIN/../python/lib' {} \; 2>/dev/null || true - - # ── Launcher script ── - cat > $out/opt/fluorine-manager/fluorine-manager <<'LAUNCH' -#!/usr/bin/env bash -set -euo pipefail -SELF="$(readlink -f "$0")" -HERE="$(cd "$(dirname "$SELF")" && pwd)" - -export FLUORINE_ORIG_LD_LIBRARY_PATH="''${LD_LIBRARY_PATH:-}" -export FLUORINE_ORIG_PATH="''${PATH}" -export FLUORINE_ORIG_XDG_DATA_DIRS="''${XDG_DATA_DIRS:-/usr/local/share:/usr/share}" -export FLUORINE_ORIG_QT_PLUGIN_PATH="''${QT_PLUGIN_PATH:-}" - -# Sync entire app to ~/.local/share/fluorine/bin/ so instance plugin -# symlinks always point to a stable location. -FLUORINE_DATA="''${HOME}/.local/share/fluorine" -BIN_DST="''${FLUORINE_DATA}/bin" - -CURRENT_VER="$(stat -c '%i:%Y' "''${HERE}/ModOrganizer-core" 2>/dev/null || echo "unknown")" -MARKER="''${BIN_DST}/.version" - -if [ ! -f "''${MARKER}" ] || [ "$(cat "''${MARKER}" 2>/dev/null)" != "''${CURRENT_VER}" ]; then - echo "Syncing Fluorine to ''${BIN_DST}..." >&2 - rm -rf "''${BIN_DST}" - mkdir -p "''${BIN_DST}" - (cd "''${HERE}" && tar --exclude-vcs -cf - .) | (cd "''${BIN_DST}" && tar -xf -) - echo "''${CURRENT_VER}" > "''${MARKER}" -fi - -RUN="''${BIN_DST}" -PYTHON_DIR="''${RUN}/python" - -export PATH="''${RUN}:''${PATH}" -# NOTE: Do NOT set LD_LIBRARY_PATH — DT_RPATH handles library resolution. -# Setting it on a capability-bearing binary drops file capabilities (AT_SECURE). -export MO2_BASE_DIR="''${RUN}" -export MO2_PLUGINS_DIR="''${RUN}/plugins" -export MO2_DLLS_DIR="''${RUN}/dlls" -export MO2_PYTHON_DIR="''${PYTHON_DIR}" -MO2_PYTHONHOME="''${PYTHON_DIR}" -unset PYTHONPATH PYTHONNOUSERSITE PYTHONHOME - -export QT_PLUGIN_PATH="''${RUN}/qt6plugins" - -cd "''${RUN}" -exec env PYTHONHOME="''${MO2_PYTHONHOME}" "''${RUN}/ModOrganizer-core" "$@" -LAUNCH - chmod +x $out/opt/fluorine-manager/fluorine-manager - - # ── Desktop integration files ── - cp -f ../data/com.fluorine.manager.desktop $out/opt/fluorine-manager/ 2>/dev/null || true - cp -f ../data/com.fluorine.manager.png $out/opt/fluorine-manager/ 2>/dev/null || true - cp -f ../data/com.fluorine.manager.metainfo.xml $out/opt/fluorine-manager/ 2>/dev/null || true - - mkdir -p $out/share/icons/hicolor/256x256/apps - mkdir -p $out/share/metainfo - cp $out/opt/fluorine-manager/com.fluorine.manager.png \ - $out/share/icons/hicolor/256x256/apps/ 2>/dev/null || true - cp $out/opt/fluorine-manager/com.fluorine.manager.metainfo.xml \ - $out/share/metainfo/ 2>/dev/null || true - - runHook postInstall - ''; - }; - - desktopItem = pkgs.makeDesktopItem { - name = "com.fluorine.manager"; - exec = "fluorine-manager"; - icon = "com.fluorine.manager"; - desktopName = "Fluorine Manager"; - genericName = "Mod Manager"; - comment = "Mod Organizer 2 for Linux — manage your game mods"; - categories = [ "Game" "Utility" ]; - mimeTypes = [ "x-scheme-handler/nxm" ]; - startupWMClass = "ModOrganizer"; - keywords = [ "mod" "organizer" "modding" "skyrim" "fallout" ]; - }; - - # ── FHS wrapper ── - fluorine-manager = pkgs.buildFHSEnv { - pname = "fluorine-manager"; - inherit version; - - runScript = "${fluorine-unwrapped}/opt/fluorine-manager/fluorine-manager"; - - targetPkgs = pkgs: with pkgs; [ - glibc - stdenv.cc.cc.lib - - libGL - libglvnd - libdrm - vulkan-loader - mesa - - libx11 - libxcb - libxext - libxrandr - libxcursor - libxi - libxfixes - libxrender - libxcomposite - libxdamage - libxtst - libxscrnsaver - libxinerama - libxkbcommon - - wayland - - fuse3 - - fontconfig - freetype - - nss - nspr - - zlib - dbus - glib - udev - libcap - polkit - ]; - - multiPkgs = pkgs: with pkgs; [ - glibc - libGL - libglvnd - vulkan-loader - libdrm - udev - alsa-lib - libpulseaudio - dbus - freetype - fontconfig - zlib - glib - libx11 - libxcb - libxext - libxrandr - libxcursor - libxi - libxfixes - libxrender - libxcomposite - libxdamage - libxtst - libxinerama - wayland - libxkbcommon - gnutls - SDL2 - ncurses - cups - ]; - - multiArch = true; - - unshareIpc = false; - unsharePid = false; - - extraInstallCommands = '' - mkdir -p $out/share - ln -s ${desktopItem}/share/applications $out/share/applications - ln -s ${fluorine-unwrapped}/share/icons $out/share/icons - ln -s ${fluorine-unwrapped}/share/metainfo $out/share/metainfo - ''; - - meta = with pkgs.lib; { - description = "Mod Organizer 2 for Linux"; - homepage = "https://github.com/SulfurNitride/Fluorine-Manager"; - license = licenses.gpl3; - platforms = [ "x86_64-linux" ]; - mainProgram = "fluorine-manager"; - }; - }; - - in { - packages.${system} = { - default = fluorine-manager; - inherit fluorine-manager fluorine-unwrapped libloot bsa-ffi nak-ffi; - }; - - apps.${system}.default = { - type = "app"; - program = "${fluorine-manager}/bin/fluorine-manager"; - }; - - # NixOS module for system-wide installation. - nixosModules.default = { config, lib, ... }: { - options.programs.fluorine-manager = { - enable = lib.mkEnableOption "Fluorine Manager (Mod Organizer 2 for Linux)"; - - fusePassthrough = lib.mkOption { - type = lib.types.bool; - default = false; - description = '' - Enable FUSE passthrough (kernel-direct I/O for mod files). - Grants CAP_SYS_ADMIN to the Fluorine binary via a - security wrapper. Requires kernel 6.9+. - ''; - }; - }; - - config = lib.mkIf config.programs.fluorine-manager.enable { - environment.systemPackages = [ self.packages.${system}.default ]; - - programs.fuse.userAllowOther = true; - - security.wrappers = lib.mkIf config.programs.fluorine-manager.fusePassthrough { - fluorine-manager = { - source = "${self.packages.${system}.default}/bin/fluorine-manager"; - capabilities = "cap_sys_admin+ep"; - owner = "root"; - group = "root"; - }; - }; - }; - }; - }; -} -- cgit v1.3.1