{ stable }: let inherit (builtins) attrNames elemAt fetchGit filter functionArgs isAttrs isPath length mapAttrs match pathExists removeAttrs toJSON tryEval; inherit (stable) callPackage makeWrapper symlinkJoin; inherit (stable.lib) attrByPath concatMapStringsSep concatStringsSep const escapeShellArg findFirst getAttrFromPath hasAttrByPath imap1 info mapAttrsToList optionalAttrs optionalString recurseIntoAttrs showAttrPath throwIf toList versionAtLeast; # Utilities composeOverrides = f1: f2: a0: let o1 = f1 a0; o2 = f2 (a0 // o1); in o1 // o2; isEmpty = attrs: length (attrNames attrs) == 0; isStable = r: isAttrs r && ! r ? "_name"; mkRepo = name: path: (import path { inherit (stable) config; overlays = [ ]; }) // { _name = name; }; repoName = r: if isPath r then toString r else r._name or "stable channel"; versionMeetsSpec = candidate: spec: let parts = match "^([^[:alnum:]]+)?(.+)$" spec; operator = elemAt parts 0; version = elemAt parts 1; in if operator == null then candidate == version else if operator == "≥" then versionAtLeast candidate version else throw "version operator not implemented: ${toJSON operator}"; # Repositories pin = rev: mkRepo "pin ${rev}" (fetchGit { name = "nixpkgs-pin-${toString rev}"; url = "https://github.com/NixOS/nixpkgs.git"; inherit rev; }); pr = id: mkRepo "PR #${toString id}" (fetchGit { name = "nixpkgs-pr-${toString id}"; url = "https://github.com/NixOS/nixpkgs.git"; ref = "refs/pull/${toString id}/head"; }); unstable = if (tryEval (pathExists )).value then mkRepo "unstable channel" else info "No unstable channel found" null; # Functions resolve = scope: pname: spec@{ # Namespacing recurseForDerivations ? false # Repository selection , condition ? null , release ? null , search ? null , version ? null # Package defaults , deps ? { } # Package attribute overlay , gappsWrapperArgs ? null , overlay ? null , patch ? null # Wrapper , env ? null # Package input override , ... }: let # Implicit arguments override = removeAttrs spec (attrNames (functionArgs (resolve scope pname))); # Specification doOverlay = gappsWrapperArgs != null || overlay != null || patch != null; doOverride = ! isEmpty override; doWrapper = env != null; # Package selection path = scope ++ [ pname ]; name = showAttrPath path; suffices = r: r != null && hasAttrByPath path r && (release == null || versionMeetsSpec r.lib.trivial.release release) && (version == null || versionMeetsSpec (getAttrFromPath path r).version version) && (condition == null || condition (getAttrFromPath path r)); extra = if search == null then [ ] else imap1 (i: s: { _extra = i; _name = "search"; } // s) (toList search); repo = findFirst suffices (./. + "/${pname}.nix") ([ stable unstable ] ++ extra); package = if isPath repo then callPackage repo deps else getAttrFromPath path repo; # Package overlay package_with_overlay = if doOverlay then package.overrideAttrs (composeOverrides (a: (optionalAttrs (patch != null) { patches = a.patches or [ ] ++ (toList patch); }) // (optionalAttrs (gappsWrapperArgs != null) { preFixup = a.preFixup + "\ngappsWrapperArgs+=(${gappsWrapperArgs})"; }) ) (if overlay == null then const { } else overlay) ) else package; # Package override package_with_overlay_with_override = if doOverride then package_with_overlay.override override else package_with_overlay; # Wrapper package_with_overlay_with_override_with_wrapper = if doWrapper then symlinkJoin { name = "${pname}-wrapper"; paths = [ package_with_overlay_with_override ]; buildInputs = [ makeWrapper ]; postBuild = '' for program in $out/bin/*; do wrapProgram "$program" \ ${concatStringsSep " " (mapAttrsToList (k: v: "--set ${escapeShellArg k} ${escapeShellArg v}") env)} done ''; } else package_with_overlay_with_override; # Report summary = "Resolved ${name}" + (optionalString (version != null) " ${version}") + (optionalString (release != null) " of NixOS ${release}") + (optionalString (doOverlay || doOverride || doWrapper) " with override") + (optionalString (condition != null) " meeting condition") + (optionalString (! isStable repo) " via ${repoName repo}"); unnecessary = isStable repo && !doOverlay && !doOverride && !doWrapper; unnecessarySearches = concatMapStringsSep ", " repoName (filter (r: r._extra > repo._extra or 0) extra); in if hasAttrByPath (path ++ [ "overrideScope'" ]) stable then (getAttrFromPath path stable).overrideScope' (_: _: mapAttrs (resolve path) spec) else if recurseForDerivations || (attrByPath (path ++ [ "recurseForDerivations" ]) false repo) then (attrByPath path { } repo) // (mapAttrs (resolve path) spec) else throwIf (unnecessarySearches != "") "${name} no longer requires searching ${unnecessarySearches}" (throwIf unnecessary "${name} no longer requires an override") (info summary package_with_overlay_with_override_with_wrapper) ; in { inherit pin pr unstable; any = { }; namespaced = mapAttrs (_: recurseIntoAttrs); specify = mapAttrs (resolve [ ]); }