{sources ? import ./nix/sources.nix}: rec { lib = import (sources.nixpkgs + "/lib"); nixosConfigurations = builtins.mapAttrs (_: import (sources.nixpkgs + "/nixos/lib/eval-config.nix")) { vanadium = { system = "x86_64-linux"; modules = [./nix/configurations/vanadium.nix]; }; tungsten = { system = "x86_64-linux"; modules = [./nix/configurations/tungsten.nix]; }; installer = { system = "x86_64-linux"; modules = [./nix/configurations/installer.nix]; }; }; packages = import ./nix/packages {inherit sources;}; # # # Module graph generation experiment below # # inherit (nixosConfigurations.vanadium) options; /* Idea: We only have filepath based information on where an option is set. The oniginal goal was to be able to tell by flipping on which option, which other option(s) will be impacted. This is hence not possible because not all options are guarded by a `enable` option, for example. We could draw out file -> option, and cluster each option in a file rectangle */ traceShowId = x: builtins.trace x x; # Drop those that fail to eval filtered = let drop = path: { inherit path; update = _: {}; }; in lib.updateManyAttrsByPath (map drop [ ["_module"] ["services" "immich"] # caused by option referencing a config value ["services" "nagios"] ["hardware" "nvidia"] ]) # options # Use this for a smaller example # {inherit (options.programs) vim less;} { inherit (options) programs; } ; # Transform each option to its path and call site mapped = lib.mapAttrsRecursiveCond (as: !(as ? _type && as._type == "option")) ( _: v: let definedBy = builtins.tryEval (map (x: x.file) v.definitionsWithLocations); in lib.optionalAttrs definedBy.success { _type = "result"; optionPath = v.loc; definedBy = definedBy.value; declaredAt = # There can only be one (builtins.head v.declarationPositions).file; } ) filtered; attrValuesRecursiveCond = cond: attrs: if lib.isAttrs attrs && cond attrs then lib.flatten ( map (attrValuesRecursiveCond cond) (builtins.attrValues attrs) ) else [attrs]; # Run `nix eval --json -f ./. 'asList' | jq >asList.json` to generate asList = let untag = lib.flip lib.removeAttrs ["_type"]; in map untag ( attrValuesRecursiveCond (as: !(as ? _type && as._type == "result")) mapped ); # TODO: Improve the granularity by checking the prefix # # Each attribute is a list of nodes in the same file clusterized = builtins.foldl' ( acc: elem: acc // { ${elem.declaredAt} = acc.${elem.declaredAt} or [] ++ [elem]; } ) {} asList; subgraphs = lib.mapAttrsToList ( declaredAt: elems: let entries = lib.concatMapStrings ( elem: lib.concatMapStrings ( def: lib.optionalString ( def != elem.declaredAt # remove loops ) '' "${def}" -> "${builtins.concatStringsSep "." elem.optionPath}"; '' ) elem.definedBy ) elems; in '' subgraph "cluster_${declaredAt}" { ${entries} label = "${declaredAt}"; } '' ) clusterized; graphvizOutput = '' digraph { // compound=true; rankdir="TB"; ${builtins.concatStringsSep "\n" subgraphs} } ''; }