-
Notifications
You must be signed in to change notification settings - Fork 6
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
change mechanism for deps
; add depsRequired
; add depsFrom
.
#10
Conversation
With this commit, the user is forced to explicitly specify all required dependency names via `depsRequired`. `options.deps` contains generated options for all names of `depsRequired`. These options are populated with defaults from `depsFrom`, The examples under `./examples` show how this is used. The `coercedTo` type of `deps` had some weaknesses: 1. It was not possible to specify required dependencies explicitly, because it was not possible to create nested options like`options.deps.bash = mkOption ...`. 2. Because of (1.) I had to add a manual check in makeModule.nix to ensure that all required `deps` are populated by the user. Having actual options for all required dependencies allows to reduce complexity and hand the `checking` off to the module system. Also, when rendering the options for a package to a doc, all dependencies now appear as an option, which I think is great. It allows the user to see all inputs of a package without having to read the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like killing the deps
pattern. I think it does a pretty good job at steering towards a separation of concerns, steering package authors to a situation where all deps are overridable, potentially even improving on callPackage
in this regard.
Of course it can be defeated, but package authors who never see pkgs
outside of deps = { pkgs, ... }:
will not think of referencing it directly. I'm also surprised to see that pkgs
is even in the package module arguments. Was that needed for something? (before this PR)
systemd | ||
; | ||
# This must be complete, otherwise options would be missing from `deps`. | ||
depsRequired = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This rename seems like a step back.
callDeps | ||
(t.lazyAttrsOf t.raw); | ||
# Unspecified `deps` are taken from `depsFrom`. It's a source for defaults. | ||
depsFrom = l.mkOption { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like it should be this then
depsFrom = l.mkOption { | |
defaultDeps = l.mkOption { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or depsDefaults
; | ||
# Define dependencies from the "outer world" only via `depsFrom`. | ||
# This allows for easy overriding via `config.deps` later. | ||
depsFrom = {inherit (pkgs) fetchurl python}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does this pkgs
come from?
The point of having a function here is to avoid bringing pkgs
into the larger scope and seducing people into using it directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This example is bad. depsFrom
should never be specified from within the package module itself. It should be set from outside.
deps = | ||
l.mapAttrs | ||
mkDepOpt | ||
(l.filterAttrs (_: enabled: enabled) config.depsRequired); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makeModule
implementation concerns are leaking into the general interface. That's not good.
Shouldn't this be in a makeModule
module?
Should deps
be a freeform module?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be in a makeModule module?
I thought explicitly specifying a list of required deps is something that we want everywhere. depsRequired
seemed like the simplest way to do that. It's not supposed to be specific to makeModule.
Should deps be a freeform module?
Will this bring back the capability of specifying deps
as a function?
I probably need to combine freeform
and apply
, I guess?
Did you try with |
Those could be understood to be flake inputs. Arguably those shouldn't be in scope though, so perhaps shadowing is a good thing. Still potentially confusing though. Especially |
Which
Not yet, but I will experiment with it now. |
I think I inferred this wrong when reading the code. Maybe got confused by what you said is a bad example. |
Which type would I pick that allows nested options? If I pick |
|
To explain my though process a bit better:One underlying question seems to be: Who gets to pick dependencies? The package maintainer, or the caller? Out in the decentralized wild, it might be more valuable to let the Therefore I thought it's a good idea to enforce an option for each dependency. Thinking about an end game where nix competes with language specific package managers, the decentralized fashion seems to be the more generic approach vs. nixpkgs is more specific. Maybe the logic of how dependencies are picked within nixpkgs, should be factored out into a separate module?
Genius! With that, I can get rid of Maybe one can get rid of A package module would then look like this: {config, lib, ...}: {
name = "test";
# all required deps must always be declared
depsRequired = {
foo = true;
bar = true;
bash = true;
};
# `deps` is only required for picking non-default versions
# This logic is specific to the repo the package resides in, but can easily
# be removed by a `mkForce`.
deps = {pkgs, ...}: {
# pick a special version for bar
bar = pkgs.bar_2;
};
} |
I don't think the change will be huge. A lot of people already live in a decentralized wild (without Nix) and they rely on distributions to provide such things as docker base images. Even a gentoo user doesn't stitch together all dependencies by hand. (I think? It's been over a decade.)
or
Not sure if going in circles, but what about I should probably merge this, shouldn't I: |
Even if you have a community maintained package set, where the community picks one Customizing dependency trees can be done by hand or by a resolver, but in any case, to support this well, the It would be nice if mixed approaches would be supported, so that users can satisfy their requirements, by taking some packages from nixpkgs and others from elsewhere (auto-generated, or different repos, etc...). Automatically. Expanding a bit on the {
depsRequired = {
foo = "^2.0.0";
bar = "*";
baz = "~2.0";
};
} If packages had that, mixing dynamic language2nix approaches with nixpkgs would be a charm. Knowing more about a package is beneficial for automation. And automation is key to scale.
depsRequired is creating options for Anyways, I might be biased by dealing with language2nix too much, and I do not want to pollute the core of drv-parts with anything that we don't agree is absolutely necessary. |
I now opened #11 which is a more minimal change, just allowing nested options for deps.
|
so this implementation / PR will be closed? |
@roberth It would be amazing if you could review this. I really want to get this right.
With this commit, the user is forced to explicitly specify all required dependency names via
depsRequired
.options.deps
contains generated options for all names ofdepsRequired
. These options are populated with defaults fromdepsFrom
, The examples under./examples
show how this is used.The
coercedTo
type ofdeps
had some weaknesses:options.deps.bash = mkOption ...
.deps
are populated by the user.Having actual options for all required dependencies allows to reduce complexity and hand the
checking
off to the module system.Also, when rendering the options for a package to a doc, all dependencies now appear as an option, which I think is great. It allows the user to see all inputs of a package without having to read the code.
This whole
deps
mechanism is generic enough to suit as a generalinputs
. It probably should be renamed toinputs
in the future, but I don't want to clutter this PR too much.EDIT:
options.stdenv
can now probably be moved tooptions.deps.stdenv
. It is basically just a dependency of the derivation and I see no need for special treatment.