diff --git a/pkgs/build-support/dart/fetch-dart-deps/default.nix b/pkgs/build-support/dart/fetch-dart-deps/default.nix new file mode 100644 index 000000000000..4bb5f257032d --- /dev/null +++ b/pkgs/build-support/dart/fetch-dart-deps/default.nix @@ -0,0 +1,133 @@ +{ stdenvNoCC +, lib +, makeSetupHook +, dart +, git +, cacert +}: + +{ + # 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" + # 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 = + # This + 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 = '' + ${pubGetScript} + + # so we can use lock, diff yaml + mkdir -p "$out/pubspec" + cp "pubspec.yaml" "$out/pubspec" + cp "pubspec.lock" "$out/pubspec" + + # 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. + find "$PUB_CACHE" -name .git -type d -prune -exec rm -rf {} + + + # 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; + } // drvArgs); +in +(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 deps; }; +}) ./setup-hook.sh diff --git a/pkgs/build-support/dart/fetch-dart-deps/setup-hook.sh b/pkgs/build-support/dart/fetch-dart-deps/setup-hook.sh new file mode 100644 index 000000000000..dcb0cc0622e8 --- /dev/null +++ b/pkgs/build-support/dart/fetch-dart-deps/setup-hook.sh @@ -0,0 +1,42 @@ +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 + # FIXME: currently this is broken. in theory this should not break, but flutter has it's own way of doing things. + # diff -u pubspec.lock "$deps/pubspec/pubspec.lock" + true + else + cp -v "$deps/pubspec/pubspec.lock" . + # Sometimes the pubspec.lock will get opened in write mode, even when offline. + chmod u+w pubspec.lock + fi + diff -u pubspec.yaml "$deps/pubspec/pubspec.yaml" +} diff --git a/pkgs/build-support/flutter/default.nix b/pkgs/build-support/flutter/default.nix index 2fec501de3ea..f148c3d046e4 100644 --- a/pkgs/build-support/flutter/default.nix +++ b/pkgs/build-support/flutter/default.nix @@ -1,4 +1,5 @@ { lib +, callPackage , stdenvNoCC , llvmPackages_13 , cacert @@ -8,135 +9,35 @@ # absolutely no mac support for now -args: +{ pubGetScript ? "flutter pub get" +, vendorHash +, nativeBuildInputs ? [ ] +, ... +}@args: let - pl = n: "##FLUTTER_${n}_PLACEHOLDER_MARKER##"; - placeholder_deps = pl "DEPS"; - placeholder_flutter = pl "FLUTTER"; - fetchAttrs = [ "src" "sourceRoot" "setSourceRoot" "unpackPhase" "patches" ]; - getAttrsOrNull = names: attrs: lib.genAttrs names (name: if attrs ? ${name} then attrs.${name} else null); + flutterSetupScript = '' + export HOME="$NIX_BUILD_TOP" + flutter config --no-analytics &>/dev/null # mute first-run + flutter config --enable-linux-desktop >/dev/null + ''; + + deps = callPackage ../dart/fetch-dart-deps { dart = flutter; } { + sdkSetupScript = flutterSetupScript; + inherit pubGetScript vendorHash; + buildDrvArgs = args; + }; self = (self: llvmPackages_13.stdenv.mkDerivation (args // { - deps = stdenvNoCC.mkDerivation (lib.recursiveUpdate (getAttrsOrNull fetchAttrs args) { - name = "${self.name}-deps-flutter"; - - nativeBuildInputs = [ - flutter - git - ]; - - # avoid pub phase - dontBuild = true; - - installPhase = '' - TMP=$(mktemp -d) - export HOME="$TMP" - - # Configure the package cache - export PUB_CACHE="$out/cache/.pub-cache" - mkdir -p "$PUB_CACHE" - - flutter config --no-analytics &>/dev/null # mute first-run - flutter config --enable-linux-desktop - flutter packages get - ${lib.optionalString (args ? flutterExtraFetchCommands) args.flutterExtraFetchCommands} - - # so we can use lock, diff yaml - mkdir -p "$out/pubspec" - cp "pubspec.yaml" "$out/pubspec" - cp "pubspec.lock" "$out/pubspec" - - # 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. - find "$PUB_CACHE" -name .git -type d -prune -exec rm -rf {} + - - # 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" - ]; - - # unnecesarry - dontFixup = true; - - # 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; - - outputHashAlgo = if self ? vendorHash then null else "sha256"; - outputHashMode = "recursive"; - outputHash = if self ? vendorHash then - self.vendorHash - else if self ? vendorSha256 then - self.vendorSha256 - else - lib.fakeSha256; - }); - nativeBuildInputs = [ + deps flutter git - ] ++ lib.optionals (args ? nativeBuildInputs) args.nativeBuildInputs; - - buildInputs = lib.optionals (args ? buildInputs) args.buildInputs; + ] ++ nativeBuildInputs; configurePhase = '' runHook preConfigure - TMP=$(mktemp -d) - export HOME="$TMP" - - flutter config --no-analytics &>/dev/null # mute first-run - flutter config --enable-linux-desktop - - # Configure the package cache - export PUB_CACHE="$TMP/.pub-cache" - mkdir -p "$PUB_CACHE" - - # 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 - - # 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 - # FIXME: currently this is broken. in theory this should not break, but flutter has it's own way of doing things. - # diff -u pubspec.lock "$deps/pubspec/pubspec.lock" - true - else - cp -v "$deps/pubspec/pubspec.lock" . - # Sometimes the pubspec.lock will get opened in write mode, even when offline. - chmod u+w pubspec.lock - fi - diff -u pubspec.yaml "$deps/pubspec/pubspec.yaml" + ${flutterSetupScript} runHook postConfigure '';