nix: add nix packaging files for qgis

This commit is contained in:
Ivan Mincik 2024-08-22 12:59:25 +02:00
parent 1b19ff6787
commit 0895e6727e
5 changed files with 473 additions and 0 deletions

60
flake.lock generated Normal file
View File

@ -0,0 +1,60 @@
{
"nodes": {
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1743550720,
"narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "c621e8422220273271f52058f618c94e405bb0f5",
"type": "github"
},
"original": {
"id": "flake-parts",
"type": "indirect"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1744868846,
"narHash": "sha256-5RJTdUHDmj12Qsv7XOhuospjAjATNiTMElplWnJE9Hs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "ebe4301cbd8f81c4f8d3244b3632338bbeb6d49c",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1743296961,
"narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"root": {
"inputs": {
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

77
flake.nix Normal file
View File

@ -0,0 +1,77 @@
{
description = "QGIS";
nixConfig = {
# extra-substituters = [ "https://example.cachix.org" ];
# extra-trusted-public-keys = [ "example.cachix.org-1:xxxx=" ];
# IFD is required for qgisVersion detection in nix/unwrapped.nix.
allow-import-from-derivation = true;
bash-prompt = "\\[\\033[1m\\][qgis-dev]\\[\\033\[m\\]\\040\\w >\\040";
};
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
};
outputs = inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = [ "x86_64-linux" ];
perSystem = { config, self', inputs', pkgs, system, ... }: {
packages = {
qgis = pkgs.libsForQt5.callPackage ./nix/package.nix { };
};
devShells.default =
let
nixPatches = pkgs.lib.concatStringsSep " " self'.packages.qgis.passthru.unwrapped.patches;
in
pkgs.mkShell {
inputsFrom = [
self'.packages.qgis
self'.packages.qgis.passthru.unwrapped
];
shellHook = ''
echo "Applying Nix patches ..."
for p in ${nixPatches}; do
echo "patch: $p"
patch --reverse --reject-file - --strip 1 < $p &> /dev/null || true
patch --strip 1 < $p
done
export QT_PLUGIN_PATH="${pkgs.libsForQt5.qt5.qtbase}/${pkgs.libsForQt5.qt5.qtbase.qtPluginPrefix}"
export QT_QPA_PLATFORM_PLUGIN_PATH="${pkgs.libsForQt5.qt5.qtbase}/${pkgs.libsForQt5.qt5.qtbase.qtPluginPrefix}/platforms"
function dev-help {
echo -e "\nWelcome to a QGIS development environment !"
echo "Build QGIS using following commands:"
echo
echo " 1. mkdir build && cd build"
echo " 2. cmake -G Ninja -D CMAKE_BUILD_TYPE=Debug -D CMAKE_INSTALL_PREFIX=\$(pwd)/app -DWITH_QTWEBKIT=OFF -DQT_PLUGINS_DIR=${pkgs.libsForQt5.qt5.qtbase}/${pkgs.libsForQt5.qt5.qtbase.qtPluginPrefix} .."
echo " 3. ninja"
echo " 4. ninja install"
echo
echo "Run tests:"
echo
echo "1. ninja test"
echo
echo "Note: run 'nix flake update' from time to time to update dependencies."
echo
echo "Run 'dev-help' to see this message again."
}
dev-help
'';
};
};
flake = { };
};
}

53
nix/package.nix Normal file
View File

@ -0,0 +1,53 @@
{ makeWrapper
, nixosTests
, symlinkJoin
, extraPythonPackages ? (ps: [ ])
, libsForQt5
# unwrapped package parameters
, withGrass ? false
, withServer ? false
, withWebKit ? false
}:
let
qgis-unwrapped = libsForQt5.callPackage ./unwrapped.nix {
withGrass = withGrass;
withServer = withServer;
withWebKit = withWebKit;
};
in
symlinkJoin rec {
inherit (qgis-unwrapped) version;
name = "qgis-${version}";
paths = [ qgis-unwrapped ];
nativeBuildInputs = [
makeWrapper
qgis-unwrapped.py.pkgs.wrapPython
];
# Extend to add to the python environment of QGIS without rebuilding QGIS application.
pythonInputs = qgis-unwrapped.pythonBuildInputs ++ (extraPythonPackages qgis-unwrapped.py.pkgs);
postBuild = ''
buildPythonPath "$pythonInputs"
for program in $out/bin/*; do
wrapProgram $program \
--prefix PATH : $program_PATH \
--set PYTHONPATH $program_PYTHONPATH
done
'';
passthru = {
unwrapped = qgis-unwrapped;
tests.qgis = nixosTests.qgis;
};
meta = qgis-unwrapped.meta;
}

View File

@ -0,0 +1,49 @@
diff --git a/cmake/FindPyQt5.cmake b/cmake/FindPyQt5.cmake
index b51fd0075e..87ee317e05 100644
--- a/cmake/FindPyQt5.cmake
+++ b/cmake/FindPyQt5.cmake
@@ -25,7 +25,7 @@ ELSE(EXISTS PYQT5_VERSION_STR)
IF(SIP_BUILD_EXECUTABLE)
# SIP >= 5.0 path
- FILE(GLOB _pyqt5_metadata "${Python_SITEARCH}/PyQt5-*.dist-info/METADATA")
+ FILE(GLOB _pyqt5_metadata "@pyQt5PackageDir@/PyQt5-*.dist-info/METADATA")
IF(_pyqt5_metadata)
FILE(READ ${_pyqt5_metadata} _pyqt5_metadata_contents)
STRING(REGEX REPLACE ".*\nVersion: ([^\n]+).*$" "\\1" PYQT5_VERSION_STR ${_pyqt5_metadata_contents})
diff --git a/cmake/FindQsci.cmake b/cmake/FindQsci.cmake
index 69e41c1fe9..5456c3d59b 100644
--- a/cmake/FindQsci.cmake
+++ b/cmake/FindQsci.cmake
@@ -24,7 +24,7 @@ ELSE(QSCI_MOD_VERSION_STR)
IF(SIP_BUILD_EXECUTABLE)
# SIP >= 5.0 path
- FILE(GLOB _qsci_metadata "${Python_SITEARCH}/QScintilla*.dist-info/METADATA")
+ FILE(GLOB _qsci_metadata "@qsciPackageDir@/QScintilla*.dist-info/METADATA")
IF(_qsci_metadata)
FILE(READ ${_qsci_metadata} _qsci_metadata_contents)
STRING(REGEX REPLACE ".*\nVersion: ([^\n]+).*$" "\\1" QSCI_MOD_VERSION_STR ${_qsci_metadata_contents})
@@ -33,7 +33,7 @@ ELSE(QSCI_MOD_VERSION_STR)
ENDIF(_qsci_metadata)
IF(QSCI_MOD_VERSION_STR)
- SET(QSCI_SIP_DIR "${PYQT_SIP_DIR}")
+ SET(QSCI_SIP_DIR "@qsciPackageDir@/PyQt5/bindings")
SET(QSCI_FOUND TRUE)
ENDIF(QSCI_MOD_VERSION_STR)
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index 4cd19c3af4..668cc6a5e6 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -212,7 +212,7 @@ if (WITH_GUI)
install(FILES ${QGIS_PYTHON_OUTPUT_DIRECTORY}/_gui.pyi DESTINATION ${QGIS_PYTHON_DIR})
endif()
if(QSCI_SIP_DIR)
- set(SIP_EXTRA_OPTIONS ${SIP_EXTRA_OPTIONS} -I ${QSCI_SIP_DIR})
+ set(SIP_BUILD_EXTRA_OPTIONS ${SIP_BUILD_EXTRA_OPTIONS} --include-dir=${QSCI_SIP_DIR})
else()
message(STATUS "Qsci sip file not found - disabling bindings for derived classes")
set(SIP_DISABLE_FEATURES ${SIP_DISABLE_FEATURES} HAVE_QSCI_SIP)

234
nix/unwrapped.nix Normal file
View File

@ -0,0 +1,234 @@
{ lib
, makeWrapper
, mkDerivation
, replaceVars
, runCommand
, wrapGAppsHook3
, wrapQtAppsHook
, withGrass
, withServer
, withWebKit
, bison
, cmake
, draco
, exiv2
, fcgi
, flex
, geos
, grass
, gsl
, hdf5
, libspatialindex
, libspatialite
, libzip
, netcdf
, ninja
, openssl
, pdal
, libpq
, proj
, protobuf
, python3
, qca-qt5
, qscintilla
, qt3d
, qtbase
, qtkeychain
, qtlocation
, qtmultimedia
, qtsensors
, qtserialport
, qtwebkit
, qtxmlpatterns
, qwt
, sqlite
, txt2tags
, zstd
}:
let
versionSourceFiles = lib.fileset.toSource {
root = ../.;
fileset = ../CMakeLists.txt;
};
qgisSourceFiles =
lib.fileset.difference
(lib.fileset.gitTracked ../.)
(lib.fileset.unions [
# excluded files
./.
../flake.nix
../flake.lock
]);
# Version parsing taken from
# https://github.com/qgis/QGIS/blob/1f0328cff6a8b4cf8a4f8d44a4304b9d9706aa72/rpm/buildrpms.sh#L118
qgisVersion =
lib.replaceStrings [ "\n" ] [ "" ]
(lib.readFile (
runCommand "qgis-version" { } ''
major=$(grep -ie 'SET(CPACK_PACKAGE_VERSION_MAJOR' ${versionSourceFiles}/CMakeLists.txt |
sed -r 's/.*\"([0-9]+)\".*/\1/g')
minor=$(grep -ie 'SET(CPACK_PACKAGE_VERSION_MINOR' ${versionSourceFiles}/CMakeLists.txt |
sed -r 's/.*\"([0-9]+)\".*/\1/g')
patch=$(grep -ie 'SET(CPACK_PACKAGE_VERSION_PATCH' ${versionSourceFiles}/CMakeLists.txt |
sed -r 's/.*\"([0-9]+)\".*/\1/g')
version=$major.$minor.$patch
echo $version > $out
''
));
py = python3.override {
self = py;
packageOverrides = self: super: {
pyqt5 = super.pyqt5.override {
withLocation = true;
withSerialPort = true;
};
};
};
pythonBuildInputs = with py.pkgs; [
chardet
gdal
jinja2
numpy
owslib
psycopg2
pygments
pyqt5
pyqt-builder
python-dateutil
pytz
pyyaml
qscintilla-qt5
requests
setuptools
sip
six
urllib3
];
in
# Print the list of included source files
# lib.fileset.trace qgisSourceFiles
mkDerivation
{
pname = "qgis-unwrapped";
version = qgisVersion; # this is a "Import from derivation (IFD)" !
src = lib.fileset.toSource {
root = ../.;
fileset = qgisSourceFiles;
};
nativeBuildInputs = [
makeWrapper
wrapGAppsHook3
wrapQtAppsHook
bison
cmake
flex
ninja
];
buildInputs = [
draco
exiv2
fcgi
geos
gsl
hdf5
libspatialindex
libspatialite
libzip
netcdf
openssl
pdal
libpq
proj
protobuf
qca-qt5
qscintilla
qt3d
qtbase
qtkeychain
qtlocation
qtmultimedia
qtsensors
qtserialport
qtxmlpatterns
qwt
sqlite
txt2tags
zstd
] ++ lib.optional withGrass grass
++ lib.optional withWebKit qtwebkit
++ pythonBuildInputs;
patches = [
(replaceVars ./set-pyqt-package-dirs.patch {
pyQt5PackageDir = "${py.pkgs.pyqt5}/${py.pkgs.python.sitePackages}";
qsciPackageDir = "${py.pkgs.qscintilla-qt5}/${py.pkgs.python.sitePackages}";
})
];
# Add path to Qt platform plugins
# (offscreen is needed by "${APIS_SRC_DIR}/generate_console_pap.py")
env.QT_QPA_PLATFORM_PLUGIN_PATH = "${qtbase}/${qtbase.qtPluginPrefix}/platforms";
cmakeFlags = [
"-DWITH_3D=True"
"-DWITH_PDAL=True"
"-DENABLE_TESTS=False"
"-DQT_PLUGINS_DIR=${qtbase}/${qtbase.qtPluginPrefix}"
] ++ lib.optional (!withWebKit) "-DWITH_QTWEBKIT=OFF"
++ lib.optional withServer [
"-DWITH_SERVER=True"
"-DQGIS_CGIBIN_SUBDIR=${placeholder "out"}/lib/cgi-bin"
]
++ lib.optional withGrass (
let
gmajor = lib.versions.major grass.version;
gminor = lib.versions.minor grass.version;
in
"-DGRASS_PREFIX${gmajor}=${grass}/grass${gmajor}${gminor}"
);
qtWrapperArgs = [
"--set QT_QPA_PLATFORM_PLUGIN_PATH ${qtbase}/${qtbase.qtPluginPrefix}/platforms"
];
dontWrapGApps = true; # wrapper params passed below
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
'';
passthru = {
inherit pythonBuildInputs;
inherit py;
};
meta = with lib; {
description = "Free and Open Source Geographic Information System";
homepage = "https://www.qgis.org";
license = licenses.gpl2Plus;
maintainers = with maintainers; teams.geospatial.members;
platforms = with platforms; linux;
mainProgram = "qgis";
};
}