diff --git a/flake-modules/lib.nix b/flake-modules/lib.nix index c905482b2e..2e606f5128 100644 --- a/flake-modules/lib.nix +++ b/flake-modules/lib.nix @@ -27,7 +27,9 @@ { pkgs, system, ... }: { # NOTE: this is the publicly documented flake output we've had for a while - check = pkgs.callPackage ../lib/tests.nix { inherit self; }; + check = pkgs.callPackage ../lib/tests.nix { + inherit lib self system; + }; # NOTE: no longer needs to be per-system helpers = lib.warn "nixvim: `.lib.${system}.helpers` has been moved to `.lib.nixvim` and no longer depends on a specific system" self.lib.nixvim; diff --git a/flake-modules/wrappers.nix b/flake-modules/wrappers.nix index 30fa85be2a..084f5ce0ab 100644 --- a/flake-modules/wrappers.nix +++ b/flake-modules/wrappers.nix @@ -1,10 +1,18 @@ -{ inputs, self, ... }: +{ + inputs, + self, + lib, + ... +}: { perSystem = { system, pkgs, ... }: { _module.args = { - makeNixvimWithModule = import ../wrappers/standalone.nix pkgs self; + makeNixvimWithModule = import ../wrappers/standalone.nix { + inherit lib self; + defaultSystem = system; + }; }; checks = diff --git a/lib/modules.nix b/lib/modules.nix index 5843d50306..fa1792756b 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -21,12 +21,16 @@ in { modules ? [ ], extraSpecialArgs ? { }, + system ? null, # Can also be defined using the `nixpkgs.hostPlatform` option }: # Ensure a suitable `lib` is used # TODO: offer a lib overlay that end-users could use to apply nixvim's extensions to their own `lib` assert lib.assertMsg (extraSpecialArgs ? lib -> extraSpecialArgs.lib ? nixvim) '' Nixvim requires a lib that includes some custom extensions, however the `lib` from `specialArgs` does not have a `nixvim` attr. Remove `lib` from nixvim's `specialArgs` or ensure you apply nixvim's extensions to your `lib`.''; + assert lib.assertMsg (system != null -> lib.isString system) '' + When `system` is supplied to `evalNixvim`, it must be a string. + To define a more complex system, please use nixvim's `nixpkgs.hostPlatform` option.''; lib.evalModules { modules = modules ++ [ ../modules/top-level @@ -34,6 +38,12 @@ in _file = ""; flake = lib.mkOptionDefault flake; } + (lib.optionalAttrs (lib.isString system) { + _file = "evalNixvim"; + # FIXME: what priority should this have? + # If the user has set it as an evalNixvim arg _and_ a module option what behaviour would they expect? + nixpkgs.hostPlatform = lib.mkOptionDefault { inherit system; }; + }) ]; specialArgs = { inherit lib; diff --git a/lib/tests.nix b/lib/tests.nix index 2363750514..b4179631eb 100644 --- a/lib/tests.nix +++ b/lib/tests.nix @@ -1,11 +1,10 @@ { self, - pkgs, - lib ? pkgs.lib, - ... + system, + lib, }: let - defaultPkgs = pkgs; + defaultSystem = system; # Create a nix derivation from a nixvim executable. # The build phase simply consists in running the provided nvim binary. @@ -30,7 +29,8 @@ let mkTestDerivationFromNixvimModule = { name ? null, - pkgs ? defaultPkgs, + pkgs ? null, + system ? defaultSystem, module, extraSpecialArgs ? { }, }: @@ -42,14 +42,18 @@ let _nixvimTests = true; }; + systemMod = + if pkgs == null then + { nixpkgs.hostPlatform = lib.mkDefault { inherit system; }; } + else + { nixpkgs.pkgs = lib.mkDefault pkgs; }; + result = helpers.modules.evalNixvim { modules = [ module (lib.optionalAttrs (name != null) { test.name = name; }) { wrapRc = true; } - # TODO: Only do this when `args?pkgs` - # Consider deprecating the `pkgs` arg too... - { nixpkgs.pkgs = lib.mkDefault pkgs; } + systemMod ]; inherit extraSpecialArgs; }; diff --git a/modules/top-level/nixpkgs.nix b/modules/top-level/nixpkgs.nix index dd7e0d11b3..c6d625315c 100644 --- a/modules/top-level/nixpkgs.nix +++ b/modules/top-level/nixpkgs.nix @@ -2,28 +2,38 @@ config, options, lib, + pkgs, ... }: let cfg = config.nixpkgs; opt = options.nixpkgs; + + isConfig = x: builtins.isAttrs x || lib.isFunction x; + + mergeConfig = + lhs_: rhs_: + let + optCall = maybeFn: x: if lib.isFunction maybeFn then maybeFn x else maybeFn; + lhs = optCall lhs_ { inherit pkgs; }; + rhs = optCall rhs_ { inherit pkgs; }; + in + lib.recursiveUpdate lhs rhs + // lib.optionalAttrs (lhs ? packageOverrides) { + packageOverrides = + pkgs: + optCall lhs.packageOverrides pkgs // optCall (lib.attrByPath [ "packageOverrides" ] { } rhs) pkgs; + } + // lib.optionalAttrs (lhs ? perlPackageOverrides) { + perlPackageOverrides = + pkgs: + optCall lhs.perlPackageOverrides pkgs + // optCall (lib.attrByPath [ "perlPackageOverrides" ] { } rhs) pkgs; + }; in { options.nixpkgs = { pkgs = lib.mkOption { - # TODO: - # defaultText = lib.literalExpression '' - # import "''${nixos}/.." { - # inherit (cfg) config overlays localSystem crossSystem; - # } - # ''; - defaultText = lib.literalMD '' - The `pkgs` inherited from your host config (i.e. NixOS, home-manager, or nix-darwin), - or the `pkgs` supplied to `makeNixvimWithModule` when building a standalone nixvim. - - > [!CAUTION] - > This default will be removed in a future version of nixvim - ''; type = lib.types.pkgs // { description = "An evaluation of Nixpkgs; the top level attribute set of packages"; }; @@ -31,17 +41,7 @@ in description = '' If set, the `pkgs` argument to all Nixvim modules is the value of this option. - - If unset, an assertion will trigger. In the future a `pkgs` instance will be constructed. - - + If unset, the `pkgs` argument is determined by importing `nixpkgs.source`. This option can be used by external applications to increase the performance of evaluation, or to create packages that depend on a container that should be built with the exact same @@ -56,6 +56,29 @@ in ''; }; + config = lib.mkOption { + default = { }; + example = { + allowBroken = true; + allowUnfree = true; + }; + type = lib.mkOptionType { + name = "nixpkgs-config"; + description = "nixpkgs config"; + check = x: isConfig x || lib.traceSeqN 1 x false; + merge = args: lib.foldr (def: mergeConfig def.value) { }; + }; + description = '' + Global configuration for Nixpkgs. + The complete list of [Nixpkgs configuration options] is in the [Nixpkgs manual section on global configuration]. + + Ignored when {option}`nixpkgs.pkgs` is set. + + [Nixpkgs configuration options]: https://nixos.org/manual/nixpkgs/unstable/#sec-config-options-reference + [Nixpkgs manual section on global configuration]: https://nixos.org/manual/nixpkgs/unstable/#chap-packageconfig + ''; + }; + overlays = lib.mkOption { type = let @@ -114,15 +137,55 @@ in For details, see the [Overlays chapter in the Nixpkgs manual](https://nixos.org/manual/nixpkgs/stable/#chap-overlays). - - Overlays specified using the {option}`nixpkgs.overlays` option will be - applied after the overlays that were already included in `nixpkgs.pkgs`. + If the {option}`nixpkgs.pkgs` option is set, overlays specified using `nixpkgs.overlays` + will be applied after the overlays that were already included in `nixpkgs.pkgs`. + ''; + }; + + hostPlatform = lib.mkOption { + type = with lib.types; either str attrs; + example = { + system = "aarch64-linux"; + }; + apply = lib.systems.elaborate; + defaultText = lib.literalMD '' + Inherited from the "host" configuration's `pkgs`. + Or the `evalNixvim`'s `system` argument. + Otherwise, must be specified manually. + ''; + description = '' + Specifies the platform where the Nixvim configuration will run. + + To cross-compile, also set `nixpkgs.buildPlatform`. - + Ignored when `nixpkgs.pkgs` is set. + ''; + }; + + buildPlatform = lib.mkOption { + type = with lib.types; either str attrs; + default = cfg.hostPlatform; + example = { + system = "x86_64-linux"; + }; + apply = + value: + let + elaborated = lib.systems.elaborate value; + in + # If equivalent to `hostPlatform`, make it actually identical so that `==` can be used + # See https://github.com/NixOS/nixpkgs/issues/278001 + if lib.systems.equals elaborated cfg.hostPlatform then cfg.hostPlatform else elaborated; + defaultText = lib.literalMD '' + Inherited from the "host" configuration's `pkgs`. + Or `config.nixpkgs.hostPlatform` when building a standalone nixvim. + ''; + description = '' + Specifies the platform on which Nixvim should be built. + By default, Nixvim is built on the system where it runs, but you can change where it's built. + Setting this option will cause Nixvim to be cross-compiled. + + Ignored when `nixpkgs.pkgs` is set. ''; }; @@ -136,26 +199,33 @@ in Ignored when `nixpkgs.pkgs` is set. ''; - - # FIXME: This is a stub option for now - internal = true; }; }; config = let - # TODO: construct a default pkgs instance from pkgsPath and cfg options - # https://github.com/nix-community/nixvim/issues/1784 - finalPkgs = if opt.pkgs.isDefined then cfg.pkgs.appendOverlays cfg.overlays else - # TODO: Remove once pkgs can be constructed internally - throw '' - nixvim: `nixpkgs.pkgs` is not defined. In the future, this option will be optional. - Currently a pkgs instance must be evaluated externally and assigned to `nixpkgs.pkgs` option. - ''; + let + args = { + inherit (cfg) config overlays; + }; + + # Configure `localSystem` and `crossSystem` as required + systemArgs = + if cfg.buildPlatform == cfg.hostPlatform then + { + localSystem = cfg.hostPlatform; + } + else + { + localSystem = cfg.buildPlatform; + crossSystem = cfg.hostPlatform; + }; + in + import cfg.source (args // systemArgs); in { # We explicitly set the default override priority, so that we do not need @@ -166,9 +236,20 @@ in # don't need to evaluate `finalPkgs`. _module.args.pkgs = lib.mkOverride lib.modules.defaultOverridePriority finalPkgs.__splicedPackages; - # FIXME: This is a stub option for now - warnings = lib.optional ( - opt.source.isDefined && opt.source.highestPrio < (lib.mkOptionDefault null).priority - ) "Defining the option `nixpkgs.source` currently has no effect"; + assertions = [ + { + assertion = opt.pkgs.isDefined -> cfg.config == { }; + message = '' + Your system configures nixpkgs with an externally created instance. + `nixpkgs.config` options should be passed when creating the instance instead. + + Current value: + ${lib.generators.toPretty { multiline = true; } cfg.config} + + Defined in: + ${lib.concatMapStringsSep "\n" (file: " - ${file}") opt.config.files} + ''; + } + ]; }; } diff --git a/templates/simple/flake.nix b/templates/simple/flake.nix index 8e80918683..ef20dfedac 100644 --- a/templates/simple/flake.nix +++ b/templates/simple/flake.nix @@ -18,12 +18,12 @@ ]; perSystem = - { pkgs, system, ... }: + { system, ... }: let nixvimLib = nixvim.lib.${system}; nixvim' = nixvim.legacyPackages.${system}; nixvimModule = { - inherit pkgs; + inherit system; # or alternatively, set `pkgs` module = import ./config; # import the module directly # You can use `extraSpecialArgs` to pass additional arguments to your module files extraSpecialArgs = { diff --git a/tests/main.nix b/tests/main.nix index 65efe95330..3fe4fcb262 100644 --- a/tests/main.nix +++ b/tests/main.nix @@ -6,13 +6,14 @@ lib ? pkgs.lib, linkFarm, pkgs, - pkgsUnfree, self, system, }: let fetchTests = callTest ./fetch-tests.nix { }; - test-derivation = callPackage ../lib/tests.nix { inherit self; }; + test-derivation = callPackage ../lib/tests.nix { + inherit lib self system; + }; inherit (test-derivation) mkTestDerivationFromNixvimModule; moduleToTest = @@ -27,8 +28,10 @@ let inherit self system; inherit (self) inputs; }; + nixpkgs.config = { + allowUnfree = true; + }; }; - pkgs = pkgsUnfree; }; # List of files containing configurations diff --git a/tests/test-sources/modules/nixpkgs.nix b/tests/test-sources/modules/nixpkgs.nix index 6efce76a93..f0ce36efc0 100644 --- a/tests/test-sources/modules/nixpkgs.nix +++ b/tests/test-sources/modules/nixpkgs.nix @@ -28,6 +28,7 @@ inputs, system, pkgs, + lib, ... }: { @@ -43,6 +44,8 @@ ]; }; + nixpkgs.config = lib.mkForce { }; + nixpkgs.overlays = [ (final: prev: { hello = "world"; @@ -71,4 +74,25 @@ } ]; }; + + # Test that a nixpkgs revision can be specified using `nixpkgs.source` + source = + { pkgs, ... }: + { + test.runNvim = false; + + nixpkgs.source = builtins.fetchTarball { + url = "https://github.com/NixOS/nixpkgs/archive/1807c2b91223227ad5599d7067a61665c52d1295.tar.gz"; + sha256 = "0xnj86751780hg1zhx9rjkbjr0sx0wps4wxz7zryvrj6hgwrng1z"; + }; + + assertions = [ + { + assertion = pkgs.lib.version == "24.11pre-git"; + message = '' + Expected `pkgs.lib.version` to be "24.11pre-git", but found "${pkgs.lib.version}" + ''; + } + ]; + }; } diff --git a/wrappers/_shared.nix b/wrappers/_shared.nix index 33c002b624..9161aaf10e 100644 --- a/wrappers/_shared.nix +++ b/wrappers/_shared.nix @@ -23,21 +23,35 @@ let map mkIf mkMerge - mkOption optionalAttrs setAttrByPath ; cfg = config.programs.nixvim; + + nixpkgsModule = + { config, ... }: + { + _file = ./_shared.nix; + + config = { + nixpkgs = { + # Use global packages by default in nixvim's submodule + # FIXME: probably doesn't need to be mkForce + # What should happen if the user configures both `nixpkgs.pkgs` and `nixpkgs.useGlobalPackages` ? + pkgs = lib.mkIf config.nixpkgs.useGlobalPackages (lib.mkForce pkgs); + + # Inherit platform spec + # FIXME: buildPlatform can't use mkOptionDefault because it already defaults to hostPlatform + hostPlatform = lib.mkOptionDefault pkgs.stdenv.hostPlatform; + buildPlatform = lib.mkOverride 1499 pkgs.stdenv.buildPlatform; + }; + }; + }; nixvimConfiguration = config.lib.nixvim.modules.evalNixvim ( evalArgs // { modules = evalArgs.modules or [ ] ++ [ - # Use global packages by default in nixvim's submodule - # TODO: `useGlobalPackages` option and/or deprecate using host packages? - { - _file = ./_shared.nix; - nixpkgs.pkgs = lib.mkDefault pkgs; - } + nixpkgsModule ]; } ); diff --git a/wrappers/modules/shared.nix b/wrappers/modules/shared.nix index 3aa9f7c557..1646854c45 100644 --- a/wrappers/modules/shared.nix +++ b/wrappers/modules/shared.nix @@ -3,6 +3,19 @@ options = { enable = lib.mkEnableOption "nixvim"; + nixpkgs.useGlobalPackages = lib.mkOption { + type = lib.types.bool; + default = true; # TODO: Added 2024-12-23; switch to false one release after adding a deprecation warning + defaultText = lib.literalMD ''`true`, but will change to `false` in a future version.''; + description = '' + Whether Nixvim should use the host configuration's `pkgs` instance. + When false, `pkgs` will be constructed from `nixpkgs.source`. + + > [!TIP] + > The host configuration is usually home-manager, nixos, or nix-darwin. + ''; + }; + meta.wrapper = { name = lib.mkOption { type = lib.types.str; diff --git a/wrappers/standalone.nix b/wrappers/standalone.nix index 494074a5b1..a7eac7ce71 100644 --- a/wrappers/standalone.nix +++ b/wrappers/standalone.nix @@ -1,8 +1,13 @@ -default_pkgs: self: +{ + self, + lib, + defaultSystem, +}: { # TODO: Deprecate this arg in favour of using module options - pkgs ? default_pkgs, - lib ? pkgs.lib, + pkgs ? null, + # NOTE: `defaultSystem` is the only reason this function can't go in `.lib` + system ? defaultSystem, extraSpecialArgs ? { }, _nixvimTests ? false, module, @@ -12,23 +17,32 @@ let helpers = self.lib.nixvim.override { inherit _nixvimTests; }; inherit (helpers.modules) evalNixvim; + systemMod = + if pkgs == null then + { + _file = ./standalone.nix; + nixpkgs.hostPlatform = lib.mkDefault { inherit system; }; + } + else + { + _file = ./standalone.nix; + nixpkgs.pkgs = lib.mkDefault pkgs; + }; + mkNvim = mod: let nixvimConfig = evalNixvim { modules = [ mod - # TODO: only include this when `args?pkgs`: - { - _file = ./standalone.nix; - nixpkgs.pkgs = lib.mkDefault pkgs; - } + systemMod ]; inherit extraSpecialArgs; }; inherit (nixvimConfig.config) enableMan build; + inherit (nixvimConfig._module.args.pkgs) symlinkJoin; in - (pkgs.symlinkJoin { + (symlinkJoin { name = "nixvim"; paths = [ build.package