diff --git a/flake.lock b/flake.lock index c31223032..31414c9a0 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1743550720, - "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "lastModified": 1754487366, + "narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18", "type": "github" }, "original": { @@ -22,11 +22,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1744932701, - "narHash": "sha256-fusHbZCyv126cyArUwwKrLdCkgVAIaa/fQJYFlCEqiU=", + "lastModified": 1755615617, + "narHash": "sha256-HMwfAJBdrr8wXAkbGhtcby1zGFvs+StOp19xNsbqdOg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b024ced1aac25639f8ca8fdfc2f8c4fbd66c48ef", + "rev": "20075955deac2583bb12f07151c2df830ef346b4", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 3f4a4eda1..c12b22b6e 100644 --- a/flake.nix +++ b/flake.nix @@ -27,48 +27,30 @@ perSystem = { pkgs, ... }: + let + python3 = pkgs.python312; + in { packages = let - python3Packages = pkgs.python312Packages; + python3Packages = python3.pkgs; pillow-jxl-plugin = python3Packages.callPackage ./nix/package/pillow-jxl-plugin.nix { inherit (pkgs) cmake; inherit pyexiv2; }; pyexiv2 = python3Packages.callPackage ./nix/package/pyexiv2.nix { inherit (pkgs) exiv2; }; - vtf2img = python3Packages.callPackage ./nix/package/vtf2img.nix { }; in rec { default = tagstudio; tagstudio = pkgs.callPackage ./nix/package { - # HACK: Remove when PySide6 is bumped to 6.9.x. - # Sourced from https://github.com/NixOS/nixpkgs/commit/2f9c1ad5e19a6154d541f878774a9aacc27381b7. - pyside6 = - if lib.versionAtLeast python3Packages.pyside6.version "6.9.0" then - (python3Packages.pyside6.override { - shiboken6 = python3Packages.shiboken6.overrideAttrs { - version = "6.8.0.2"; + inherit python3Packages; - src = pkgs.fetchurl { - url = "mirror://qt/official_releases/QtForPython/shiboken6/PySide6-6.8.0.2-src/pyside-setup-everywhere-src-6.8.0.tar.xz"; - hash = "sha256-Ghohmo8yfjQNJYJ1+tOp8mG48EvFcEF0fnPdatJStOE="; - }; - - sourceRoot = "pyside-setup-everywhere-src-6.8.0/sources/shiboken6"; - - patches = [ ./nix/package/shiboken6-fix-include-qt-headers.patch ]; - }; - }).overrideAttrs - { sourceRoot = "pyside-setup-everywhere-src-6.8.0/sources/pyside6"; } - else - python3Packages.pyside6; - - inherit pillow-jxl-plugin vtf2img; + inherit pillow-jxl-plugin; }; tagstudio-jxl = tagstudio.override { withJXLSupport = true; }; - inherit pillow-jxl-plugin pyexiv2 vtf2img; + inherit pillow-jxl-plugin pyexiv2; }; devShells = rec { @@ -79,6 +61,8 @@ lib pkgs self + + python3 ; }; }; diff --git a/nix/package/default.nix b/nix/package/default.nix index 0ee7d464a..631a4f475 100644 --- a/nix/package/default.nix +++ b/nix/package/default.nix @@ -4,13 +4,11 @@ pipewire, python3Packages, qt6, + ripgrep, stdenv, wrapGAppsHook, - wcmatch, pillow-jxl-plugin, - pyside6, - vtf2img, withJXLSupport ? false, }: @@ -29,7 +27,7 @@ python3Packages.buildPythonApplication { python3Packages.pythonRelaxDepsHook qt6.wrapQtAppsHook - # INFO: Should be unnecessary once PR is pulled. + # Should be unnecessary once PR is pulled. # PR: https://github.com/NixOS/nixpkgs/pull/271037 # Issue: https://github.com/NixOS/nixpkgs/issues/149812 wrapGAppsHook @@ -54,11 +52,23 @@ python3Packages.buildPythonApplication { cp $src/src/tagstudio/resources/icon.png $out/share/icons/hicolor/512x512/apps/tagstudio.png ''; - makeWrapperArgs = - [ "--prefix PATH : ${lib.makeBinPath [ ffmpeg-headless ]}" ] - ++ lib.optional stdenv.hostPlatform.isLinux "--prefix LD_LIBRARY_PATH : ${ - lib.makeLibraryPath [ pipewire ] - }"; + dontWrapGApps = true; + dontWrapQtApps = true; + makeWrapperArgs = [ + "--prefix PATH : ${ + lib.makeBinPath [ + ffmpeg-headless + ripgrep + ] + }" + ] + ++ lib.optional stdenv.hostPlatform.isLinux "--prefix LD_LIBRARY_PATH : ${ + lib.makeLibraryPath [ pipewire ] + }" + ++ [ + "\${gappsWrapperArgs[@]}" + "\${qtWrapperArgs[@]}" + ]; pythonRemoveDeps = lib.optional (!withJXLSupport) [ "pillow_jxl" ]; pythonRelaxDeps = [ @@ -66,6 +76,7 @@ python3Packages.buildPythonApplication { "pillow" "pillow-heif" "pillow-jxl-plugin" + "pyside6" "structlog" "typing-extensions" ]; @@ -89,15 +100,16 @@ python3Packages.buildPythonApplication { rawpy send2trash sqlalchemy + srctools structlog toml ujson - vtf2img + wcmatch ] ++ lib.optional withJXLSupport pillow-jxl-plugin; disabledTests = [ - # INFO: These tests require modifications to a library, which does not work + # These tests require modifications to a library, which does not work # in a read-only environment. "test_build_tag_panel_add_alias_callback" "test_build_tag_panel_add_aliases" @@ -122,7 +134,7 @@ python3Packages.buildPythonApplication { "test_update_selection_multiple" "test_update_selection_single" - # INFO: This test requires modification of a configuration file. + # This test requires modification of a configuration file. "test_filepath_setting" ]; diff --git a/nix/package/pillow-jxl-plugin.nix b/nix/package/pillow-jxl-plugin.nix index 9c8c2fa26..4df28aeda 100644 --- a/nix/package/pillow-jxl-plugin.nix +++ b/nix/package/pillow-jxl-plugin.nix @@ -13,18 +13,18 @@ buildPythonPackage rec { pname = "pillow-jxl-plugin"; - version = "1.3.2"; + version = "1.3.4"; pyproject = true; src = fetchPypi { - pname = builtins.replaceStrings [ "-" ] [ "_" ] pname; + pname = "pillow_jxl_plugin"; inherit version; - hash = "sha256-efBoek8yUFR+ArhS55lm9F2XhkZ7/I3GsScQEe8U/2I="; + hash = "sha256-jqWJ/FWep8XfzLQq9NgUj121CPX01FGDKLq1ox/LJo4="; }; cargoDeps = rustPlatform.fetchCargoVendor { inherit src; - hash = "sha256-vZHrwGfgo3fIIOY7p0vy4XIKiHoddPDdJggkBen+w/A="; + hash = "sha256-7j+sCn+P6q6tsm2MJ/cM7hF2KEjILJNA6SDb35tecPg="; }; nativeBuildInputs = [ @@ -39,9 +39,9 @@ buildPythonPackage rec { pytestCheckHook ]; - # INFO: Working directory takes precedence in the Python path. Remove + # Working directory takes precedence in the Python path. Remove # `pillow_jxl` to prevent it from being loaded during pytest, rather than the - # built module, as it includes a `pillow_jxl.pillow_jxl` .so that is imported. + # built module, as it includes a `pillow_jxl.pillow_jxl.so` that is imported. # See: https://github.com/NixOS/nixpkgs/issues/255262 # See: https://github.com/NixOS/nixpkgs/pull/255471 preCheck = '' @@ -58,8 +58,9 @@ buildPythonPackage rec { ]; meta = { - description = "Pillow plugin for JPEG-XL, using Rust for bindings."; + description = "Pillow plugin for JPEG-XL, using Rust for bindings"; homepage = "https://github.com/Isotr0py/pillow-jpegxl-plugin"; + changelog = "https://github.com/Isotr0py/pillow-jpegxl-plugin/releases/tag/v${version}"; license = lib.licenses.gpl3; maintainers = with lib.maintainers; [ xarvex ]; platforms = lib.platforms.unix; diff --git a/nix/package/pyexiv2.nix b/nix/package/pyexiv2.nix index b946e0eae..4761db496 100644 --- a/nix/package/pyexiv2.nix +++ b/nix/package/pyexiv2.nix @@ -4,16 +4,18 @@ exiv2, fetchFromGitHub, lib, + setuptools, }: buildPythonPackage rec { pname = "pyexiv2"; version = "2.15.3"; + pyproject = true; src = fetchFromGitHub { owner = "LeoHsiao1"; - repo = pname; - rev = "v${version}"; + repo = "pyexiv2"; + tag = "v${version}"; hash = "sha256-83bFMaoXncvhRJNcCgkkC7B29wR5pjuLO/EdkQdqxxo="; }; @@ -22,9 +24,12 @@ buildPythonPackage rec { pythonImportsCheck = [ "pyexiv2" ]; + build-system = [ setuptools ]; + meta = { - description = "Read and write image metadata, including EXIF, IPTC, XMP, ICC Profile."; + description = "Read and write image metadata, including EXIF, IPTC, XMP, ICC Profile"; homepage = "https://github.com/LeoHsiao1/pyexiv2"; + changelog = "https://github.com/LeoHsiao1/pyexiv2/releases/tag/v${version}"; license = lib.licenses.gpl3; maintainers = with lib.maintainers; [ xarvex ]; platforms = with lib.platforms; darwin ++ linux ++ windows; diff --git a/nix/package/shiboken6-fix-include-qt-headers.patch b/nix/package/shiboken6-fix-include-qt-headers.patch deleted file mode 100644 index 6ebca0063..000000000 --- a/nix/package/shiboken6-fix-include-qt-headers.patch +++ /dev/null @@ -1,81 +0,0 @@ -Sourced from https://github.com/NixOS/nixpkgs/blob/5ba0f1ea90b0afa2abc23a43edb63af51d932e6d/pkgs/development/python-modules/shiboken6/fix-include-qt-headers.patch. ---- a/ApiExtractor/clangparser/compilersupport.cpp -+++ b/ApiExtractor/clangparser/compilersupport.cpp -@@ -16,6 +16,7 @@ - #include - #include - #include -+#include - - #include - -@@ -341,6 +342,13 @@ QByteArrayList emulatedCompilerOptions() - { - QByteArrayList result; - HeaderPaths headerPaths; -+ -+ bool isNixDebug = qgetenv("NIX_DEBUG").toInt() > 0; -+ // examples: -+ // /nix/store/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-qtsensors-6.4.2-dev/include -+ // /nix/store/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-qtbase-6.4.2-dev/include -+ QRegularExpression qtHeaderRegex(uR"(/[0-9a-z]{32}-qt[a-z0-9]+-)"_s); -+ - switch (compiler()) { - case Compiler::Msvc: - result.append(QByteArrayLiteral("-fms-compatibility-version=19.26.28806")); -@@ -352,9 +360,30 @@ QByteArrayList emulatedCompilerOptions() - appendClangBuiltinIncludes(&headerPaths); - break; - case Compiler::Clang: -- headerPaths.append(gppInternalIncludePaths(compilerFromCMake(u"clang++"_s))); -+ // fix: error: cannot jump from switch statement to this case label: case Compiler::Gpp -+ // note: jump bypasses variable initialization: const HeaderPaths clangPaths = -+ { -+ //headerPaths.append(gppInternalIncludePaths(compilerFromCMake(u"clang++"_s))); -+ // fix: qt.shiboken: x is specified in typesystem, but not defined. This could potentially lead to compilation errors. -+ // PySide requires that Qt headers are not -isystem -+ // https://bugreports.qt.io/browse/PYSIDE-787 -+ const HeaderPaths clangPaths = gppInternalIncludePaths(compilerFromCMake(u"clang++"_qs)); -+ for (const HeaderPath &h : clangPaths) { -+ auto match = qtHeaderRegex.match(QString::fromUtf8(h.path)); -+ if (!match.hasMatch()) { -+ if (isNixDebug) -+ qDebug() << "shiboken compilersupport.cpp: found non-qt header: " << h.path; -+ // add using -isystem -+ headerPaths.append(h); -+ } else { -+ if (isNixDebug) -+ qDebug() << "shiboken compilersupport.cpp: found qt header: " << h.path; -+ headerPaths.append({h.path, HeaderType::Standard}); -+ } -+ } - result.append(noStandardIncludeOption()); - break; -+ } - case Compiler::Gpp: - if (needsClangBuiltinIncludes()) - appendClangBuiltinIncludes(&headerPaths); -@@ -363,8 +392,20 @@ QByteArrayList emulatedCompilerOptions() - // etc (g++ 11.3). - const HeaderPaths gppPaths = gppInternalIncludePaths(compilerFromCMake(u"g++"_qs)); - for (const HeaderPath &h : gppPaths) { -- if (h.path.contains("c++") || h.path.contains("sysroot")) -+ // fix: qt.shiboken: x is specified in typesystem, but not defined. This could potentially lead to compilation errors. -+ // PySide requires that Qt headers are not -isystem -+ // https://bugreports.qt.io/browse/PYSIDE-787 -+ auto match = qtHeaderRegex.match(QString::fromUtf8(h.path)); -+ if (!match.hasMatch()) { -+ if (isNixDebug) -+ qDebug() << "shiboken compilersupport.cpp: found non-qt header: " << h.path; -+ // add using -isystem - headerPaths.append(h); -+ } else { -+ if (isNixDebug) -+ qDebug() << "shiboken compilersupport.cpp: found qt header: " << h.path; -+ headerPaths.append({h.path, HeaderType::Standard}); -+ } - } - break; - } --- -2.39.0 diff --git a/nix/package/vtf2img.nix b/nix/package/vtf2img.nix deleted file mode 100644 index edfe42da3..000000000 --- a/nix/package/vtf2img.nix +++ /dev/null @@ -1,29 +0,0 @@ -{ - buildPythonPackage, - fetchPypi, - lib, - pillow, -}: - -buildPythonPackage rec { - pname = "vtf2img"; - version = "0.1.0"; - - src = fetchPypi { - inherit pname version; - hash = "sha256-YmWs8673d72wH4nTOXP4AFGs2grIETln4s1MD5PfE0A="; - }; - - pythonImportsCheck = [ "vtf2img" ]; - - dependencies = [ pillow ]; - - meta = { - description = "A Python library to convert Valve Texture Format (VTF) files to images."; - homepage = "https://github.com/julienc91/vtf2img"; - license = lib.licenses.mit; - maintainers = with lib.maintainers; [ xarvex ]; - mainProgram = "vtf2img"; - platforms = lib.platforms.unix; - }; -} diff --git a/nix/shell.nix b/nix/shell.nix index 22e9d7f7b..7f74cd74f 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -1,59 +1,72 @@ -{ lib, pkgs, ... }: +{ + lib, + pkgs, + + python3, + ... +}: let - # INFO: PySide6 from PIP is compiled for 6.8.0 of Qt, must pin to match version. - # Long term this can be solved with an alternative like uv2nix for the devshell. - qt6Pkgs = import (builtins.fetchTarball { - name = "nixos-unstable-qt6-pinned"; - url = "https://github.com/NixOS/nixpkgs/archive/93ff48c9be84a76319dac293733df09bbbe3f25c.tar.gz"; - sha256 = "038m7932v4z0zn2lk7k7mbqbank30q84r1viyhchw9pcm3aq3q23"; - }) { inherit (pkgs) system; }; - - pythonLibraryPath = lib.makeLibraryPath ( - (with pkgs; [ - fontconfig.lib - freetype - glib - stdenv.cc.cc.lib - zstd - ]) - ++ (with qt6Pkgs.qt6; [ qtbase ]) - ++ lib.optionals (!pkgs.stdenv.isDarwin) ( - (with pkgs; [ - dbus.lib + pythonLibraryPath = + with pkgs; + lib.makeLibraryPath ( + [ + fontconfig + freetype + glib + qt6.qtbase + stdenv.cc.cc + zstd + ] + ++ lib.optionals (!stdenv.isDarwin) [ + dbus libGL libdrm libpulseaudio libva libxkbcommon pipewire + qt6.qtwayland xorg.libX11 xorg.libXrandr - ]) - ++ (with qt6Pkgs.qt6; [ qtwayland ]) - ) - ); + ] + ); + libraryPath = "${lib.optionalString pkgs.stdenv.isDarwin "DY"}LD_LIBRARY_PATH"; - python = pkgs.python312; - pythonWrapped = pkgs.symlinkJoin { - inherit (python) + python3Wrapped = pkgs.symlinkJoin { + inherit (python3) name pname version meta ; - paths = [ python ]; + paths = [ python3 ]; + + nativeBuildInputs = with pkgs; [ + makeWrapper + qt6.wrapQtAppsHook - nativeBuildInputs = (with pkgs; [ makeWrapper ]) ++ (with qt6Pkgs.qt6; [ wrapQtAppsHook ]); - buildInputs = with qt6Pkgs.qt6; [ qtbase ]; + # Should be unnecessary once PR is pulled. + # PR: https://github.com/NixOS/nixpkgs/pull/271037 + # Issue: https://github.com/NixOS/nixpkgs/issues/149812 + wrapGAppsHook + ]; + buildInputs = with pkgs.qt6; [ + qtbase + qtmultimedia + ]; postBuild = '' - wrapProgram $out/bin/python3.12 \ + wrapProgram $out/bin/${python3.meta.mainProgram} \ --prefix ${libraryPath} : ${pythonLibraryPath} \ + "''${gappsWrapperArgs[@]}" \ "''${qtWrapperArgs[@]}" ''; + + dontWrapGApps = true; + dontWrapQtApps = true; }; in pkgs.mkShellNoCC { @@ -63,7 +76,13 @@ pkgs.mkShellNoCC { ruff ]; - buildInputs = [ pythonWrapped ] ++ (with pkgs; [ ffmpeg-headless ]); + buildInputs = [ + python3Wrapped + ] + ++ (with pkgs; [ + ffmpeg-headless + ripgrep + ]); env = { QT_QPA_PLATFORM = "wayland;xcb"; @@ -74,11 +93,16 @@ pkgs.mkShellNoCC { shellHook = let - python = lib.getExe pythonWrapped; + python = lib.getExe python3Wrapped; + + # PySide/Qt are very particular about matching versions. Override with nixpkgs package. + pythonPath = lib.makeSearchPathOutput "lib" python3.sitePackages ( + with python3.pkgs; [ pyside6 ] ++ pyside6.propagatedBuildInputs or [ ] + ); in # bash '' - venv="''${UV_PROJECT_ENVIRONMENT:-.venv}" + venv=''${UV_PROJECT_ENVIRONMENT:-.venv} if [ ! -f "''${venv}"/bin/activate ] || [ "$(readlink -f "''${venv}"/bin/python)" != "$(readlink -f ${python})" ]; then printf '%s\n' 'Regenerating virtual environment, Python interpreter changed...' >&2 @@ -87,6 +111,7 @@ pkgs.mkShellNoCC { fi source "''${venv}"/bin/activate + PYTHONPATH=${pythonPath}''${PYTHONPATH:+:}''${PYTHONPATH:-} if [ ! -f "''${venv}"/pyproject.toml ] || ! diff --brief pyproject.toml "''${venv}"/pyproject.toml >/dev/null; then printf '%s\n' 'Installing dependencies, pyproject.toml changed...' >&2