diff options
author | hacker1024 <hacker1024@users.sourceforge.net> | 2023-10-25 22:29:28 +1100 |
---|---|---|
committer | FlafyDev <flafyarazi@gmail.com> | 2023-12-26 17:05:20 +0200 |
commit | 3081228cc4a2e345880cba80ff11544a466b1285 (patch) | |
tree | 545628c83535578df538c27ff393cc27da38c5a8 /pkgs/build-support | |
parent | a07ba135b0ec74f6411b4bb07d24850b7c060a04 (diff) | |
download | nixlib-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')
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 ''; |