about summary refs log tree commit diff
path: root/pkgs/build-support
diff options
context:
space:
mode:
authorhacker1024 <hacker1024@users.sourceforge.net>2023-10-25 22:29:28 +1100
committerFlafyDev <flafyarazi@gmail.com>2023-12-26 17:05:20 +0200
commit3081228cc4a2e345880cba80ff11544a466b1285 (patch)
tree545628c83535578df538c27ff393cc27da38c5a8 /pkgs/build-support
parenta07ba135b0ec74f6411b4bb07d24850b7c060a04 (diff)
downloadnixlib-3081228cc4a2e345880cba80ff11544a466b1285.tar
nixlib-3081228cc4a2e345880cba80ff11544a466b1285.tar.gz
nixlib-3081228cc4a2e345880cba80ff11544a466b1285.tar.bz2
nixlib-3081228cc4a2e345880cba80ff11544a466b1285.tar.lz
nixlib-3081228cc4a2e345880cba80ff11544a466b1285.tar.xz
nixlib-3081228cc4a2e345880cba80ff11544a466b1285.tar.zst
nixlib-3081228cc4a2e345880cba80ff11544a466b1285.zip
dart: Use Nix instead of Pub
Diffstat (limited to 'pkgs/build-support')
-rw-r--r--pkgs/build-support/dart/build-dart-application/default.nix111
-rw-r--r--pkgs/build-support/dart/build-dart-application/generators.nix80
-rw-r--r--pkgs/build-support/dart/build-dart-application/hooks/dart-config-hook.sh7
-rw-r--r--pkgs/build-support/dart/build-dart-application/hooks/dart-install-hook.sh6
-rw-r--r--pkgs/build-support/dart/build-dart-application/hooks/default.nix2
-rw-r--r--pkgs/build-support/dart/fetch-dart-deps/default.nix248
-rw-r--r--pkgs/build-support/dart/fetch-dart-deps/setup-hook.sh46
-rw-r--r--pkgs/build-support/dart/pub2nix/default.nix6
-rw-r--r--pkgs/build-support/dart/pub2nix/package-config.nix57
-rw-r--r--pkgs/build-support/dart/pub2nix/pubspec-lock.nix107
-rw-r--r--pkgs/build-support/flutter/default.nix39
11 files changed, 368 insertions, 341 deletions
diff --git a/pkgs/build-support/dart/build-dart-application/default.nix b/pkgs/build-support/dart/build-dart-application/default.nix
index 2cb193ac6f16..c9b03b28dd46 100644
--- a/pkgs/build-support/dart/build-dart-application/default.nix
+++ b/pkgs/build-support/dart/build-dart-application/default.nix
@@ -1,6 +1,23 @@
-{ lib, stdenv, callPackage, fetchDartDeps, runCommand, symlinkJoin, writeText, dartHooks, makeWrapper, dart, cacert, nodejs, darwin, jq }:
+{ lib
+, stdenv
+, callPackage
+, writeText
+, pub2nix
+, dartHooks
+, makeWrapper
+, dart
+, nodejs
+, darwin
+, jq
+}:
 
-{ sdkSetupScript ? ""
+{ src
+, sourceRoot ? "source"
+, packageRoot ? (lib.removePrefix "/" (lib.removePrefix "source" sourceRoot))
+, gitHashes ? { }
+, sdkSourceBuilders ? { }
+
+, sdkSetupScript ? ""
 , pubGetScript ? "dart pub get"
 
   # Output type to produce. Can be any kind supported by dart
@@ -29,33 +46,42 @@
 , customPackageOverrides ? { }
 , autoDepsList ? false
 , depsListFile ? null
-, pubspecLockFile ? null
-, vendorHash ? ""
+, pubspecLock
 , ...
 }@args:
 
 let
-  dartDeps = (fetchDartDeps.override {
-    dart = symlinkJoin {
-      name = "dart-sdk-fod";
-      paths = [
-        (runCommand "dart-fod" { nativeBuildInputs = [ makeWrapper ]; } ''
-          mkdir -p "$out/bin"
-          makeWrapper "${dart}/bin/dart" "$out/bin/dart" \
-            --add-flags "--root-certs-file=${cacert}/etc/ssl/certs/ca-bundle.crt"
-        '')
-        dart
-      ];
-    };
-  }) {
-    buildDrvArgs = args;
-    inherit sdkSetupScript pubGetScript vendorHash pubspecLockFile;
+  generators = callPackage ./generators.nix { inherit dart; } { inherit sdkSetupScript; buildDrvArgs = args; };
+
+  depsList = if depsListFile == null then null else lib.importJSON depsListFile;
+  generatedDepsList = generators.mkDepsList { inherit pubspecLockFile pubspecLockData packageConfig; };
+
+  pubspecLockFile = builtins.toJSON pubspecLock;
+  pubspecLockData = pub2nix.readPubspecLock { inherit src packageRoot pubspecLock gitHashes sdkSourceBuilders; };
+  packageConfig = pub2nix.generatePackageConfig {
+    pname = if args.pname != null then "${args.pname}-${args.version}" else null;
+
+    dependencies =
+      # Ideally, we'd only include the main dependencies and their transitive
+      # dependencies.
+      #
+      # The pubspec.lock file does not contain information about where
+      # transitive dependencies come from, though, and it would be weird to
+      # include the transitive dependencies of dev and override dependencies
+      # without including the dev and override dependencies themselves.
+      builtins.concatLists (builtins.attrValues pubspecLockData.dependencies);
+
+    inherit (pubspecLockData) dependencySources;
   };
+
   inherit (dartHooks.override { inherit dart; }) dartConfigHook dartBuildHook dartInstallHook dartFixupHook;
 
-  baseDerivation = stdenv.mkDerivation (finalAttrs: args // {
-    inherit sdkSetupScript pubGetScript dartCompileCommand dartOutputType
-      dartRuntimeCommand dartCompileFlags dartJitFlags runtimeDependencies;
+  baseDerivation = stdenv.mkDerivation (finalAttrs: (builtins.removeAttrs args [ "gitHashes" "sdkSourceBuilders" "pubspecLock" ]) // {
+    inherit pubspecLockFile packageConfig sdkSetupScript pubGetScript
+      dartCompileCommand dartOutputType dartRuntimeCommand dartCompileFlags
+      dartJitFlags runtimeDependencies;
+
+    outputs = args.outputs or [ ] ++ [ "out" "pubcache" ];
 
     dartEntryPoints =
       if (dartEntryPoints != null)
@@ -66,7 +92,6 @@ let
 
     nativeBuildInputs = (args.nativeBuildInputs or [ ]) ++ [
       dart
-      dartDeps
       dartConfigHook
       dartBuildHook
       dartInstallHook
@@ -77,25 +102,32 @@ let
       darwin.sigtool
     ];
 
-    preUnpack = ''
-      ${lib.optionalString (!autoDepsList) ''
-        if ! { [ '${lib.boolToString (depsListFile != null)}' = 'true' ] ${lib.optionalString (depsListFile != null) "&& cmp -s <(jq -Sc . '${depsListFile}') <(jq -Sc . '${finalAttrs.passthru.dartDeps.depsListFile}')"}; }; then
-          echo 1>&2 -e '\nThe dependency list file was either not given or differs from the expected result.' \
-                      '\nPlease choose one of the following solutions:' \
-                      '\n - Duplicate the following file and pass it to the depsListFile argument.' \
-                      '\n   ${finalAttrs.passthru.dartDeps.depsListFile}' \
-                      '\n - Set autoDepsList to true (not supported by Hydra or permitted in Nixpkgs)'.
-          exit 1
-        fi
-      ''}
-      ${args.preUnpack or ""}
+    preConfigure = args.preConfigure or "" + ''
+      ln -sf "$pubspecLockFilePath" pubspec.lock
+    '';
+
+    preBuild = args.preBuild or "" + lib.optionalString (!autoDepsList) ''
+      if ! { [ '${lib.boolToString (depsListFile != null)}' = 'true' ] ${lib.optionalString (depsListFile != null) "&& cmp -s <(jq -Sc . '${depsListFile}') <(jq -Sc . deps.json)"}; }; then
+        echo 1>&2 -e '\nThe dependency list file was either not given or differs from the expected result.' \
+                     '\nPlease choose one of the following solutions:' \
+                     '\n - Duplicate the following file and pass it to the depsListFile argument.' \
+                     '\n   ${generatedDepsList}' \
+                     '\n - Set autoDepsList to true (not supported by Hydra or permitted in Nixpkgs)'.
+        exit 1
+      fi
     '';
 
     # When stripping, it seems some ELF information is lost and the dart VM cli
     # runs instead of the expected program. Don't strip if it's an exe output.
     dontStrip = args.dontStrip or (dartOutputType == "exe");
 
-    passthru = { inherit dartDeps; } // (args.passthru or { });
+    passAsFile = [ "pubspecLockFile" ];
+
+    passthru = {
+      pubspecLock = pubspecLockData;
+      depsList = generatedDepsList;
+      generatePubspecLock = generators.generatePubspecLock { inherit pubGetScript; };
+    } // (args.passthru or { });
 
     meta = (args.meta or { }) // { platforms = args.meta.platforms or dart.meta.platforms; };
   });
@@ -103,11 +135,8 @@ let
   packageOverrideRepository = (callPackage ../../../development/compilers/dart/package-overrides { }) // customPackageOverrides;
   productPackages = builtins.filter (package: package.kind != "dev")
     (if autoDepsList
-    then lib.importJSON dartDeps.depsListFile
-    else
-      if depsListFile == null
-      then [ ]
-      else lib.importJSON depsListFile);
+    then lib.importJSON generatedDepsList
+    else if depsList == null then [ ] else depsList);
 in
 assert !(builtins.isString dartOutputType && dartOutputType != "") ->
 throw "dartOutputType must be a non-empty string";
diff --git a/pkgs/build-support/dart/build-dart-application/generators.nix b/pkgs/build-support/dart/build-dart-application/generators.nix
new file mode 100644
index 000000000000..622ba7fc8fa7
--- /dev/null
+++ b/pkgs/build-support/dart/build-dart-application/generators.nix
@@ -0,0 +1,80 @@
+{ lib
+, stdenvNoCC
+, dart
+, dartHooks
+, yq
+, cacert
+}:
+
+{
+  # Commands to run once before using Dart or pub.
+  sdkSetupScript ? ""
+  # Arguments used in the derivation that builds the Dart package.
+  # Passing these is recommended to ensure that the same steps are made to
+  # prepare the sources in both this derivation and the one that builds the Dart
+  # package.
+, buildDrvArgs ? { }
+, ...
+}@args:
+
+# This is a fixed-output derivation and setup hook that can be used to fetch dependencies for Dart projects.
+# It is designed to be placed in the nativeBuildInputs of a derivation that builds a Dart package.
+# Providing the buildDrvArgs argument is highly recommended.
+let
+  buildDrvInheritArgNames = [
+    "name"
+    "pname"
+    "version"
+    "src"
+    "sourceRoot"
+    "setSourceRoot"
+    "preUnpack"
+    "unpackPhase"
+    "unpackCmd"
+    "postUnpack"
+    "prePatch"
+    "patchPhase"
+    "patches"
+    "patchFlags"
+    "postPatch"
+  ];
+
+  buildDrvInheritArgs = builtins.foldl'
+    (attrs: arg:
+      if buildDrvArgs ? ${arg}
+      then attrs // { ${arg} = buildDrvArgs.${arg}; }
+      else attrs)
+    { }
+    buildDrvInheritArgNames;
+
+  drvArgs = buildDrvInheritArgs // (removeAttrs args [ "buildDrvArgs" ]);
+  name = (if drvArgs ? name then drvArgs.name else "${drvArgs.pname}-${drvArgs.version}");
+
+  mkDepsDrv = { pubspecLockFile, pubspecLockData, packageConfig }: args: stdenvNoCC.mkDerivation (drvArgs // args // {
+    inherit pubspecLockFile packageConfig;
+
+    nativeBuildInputs = drvArgs.nativeBuildInputs or [ ] ++ args.nativeBuildInputs or [ ] ++ [ dart dartHooks.dartConfigHook ];
+
+    preConfigure = drvArgs.preConfigure or "" + args.preConfigure or "" + ''
+      ln -sf "$pubspecLockFilePath" pubspec.lock
+    '';
+
+    passAsFile = drvArgs.passAsFile or [ ] ++ args.passAsFile or [ ] ++ [ "pubspecLockFile" ];
+  } // (removeAttrs buildDrvInheritArgs [ "name" "pname" ]));
+
+  mkDepsList = args: mkDepsDrv args {
+    name = "${name}-dart-deps-list.json";
+
+    dontBuild = true;
+
+    installPhase = ''
+      runHook preInstall
+      cp deps.json "$out"
+      runHook postInstall
+    '';
+  };
+in
+{
+  inherit
+    mkDepsList;
+}
diff --git a/pkgs/build-support/dart/build-dart-application/hooks/dart-config-hook.sh b/pkgs/build-support/dart/build-dart-application/hooks/dart-config-hook.sh
index f22d7d2ce64d..e9b9a397509b 100644
--- a/pkgs/build-support/dart/build-dart-application/hooks/dart-config-hook.sh
+++ b/pkgs/build-support/dart/build-dart-application/hooks/dart-config-hook.sh
@@ -7,7 +7,12 @@ dartConfigHook() {
     eval "$sdkSetupScript"
 
     echo "Installing dependencies"
-    eval doPubGet "$pubGetScript" --offline
+    mkdir -p .dart_tool
+    packageName="$(@yq@ --raw-output .name pubspec.yaml)"
+    @jq@ '.packages |= . + [{ name: "'"$packageName"'", rootUri: "../", packageUri: "lib/" }]' "$packageConfig" > .dart_tool/package_config.json
+
+    echo "Generating the dependency list"
+    dart pub deps --json | @jq@ .packages > deps.json
 
     echo "Finished dartConfigHook"
 }
diff --git a/pkgs/build-support/dart/build-dart-application/hooks/dart-install-hook.sh b/pkgs/build-support/dart/build-dart-application/hooks/dart-install-hook.sh
index 1906bcfbca4c..888e12a07d83 100644
--- a/pkgs/build-support/dart/build-dart-application/hooks/dart-install-hook.sh
+++ b/pkgs/build-support/dart/build-dart-application/hooks/dart-install-hook.sh
@@ -5,8 +5,8 @@ dartInstallHook() {
 
     runHook preInstall
 
+    # Install snapshots and executables.
     mkdir -p "$out"
-
     while IFS=$'\t' read -ra target; do
         dest="${target[0]}"
         # Wrap with runtime command, if it's defined
@@ -19,6 +19,10 @@ dartInstallHook() {
         fi
     done < <(_getDartEntryPoints)
 
+    # Install the package_config.json file.
+    mkdir -p "$pubcache"
+    cp .dart_tool/package_config.json "$pubcache/package_config.json"
+
     runHook postInstall
 
     echo "Finished dartInstallHook"
diff --git a/pkgs/build-support/dart/build-dart-application/hooks/default.nix b/pkgs/build-support/dart/build-dart-application/hooks/default.nix
index 134989426d96..253d3132ad02 100644
--- a/pkgs/build-support/dart/build-dart-application/hooks/default.nix
+++ b/pkgs/build-support/dart/build-dart-application/hooks/default.nix
@@ -3,6 +3,8 @@
 {
   dartConfigHook = makeSetupHook {
     name = "dart-config-hook";
+    substitutions.yq = "${yq}/bin/yq";
+    substitutions.jq = "${jq}/bin/jq";
   } ./dart-config-hook.sh;
   dartBuildHook = makeSetupHook {
     name = "dart-build-hook";
diff --git a/pkgs/build-support/dart/fetch-dart-deps/default.nix b/pkgs/build-support/dart/fetch-dart-deps/default.nix
deleted file mode 100644
index 29e5209a2877..000000000000
--- a/pkgs/build-support/dart/fetch-dart-deps/default.nix
+++ /dev/null
@@ -1,248 +0,0 @@
-{ stdenvNoCC
-, lib
-, makeSetupHook
-, writeShellScriptBin
-, dart
-, git
-, cacert
-, jq
-}:
-
-{
-  # The output hash of the dependencies for this project.
-  vendorHash ? ""
-  # Commands to run once before using Dart or pub.
-, sdkSetupScript ? ""
-  # Commands to run to populate the pub cache.
-, pubGetScript ? "dart pub get"
-  # A path to a pubspec.lock file to use instead of the one in the source directory.
-, pubspecLockFile ? null
-  # Arguments used in the derivation that builds the Dart package.
-  # Passing these is recommended to ensure that the same steps are made to prepare the sources in both this
-  # derivation and the one that builds the Dart package.
-, buildDrvArgs ? { }
-, ...
-}@args:
-
-# This is a fixed-output derivation and setup hook that can be used to fetch dependencies for Dart projects.
-# It is designed to be placed in the nativeBuildInputs of a derivation that builds a Dart package.
-# Providing the buildDrvArgs argument is highly recommended.
-let
-  buildDrvInheritArgNames = [
-    "name"
-    "pname"
-    "version"
-    "src"
-    "sourceRoot"
-    "setSourceRoot"
-    "preUnpack"
-    "unpackPhase"
-    "unpackCmd"
-    "postUnpack"
-    "prePatch"
-    "patchPhase"
-    "patches"
-    "patchFlags"
-    "postPatch"
-  ];
-
-  buildDrvInheritArgs = builtins.foldl'
-    (attrs: arg:
-      if buildDrvArgs ? ${arg}
-      then attrs // { ${arg} = buildDrvArgs.${arg}; }
-      else attrs)
-    { }
-    buildDrvInheritArgNames;
-
-  drvArgs = buildDrvInheritArgs // (removeAttrs args [ "buildDrvArgs" ]);
-  name = (if drvArgs ? name then drvArgs.name else "${drvArgs.pname}-${drvArgs.version}");
-
-  deps =
-    stdenvNoCC.mkDerivation ({
-      name = "${name}-dart-deps";
-
-      nativeBuildInputs = [
-        dart
-        git
-      ];
-
-      # avoid pub phase
-      dontBuild = true;
-
-      configurePhase = ''
-        # Configure the package cache
-        export PUB_CACHE="$out/cache/.pub-cache"
-        mkdir -p "$PUB_CACHE"
-
-        ${sdkSetupScript}
-      '';
-
-      installPhase = ''
-        _pub_get() {
-          ${pubGetScript}
-        }
-
-        # so we can use lock, diff yaml
-        mkdir -p "$out/pubspec"
-        cp "pubspec.yaml" "$out/pubspec"
-        ${lib.optionalString (pubspecLockFile != null) "install -m644 ${pubspecLockFile} pubspec.lock"}
-        if ! cp "pubspec.lock" "$out/pubspec"; then
-          echo 1>&2 -e '\nThe pubspec.lock file is missing. This is a requirement for reproducible builds.' \
-                       '\nThe following steps should be taken to fix this issue:' \
-                       '\n  1. If you are building an application, contact the developer(s).' \
-                       '\n     The pubspec.lock file should be provided with the source code.' \
-                       '\n     https://dart.dev/guides/libraries/private-files#pubspeclock' \
-                       '\n  2. An attempt to generate and print a compressed pubspec.lock file will be made now.' \
-                       '\n     It is compressed with gzip and base64 encoded.' \
-                       '\n     Paste it to a file and extract it with `base64 -d pubspec.lock.in | gzip -d > pubspec.lock`.' \
-                       '\n     Provide the path to the pubspec.lock file in the pubspecLockFile argument.' \
-                       '\n     This must be updated whenever the application is updated.' \
-                       '\n'
-          _pub_get
-          echo ""
-          gzip --to-stdout --best pubspec.lock | base64 1>&2
-          echo 1>&2 -e '\nA gzipped pubspec.lock file has been printed. Please see the informational message above.'
-          exit 1
-        fi
-
-        _pub_get
-
-        # nuke nondeterminism
-
-        # Remove Git directories in the Git package cache - these are rarely used by Pub,
-        # which instead maintains a corresponsing mirror and clones cached packages through it.
-        #
-        # An exception is made to keep .git/pub-packages files, which are important.
-        # https://github.com/dart-lang/pub/blob/c890afa1d65b340fa59308172029680c2f8b0fc6/lib/src/source/git.dart#L621
-        if [ -d "$PUB_CACHE"/git ]; then
-          find "$PUB_CACHE"/git -maxdepth 4 -path "*/.git/*" ! -name "pub-packages" -prune -exec rm -rf {} +
-        fi
-
-        # Remove continuously updated package metadata caches
-        rm -rf "$PUB_CACHE"/hosted/*/.cache # Not pinned by pubspec.lock
-        rm -rf "$PUB_CACHE"/git/cache/*/* # Recreate this on the other end. See: https://github.com/dart-lang/pub/blob/c890afa1d65b340fa59308172029680c2f8b0fc6/lib/src/source/git.dart#L531
-
-        # Miscelaneous transient package cache files
-        rm -f "$PUB_CACHE"/README.md # May change with different Dart versions
-        rm -rf "$PUB_CACHE"/_temp # https://github.com/dart-lang/pub/blob/c890afa1d65b340fa59308172029680c2f8b0fc6/lib/src/system_cache.dart#L131
-        rm -rf "$PUB_CACHE"/log # https://github.com/dart-lang/pub/blob/c890afa1d65b340fa59308172029680c2f8b0fc6/lib/src/command.dart#L348
-      '';
-
-      GIT_SSL_CAINFO = "${cacert}/etc/ssl/certs/ca-bundle.crt";
-      SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt";
-
-      impureEnvVars = lib.fetchers.proxyImpureEnvVars ++ [
-        "GIT_PROXY_COMMAND"
-        "NIX_GIT_SSL_CAINFO"
-        "SOCKS_SERVER"
-      ];
-
-      # Patching shebangs introduces input references to this fixed-output derivation.
-      # This triggers a bug in Nix, causing the output path to change unexpectedly.
-      # https://github.com/NixOS/nix/issues/6660
-      dontPatchShebangs = true;
-
-      # The following operations are not generally useful for this derivation.
-      # If a package does contain some native components used at build time,
-      # please file an issue.
-      dontStrip = true;
-      dontMoveSbin = true;
-      dontPatchELF = true;
-
-      outputHashAlgo = "sha256";
-      outputHashMode = "recursive";
-      outputHash = if vendorHash != "" then vendorHash else lib.fakeSha256;
-    } // (removeAttrs drvArgs [ "name" "pname" ]));
-
-  mkDepsDrv = args: stdenvNoCC.mkDerivation (args // {
-    nativeBuildInputs = args.nativeBuildInputs or [ ] ++ [ hook dart ];
-
-    configurePhase = args.configurePhase or ''
-      runHook preConfigure
-
-      ${sdkSetupScript}
-
-      _pub_get() {
-        ${pubGetScript} --offline
-      }
-      doPubGet _pub_get
-
-      runHook postConfigure
-    '';
-  } // (removeAttrs buildDrvInheritArgs [ "name" "pname" ]));
-
-  depsListDrv = mkDepsDrv {
-    name = "${name}-dart-deps-list.json";
-
-    nativeBuildInputs = [ jq ];
-
-    buildPhase = ''
-      runHook preBuild
-      if [ -e ${dart}/bin/flutter ]; then
-        flutter pub deps --json | jq .packages > $out
-      else
-        dart pub deps --json | jq .packages > $out
-      fi
-      runHook postBuild
-    '';
-
-    dontInstall = true;
-  };
-
-  packageConfigDrv = mkDepsDrv {
-    name = "${name}-package-config.json";
-
-    nativeBuildInputs = [ jq ];
-
-    buildPhase = ''
-      runHook preBuild
-
-      # Canonicalise the package_config.json, and replace references to the
-      # reconstructed package cache with the original FOD.
-      #
-      # The reconstructed package cache is not reproducible. The intended
-      # use-case of this derivation is for use with tools that use a
-      # package_config.json to load assets from packages, and not for use with
-      # Pub directly, which requires the setup performed by the hook before
-      # usage.
-      jq -S '
-        .packages[] |= . + { rootUri: .rootUri | gsub("'"$PUB_CACHE"'"; "${hook.deps}/cache/.pub-cache") }
-      | .generated |= "1970-01-01T00:00:00.000Z"
-      ' .dart_tool/package_config.json > $out
-
-      runHook postBuild
-    '';
-
-    dontInstall = true;
-  };
-
-  # As of Dart 3.0.0, Pub checks the revision of cached Git-sourced packages.
-  # Git must be wrapped to return a positive result, as the real .git directory is wiped
-  # to produce a deteministic dependency derivation output.
-  # https://github.com/dart-lang/pub/pull/3791/files#diff-1639c4669c428c26e68cfebd5039a33f87ba568795f2c058c303ca8528f62b77R631
-  gitSourceWrapper = writeShellScriptBin "git" ''
-    args=("$@")
-    if [[ "''${args[0]}" == "rev-list" && "''${args[1]}" == "--max-count=1" ]]; then
-      revision="''${args[''${#args[@]}-1]}"
-      echo "$revision"
-    else
-      ${git}/bin/git "''${args[@]}"
-    fi
-  '';
-
-  hook = (makeSetupHook {
-    # The setup hook should not be part of the fixed-output derivation.
-    # Updates to the hook script should not change vendor hashes, and it won't
-    # work at all anyway due to https://github.com/NixOS/nix/issues/6660.
-    name = "${name}-dart-deps-setup-hook";
-    substitutions = { inherit gitSourceWrapper deps; };
-    propagatedBuildInputs = [ dart git ];
-    passthru = {
-      inherit deps;
-      files = deps.outPath;
-      depsListFile = depsListDrv.outPath;
-      packageConfig = packageConfigDrv;
-    };
-  }) ./setup-hook.sh;
-in
-hook
diff --git a/pkgs/build-support/dart/fetch-dart-deps/setup-hook.sh b/pkgs/build-support/dart/fetch-dart-deps/setup-hook.sh
deleted file mode 100644
index 689e0e8c5b5f..000000000000
--- a/pkgs/build-support/dart/fetch-dart-deps/setup-hook.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-preConfigureHooks+=(_setupPubCache)
-
-_setupPubCache() {
-    deps="@deps@"
-
-    # Configure the package cache.
-    export PUB_CACHE="$(mktemp -d)"
-    mkdir -p "$PUB_CACHE"
-
-    if [ -d "$deps/cache/.pub-cache/git" ]; then
-        # Link the Git package cache.
-        mkdir -p "$PUB_CACHE/git"
-        ln -s "$deps/cache/.pub-cache/git"/* "$PUB_CACHE/git"
-
-        # Recreate the internal Git cache subdirectory.
-        # See: https://github.com/dart-lang/pub/blob/c890afa1d65b340fa59308172029680c2f8b0fc6/lib/src/source/git.dart#L339)
-        # Blank repositories are created instead of attempting to match the cache mirrors to checkouts.
-        # This is not an issue, as pub does not need the mirrors in the Flutter build process.
-        rm "$PUB_CACHE/git/cache" && mkdir "$PUB_CACHE/git/cache"
-        for mirror in $(ls -A "$deps/cache/.pub-cache/git/cache"); do
-            git --git-dir="$PUB_CACHE/git/cache/$mirror" init --bare --quiet
-        done
-    fi
-
-    # Link the remaining package cache directories.
-    # At this point, any subdirectories that must be writable must have been taken care of.
-    for file in $(comm -23 <(ls -A "$deps/cache/.pub-cache") <(ls -A "$PUB_CACHE")); do
-        ln -s "$deps/cache/.pub-cache/$file" "$PUB_CACHE/$file"
-    done
-
-    # ensure we're using a lockfile for the right package version
-    if [ ! -e pubspec.lock ]; then
-        cp -v "$deps/pubspec/pubspec.lock" .
-        # Sometimes the pubspec.lock will get opened in write mode, even when offline.
-        chmod u+w pubspec.lock
-    elif ! { diff -u pubspec.lock "$deps/pubspec/pubspec.lock" && diff -u pubspec.yaml "$deps/pubspec/pubspec.yaml"; }; then
-        echo 1>&2 -e 'The pubspec.lock or pubspec.yaml of the project derivation differs from the one in the dependency derivation.' \
-                   '\nYou most likely forgot to update the vendorHash while updating the sources.'
-        exit 1
-    fi
-}
-
-# Performs the given pub get command with an appropriate environment.
-doPubGet() {
-    PATH="@gitSourceWrapper@/bin:$PATH" "$@"
-}
diff --git a/pkgs/build-support/dart/pub2nix/default.nix b/pkgs/build-support/dart/pub2nix/default.nix
new file mode 100644
index 000000000000..ace2cc5a1e0c
--- /dev/null
+++ b/pkgs/build-support/dart/pub2nix/default.nix
@@ -0,0 +1,6 @@
+{ callPackage }:
+
+{
+  readPubspecLock = callPackage ./pubspec-lock.nix { };
+  generatePackageConfig = callPackage ./package-config.nix { };
+}
diff --git a/pkgs/build-support/dart/pub2nix/package-config.nix b/pkgs/build-support/dart/pub2nix/package-config.nix
new file mode 100644
index 000000000000..29c6bac6b2cd
--- /dev/null
+++ b/pkgs/build-support/dart/pub2nix/package-config.nix
@@ -0,0 +1,57 @@
+{ lib
+, runCommand
+, jq
+, yq
+}:
+
+{ pname ? null
+
+  # A list of dependency package names.
+, dependencies
+
+  # An attribute set of package names to sources.
+, dependencySources
+}:
+
+let
+  packages = lib.getAttrs dependencies dependencySources;
+in
+(runCommand "${lib.optionalString (pname != null) "${pname}-"}package-config.json" {
+  inherit packages;
+
+  nativeBuildInputs = [ jq yq ];
+
+  __structuredAttrs = true;
+}) ''
+  declare -A packages
+  while IFS='=' read -r name path; do
+    packages["$name"]="$path"
+  done < <(jq -r '.packages | to_entries | map("\(.key)=\(.value)") | .[]' "$NIX_ATTRS_JSON_FILE")
+
+  jq > "$out" --slurp '{
+    configVersion: 2,
+    generator: "nixpkgs",
+    packages: .,
+  }' <(for package in "''${!packages[@]}"; do
+      languageConstraint="$(yq -r .environment.sdk "''${packages["$package"]}/pubspec.yaml")"
+      if [[ "$languageConstraint" =~ ^[[:space:]]*(\^|>=|>|)[[:space:]]*([[:digit:]]+\.[[:digit:]]+)\.[[:digit:]]+.*$ ]]; then
+        languageVersionJson="\"''${BASH_REMATCH[2]}\""
+      elif [ "$languageConstraint" = 'any' ]; then
+        languageVersionJson='null'
+      else
+        # https://github.com/dart-lang/pub/blob/68dc2f547d0a264955c1fa551fa0a0e158046494/lib/src/language_version.dart#L106C35-L106C35
+        languageVersionJson='"2.7"'
+      fi
+
+      jq --null-input \
+        --arg name "$package" \
+        --arg path "''${packages["$package"]}" \
+        --argjson languageVersion "$languageVersionJson" \
+        '{
+          name: $name,
+          rootUri: "file://\($path)",
+          packageUri: "lib/",
+          languageVersion: $languageVersion,
+        }'
+    done)
+''
diff --git a/pkgs/build-support/dart/pub2nix/pubspec-lock.nix b/pkgs/build-support/dart/pub2nix/pubspec-lock.nix
new file mode 100644
index 000000000000..07ca54461f5b
--- /dev/null
+++ b/pkgs/build-support/dart/pub2nix/pubspec-lock.nix
@@ -0,0 +1,107 @@
+{ lib
+, fetchurl
+, fetchgit
+, runCommand
+}:
+
+{
+  # The source directory of the package.
+  src
+
+  # The package subdirectory within src.
+  # Useful if the package references sibling packages with relative paths.
+, packageRoot ? "."
+
+  # The pubspec.lock file, in attribute set form.
+, pubspecLock
+
+  # Hashes for Git dependencies.
+  # Pub does not record these itself, so they must be manually provided.
+, gitHashes ? { }
+
+  # Functions to generate SDK package sources.
+  # The function names should match the SDK names, and the package name is given as an argument.
+, sdkSourceBuilders ? { }
+}:
+
+let
+  dependencyVersions = builtins.mapAttrs (name: details: details.version) pubspecLock.packages;
+
+  dependencyTypes = {
+    "direct main" = "main";
+    "direct dev" = "dev";
+    "direct overridden" = "overridden";
+    "transitive" = "transitive";
+  };
+
+  dependencies = lib.foldlAttrs
+    (dependencies: name: details: dependencies // { ${dependencyTypes.${details.dependency}} = dependencies.${dependencyTypes.${details.dependency}} ++ [ name ]; })
+    (lib.genAttrs (builtins.attrValues dependencyTypes) (dependencyType: [ ]))
+    pubspecLock.packages;
+
+  mkHostedDependencySource = name: details:
+    let
+      archive = fetchurl {
+        name = "pub-${name}-${details.version}.tar.gz";
+        url = "${details.description.url}/packages/${details.description.name}/versions/${details.version}.tar.gz";
+        sha256 = details.description.sha256;
+      };
+    in
+    runCommand "pub-${name}-${details.version}"
+      { } ''
+      mkdir -p "$out"
+      tar xf '${archive}' -C "$out"
+    '';
+
+  mkGitDependencySource = name: details: fetchgit {
+    name = "pub-${name}-${details.version}";
+    url = details.description.url;
+    rev = details.description.resolved-ref;
+    postFetch = ''
+      if [ "$(realpath "$out/${details.description.path}")" != "$(realpath "$out")" ]; then
+        (shopt -s dotglob; mv "$out"/* .)
+        rmdir "$out"
+        mv '${details.description.path}' "$out"
+      fi
+    '';
+    hash = gitHashes.${name} or (throw "A Git hash is required for ${name}! Set to an empty string to obtain it.");
+  };
+
+  mkPathDependencySource = name: details:
+    if builtins.isPath src
+    then
+    # When src is a path, avoid copying it to the store entirely, and allow
+    # non-relative paths.
+      (builtins.path {
+        name = "pub-${name}-${details.version}";
+        path = if details.description.relative then src + "/${packageRoot}" + "/${details.description.path}" else details.description.path;
+      })
+    else
+      assert lib.assertMsg details.description.relative "Only relative paths are supported!";
+      runCommand "pub-${name}-${details.version}" { } ''
+        cp -r '${src}/${packageRoot}/${details.description.path}' "$out"
+      '';
+
+  mkSdkDependencySource = name: details:
+    (sdkSourceBuilders.${details.description} or (throw "No SDK source builder has been given for ${details.description}!")) name;
+
+  dependencySources = lib.filterAttrs (name: src: src != null) (builtins.mapAttrs
+    (name: details: ({
+      "hosted" = mkHostedDependencySource;
+      "git" = mkGitDependencySource;
+      "path" = mkPathDependencySource;
+      "sdk" = mkSdkDependencySource;
+    }.${details.source} name) details)
+    pubspecLock.packages);
+in
+{
+  inherit
+    # An attribute set of dependency categories to package name lists.
+    dependencies
+
+    # An attribute set of package names to their versions.
+    dependencyVersions
+
+    # An attribute set of package names to their sources.
+    dependencySources;
+}
diff --git a/pkgs/build-support/flutter/default.nix b/pkgs/build-support/flutter/default.nix
index bcee31506df1..dcf14438cb64 100644
--- a/pkgs/build-support/flutter/default.nix
+++ b/pkgs/build-support/flutter/default.nix
@@ -3,11 +3,13 @@
 , runCommand
 , makeWrapper
 , wrapGAppsHook
-, fetchDartDeps
 , buildDartApplication
 , cacert
 , glib
 , flutter
+, jq
+, yq
+, moreutils
 }:
 
 # absolutely no mac support for now
@@ -20,7 +22,6 @@
 
 (buildDartApplication.override {
   dart = flutter;
-  fetchDartDeps = fetchDartDeps.override { dart = flutter; };
 }) (args // {
   sdkSetupScript = ''
     # Pub needs SSL certificates. Dart normally looks in a hardcoded path.
@@ -50,7 +51,24 @@
 
   inherit pubGetScript;
 
-  nativeBuildInputs = (args.nativeBuildInputs or [ ]) ++ [ wrapGAppsHook ];
+  sdkSourceBuilders = {
+    # https://github.com/dart-lang/pub/blob/68dc2f547d0a264955c1fa551fa0a0e158046494/lib/src/sdk/flutter.dart#L81
+    "flutter" = name: runCommand "flutter-sdk-${name}" { } ''
+      for path in '${flutter}/packages/${name}' '${flutter}/bin/cache/pkg/${name}'; do
+        if [ -d "$path" ]; then
+          ln -s "$path" "$out"
+          break
+        fi
+      done
+
+      if [ ! -e "$out" ]; then
+        echo 1>&2 'The Flutter SDK does not contain the requested package: ${name}!'
+        exit 1
+      fi
+    '';
+  };
+
+  nativeBuildInputs = (args.nativeBuildInputs or [ ]) ++ [ wrapGAppsHook jq yq moreutils ];
   buildInputs = (args.buildInputs or [ ]) ++ [ glib ];
 
   dontDartBuild = true;
@@ -59,7 +77,15 @@
 
     mkdir -p build/flutter_assets/fonts
 
-    doPubGet flutter pub get --offline -v
+    # https://github.com/flutter/flutter/blob/3.13.8/packages/flutter_tools/lib/src/dart/pub.dart#L755
+    if [ "$(yq '.flutter.generate // false' pubspec.yaml)" = "true" ]; then
+      jq '.packages |= . + [{
+        name: "flutter_gen",
+        rootUri: "flutter_gen",
+        languageVersion: "2.12",
+      }]' .dart_tool/package_config.json | sponge .dart_tool/package_config.json
+    fi
+
     flutter build linux -v --release --split-debug-info="$debug" ${builtins.concatStringsSep " " (map (flag: "\"${flag}\"") flutterBuildFlags)}
 
     runHook postBuild
@@ -94,6 +120,11 @@
       fi
     done
 
+    # Install the package_config.json file.
+    # This is normally done by dartInstallHook, but we disable it.
+    mkdir -p "$pubcache"
+    cp .dart_tool/package_config.json "$pubcache/package_config.json"
+
     runHook postInstall
   '';