diff --git a/dev/README.md b/dev/README.md index 270517d..c8f5e4b 100644 --- a/dev/README.md +++ b/dev/README.md @@ -1,6 +1,10 @@ - # Separate `dev` flake Wouldn't recommend this pattern normally, but I'm trying to keep deps low for `flake-parts` until we have split dev inputs that don't carry over to dependent lock files. + +```sh +nix develop --impure -f './dev' 'mySystem.devShells.default' +nix repl -f './dev' +``` diff --git a/dev/default.nix b/dev/default.nix index df712a4..bae0ed8 100644 --- a/dev/default.nix +++ b/dev/default.nix @@ -1,16 +1,36 @@ let + flake-parts = builtins.getFlake (toString ../.); + lib = flake-parts.inputs.nixpkgs-lib.lib; + sourceInfo = inputs.flake-parts.sourceInfo; # used by pre-commit module, etc flake = builtins.getFlake (toString ./.); - fmc-lib = (builtins.getFlake (toString ../.)).lib; - args = { - inherit self; - } // flake.inputs; - self = { - inherit (flake) inputs; - outPath = ../.; # used by pre-commit module, etc - outputs = self.config.flake; - } // - fmc-lib.mkFlake - { inputs = args; } - ./flake-module.nix; + inputs = flake.inputs // { inherit flake-parts; }; + makeResult = specialArgs: flakeModule: result: + let + outputs = flake.outputs // flake-parts.lib.mkFlake + { + inputs = inputs // { self = result; }; + # debugging tool + specialArgs = { + replaceSpecialArgs = newSpecialArgs: + let + newSpecialArgs' = + if lib.isFunction newSpecialArgs + then newSpecialArgs specialArgs + else newSpecialArgs; + newResult = makeResult newSpecialArgs' flakeModule newResult; + in + newResult; + } // specialArgs; + } + flakeModule; + in + outputs // sourceInfo // { + inherit inputs outputs sourceInfo; + _type = "flake"; + }; in -self.config.flake // { inherit (flake) inputs; } +let + # eagerly import to reproduce inline evaluation + result = makeResult { } (import ./flake-module.nix) result; +in +result diff --git a/dev/flake-module.nix b/dev/flake-module.nix index 5ac60b9..397c0ae 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -1,19 +1,25 @@ -{ config, lib, inputs, withSystem, ... }: - -{ +{ config, inputs, lib, options, specialArgs, withSystem, ... } @ args: +let + rootArgs = args; + rootConfig = config; + rootOptions = options; + rootSpecialArgs = specialArgs; +in +# debugging tool +specialArgs.flakeModuleTransformer or (args: flakeModule: flakeModule) args { imports = [ inputs.pre-commit-hooks-nix.flakeModule inputs.hercules-ci-effects.flakeModule # herculesCI attr ]; - systems = [ "x86_64-linux" "aarch64-darwin" ]; + config.systems = [ "x86_64-linux" "aarch64-darwin" ]; - hercules-ci.flake-update = { + config.hercules-ci.flake-update = { enable = true; autoMergeMethod = "merge"; when.dayOfMonth = 1; }; - perSystem = { config, pkgs, ... }: { + config.perSystem = { config, pkgs, ... }: { devShells.default = pkgs.mkShell { nativeBuildInputs = [ @@ -38,11 +44,18 @@ in tests.runTests pkgs.emptyFile // { internals = tests; }; }; - flake = { - # for repl exploration / debug - config.config = config; - options.mySystem = lib.mkOption { default = config.allSystems.${builtins.currentSystem}; }; - config.effects = withSystem "x86_64-linux" ({ pkgs, hci-effects, ... }: { + config.flake = { config, options, specialArgs, ... } @ args: { + # for REPL exploration / debugging + config.allFlakeModuleArgs = args // config._module.args // specialArgs; + config.allRootModuleArgs = rootArgs // rootConfig._module.args // rootSpecialArgs; + config.transformFlakeModule = flakeModuleTransformer: + rootSpecialArgs.replaceSpecialArgs (prevSpecialArgs: prevSpecialArgs // { + inherit flakeModuleTransformer; + }); + options.mySystem = lib.mkOption { + default = rootConfig.allSystems.${builtins.currentSystem}; + }; + config.effects = withSystem "x86_64-linux" ({ hci-effects, pkgs, ... }: { tests = { template = pkgs.callPackage ./tests/template.nix { inherit hci-effects; }; }; diff --git a/dev/tests/eval-tests.nix b/dev/tests/eval-tests.nix index 437ee1a..a1ebd5f 100644 --- a/dev/tests/eval-tests.nix +++ b/dev/tests/eval-tests.nix @@ -2,17 +2,54 @@ # # nix build -f dev checks.x86_64-linux.eval-tests -rec { - f-p = builtins.getFlake (toString ../..); - flake-parts = f-p; +let + flake-parts = builtins.getFlake (toString ../..); + lib = flake-parts.inputs.nixpkgs-lib.lib; +in +(lib.makeExtensibleWithCustomName "extendEvalTests" (evalTests: { + inherit evalTests; + inherit flake-parts; + flake-parts-lib = evalTests.flake-parts.lib; + inherit lib; devFlake = builtins.getFlake (toString ../.); - nixpkgs = devFlake.inputs.nixpkgs; + nixpkgs = evalTests.devFlake.inputs.nixpkgs; - f-p-lib = f-p.lib; + inherit (evalTests.flake-parts-lib) mkFlake; + weakEvalTests.callFlake = { ... } @ flake: + let + sourceInfo = flake.sourceInfo or { }; + inputs = flake.inputs or { }; + outputs = flake.outputs inputs; + result = outputs; + in + result; + strongEvalTests.callFlake = { ... } @ flake: + let + sourceInfo = { outPath = "/unknown_eval-tests_flake"; } // + flake.sourceInfo or { }; + inputs = flake.inputs or { }; + outputs = flake.outputs (inputs // { self = result; }); + result = outputs // sourceInfo // { + inherit inputs outputs sourceInfo; + _type = "flake"; + }; + in + assert builtins.isFunction flake.outputs; + result; + + withWeakEvalTests = evalTests.extendEvalTests (finalEvalTests: prevEvalTests: + builtins.mapAttrs (name: value: finalEvalTests.weakEvalTests.${name}) + prevEvalTests.weakEvalTests + ); + withStrongEvalTests = evalTests.extendEvalTests (finalEvalTests: prevEvalTests: + builtins.mapAttrs (name: value: finalEvalTests.strongEvalTests.${name}) + prevEvalTests.strongEvalTests + ); - inherit (f-p-lib) mkFlake; - inherit (f-p.inputs.nixpkgs-lib) lib; + exhibitingInfiniteRecursion = false; + exhibitInfiniteRecursion = evalTests.extendEvalTests + (finalEvalTest: prevEvalTests: { exhibitingInfiniteRecursion = true; }); pkg = system: name: derivation { name = name; @@ -20,41 +57,202 @@ rec { system = system; }; - empty = mkFlake - { inputs.self = { }; } - { + empty = evalTests.callFlake { + inputs.flake-parts = evalTests.flake-parts; + inputs.self = { }; + outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } { systems = [ ]; }; + }; + weakEvalTests.emptyResult = { + apps = { }; + checks = { }; + devShells = { }; + formatter = { }; + legacyPackages = { }; + nixosConfigurations = { }; + nixosModules = { }; + overlays = { }; + packages = { }; + }; + strongEvalTests.emptyResult = let + _type = "flake"; + inputs.flake-parts = evalTests.flake-parts; + inputs.self = { }; + outputs = evalTests.weakEvalTests.emptyResult; + sourceInfo.outPath = "/unknown_eval-tests_flake"; + result = outputs // sourceInfo // { inherit _type inputs outputs sourceInfo; }; + in result; + runEmptyTests = ok: + assert evalTests.empty == evalTests.emptyResult; + ok; + emptyTestsResult = evalTests.runEmptyTests "ok"; - example1 = mkFlake - { inputs.self = { }; } - { + tooEmpty = evalTests.callFlake { + inputs.flake-parts = evalTests.flake-parts; + inputs.self = { }; + outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } { + }; + }; + # Shallow evaluation is successful… + weakEvalTests.tooEmptyResultTried0.success = true; + weakEvalTests.tooEmptyResultTried0.value = { }; + weakEvalTests.tooEmptyResultTried0TestTried.success = true; + weakEvalTests.tooEmptyResultTried0TestTried.value = false; + # …including for flake outputs… + strongEvalTests.tooEmptyResultTried0 = evalTests.weakEvalTests.tooEmptyResultTried0; + strongEvalTests.tooEmptyResultTried0TestTried = evalTests.weakEvalTests.tooEmptyResultTried0TestTried; + # …but any evaluations of attribute values (flake output values) are not. + weakEvalTests.tooEmptyResultTried1.success = true; + weakEvalTests.tooEmptyResultTried1.value = { + apps = { }; + checks = { }; + devShells = { }; + formatter = { }; + legacyPackages = { }; + nixosConfigurations = { }; + nixosModules = { }; + overlays = { }; + packages = { }; + }; + weakEvalTests.tooEmptyResultTried1TestTried.success = false; + weakEvalTests.tooEmptyResultTried1TestTried.value = false; + strongEvalTests.tooEmptyResultTried1.success = true; + strongEvalTests.tooEmptyResultTried1.value = let + _type = "flake"; + inputs.flake-parts = evalTests.flake-parts; + inputs.self = { }; + outputs = evalTests.weakEvalTests.tooEmptyResultTried1.value; + sourceInfo.outPath = "/unknown_eval-tests_flake"; + result = outputs // sourceInfo // { inherit _type inputs outputs sourceInfo; }; + in result; + strongEvalTests.tooEmptyResultTried1TestTried.success = false; + strongEvalTests.tooEmptyResultTried1TestTried.value = false; + runTooEmptyTests = ok: + let + tooEmptyResultTried = builtins.tryEval evalTests.tooEmpty; + tooEmptyResultTried0TestTried = builtins.tryEval (tooEmptyResultTried == evalTests.tooEmptyResultTried0); + tooEmptyResultTried1TestTried = builtins.tryEval (tooEmptyResultTried == evalTests.tooEmptyResultTried1); + in + assert tooEmptyResultTried0TestTried == evalTests.tooEmptyResultTried0TestTried; + assert tooEmptyResultTried1TestTried == evalTests.tooEmptyResultTried1TestTried; + ok; + tooEmptyTestsResult = evalTests.runTooEmptyTests "ok"; + + example1 = evalTests.callFlake { + inputs.flake-parts = evalTests.flake-parts; + inputs.self = { }; + outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } { systems = [ "a" "b" ]; perSystem = { system, ... }: { - packages.hello = pkg system "hello"; + packages.hello = evalTests.pkg system "hello"; }; }; + }; + weakEvalTests.example1Result = { + apps = { a = { }; b = { }; }; + checks = { a = { }; b = { }; }; + devShells = { a = { }; b = { }; }; + formatter = { }; + legacyPackages = { a = { }; b = { }; }; + nixosConfigurations = { }; + nixosModules = { }; + overlays = { }; + packages = { + a = { hello = evalTests.pkg "a" "hello"; }; + b = { hello = evalTests.pkg "b" "hello"; }; + }; + }; + strongEvalTests.example1Result = let + _type = "flake"; + inputs.flake-parts = evalTests.flake-parts; + inputs.self = { }; + outputs = evalTests.weakEvalTests.example1Result; + sourceInfo.outPath = "/unknown_eval-tests_flake"; + result = outputs // sourceInfo // { inherit _type inputs outputs sourceInfo; }; + in result; + runExample1Tests = ok: + assert evalTests.example1 == evalTests.example1Result; + ok; + example1TestsResult = evalTests.runExample1Tests "ok"; + + # This test case is a fun one. In the REPL, try `exhibitInfiniteRecursion.*`. + # In the case that `mkFlake` *isn't* called from a flake, `inputs.self` is + # unlikely to refer to the result of the `mkFlake` evaluation. If + # `inputs.self` isn't actually self-referential, evaluating attribute values + # of `self` is not divergent. Evaluation of `self.outPath` is useful for + # paths in documentation & error messages. However, if that evaluation occurs + # in a `builtins.addErrorContext` message forced by an erroring `self`, both + # `self` will never evaluate *and* `builtins.toString self.outPath` must + # evaluate, causing Nix to instead throw an infinite recursion error. Even + # just `inputs.self ? outPath` throws an infinite recursion error. + # (`builtins.tryEval` can only catch errors created by `builtins.throw` or + # `builtins.assert`, so evaluation is guarded with + # `exhibitingInfiniteRecursion` here to keep `runTests` from diverging.) + # In this particular case, `mkFlake` evaluates `self ? outPath` to know if the + # default module location it provides should be generic or specific. As + # explained, this evaluation is unsafe under an uncatchably divergent `self`. + # Thus, `outPath` cannot be safely sourced from `self` at the top-level. + # + # When tests are exhibititing infinite recursion, the abnormally correct + # `self` is provided. + weakEvalTests.nonexistentOption = let result = evalTests.callFlake { + inputs.flake-parts = evalTests.flake-parts; + inputs.self = if !evalTests.exhibitingInfiniteRecursion then { } else result; + outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } { + config.systems = [ ]; + config.nonexistentOption = null; + }; + }; in result; + # When using actual flakes, this test always diverges. Unless tests are + # exhibiting infinite recursion, the flake is made equivalent to `empty`. + strongEvalTests.nonexistentOption = evalTests.callFlake { + inputs.flake-parts = evalTests.flake-parts; + inputs.self = { }; + outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } ({ + config.systems = [ ]; + } // (if !evalTests.exhibitingInfiniteRecursion then { } else { + config.nonexistentOption = null; + })); + }; + weakEvalTests.nonexistentOptionResultTried0.success = true; + weakEvalTests.nonexistentOptionResultTried0.value = { }; + weakEvalTests.nonexistentOptionResultTried0TestTried.success = true; + weakEvalTests.nonexistentOptionResultTried0TestTried.value = false; + strongEvalTests.nonexistentOptionResultTried0 = evalTests.weakEvalTests.nonexistentOptionResultTried0; + strongEvalTests.nonexistentOptionResultTried0TestTried = evalTests.weakEvalTests.nonexistentOptionResultTried0TestTried; + runNonexistentOptionTests = ok: + let + nonexistentOptionResultTried = builtins.tryEval evalTests.nonexistentOption; + nonexistentOptionResultTried0TestTried = builtins.tryEval (nonexistentOptionResultTried == evalTests.nonexistentOptionResultTried0); + in + assert nonexistentOptionResultTried0TestTried == evalTests.nonexistentOptionResultTried0TestTried; + ok; + nonexistentOptionTestsResult = evalTests.runNonexistentOptionTests "ok"; - packagesNonStrictInDevShells = mkFlake - { inputs.self = packagesNonStrictInDevShells; /* approximation */ } - { + packagesNonStrictInDevShells = evalTests.callFlake { + inputs.flake-parts = evalTests.flake-parts; + # approximation + inputs.self = evalTests.packagesNonStrictInDevShells; + outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } { systems = [ "a" "b" ]; - perSystem = { system, self', ... }: { - packages.hello = pkg system "hello"; + perSystem = { self', system, ... }: { + packages.hello = evalTests.pkg system "hello"; packages.default = self'.packages.hello; devShells = throw "can't be strict in perSystem.devShells!"; }; flake.devShells = throw "can't be strict in devShells!"; }; - easyOverlay = mkFlake - { inputs.self = { }; } - { - imports = [ flake-parts.flakeModules.easyOverlay ]; + easyOverlay = evalTests.callFlake { + inputs.flake-parts = evalTests.flake-parts; + inputs.self = { }; + outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } { + imports = [ inputs.flake-parts.flakeModules.easyOverlay ]; systems = [ "a" "aarch64-linux" ]; perSystem = { system, config, final, pkgs, ... }: { packages.default = config.packages.hello; - packages.hello = pkg system "hello"; + packages.hello = evalTests.pkg system "hello"; packages.hello_new = final.hello; overlayAttrs = { hello = config.packages.hello; @@ -63,11 +261,13 @@ rec { }; }; }; + }; - flakeModulesDeclare = mkFlake - { inputs.self = { outPath = ./.; }; } - ({ config, ... }: { - imports = [ flake-parts.flakeModules.flakeModules ]; + flakeModulesDeclare = evalTests.callFlake { + inputs.flake-parts = evalTests.flake-parts; + inputs.self = { outPath = ./.; }; + outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } ({ config, inputs, lib, ... }: { + imports = [ inputs.flake-parts.flakeModules.flakeModules ]; systems = [ ]; flake.flakeModules.default = { lib, ... }: { options.flake.test123 = lib.mkOption { default = "option123"; }; @@ -77,92 +277,90 @@ rec { flake.test123 = "123test"; }; }); + }; - flakeModulesImport = mkFlake - { inputs.self = { }; } - { - imports = [ flakeModulesDeclare.flakeModules.default ]; + flakeModulesImport = evalTests.callFlake { + inputs.flake-parts = evalTests.flake-parts; + inputs.flakeModulesDeclare = evalTests.flakeModulesDeclare; + inputs.self = { }; + outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } { + imports = [ inputs.flakeModulesDeclare.flakeModules.default ]; }; + }; + runFlakeModulesImportTests = ok: + assert evalTests.flakeModulesImport.test123 == "123test"; + ok; + flakeModulesImportTestsResult = evalTests.runFlakeModulesImportTests "ok"; - flakeModulesDisable = mkFlake - { inputs.self = { }; } - { - imports = [ flakeModulesDeclare.flakeModules.default ]; - disabledModules = [ flakeModulesDeclare.flakeModules.extra ]; + flakeModulesDisable = evalTests.callFlake { + inputs.flake-parts = evalTests.flake-parts; + inputs.flakeModulesDeclare = evalTests.flakeModulesDeclare; + inputs.self = { }; + outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } { + imports = [ inputs.flakeModulesDeclare.flakeModules.default ]; + disabledModules = [ inputs.flakeModulesDeclare.flakeModules.extra ]; }; + }; + runFlakeModulesDisableTests = ok: + assert evalTests.flakeModulesDisable.test123 == "option123"; + ok; + flakeModulesDisableTestsResult = evalTests.runFlakeModulesDisableTests "ok"; - nixpkgsWithoutEasyOverlay = import nixpkgs { + nixpkgsWithoutEasyOverlay = import evalTests.nixpkgs { system = "x86_64-linux"; overlays = [ ]; config = { }; }; - nixpkgsWithEasyOverlay = import nixpkgs { + nixpkgsWithEasyOverlay = import evalTests.nixpkgs { # non-memoized system = "x86_64-linux"; - overlays = [ easyOverlay.overlays.default ]; + overlays = [ evalTests.easyOverlay.overlays.default ]; config = { }; }; - nixpkgsWithEasyOverlayMemoized = import nixpkgs { + nixpkgsWithEasyOverlayMemoized = import evalTests.nixpkgs { # memoized system = "aarch64-linux"; - overlays = [ easyOverlay.overlays.default ]; + overlays = [ evalTests.easyOverlay.overlays.default ]; config = { }; }; + tryEvalOutputs = outputs: builtins.seq (builtins.attrNames outputs) outputs; + runTests = ok: - assert empty == { - apps = { }; - checks = { }; - devShells = { }; - formatter = { }; - legacyPackages = { }; - nixosConfigurations = { }; - nixosModules = { }; - overlays = { }; - packages = { }; - }; + assert evalTests.runEmptyTests true; - assert example1 == { - apps = { a = { }; b = { }; }; - checks = { a = { }; b = { }; }; - devShells = { a = { }; b = { }; }; - formatter = { }; - legacyPackages = { a = { }; b = { }; }; - nixosConfigurations = { }; - nixosModules = { }; - overlays = { }; - packages = { - a = { hello = pkg "a" "hello"; }; - b = { hello = pkg "b" "hello"; }; - }; - }; + assert evalTests.runTooEmptyTests true; + + assert evalTests.runExample1Tests true; + + assert evalTests.runNonexistentOptionTests true; # - exported package becomes part of overlay. # - perSystem is invoked for the right system, when system is non-memoized - assert nixpkgsWithEasyOverlay.hello == pkg "x86_64-linux" "hello"; + assert evalTests.nixpkgsWithEasyOverlay.hello == evalTests.pkg "x86_64-linux" "hello"; # - perSystem is invoked for the right system, when system is memoized - assert nixpkgsWithEasyOverlayMemoized.hello == pkg "aarch64-linux" "hello"; + assert evalTests.nixpkgsWithEasyOverlayMemoized.hello == evalTests.pkg "aarch64-linux" "hello"; # - Non-exported package does not become part of overlay. - assert nixpkgsWithEasyOverlay.default or null != pkg "x86_64-linux" "hello"; + assert evalTests.nixpkgsWithEasyOverlay.default or null != evalTests.pkg "x86_64-linux" "hello"; # - hello_old comes from super - assert nixpkgsWithEasyOverlay.hello_old == nixpkgsWithoutEasyOverlay.hello; + assert evalTests.nixpkgsWithEasyOverlay.hello_old == evalTests.nixpkgsWithoutEasyOverlay.hello; # - `hello_new` shows that the `final` wiring works - assert nixpkgsWithEasyOverlay.hello_new == nixpkgsWithEasyOverlay.hello; + assert evalTests.nixpkgsWithEasyOverlay.hello_new == evalTests.nixpkgsWithEasyOverlay.hello; - assert flakeModulesImport.test123 == "123test"; + assert evalTests.runFlakeModulesImportTests true; - assert flakeModulesDisable.test123 == "option123"; + assert evalTests.runFlakeModulesDisableTests true; - assert packagesNonStrictInDevShells.packages.a.default == pkg "a" "hello"; + assert evalTests.packagesNonStrictInDevShells.packages.a.default == evalTests.pkg "a" "hello"; ok; - result = runTests "ok"; -} + result = evalTests.runTests "ok"; +})).withWeakEvalTests diff --git a/lib.nix b/lib.nix index deebf70..31871c4 100644 --- a/lib.nix +++ b/lib.nix @@ -37,8 +37,11 @@ let attrs@{ staticModules ? [ ] }: mkOptionType { name = "deferredModule"; description = "module"; + descriptionClass = "noun"; check = x: isAttrs x || isFunction x || path.check x; - merge = loc: defs: staticModules ++ map (def: lib.setDefaultModuleLocation "${def.file}, via option ${showOption loc}" def.value) defs; + merge = loc: defs: { + imports = staticModules ++ map (def: lib.setDefaultModuleLocation "${def.file}, via option ${showOption loc}" def.value) defs; + }; inherit (submoduleWith { modules = staticModules; }) getSubOptions getSubModules; diff --git a/modules/apps.nix b/modules/apps.nix index 0fd5d7c..e395238 100644 --- a/modules/apps.nix +++ b/modules/apps.nix @@ -15,7 +15,9 @@ let }; getExe = x: - "${lib.getBin x}/bin/${x.meta.mainProgram or (throw ''Package ${x.name or ""} does not have meta.mainProgram set, so I don't know how to find the main executable. You can set meta.mainProgram, or pass the full path to executable, e.g. program = "''${pkg}/bin/foo"'')}"; + "${lib.getBin x}/bin/${x.meta.mainProgram or (throw + ''Package ${x.name or ""} does not have meta.mainProgram set, so I don't know how to find the main executable. You can set `meta.mainProgram`, or pass the full path to executable, e.g. program = "''${pkg}/bin/foo"'' + )}"; appType = lib.types.submodule { options = { diff --git a/modules/moduleWithSystem.nix b/modules/moduleWithSystem.nix index e5c7008..e4d397f 100644 --- a/modules/moduleWithSystem.nix +++ b/modules/moduleWithSystem.nix @@ -9,22 +9,23 @@ let system = config._module.args.system or - config._module.args.pkgs.stdenv.hostPlatform.system or - (throw "moduleWithSystem: Could not determine the configuration's system parameter for this module system application."); + config._module.args.pkgs.stdenv.hostPlatform.system or (throw + "moduleWithSystem: Could not determine the `system` parameter for this module set evaluation." + ); - allArgs = withSystem system (args: args); + allPerSystemArgs = withSystem system (args: args); - lazyArgsPerParameter = f: builtins.mapAttrs - (k: v: allArgs.${k} or (throw "moduleWithSystem: module argument `${k}` does not exist.")) + lazyPerSystemArgsPerParameter = f: builtins.mapAttrs + (k: v: allPerSystemArgs.${k} or (throw "moduleWithSystem: per-system argument `${k}` does not exist.")) (builtins.functionArgs f); # Use reflection to make the call lazy in the argument. # Restricts args to the ones declared. - callLazily = f: a: f (lazyArgsPerParameter f); + callLazily = f: a: f (lazyPerSystemArgsPerParameter f); in { imports = [ - (callLazily module allArgs) + (callLazily module allPerSystemArgs) ]; }; }; diff --git a/modules/nixpkgs.nix b/modules/nixpkgs.nix index 44df915..730af49 100644 --- a/modules/nixpkgs.nix +++ b/modules/nixpkgs.nix @@ -13,11 +13,13 @@ # { config = { - perSystem = { inputs', lib, ... }: { + perSystem = { inputs', lib, options, ... }: { config = { _module.args.pkgs = lib.mkOptionDefault ( builtins.seq - (inputs'.nixpkgs or (throw "flake-parts: The flake does not have a `nixpkgs` input. Please add it, or set `perSystem._module.args.pkgs` yourself.")) + inputs'.nixpkgs or (throw + "flake-parts: The flake does not have a `nixpkgs` input. Please add it, or set `${options._module.args}.pkgs` yourself." + ) inputs'.nixpkgs.legacyPackages ); }; diff --git a/modules/perSystem.nix b/modules/perSystem.nix index 851b30a..29a7fc5 100644 --- a/modules/perSystem.nix +++ b/modules/perSystem.nix @@ -1,4 +1,4 @@ -{ config, lib, flake-parts-lib, self, ... }: +{ config, lib, flake-parts-lib, inputs, self, ... }: let inherit (lib) genAttrs @@ -92,7 +92,7 @@ in type = mkPerSystemType ({ config, system, ... }: { _file = ./perSystem.nix; config = { - _module.args.inputs' = mapAttrs (k: rootConfig.perInput system) self.inputs; + _module.args.inputs' = mapAttrs (k: rootConfig.perInput system) inputs; _module.args.self' = rootConfig.perInput system self; # Custom error messages @@ -103,9 +103,9 @@ in _module.args.moduleWithSystem = throwAliasError "moduleWithSystem"; }; }); - apply = modules: system: + apply = module: system: (lib.evalModules { - inherit modules; + modules = [ module ]; prefix = [ "perSystem" system ]; specialArgs = { inherit system; diff --git a/template/multi-module/hello/flake-module.nix b/template/multi-module/hello/flake-module.nix index 0ddc6d3..e6817a7 100644 --- a/template/multi-module/hello/flake-module.nix +++ b/template/multi-module/hello/flake-module.nix @@ -1,7 +1,7 @@ # Definitions can be imported from a separate file like this one -{ self, lib, ... }: { - perSystem = { config, self', inputs', pkgs, ... }: { +{ config, lib, inputs, ... }: { + perSystem = { config, inputs', pkgs, ... }: { # Definitions like this are entirely equivalent to the ones # you may have directly in flake.nix. packages.hello = pkgs.hello; @@ -9,8 +9,8 @@ flake = { nixosModules.hello = { pkgs, ... }: { environment.systemPackages = [ - # or self.inputs.nixpkgs.legacyPackages.${pkgs.stdenv.hostPlatform.system}.hello - self.packages.${pkgs.stdenv.hostPlatform.system}.hello + # or inputs.nixpkgs.legacyPackages.${pkgs.stdenv.hostPlatform.system}.hello + config.flake.packages.${pkgs.stdenv.hostPlatform.system}.hello ]; }; };