summaryrefslogtreecommitdiffstats
path: root/pkgs/build-support/fetchurl/default.nix
blob: 0fff1c5861e079ad8ac71f909984e978458a72d2 (plain)
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
{
  lib,
  stdenvNoCC,
  curl, # Note that `curl' may be `null', in case of the native stdenvNoCC.
  cacert ? null,
  rewriteURL,
  hashedMirrors,
}:

let
  defaultNativeBuildInputs = [ curl ];
  inherit (lib)
    concatMap
    elemAt
    fakeHash
    fakeSha256
    fakeSha512
    filter
    hasPrefix
    head
    isList
    isString
    length
    mapAttrs'
    match
    nameValuePair
    toFile
    toShellVars
    warn
    ;
  nixpkgsVersion = lib.trivial.release;

  mirrors = import ./mirrors.nix // {
    inherit hashedMirrors;
  };

  # Write the list of mirrors to a file that we can reuse between
  # fetchurl instantiations, instead of passing the mirrors to
  # fetchurl instantiations via environment variables.  This makes the
  # resulting store derivations (.drv files) much smaller, which in
  # turn makes nix-env/nix-instantiate faster.
  mirrorsListFile =
    let
      # Add a prefix to the names of the mirrors to avoid variable name clashes in the builder
      mirrorsPrefixed = mapAttrs' (n: v: nameValuePair ("_mirror_" + n) v) mirrors;
    in
    toFile "mirrors-list" (toShellVars mirrorsPrefixed);

  # Names of the master sites that are mirrored (i.e., "sourceforge",
  # "gnu", etc.).
  sites = builtins.attrNames mirrors;

  # partially applied set of functions for each hash type
  # this is indexed into with a prefix to avoid re-calling hasPrefix, since it
  # takes advantage of partial application for performance reasons
  hasAlgoPrefix = lib.genAttrs [ "sha256" "sha1" "sha512" ] hasPrefix;

  /**
    Resolve a URL against the available mirrors.

    If the input is a `"mirror://"` URL, it is normalized.
    Otherwise, the URL is returned unmodified in a singleton list.

    Mirror URLs should be formatted as:
    ```
    mirror://{mirror_name}/{path}
    ```

    The specified `mirror_name` must correspond to an entry in `pkgs/build-support/fetchurl/mirrors.nix`, otherwise an error is thrown.

    # Inputs

    `url` (String)
    : A (possibly `"mirror://"`) URL to resolve.

    # Output

    A list of resolved URLs.
  */
  resolveUrl =
    url:
    let
      mirrorSplit = match "mirror://([[:alpha:]]+)/(.+)" url;
      mirrorName = head mirrorSplit;
      mirrorList = mirrors."${mirrorName}" or (throw "unknown mirror:// site ${mirrorName}");
    in
    if mirrorSplit == null || mirrorName == null then
      [ url ]
    else
      map (mirror: mirror + elemAt mirrorSplit 1) mirrorList;

  rewriteAllUrls =
    if rewriteURL == null then
      urls: urls
    else
      urls:
      let
        u = concatMap (
          url:
          let
            rewritten = rewriteURL url;
          in
          if isString rewritten then [ rewritten ] else [ ]
        ) urls;
      in
      if u == [ ] then throw "urls is empty after rewriteURL (was ${toString urls})" else u;

  impureEnvVars =
    lib.fetchers.proxyImpureEnvVars
    ++ [
      # This variable allows the user to pass additional options to curl
      "NIX_CURL_FLAGS"

      # This variable allows the user to override hashedMirrors from the
      # command-line.
      "NIX_HASHED_MIRRORS"

      # This variable allows overriding the timeout for connecting to
      # the hashed mirrors.
      "NIX_CONNECT_TIMEOUT"
    ]
    ++ (map (site: "NIX_MIRRORS_${site}") sites);

in

lib.extendMkDerivation {
  constructDrv = stdenvNoCC.mkDerivation;

  excludeDrvArgNames = [
    # Passed via passthru
    "url"

    # Additional stdenv.mkDerivation arguments from derived fetchers.
    "derivationArgs"

    # Hash attributes will be map to the corresponding outputHash*
    "sha1"
    "sha256"
    "sha512"
  ];

  extendDrvArgs =
    finalAttrs:
    {
      # URL to fetch.
      url ? "",

      # Alternatively, a list of URLs specifying alternative download
      # locations.  They are tried in order.
      urls ? [ ],

      # Additional curl options needed for the download to succeed.
      # Warning: Each space (no matter the escaping) will start a new argument.
      # If you wish to pass arguments with spaces, use `curlOptsList`
      curlOpts ? "",

      # Additional curl options needed for the download to succeed.
      curlOptsList ? [ ],

      # Name of the file when pname + version is unspecified.
      # Default to the basename of `url' (or of the first element of `urls').
      name ? null,

      # for versioned downloads optionally take pname + version.
      pname ? null,
      version ? null,

      # SRI hash.
      hash ? "",

      # Legacy ways of specifying the hash.
      outputHash ? "",
      outputHashAlgo ? "",
      sha1 ? "",
      sha256 ? "",
      sha512 ? "",

      recursiveHash ? false,

      # Shell code to build a netrc file for BASIC auth
      netrcPhase ? null,

      # Impure env vars (https://nixos.org/nix/manual/#sec-advanced-attributes)
      # needed for netrcPhase
      netrcImpureEnvVars ? [ ],

      # Shell code executed after the file has been fetched
      # successfully. This can do things like check or transform the file.
      postFetch ? "",

      # Whether to download to a temporary path rather than $out. Useful
      # in conjunction with postFetch. The location of the temporary file
      # is communicated to postFetch via $downloadedFile.
      downloadToTemp ? false,

      # If true, set executable bit on downloaded file
      executable ? false,

      # If set, don't download the file, but write a list of all possible
      # URLs (resulting from resolving mirror:// URLs) to $out.
      showURLs ? false,

      # Meta information, if any.
      meta ? { },

      # Passthru information, if any.
      passthru ? { },

      # Doing the download on a remote machine just duplicates network
      # traffic, so don't do that by default
      preferLocalBuild ? true,

      # Additional packages needed as part of a fetch
      nativeBuildInputs ? [ ],

      # Additional stdenvNoCC.mkDerivation arguments.
      # It is typically for derived fetchers to pass down additional arguments,
      # and the specified arguments have lower precedence than other mkDerivation arguments.
      derivationArgs ? { },
    }@args:

    let
      preRewriteUrls =
        if urls == [ ] && url != "" then
          (
            if isString url then [ url ] else throw "`url` is not a string: ${lib.generators.toPretty { } urls}"
          )
        else if urls != [ ] && url == "" then
          (if isList urls then urls else throw "`urls` is not a list: ${lib.generators.toPretty { } urls}")
        else
          throw "fetchurl requires either `url` or `urls` to be set: ${lib.generators.toPretty { } args}";

      urls_ = rewriteAllUrls preRewriteUrls;

      hash_ =
        if
          length (
            filter (s: s != "") [
              hash
              outputHash
              sha1
              sha256
              sha512
            ]
          ) > 1
        then
          throw "multiple hashes passed to fetchurl: ${lib.generators.toPretty { } urls_}"
        else

        if hash != "" then
          {
            outputHashAlgo = null;
            outputHash = hash;
          }
        else if outputHash != "" then
          if outputHashAlgo != "" then
            { inherit outputHashAlgo outputHash; }
          else
            throw "fetchurl was passed outputHash without outputHashAlgo: ${lib.generators.toPretty { } urls_}"
        else if sha512 != "" then
          {
            outputHashAlgo = "sha512";
            outputHash = sha512;
          }
        else if sha256 != "" then
          {
            outputHashAlgo = "sha256";
            outputHash = sha256;
          }
        else if sha1 != "" then
          {
            outputHashAlgo = "sha1";
            outputHash = sha1;
          }
        else if cacert != null then
          {
            outputHashAlgo = null;
            outputHash = fakeHash;
          }
        else
          throw "fetchurl requires a hash for fixed-output derivation: ${lib.generators.toPretty { } urls_}";

      finalHashHasColon = match ".*:.*" finalAttrs.hash != null;
      finalHashColonMatch = match "([^:]+)[:](.*)" finalAttrs.hash;
    in

    derivationArgs
    // {
      __structuredAttrs = true;

      name =
        if finalAttrs.pname or null != null && finalAttrs.version or null != null then
          "${finalAttrs.pname}-${finalAttrs.version}"
        else if showURLs then
          "urls"
        else if name != null then
          name
        else
          baseNameOf (toString (head urls_));

      builder = ./builder.sh;

      nativeBuildInputs = defaultNativeBuildInputs ++ nativeBuildInputs;

      strictDeps = true;

      urls = urls_;

      # If set, prefer the content-addressable mirrors
      # (http://tarballs.nixos.org) over the original URLs.
      preferHashedMirrors = false;

      # New-style output content requirements.
      hash =
        if
          hash_.outputHashAlgo == null
          || hash_.outputHash == ""
          || hasAlgoPrefix.${hash_.outputHashAlgo} hash_.outputHash
        then
          hash_.outputHash
        else
          "${hash_.outputHashAlgo}:${hash_.outputHash}";
      outputHashAlgo = if finalHashHasColon then head finalHashColonMatch else null;
      outputHash =
        if finalAttrs.hash == "" then
          fakeHash
        else if finalHashHasColon then
          elemAt finalHashColonMatch 1
        else
          finalAttrs.hash;

      # Disable TLS verification only when we know the hash and no credentials are
      # needed to access the resource
      env = {
        SSL_CERT_FILE =
          if
            (
              hash_.outputHash == ""
              || hash_.outputHash == fakeSha256
              || hash_.outputHash == fakeSha512
              || hash_.outputHash == fakeHash
              || netrcPhase != null
            )
          then
            "${cacert}/etc/ssl/certs/ca-bundle.crt"
          else
            "/no-cert-file.crt";
      }
      // (derivationArgs.env or { });

      outputHashMode = if (recursiveHash || executable) then "recursive" else "flat";

      curlOpts =
        if isList curlOpts then
          warn (
            let
              url = toString (builtins.head urls_);
              curlOptsRepresentation = lib.generators.toPretty { multiline = false; } curlOpts;
              curlOptsAsStringRepresentation = lib.strings.escapeNixString (toString curlOpts);
              curlOptsListElementsRepresentation =
                lib.concatMapStringsSep " " lib.strings.escapeNixString
                  curlOpts;
            in
            ''
              fetchurl for ${url}: curlOpts is a list (${curlOptsRepresentation}), which is not supported anymore.
              - If you wish to get the same effect as before, for elements with spaces (even if escaped) to expand to multiple curl arguments, use a string argument instead:
                curlOpts = ${curlOptsAsStringRepresentation};
              - If you wish for each list element to be passed as a separate curl argument, allowing arguments to contain spaces, use curlOptsList instead:
                curlOptsList = [ ${curlOptsListElementsRepresentation} ];
            ''
          ) curlOpts
        else
          curlOpts;

      inherit
        curlOptsList
        downloadToTemp
        executable
        mirrorsListFile
        postFetch
        showURLs
        ;

      impureEnvVars = impureEnvVars ++ netrcImpureEnvVars;

      inherit nixpkgsVersion;

      inherit preferLocalBuild;

      inherit meta;
      passthru = {
        inherit url;
        resolvedUrl = head (resolveUrl url);
      }
      // passthru;
    };

  # No ellipsis
  inheritFunctionArgs = false;
}
// {
  inherit resolveUrl;
}