Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 29 additions & 12 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ You may optionally set the `meta.description` option to provide a short descript

## Guidelines and Examples:

When you provide an option to `enable` or `disable` something, you should call it `enable` regardless of its default value.
- When you provide an option to `enable` or `disable` something, you should call it `enable` regardless of its default value.
This prevents people from needing to look it up to use it, and prevents contributors from having to think too hard about which to call it.

When you provide a `wlib.types.file` option, you should name it the actual filename, especially if there are multiple, but `configFile` is also OK, especially if it is unambiguous.
- When you provide a `wlib.types.file` option, you should name it the actual filename, especially if there are multiple, but `configFile` is also OK, especially if it is unambiguous.

If you do name it `configFile` instead, you can fix the filename if necessary/desired by setting `default.path` explicitly, as shown in the example below.

Expand All @@ -60,6 +60,14 @@ However, this makes the user of your module search for it, and in some situation

So making use of the `wlib.types.file` type or giving some other method of overriding the filepath when providing a file is generally recommended for this reason.

- When you generate a file, it is generally better to do so as a string, and create it using the `constructFiles` option.

This is because, this will make placeholders such as `${placeholder "out"}` work consistently across all your options.

What this allows you to do, is manually build files later using `buildCommand` option or a stdenv phase, and then refer to that created file within your settings!

Making placeholders work in your module makes your modules generally more easily extensible, and is preferred when it is possible to generate a usable string.

Example:

```nix
Expand All @@ -70,31 +78,40 @@ Example:
pkgs,
...
}:
let
gitIniFmt = pkgs.formats.gitIni { };
in
{
imports = [ wlib.modules.default ];
options = {
settings = lib.mkOption {
inherit (gitIniFmt) type;
inherit (pkgs.formats.gitIni { }) type;
default = { };
description = ''
Git configuration settings.
See {manpage}`git-config(1)` for available options.
'';
};

configFile = lib.mkOption {
type = wlib.types.file pkgs;
default.path = gitIniFmt.generate "gitconfig" config.settings;
default = {
path = config.constructFiles.gitconfig.path; # <- we can refer to the placeholder of our constructed file!
content = "";
};
description = "Generated git configuration file.";
};
};

config.env.GIT_CONFIG_GLOBAL = config.configFile.path;
config.package = lib.mkDefault pkgs.git;
config.meta.maintainers = [ wlib.maintainers.birdee ];
config = {
env.GIT_CONFIG_GLOBAL = config.configFile.path;
package = lib.mkDefault pkgs.git;
constructFiles.gitconfig = { # <- constructs the path directly in the final wrapper derivation, such that placeholders work correctly.
relPath = "${config.binName}config";
# A string, which is to become the file contents
content =
# nixpkgs has a lot of handy generation functions!
lib.generators.toGitINI config.settings
# and gitconfig format allows you to arbitrarily append contents!
+ "\n" + config.configFile.content;
};
meta.maintainers = [ wlib.maintainers.birdee ]; # <- don't forget to make yourself the maintainer of your module!
};
}
```

Expand Down
133 changes: 50 additions & 83 deletions lib/core.nix
Original file line number Diff line number Diff line change
Expand Up @@ -526,78 +526,64 @@ in
It is added via the apply field of the option for you.
'';
};
wrapperFunction = lib.mkOption {
type = lib.types.nullOr (lib.types.functionTo lib.types.raw);
default = null;
buildCommand = lib.mkOption {
type = wlib.types.dagOf lib.types.lines;
default = { };
description = ''
Arguments:

This option takes a function receiving the following arguments:
This option is to be used to fulfill the contract formed by `config.wrapperPaths`

module arguments + `pkgs.callPackage`

```
{
config,
wlib,
... # <- anything you can get from pkgs.callPackage
}
```

The result of this function is passed DIRECTLY to the value of the `builderFunction` function.
This option is used by builderFunction to create a build script which will be ran in the resulting derivation.

The relative path to the thing to wrap is `config.wrapperPaths.input`

This function is to return a value which creates a result at `config.wrapperPaths.placeholder`
The result should be created at `config.wrapperPaths.placeholder`

The type this value is to return is dictated by `config.builderFunction`.
In most wrapper modules, this contract has been fulfilled for you by `wlib.modules.makeWrapper`
and `wlib.modules.symlinkScript`, which are imported by `wlib.modules.default`

The default implementation, as well as the implementation from `wlib.modules.symlinkScript`
accept either a string which will be prepended to `buildCommand` (preferred),
or a derivation which can be symlinked into the resulting derivation output to create the desired path.
However, you may add extra entries, and place them before or after the commands provided by those modules.

Again, the result is passed DIRECTLY as an argument to the function which is the value of `config.builderFunction`
This is a more flexible form of providing derivation build commands than the normal `drv.buildPhase` style options.
However, those are also usable, as are the other `drv` attributes such as things like `drv.__structuredAttrs`.
'';
};
builderFunction = lib.mkOption {
type = lib.types.functionTo (
lib.types.either lib.types.str (lib.types.functionTo (lib.types.attrsOf lib.types.raw))
);
description = ''
Outside of importing `wlib.modules.symlinkScript` module,
which is included in `wlib.modules.default`,
This is usually an option you will never have to redefine.

This option takes a function receiving the following arguments:

module arguments + `wrapper` + `pkgs.callPackage`
module arguments + `buildCommand` + `pkgs.callPackage`

`buildCommand` is the already sorted and concatenated result of the `config.buildCommand` DAG option,
for convenience.

This function is in charge of running the generated `buildCommand` build script,
generated from the `config.buildCommand` option.

The function provided may be in 1 of 3 forms.

- The function is to return a string which will be added to the buildCommand of the wrapper.

```
{
wlib,
config,
wrapper,
buildCommand,
... # <- anything you can get from pkgs.callPackage
}@initialArgs:
"<buildCommand>"
buildCommand # <- gets provided to buildCommand attribute of the final drv
```

It is in charge of linking `wrapper` and `config.outputs` to the final package.

`wrapper` is the unchecked result of calling `wrapperFunction`, or null if one was not provided.

- The function is to return a string which will be added to the buildCommand of the wrapper.

The builtin implementation, and also the `wlib.modules.symlinkScript` module,
accept either a string to prepend to the returned `buildCommand` string,
or a derivation to link with lndir

- Alternatively, it may return a function which returns a set like:

```nix
{ wlib, config, wrapper, ... }@initialArgs:
{ wlib, config, buildCommand, ... }@initialArgs:
drvArgs:
drvArgs // {}
drvArgs // { inherit buildCommand; }
```

If it does this, that function will be given the final computed derivation attributes,
Expand All @@ -612,7 +598,7 @@ in
- You can also return a _functor_ with a (required) `mkDerivation` field.

```nix
{ config, stdenv, wrapper, wlib, ... }@initialArgs:
{ config, stdenv, buildCommand, wlib, ... }@initialArgs:
{
inherit (stdenv) mkDerivation;
__functor = {
Expand All @@ -624,7 +610,12 @@ in
...
}@self:
defaultArgs:
defaultArgs // (if config.sourceStdenv then { } else { buildCommand = ""; }
defaultArgs // {
buildCommand =
lib.optionalString config.sourceStdenv (setupPhases defaultPhases)
+ buildCommand
+ lib.optionalString config.sourceStdenv runPhases;
};
}
```

Expand All @@ -642,44 +633,7 @@ in
Tip: A _functor_ is a set with a `{ __functor = self: args: ...; }` field.
You can call it like a function and it gets passed itself as its first argument!
'';
default =
{
wlib,
config,
wrapper,
lib,
lndir,
...
}:
let
originalOutputs = wlib.getPackageOutputsSet config.package;
in
"mkdir -p ${placeholder config.outputName} \n"
+ (
if builtins.isString wrapper then
wrapper
else if wrapper != null then
"${lndir}/bin/lndir -silent \"${toString wrapper}\" ${placeholder config.outputName}"
else
""
)
+ ''

# Handle additional outputs by symlinking from the original package's outputs
${lib.concatMapStringsSep "\n" (
output:
if originalOutputs ? ${output} && originalOutputs.${output} != null then
''
if [[ -n "''${${output}:-}" ]]; then
mkdir -p ${placeholder output}
# Only symlink from the original package's corresponding output
${lndir}/bin/lndir -silent "${originalOutputs.${output}}" ${placeholder output}
fi
''
else
""
) config.outputs}
'';
default = { buildCommand, ... }: buildCommand;
};
sourceStdenv = lib.mkOption {
type = lib.types.bool;
Expand Down Expand Up @@ -835,9 +789,22 @@ in
'';
initial = pkgs.callPackage config.builderFunction (
args
// {
wrapper =
if config.wrapperFunction == null then null else pkgs.callPackage config.wrapperFunction args;
// rec {
wrapper = lib.warn ''
the `wrapper` argument of config.builderFunction is deprecated.

Instead of `wrapper`, you will need to run `buildCommand` argument instead.

It contains the sorted and concatenated value of `config.buildCommand` DAG option

If you wish to sort the `config.buildCommand` DAG yourself instead, this is fine,
but it has been provided in sorted form via the `buildCommand` argument for convenience.
'' buildCommand;
buildCommand = lib.pipe config.buildCommand [
(wlib.dag.unwrapSort "buildCommand")
(map (v: v.data))
(builtins.concatStringsSep "\n")
];
}
);
in
Expand Down
Loading
Loading