Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typed nixpkgs.config married to NixOS #57123

Closed
wants to merge 12 commits into from

Conversation

oxij
Copy link
Member

@oxij oxij commented Mar 9, 2019

What? Why?

This is the next completely ready piece of #56227.

The four commits from master proceeding this branch, namely 570aed4 4a647dd 5aa813d 83ae1ff, are the previous completely ready piece cherry-picked onto master from #56227 by @danbst.

The previous piece introduced option checking to pkgs, this piece marries it to nixpkgs.config option of NixOS.

To make this marriage possible I had to type packageOverrides in pkgs (to remove ad-hoc processing of nixpkgs.config in the corresponding NixOS module). To type packageOverrides I had to revive types.functionTo.

git log

  • check-meta.nix: move options to config.nix

    This also makes "Unfree" and "Insecure" options homogenous.

  • lib: resurrect types.functionTo

    This reverts commit 4ff1ab5.

    This type was removed because it usually implies a bad design. However, since that
    commit NixOS modules that can't live without it implemented several ugly ad-hoc
    replacements for it while other modules that could make a good use of it
    simply suffer. See the following commits.

  • pkgs/top-level/config.nix: add various packageOverrides

    Hm, Haskell's one is actually an overlay.

  • lib/modules.nix: rename _file -> file in unifyModuleSyntax

    ... and change all of its uses.

    Strangely enough, before this _file was in the public interface of the module
    system while file was used internally by lib.evalModules code
    after lib.unifyModuleSyntax was applied.

    _file attribute was introduced in 800f9c2 but
    I see no reason behind having "_" in the name there, all other attrs
    processed by lib.unifyModuleSyntax keep their names between its input and output.

    On the other hand, renaming _file into file makes lib.unifyModuleSyntax
    accept its own output which is useful in cases when you want to modify
    modules before shoving them into lib.evalModules but you don't want to duplicate
    lib.unifyModuleSyntax.

  • lib/modules.nix: make unifyModuleSyntax idempotent

    The previous patch made it accept its own output, this also makes it
    idempotent.

  • lib/modules.nix: simplify pushDownProperties

  • lib/types.nix: add opaque type

  • pkgs/top-level/impure.nix: move defaults into the preceding let block

  • pkgs/top-level: introduce configs argument and marry nixpkgs.config of NixOS to it

    This types nixpkgs.config of NixOS with types.opaque type and applies the
    resulting evaluated value to the newly introduced configs argument of pkgs.

    The new configs argument is like the old config argument (still available for
    convenience) but a list.

    This design was chosen because we can't just make a submodule out of
    pkgs/top-level/config.nix in NixOS because

    • firstly, we would have to duplicate all the magic pkgs/top-level/default.nix
      does to config in NixOS,

    • secondly, in principle, we might not have all the arguments needed by
      pkgs/top-level/config.nix in NixOS because some of them can be
      computed internally by pkgs (this is not a problem now
      but it will be a problem in use-flags patchset that comes after), thus all
      of those computations will need to be duplicated too,

    • thirdly, doing it this way keeps NixOS and pkgs separated, so that, in
      principle, one could replace NixOS with an alternative thing without duplicating
      all of the above once again and/or one could use separate module and type
      systems between NixOS and pkgs (types.opaque is trivial to
      implement in any type system and pkgs can use "types-simple" of Rewrite meta-check.nix type checking logic. #37252 today,
      if we were to wish it so).

    Note that since this design removes the need to do any preprocessing of
    nixpkgs.config in NixOS all the ad-hoc merging code was removed. Yay!

nix-instantiate environment

  • Host OS: Linux 4.9, SLNOS 19.09
  • Nix: nix-env (Nix) 2.2
  • Multi-user: yes
  • Sandbox: yes
  • NIXPKGS_CONFIG:
{
  checkMeta = true;
  doCheckByDefault = true;
}

nix-env -qaP diffs

  • On x86_64-linux:
    • Updated (1):
      • tests.nixos-functions.nixos-test
  • On aarch64-linux: ditto
  • On x86_64-darwin: noop

/cc @dtzWill @Ericson2314 @matthewbauer @7c6f434c @infinisil @edolstra from the previous PR

oxij added 2 commits March 8, 2019 12:58
This also makes "Unfree" and "Insecure" options homogenous.
This reverts commit 4ff1ab5.

This type was removed because it usually implies a bad design. However, since that
commit NixOS modules that can't live without it implemented several ugly ad-hoc
replacements for it while other modules that could make a good use of it
simply suffer. See the following commits.
@oxij
Copy link
Member Author

oxij commented Mar 9, 2019

What to pay attention to while reviewing:

  • types in config.nix, especially the ones for *packageOverrides, I think they are correct, but I might have missed something,
  • do commit messages make sense?

@oxij
Copy link
Member Author

oxij commented Mar 9, 2019

Btw, changes in metrics of nix-env -f . -qaP --drv-path --out-path between the parent of 570aed4 and this HEAD:

Before:

time 47.205 s
maxresident 4264000 KiB
allocations 6007093832 B
values 80800261

After:

time 47.047 s
maxresident 4241936 KiB
allocations 5956525992 B
values 79612951

I wouldn't believe the time metric, it's too random, but the rest are most certainly valid.

So, despite adding type checking to nixpkgs.config I improved all the things :]

@oxij
Copy link
Member Author

oxij commented Mar 9, 2019

Hm, also 1d1ebf5 changes the semantics of haskellPackageOverrides a bit as config.haskellPackageOverrides is never undefined now. @peti, should I change that? If so then how? The interaction between config.haskellPackageOverrides and haskell.packageOverrides seems nontrivial.

@oxij oxij force-pushed the tree/checked-nixpkgs-config branch 2 times, most recently from 682ccf1 to 9ab9e2a Compare March 9, 2019 15:21
@danbst
Copy link
Contributor

danbst commented Mar 9, 2019

haskellPackageOverrides

As for haskellPackageOverrides, it is better to comment it. Maybe it is deprecated by overlays? It will also remove the need for @peti to review this PR.

functionTo

Do we need generic "function to"? I'm biased here, as I like my "dependently typed selector" variant (#44601 (comment)). listSelector, attrsSelector and overlay are the only required function types. ... I'm not sure overlays are worth to encode in types.

_file -> file

I've used _file for my custom modules already. There has to be backwards compat. But I also wonder why was it named like that. Maybe, to discourage usage? In that case I have a valid usecase:

let
  module1 = {
     key = "module1"; _file = "this_file.nix";
     config = { error.option = true; };
  };
  module2 = {
     key = "module2"; _file = "this_file.nix";
  };
in {
  imports = [ module1 module2 ];
}
# without _file
nix-repl> evalModules { modules = [ (import ../simple-2-modules.nix) ]; }
{ config = «error: The option `error' defined in `<unknown-file>' does not exist.»; options = { ... }; }

# with _file
nix-repl> evalModules { modules = [ (import ../simple-2-modules.nix) ]; }
{ config = «error: The option `error' defined in `this_file.nix' does not exist.»; options = { ... }; }

At least I know file name.

make unifyModuleSyntax idempotent

The previous patch made it accept its own output, this also makes it
idempotent.

I don't understand the comment. Which previous patch? As for changes, looks like you've removed mkMerge in cases when there is no meta, which is nice 👍

pushDownProperties

👍 I've also discovered that inside module system sometimes code is duplicated to not introduce extra variables, which take memory. Can you verify that it isn't the case here?

opaque type

This looks like introspection thing. It would generate questions - "what is this?". I guess a "module system manual" is needed now. I'm also not a фанат of introducing concepts without usages - maybe combine into one commit with next one?

move defaults

I'm okayish here, just a note that it makes you an author in git blame. @7c6f434c do we care about git blame when moving large pieces of code?

the last commit

looks very strange, have yet to review it.

@oxij
Copy link
Member Author

oxij commented Mar 9, 2019 via email

@danbst
Copy link
Contributor

danbst commented Mar 9, 2019

Module configuration.nix:anon-1' has an unsupported attribute _file'

that is because it is a top-level module parameter. You can't place it into random config, you have to massage module first.

You don't have a filename in your example because you evalModules on a nix expression, try to supply a path instead

You're right. But in my case I did exactly import. Another example:

# simple-module-generator.nix
{
    module1 = {
        error = true;
    };
    module2 = {
    };
}
# simple-2-modules.nix
with import ./simple-module-generator.nix;
{
  imports = [ module1 module2 ];
}
nix-repl> evalModules { modules = [ ../simple-2-modules.nix ]; }
{ config = «error: The option `error' defined in `/home/danbst/dev/simple-2-modules.nix' does not exist.»; options = { ... }; }

Error is not in simple-2-modules.nix file.

@oxij
Copy link
Member Author

oxij commented Mar 10, 2019 via email

@peti
Copy link
Member

peti commented Mar 10, 2019

@oxij,

1d1ebf5 changes the semantics of haskellPackageOverrides a bit as config.haskellPackageOverrides is never undefined now. @peti, should I change that? If so then how? The interaction between config.haskellPackageOverrides and haskell.packageOverrides seems nontrivial.

I removed haskellPackageOverrides entirely in ee1f34e. Does that help?

@oxij
Copy link
Member Author

oxij commented Mar 10, 2019 via email

@danbst
Copy link
Contributor

danbst commented Mar 10, 2019

Well, sure, but it works the same on master and I don't see how this can be fixed using the current NixOS modules infra, if you don't specify file nor import the module via a path so that evalModules would set file for you it will be a "best guess", so to say.

{
    module1 = {
        _file = "module at ${__curPos.file}:${toString __curPos.line}";
        config = {
            error = true;
        };
    };

    module2 = {
        _file = "module at ${__curPos.file}:${toString __curPos.line}";
    };
}
nix-repl> evalModules { modules = [ (import ../simple-2-modules.nix) ]; }
{ config = «error: The option `error' defined in `module at /home/danbst/dev/simple-module-generator.nix:3' does not exist.»; options = { ... }; }

But I see now that key and _file are not public module interface, nowhere in manual is said that you can use anonymous modules and should set key/_file explicitly. I'd vote for at least release notes then.

@peti
Copy link
Member

peti commented Mar 10, 2019

But in the meantime I was thinking that maybe it would make sense to make haskellPackageOverrides into the default for haskell.packageOverrides. E.g. suppose you want to override several ghc versions at the same time.

I am sorry, but I don't understand what you mean.

@oxij
Copy link
Member Author

oxij commented Mar 10, 2019 via email

@oxij
Copy link
Member Author

oxij commented Mar 13, 2019 via email

@peti
Copy link
Member

peti commented Mar 13, 2019

haskellPackageOverrides
See the new patch. I made your ee1f34e not as violent with the HEAD of this branch. Does that look ok?

No, please don't re-introduce that option. I'm glad it is gone.

I also notice there's an rPackageOverrides now which I don't want either (or did I misread the patch?).

…ate it

This makes a removal of that attribute in ee1f34e
less violent. Now it can be properly deprecated using the new infra.
@oxij oxij force-pushed the tree/checked-nixpkgs-config branch from d09994b to 52e5e07 Compare March 13, 2019 15:41
@oxij
Copy link
Member Author

oxij commented Mar 13, 2019 via email

@danbst
Copy link
Contributor

danbst commented Mar 13, 2019

@oxij Your thoughts about overlays in config are very interesting!

@danbst
Copy link
Contributor

danbst commented Mar 13, 2019

So, I'm reading the finalizing commit... I have two questions:

  1. Maybe it would be cleaner to support imports directive in config? Think of:
{
  imports = [ ./my-custom-config-modules.nix ];

  allowBroken = true;  
}

Then a) it would be possible to add use-flags thing without amending NixOS code, only extraScope needed; b) it would remove the need to pass list of configs, just pass config = { imports = cfg.config; }

As for implementation, I think it would require some massaging of config, but should be doable.

  1. It also not very cool that pkgs/top-level/default.nix includes hacky "check only known options". Yes, I agree this is sole usage, but it may be harder to sell to others. The unknowns hack is because module system doesn't support partial checks, right?

@oxij
Copy link
Member Author

oxij commented Mar 14, 2019 via email

@oxij
Copy link
Member Author

oxij commented Mar 14, 2019 via email

Copy link
Member

@infinisil infinisil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool overall!


# An opaque value. The result of evaluation of an option of this
# type is a list of { file, value } pairs describing all the
# values assigned to this option (with corresponding files where
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this the complete opposite of opaque? Having access to all assignments is as transparent as can be, nothing gets hidden, maybe it should be called passthru instead?

@@ -113,7 +78,7 @@ in
''
{ allowBroken = true; allowUnfree = true; }
'';
type = configType;
type = types.opaque;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And with such a type, all the values you can set won't be visible in the manual. I really think the whole config type checking part should be cleanly separated as a module not tied to pkgs, then it can be used as type = types.submodule (import ./path/to/config-module.nix) here, and similarly in pkgs.

};

allowBroken = mkMeta {
default = builtins.getEnv "NIXPKGS_ALLOW_BROKEN" == "1";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if all the impurities could be put into a separate module which then gets imported with imports = [ ./impure.nix ]. With nixpkgs becoming a Flake eventually, these impurities will have to be removed, so this would be a good preparation. And it's nice to know all impurities upfront too.

# should use something else. This type is a last resort and should
# only be used when there is absolutely nothing else you can do.
# Most uses of this type imply bad design.
functionTo = elemType: mkOptionType {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to get #44601 moving and merged so it can be used here

// args.localSystem);

crossSystem = if crossSystem0 == null then localSystem
else lib.systems.elaborate crossSystem0;

# Massage e into a NixOS module.
# We need all this stuff only because we want to support unknown options,
# without them this can be simplified a lot.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should also indicate how unknown options are not something to stay, which can justify this hacky implementation.

@@ -291,6 +291,15 @@ rec {
functor = (defaultFunctor name) // { wrapped = elemType; };
};

# Same as attrsOf, but lazy towards attribute values (at the cost of not supporting mkIf and etc)
lazyAttrsOf = elemType: attrsOf elemType // {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if it's worth introducing this confusing and slightly module breaking type (because it makes mkIf, etc. not work anymore), just to add support for using the slightly deprecated packageOverrides as overlays. If people want overlays they can use overlays directly, if the package set they're using doesn't support this then that should be fixed there imo.

options = {
mkOverrides = args: mkMassRebuild ({
type = types.functionTo (types.lazyAttrsOf (types.uniq types.unspecified));
default = super: {};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I don't think we need all these new types by just not supporting setting packageOverrides multiple times, which is totally okay because this wasn't supported previously either. packageOverrides-like ways are kind of deprecated anyways too, we don't need to extend their support by giving new features. If people want to have overlay-like behavior they should use overlays, which package sets can add support for too (e.g. I'm trying to get python to support overlays right now in #67422). In addition, this type introduces yet another slightly different way of overriding packages, different from any other ways. I think we have enough ways of overriding things already, this will only make it more confusing.

So in short, I think this can be types.uniq types.unspecified without worry, then the controversial functionTo and lazyAttrsOf can be dropped from this PR.

@oxij
Copy link
Member Author

oxij commented Sep 6, 2019 via email

@infinisil
Copy link
Member

A problem with this is that this doesn't give you the full power of overlays. I think with how it's defined right now, it's impossible to do multiple changes to the same attribute in different packageOverrides, which standard overlays support. However it might be possible to weave the different layers of the overlays through the merge function, so this can maybe even gotten to work.

But a big problem with this I'm just seeing is that this will be very unnatural and maybe problematic for other package sets. E.g. suppose you want to override some python package in an overlay-style fashion and you decide to use this new way of defining pythonPackageOverrides in config.nix. What is self going to be then? Is it python.pkgs/pythonPackages or perhaps python3Packages because a different overlay changed the default or so? And maybe some program even wants a program-specific overlay, which doesn't get propagated to the pkgs the module receives, then the package set from pkgs would be very wrong.

I guess the thing I'm getting at here is that pkgs is a like a global variable, it's shared accross everything and it would be the only way to access the result of the pkgs fixed-point. Overlays in comparison have a locally scoped self, which allows it to be much more flexible. And the self: super: structure lends itself well to uses outside of the module system too, see lib.makeExtensible and co.

@infinisil
Copy link
Member

In fact I think the python example doesn't actually have a correct answer, because the overlay would get used for both python2 and python3

@oxij
Copy link
Member Author

oxij commented Sep 6, 2019 via email

@infinisil
Copy link
Member

The way you showed there, using isPy3k to decide where to get self from, is really not very good, because it's easy to make mistakes and get a bad override. Having to point out that everybody needs to remember to add some isPy3k boilerplate to their overrides otherwise they won't work seems like a step down. And what about package sets with 10 different versions? This doesn't scale well at all.

I'd really like for this "making packageOverrides work as overlays" functionality to be moved into a separate PR, it's the only change here that's rather controversial, and it can be separated cleanly from here.

Also pinging @nbp who probably has an opinion on this.

@nbp
Copy link
Member

nbp commented Sep 9, 2019

A 5 seconds reply, before a larger one coming over the weekend.

_file -> file

I've used _file for my custom modules already. There has to be backwards compat. But I also wonder why was it named like that. Maybe, to discourage usage?

I named it _file, because we have to carry the source information. The problem of naming it file is that file is very likely to be a valid attribute set or option name.

What you do not see is that this attribute is propagated at all the level of the evaluation, and will cause issues as soon as a single option is named file. Either you will accidentally define the option, or you will take the value defined as argument as a file location.

_file is meant, as the underscore suggests, to be an artifact of the module system, not a valid option name.

@infinisil
Copy link
Member

@nbp is the longer review still coming?

@infinisil
Copy link
Member

My #75031 includes making unifyModuleSyntax idempotent the other way around, with file -> _file (where necessary)

@stale
Copy link

stale bot commented Jun 2, 2020

Thank you for your contributions.

This has been automatically marked as stale because it has had no activity for 180 days.

If this is still important to you, we ask that you leave a comment below. Your comment can be as simple as "still important to me". This lets people see that at least one person still cares about this. Someone will have to do this at most twice a year if there is no other activity.

Here are suggestions that might help resolve this more quickly:

  1. Search for maintainers and people that previously touched the related code and @ mention them in a comment.
  2. Ask on the NixOS Discourse.
  3. Ask on the #nixos channel on irc.freenode.net.

@stale stale bot added the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Jun 2, 2020
@infinisil infinisil closed this Aug 10, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants