nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1# Checks derivation meta and attrs for problems (like brokenness,
2# licenses, etc).
3
4{
5 lib,
6 config,
7 hostPlatform,
8}:
9
10let
11 inherit (lib)
12 all
13 attrNames
14 attrValues
15 concatMapStrings
16 concatMapStringsSep
17 concatStrings
18 filter
19 findFirst
20 getName
21 isDerivation
22 length
23 concatMap
24 mutuallyExclusive
25 optional
26 optionalString
27 isAttrs
28 isString
29 mapAttrs
30 ;
31
32 inherit (lib.lists)
33 any
34 toList
35 isList
36 elem
37 unique
38 ;
39
40 inherit (lib.meta)
41 availableOn
42 cpeFullVersionWithVendor
43 tryCPEPatchVersionInUpdateWithVendor
44 ;
45
46 inherit (lib.generators)
47 toPretty
48 ;
49
50 inherit (builtins)
51 getEnv
52 trace
53 ;
54
55 # If we're in hydra, we can dispense with the more verbose error
56 # messages and make problems easier to spot.
57 inHydra = config.inHydra or false;
58 # Allow the user to opt-into additional warnings, e.g.
59 # import <nixpkgs> { config = { showDerivationWarnings = [ "maintainerless" ]; }; }
60 showWarnings = config.showDerivationWarnings;
61
62 getNameWithVersion =
63 attrs: attrs.name or "${attrs.pname or "«name-missing»"}-${attrs.version or "«version-missing»"}";
64
65 allowUnfree = config.allowUnfree || getEnv "NIXPKGS_ALLOW_UNFREE" == "1";
66
67 allowNonSource =
68 let
69 envVar = getEnv "NIXPKGS_ALLOW_NONSOURCE";
70 in
71 if envVar != "" then envVar != "0" else config.allowNonSource or true;
72
73 allowlist = config.allowlistedLicenses or config.whitelistedLicenses or [ ];
74 blocklist = config.blocklistedLicenses or config.blacklistedLicenses or [ ];
75
76 areLicenseListsValid =
77 if mutuallyExclusive allowlist blocklist then
78 true
79 else
80 throw "allowlistedLicenses and blocklistedLicenses are not mutually exclusive.";
81
82 hasListedLicense =
83 assert areLicenseListsValid;
84 list:
85 if list == [ ] then
86 attrs: false
87 else
88 attrs:
89 attrs ? meta.license
90 && (
91 if isList attrs.meta.license then
92 any (l: elem l list) attrs.meta.license
93 else
94 elem attrs.meta.license list
95 );
96
97 hasAllowlistedLicense = hasListedLicense allowlist;
98
99 hasBlocklistedLicense = hasListedLicense blocklist;
100
101 allowBroken = config.allowBroken || getEnv "NIXPKGS_ALLOW_BROKEN" == "1";
102
103 allowUnsupportedSystem =
104 config.allowUnsupportedSystem || getEnv "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM" == "1";
105
106 isUnfree =
107 licenses:
108 if isAttrs licenses then
109 !(licenses.free or true)
110 # TODO: Returning false in the case of a string is a bug that should be fixed.
111 # In a previous implementation of this function the function body
112 # was `licenses: lib.lists.any (l: !l.free or true) licenses;`
113 # which always evaluates to `!true` for strings.
114 else if isString licenses then
115 false
116 else
117 any (l: !(l.free or true)) licenses;
118
119 hasUnfreeLicense = attrs: attrs ? meta.license && isUnfree attrs.meta.license;
120
121 hasNoMaintainers =
122 # To get usable output, we want to avoid flagging "internal" derivations.
123 # Because we do not have a way to reliably decide between internal or
124 # external derivation, some heuristics are required to decide.
125 #
126 # If `outputHash` is defined, the derivation is a FOD, such as the output of a fetcher.
127 # If `description` is not defined, the derivation is probably not a package.
128 # Simply checking whether `meta` is defined is insufficient,
129 # as some fetchers and trivial builders do define meta.
130 attrs:
131 (!attrs ? outputHash)
132 && (attrs ? meta.description)
133 && (attrs.meta.maintainers or [ ] == [ ])
134 && (attrs.meta.teams or [ ] == [ ]);
135
136 isMarkedBroken = attrs: attrs.meta.broken or false;
137
138 # Allow granular checks to allow only some broken packages
139 # Example:
140 # { pkgs, ... }:
141 # {
142 # allowBroken = false;
143 # allowBrokenPredicate = pkg: builtins.elem (pkgs.lib.getName pkg) [ "hello" ];
144 # }
145 allowBrokenPredicate = config.allowBrokenPredicate or (x: false);
146
147 hasDeniedBroken =
148 attrs: (attrs.meta.broken or false) && !allowBroken && !allowBrokenPredicate attrs;
149
150 hasUnsupportedPlatform = pkg: !(availableOn hostPlatform pkg);
151
152 isMarkedInsecure = attrs: (attrs.meta.knownVulnerabilities or [ ]) != [ ];
153
154 # Allow granular checks to allow only some unfree packages
155 # Example:
156 # {pkgs, ...}:
157 # {
158 # allowUnfree = false;
159 # allowUnfreePredicate = (x: pkgs.lib.hasPrefix "vscode" x.name);
160 # }
161 # Defaults to allow all names defined in config.allowUnfreePackages
162 allowUnfreePredicate =
163 let
164 listPredicate = pkg: builtins.elem (lib.getName pkg) (config.allowUnfreePackages or [ ]);
165
166 # Be robust against misconfigured allowUnfreePredicate values such as null
167 explicitPredicate =
168 let
169 raw = config.allowUnfreePredicate or null;
170 in
171 if builtins.isFunction raw then raw else (_: false);
172 in
173 pkg: (listPredicate pkg) || (explicitPredicate pkg);
174
175 # Check whether unfree packages are allowed and if not, whether the
176 # package has an unfree license and is not explicitly allowed by the
177 # `allowUnfreePredicate` function.
178 hasDeniedUnfreeLicense =
179 attrs: hasUnfreeLicense attrs && !allowUnfree && !allowUnfreePredicate attrs;
180
181 allowInsecureDefaultPredicate =
182 x: elem (getNameWithVersion x) (config.permittedInsecurePackages or [ ]);
183 allowInsecurePredicate = config.allowInsecurePredicate or allowInsecureDefaultPredicate;
184
185 allowInsecure = getEnv "NIXPKGS_ALLOW_INSECURE" == "1";
186
187 hasDisallowedInsecure =
188 attrs: isMarkedInsecure attrs && !allowInsecure && !allowInsecurePredicate attrs;
189
190 # Allow granular checks to allow only some non-source-built packages
191 # Example:
192 # { pkgs, ... }:
193 # {
194 # allowNonSource = false;
195 # allowNonSourcePredicate = with pkgs.lib.lists; pkg: !(any (p: !p.isSource && p != lib.sourceTypes.binaryFirmware) pkg.meta.sourceProvenance);
196 # }
197 allowNonSourcePredicate = config.allowNonSourcePredicate or (x: false);
198
199 # Check whether non-source packages are allowed and if not, whether the
200 # package has non-source provenance and is not explicitly allowed by the
201 # `allowNonSourcePredicate` function.
202 hasDeniedNonSourceProvenance =
203 attrs:
204 attrs ? meta.sourceProvenance
205 && any (t: !t.isSource) attrs.meta.sourceProvenance
206 && !allowNonSource
207 && !allowNonSourcePredicate attrs;
208
209 showLicenseOrSourceType =
210 value: toString (map (v: v.shortName or v.fullName or "unknown") (toList value));
211 showLicense = showLicenseOrSourceType;
212 showSourceType = showLicenseOrSourceType;
213
214 pos_str = meta: meta.position or "«unknown-file»";
215
216 remediation_env_var =
217 allow_attr:
218 {
219 Unfree = "NIXPKGS_ALLOW_UNFREE";
220 Broken = "NIXPKGS_ALLOW_BROKEN";
221 UnsupportedSystem = "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM";
222 NonSource = "NIXPKGS_ALLOW_NONSOURCE";
223 }
224 .${allow_attr};
225 remediation_phrase =
226 allow_attr:
227 {
228 Unfree = "unfree packages";
229 Broken = "broken packages";
230 UnsupportedSystem = "packages that are unsupported for this system";
231 NonSource = "packages not built from source";
232 }
233 .${allow_attr};
234 remediate_predicate = predicateConfigAttr: attrs: ''
235
236 Alternatively you can configure a predicate to allow specific packages:
237 { nixpkgs.config.${predicateConfigAttr} = pkg: builtins.elem (lib.getName pkg) [
238 "${getName attrs}"
239 ];
240 }
241 '';
242
243 # flakeNote will be printed in the remediation messages below.
244 flakeNote = "
245 Note: When using `nix shell`, `nix build`, `nix develop`, etc with a flake,
246 then pass `--impure` in order to allow use of environment variables.
247 ";
248
249 remediate_allowlist = allow_attr: rebuild_amendment: ''
250 a) To temporarily allow ${remediation_phrase allow_attr}, you can use an environment variable
251 for a single invocation of the nix tools.
252
253 $ export ${remediation_env_var allow_attr}=1
254 ${flakeNote}
255 b) For `nixos-rebuild` you can set
256 { nixpkgs.config.allow${allow_attr} = true; }
257 in configuration.nix to override this.
258 ${rebuild_amendment}
259 c) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add
260 { allow${allow_attr} = true; }
261 to ~/.config/nixpkgs/config.nix.
262 '';
263
264 remediate_insecure =
265 attrs:
266 ''
267
268 Known issues:
269 ''
270 + (concatStrings (map (issue: " - ${issue}\n") attrs.meta.knownVulnerabilities))
271 + ''
272
273 You can install it anyway by allowing this package, using the
274 following methods:
275
276 a) To temporarily allow all insecure packages, you can use an environment
277 variable for a single invocation of the nix tools:
278
279 $ export NIXPKGS_ALLOW_INSECURE=1
280 ${flakeNote}
281 b) for `nixos-rebuild` you can add ‘${getNameWithVersion attrs}’ to
282 `nixpkgs.config.permittedInsecurePackages` in the configuration.nix,
283 like so:
284
285 {
286 nixpkgs.config.permittedInsecurePackages = [
287 "${getNameWithVersion attrs}"
288 ];
289 }
290
291 c) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add
292 ‘${getNameWithVersion attrs}’ to `permittedInsecurePackages` in
293 ~/.config/nixpkgs/config.nix, like so:
294
295 {
296 permittedInsecurePackages = [
297 "${getNameWithVersion attrs}"
298 ];
299 }
300
301 '';
302
303 remediateOutputsToInstall =
304 attrs:
305 let
306 expectedOutputs = attrs.meta.outputsToInstall or [ ];
307 actualOutputs = attrs.outputs or [ "out" ];
308 missingOutputs = filter (output: !elem output actualOutputs) expectedOutputs;
309 in
310 ''
311 The package ${getNameWithVersion attrs} has set meta.outputsToInstall to: ${builtins.concatStringsSep ", " expectedOutputs}
312
313 however ${getNameWithVersion attrs} only has the outputs: ${builtins.concatStringsSep ", " actualOutputs}
314
315 and is missing the following outputs:
316
317 ${concatStrings (map (output: " - ${output}\n") missingOutputs)}
318 '';
319
320 metaTypes =
321 let
322 types = import ./meta-types.nix { inherit lib; };
323 inherit (types)
324 str
325 union
326 int
327 attrs
328 attrsOf
329 any
330 listOf
331 bool
332 ;
333 platforms = listOf (union [
334 str
335 (attrsOf any)
336 ]); # see lib.meta.platformMatch
337 in
338 {
339 # These keys are documented
340 description = str;
341 mainProgram = str;
342 longDescription = str;
343 branch = str;
344 homepage = union [
345 (listOf str)
346 str
347 ];
348 downloadPage = str;
349 changelog = union [
350 (listOf str)
351 str
352 ];
353 license =
354 let
355 # TODO disallow `str` licenses, use a module
356 licenseType = union [
357 (attrsOf any)
358 str
359 ];
360 in
361 union [
362 (listOf licenseType)
363 licenseType
364 ];
365 sourceProvenance = listOf attrs;
366 maintainers = listOf (attrsOf any); # TODO use the maintainer type from lib/tests/maintainer-module.nix
367 teams = listOf (attrsOf any); # TODO similar to maintainers, use a teams type
368 priority = int;
369 pkgConfigModules = listOf str;
370 inherit platforms;
371 hydraPlatforms = listOf str;
372 broken = bool;
373 unfree = bool;
374 unsupported = bool;
375 insecure = bool;
376 timeout = int;
377 knownVulnerabilities = listOf str;
378 badPlatforms = platforms;
379
380 # Needed for Hydra to expose channel tarballs:
381 # https://github.com/NixOS/hydra/blob/53335323ae79ca1a42643f58e520b376898ce641/doc/manual/src/jobs.md#meta-fields
382 isHydraChannel = bool;
383
384 # Weirder stuff that doesn't appear in the documentation?
385 maxSilent = int;
386 name = str;
387 version = str;
388 tag = str;
389 executables = listOf str;
390 outputsToInstall = listOf str;
391 position = str;
392 available = any;
393 isBuildPythonPackage = platforms;
394 schedulingPriority = int;
395 isFcitxEngine = bool;
396 isIbusEngine = bool;
397 isGutenprint = bool;
398
399 # Used for the original location of the maintainer and team attributes to assist with pings.
400 maintainersPosition = any;
401 teamsPosition = any;
402
403 identifiers = attrs;
404 };
405
406 # Map attrs directly to the verify function for performance
407 metaTypes' = mapAttrs (_: t: t.verify) metaTypes;
408
409 checkMetaAttr =
410 k: v:
411 if metaTypes ? ${k} then
412 if metaTypes'.${k} v then
413 [ ]
414 else
415 [
416 "key 'meta.${k}' has invalid value; expected ${metaTypes.${k}.name}, got\n ${
417 toPretty { indent = " "; } v
418 }"
419 ]
420 else
421 [
422 "key 'meta.${k}' is unrecognized; expected one of: \n [${
423 concatMapStringsSep ", " (x: "'${x}'") (attrNames metaTypes)
424 }]"
425 ];
426
427 checkMeta = meta: concatMap (attr: checkMetaAttr attr meta.${attr}) (attrNames meta);
428
429 metaInvalid =
430 if config.checkMeta then
431 meta: !all (attr: metaTypes ? ${attr} && metaTypes'.${attr} meta.${attr}) (attrNames meta)
432 else
433 meta: false;
434
435 checkOutputsToInstall =
436 if config.checkMeta then
437 attrs:
438 let
439 actualOutputs = attrs.outputs or [ "out" ];
440 in
441 any (output: !elem output actualOutputs) (attrs.meta.outputsToInstall or [ ])
442 else
443 attrs: false;
444
445 # Check if a derivation is valid, that is whether it passes checks for
446 # e.g brokenness or license.
447 #
448 # Return { valid: "yes", "warn" or "no" } and additionally
449 # { reason: String; errormsg: String, remediation: String } if it is not valid, where
450 # reason is one of "unfree", "blocklisted", "broken", "insecure", ...
451 # !!! reason strings are hardcoded into OfBorg, make sure to keep them in sync
452 # Along with a boolean flag for each reason
453 checkValidity =
454 attrs:
455 # Check meta attribute types first, to make sure it is always called even when there are other issues
456 # Note that this is not a full type check and functions below still need to by careful about their inputs!
457 if metaInvalid (attrs.meta or { }) then
458 {
459 reason = "unknown-meta";
460 errormsg = "has an invalid meta attrset:${
461 concatMapStrings (x: "\n - " + x) (checkMeta attrs.meta)
462 }\n";
463 remediation = "";
464 }
465
466 # --- Put checks that cannot be ignored here ---
467 else if checkOutputsToInstall attrs then
468 {
469 reason = "broken-outputs";
470 errormsg = "has invalid meta.outputsToInstall";
471 remediation = remediateOutputsToInstall attrs;
472 }
473
474 # --- Put checks that can be ignored here ---
475 else if hasDeniedUnfreeLicense attrs && !(hasAllowlistedLicense attrs) then
476 {
477 reason = "unfree";
478 errormsg = "has an unfree license (‘${showLicense attrs.meta.license}’)";
479 remediation = remediate_allowlist "Unfree" (remediate_predicate "allowUnfreePredicate" attrs);
480 }
481 else if hasBlocklistedLicense attrs then
482 {
483 reason = "blocklisted";
484 errormsg = "has a blocklisted license (‘${showLicense attrs.meta.license}’)";
485 remediation = "";
486 }
487 else if hasDeniedNonSourceProvenance attrs then
488 {
489 reason = "non-source";
490 errormsg = "contains elements not built from source (‘${showSourceType attrs.meta.sourceProvenance}’)";
491 remediation = remediate_allowlist "NonSource" (remediate_predicate "allowNonSourcePredicate" attrs);
492 }
493 else if hasDeniedBroken attrs then
494 {
495 reason = "broken";
496 errormsg = "is marked as broken";
497 remediation = remediate_allowlist "Broken" "";
498 }
499 else if hasUnsupportedPlatform attrs && !allowUnsupportedSystem then
500 let
501 toPretty' = toPretty {
502 allowPrettyValues = true;
503 indent = " ";
504 };
505 in
506 {
507 reason = "unsupported";
508 errormsg = ''
509 is not available on the requested hostPlatform:
510 hostPlatform.system = "${hostPlatform.system}"
511 package.meta.platforms = ${toPretty' (attrs.meta.platforms or [ ])}
512 package.meta.badPlatforms = ${toPretty' (attrs.meta.badPlatforms or [ ])}
513 '';
514 remediation = remediate_allowlist "UnsupportedSystem" "";
515 }
516 else if hasDisallowedInsecure attrs then
517 {
518 reason = "insecure";
519 errormsg = "is marked as insecure";
520 remediation = remediate_insecure attrs;
521 }
522 else
523 null;
524
525 # Please also update the type in /pkgs/top-level/config.nix alongside this.
526 checkWarnings =
527 attrs:
528 if hasNoMaintainers attrs then
529 {
530 reason = "maintainerless";
531 errormsg = "has no maintainers or teams";
532 remediation = "";
533 }
534 else
535 null;
536
537 # Helper functions and declarations to handle identifiers, extracted to reduce allocations
538 hasAllCPEParts =
539 cpeParts:
540 let
541 values = attrValues cpeParts;
542 in
543 (length values == 11) && !any isNull values;
544 makeCPE =
545 {
546 part,
547 vendor,
548 product,
549 version,
550 update,
551 edition,
552 sw_edition,
553 target_sw,
554 target_hw,
555 language,
556 other,
557 }:
558 "cpe:2.3:${part}:${vendor}:${product}:${version}:${update}:${edition}:${sw_edition}:${target_sw}:${target_hw}:${language}:${other}";
559 possibleCPEPartsFuns = [
560 (vendor: version: {
561 success = true;
562 value = cpeFullVersionWithVendor vendor version;
563 })
564 tryCPEPatchVersionInUpdateWithVendor
565 ];
566
567 # The meta attribute is passed in the resulting attribute set,
568 # but it's not part of the actual derivation, i.e., it's not
569 # passed to the builder and is not a dependency. But since we
570 # include it in the result, it *is* available to nix-env for queries.
571 # Example:
572 # meta = checkMeta.commonMeta { inherit validity attrs pos references; };
573 # validity = checkMeta.assertValidity { inherit meta attrs; };
574 commonMeta =
575 {
576 validity,
577 attrs,
578 pos ? null,
579 references ? [ ],
580 }:
581 let
582 outputs = attrs.outputs or [ "out" ];
583 hasOutput = out: builtins.elem out outputs;
584 maintainersPosition = builtins.unsafeGetAttrPos "maintainers" (attrs.meta or { });
585 teamsPosition = builtins.unsafeGetAttrPos "teams" (attrs.meta or { });
586 in
587 {
588 # `name` derivation attribute includes cross-compilation cruft,
589 # is under assert, and is sanitized.
590 # Let's have a clean always accessible version here.
591 name = attrs.name or "${attrs.pname}-${attrs.version}";
592
593 # If the packager hasn't specified `outputsToInstall`, choose a default,
594 # which is the name of `p.bin or p.out or p` along with `p.man` when
595 # present.
596 #
597 # If the packager has specified it, it will be overridden below in
598 # `// meta`.
599 #
600 # Note: This default probably shouldn't be globally configurable.
601 # Services and users should specify outputs explicitly,
602 # unless they are comfortable with this default.
603 outputsToInstall = [
604 (
605 if hasOutput "bin" then
606 "bin"
607 else if hasOutput "out" then
608 "out"
609 else
610 findFirst hasOutput null outputs
611 )
612 ]
613 ++ optional (hasOutput "man") "man";
614
615 # CI scripts look at these to determine pings. Note that we should filter nulls out of this,
616 # or nix-env complains: https://github.com/NixOS/nix/blob/2.18.8/src/nix-env/nix-env.cc#L963
617 ${if maintainersPosition == null then null else "maintainersPosition"} = maintainersPosition;
618 ${if teamsPosition == null then null else "teamsPosition"} = teamsPosition;
619 }
620 // attrs.meta or { }
621 // {
622 # Fill `meta.position` to identify the source location of the package.
623 ${if pos == null then null else "position"} = pos.file + ":" + toString pos.line;
624
625 # Maintainers should be inclusive of teams.
626 # Note that there may be external consumers of this API (repology, for instance) -
627 # if you add a new maintainer or team attribute please ensure that this expectation is still met.
628 maintainers = unique (
629 attrs.meta.maintainers or [ ] ++ concatMap (team: team.members or [ ]) attrs.meta.teams or [ ]
630 );
631
632 identifiers =
633 let
634 # nix-env writes a warning for each derivation that has null in its meta values, so
635 # fields without known values are removed from the result
636 defaultCPEParts = {
637 part = "a";
638 #vendor = null;
639 ${if attrs.pname or null != null then "product" else null} = attrs.pname;
640 #version = null;
641 #update = null;
642 edition = "*";
643 sw_edition = "*";
644 target_sw = "*";
645 target_hw = "*";
646 language = "*";
647 other = "*";
648 };
649
650 cpeParts = defaultCPEParts // attrs.meta.identifiers.cpeParts or { };
651 cpe = if hasAllCPEParts cpeParts then makeCPE cpeParts else null;
652
653 possibleCPEs =
654 if cpe != null then
655 [ { inherit cpeParts cpe; } ]
656 else if attrs.meta.identifiers.cpeParts.vendor or null == null || attrs.version or null == null then
657 [ ]
658 else
659 concatMap (
660 f:
661 let
662 result = f attrs.meta.identifiers.cpeParts.vendor attrs.version;
663 # Note that attrs.meta.identifiers.cpeParts at this point can include defaults with user overrides.
664 # Since we can't split them apart, user overrides don't apply to possibleCPEs.
665 guessedParts = cpeParts // result.value;
666 in
667 optional (result.success && hasAllCPEParts guessedParts) {
668 cpeParts = guessedParts;
669 cpe = makeCPE guessedParts;
670 }
671 ) possibleCPEPartsFuns;
672 v1 = {
673 inherit cpeParts possibleCPEs;
674 ${if cpe != null then "cpe" else null} = cpe;
675 };
676 in
677 v1
678 // {
679 inherit v1;
680 };
681
682 # Expose the result of the checks for everyone to see.
683 unfree = hasUnfreeLicense attrs;
684 broken = isMarkedBroken attrs;
685 unsupported = hasUnsupportedPlatform attrs;
686 insecure = isMarkedInsecure attrs;
687
688 available =
689 validity.valid != "no"
690 && ((config.checkMetaRecursively or false) -> all (d: d.meta.available or true) references);
691 };
692
693 validYes = {
694 valid = "yes";
695 handled = true;
696 };
697
698 assertValidity =
699 { meta, attrs }:
700 let
701 invalid = checkValidity attrs;
702 warning = checkWarnings attrs;
703 in
704 if isNull invalid then
705 if isNull warning then
706 validYes
707 else
708 let
709 msg =
710 if inHydra then
711 "Warning while evaluating ${getNameWithVersion attrs}: «${warning.reason}»: ${warning.errormsg}"
712 else
713 "Package ${getNameWithVersion attrs} in ${pos_str meta} ${warning.errormsg}, continuing anyway."
714 + (optionalString (warning.remediation != "") "\n${warning.remediation}");
715
716 handled = if elem warning.reason showWarnings then trace msg true else true;
717 in
718 warning
719 // {
720 valid = "warn";
721 handled = handled;
722 }
723 else
724 let
725 msg =
726 if inHydra then
727 "Failed to evaluate ${getNameWithVersion attrs}: «${invalid.reason}»: ${invalid.errormsg}"
728 else
729 ''
730 Package ‘${getNameWithVersion attrs}’ in ${pos_str meta} ${invalid.errormsg}, refusing to evaluate.
731
732 ''
733 + invalid.remediation;
734
735 handled = if config ? handleEvalIssue then config.handleEvalIssue invalid.reason msg else throw msg;
736 in
737 invalid
738 // {
739 valid = "no";
740 handled = handled;
741 };
742
743in
744{
745 inherit assertValidity commonMeta;
746}