summary refs log tree commit diff
path: root/pkgs/development/libraries/qt-6
diff options
context:
space:
mode:
authorLuna Nova <git@lunnova.dev>2022-08-14 10:18:40 -0700
committerLuna Nova <git@lunnova.dev>2022-10-05 22:09:13 -0700
commitb5f31454a36d0d06ada71e3dde4b3534a5aacea3 (patch)
treee8866f6253d0e283bf6b84d4277a81dc7bb26c80 /pkgs/development/libraries/qt-6
parent7e7c3e77fbdae2bb5d2fb776409df4f4a3c936cf (diff)
qt-6: Port hooks from qt-5
Diffstat (limited to 'pkgs/development/libraries/qt-6')
-rw-r--r--pkgs/development/libraries/qt-6/default.nix11
-rw-r--r--pkgs/development/libraries/qt-6/hooks/fix-qmake-libtool.sh25
-rw-r--r--pkgs/development/libraries/qt-6/hooks/fix-qt-builtin-paths.sh66
-rw-r--r--pkgs/development/libraries/qt-6/hooks/fix-qt-module-paths.sh36
-rw-r--r--pkgs/development/libraries/qt-6/hooks/move-qt-dev-tools.sh34
-rw-r--r--pkgs/development/libraries/qt-6/hooks/qmake-hook.sh48
-rw-r--r--pkgs/development/libraries/qt-6/hooks/qtbase-setup-hook.sh102
-rw-r--r--pkgs/development/libraries/qt-6/hooks/wrap-qt-apps-hook.sh191
-rw-r--r--pkgs/development/libraries/qt-6/modules/qtbase.nix53
-rw-r--r--pkgs/development/libraries/qt-6/modules/qtdeclarative.nix18
-rw-r--r--pkgs/development/libraries/qt-6/modules/qtlanguageserver.nix3
-rw-r--r--pkgs/development/libraries/qt-6/patches/cmake.patch (renamed from pkgs/development/libraries/qt-6/cmake.patch)0
-rw-r--r--pkgs/development/libraries/qt-6/patches/qtbase-qmake-pkg-config.patch14
-rw-r--r--pkgs/development/libraries/qt-6/qtModule.nix43
14 files changed, 513 insertions, 131 deletions
diff --git a/pkgs/development/libraries/qt-6/default.nix b/pkgs/development/libraries/qt-6/default.nix
index 7615c4f904311..754436cb0b962 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 0000000000000..44bf10a0db068
--- /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 0000000000000..0fd0aee7dbf9a
--- /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 0000000000000..0a0e0d51e27da
--- /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 0000000000000..85489c85105bc
--- /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 0000000000000..ec1d2ea6124ea
--- /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 0ae969e8f8fcc..613b42fc97e93 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
+    # 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
 
-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
+    # 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
-}
-prePhases+=" qtPreHook"
 
-addQtModulePrefix () {
-    addToSearchPath QT_ADDITIONAL_PACKAGES_PREFIX_PATH $1
-}
-addEnvHooks "$hostOffset" addQtModulePrefix
+    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 b669da3d058c8..8b135a7d7492f 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 cdae37ba54d28..8717b94545b62 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 3f691894ff86c..b76baea499306 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 b2e6e450d5f77..07115d6755a56 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
index 1fb2ea26c35df..1fb2ea26c35df 100644
--- a/pkgs/development/libraries/qt-6/cmake.patch
+++ b/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 0000000000000..90caaea1cf4d4
--- /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 04349ed68ad29..999d69ce683a9 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++";