Skip to content

Commit

Permalink
nixos/modules: Add declarationsWithLocations
Browse files Browse the repository at this point in the history
What it does: line and column level *declaration* position information:

$ nix repl .
nix-repl> :p nixosConfigurations.micro.options.environment.systemPackages.declarationsWithLocations
[ { column = 7; file = "/nix/store/24aj3k7fgqv3ly7qkbf98qvphasrw9nb-source/nixos/modules/config/system-path.nix"; line = 63; } ]

Use cases:
- ctags over NixOS options, as will be presented at NixCon 2023 ;)
- improving the documentation pages to go to the exact line of the
  declarations.

Related work:
- NixOS#65024

  This one does it for all *definitions* rather than declarations, and
  it was not followed through with due to performance worries.
- NixOS#208173

  The basis for this change. This change is just a rebase of that one.
  I split it out to add the capability before adding users of it, in
  order to simplify review. However, the ctags script in there is a
  sample user of this feature.

Benchmarks: conducted by evaluating my own reasonably complex NixOS
configuration with the command:
`hyperfine -S none -w 1 -- "nix eval .#nixosConfigurations.snowflake.config.system.build.toplevel.outPath"`

```
Benchmark 1: nix eval .#nixosConfigurations.snowflake.config.system.build.toplevel.outPath
  Time (mean ± σ):      8.971 s ±  0.254 s    [User: 5.872 s, System: 1.388 s]
  Range (min … max):    8.574 s …  9.327 s    10 runs

Benchmark 1: nix eval .#nixosConfigurations.snowflake.config.system.build.toplevel.outPath
  Time (mean ± σ):      8.766 s ±  0.160 s    [User: 5.873 s, System: 1.346 s]
  Range (min … max):    8.496 s …  9.033 s    10 runs
```

Summary of results: it seems to be in the noise, this does not cause any
visible regression in times.
  • Loading branch information
lf- committed Aug 15, 2023
1 parent 450d643 commit 177beab
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 8 deletions.
16 changes: 10 additions & 6 deletions lib/modules.nix
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ let
# there's _module.args. If specialArgs.modulesPath is defined it will be
# used as the base path for disabledModules.
specialArgs ? {}
, # Whether to get declaration locations. This has some
# performance cost to evaluation time.
declarationLocations ? false
, # `class`:
# A nominal type for modules. When set and non-null, this adds a check to
# make sure that only compatible modules are imported.
Expand Down Expand Up @@ -459,7 +462,7 @@ let
else config;
in
if m ? config || m ? options then
let badAttrs = removeAttrs m ["_class" "_file" "key" "disabledModules" "imports" "options" "config" "meta" "freeformType"]; in
let badAttrs = removeAttrs m ["_class" "_file" "_loc" "key" "disabledModules" "imports" "options" "config" "meta" "freeformType"]; in
if badAttrs != {} then
throw "Module `${key}' has an unsupported attribute `${head (attrNames badAttrs)}'. This is caused by introducing a top-level `config' or `options' attribute. Add configuration attributes immediately on the top level instead, or move all of them (namely: ${toString (attrNames badAttrs)}) into the explicit `config' attribute."
else
Expand All @@ -480,7 +483,7 @@ let
disabledModules = m.disabledModules or [];
imports = m.require or [] ++ m.imports or [];
options = {};
config = addFreeformType (removeAttrs m ["_class" "_file" "key" "disabledModules" "require" "imports" "freeformType"]);
config = addFreeformType (removeAttrs m ["_class" "_file" "_loc" "key" "disabledModules" "require" "imports" "freeformType"]);
};

applyModuleArgsIfFunction = key: f: args@{ config, options, lib, ... }:
Expand Down Expand Up @@ -537,7 +540,7 @@ let
mergeModules' prefix modules
(concatMap (m: map (config: { file = m._file; inherit config; }) (pushDownProperties m.config)) modules);

mergeModules' = prefix: options: configs:
mergeModules' = prefix: modules: configs:
let
# an attrset 'name' => list of submodules that declare ‘name’.
declsByName =
Expand All @@ -554,11 +557,11 @@ let
else
mapAttrs
(n: option:
[{ inherit (module) _file; options = option; }]
[{ inherit (module) _file; _loc = builtins.unsafeGetAttrPos n subtree; options = option; }]
)
subtree
)
options);
modules);

# The root of any module definition must be an attrset.
checkedConfigs =
Expand Down Expand Up @@ -730,9 +733,10 @@ let
else res.options;
in opt.options // res //
{ declarations = res.declarations ++ [opt._file];
declarationsWithLocations = res.declarationsWithLocations ++ optional (opt._loc != null) opt._loc;
options = submodules;
} // typeSet
) { inherit loc; declarations = []; options = []; } opts;
) { inherit loc; declarations = []; declarationsWithLocations = []; options = []; } opts;

/* Merge all the definitions of an option to produce the final
config value. */
Expand Down
3 changes: 2 additions & 1 deletion nixos/lib/eval-config-minimal.nix
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@ let
prefix ? [],
modules ? [],
specialArgs ? {},
declarationLocations ? false,
}:
# NOTE: Regular NixOS currently does use this function! Don't break it!
# Ideally we don't diverge, unless we learn that we should.
# In other words, only the public interface of nixos.evalModules
# is experimental.
lib.evalModules {
inherit prefix modules;
inherit prefix modules declarationLocations;
class = "nixos";
specialArgs = {
modulesPath = builtins.toString ../modules;
Expand Down
3 changes: 2 additions & 1 deletion nixos/lib/eval-config.nix
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ evalConfigArgs@
, lib ? import ../../lib
, extraModules ? let e = builtins.getEnv "NIXOS_EXTRA_MODULE_PATH";
in lib.optional (e != "") (import e)
, declarationLocations ? false
}:

let pkgs_ = pkgs;
Expand Down Expand Up @@ -94,7 +95,7 @@ let
locatedModules ++ legacyModules;

noUserModules = evalModulesMinimal ({
inherit prefix specialArgs;
inherit prefix specialArgs declarationLocations;
modules = baseModules ++ extraModules ++ [ pkgsModule modulesModule ];
});

Expand Down

0 comments on commit 177beab

Please sign in to comment.