TL;DR
I propose to move most hardcoded values from derivation attributes to default-valued function arguments.
Example (simplified ipxe
expression)
Go from this
{ stdenv, lib, fetchFromGitHub, unstableGitUpdater
, gnu-efi, mtools, openssl, perl, xorriso, xz
, syslinux ? null
, embedScript ? null
, additionalTargets ? {}
}:
stdenv.mkDerivation rec {
pname = "ipxe";
version = "unstable-2022-04-06";
src = fetchFromGitHub {
owner = "ipxe";
repo = "ipxe";
rev = "70995397e5bdfd3431e12971aa40630c7014785f";
sha256 = "SrTNEYk13JXAcJuogm9fZ7CrzJIDRc0aziGdjRNv96I=";
};
enabledOptions = [
"PING_CMD"
"IMAGE_TRUST_CMD"
"DOWNLOAD_PROTO_HTTP"
"DOWNLOAD_PROTO_HTTPS"
];
configurePhase = ''
runHook preConfigure
for opt in ${lib.escapeShellArgs enabledOptions}; do echo "#define $opt" >> src/config/general.h; done
substituteInPlace src/Makefile.housekeeping --replace '/bin/echo' echo
'' + lib.optionalString stdenv.hostPlatform.isx86 ''
substituteInPlace src/util/genfsimg --replace /usr/lib/syslinux ${syslinux}/share/syslinux
'' + ''
runHook postConfigure
'';
…
to this
{ stdenv, lib, fetchFromGitHub, unstableGitUpdater
, gnu-efi, mtools, openssl, perl, xorriso, xz
, syslinux ? null
, embedScript ? null
, additionalTargets ? {}
, rev ? "70995397e5bdfd3431e12971aa40630c7014785f"
, sha256 ? "SrTNEYk13JXAcJuogm9fZ7CrzJIDRc0aziGdjRNv96I="
, enabledOptions ? [
"PING_CMD"
"IMAGE_TRUST_CMD"
"DOWNLOAD_PROTO_HTTP"
"DOWNLOAD_PROTO_HTTPS"
]
}:
stdenv.mkDerivation rec {
pname = "ipxe";
version = "unstable-2022-04-06";
src = fetchFromGitHub {
owner = "ipxe";
repo = "ipxe";
inherit rev sha256;
};
configurePhase = ''
runHook preConfigure
for opt in ${lib.escapeShellArgs enabledOptions}; do echo "#define $opt" >> src/config/general.h; done
substituteInPlace src/Makefile.housekeeping --replace '/bin/echo' echo
'' + lib.optionalString stdenv.hostPlatform.isx86 ''
substituteInPlace src/util/genfsimg --replace /usr/lib/syslinux ${syslinux}/share/syslinux
'' + ''
runHook postConfigure
'';
…
Reasoning
I have always been annoyed by the fact upgrading a package using an override required specifying the whole src
attribute again instead of just passing the new rev
and sha256
, however once I learnt how to do it properly, I simply forgot about it. I have however spent multiple hopeless hours recently trying to figure out why using overrideAttrs
in the ipxe
derivation to override enabledOptions
didn’t work. Sure, an experienced Nixer will quickly realize configurePhase
is computed before overrideAttrs
takes place and changing the enabledOptions
attribute does not trigger a seemingly unrelated function recomputation. That explains this is not a proper way to do it, but does not help with finding the proper one. Inlining both enabledOptions
and configurePhase
into the overrideAttrs
is not a good option either as the duplication rapidly increases maintenance burden and somewhat kills the idea of using Nix to comply with the DRY principle.
I therefore propose to move as many values as possible into default-valued function arguments as demonstrated in the TL;DR section. It would fix the mentioned problems, but still keep the original behavior possible – overrideAttrs
behaves the same, but override
begins to be useful.
I was thinking about publishing this as an RFC, but I doubt I am capable of pushing it to the end. There also must be a reason we are currently doing it the way we are. What it is? Why are we putting configuration into derivation attributes? Is there any catch in my approach?