Split out buildWithCargo into a lower level mkCargoDerivation

* The intention here is to split up different "responsibilities" into
  smaller parts which can be composed as a DAG rather than mutually
  recursive functions. Specifically:
* `mkCargoDerivation` represents a lower-level thin wrapper around
  `stdenv.mkDerivation` which will
  - set up hooks
  - require the caller to define the variables needed by the hooks (like
    vendor dir, or artifacts to inherit)
  - ensure that build/check/install phases can be configured by the
    caller without having them remember to call pre/post hooks
* This allows `buildDepsOnly` to only focus on setting some default
  values (like good default commands to build all artifacts, setting the
  derivation name, etc.) and delegating the rest to `mkCargoDerivation`
* Lastly, the responsibility of `buildWithCargo` ends up ensuring that
  `cargoArtifacts` and `cargoVendorDir` are defined if the caller does
  not pass them in
This commit is contained in:
Ivan Petkov 2022-01-02 14:48:32 -08:00
parent 10a60cc085
commit 4c1711399d
No known key found for this signature in database
GPG Key ID: BB6F9EFC065832B6
4 changed files with 135 additions and 78 deletions

View File

@ -1,36 +1,46 @@
{ buildWithCargo
, crateNameFromCargoToml
{ crateNameFromCargoToml
, mkCargoDerivation
, mkDummySrc
, vendorCargoDeps
}:
args:
{ cargoExtraArgs ? ""
, cargoCheckCommand ? "cargo check --workspace --release"
, cargoBuildCommand ? "cargo build --workspace --release"
, cargoTestCommand ? "cargo test --workspace --release"
, ...
}@args:
let
crateName = crateNameFromCargoToml args;
defaults = {
inherit (crateName) version;
pname = "${crateName.pname}-deps";
buildPhase = ''
runHook preBuild
cargo check --workspace --release
cargo build --workspace --release
runHook postBuild
'';
# Don't install anything by default, but let the caller set their own if they wish
installPhase = ''
runHook preInstall
mkdir -p $out
runHook postInstall
'';
};
forced = {
# Prevent infinite recursion, we are the root of all artifacts
cargoArtifacts = null;
# No point in building this if not for the cargo artifacts
doCopyTargetToOutput = true;
src = mkDummySrc args;
};
in
buildWithCargo (defaults // args // forced)
mkCargoDerivation (args // {
src = mkDummySrc args;
pname = args.pname or "${crateName.pname}-deps";
version = args.version or crateName.version;
cargoArtifacts = null;
cargoVendorDir = args.cargoVendorDir or vendorCargoDeps {
cargoLock = args.src + /Cargo.lock;
};
# First we run `cargo check` to cache cargo's internal artifacts, fingerprints, etc. for all deps.
# Then we run `cargo build` to actually compile the deps and cache the results
buildPhaseCargoCommand = args.buildPhaseCargoCommand or ''
${cargoCheckCommand} ${cargoExtraArgs}
${cargoBuildCommand} ${cargoExtraArgs}
'';
checkPhaseCargoCommand = args.checkPhaseCargoCommand or ''
${cargoTestCommand} ${cargoExtraArgs}
'';
# No point in building this if not for the cargo artifacts
doCopyTargetToOutput = true;
# By default, don't install anything (else, besides the cargo target directory),
# but let the caller set their own if they wish
installPhaseCargoCommand = args.installPhaseCargoCommand or ''
mkdir -p $out
'';
})

View File

@ -1,14 +1,7 @@
{ buildDepsOnly
, cargo
, configureCargoCommonVarsHook
, configureCargoVendoredDepsHook
, copyCargoTargetToOutputHook
, crateNameFromCargoToml
, inheritCargoArtifactsHook
, installFromCargoArtifactsHook
, lib
, remapSourcePathPrefixHook
, stdenv
, mkCargoDerivation
, vendorCargoDeps
}:
let
@ -60,47 +53,36 @@ in
# access. Directory structure should basically follow the output of `cargo vendor`.
# This can be inferred automatically if the `src` root has a Cargo.lock file.
, cargoVendorDir ? vendorCargoDepsFromArgs args
# Controls whether cargo's `target` directory should be copied as an output
, doCopyTargetToOutput ? true
# Controls instructing rustc to remap the path prefix of any sources it
# captures (for example, this can include file names in panic info). This is
# useful to omit any references to `/nix/store/...` from the final binary,
# as including them will make Nix pull in all sources when installing any binaries.
, doRemapSourcePathPrefix ? true
, nativeBuildInputs ? [ ]
, cargoBuildCommand ? "cargo build --workspace --release"
, cargoTestCommand ? "cargo test --workspace --release"
, cargoExtraArgs ? ""
, ...
}@args:
let
defaultValues = (crateNameFromCargoToml args) // {
inherit
cargoArtifacts
cargoVendorDir
doCopyTargetToOutput
doRemapSourcePathPrefix;
buildPhase = ''
runHook preBuild
cargo build --workspace --release
runHook postBuild
'';
checkPhase = ''
runHook preCheck
cargo test --workspace --release
runHook postCheck
'';
};
additions = {
nativeBuildInputs = nativeBuildInputs ++ [
cargo
configureCargoCommonVarsHook
configureCargoVendoredDepsHook
copyCargoTargetToOutputHook
inheritCargoArtifactsHook
installFromCargoArtifactsHook
remapSourcePathPrefixHook
];
};
crateName = crateNameFromCargoToml args;
in
stdenv.mkDerivation (defaultValues // args // additions)
mkCargoDerivation (args // {
pname = args.pname or crateName.pname;
version = args.version or crateName.version;
inherit cargoArtifacts cargoVendorDir;
# Don't copy target dir by default since we are going to be installing bins/libs
doCopyTargetToOutput = args.doCopyTargetToOutput ? false;
nativeBuildInputs = (args.nativeBuildInputs or [ ]) ++ [
installFromCargoArtifactsHook
];
buildPhaseCargoCommand = args.buildPhaseCargoCommand or ''
${cargoBuildCommand} ${cargoExtraArgs}
'';
checkPhaseCargoCommand = args.checkPhaseCargoCommand or ''
${cargoTestCommand} ${cargoExtraArgs}
'';
installPhaseCargoCommand = args.installPhaseCargoCommand or ''
installFromCargoArtifacts
'';
})

View File

@ -17,6 +17,7 @@ lib.makeScope newScope (self:
cleanCargoToml = callPackage ./cleanCargoToml.nix { };
crateNameFromCargoToml = callPackage ./crateNameFromCargoToml.nix { };
downloadCargoPackage = callPackage ./downloadCargoPackage.nix { };
mkCargoDerivation = callPackage ./mkCargoDerivation.nix { };
mkDummySrc = callPackage ./mkDummySrc.nix { };
urlForCargoPackage = callPackage ./urlForCargoPackage.nix { };
vendorCargoDeps = callPackage ./vendorCargoDeps.nix { };

64
lib/mkCargoDerivation.nix Normal file
View File

@ -0,0 +1,64 @@
{ cargo
, configureCargoCommonVarsHook
, configureCargoVendoredDepsHook
, copyCargoTargetToOutputHook
, inheritCargoArtifactsHook
, remapSourcePathPrefixHook
, stdenv
}:
args@{
# A directory to an existing cargo `target` directory, which will be reused
# at the start of the derivation. Useful for caching incremental cargo builds.
cargoArtifacts
# A directory of vendored cargo sources which can be consumed without network
# access. Directory structure should basically follow the output of `cargo vendor`.
, cargoVendorDir
# A command (likely a cargo invocation) to run during the derivation's build
# phase. Pre and post build hooks will automatically be run.
, buildPhaseCargoCommand
# A command (likely a cargo invocation) to run during the derivation's check
# phase. Pre and post check hooks will automatically be run.
, checkPhaseCargoCommand
# A command (likely a cargo invocation) to run during the derivation's install
# phase. Pre and post install hooks will automatically be run.
, installPhaseCargoCommand
, ...
}:
stdenv.mkDerivation (args // {
# Controls whether cargo's `target` directory should be copied as an output
doCopyTargetToOutput = args.doCopyTargetToOutput or true;
# Controls instructing rustc to remap the path prefix of any sources it
# captures (for example, this can include file names in panic info). This is
# useful to omit any references to `/nix/store/...` from the final binary,
# as including them will make Nix pull in all sources when installing any binaries.
doRemapSourcePathPrefix = args.doRemapSourcePathPrefix or true;
nativeBuildInputs = (args.nativeBuildInputs or [ ]) ++ [
cargo
configureCargoCommonVarsHook
configureCargoVendoredDepsHook
copyCargoTargetToOutputHook
inheritCargoArtifactsHook
remapSourcePathPrefixHook
];
buildPhase = args.buildPhase or ''
runHook preBuild
${buildPhaseCargoCommand}
runHook postBuild
'';
checkPhase = args.checkPhase or ''
runHook preCheck
${checkPhaseCargoCommand}
runHook postCheck
'';
installPhase = args.installPhase or ''
runHook preInstall
${installPhaseCargoCommand}
runHook postInstall
'';
})