about summary refs log tree commit diff
path: root/pkgs/build-support/dart/fetch-dart-deps/default.nix
blob: 460bba2b1b62b6a9a218d1c4d55dab7d480779ce (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
{ 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"
  # 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" ]));
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; };
  passthru.files = deps.outPath;
}) ./setup-hook.sh