{ lib, devFlake, }: let # Some helper functions /** Compute a filtered closure of build inputs. Specifically, `buildInputsClosure cond startSet` computes the closure formed by recursive application of `p: filter cond p.buildInputs ++ filter cond p.propagatedBuildInputs` to `startSet`. Example: ```nix builtInputsClosure isInternal [ pkg1 pkg2 ] => [ pkg1 pkg3 pkg2 pkg10 ] ``` Note: order tbd Note: `startSet` is *NOT* filtered. */ buildInputsClosureCond = cond: startSet: let closure = builtins.genericClosure { startSet = map (d: { key = d.drvPath; value = d; }) startSet; operator = d: let r = map (d': { key = d'.drvPath; value = d'; }) ( lib.filter cond d.value.buildInputs or [ ] ++ lib.filter cond d.value.propagatedBuildInputs or [ ] ); in r; }; in map (item: item.value) closure; /** `[ pkg1 pkg2 ]` -> `{ "...-pkg2.drv" = null; "...-pkg1.drv" = null }` Note: fairly arbitrary order (hash based). Use for efficient set membership test only. */ byDrvPath = l: lib.listToAttrs ( map (c: { name = # Just a lookup key builtins.unsafeDiscardStringContext c.drvPath; value = null; }) l ); /** Stable dedup. Unlike `listToAttrs` -> `attrValues`, this preserves the input ordering, which is more predictable ("deterministic") than e.g. sorting store paths, whose hashes affect the ordering on every change. */ # TODO: add to Nixpkgs lib, refer from uniqueStrings dedupByString = key: l: let r = lib.foldl' ( a@{ list, set }: elem: let k = builtins.unsafeDiscardStringContext (key elem); in if set ? ${k} then a else let # Note: O(n²) copying. Use linkedLists to concat them in one go at the end. # https://github.com/NixOS/nixpkgs/pull/452088 newList = [ elem ] ++ list; newSet = set // { ${k} = null; }; in builtins.seq newList builtins.seq newSet { list = newList; set = newSet; } ) { list = [ ]; set = { }; } l; in r.list; in { pkgs }: # TODO: don't use nix-util for this? pkgs.nixComponents2.nix-util.overrideAttrs ( finalAttrs: prevAttrs: let stdenv = pkgs.nixDependencies2.stdenv; buildCanExecuteHost = stdenv.buildPlatform.canExecute stdenv.hostPlatform; modular = devFlake.getSystem stdenv.buildPlatform.system; transformFlag = prefix: flag: assert builtins.isString flag; let rest = builtins.substring 2 (builtins.stringLength flag) flag; in "-D${prefix}:${rest}"; havePerl = stdenv.buildPlatform == stdenv.hostPlatform && stdenv.hostPlatform.isUnix; ignoreCrossFile = flags: builtins.filter (flag: !(lib.strings.hasInfix "cross-file" flag)) flags; activeComponents = buildInputsClosureCond isInternal ( lib.attrValues (finalAttrs.passthru.config.getComponents allComponents) ); allComponents = lib.filterAttrs (k: v: lib.isDerivation v) pkgs.nixComponents2; internalDrvs = byDrvPath ( # Drop the attr names (not present in buildInputs anyway) lib.attrValues allComponents ++ lib.concatMap (c: lib.attrValues c.tests or { }) (lib.attrValues allComponents) ); isInternal = dep: internalDrvs ? ${builtins.unsafeDiscardStringContext dep.drvPath or "_non-existent_"}; in { pname = "shell-for-nix"; passthru = { inherit activeComponents; # We use this attribute to store non-derivation values like functions and # perhaps other things that are primarily for overriding and not the shell. config = { # Default getComponents getComponents = c: builtins.removeAttrs c ( lib.optionals (!havePerl) [ "nix-perl-bindings" ] ++ lib.optionals (!buildCanExecuteHost) [ "nix-manual" ] ); }; /** Produce a devShell for a given set of nix components Example: ```nix shell.withActiveComponents (c: { inherit (c) nix-util; }) ``` */ withActiveComponents = f2: finalAttrs.finalPackage.overrideAttrs ( finalAttrs: prevAttrs: { passthru = prevAttrs.passthru // { config = prevAttrs.passthru.config // { getComponents = f2; }; }; } ); small = (finalAttrs.finalPackage.withActiveComponents (c: { inherit (c) nix-cli nix-util-tests nix-store-tests nix-expr-tests nix-fetchers-tests nix-flake-tests nix-functional-tests # Currently required nix-perl-bindings ; })).overrideAttrs (o: { mesonFlags = o.mesonFlags ++ [ # TODO: infer from activeComponents or vice versa "-Dkaitai-struct-checks=false" "-Djson-schema-checks=false" ]; }); }; # Remove the version suffix to avoid unnecessary attempts to substitute in nix develop version = lib.fileContents ../.version; name = finalAttrs.pname; installFlags = "sysconfdir=$(out)/etc"; shellHook = '' PATH=$prefix/bin:$PATH unset PYTHONPATH export MANPATH=$out/share/man:$MANPATH # Make bash completion work. XDG_DATA_DIRS+=:$out/share # Make the default phases do the right thing. # FIXME: this wouldn't be needed if the ninja package set buildPhase() instead of $buildPhase. # FIXME: mesonConfigurePhase shouldn't cd to the build directory. It would be better to pass '-C