diff --git a/pkgs/applications/gis/qgis/default.nix b/pkgs/applications/gis/qgis/default.nix index fffd5ddd398d..9174d6452f70 100644 --- a/pkgs/applications/gis/qgis/default.nix +++ b/pkgs/applications/gis/qgis/default.nix @@ -1,4 +1,6 @@ { + lib, + stdenv, makeWrapper, nixosTests, symlinkJoin, @@ -40,7 +42,8 @@ symlinkJoin { --prefix PATH : $program_PATH \ --set PYTHONPATH $program_PYTHONPATH done - + '' + + lib.optionalString (!stdenv.hostPlatform.isDarwin) '' ln -s ${qgis-unwrapped.man} $man ''; diff --git a/pkgs/applications/gis/qgis/ltr.nix b/pkgs/applications/gis/qgis/ltr.nix index 21d906debaa4..7e8ce827e874 100644 --- a/pkgs/applications/gis/qgis/ltr.nix +++ b/pkgs/applications/gis/qgis/ltr.nix @@ -1,4 +1,6 @@ { + lib, + stdenv, makeWrapper, nixosTests, symlinkJoin, @@ -14,15 +16,13 @@ }: let qgis-ltr-unwrapped = libsForQt5.callPackage ./unwrapped-ltr.nix { - withGrass = withGrass; - withServer = withServer; - withWebKit = withWebKit; + inherit withGrass withServer withWebKit; }; in symlinkJoin { - inherit (qgis-ltr-unwrapped) version src; - pname = "qgis"; + inherit (qgis-ltr-unwrapped) version outputs src; + pname = "qgis-ltr"; paths = [ qgis-ltr-unwrapped ]; @@ -43,6 +43,9 @@ symlinkJoin { --prefix PATH : $program_PATH \ --set PYTHONPATH $program_PYTHONPATH done + '' + + lib.optionalString (!stdenv.hostPlatform.isDarwin) '' + ln -s ${qgis-ltr-unwrapped.man} $man ''; passthru = { diff --git a/pkgs/applications/gis/qgis/unwrapped-ltr.nix b/pkgs/applications/gis/qgis/unwrapped-ltr.nix index 6ca7116a458f..de9d44d09f37 100644 --- a/pkgs/applications/gis/qgis/unwrapped-ltr.nix +++ b/pkgs/applications/gis/qgis/unwrapped-ltr.nix @@ -1,6 +1,8 @@ { lib, + stdenv, fetchFromGitHub, + lndir, makeWrapper, mkDerivation, replaceVars, @@ -11,6 +13,8 @@ withServer, withWebKit, + darwin, + libtasn1, bison, cmake, draco, @@ -40,6 +44,7 @@ qtmultimedia, qtsensors, qtserialport, + qtsvg, qtwebkit, qtxmlpatterns, qwt, @@ -68,6 +73,7 @@ let psycopg2 pygments pyqt5 + pyqt5-sip pyqt-builder python-dateutil pytz @@ -83,6 +89,7 @@ in mkDerivation rec { version = "3.40.15"; pname = "qgis-ltr-unwrapped"; + outputs = [ "out" ] ++ lib.optional (!stdenv.hostPlatform.isDarwin) "man"; src = fetchFromGitHub { owner = "qgis"; @@ -105,6 +112,10 @@ mkDerivation rec { cmake flex ninja + ] + ++ lib.optionals stdenv.hostPlatform.isDarwin [ + darwin.autoSignDarwinBinariesHook + lndir ]; buildInputs = [ @@ -131,6 +142,7 @@ mkDerivation rec { qtmultimedia qtsensors qtserialport + qtsvg qtxmlpatterns qwt sqlite @@ -139,6 +151,7 @@ mkDerivation rec { ] ++ lib.optional withGrass grass ++ lib.optional withWebKit qtwebkit + ++ lib.optional stdenv.hostPlatform.isDarwin libtasn1 ++ pythonBuildInputs; patches = [ @@ -168,6 +181,10 @@ mkDerivation rec { # See https://github.com/libspatialindex/libspatialindex/issues/276 "-DWITH_INTERNAL_SPATIALINDEX=True" ] + ++ lib.optionals stdenv.hostPlatform.isDarwin [ + "-DQGIS_MACAPP_BUNDLE=0" # Don't copy Qt into bundle; we fix paths in postFixup + "-DSQLITE3_INCLUDE_DIR=${sqlite.dev}/include" # FindSqlite3.cmake incorrectly assumes framework + ] ++ lib.optional (!withWebKit) "-DWITH_QTWEBKIT=OFF" ++ lib.optional withServer [ "-DWITH_SERVER=True" @@ -186,18 +203,102 @@ mkDerivation rec { ]; dontWrapGApps = true; # wrapper params passed below + dontWrapQtApps = stdenv.hostPlatform.isDarwin; - postFixup = lib.optionalString withGrass '' - # GRASS has to be availble on the command line even though we baked in - # the path at build time using GRASS_PREFIX. - # Using wrapGAppsHook also prevents file dialogs from crashing the program - # on non-NixOS. - for program in $out/bin/*; do - wrapProgram $program \ - "''${gappsWrapperArgs[@]}" \ - --prefix PATH : ${lib.makeBinPath [ grass ]} - done - ''; + postFixup = + lib.optionalString (withGrass && stdenv.hostPlatform.isLinux) '' + # GRASS has to be available on the command line even though we baked in + # the path at build time using GRASS_PREFIX. + # Using wrapGAppsHook also prevents file dialogs from crashing the program + # on non-NixOS. + for program in $out/bin/*; do + wrapProgram $program \ + "''${gappsWrapperArgs[@]}" \ + --prefix PATH : ${lib.makeBinPath [ grass ]} + done + '' + + lib.optionalString stdenv.hostPlatform.isDarwin '' + mkdir -p $out/Applications $out/bin + mv $out/QGIS.app $out/Applications/ + ln -s $out/Applications/QGIS.app/Contents/MacOS/QGIS $out/bin/qgis + + SHORT_VERSION=$(echo "${version}" | cut -d. -f1,2) + BUNDLE="$out/Applications/QGIS.app" + FRAMEWORKS="$BUNDLE/Contents/Frameworks" + + fix_binary() { + local f="$1" + [[ -f "$f" ]] || return 0 + file "$f" | grep -q "Mach-O" || return 0 + + install_name_tool \ + -change "@loader_path/../lib/libqscintilla2_qt5.dylib" "${qscintilla}/lib/libqscintilla2_qt5.dylib" \ + -change "@loader_path/../lib/libqt5keychain.dylib" "${qtkeychain}/lib/libqt5keychain.dylib" \ + -change "@loader_path/../lib/libqwt.dylib" "${qwt}/lib/libqwt.dylib" \ + -change "@loader_path/../../Frameworks/qca-qt5.framework/qca-qt5" "${qca-qt5}/lib/qca-qt5.framework/qca-qt5" \ + -change "@loader_path/../../../qca-qt5.framework/qca-qt5" "${qca-qt5}/lib/qca-qt5.framework/qca-qt5" \ + -change "@loader_path/../../../../MacOS/lib/libqscintilla2_qt5.dylib" "${qscintilla}/lib/libqscintilla2_qt5.dylib" \ + -change "@loader_path/../../../../MacOS/lib/libqt5keychain.dylib" "${qtkeychain}/lib/libqt5keychain.dylib" \ + -change "@loader_path/../../../../MacOS/lib/libqwt.dylib" "${qwt}/lib/libqwt.dylib" \ + -change "@executable_path/lib/libqwt.dylib" "${qwt}/lib/libqwt.dylib" \ + -change "@executable_path/lib/libqscintilla2_qt5.dylib" "${qscintilla}/lib/libqscintilla2_qt5.dylib" \ + -change "@executable_path/lib/libqt5keychain.dylib" "${qtkeychain}/lib/libqt5keychain.dylib" \ + -change "@executable_path/../Frameworks/qca-qt5.framework/qca-qt5" "${qca-qt5}/lib/qca-qt5.framework/qca-qt5" \ + -change "@loader_path/../../../qgis_core.framework/qgis_core" "$FRAMEWORKS/qgis_core.framework/Versions/$SHORT_VERSION/qgis_core" \ + -change "@loader_path/../../../qgis_gui.framework/qgis_gui" "$FRAMEWORKS/qgis_gui.framework/Versions/$SHORT_VERSION/qgis_gui" \ + -change "@loader_path/../../../qgis_analysis.framework/qgis_analysis" "$FRAMEWORKS/qgis_analysis.framework/Versions/$SHORT_VERSION/qgis_analysis" \ + -change "@loader_path/../../../qgis_3d.framework/qgis_3d" "$FRAMEWORKS/qgis_3d.framework/Versions/$SHORT_VERSION/qgis_3d" \ + -change "@loader_path/../../../qgis_native.framework/qgis_native" "$FRAMEWORKS/qgis_native.framework/Versions/$SHORT_VERSION/qgis_native" \ + "$f" 2>/dev/null || true + } + + fix_binary "$BUNDLE/Contents/MacOS/QGIS" + for lib in "$BUNDLE/Contents/MacOS/lib"/*.dylib; do fix_binary "$lib"; done + for fw in qgis_core qgis_gui qgis_analysis qgis_3d qgis_native; do + fix_binary "$FRAMEWORKS/$fw.framework/Versions/$SHORT_VERSION/$fw" + [[ -f "$FRAMEWORKS/$fw.framework/$fw" && ! -L "$FRAMEWORKS/$fw.framework/$fw" ]] && \ + fix_binary "$FRAMEWORKS/$fw.framework/$fw" + done + for plugin in "$BUNDLE/Contents/PlugIns/qgis"/*.so; do fix_binary "$plugin"; done + + ${lib.optionalString withGrass '' + fix_binary "$FRAMEWORKS/qgisgrass8.framework/Versions/$SHORT_VERSION/qgisgrass8" + install_name_tool \ + -change "@loader_path/../../../qgisgrass8.framework/qgisgrass8" "$FRAMEWORKS/qgisgrass8.framework/Versions/$SHORT_VERSION/qgisgrass8" \ + "$BUNDLE/Contents/MacOS/QGIS" 2>/dev/null || true + for lib in "$BUNDLE/Contents/MacOS/lib"/*.dylib; do + install_name_tool \ + -change "@loader_path/../../../qgisgrass8.framework/qgisgrass8" "$FRAMEWORKS/qgisgrass8.framework/Versions/$SHORT_VERSION/qgisgrass8" \ + "$lib" 2>/dev/null || true + done + ''} + + ${lib.optionalString withServer '' + fix_binary "$BUNDLE/Contents/MacOS/lib/libqgis_server.${version}.dylib" + ''} + + # Merge Python packages (lndir handles namespace packages correctly) + QGIS_PYTHON="$BUNDLE/Contents/Resources/python" + for pkg in ${ + lib.concatMapStringsSep " " (p: "${p}/${py.pkgs.python.sitePackages}") ( + py.pkgs.requiredPythonModules pythonBuildInputs + ) + }; do + [[ -d "$pkg" ]] && lndir -silent "$pkg" "$QGIS_PYTHON" + done + # Remove broken symlinks + find "$QGIS_PYTHON" -type l ! -exec test -e {} \; -delete + + # Create merged Qt plugins directory in the bundle (LSEnvironment is unreliable) + mkdir -p "$BUNDLE/Contents/PlugIns" + lndir -silent "${qtbase}/${qtbase.qtPluginPrefix}" "$BUNDLE/Contents/PlugIns" + lndir -silent "${qtsvg.bin}/${qtbase.qtPluginPrefix}" "$BUNDLE/Contents/PlugIns" + + cat > "$BUNDLE/Contents/Resources/qt.conf" << 'EOF' + [Paths] + Plugins = PlugIns + EOF + ''; # >9k objects, >3h build time on a normal build slot requiredSystemFeatures = [ "big-parallel" ]; @@ -207,6 +308,6 @@ mkDerivation rec { homepage = "https://www.qgis.org"; license = lib.licenses.gpl2Plus; teams = [ lib.teams.geospatial ]; - platforms = with lib.platforms; linux; + platforms = lib.platforms.unix; }; } diff --git a/pkgs/applications/gis/qgis/unwrapped.nix b/pkgs/applications/gis/qgis/unwrapped.nix index 37f6a659b13d..b71c57880760 100644 --- a/pkgs/applications/gis/qgis/unwrapped.nix +++ b/pkgs/applications/gis/qgis/unwrapped.nix @@ -1,6 +1,8 @@ { lib, + stdenv, fetchFromGitHub, + lndir, makeWrapper, mkDerivation, replaceVars, @@ -11,6 +13,8 @@ withServer, withWebKit, + darwin, + libtasn1, bison, cmake, draco, @@ -40,6 +44,7 @@ qtmultimedia, qtsensors, qtserialport, + qtsvg, qtwebkit, qtxmlpatterns, qwt, @@ -68,6 +73,7 @@ let psycopg2 pygments pyqt5 + pyqt5-sip pyqt-builder python-dateutil pytz @@ -83,10 +89,7 @@ in mkDerivation rec { version = "3.44.7"; pname = "qgis-unwrapped"; - outputs = [ - "out" - "man" - ]; + outputs = [ "out" ] ++ lib.optional (!stdenv.hostPlatform.isDarwin) "man"; src = fetchFromGitHub { owner = "qgis"; @@ -109,6 +112,10 @@ mkDerivation rec { cmake flex ninja + ] + ++ lib.optionals stdenv.hostPlatform.isDarwin [ + darwin.autoSignDarwinBinariesHook + lndir ]; buildInputs = [ @@ -135,6 +142,7 @@ mkDerivation rec { qtmultimedia qtsensors qtserialport + qtsvg qtxmlpatterns qwt sqlite @@ -143,6 +151,7 @@ mkDerivation rec { ] ++ lib.optional withGrass grass ++ lib.optional withWebKit qtwebkit + ++ lib.optional stdenv.hostPlatform.isDarwin libtasn1 ++ pythonBuildInputs; patches = [ @@ -168,6 +177,10 @@ mkDerivation rec { # See https://github.com/libspatialindex/libspatialindex/issues/276 "-DWITH_INTERNAL_SPATIALINDEX=True" ] + ++ lib.optionals stdenv.hostPlatform.isDarwin [ + "-DQGIS_MACAPP_BUNDLE=0" # Don't copy Qt into bundle; we fix paths in postFixup + "-DSQLITE3_INCLUDE_DIR=${sqlite.dev}/include" # FindSqlite3.cmake incorrectly assumes framework + ] ++ lib.optional (!withWebKit) "-DWITH_QTWEBKIT=OFF" ++ lib.optional withServer [ "-DWITH_SERVER=True" @@ -186,18 +199,102 @@ mkDerivation rec { ]; dontWrapGApps = true; # wrapper params passed below + dontWrapQtApps = stdenv.hostPlatform.isDarwin; - postFixup = lib.optionalString withGrass '' - # GRASS has to be available on the command line even though we baked in - # the path at build time using GRASS_PREFIX. - # Using wrapGAppsHook also prevents file dialogs from crashing the program - # on non-NixOS. - for program in $out/bin/*; do - wrapProgram $program \ - "''${gappsWrapperArgs[@]}" \ - --prefix PATH : ${lib.makeBinPath [ grass ]} - done - ''; + postFixup = + lib.optionalString (withGrass && stdenv.hostPlatform.isLinux) '' + # GRASS has to be available on the command line even though we baked in + # the path at build time using GRASS_PREFIX. + # Using wrapGAppsHook also prevents file dialogs from crashing the program + # on non-NixOS. + for program in $out/bin/*; do + wrapProgram $program \ + "''${gappsWrapperArgs[@]}" \ + --prefix PATH : ${lib.makeBinPath [ grass ]} + done + '' + + lib.optionalString stdenv.hostPlatform.isDarwin '' + mkdir -p $out/Applications $out/bin + mv $out/QGIS.app $out/Applications/ + ln -s $out/Applications/QGIS.app/Contents/MacOS/QGIS $out/bin/qgis + + SHORT_VERSION=$(echo "${version}" | cut -d. -f1,2) + BUNDLE="$out/Applications/QGIS.app" + FRAMEWORKS="$BUNDLE/Contents/Frameworks" + + fix_binary() { + local f="$1" + [[ -f "$f" ]] || return 0 + file "$f" | grep -q "Mach-O" || return 0 + + install_name_tool \ + -change "@loader_path/../lib/libqscintilla2_qt5.dylib" "${qscintilla}/lib/libqscintilla2_qt5.dylib" \ + -change "@loader_path/../lib/libqt5keychain.dylib" "${qtkeychain}/lib/libqt5keychain.dylib" \ + -change "@loader_path/../lib/libqwt.dylib" "${qwt}/lib/libqwt.dylib" \ + -change "@loader_path/../../Frameworks/qca-qt5.framework/qca-qt5" "${qca-qt5}/lib/qca-qt5.framework/qca-qt5" \ + -change "@loader_path/../../../qca-qt5.framework/qca-qt5" "${qca-qt5}/lib/qca-qt5.framework/qca-qt5" \ + -change "@loader_path/../../../../MacOS/lib/libqscintilla2_qt5.dylib" "${qscintilla}/lib/libqscintilla2_qt5.dylib" \ + -change "@loader_path/../../../../MacOS/lib/libqt5keychain.dylib" "${qtkeychain}/lib/libqt5keychain.dylib" \ + -change "@loader_path/../../../../MacOS/lib/libqwt.dylib" "${qwt}/lib/libqwt.dylib" \ + -change "@executable_path/lib/libqwt.dylib" "${qwt}/lib/libqwt.dylib" \ + -change "@executable_path/lib/libqscintilla2_qt5.dylib" "${qscintilla}/lib/libqscintilla2_qt5.dylib" \ + -change "@executable_path/lib/libqt5keychain.dylib" "${qtkeychain}/lib/libqt5keychain.dylib" \ + -change "@executable_path/../Frameworks/qca-qt5.framework/qca-qt5" "${qca-qt5}/lib/qca-qt5.framework/qca-qt5" \ + -change "@loader_path/../../../qgis_core.framework/qgis_core" "$FRAMEWORKS/qgis_core.framework/Versions/$SHORT_VERSION/qgis_core" \ + -change "@loader_path/../../../qgis_gui.framework/qgis_gui" "$FRAMEWORKS/qgis_gui.framework/Versions/$SHORT_VERSION/qgis_gui" \ + -change "@loader_path/../../../qgis_analysis.framework/qgis_analysis" "$FRAMEWORKS/qgis_analysis.framework/Versions/$SHORT_VERSION/qgis_analysis" \ + -change "@loader_path/../../../qgis_3d.framework/qgis_3d" "$FRAMEWORKS/qgis_3d.framework/Versions/$SHORT_VERSION/qgis_3d" \ + -change "@loader_path/../../../qgis_native.framework/qgis_native" "$FRAMEWORKS/qgis_native.framework/Versions/$SHORT_VERSION/qgis_native" \ + "$f" 2>/dev/null || true + } + + fix_binary "$BUNDLE/Contents/MacOS/QGIS" + for lib in "$BUNDLE/Contents/MacOS/lib"/*.dylib; do fix_binary "$lib"; done + for fw in qgis_core qgis_gui qgis_analysis qgis_3d qgis_native; do + fix_binary "$FRAMEWORKS/$fw.framework/Versions/$SHORT_VERSION/$fw" + [[ -f "$FRAMEWORKS/$fw.framework/$fw" && ! -L "$FRAMEWORKS/$fw.framework/$fw" ]] && \ + fix_binary "$FRAMEWORKS/$fw.framework/$fw" + done + for plugin in "$BUNDLE/Contents/PlugIns/qgis"/*.so; do fix_binary "$plugin"; done + + ${lib.optionalString withGrass '' + fix_binary "$FRAMEWORKS/qgisgrass8.framework/Versions/$SHORT_VERSION/qgisgrass8" + install_name_tool \ + -change "@loader_path/../../../qgisgrass8.framework/qgisgrass8" "$FRAMEWORKS/qgisgrass8.framework/Versions/$SHORT_VERSION/qgisgrass8" \ + "$BUNDLE/Contents/MacOS/QGIS" 2>/dev/null || true + for lib in "$BUNDLE/Contents/MacOS/lib"/*.dylib; do + install_name_tool \ + -change "@loader_path/../../../qgisgrass8.framework/qgisgrass8" "$FRAMEWORKS/qgisgrass8.framework/Versions/$SHORT_VERSION/qgisgrass8" \ + "$lib" 2>/dev/null || true + done + ''} + + ${lib.optionalString withServer '' + fix_binary "$BUNDLE/Contents/MacOS/lib/libqgis_server.${version}.dylib" + ''} + + # Merge Python packages (lndir handles namespace packages correctly) + QGIS_PYTHON="$BUNDLE/Contents/Resources/python" + for pkg in ${ + lib.concatMapStringsSep " " (p: "${p}/${py.pkgs.python.sitePackages}") ( + py.pkgs.requiredPythonModules pythonBuildInputs + ) + }; do + [[ -d "$pkg" ]] && lndir -silent "$pkg" "$QGIS_PYTHON" + done + # Remove broken symlinks + find "$QGIS_PYTHON" -type l ! -exec test -e {} \; -delete + + # Create merged Qt plugins directory in the bundle (LSEnvironment is unreliable) + mkdir -p "$BUNDLE/Contents/PlugIns" + lndir -silent "${qtbase}/${qtbase.qtPluginPrefix}" "$BUNDLE/Contents/PlugIns" + lndir -silent "${qtsvg.bin}/${qtbase.qtPluginPrefix}" "$BUNDLE/Contents/PlugIns" + + cat > "$BUNDLE/Contents/Resources/qt.conf" << 'EOF' + [Paths] + Plugins = PlugIns + EOF + ''; # >9k objects, >3h build time on a normal build slot requiredSystemFeatures = [ "big-parallel" ]; @@ -207,6 +304,6 @@ mkDerivation rec { homepage = "https://www.qgis.org"; license = lib.licenses.gpl2Plus; teams = [ lib.teams.geospatial ]; - platforms = with lib.platforms; linux; + platforms = lib.platforms.unix; }; }