From b5f31454a36d0d06ada71e3dde4b3534a5aacea3 Mon Sep 17 00:00:00 2001 From: Luna Nova Date: Sun, 14 Aug 2022 10:18:40 -0700 Subject: [PATCH 1/2] qt-6: Port hooks from qt-5 --- pkgs/development/libraries/qt-6/default.nix | 11 + .../libraries/qt-6/hooks/fix-qmake-libtool.sh | 25 +++ .../qt-6/hooks/fix-qt-builtin-paths.sh | 66 ++++++ .../qt-6/hooks/fix-qt-module-paths.sh | 36 ++++ .../libraries/qt-6/hooks/move-qt-dev-tools.sh | 34 ++++ .../libraries/qt-6/hooks/qmake-hook.sh | 48 +++++ .../libraries/qt-6/hooks/qtbase-setup-hook.sh | 104 +++++++--- .../libraries/qt-6/hooks/wrap-qt-apps-hook.sh | 191 +++++++++--------- .../libraries/qt-6/modules/qtbase.nix | 53 ++++- .../libraries/qt-6/modules/qtdeclarative.nix | 18 +- .../qt-6/modules/qtlanguageserver.nix | 3 + .../libraries/qt-6/{ => patches}/cmake.patch | 0 .../patches/qtbase-qmake-pkg-config.patch | 14 ++ pkgs/development/libraries/qt-6/qtModule.nix | 43 ++++ pkgs/top-level/all-packages.nix | 2 +- 15 files changed, 515 insertions(+), 133 deletions(-) create mode 100644 pkgs/development/libraries/qt-6/hooks/fix-qmake-libtool.sh create mode 100644 pkgs/development/libraries/qt-6/hooks/fix-qt-builtin-paths.sh create mode 100644 pkgs/development/libraries/qt-6/hooks/fix-qt-module-paths.sh create mode 100644 pkgs/development/libraries/qt-6/hooks/move-qt-dev-tools.sh create mode 100644 pkgs/development/libraries/qt-6/hooks/qmake-hook.sh rename pkgs/development/libraries/qt-6/{ => patches}/cmake.patch (100%) create mode 100644 pkgs/development/libraries/qt-6/patches/qtbase-qmake-pkg-config.patch diff --git a/pkgs/development/libraries/qt-6/default.nix b/pkgs/development/libraries/qt-6/default.nix index 7615c4f90431..754436cb0b96 100644 --- a/pkgs/development/libraries/qt-6/default.nix +++ b/pkgs/development/libraries/qt-6/default.nix @@ -49,6 +49,9 @@ let withGtk3 = true; inherit (srcs.qtbase) src version; inherit bison cups harfbuzz libGL dconf gtk3 developerBuild cmake; + patches = [ + ./patches/qtbase-qmake-pkg-config.patch + ]; }; qt3d = callPackage ./modules/qt3d.nix { }; @@ -90,6 +93,14 @@ let wrapQtAppsHook = makeSetupHook { deps = [ buildPackages.makeWrapper ]; } ./hooks/wrap-qt-apps-hook.sh; + + qmake = makeSetupHook { + deps = [ self.qtbase.dev ]; + substitutions = { + inherit debug; + fix_qmake_libtool = ./hooks/fix-qmake-libtool.sh; + }; + } ./hooks/qmake-hook.sh; }; self = lib.makeScope newScope addPackages; diff --git a/pkgs/development/libraries/qt-6/hooks/fix-qmake-libtool.sh b/pkgs/development/libraries/qt-6/hooks/fix-qmake-libtool.sh new file mode 100644 index 000000000000..44bf10a0db06 --- /dev/null +++ b/pkgs/development/libraries/qt-6/hooks/fix-qmake-libtool.sh @@ -0,0 +1,25 @@ +# Fix libtool libraries generated by qmake. +# qmake started inserting filenames of shared objects instead of the appropriate +# linker flags. fixQmakeLibtool searches for broken libtool libraries and +# replaces the filenames with the linker flags that should have been there. +fixQmakeLibtool() { + if [ -d "$1" ]; then + find "$1" -name '*.la' | while read la; do + set +e + framework_libs=$(grep '^dependency_libs' "$la" | grep -Eo -- '-framework +\w+' | tr '\n' ' ') + set -e + sed -i "$la" \ + -e '/^dependency_libs/ s,\(/[^ ]\+\)/lib\([^/ ]\+\)\.so,-L\1 -l\2,g' \ + -e '/^dependency_libs/ s,-framework \+\w\+,,g' + if [ ! -z "$framework_libs" ]; then + if grep '^inherited_linker_flags=' $la >/dev/null; then + sed -i "$la" -e "s/^\(inherited_linker_flags='[^']*\)/\1 $framework_libs/" + else + echo "inherited_linker_flags='$framework_libs'" >>"$la" + fi + fi + done + fi +} + +fixupOutputHooks+=('fixQmakeLibtool $prefix') diff --git a/pkgs/development/libraries/qt-6/hooks/fix-qt-builtin-paths.sh b/pkgs/development/libraries/qt-6/hooks/fix-qt-builtin-paths.sh new file mode 100644 index 000000000000..0fd0aee7dbf9 --- /dev/null +++ b/pkgs/development/libraries/qt-6/hooks/fix-qt-builtin-paths.sh @@ -0,0 +1,66 @@ +# fixQtBuiltinPaths +# +# Usage: fixQtBuiltinPaths _dir_ _pattern_ +# +# Fix Qt builtin paths in files matching _pattern_ under _dir_. +# +fixQtBuiltinPaths() { + local dir="$1" + local pattern="$2" + local bin="${!outputBin}" + local dev="${!outputDev}" + local doc="${!outputDoc}" + local lib="${!outputLib}" + + if [ -d "$dir" ]; then + find "$dir" -name "$pattern" | while read pr_; do + if grep -q '\$\$\[QT_' "${pr_:?}"; then + echo "fixQtBuiltinPaths: Fixing Qt builtin paths in \`${pr_:?}'..." + sed -i "${pr_:?}" \ + -e "s|\\\$\\\$\\[QT_HOST_BINS[^]]*\\]|$dev/bin|g" \ + -e "s|\\\$\\\$\\[QT_HOST_LIBEXECS[^]]*\\]|$dev/libexec|g" \ + -e "s|\\\$\\\$\\[QT_HOST_DATA[^]]*\\]/mkspecs|$dev/mkspecs|g" \ + -e "s|\\\$\\\$\\[QT_HOST_PREFIX[^]]*\\]|$dev|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_ARCHDATA[^]]*\\]|$lib|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_BINS[^]]*\\]|$bin/bin|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_CONFIGURATION[^]]*\\]|$bin|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_DATA[^]]*\\]|$lib|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_DOCS[^]]*\\]|$doc/share/doc|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_EXAMPLES[^]]*\\]|$doc/examples|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_HEADERS[^]]*\\]|$dev/include|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_LIBS[^]]*\\]|$lib/lib|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_LIBEXECS[^]]*\\]|$lib/libexec|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_PLUGINS[^]]*\\]|$bin/$qtPluginPrefix|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_PREFIX[^]]*\\]|$lib|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_TESTS[^]]*\\]|$dev/tests|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_TRANSLATIONS[^]]*\\]|$lib/translations|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_QML[^]]*\\]|$bin/$qtQmlPrefix|g" + fi + done + elif [ -e "$dir" ]; then + if grep -q '\$\$\[QT_' "${dir:?}"; then + echo "fixQtBuiltinPaths: Fixing Qt builtin paths in \`${dir:?}'..." + sed -i "${dir:?}" \ + -e "s|\\\$\\\$\\[QT_HOST_BINS[^]]*\\]|$dev/bin|g" \ + -e "s|\\\$\\\$\\[QT_HOST_LIBEXECS[^]]*\\]|$dev/libexec|g" \ + -e "s|\\\$\\\$\\[QT_HOST_DATA[^]]*\\]/mkspecs|$dev/mkspecs|g" \ + -e "s|\\\$\\\$\\[QT_HOST_PREFIX[^]]*\\]|$dev|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_ARCHDATA[^]]*\\]|$lib|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_BINS[^]]*\\]|$bin/bin|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_CONFIGURATION[^]]*\\]|$bin|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_DATA[^]]*\\]|$lib|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_DOCS[^]]*\\]|$doc/share/doc|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_EXAMPLES[^]]*\\]|$doc/examples|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_HEADERS[^]]*\\]|$dev/include|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_LIBS[^]]*\\]|$lib/lib|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_LIBEXECS[^]]*\\]|$lib/libexec|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_PLUGINS[^]]*\\]|$bin/$qtPluginPrefix|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_PREFIX[^]]*\\]|$lib|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_TESTS[^]]*\\]|$dev/tests|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_TRANSLATIONS[^]]*\\]|$lib/translations|g" \ + -e "s|\\\$\\\$\\[QT_INSTALL_QML[^]]*\\]|$bin/$qtQmlPrefix|g" + fi + else + echo "fixQtBuiltinPaths: Warning: \`$dir' does not exist" + fi +} diff --git a/pkgs/development/libraries/qt-6/hooks/fix-qt-module-paths.sh b/pkgs/development/libraries/qt-6/hooks/fix-qt-module-paths.sh new file mode 100644 index 000000000000..0a0e0d51e27d --- /dev/null +++ b/pkgs/development/libraries/qt-6/hooks/fix-qt-module-paths.sh @@ -0,0 +1,36 @@ +# fixQtModulePaths +# +# Usage: fixQtModulePaths _dir_ +# +# Find Qt module definitions in directory _dir_ and patch the module paths. +# +fixQtModulePaths() { + local dir="$1" + local bin="${!outputBin}" + local dev="${!outputDev}" + local lib="${!outputLib}" + + if [ -d "$dir" ]; then + find "$dir" -name 'qt_*.pri' | while read pr; do + if grep -q '\$\$QT_MODULE_' "${pr:?}"; then + echo "fixQtModulePaths: Fixing module paths in \`${pr:?}'..." + sed -i "${pr:?}" \ + -e "s|\\\$\\\$QT_MODULE_LIB_BASE|$lib/lib|g" \ + -e "s|\\\$\\\$QT_MODULE_HOST_LIB_BASE|$lib/lib|g" \ + -e "s|\\\$\\\$QT_MODULE_INCLUDE_BASE|$dev/include|g" \ + -e "s|\\\$\\\$QT_MODULE_BIN_BASE|$dev/bin|g" + fi + done + elif [ -e "$dir" ]; then + echo "fixQtModulePaths: Warning: \`$dir' is not a directory" + else + echo "fixQtModulePaths: Warning: \`$dir' does not exist" + fi + + if [ "z$bin" != "z$dev" ]; then + if [ -d "$bin/bin" ]; then + mkdir -p "$dev/bin" + lndir -silent "$bin/bin" "$dev/bin" + fi + fi +} diff --git a/pkgs/development/libraries/qt-6/hooks/move-qt-dev-tools.sh b/pkgs/development/libraries/qt-6/hooks/move-qt-dev-tools.sh new file mode 100644 index 000000000000..85489c85105b --- /dev/null +++ b/pkgs/development/libraries/qt-6/hooks/move-qt-dev-tools.sh @@ -0,0 +1,34 @@ +updateToolPath() { + local tool="$1" + local target="$2" + local original="${!outputBin}/$tool" + local actual="${!outputDev}/$tool" + if grep -q "$original" "$target"; then + echo "updateToolPath: Updating \`$original' in \`$target\'..." + sed -i "$target" -e "s|$original|$actual|" + fi +} + +moveQtDevTools() { + if [ -n "$devTools" ]; then + for tool in $devTools; do + moveToOutput "$tool" "${!outputDev}" + done + + if [ -d "${!outputDev}/mkspecs" ]; then + find "${!outputDev}/mkspecs" -name '*.pr?' | while read pr_; do + for tool in $devTools; do + updateToolPath "$tool" "$pr_" + done + done + fi + + if [ -d "${!outputDev}/lib/cmake" ]; then + find "${!outputDev}/lib/cmake" -name '*.cmake' | while read cmake; do + for tool in $devTools; do + updateToolPath "$tool" "$cmake" + done + done + fi + fi +} diff --git a/pkgs/development/libraries/qt-6/hooks/qmake-hook.sh b/pkgs/development/libraries/qt-6/hooks/qmake-hook.sh new file mode 100644 index 000000000000..ec1d2ea6124e --- /dev/null +++ b/pkgs/development/libraries/qt-6/hooks/qmake-hook.sh @@ -0,0 +1,48 @@ +. @fix_qmake_libtool@ + +qmakeFlags=(${qmakeFlags-}) + +qmakePrePhase() { + qmakeFlags_orig=("${qmakeFlags[@]}") + + # These flags must be added _before_ the flags specified in the derivation. + # TODO: these flags also need a patch which isn't applied + # can we either remove these flags or update the qt5 patch? + # "NIX_OUTPUT_DOC=${!outputDev}/${qtDocPrefix:?}" \ + qmakeFlags=( + "PREFIX=$out" + "NIX_OUTPUT_OUT=$out" + "NIX_OUTPUT_DEV=${!outputDev}" + "NIX_OUTPUT_BIN=${!outputBin}" + "NIX_OUTPUT_QML=${!outputBin}/${qtQmlPrefix:?}" + "NIX_OUTPUT_PLUGIN=${!outputBin}/${qtPluginPrefix:?}" + ) + + if [ -n "@debug@" ]; then + qmakeFlags+=("CONFIG+=debug") + else + qmakeFlags+=("CONFIG+=release") + fi + + qmakeFlags+=("${qmakeFlags_orig[@]}") +} +prePhases+=" qmakePrePhase" + +qmakeConfigurePhase() { + runHook preConfigure + + echo "QMAKEPATH=$QMAKEPATH" + echo qmake "${qmakeFlags[@]}" + qmake "${qmakeFlags[@]}" + + if ! [[ -v enableParallelBuilding ]]; then + enableParallelBuilding=1 + echo "qmake: enabled parallel building" + fi + + runHook postConfigure +} + +if [ -z "${dontUseQmakeConfigure-}" -a -z "${configurePhase-}" ]; then + configurePhase=qmakeConfigurePhase +fi diff --git a/pkgs/development/libraries/qt-6/hooks/qtbase-setup-hook.sh b/pkgs/development/libraries/qt-6/hooks/qtbase-setup-hook.sh index 0ae969e8f8fc..613b42fc97e9 100644 --- a/pkgs/development/libraries/qt-6/hooks/qtbase-setup-hook.sh +++ b/pkgs/development/libraries/qt-6/hooks/qtbase-setup-hook.sh @@ -7,37 +7,87 @@ if [[ -n "${__nix_qtbase-}" ]]; then exit 1 fi else # Only set up Qt once. -__nix_qtbase="@dev@" + __nix_qtbase="@dev@" -qtPluginPrefix=@qtPluginPrefix@ -qtQmlPrefix=@qtQmlPrefix@ + qtPluginPrefix=@qtPluginPrefix@ + qtQmlPrefix=@qtQmlPrefix@ -# Disable debug symbols if qtbase was built without debugging. -# This stops -dev paths from leaking into other outputs. -if [ -z "@debug@" ]; then - NIX_CFLAGS_COMPILE="${NIX_CFLAGS_COMPILE-}${NIX_CFLAGS_COMPILE:+ }-DQT_NO_DEBUG" -fi + . @fix_qt_builtin_paths@ + . @fix_qt_module_paths@ -# Integration with CMake: -# Set the CMake build type corresponding to how qtbase was built. -if [ -n "@debug@" ]; then - cmakeBuildType="Debug" -else - cmakeBuildType="Release" -fi - -qtPreHook() { - # Check that wrapQtAppsHook is used, or it is explicitly disabled. - if [[ -z "$__nix_wrapQtAppsHook" && -z "$dontWrapQtApps" ]]; then - echo >&2 "Error: wrapQtAppsHook is not used, and dontWrapQtApps is not set." - exit 1 + # Disable debug symbols if qtbase was built without debugging. + # This stops -dev paths from leaking into other outputs. + if [ -z "@debug@" ]; then + NIX_CFLAGS_COMPILE="${NIX_CFLAGS_COMPILE-}${NIX_CFLAGS_COMPILE:+ }-DQT_NO_DEBUG" fi -} -prePhases+=" qtPreHook" -addQtModulePrefix () { - addToSearchPath QT_ADDITIONAL_PACKAGES_PREFIX_PATH $1 -} -addEnvHooks "$hostOffset" addQtModulePrefix + # Integration with CMake: + # Set the CMake build type corresponding to how qtbase was built. + if [ -n "@debug@" ]; then + cmakeBuildType="Debug" + else + cmakeBuildType="Release" + fi + + # Build tools are often confused if QMAKE is unset. + export QMAKE=@dev@/bin/qmake + + export QMAKEPATH= + + export QMAKEMODULES= + + declare -Ag qmakePathSeen=() + qmakePathHook() { + # Skip this path if we have seen it before. + # MUST use 'if' because 'qmakePathSeen[$]' may be unset. + if [ -n "${qmakePathSeen[$1]-}" ]; then return; fi + qmakePathSeen[$1]=1 + if [ -d "$1/mkspecs" ]; then + QMAKEMODULES="${QMAKEMODULES}${QMAKEMODULES:+:}/mkspecs" + QMAKEPATH="${QMAKEPATH}${QMAKEPATH:+:}$1" + fi + } + envBuildHostHooks+=(qmakePathHook) + + postPatchMkspecs() { + # Prevent this hook from running multiple times + dontPatchMkspecs=1 + + local bin="${!outputBin}" + local dev="${!outputDev}" + local doc="${!outputDoc}" + local lib="${!outputLib}" + + moveToOutput "mkspecs" "$dev" + + if [ -d "$dev/mkspecs/modules" ]; then + fixQtModulePaths "$dev/mkspecs/modules" + fi + + if [ -d "$dev/mkspecs" ]; then + fixQtBuiltinPaths "$dev/mkspecs" '*.pr?' + fi + + if [ -d "$lib" ]; then + fixQtBuiltinPaths "$lib" '*.pr?' + fi + } + if [ -z "${dontPatchMkspecs-}" ]; then + postPhases="${postPhases-}${postPhases:+ }postPatchMkspecs" + fi + + qtPreHook() { + # Check that wrapQtAppsHook is used, or it is explicitly disabled. + if [[ -z "$__nix_wrapQtAppsHook" && -z "$dontWrapQtApps" ]]; then + echo >&2 "Error: wrapQtAppsHook is not used, and dontWrapQtApps is not set." + exit 1 + fi + } + prePhases+=" qtPreHook" + + addQtModulePrefix() { + addToSearchPath QT_ADDITIONAL_PACKAGES_PREFIX_PATH $1 + } + addEnvHooks "$hostOffset" addQtModulePrefix fi diff --git a/pkgs/development/libraries/qt-6/hooks/wrap-qt-apps-hook.sh b/pkgs/development/libraries/qt-6/hooks/wrap-qt-apps-hook.sh index b669da3d058c..8b135a7d7492 100644 --- a/pkgs/development/libraries/qt-6/hooks/wrap-qt-apps-hook.sh +++ b/pkgs/development/libraries/qt-6/hooks/wrap-qt-apps-hook.sh @@ -1,110 +1,99 @@ if [[ -z "${__nix_wrapQtAppsHook-}" ]]; then -__nix_wrapQtAppsHook=1 # Don't run this hook more than once. + __nix_wrapQtAppsHook=1 # Don't run this hook more than once. -# Inherit arguments given in mkDerivation -qtWrapperArgs=( ${qtWrapperArgs-} ) + # Inherit arguments given in mkDerivation + qtWrapperArgs=(${qtWrapperArgs-}) -qtHostPathSeen=() + qtHostPathSeen=() -qtUnseenHostPath() { - for pkg in "${qtHostPathSeen[@]}" - do - if [ "${pkg:?}" == "$1" ] - then - return 1 - fi - done - - qtHostPathSeen+=("$1") - return 0 -} - -qtHostPathHook() { - qtUnseenHostPath "$1" || return 0 - - if ! [ -v qtPluginPrefix ] - then - echo "wrapQtAppsHook qtHostPathHook: qtPluginPrefix is unset. hint: add qt6.qtbase to buildInputs" - fi - - local pluginDir="$1/${qtPluginPrefix:?}" - if [ -d "$pluginDir" ] - then - qtWrapperArgs+=(--prefix QT_PLUGIN_PATH : "$pluginDir") - fi - - local qmlDir="$1/${qtQmlPrefix:?}" - if [ -d "$qmlDir" ] - then - qtWrapperArgs+=(--prefix QML2_IMPORT_PATH : "$qmlDir") - fi -} -addEnvHooks "$targetOffset" qtHostPathHook - -makeQtWrapper() { - local original="$1" - local wrapper="$2" - shift 2 - makeWrapper "$original" "$wrapper" "${qtWrapperArgs[@]}" "$@" -} - -wrapQtApp() { - local program="$1" - shift 1 - wrapProgram "$program" "${qtWrapperArgs[@]}" "$@" -} - -qtOwnPathsHook() { - local xdgDataDir="${!outputBin}/share" - if [ -d "$xdgDataDir" ] - then - qtWrapperArgs+=(--prefix XDG_DATA_DIRS : "$xdgDataDir") - fi - - local xdgConfigDir="${!outputBin}/etc/xdg" - if [ -d "$xdgConfigDir" ] - then - qtWrapperArgs+=(--prefix XDG_CONFIG_DIRS : "$xdgConfigDir") - fi - - qtHostPathHook "${!outputBin}" -} - -preFixupPhases+=" qtOwnPathsHook" - -# Note: $qtWrapperArgs still gets defined even if ${dontWrapQtApps-} is set. -wrapQtAppsHook() { - # skip this hook when requested - [ -z "${dontWrapQtApps-}" ] || return 0 - - # guard against running multiple times (e.g. due to propagation) - [ -z "$wrapQtAppsHookHasRun" ] || return 0 - wrapQtAppsHookHasRun=1 - - local targetDirs=( "$prefix/bin" "$prefix/sbin" "$prefix/libexec" "$prefix/Applications" "$prefix/"*.app ) - echo "wrapping Qt applications in ${targetDirs[@]}" - - for targetDir in "${targetDirs[@]}" - do - [ -d "$targetDir" ] || continue - - find "$targetDir" ! -type d -executable -print0 | while IFS= read -r -d '' file - do - if [ -f "$file" ] - then - echo "wrapping $file" - wrapQtApp "$file" - elif [ -h "$file" ] - then - target="$(readlink -e "$file")" - echo "wrapping $file -> $target" - rm "$file" - makeQtWrapper "$target" "$file" + qtUnseenHostPath() { + for pkg in "${qtHostPathSeen[@]}"; do + if [ "${pkg:?}" == "$1" ]; then + return 1 fi done - done -} -fixupOutputHooks+=(wrapQtAppsHook) + qtHostPathSeen+=("$1") + return 0 + } + + qtHostPathHook() { + qtUnseenHostPath "$1" || return 0 + + if ! [ -v qtPluginPrefix ]; then + echo "wrapQtAppsHook qtHostPathHook: qtPluginPrefix is unset. hint: add qt6.qtbase to buildInputs" + fi + + local pluginDir="$1/${qtPluginPrefix:?}" + if [ -d "$pluginDir" ]; then + qtWrapperArgs+=(--prefix QT_PLUGIN_PATH : "$pluginDir") + fi + + local qmlDir="$1/${qtQmlPrefix:?}" + if [ -d "$qmlDir" ]; then + qtWrapperArgs+=(--prefix QML2_IMPORT_PATH : "$qmlDir") + fi + } + addEnvHooks "$targetOffset" qtHostPathHook + + makeQtWrapper() { + local original="$1" + local wrapper="$2" + shift 2 + makeWrapper "$original" "$wrapper" "${qtWrapperArgs[@]}" "$@" + } + + wrapQtApp() { + local program="$1" + shift 1 + wrapProgram "$program" "${qtWrapperArgs[@]}" "$@" + } + + qtOwnPathsHook() { + local xdgDataDir="${!outputBin}/share" + if [ -d "$xdgDataDir" ]; then + qtWrapperArgs+=(--prefix XDG_DATA_DIRS : "$xdgDataDir") + fi + + local xdgConfigDir="${!outputBin}/etc/xdg" + if [ -d "$xdgConfigDir" ]; then + qtWrapperArgs+=(--prefix XDG_CONFIG_DIRS : "$xdgConfigDir") + fi + + qtHostPathHook "${!outputBin}" + } + + preFixupPhases+=" qtOwnPathsHook" + + # Note: $qtWrapperArgs still gets defined even if ${dontWrapQtApps-} is set. + wrapQtAppsHook() { + # skip this hook when requested + [ -z "${dontWrapQtApps-}" ] || return 0 + + # guard against running multiple times (e.g. due to propagation) + [ -z "$wrapQtAppsHookHasRun" ] || return 0 + wrapQtAppsHookHasRun=1 + + local targetDirs=("$prefix/bin" "$prefix/sbin" "$prefix/libexec" "$prefix/Applications" "$prefix/"*.app) + echo "wrapping Qt applications in ${targetDirs[@]}" + + for targetDir in "${targetDirs[@]}"; do + [ -d "$targetDir" ] || continue + + find "$targetDir" ! -type d -executable -print0 | while IFS= read -r -d '' file; do + if [ -f "$file" ]; then + echo "wrapping $file" + wrapQtApp "$file" + elif [ -h "$file" ]; then + target="$(readlink -e "$file")" + echo "wrapping $file -> $target" + rm "$file" + makeQtWrapper "$target" "$file" + fi + done + done + } + + fixupOutputHooks+=(wrapQtAppsHook) fi diff --git a/pkgs/development/libraries/qt-6/modules/qtbase.nix b/pkgs/development/libraries/qt-6/modules/qtbase.nix index cdae37ba54d2..8717b94545b6 100644 --- a/pkgs/development/libraries/qt-6/modules/qtbase.nix +++ b/pkgs/development/libraries/qt-6/modules/qtbase.nix @@ -182,6 +182,15 @@ stdenv.mkDerivation rec { substituteInPlace src/corelib/CMakeLists.txt --replace /bin/ls ${coreutils}/bin/ls ''; + fix_qt_builtin_paths = ../hooks/fix-qt-builtin-paths.sh; + fix_qt_module_paths = ../hooks/fix-qt-module-paths.sh; + preHook = '' + . "$fix_qt_builtin_paths" + . "$fix_qt_module_paths" + . ${../hooks/move-qt-dev-tools.sh} + . ${../hooks/fix-qmake-libtool.sh} + ''; + qtPluginPrefix = "lib/qt-6/plugins"; qtQmlPrefix = "lib/qt-6/qml"; @@ -199,8 +208,46 @@ stdenv.mkDerivation rec { outputs = [ "out" "dev" ]; postInstall = '' - mkdir -p $dev - mv $out/mkspecs $out/bin $out/libexec $dev/ + moveToOutput "mkspecs" "$dev" + ''; + + devTools = [ + "libexec/moc" + "libexec/rcc" + "libexec/syncqt.pl" + "libexec/qlalr" + "libexec/ensure_pro_file.cmake" + "libexec/cmake_automoc_parser" + "libexec/qvkgen" + "libexec/tracegen" + "libexec/uic" + "bin/fixqt4headers.pl" + "bin/moc" + "bin/qdbuscpp2xml" + "bin/qdbusxml2cpp" + "bin/qlalr" + "bin/qmake" + "bin/rcc" + "bin/syncqt.pl" + "bin/uic" + ]; + + postFixup = '' + # Don't retain build-time dependencies like gdb. + sed '/QMAKE_DEFAULT_.*DIRS/ d' -i $dev/mkspecs/qconfig.pri + fixQtModulePaths "''${!outputDev}/mkspecs/modules" + fixQtBuiltinPaths "''${!outputDev}" '*.pr?' + + # Move development tools to $dev + moveQtDevTools + moveToOutput bin "$dev" + moveToOutput libexec "$dev" + + # fixup .pc file (where to find 'moc' etc.) + sed -i "$dev/lib/pkgconfig/Qt6Core.pc" \ + -e "/^bindir=/ c bindir=$dev/bin" + + patchShebangs $out $dev ''; dontStrip = debugSymbols; @@ -211,7 +258,7 @@ stdenv.mkDerivation rec { homepage = "https://www.qt.io/"; description = "A cross-platform application framework for C++"; license = with licenses; [ fdl13 gpl2 lgpl21 lgpl3 ]; - maintainers = with maintainers; [ milahu nickcao ]; + maintainers = with maintainers; [ milahu nickcao LunNova ]; platforms = platforms.linux; }; } diff --git a/pkgs/development/libraries/qt-6/modules/qtdeclarative.nix b/pkgs/development/libraries/qt-6/modules/qtdeclarative.nix index 3f691894ff86..b76baea49930 100644 --- a/pkgs/development/libraries/qt-6/modules/qtdeclarative.nix +++ b/pkgs/development/libraries/qt-6/modules/qtdeclarative.nix @@ -12,8 +12,24 @@ qtModule { preConfigure = '' export LD_LIBRARY_PATH="$PWD/build/lib''${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH" ''; + cmakeFlags = [ + "-DQT6_INSTALL_PREFIX=${placeholder "out"}" + "-DQT_INSTALL_PREFIX=${placeholder "out"}" + ]; postInstall = '' substituteInPlace "$out/lib/cmake/Qt6Qml/Qt6QmlMacros.cmake" \ - --replace ''\'''${QT6_INSTALL_PREFIX}' "$out" + --replace ''\'''${QT6_INSTALL_PREFIX}' "$dev" ''; + devTools = [ + "bin/qml" + "bin/qmlcachegen" + "bin/qmleasing" + "bin/qmlimportscanner" + "bin/qmllint" + "bin/qmlmin" + "bin/qmlplugindump" + "bin/qmlprofiler" + "bin/qmlscene" + "bin/qmltestrunner" + ]; } diff --git a/pkgs/development/libraries/qt-6/modules/qtlanguageserver.nix b/pkgs/development/libraries/qt-6/modules/qtlanguageserver.nix index b2e6e450d5f7..07115d6755a5 100644 --- a/pkgs/development/libraries/qt-6/modules/qtlanguageserver.nix +++ b/pkgs/development/libraries/qt-6/modules/qtlanguageserver.nix @@ -5,4 +5,7 @@ qtModule { pname = "qtlanguageserver"; qtInputs = [ qtbase ]; + + # Doesn't have version set + dontCheckQtModuleVersion = true; } diff --git a/pkgs/development/libraries/qt-6/cmake.patch b/pkgs/development/libraries/qt-6/patches/cmake.patch similarity index 100% rename from pkgs/development/libraries/qt-6/cmake.patch rename to pkgs/development/libraries/qt-6/patches/cmake.patch diff --git a/pkgs/development/libraries/qt-6/patches/qtbase-qmake-pkg-config.patch b/pkgs/development/libraries/qt-6/patches/qtbase-qmake-pkg-config.patch new file mode 100644 index 000000000000..90caaea1cf4d --- /dev/null +++ b/pkgs/development/libraries/qt-6/patches/qtbase-qmake-pkg-config.patch @@ -0,0 +1,14 @@ +diff --git a/qmake/generators/makefile.cpp b/qmake/generators/makefile.cpp +--- a/qmake/generators/makefile.cpp ++++ b/qmake/generators/makefile.cpp +@@ -3390,8 +3390,7 @@ MakefileGenerator::writePkgConfigFile() + << varGlue("QMAKE_PKGCONFIG_CFLAGS", "", " ", " ") + // << varGlue("DEFINES","-D"," -D"," ") + ; +- if (!project->values("QMAKE_DEFAULT_INCDIRS").contains(includeDir)) +- t << "-I${includedir}"; ++ t << "-I${includedir}"; + if (target_mode == TARG_MAC_MODE && project->isActiveConfig("lib_bundle") + && libDir != QLatin1String("/Library/Frameworks")) { + t << " -F${libdir}"; + diff --git a/pkgs/development/libraries/qt-6/qtModule.nix b/pkgs/development/libraries/qt-6/qtModule.nix index 04349ed68ad2..999d69ce683a 100644 --- a/pkgs/development/libraries/qt-6/qtModule.nix +++ b/pkgs/development/libraries/qt-6/qtModule.nix @@ -18,9 +18,15 @@ stdenv.mkDerivation (args // { perl cmake ninja + self.qmake ]; propagatedBuildInputs = args.qtInputs ++ (args.propagatedBuildInputs or [ ]); + preHook = '' + . ${./hooks/move-qt-dev-tools.sh} + . ${./hooks/fix-qt-builtin-paths.sh} + ''; + outputs = args.outputs or [ "out" "dev" ]; dontWrapQtApps = args.dontWrapQtApps or true; @@ -32,9 +38,46 @@ stdenv.mkDerivation (args // { moveToOutput "$dir" "$dev" done fi + fixQtBuiltinPaths $out/lib "*.pr?" ${args.postInstall or ""} ''; + preConfigure = args.preConfigure or "" + '' + fixQtBuiltinPaths . '*.pr?' + '' + lib.optionalString (builtins.compareVersions "5.15.0" version <= 0) + # Note: We use ${version%%-*} to remove any tag from the end of the version + # string. Version tags are added by Nixpkgs maintainers and not reflected in + # the source version. + '' + if [[ -z "$dontCheckQtModuleVersion" ]] \ + && grep -q '^MODULE_VERSION' .qmake.conf 2>/dev/null \ + && ! grep -q -F "''${version%%-*}" .qmake.conf 2>/dev/null + then + echo >&2 "error: could not find version ''${version%%-*} in .qmake.conf" + echo >&2 "hint: check .qmake.conf and update the package version in Nixpkgs" + exit 1 + fi + + if [[ -z "$dontSyncQt" && -f sync.profile ]]; then + # FIXME: this probably breaks crosscompiling as it's not from nativeBuildInputs + # I don't know how to get /libexec from nativeBuildInputs to work, it's not under /bin + ${self.qtbase.dev.nativeDrv or self.qtbase.dev}/libexec/syncqt.pl -version "''${version%%-*}" + fi + ''; + + postFixup = '' + if [ -d "''${!outputDev}/lib/pkgconfig" ]; then + find "''${!outputDev}/lib/pkgconfig" -name '*.pc' | while read pc; do + sed -i "$pc" \ + -e "/^prefix=/ c prefix=''${!outputLib}" \ + -e "/^exec_prefix=/ c exec_prefix=''${!outputBin}" \ + -e "/^includedir=/ c includedir=''${!outputDev}/include" + done + fi + + moveQtDevTools + '' + args.postFixup or ""; + meta = with lib; { homepage = "https://www.qt.io/"; description = "A cross-platform application framework for C++"; diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 0da2d62627be..14ff37838b07 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -21544,7 +21544,7 @@ with pkgs; inherit buildPackages; cmake = cmake.overrideAttrs (attrs: { patches = attrs.patches ++ [ - ../development/libraries/qt-6/cmake.patch + ../development/libraries/qt-6/patches/cmake.patch ]; }); }); From 1824a128a150d743f1c6614ae849ad64894057a1 Mon Sep 17 00:00:00 2001 From: Luna Nova Date: Sun, 14 Aug 2022 10:29:56 -0700 Subject: [PATCH 2/2] PyQt6: init at 6.4.0 --- pkgs/development/python-modules/pyqt/6.x.nix | 138 ++++++++++++++++++ .../python-modules/pyqt/pyqt6-sip.nix | 28 ++++ pkgs/top-level/python-packages.nix | 4 + 3 files changed, 170 insertions(+) create mode 100644 pkgs/development/python-modules/pyqt/6.x.nix create mode 100644 pkgs/development/python-modules/pyqt/pyqt6-sip.nix diff --git a/pkgs/development/python-modules/pyqt/6.x.nix b/pkgs/development/python-modules/pyqt/6.x.nix new file mode 100644 index 000000000000..c2ef82a158c6 --- /dev/null +++ b/pkgs/development/python-modules/pyqt/6.x.nix @@ -0,0 +1,138 @@ +{ lib +, buildPythonPackage +, isPy27 +, fetchPypi +, pkg-config +, dbus +, lndir +, setuptools +, dbus-python +, sip +, pyqt6-sip +, pyqt-builder +, qt6Packages +, pythonOlder +, withMultimedia ? true +, withWebSockets ? true +# FIXME: Once QtLocation is available for Qt6 enable this +# https://bugreports.qt.io/browse/QTBUG-96795 +#, withLocation ? true +# Not currently part of PyQt6 +#, withConnectivity ? true +}: + +buildPythonPackage rec { + pname = "PyQt6"; + version = "6.4.0"; + format = "pyproject"; + + disabled = pythonOlder "3.6"; + + src = fetchPypi { + inherit pname version; + sha256 = "sha256-kTkkab4fSRkF+p54+k5AWaiathbd8uz9UlvB1lwmu5M="; + }; + + patches = [ + # Fix some wrong assumptions by ./project.py + # TODO: figure out how to send this upstream + # FIXME: make a version for PyQt6? + # ./pyqt5-fix-dbus-mainloop-support.patch + # confirm license when installing via pyqt6_sip + ./pyqt5-confirm-license.patch + ]; + + # be more verbose + postPatch = '' + cat >> pyproject.toml < error: could not create 'PyQt5/sip.cpython-38-x86_64-linux-gnu.so': No such file or directory + doCheck = false; + pythonImportsCheck = [ "PyQt6.sip" ]; + + meta = with lib; { + description = "Python bindings for Qt5"; + homepage = "https://www.riverbankcomputing.com/software/sip/"; + license = licenses.gpl3Only; + platforms = platforms.mesaPlatforms; + maintainers = with maintainers; [ LunNova ]; + }; +} diff --git a/pkgs/top-level/python-packages.nix b/pkgs/top-level/python-packages.nix index 98e7c6c5f273..d6275e47cf21 100644 --- a/pkgs/top-level/python-packages.nix +++ b/pkgs/top-level/python-packages.nix @@ -8367,6 +8367,10 @@ in { withWebKit = true; }; + pyqt6 = callPackage ../development/python-modules/pyqt/6.x.nix { }; + + pyqt6-sip = callPackage ../development/python-modules/pyqt/pyqt6-sip.nix { }; + pyqtgraph = callPackage ../development/python-modules/pyqtgraph { }; pyqtwebengine = pkgs.libsForQt5.callPackage ../development/python-modules/pyqtwebengine {