Crane
A Nix library for building cargo projects.
- Source fetching: automatically done using a Cargo.lock file
- Incremental: build your workspace dependencies just once, then quickly lint, build, and test changes to your project without slowing down
- Composable: split builds and tests into granular steps. Gate CI without burdening downstream consumers building from source.
Features
- Automatic vendoring of dependencies in a way that works with Nix
- Alternative cargo registries are supported (with a minor configuration change)
- Git dependencies are automatically supported without additional
configuration.
- Cargo retains the flexibility to only use these dependencies when they are actually needed, without forcing an override for the entire workspace.
- Reusing dependency artifacts after only building them once
- clippy checks
- rustfmt checks
- cargo-doc generation
- And support for a number of popular tools such as:
Getting Started
The easiest way to get started is to initialize a flake from a template:
# Start with a comprehensive suite of tests
nix flake init -t github:ipetkov/crane#quick-start
Otherwise check out the examples and templates for more detailed examples. An API Reference is also available.
Compatibility Policy
Breaking changes can land on the master
branch at any time, so it is
recommended you use a versioning strategy when consuming this library (for
example, using something like flakes or niv).
Tagged releases will be cut periodically and changes will be documented in the CHANGELOG. Release versions will follow Semantic Versioning.
The test suite is run against the latest stable nixpkgs release, as well as
nixpkgs-unstable
. Any breakage on those channels is considered a bug and
should be reported as such.
License
This project is licensed under the MIT license.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion by you, shall be licensed as MIT, without any additional terms or conditions.
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
Unreleased
0.19.3 - 2024-11-18
A republish of 0.19.2 which was incorrectly tagged.
0.19.2 - 2024-11-18
Added
- Added a number of fileset helpers to more easily compose source filtering:
fileset.cargoTomlAndLock
: forCargo.toml
andCargo.lock
filesfileset.commonCargoSources
: for files commonly used by cargo projectsfileset.configToml
: forconfig.toml
filesfileset.rust
: for*.rs
filesfileset.toml
: for*.toml
files
Fixed
buildTrunkPackage
will pass in--release=true
(instead of just--release
) for trunk versions 0.21 or higher to avoid argument ambiguitiesbuildTrunkPackage
will now correctly honorbuildPhaseCargoCommand
if specified (previously the value ofbuildPhaseCommand
was incorrectly being used)removeReferencesToVendoredSourcesHook
avoids referencing/dev/fd
directly since it may not be present on certain platforms
0.19.1 - 2024-10-12
Added
cargoDocTest
is now available as an alternative tocargoTest
which runs only doc tests.
Changed
buildDepsOnly
now setsCRANE_BUILD_DEPS_ONLY
as an environment variable when it runs. Build hooks can use this as a shortcut to determine whether running inside of abuildDepsOnly
derivation in case they need to tailor their behavior accordingly.
Fixed
- Vendoring dependencies avoids creating malformed TOML configurations in situations where registry name/url definitions cannot be found. When this happens a warning will be printed out during evaluation to highlight the issue.
0.19.0 - 2024-09-25
Added
taploFmt
is now available for checking TOML formatting
Changed
- Breaking (technically):
buildPackage
no longer addsjq
tonativeBuildInputs
as doing so can result in rebuilding any*-sys
crates which rely onPKG_CONFIG_PATH
remaining stable - Breaking:
downloadCargoPackageFromGit
now takeshash
instead ofsha256
when specifying an output hash for the download installFromCargoBuildLogHook
no longer assumes or requires thatjq
is available on$PATH
and will instead directly referencepkgs.jq
downloadCargoPackageFromGit
will now setfetchLFS = true
when fetching git repos with defined output hashes
Fixed
cargoDoc
correctly honorsdocInstallRoot
when specifiedcargoDoc
falls back to installing from./target/doc
even if$CARGO_BUILD_TARGET
is set but./target/$CARGO_BUILD_TARGET/doc
does not exist
Removed
- The deprecated top-level (flake) attribute
lib
no longer exists. Please usemkLib
with an instance ofpkgs
instead.
0.18.1 - 2024-08-22
Fixed
- Fixed vendoring dependencies from an alternative registry which they themselves have dependencies on crates from other registries.
- Fixed
cargoNextest
's positioning ofcargoExtraArgs
to form a valid command invocation when specified.
0.18.0 - 2024-07-05
Changed
- Breaking: dropped compatibility for Nix versions below 2.18.2
- Breaking: dropped compatibility for nixpkgs-23.11.
- The guidance around using (both)
cleanCargoSource
andpath
has been updated. Namely, it is no longer necessary to call both (e.g.craneLib.cleanCargoSource (craneLib.path ./.)
): it is recommended to either usecraneLib.cleanCargoSource ./.
directly (if the default source cleaning is desired) orcraneLib.path ./.
(if not). overrideToolchain
has been updated to better handle cross-compilation splicing for a customized toolchain. This means thatoverrideToolchain
should now be called with a function which constructs said toolchain for any givenpkgs
instantiation. For example:craneLib.overrideToolchain (p: p.rust-bin.stable.latest.default)
Fixed
- The cross compilation example also hows how to set the
TARGET_CC
environment variable which may be required by some build scripts to function properly vendorCargoDeps
andcrateNameFromCargoToml
do their best to avoid IFD whensrc
is the result oflib.cleanSourceWith
(and by extensioncleanCargoSource
)removeReferencesToVendoredSources
handles the edge case wherecargoVendorDir
does not point to a path within the Nix store- It is now possible to use
.overrideScope
to change what instance ofcraneUtils
will be used during vendoring.
0.17.3 - 2024-06-02
Fixed
removeReferencesToVendoredSources
correctly signs aarch64-darwin builds (which was accidentally broken in 0.17.2)
0.17.2 - 2024-05-26
Fixed
removeReferencesToVendoredSources
has been optimized to search for source references only once. For derivations which install many files, this phase can run up to 99% faster than before.cleanCargoToml
now cleans underscored versions of the same attributes (e.g.lib.proc-macro
andlib.proc_macro
)
0.17.1 - 2024-05-19
Fixed
downloadCargoPackage
anddownloadCargoPackageFromGit
no longer run the fixup phase by default, avoiding issues with source directories and files being moved to different locationsdownloadCargoPackage
now unpacks and installs from a fresh directory, avoiding having build environment files (likeenv-vars
) appearing in the output
0.17.0 - 2024-05-18
Added
cargoDoc
now supportsdocInstallRoot
to influence which directory will be installed to$out/share
(which can be useful when cross-compiling). By default$CARGO_TARGET_DIR
and$CARGO_BUILD_TARGET
(if set) will be taken into accountcrateNameFromCargoToml
now supports selecting a derivation name by settingpackage.metadata.crane.name
orworkspace.metadata.crane.name
in the rootCargo.toml
vendorCargoDeps
,vendorCargoRegistries
,vendorGitDeps
, andvendorMultipleCargoDeps
now support arbitrary overrides (i.e. patching) at the individual crate/repo level when vendoring sources.
Changed
- Breaking
cargoAudit
no longer acceptscargoExtraArgs
(since it does not support the regular set ofcargo
flags like most cargo-commands do, it does not make much sense to propagate those flags through) buildTrunkPackage
now setsenv.TRUNK_SKIP_VERSION_CHECK = "true";
if not specified
Deprecations
- In the future,
crateNameFromCargoToml
will stop consideringworkspace.package.name
in the rootCargo.toml
when determining the crate name. This attribute is not recognized by cargo (which will emit its own warnings about it) and should be avoided going forward. - In the future,
crane.lib.${system}
will be removed. Please switch to using(crane.mkLib nixpkgs.lib.${system})
as an equivalent alternative.
0.16.6 - 2024-05-04
Fixed
- Same as 0.16.5 but with the correct tag deployed to Flakestry/FlakeHub
0.16.5 - 2024-05-04
Fixed
- Workspace inheritance for git dependencies now ignores (removes) all comments
around dependency declarations to work around a mangling bug in
toml_edit
(see https://github.com/ipetkov/crane/issues/527 and https://github.com/toml-rs/toml/issues/691)
0.16.4 - 2024-04-07
Added
- Added a warning if an unsupported version of nixpkgs is used
Changed
cargoNextest
now supports settingwithLlvmCov
which will automatically runcargo llvm-cov nextest
. Note thatwithLlvmCov = true;
is (currently) only supported whenpartitions = 1;
Fixed
inheritCargoArtifactsHook
andinstallCargoArtifactsHook
now correctly handle the case whenCARGO_TARGET_DIR
is set to a nested directory- Dependency vendoring now correctly takes unused patch dependencies into account
0.16.3 - 2024-03-19
Changed
- Sources are now fetched crates.io's CDN, following cargo's (new) default behavior.
Fixed
vendorMultipleCargoDeps
correctly listsregistries
as an optional parameter
0.16.2 - 2024-02-21
Changed
cleanCargoToml
now also strips out[lints]
and[workspace.lints]
definitions. This means avoiding unnecessarily rebuilding dependencies when the lint definitions change, and it avoids issues with failing to build dummified sources which might have violated a lint marked asdeny
orforbid
Fixed
- Fixed an edge case with inheriting workspace dependencies where the workspace
dependency is a string (e.g.
foo = "0.1.2"
) but the crate definition is a table (e.g.foo = { workspace = true, optional = true }
)
0.16.1 - 2024-01-28
Changed
buildDepsOnly
now ignores any outputs (besides the defaultout
)
Fixed
buildDepsOnly
no longer fails when workspace is configured with#[deny(unused-extern-crates)]
vendorCargoDeps
(and friends) are now much more friendly to cross-compilation definitions. Specifically, source vendoring will always build dependencies to run on the build machine (and not for the host we're cross compiling to).
0.16.0 - 2024-01-18
Changed
- Breaking: dropped compatibility for Nix versions below 2.18.1
- Breaking: dropped compatibility for nixpkgs-23.05.
buildTrunkPackage
has a new argument,wasm-bindgen-cli
must be set to avoid mismatching versions between the wasm-bindgen library and CLI tool.
Fixed
- Workspace inheritance of
lints
in git dependencies is now correctly handled
0.15.1 - 2023-11-30
Changed
buildDepsOnly
will now assumecargoTestExtraArgs = "--no-run";
if not specified (since there is no point to trying to run tests with the stripped sources). To get the old behavior back, setcargoTestExtraArgs = "";
Fixed
buildTrunkPackage
'spreConfigure
script to fail quicker with a more obvious error message if dependencies at not appropriately met
0.15.0 - 2023-11-05
Added
cargoDeny
added for runningcargo-deny
.installCargoArtifactsHook
will now pass along the contents of$zstdCompressionExtraArgs
as arguments tozstd
when compressing artifacts. This allows for tailoring compression behavior, for example, by settingzstdCompressionExtraArgs = "-19";
on the derivation.
Changed
- The
use-zstd
artifact installation mode now uses a chained, incremental approach to avoid redundancy. Old behavior (taking a full snapshot of the cargo artifacts) can be achieved by settingdoCompressAndInstallFullArchive = true
. - The default
installCargoArtifactsMode
has been changed touse-zstd
, meaning cargo artifacts will be compressed to a series of incremental, zstd compressed tarballs across derivations. To get the old behavior back, setinstallCargoArtifactsMode = "use-symlink"
to any derivation which produces cargo artifacts. - All dependencies (outside of
nixpkgs
) have been dropped from the (main) flake.lock file so they do not pollute downstream projects' lock files.
Fixed
mkDummySrc
now properly handles file cleaning (and file including) when a build is invoked with a--store ...
override
0.14.3 - 2023-10-17
Changed
craneUtils
will now be built with therustPlatform
provided by nixpkgs instead of the currently configured toolchain. This should hopefully result in fewer surprises for those testing with really old MSRV toolchains.devShell
will now additionally includeclippy
andrustfmt
from the currently configured toolchain
Fixed
replaceCargoLockHook
now runs as aprePatch
hook (rather thanpostUnpack
) which correctly replaces theCargo.lock
in the source directory rather than the parent directory
0.14.2 - 2023-10-15
Added
replaceCargoLockHook
can now be used to easily replace or insert aCargo.lock
file in the current derivation
Changed
cargoAudit
will pass--ignore yanked
by default ifcargoAuditExtraArgs
are not specified. This is becausecargo-audit
cannot check for yanked crates from inside of the sandbox. To get the old behavior back, setcargoAuditExtraArgs = "";
.
Fixed
- Fixed handling of Cargo workspace inheritance for git-dependencies where said crate relies on reading non-TOML metadata (i.e. comments) from its Cargo.toml at build time. (#407)
- Fixed handling of dummy target names to avoid issues with
cargo doc
. (#410) - When using
installCargoArtifactsMode = "use-zstd";
all files will be marked as user-writable while compressing removeReferencesToVendoredSources
now signsaarch64-darwin
binaries. (#418)
0.14.1 - 2023-09-23
Fixed
- Fixed a bug where
buildPackage
would fail to inherit artifacts from dependency crates ifcargoArtifacts
was not explicitly specified.
0.14.0 - 2023-09-21
Added
- Added
devShell
, a thin wrapper aroundpkgs.mkShell
which automatically providescargo
andrustc
. - Added the ability to specify output hashes of git dependencies for fully
offline evaluations. The
outputHashes
attribute can now be optionally specified invendorCargoDeps
,vendorGitDeps
,vendorMultipleCargoDeps
, or anything else which delegates to them.
Changed
- Breaking (technically):
buildDepsOnly
,buildPackage
,cargoBuild
,cargoClippy
,cargoDoc
,cargoLlvmCov
, andcargoTest
's defaults have been changed such that ifcargoExtraArgs
have not been set, a default value of--locked
will be used. This ensures that a project's committedCargo.lock
is exactly what is expected (without implicit changes at build time) but this may end up rejecting builds which were previously passing. To get the old behavior back, setcargoExtraArgs = "";
- Breaking:
cargoDoc
will no longer install cargo artifacts by default. SetdoInstallCargoArtifacts = true;
to get the old behavior back. cargoDoc
will now install generated documentation in$out/share/doc
- Fixed a bug when testing proc macro crates with
cargoNextest
on macOS. (#376) - Replaced various internal usages of
runCommandLocal
withrunCommand
for more optimal behavior when downloading cached artifacts
0.13.1 - 2023-08-22
Changed
buildTrunkPackage
will now usedart-sass
instead ofnodePackages.sass
- Vendoring git dependencies will now always resolve symlinks inside of a crate's directory. This allows for symlinks inside of a crate's directory to possibly refer to files at the root of the git repo itself (via symlink) and have those contents preserved during vendoring.
0.13.0 - 2023-08-07
Added
buildPackage
now supports installingdylib
targets- Added support for sparse registries
Changed
- Breaking: dropped compatibility for Nix versions below 2.13.3
- Breaking: dropped compatibility for nixpkgs-22.05. nixpkgs-23.05 and
- Breaking (technically): if
buildPackage
is called without settingcargoArtifacts
, the defaultbuildDepsOnly
invocation will now stop running any installation hooks - Breaking (technically):
buildPackage
no longer installs cargo binary dependencies (i.e. when thebindeps
feature is used) by default inheritCargoArtifactsHook
will now symlink dependency.rlib
and.rmeta
files. This means that derivations which reuse existing cargo artifacts will run faster as fewer files (and bytes!) need to be copied around. To disable this behavior, setdoNotLinkInheritedArtifacts = true;
.cargoTarpaulin
will now setdoNotLinkInheritedArtifacts = true;
unless otherwise specified- Update
crane-utils
dependencies for successful build in nightly Rust (2023-06-28)
0.12.2 - 2023-06-06
Added
- Added support for the Trunk wasm app build tool
Changed
resolver
key is no longer cleaned from Cargo.toml
Fixed
buildTrunkPackage
will now strip references to store files by defaultbuildTrunkPackage
will now set the rightwasm-opt
version
0.12.1 - 2023-04-10
Changed
- Breaking: When setting a default value for
cargoArtifacts
,buildPackage
will now ignoreinstallPhase
andinstallPhaseCommand
when callingbuildPackage
. To bring back the old behavior, please specifycargoArtifacts
explicitly
Added
vendorMultipleCargoDeps
can now be used to vendor crates from multiple distinctCargo.lock
files. Notably this allows for building the standard library (via-Z build-std
or equivalent) since both the project's and the Rust toolchain'sCargo.lock
files can be vendored together
Changed
vendorCargoRegistries
now accepts aregistries
parameter from the caller. If not specified, it will be computed viacargoConfigs
. AlsocargoConfigs
is now an optional parameter which will default to[]
if not specified.
Fixed
vendorCargoDeps
correctly accepts arguments which have not setsrc
, so long as one ofcargoLock
,cargoLockContents
, orcargoLockParsed
is set
0.12.0 - 2023-03-19
Added
- Add a stubbed binary target to each "dummy" crate generated to support
"artifact dependencies" nightly feature
in case a crate is used as
bin
artifact dependency. - Add
cargoLlvmCov
to runcargo llvm-cov
- Add
cargoLockParsed
option tovendorCargoDeps
to supportCargo.lock
files parsed as nix attribute sets. craneLib.path
can now be used as a convenience wrapper on (or drop in replacement of)builtins.path
to ensure reproducible results whenever paths like./.
or./..
are used directly.
Changed
- Breaking (technically):
mkCargoDerivation
will remove the following attributes before lowering tomkDerivation
:cargoLock
,cargoLockContents
andcargoLockParsed
. If your derivation needs these values to be present they can be explicitly passed through via.overrideAttrs
buildDepsOnly
asdummySrc
will take priority - The API docs have been updated to refer to
craneLib
(instead of justlib
) to avoid ambiguities withpkgs.lib
. - cargo is now invoked with
--release
when$CARGO_PROFILE == release
instead of passing in--profile release
to better support tools which do not understand the latter
Fixed
- Fixed support for projects depending on crates utilising per-target workspace dependencies.
0.11.3 - 2023-02-19
Fixed
- Fixed an unintentional cache invalidation whenever
buildDepsOnly
would run on an unfiltered source (likesrc = ./.;
).
Changed
- A warning will now be emitted if a derivation's
pname
orversion
attributes are not set and the value cannot be loaded from the derivation's rootCargo.toml
. To resolve it consider settingpname = "...";
orversion = "...";
explicitly on the derivation. - A warning will now be emitted if
src
anddummySrc
are passed tobuildDepsOnly
asdummySrc
will take priority
0.11.2 - 2023-02-11
Fixed
buildPackage
is more tolerant of misbehaving proc macros which write to stdout during the build
0.11.1 - 2023-01-21
Changed
- Documented and made it easier to build a cargo workspace located in a subdirectory of the source root
Fixed
- Previously compiled build scripts now maintain their executable bit when inherited
- Workspace inheritance in git dependencies is now correctly handled
0.11.0 - 2022-12-26
Added
- Documentation is now available at crane.dev
Changed
- Breaking: dropped compatibility for Nix versions below 2.11.0
- Breaking: dropped compatibility for nixpkgs-22.05. nixpkgs-22.11 and nixpkgs-unstable are fully supported
- Zstd compression of cargo artifacts now defaults to using as many cores as
$NIX_BUILD_CORES
allows for (or all available cores if it isn't defined) - Dummy sources now attempt to use the same name as their original source (minus the Nix store path and hash) to minimize errors with build scripts which expect their full path to not change between runs
0.10.0 - 2022-12-01
Added
- A new installation mode has been defined which symlinks identical cargo artifacts against previously generated ones. This allows for linear space usage in the Nix store across many chained derivations (as opposed to using a zstd compressed tarball which uses quadratic space across many chained derivations).
mkDummySrc
optionally accepts adummyrs
argument which allows for customizing the contents of the dummy Rust files that will be generated.
Changed
- Breaking: all cargo-based derivations will now default to using symlinking
their installed artifacts together instead of using zstd compressed tarballs.
To get the old behavior back, set
installCargoArtifactsMode = "use-zstd";
in the derivation.- Note that
buildPackage
will continue to use zstd compressed tarballs while building dependencies (unless either ofcargoArtifacts
orinstallCargoArtifactsMode
is defined, in which case they will be honored)
- Note that
- Breaking: the format for defining crate registries has been changed: each
registry URL should map to a set containing a
downloadUrl
attribute. This set may also definefetchurlExtraArgs
(another set) which will be forwarded to thefetchurl
invocations for crates for that registry. - Breaking (technically):
buildDepsOnly
will now only default to runningcargo check
with the--all-targets
flag only ifdoCheck = true;
is set on the derivation (otherwise the flag is omitted). To get the previous behavior back simply setcargoCheckExtraArgs = "--all-targets";
. registryFromGitIndex
now uses shallow checkouts for better performanceregistryFromDownloadUrl
andregistryFromGitIndex
now allow specifyingfetchurlExtraArgs
which will be forwarded to thefetchurl
invocations for crates for that registry
Fixed
- Unpacking a git repository now ignores duplicate crates to match cargo's behavior
- Sped up stripping references to source files
- Dummy sources now import the
core
crate more robustly (playing more nicely withcargo-hakari
) - Building a crate's dependencies automatically works for uefi targets
0.9.0 - 2022-10-29
Changed
- Breaking: all setup hooks have been removed from the
packages
flake output. They can still be accessed via thelib
flake output. - Breaking:
cargoBuild
now only runscargo build
in a workspace, tests are no longer run - Breaking:
buildDepsOnly
does not automatically imply the--all-targets
flag when invokingcargo check
. UsecargoCheckExtraArgs
to control this buildDepsOnly
now acceptscargoCheckExtraArgs
for passing additional arguments just to thecargo check
invocation. By default--all-targets
will be usedbuildDepsOnly
now acceptscargoTestExtraArgs
for passing additional arguments just to thecargo test
invocationbuildPackage
now delegates tomkCargoDerivation
instead ofcargoBuild
Fixed
crateNameFromCargoToml
now takes workspace inheritance into account. If a crate does not specifypackage.version
in its (root) Cargo.toml but does specifyworkspace.package.version
then the latter will be returned.- Freestanding (
#![no_std]
) targets are now supported
0.8.0 - 2022-10-09
Added
cargoTest
can now be used for only running the tests of a workspace
Changed
- Breaking (technically): build hooks now expect helper tools (like
cargo
,jq
,zstd
, etc.) to be present on the path instead of substituting a reference to a (possibly different) executable in the store. mkCargoDerivation
now automatically vendors dependencies ifcargoVendorDir
is not definedmkCargoDerivation
now automatically populatespname
andversion
(viacrateNameFromCargoToml
) if they are not specifiedmkCargoDerivation
now defaults to an emptycheckPhaseCargoCommand
if not specifiedcargoAudit
now delegates tomkCargoDerivation
instead ofcargoBuild
cargoClippy
now delegates tomkCargoDerivation
instead ofcargoBuild
cargoDoc
now delegates tomkCargoDerivation
instead ofcargoBuild
cargoFmt
now delegates tomkCargoDerivation
instead ofcargoBuild
cargoNextest
now delegates tomkCargoDerivation
instead ofcargoBuild
cargoTarpaulin
now delegates tomkCargoDerivation
instead ofcargoBuild
Fixed
- Installing binaries now uses the same version of cargo as was used to build the package (instead of using whatever version is present in nixpkgs)
Deprecated
- The
packages
flake output has been deprecated. All setup hooks can be accessed via thelib
flake output (or via the result of themkLib
flake output)
0.7.0 - 2022-09-28
Added
cargoDoc
can now be used for building the documentation of a workspacecleanCargoSource
can now be used to filter sources to only include cargo and Rust files (and avoid rebuilds when irrelevant files change).filterCargoSources
is the underlying filter implementation and can be composed with other filtersremoveReferencesToVendoredSourcesHook
defines a post-install hook which will remove any references to vendored sources from any installed binaries. Useful for preventing nix from considering the binaries as having a (runtime) dependency on said sources
Changed
- Breaking:
mkCargoDerivation
now includes a defaultconfigurePhase
which does nothing but run thepreConfigure
andpostConfigure
hooks. This is done to avoid breaking builds by including puts happen to have setup-hooks which try to claim the configure phase (such ascmake
). To get the old behavior back, setconfigurePhase = null;
in the derivation. mkCargoDerivation
(along with any of its callers likecargoBuild
,buildPackage
, etc.) now accept astdenv
argument which will override the default environment (coming frompkgs.stdenv
) for that particular derivationmkDummySrc
now acceptsextraScript
which can be used to run a custom script, and therefore customize what the dummy source containsbuildDepsOnly
now acceptsdummySrc
as a way to directly pass in the dummy source to be used. Automatically derived viaargs.src
if not specified.
Fixed
cargoAudit
properly keeps anyaudit.toml
files when cleaning the sourcebuildPackage
now has more robust checks to ensure that all references to vendored sources are removed after installation (which avoids consumers of the final binaries having to download the sources as well)mkDummySrc
how handles build scripts in a manner which ensures cargo runs the real script later (instead of thinking it has not changed)
0.6.0 - 2022-09-07
Added
- Added
cargoNextest
for running tests via cargo-nextest - Added
cargoAudit
for running cargo-audit with a provided advisory database instance.
Changed
- Breaking: the
--workspace
flag is no longer set for all cargo commands by default. The previous behavior can be recovered by settingcargoExtraArgs = "--workspace";
in any derivation. - Breaking: the
$CARGO_PROFILE
environment variable can be used to specify which cargo-profile all invocations use (by defaultrelease
will be used). Technically breaking if the default command was overridden for any derivation; setCARGO_PROFILE = "";
to avoid telling cargo to use a release build. - Breaking:
cargoTarpaulin
will use the release profile by default - Breaking:
cargoClippy
'scargoClippyExtraArgs
now default to"--all-targets"
instead of being specified as the cargo command itself. If you have setcargoClippyExtraArgs
to an explicit value and wish to retain the previous behavior you should prepend"--all-targets"
to it. - Breaking:
remapSourcePathPrefixHook
and thedoRemapSourcePathPrefix
option have been removed, and the behavior ofbuildPackage
has been updated to break false dependencies on the crate sources from the final binaries (which was the old behavior of thedoRemapSourcePathPrefix
option). To disable this behavior, set thedoNotRemoveReferencesToVendorDir
environment variable to any non-empty string. - All cargo invocations made during the build are automatically logged
- Vendoring git dependencies will throw a descriptive error message if a locked
revision is missing from
Cargo.lock
and a hint towards resolution
Fixed
- Breaking:
vendorGitDeps
will only include crates referenced by theCargo.lock
file, meaning any extraneous crates which happen to be present in the git repository will be ignored.
0.5.1 - 2022-07-20
Added
- Added
.overrideToolchain
as a convenience for using a custom rust toolchain
Fixed
- Fixed an issue where
mkDummySrc
would produce incorrect results for filtered sources: #46
0.5.0 - 2022-06-12
Changed
- Breaking: dropped compatibility for Nix versions below 2.8.1
- Breaking: updated all flake attributes to follow the new
.default
guidance as per Nix's warnings. Specifically:- Crane's default overlay is now available at
.overlays.default
(previously.overlay
) - All templates now use
{app,devShells,packages}.default
as well
- Crane's default overlay is now available at
- Breaking:
lib.fromTOML
andlib.toTOML
have been removed in favor ofbuiltins.fromTOML
- Improved support for consuming
crane
without using flakes - The
nix-std
dependency has been dropped
0.4.1 - 2022-05-29
Fixed
- Dummy source derivations go to greater lengths to only depend on the files
they consume. Specifying the entire flake source as an input (e.g. via
buildPackage { src = self; }
) now avoids rebuilding everything from scratch whenever any file is changed. #28
0.4.0 - 2022-05-10
Changed
- Breaking: the previously named
utils
flake input has been renamed toflake-utils
buildDepsOnly
now adds--all-targets
to the defaultcargo check
invocation. This allows caching all artifacts (including from dev-dependencies) such that tools like clippy don't have to generate them every time they run.- Templates now use the newer flake format accepted by Nix 2.8 (e.g.
{packages,overlays,devShells}.default
, etc.)
Fixed
- Fixed project and template flakes to avoid superfluous follows declaration for
flake-utils
- Fixed quoting of relative paths to allow building with external sources
0.3.3 - 2022-02-24
Fixed
- Use
lib.groupBy
ifbuiltins.groupBy
isn't available (i.e. if a Nix version earlier than 2.5 is used) - The cross compilation example also hows how to set the
HOST_CC
environment variable which may be required by some build scripts to function properly
0.3.2 - 2022-02-18
Fixed
- Fixed handling git dependencies whose locked revision is not on the repository's main branch
0.3.1 - 2022-02-17
Added
- Added template and example for cross compiling to other platforms
- Added template and example for building static binaries using musl
Changed
cargoClippy
andcargoTarpaulin
will install cargo artifacts by default (or install an emptytarget
directory if there are none). This allows for more easily chaining derivations if doing so is desired.- This can be disabled by setting
doInstallCargoArtifacts = false;
in the derivation
- This can be disabled by setting
Fixed
- Fixed an issue where cross compiling would try to needlessly cross compile rustc and cargo themselves
0.3.0 - 2022-02-11
Added
downloadCargoPackageFromGit
has been added to handle downloading and unpacking a cargo workspace from a git repositoryvendorCargoRegistries
has been added to handle vendoring crates from all registries used in aCargo.lock
filevendorGitDeps
has been added to handle vendoring crates from all git sources used in aCargo.lock
file
Changed
vendorCargoDeps
now automatically handles git dependencies by default- Git dependencies will be vendored as another source in the output derivation
- The cargo configuration is done such that the sources are available to use
when it decides, without overriding that crate for the entire workspace
- For example, if your workspace contains a crate only used for testing which has a git dependency of a crate used by other parts of the workspace, then only that crate will use the git dependency. The rest of the workspace will continue to use the crates.io version, just like cargo behaves when used outside of Nix.
0.2.1 - 2022-02-11
Changed
cargoFmt
will install cargo artifacts by default (or install an emptytarget
directory if there are none). This allows for more easily chaining derivations if doing so is desired.- This can be disabled by setting
doInstallCargoArtifacts = false;
in the derivation
- This can be disabled by setting
0.2.0 - 2022-01-30
Added
- Support for alternative cargo registries
Changed
urlForCargoPackage
now takes configured registries into account when downloading crate sources- Breaking:
vendorCargoDeps
now vendors each unique registry as a subdirectory within the derivation's output. Aconfig.toml
file is also placed at the output root which contains the necessary configurations to point cargo at the vendored sources. configureCargoVendoredDepsHook
is now aware of the updatedvendorCargoDeps
output format, and will use theconfig.toml
file it generates if it is present. Otherwise it will fall back to the previous behavior (which is treat the entire directory as only vendoring crates.io).- Source vendoring now uses
runCommandLocal
(instead ofrunCommand
) to reduce network pressure in trying to fetch results which can quickly be built locally - Searching for
Cargo.toml
or.cargo/config.toml
files is now done more efficiently
0.1.0 - 2022-01-22
- First release
Philosophy
Crane is designed around the idea of composing cargo invocations such that they can take advantage of the artifacts generated in previous invocations. This allows for both flexible configurations and great caching (à la Cachix) in CI and local development builds.
Here's how it works at a high level: when a cargo workspace is built its source
is first transformed such that only the dependencies listed by the Cargo.toml
and Cargo.lock
files are built, and none of the crate's real source is
included. This allows cargo to build all dependency crates and prevents Nix from
invalidating the derivation whenever the source files are updated. Then, a
second derivation is built, this time using the real source files, which also
imports the cargo artifacts generated in the first step.
This pattern can be used with any arbitrary sequence of commands, regardless of whether those commands are running additional lints, performing code coverage analysis, or even generating types from a model schema. Let's take a look at two examples at how very similar configurations can give us very different behavior!
Example One: Artifact Reuse
Suppose we are developing a crate and want to run our CI assurance checks
via nix flake check
. Perhaps we want the CI gate to be very strict and block
any changes which raise warnings when run with cargo clippy
. Oh, and we want
to enforce some code coverage too!
Except we do not want to push our strict guidelines on any downstream consumers
who may want to build our crate. Suppose they need to build the crate with a
different compiler version (for one reason or another) which comes with a new lint
whose warnings we have not yet addressed. We don't want to make their life
harder, so we want to make sure we do not run cargo clippy
as part of the
crate's actual derivation, but at the same time, we don't want to have to
rebuild dependencies from scratch.
Here's how we can set up our flake to achieve our goals:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, crane, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
craneLib = crane.mkLib pkgs;
# Common derivation arguments used for all builds
commonArgs = {
src = craneLib.cleanCargoSource ./.;
strictDeps = true;
buildInputs = with pkgs; [
# Add extra build inputs here, etc.
# openssl
];
nativeBuildInputs = with pkgs; [
# Add extra native build inputs here, etc.
# pkg-config
];
};
# Build *just* the cargo dependencies, so we can reuse
# all of that work (e.g. via cachix) when running in CI
cargoArtifacts = craneLib.buildDepsOnly (commonArgs // {
# Additional arguments specific to this derivation can be added here.
# Be warned that using `//` will not do a deep copy of nested
# structures
pname = "mycrate-deps";
});
# Run clippy (and deny all warnings) on the crate source,
# reusing the dependency artifacts (e.g. from build scripts or
# proc-macros) from above.
#
# Note that this is done as a separate derivation so it
# does not impact building just the crate by itself.
myCrateClippy = craneLib.cargoClippy (commonArgs // {
# Again we apply some extra arguments only to this derivation
# and not every where else. In this case we add some clippy flags
inherit cargoArtifacts;
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
});
# Build the actual crate itself, reusing the dependency
# artifacts from above.
myCrate = craneLib.buildPackage (commonArgs // {
inherit cargoArtifacts;
});
# Also run the crate tests under cargo-tarpaulin so that we can keep
# track of code coverage
myCrateCoverage = craneLib.cargoTarpaulin (commonArgs // {
inherit cargoArtifacts;
});
in
{
packages.default = myCrate;
checks = {
inherit
# Build the crate as part of `nix flake check` for convenience
myCrate
myCrateClippy
myCrateCoverage;
};
});
}
When we run nix flake check
the following will happen:
- The sources for any dependency crates will be fetched
- They will be built without our crate's code and the artifacts propagated
- Our crate, the clippy checks, and code coverage collection will be built, each reusing the same set of artifacts from the initial source-free build. If enough cores are available to Nix it may build all three derivations completely in parallel, or schedule them in some arbitrary order.
Splitting up our builds like this also gives us the benefit of granular control over what is rebuilt. Suppose we change our mind and decide to adjust the clippy flags (e.g. to allow certain lints or forbid others). Doing so will only rebuild the clippy derivation, without having to rebuild and rerun any of our other tests!
Example Two: Sequential Builds
Let's take an alternative approach to the previous example. Suppose instead that we care more about not wasting any resources building certain tests (even if they would succeed!) if another particular check fails. Perhaps binary substitutes are readily available so that we do not mind if anyone building from source is bound by our rules, and we can be sure that all tests have passed as part of the build.
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, crane, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
craneLib = crane.mkLib pkgs;
# Common derivation arguments used for all builds
commonArgs = {
src = craneLib.cleanCargoSource ./.;
strictDeps = true;
buildInputs = with pkgs; [
# Add extra build inputs here, etc.
# openssl
];
nativeBuildInputs = with pkgs; [
# Add extra native build inputs here, etc.
# pkg-config
];
};
# Build *just* the cargo dependencies, so we can reuse
# all of that work (e.g. via cachix) when running in CI
cargoArtifacts = craneLib.buildDepsOnly (commonArgs // {
# Additional arguments specific to this derivation can be added here.
# Be warned that using `//` will not do a deep copy of nested
# structures
pname = "mycrate-deps";
});
# First, run clippy (and deny all warnings) on the crate source.
myCrateClippy = craneLib.cargoClippy (commonArgs // {
# Again we apply some extra arguments only to this derivation
# and not every where else. In this case we add some clippy flags
inherit cargoArtifacts;
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
});
# Next, we want to run the tests and collect code-coverage, _but only if
# the clippy checks pass_ so we do not waste any extra cycles.
myCrateCoverage = craneLib.cargoTarpaulin (commonArgs // {
cargoArtifacts = myCrateClippy;
});
# Build the actual crate itself, _but only if the previous tests pass_.
myCrate = craneLib.buildPackage (commonArgs // {
cargoArtifacts = myCrateCoverage;
});
in
{
packages.default = myCrate;
checks = {
inherit
# Build the crate as part of `nix flake check` for convenience
myCrate
myCrateCoverage;
};
});
}
When we run nix flake check
the following will happen:
- The sources for any dependency crates will be fetched
- They will be built without our crate's code and the artifacts propagated
- Next the clippy checks will run, reusing the dependency artifacts above.
- Next the code coverage tests will run, reusing the artifacts from the clippy run
- Finally the actual crate itself is built
In this case we lose the ability to build derivations independently, but we gain the ability to enforce a strict build order. However, we can easily change our mind, which would be much more difficult if we had written everything as one giant derivation.
Getting Started
The easiest way to get started is to initialize a flake from a template:
# Start with a comprehensive suite of tests
nix flake init -t github:ipetkov/crane#quick-start
# Or if you want something simpler
nix flake init -t github:ipetkov/crane#quick-start-simple
# If you need a custom rust toolchain (e.g. to build WASM targets):
nix flake init -t github:ipetkov/crane#custom-toolchain
# If you need to use another crate registry besides crates.io
nix flake init -t github:ipetkov/crane#alt-registry
# If you need cross-compilation, you can also try out
nix flake init -t github:ipetkov/crane#cross-rust-overlay
# For statically linked binaries using musl
nix flake init -t github:ipetkov/crane#cross-musl
# If you are building a WASM webapp with trunk
nix flake init -t github:ipetkov/crane#trunk
# If you are building a workspace with trunk member
nix flake init -t github:ipetkov/crane#trunk-workspace
# If you would like to perform end to end testing of a webapp
nix flake init -t github:ipetkov/crane#end-to-end-testing
For an even more lean, no frills set up, create a flake.nix
file with the
following contents at the root of your cargo workspace:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, crane, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
craneLib = crane.mkLib pkgs;
in
{
packages.default = craneLib.buildPackage {
src = craneLib.cleanCargoSource ./.;
# Add extra inputs here or any other derivation settings
# doCheck = true;
# buildInputs = [];
# nativeBuildInputs = [];
};
});
}
To build a cargo project with a comprehensive test suite, run the following in a fresh directory:
nix flake init -t github:ipetkov/crane#quick-start
Alternatively, if you have an existing project already, copy and paste the
following flake.nix
:
{
description = "Build a cargo project";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
fenix = {
url = "github:nix-community/fenix";
inputs.nixpkgs.follows = "nixpkgs";
inputs.rust-analyzer-src.follows = "";
};
flake-utils.url = "github:numtide/flake-utils";
advisory-db = {
url = "github:rustsec/advisory-db";
flake = false;
};
};
outputs = { self, nixpkgs, crane, fenix, flake-utils, advisory-db, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
inherit (pkgs) lib;
craneLib = crane.mkLib pkgs;
src = craneLib.cleanCargoSource ./.;
# Common arguments can be set here to avoid repeating them later
commonArgs = {
inherit src;
strictDeps = true;
buildInputs = [
# Add additional build inputs here
] ++ lib.optionals pkgs.stdenv.isDarwin [
# Additional darwin specific inputs can be set here
pkgs.libiconv
];
# Additional environment variables can be set directly
# MY_CUSTOM_VAR = "some value";
};
craneLibLLvmTools = craneLib.overrideToolchain
(fenix.packages.${system}.complete.withComponents [
"cargo"
"llvm-tools"
"rustc"
]);
# Build *just* the cargo dependencies, so we can reuse
# all of that work (e.g. via cachix) when running in CI
cargoArtifacts = craneLib.buildDepsOnly commonArgs;
# Build the actual crate itself, reusing the dependency
# artifacts from above.
my-crate = craneLib.buildPackage (commonArgs // {
inherit cargoArtifacts;
});
in
{
checks = {
# Build the crate as part of `nix flake check` for convenience
inherit my-crate;
# Run clippy (and deny all warnings) on the crate source,
# again, reusing the dependency artifacts from above.
#
# Note that this is done as a separate derivation so that
# we can block the CI if there are issues here, but not
# prevent downstream consumers from building our crate by itself.
my-crate-clippy = craneLib.cargoClippy (commonArgs // {
inherit cargoArtifacts;
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
});
my-crate-doc = craneLib.cargoDoc (commonArgs // {
inherit cargoArtifacts;
});
# Check formatting
my-crate-fmt = craneLib.cargoFmt {
inherit src;
};
my-crate-toml-fmt = craneLib.taploFmt {
src = pkgs.lib.sources.sourceFilesBySuffices src [ ".toml" ];
# taplo arguments can be further customized below as needed
# taploExtraArgs = "--config ./taplo.toml";
};
# Audit dependencies
my-crate-audit = craneLib.cargoAudit {
inherit src advisory-db;
};
# Audit licenses
my-crate-deny = craneLib.cargoDeny {
inherit src;
};
# Run tests with cargo-nextest
# Consider setting `doCheck = false` on `my-crate` if you do not want
# the tests to run twice
my-crate-nextest = craneLib.cargoNextest (commonArgs // {
inherit cargoArtifacts;
partitions = 1;
partitionType = "count";
});
};
packages = {
default = my-crate;
} // lib.optionalAttrs (!pkgs.stdenv.isDarwin) {
my-crate-llvm-coverage = craneLibLLvmTools.cargoLlvmCov (commonArgs // {
inherit cargoArtifacts;
});
};
apps.default = flake-utils.lib.mkApp {
drv = my-crate;
};
devShells.default = craneLib.devShell {
# Inherit inputs from checks.
checks = self.checks.${system};
# Additional dev-shell environment variables can be set directly
# MY_CUSTOM_DEVELOPMENT_VAR = "something else";
# Extra inputs can be added here; cargo and rustc are provided by default.
packages = [
# pkgs.ripgrep
];
};
});
}
To build a cargo project without extra tests, run the following in a fresh directory:
nix flake init -t github:ipetkov/crane#quick-start-simple
Alternatively, if you have an existing project already, copy and paste the
following flake.nix
:
{
description = "Build a cargo project without extra checks";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, crane, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
craneLib = crane.mkLib pkgs;
# Common arguments can be set here to avoid repeating them later
# Note: changes here will rebuild all dependency crates
commonArgs = {
src = craneLib.cleanCargoSource ./.;
strictDeps = true;
buildInputs = [
# Add additional build inputs here
] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [
# Additional darwin specific inputs can be set here
pkgs.libiconv
];
};
my-crate = craneLib.buildPackage (commonArgs // {
cargoArtifacts = craneLib.buildDepsOnly commonArgs;
# Additional environment variables or build phases/hooks can be set
# here *without* rebuilding all dependency crates
# MY_CUSTOM_VAR = "some value";
});
in
{
checks = {
inherit my-crate;
};
packages.default = my-crate;
apps.default = flake-utils.lib.mkApp {
drv = my-crate;
};
devShells.default = craneLib.devShell {
# Inherit inputs from checks.
checks = self.checks.${system};
# Additional dev-shell environment variables can be set directly
# MY_CUSTOM_DEVELOPMENT_VAR = "something else";
# Extra inputs can be added here; cargo and rustc are provided by default.
packages = [
# pkgs.ripgrep
];
};
});
}
Note that it is highly recommended to use something like
cargo-hakari
to avoid cache misses when building various workspace crates.
To build a cargo workspace with a comprehensive test suite, run the following in a fresh directory:
nix flake init -t github:ipetkov/crane#quick-start-workspace
Alternatively, if you have an existing project already, copy and paste the
following flake.nix
:
{
description = "Build a cargo workspace";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
fenix = {
url = "github:nix-community/fenix";
inputs.nixpkgs.follows = "nixpkgs";
inputs.rust-analyzer-src.follows = "";
};
flake-utils.url = "github:numtide/flake-utils";
advisory-db = {
url = "github:rustsec/advisory-db";
flake = false;
};
};
outputs = { self, nixpkgs, crane, fenix, flake-utils, advisory-db, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
inherit (pkgs) lib;
craneLib = crane.mkLib pkgs;
src = craneLib.cleanCargoSource ./.;
# Common arguments can be set here to avoid repeating them later
commonArgs = {
inherit src;
strictDeps = true;
buildInputs = [
# Add additional build inputs here
] ++ lib.optionals pkgs.stdenv.isDarwin [
# Additional darwin specific inputs can be set here
pkgs.libiconv
];
# Additional environment variables can be set directly
# MY_CUSTOM_VAR = "some value";
};
craneLibLLvmTools = craneLib.overrideToolchain
(fenix.packages.${system}.complete.withComponents [
"cargo"
"llvm-tools"
"rustc"
]);
# Build *just* the cargo dependencies (of the entire workspace),
# so we can reuse all of that work (e.g. via cachix) when running in CI
# It is *highly* recommended to use something like cargo-hakari to avoid
# cache misses when building individual top-level-crates
cargoArtifacts = craneLib.buildDepsOnly commonArgs;
individualCrateArgs = commonArgs // {
inherit cargoArtifacts;
inherit (craneLib.crateNameFromCargoToml { inherit src; }) version;
# NB: we disable tests since we'll run them all via cargo-nextest
doCheck = false;
};
fileSetForCrate = crate: lib.fileset.toSource {
root = ./.;
fileset = lib.fileset.unions [
./Cargo.toml
./Cargo.lock
(craneLib.fileset.commonCargoSources ./crates/my-common)
(craneLib.fileset.commonCargoSources ./crates/my-workspace-hack)
(craneLib.fileset.commonCargoSources crate)
];
};
# Build the top-level crates of the workspace as individual derivations.
# This allows consumers to only depend on (and build) only what they need.
# Though it is possible to build the entire workspace as a single derivation,
# so this is left up to you on how to organize things
#
# Note that the cargo workspace must define `workspace.members` using wildcards,
# otherwise, omitting a crate (like we do below) will result in errors since
# cargo won't be able to find the sources for all members.
my-cli = craneLib.buildPackage (individualCrateArgs // {
pname = "my-cli";
cargoExtraArgs = "-p my-cli";
src = fileSetForCrate ./crates/my-cli;
});
my-server = craneLib.buildPackage (individualCrateArgs // {
pname = "my-server";
cargoExtraArgs = "-p my-server";
src = fileSetForCrate ./crates/my-server;
});
in
{
checks = {
# Build the crates as part of `nix flake check` for convenience
inherit my-cli my-server;
# Run clippy (and deny all warnings) on the workspace source,
# again, reusing the dependency artifacts from above.
#
# Note that this is done as a separate derivation so that
# we can block the CI if there are issues here, but not
# prevent downstream consumers from building our crate by itself.
my-workspace-clippy = craneLib.cargoClippy (commonArgs // {
inherit cargoArtifacts;
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
});
my-workspace-doc = craneLib.cargoDoc (commonArgs // {
inherit cargoArtifacts;
});
# Check formatting
my-workspace-fmt = craneLib.cargoFmt {
inherit src;
};
my-workspace-toml-fmt = craneLib.taploFmt {
src = pkgs.lib.sources.sourceFilesBySuffices src [ ".toml" ];
# taplo arguments can be further customized below as needed
# taploExtraArgs = "--config ./taplo.toml";
};
# Audit dependencies
my-workspace-audit = craneLib.cargoAudit {
inherit src advisory-db;
};
# Audit licenses
my-workspace-deny = craneLib.cargoDeny {
inherit src;
};
# Run tests with cargo-nextest
# Consider setting `doCheck = false` on other crate derivations
# if you do not want the tests to run twice
my-workspace-nextest = craneLib.cargoNextest (commonArgs // {
inherit cargoArtifacts;
partitions = 1;
partitionType = "count";
});
# Ensure that cargo-hakari is up to date
my-workspace-hakari = craneLib.mkCargoDerivation {
inherit src;
pname = "my-workspace-hakari";
cargoArtifacts = null;
doInstallCargoArtifacts = false;
buildPhaseCargoCommand = ''
cargo hakari generate --diff # workspace-hack Cargo.toml is up-to-date
cargo hakari manage-deps --dry-run # all workspace crates depend on workspace-hack
cargo hakari verify
'';
nativeBuildInputs = [
pkgs.cargo-hakari
];
};
};
packages = {
inherit my-cli my-server;
} // lib.optionalAttrs (!pkgs.stdenv.isDarwin) {
my-workspace-llvm-coverage = craneLibLLvmTools.cargoLlvmCov (commonArgs // {
inherit cargoArtifacts;
});
};
apps = {
my-cli = flake-utils.lib.mkApp {
drv = my-cli;
};
my-server = flake-utils.lib.mkApp {
drv = my-server;
};
};
devShells.default = craneLib.devShell {
# Inherit inputs from checks.
checks = self.checks.${system};
# Additional dev-shell environment variables can be set directly
# MY_CUSTOM_DEVELOPMENT_VAR = "something else";
# Extra inputs can be added here; cargo and rustc are provided by default.
packages = [
pkgs.cargo-hakari
];
};
});
}
To build a cargo project with a custom toolchain (e.g. WASM builds), run the following in a fresh directory:
nix flake init -t github:ipetkov/crane#custom-toolchain
Alternatively, if you have an existing project already, copy and paste the
following flake.nix
:
{
description = "Build a cargo project with a custom toolchain";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
flake-utils.url = "github:numtide/flake-utils";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, crane, flake-utils, rust-overlay, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ (import rust-overlay) ];
};
# NB: we don't need to overlay our custom toolchain for the *entire*
# pkgs (which would require rebuidling anything else which uses rust).
# Instead, we just want to update the scope that crane will use by appending
# our specific toolchain there.
craneLib = (crane.mkLib pkgs).overrideToolchain (p: p.rust-bin.stable.latest.default.override {
targets = [ "wasm32-wasi" ];
});
my-crate = craneLib.buildPackage {
src = craneLib.cleanCargoSource ./.;
strictDeps = true;
cargoExtraArgs = "--target wasm32-wasi";
# Tests currently need to be run via `cargo wasi` which
# isn't packaged in nixpkgs yet...
doCheck = false;
buildInputs = [
# Add additional build inputs here
] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [
# Additional darwin specific inputs can be set here
pkgs.libiconv
];
};
in
{
checks = {
inherit my-crate;
};
packages.default = my-crate;
apps.default = flake-utils.lib.mkApp {
drv = pkgs.writeShellScriptBin "my-app" ''
${pkgs.wasmtime}/bin/wasmtime run ${my-crate}/bin/custom-toolchain.wasm
'';
};
devShells.default = craneLib.devShell {
# Inherit inputs from checks.
checks = self.checks.${system};
# Extra inputs can be added here; cargo and rustc are provided by default
# from the toolchain that was specified earlier.
packages = [
];
};
});
}
To build a cargo project which uses another crate registry, run the following in a fresh directory:
nix flake init -t github:ipetkov/crane#alt-registry
Alternatively, if you have an existing project already:
- Ensure that declaration of the registry and its index url are present in
.cargo/config.toml
- Either commit
.cargo/config.toml
or ensure it is staged in git (git add -N .cargo/config.toml
) - Copy and paste the following
flake.nix
:
{
description = "Build a cargo project with alternative crate registries";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
flake-utils = {
url = "github:numtide/flake-utils";
};
};
outputs = { self, nixpkgs, crane, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
craneLibOrig = crane.mkLib pkgs;
craneLib = craneLibOrig.appendCrateRegistries [
# Automatically infer the download URL from the registry's index
#
# Note that this approach requires checking out the full index at the specified revision
# and adding a copy to the Nix store.
#
# Also note that the specified git revision _does not need to track updates to the index
# itself_ as long as the pinned revision contains the most recent version of the
# registry's `config.json` file. In other words, this commit revision only needs to be
# updated if the `config.json` file changes the download endpoint for this registry.
(craneLibOrig.registryFromGitIndex {
indexUrl = "https://github.com/Hirevo/alexandrie-index";
rev = "90df25daf291d402d1ded8c32c23d5e1498c6725";
fetchurlExtraArgs = {
# Extra parameters which will be passed to the fetchurl invocation for each crate
};
})
# If the registry in question is a sparse index, instead configure as
#(craneLibOrig.registryFromSparse {
# indexUrl = "https://index.crates.io";
# # where the sha256 is the sha256 of https://index.crates.io/config.json.
# configSha256 = "d16740883624df970adac38c70e35cf077a2a105faa3862f8f99a65da96b14a3";
# fetchurlExtraArgs = {
# # Extra parameters which will be passed to the fetchurl invocation for each crate
# };
#})
# As a more lightweight alternative, the `dl` endpoint of the registry's `config.json`
# file can be copied here to avoid needing to copy the index to the Nix store.
# (craneLibOrig.registryFromDownloadUrl {
# indexUrl = "https://github.com/Hirevo/alexandrie-index";
# dl = "https://crates.polomack.eu/api/v1/crates/{crate}/{version}/download";
# fetchurlExtraArgs = {
# # Extra parameters which will be passed to the fetchurl invocation for each crate
# };
# })
];
my-crate = craneLib.buildPackage {
src = craneLib.cleanCargoSource ./.;
strictDeps = true;
buildInputs = [
# Add additional build inputs here
pkgs.openssl
] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [
# Additional darwin specific inputs can be set here
pkgs.libiconv
pkgs.darwin.apple_sdk.frameworks.Security
];
# Specific to our example, but not always necessary in the general case.
nativeBuildInputs = [
pkgs.pkg-config
];
};
in
{
checks = {
# Build the crate as part of `nix flake check` for convenience
inherit my-crate;
};
packages.default = my-crate;
apps.default = flake-utils.lib.mkApp {
drv = my-crate;
};
devShells.default = craneLib.devShell {
# Inherit inputs from checks.
checks = self.checks.${system};
# Additional dev-shell environment variables can be set directly
# MY_CUSTOM_DEVELOPMENT_VAR = "something else";
# Extra inputs can be added here; cargo and rustc are provided by default.
packages = [
# pkgs.ripgrep
];
};
});
}
To build a cargo project while also compiling the standard library or other crates distributed with the Rust toolchain, run the following in a fresh directory:
nix flake init -t github:ipetkov/crane#build-std
Alternatively, if you have an existing project already, copy and paste the
following flake.nix
:
{
description = "Build a cargo project while also compiling the standard library";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
flake-utils.url = "github:numtide/flake-utils";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, crane, flake-utils, rust-overlay, ... }:
flake-utils.lib.eachSystem [ "x86_64-linux" ] (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ (import rust-overlay) ];
};
rustToolchainFor = p: p.rust-bin.selectLatestNightlyWith (toolchain: toolchain.default.override {
extensions = [ "rust-src" ];
targets = [ "x86_64-unknown-linux-gnu" ];
});
rustToolchain = rustToolchainFor pkgs;
# NB: we don't need to overlay our custom toolchain for the *entire*
# pkgs (which would require rebuidling anything else which uses rust).
# Instead, we just want to update the scope that crane will use by appending
# our specific toolchain there.
craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchainFor;
src = craneLib.cleanCargoSource ./.;
my-crate = craneLib.buildPackage {
inherit src;
strictDeps = true;
cargoVendorDir = craneLib.vendorMultipleCargoDeps {
inherit (craneLib.findCargoFiles src) cargoConfigs;
cargoLockList = [
./Cargo.lock
# Unfortunately this approach requires IFD (import-from-derivation)
# otherwise Nix will refuse to read the Cargo.lock from our toolchain
# (unless we build with `--impure`).
#
# Another way around this is to manually copy the rustlib `Cargo.lock`
# to the repo and import it with `./path/to/rustlib/Cargo.lock` which
# will avoid IFD entirely but will require manually keeping the file
# up to date!
"${rustToolchain.passthru.availableComponents.rust-src}/lib/rustlib/src/rust/library/Cargo.lock"
];
};
cargoExtraArgs = "-Z build-std --target x86_64-unknown-linux-gnu";
buildInputs = [
# Add additional build inputs here
];
};
in
{
checks = {
inherit my-crate;
};
packages.default = my-crate;
devShells.default = craneLib.devShell {
# Inherit inputs from checks.
checks = self.checks.${system};
# Extra inputs can be added here; cargo and rustc are provided by default
# from the toolchain that was specified earlier.
packages = [
];
};
});
}
To cross compile a rust project using oxalica/rust-overlay
, run the following
in a fresh directory:
nix flake init -t github:ipetkov/crane#cross-rust-overlay
Alternatively, if you have an existing project already, copy and paste the
following flake.nix
:
{
description = "Cross compiling a rust program using rust-overlay";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
flake-utils.url = "github:numtide/flake-utils";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { nixpkgs, crane, flake-utils, rust-overlay, ... }:
flake-utils.lib.eachDefaultSystem (localSystem:
let
# Replace with the system you want to build for
crossSystem = "aarch64-linux";
pkgs = import nixpkgs {
inherit crossSystem localSystem;
overlays = [ (import rust-overlay) ];
};
craneLib = (crane.mkLib pkgs).overrideToolchain (p: p.rust-bin.stable.latest.default);
# Note: we have to use the `callPackage` approach here so that Nix
# can "splice" the packages in such a way that dependencies are
# compiled for the appropriate targets. If we did not do this, we
# would have to manually specify things like
# `nativeBuildInputs = with pkgs.pkgsBuildHost; [ someDep ];` or
# `buildInputs = with pkgs.pkgsHostHost; [ anotherDep ];`.
#
# Normally you can stick this function into its own file and pass
# its path to `callPackage`.
crateExpression =
{ openssl
, libiconv
, lib
, pkg-config
, qemu
, stdenv
}:
craneLib.buildPackage {
src = craneLib.cleanCargoSource ./.;
strictDeps = true;
# Build-time tools which are target agnostic. build = host = target = your-machine.
# Emulators should essentially also go `nativeBuildInputs`. But with some packaging issue,
# currently it would cause some rebuild.
# We put them here just for a workaround.
# See: https://github.com/NixOS/nixpkgs/pull/146583
depsBuildBuild = [
qemu
];
# Dependencies which need to be build for the current platform
# on which we are doing the cross compilation. In this case,
# pkg-config needs to run on the build platform so that the build
# script can find the location of openssl. Note that we don't
# need to specify the rustToolchain here since it was already
# overridden above.
nativeBuildInputs = [
pkg-config
stdenv.cc
] ++ lib.optionals stdenv.buildPlatform.isDarwin [
libiconv
];
# Dependencies which need to be built for the platform on which
# the binary will run. In this case, we need to compile openssl
# so that it can be linked with our executable.
buildInputs = [
# Add additional build inputs here
openssl
];
# Tell cargo about the linker and an optional emulater. So they can be used in `cargo build`
# and `cargo run`.
# Environment variables are in format `CARGO_TARGET_<UPPERCASE_UNDERSCORE_RUST_TRIPLE>_LINKER`.
# They are also be set in `.cargo/config.toml` instead.
# See: https://doc.rust-lang.org/cargo/reference/config.html#target
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER = "${stdenv.cc.targetPrefix}cc";
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER = "qemu-aarch64";
# Tell cargo which target we want to build (so it doesn't default to the build system).
# We can either set a cargo flag explicitly with a flag or with an environment variable.
cargoExtraArgs = "--target aarch64-unknown-linux-gnu";
# CARGO_BUILD_TARGET = "aarch64-unknown-linux-gnu";
# These environment variables may be necessary if any of your dependencies use a
# build-script which invokes the `cc` crate to build some other code. The `cc` crate
# should automatically pick up on our target-specific linker above, but this may be
# necessary if the build script needs to compile and run some extra code on the build
# system.
HOST_CC = "${stdenv.cc.nativePrefix}cc";
TARGET_CC = "${stdenv.cc.targetPrefix}cc";
};
# Assuming the above expression was in a file called myCrate.nix
# this would be defined as:
# my-crate = pkgs.callPackage ./myCrate.nix { };
my-crate = pkgs.callPackage crateExpression { };
in
{
checks = {
inherit my-crate;
};
packages.default = my-crate;
apps.default = flake-utils.lib.mkApp {
drv = pkgs.writeScriptBin "my-app" ''
${pkgs.pkgsBuildBuild.qemu}/bin/qemu-aarch64 ${my-crate}/bin/cross-rust-overlay
'';
};
});
}
To build a cargo project with musl to crate statically linked binaries, run the following in a fresh directory:
nix flake init -t github:ipetkov/crane#cross-musl
Alternatively, if you have an existing project already, copy and paste the
following flake.nix
:
{
description = "Building static binaries with musl";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
flake-utils.url = "github:numtide/flake-utils";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { nixpkgs, crane, flake-utils, rust-overlay, ... }:
flake-utils.lib.eachSystem [ "x86_64-linux" ] (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ (import rust-overlay) ];
};
craneLib = (crane.mkLib pkgs).overrideToolchain (p: p.rust-bin.stable.latest.default.override {
targets = [ "x86_64-unknown-linux-musl" ];
});
my-crate = craneLib.buildPackage {
src = craneLib.cleanCargoSource ./.;
strictDeps = true;
CARGO_BUILD_TARGET = "x86_64-unknown-linux-musl";
CARGO_BUILD_RUSTFLAGS = "-C target-feature=+crt-static";
};
in
{
checks = {
inherit my-crate;
};
packages.default = my-crate;
});
}
To cross compiling a rust program for windows, run the following in a fresh directory:
nix flake init -t github:ipetkov/crane#cross-windows
Alternatively, if you have an existing project already, copy and paste the
following flake.nix
:
{
description = "Cross compiling a rust program for windows";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
fenix = {
url = "github:nix-community/fenix";
inputs.nixpkgs.follows = "nixpkgs";
};
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { nixpkgs, crane, fenix, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
toolchain = with fenix.packages.${system};
combine [
minimal.rustc
minimal.cargo
targets.x86_64-pc-windows-gnu.latest.rust-std
];
craneLib = (crane.mkLib pkgs).overrideToolchain toolchain;
my-crate = craneLib.buildPackage {
src = craneLib.cleanCargoSource ./.;
strictDeps = true;
doCheck = false;
CARGO_BUILD_TARGET = "x86_64-pc-windows-gnu";
# fixes issues related to libring
TARGET_CC = "${pkgs.pkgsCross.mingwW64.stdenv.cc}/bin/${pkgs.pkgsCross.mingwW64.stdenv.cc.targetPrefix}cc";
#fixes issues related to openssl
OPENSSL_DIR = "${pkgs.openssl.dev}";
OPENSSL_LIB_DIR = "${pkgs.openssl.out}/lib";
OPENSSL_INCLUDE_DIR = "${pkgs.openssl.dev}/include/";
depsBuildBuild = with pkgs; [
pkgsCross.mingwW64.stdenv.cc
pkgsCross.mingwW64.windows.pthreads
];
};
in
{
packages = {
inherit my-crate;
default = my-crate;
};
checks = {
inherit my-crate;
};
}
);
}
Trunk is a tool that allow you to build web apps using Rust and webassembly, including compiling scss, and distributing other assets.
Being a more specialized tool, it comes with some constraints that must be noted when using it in combination with crane:
- Your Toolchain must have the
wasm32-unknown-unknown
target installed (See: Custom toolchain) - For
craneLib.buildDepsOnly
to work you will need to set the build target (See: API Reference) craneLib.filterCargoSources
will remove html, css, your assets folder, so you need to modify the source filtering function (See: Source filtering)- You will need to set
wasm-bindgen-cli
to a version that matches your Cargo.lock file. (See examples)
For a quick-start run the following in a fresh directory:
nix flake init -t github:ipetkov/crane#trunk
Alternatively, if you have an existing project already, copy and paste the
following flake.nix
:
{
description = "Build a cargo project";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
flake-utils.url = "github:numtide/flake-utils";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, crane, flake-utils, rust-overlay, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ (import rust-overlay) ];
};
inherit (pkgs) lib;
rustToolchainFor = p: p.rust-bin.stable.latest.default.override {
# Set the build targets supported by the toolchain,
# wasm32-unknown-unknown is required for trunk
targets = [ "wasm32-unknown-unknown" ];
};
craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchainFor;
# When filtering sources, we want to allow assets other than .rs files
unfilteredRoot = ./.; # The original, unfiltered source
src = lib.fileset.toSource {
root = unfilteredRoot;
fileset = lib.fileset.unions [
# Default files from crane (Rust and cargo files)
(craneLib.fileset.commonCargoSources unfilteredRoot)
(lib.fileset.fileFilter
(file: lib.any file.hasExt [ "html" "scss" ])
unfilteredRoot
)
# Example of a folder for images, icons, etc
(lib.fileset.maybeMissing ./assets)
];
};
# Common arguments can be set here to avoid repeating them later
commonArgs = {
inherit src;
strictDeps = true;
# We must force the target, otherwise cargo will attempt to use your native target
CARGO_BUILD_TARGET = "wasm32-unknown-unknown";
buildInputs = [
# Add additional build inputs here
] ++ lib.optionals pkgs.stdenv.isDarwin [
# Additional darwin specific inputs can be set here
pkgs.libiconv
];
};
# Build *just* the cargo dependencies, so we can reuse
# all of that work (e.g. via cachix) when running in CI
cargoArtifacts = craneLib.buildDepsOnly (commonArgs // {
# You cannot run cargo test on a wasm build
doCheck = false;
});
# Build the actual crate itself, reusing the dependency
# artifacts from above.
# This derivation is a directory you can put on a webserver.
my-app = craneLib.buildTrunkPackage (commonArgs // {
inherit cargoArtifacts;
# The version of wasm-bindgen-cli here must match the one from Cargo.lock.
wasm-bindgen-cli = pkgs.wasm-bindgen-cli.override {
version = "0.2.93";
hash = "sha256-DDdu5mM3gneraM85pAepBXWn3TMofarVR4NbjMdz3r0=";
cargoHash = "sha256-birrg+XABBHHKJxfTKAMSlmTVYLmnmqMDfRnmG6g/YQ=";
# When updating to a new version comment out the above two lines and
# uncomment the bottom two lines. Then try to do a build, which will fail
# but will print out the correct value for `hash`. Replace the value and then
# repeat the process but this time the printed value will be for `cargoHash`
# hash = lib.fakeHash;
# cargoHash = lib.fakeHash;
};
});
# Quick example on how to serve the app,
# This is just an example, not useful for production environments
serve-app = pkgs.writeShellScriptBin "serve-app" ''
${pkgs.python3Minimal}/bin/python3 -m http.server --directory ${my-app} 8000
'';
in
{
checks = {
# Build the crate as part of `nix flake check` for convenience
inherit my-app;
# Run clippy (and deny all warnings) on the crate source,
# again, reusing the dependency artifacts from above.
#
# Note that this is done as a separate derivation so that
# we can block the CI if there are issues here, but not
# prevent downstream consumers from building our crate by itself.
my-app-clippy = craneLib.cargoClippy (commonArgs // {
inherit cargoArtifacts;
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
});
# Check formatting
my-app-fmt = craneLib.cargoFmt {
inherit src;
};
};
packages.default = my-app;
apps.default = flake-utils.lib.mkApp {
drv = serve-app;
};
devShells.default = craneLib.devShell {
# Inherit inputs from checks.
checks = self.checks.${system};
# Additional dev-shell environment variables can be set directly
# MY_CUSTOM_DEVELOPMENT_VAR = "something else";
# Extra inputs can be added here; cargo and rustc are provided by default.
packages = [
pkgs.trunk
];
};
});
}
Trunk is a tool that allow you to build web apps using Rust and webassembly, including compiling scss, and distributing other assets. It can be used in conjunction with any of Rust's web frameworks for the development of full stack web applications.
In this example we have a workspace with three members:
- client: a Yew application compiled using Trunk
- server: a Axum server built using Cargo
- shared: a library that contains types to be imported in both the client and server
For a quick-start run the following in a fresh directory:
nix flake init -t github:ipetkov/crane#trunk-workspace
Alternatively, if you have an existing project already, copy and paste the
following flake.nix
and modify it to build your workspace's packages:
{
description = "Build a cargo project";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
flake-utils.url = "github:numtide/flake-utils";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, crane, flake-utils, rust-overlay, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ (import rust-overlay) ];
};
inherit (pkgs) lib;
rustToolchainFor = p: p.rust-bin.stable.latest.default.override {
# Set the build targets supported by the toolchain,
# wasm32-unknown-unknown is required for trunk.
targets = [ "wasm32-unknown-unknown" ];
};
craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchainFor;
# When filtering sources, we want to allow assets other than .rs files
unfilteredRoot = ./.; # The original, unfiltered source
src = lib.fileset.toSource {
root = unfilteredRoot;
fileset = lib.fileset.unions [
# Default files from crane (Rust and cargo files)
(craneLib.fileset.commonCargoSources unfilteredRoot)
(lib.fileset.fileFilter
(file: lib.any file.hasExt [ "html" "scss" ])
unfilteredRoot
)
# Example of a folder for images, icons, etc
(lib.fileset.maybeMissing ./assets)
];
};
# Arguments to be used by both the client and the server
# When building a workspace with crane, it's a good idea
# to set "pname" and "version".
commonArgs = {
inherit src;
strictDeps = true;
buildInputs = [
# Add additional build inputs here
] ++ lib.optionals pkgs.stdenv.isDarwin [
# Additional darwin specific inputs can be set here
pkgs.libiconv
];
};
# Native packages
nativeArgs = commonArgs // {
pname = "trunk-workspace-native";
};
# Build *just* the cargo dependencies, so we can reuse
# all of that work (e.g. via cachix) when running in CI
cargoArtifacts = craneLib.buildDepsOnly nativeArgs;
# Simple JSON API that can be queried by the client
myServer = craneLib.buildPackage (nativeArgs // {
inherit cargoArtifacts;
# The server needs to know where the client's dist dir is to
# serve it, so we pass it as an environment variable at build time
CLIENT_DIST = myClient;
});
# Wasm packages
# it's not possible to build the server on the
# wasm32 target, so we only build the client.
wasmArgs = commonArgs // {
pname = "trunk-workspace-wasm";
cargoExtraArgs = "--package=client";
CARGO_BUILD_TARGET = "wasm32-unknown-unknown";
};
cargoArtifactsWasm = craneLib.buildDepsOnly (wasmArgs // {
doCheck = false;
});
# Build the frontend of the application.
# This derivation is a directory you can put on a webserver.
myClient = craneLib.buildTrunkPackage (wasmArgs // {
pname = "trunk-workspace-client";
cargoArtifacts = cargoArtifactsWasm;
# Trunk expects the current directory to be the crate to compile
preBuild = ''
cd ./client
'';
# After building, move the `dist` artifacts and restore the working directory
postBuild = ''
mv ./dist ..
cd ..
'';
# The version of wasm-bindgen-cli here must match the one from Cargo.lock.
wasm-bindgen-cli = pkgs.wasm-bindgen-cli.override {
version = "0.2.93";
hash = "sha256-DDdu5mM3gneraM85pAepBXWn3TMofarVR4NbjMdz3r0=";
cargoHash = "sha256-birrg+XABBHHKJxfTKAMSlmTVYLmnmqMDfRnmG6g/YQ=";
# When updating to a new version comment out the above two lines and
# uncomment the bottom two lines. Then try to do a build, which will fail
# but will print out the correct value for `hash`. Replace the value and then
# repeat the process but this time the printed value will be for `cargoHash`
# hash = lib.fakeHash;
# cargoHash = lib.fakeHash;
};
});
in
{
checks = {
# Build the crate as part of `nix flake check` for convenience
inherit myServer myClient;
# Run clippy (and deny all warnings) on the crate source,
# again, reusing the dependency artifacts from above.
#
# Note that this is done as a separate derivation so that
# we can block the CI if there are issues here, but not
# prevent downstream consumers from building our crate by itself.
my-app-clippy = craneLib.cargoClippy (commonArgs // {
inherit cargoArtifacts;
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
# Here we don't care about serving the frontend
CLIENT_DIST = "";
});
# Check formatting
my-app-fmt = craneLib.cargoFmt commonArgs;
};
apps.default = flake-utils.lib.mkApp {
name = "server";
drv = myServer;
};
devShells.default = craneLib.devShell {
# Inherit inputs from checks.
checks = self.checks.${system};
shellHook = ''
export CLIENT_DIST=$PWD/client/dist;
'';
# Extra inputs can be added here; cargo and rustc are provided by default.
packages = [
pkgs.trunk
];
};
});
}
In addition to Unit and Integration tests, you can also write tests that interact with your application as a real user would. That technique is called End to End(E2E) testing.
In this example we have a workspace with two members:
- server: a web server that uses Axum for HTTP and Sqlx connect to an instance of PostgreSQL
- e2e: a end-to-end test "script" that drives Firefox into interacting with the sever
Quick-start an E2E project in a fresh directory with:
nix flake init -t github:ipetkov/crane#end-to-end-testing
Alternatively, if you have an existing project already, copy and paste the
following flake.nix
and modify it to build your workspace's packages:
{
description = "Example E2E testing";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
flake-utils.url = "github:numtide/flake-utils";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { nixpkgs, crane, flake-utils, rust-overlay, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ (import rust-overlay) ];
};
inherit (pkgs) lib;
craneLib = (crane.mkLib pkgs).overrideToolchain (p: p.rust-bin.stable.latest.default);
src = craneLib.cleanCargoSource ./.;
workspace = craneLib.buildPackage {
inherit src;
doCheck = false;
nativeBuildInputs = lib.optionals pkgs.stdenv.isDarwin
(with pkgs.darwin.apple_sdk.frameworks; [
pkgs.libiconv
CoreFoundation
Security
SystemConfiguration
]);
};
# The script inlined for brevity, consider extracting it
# so that it becomes independent of nix
runE2ETests = pkgs.runCommand "e2e-tests"
{
nativeBuildInputs = with pkgs; [
retry
curl
geckodriver
firefox
cacert
postgresql
];
} ''
wait-for-connection() {
timeout 5s \
retry --until=success --delay "1" -- \
curl -s "$@"
}
initdb postgres-data
pg_ctl --pgdata=postgres-data --options "-c unix_socket_directories=$PWD" start
export DATABASE_URL="postgres:///postgres?host=$PWD"
psql "$DATABASE_URL" <<EOF
CREATE TABLE users(name TEXT);
EOF
${workspace}/bin/server &
wait-for-connection --fail localhost:8000
# Firefox likes to write to $HOME
HOME="$(mktemp -d)" geckodriver &
wait-for-connection localhost:4444
${workspace}/bin/e2e_tests
touch $out
'';
pkgsSupportsPackage = pkg:
(lib.elem system pkg.meta.platforms) && !(lib.elem system pkg.meta.badPlatforms);
in
{
checks = {
inherit workspace;
# Firefox is broken in some platforms (namely "aarch64-apple-darwin"), skip those
} // (lib.optionalAttrs (pkgsSupportsPackage pkgs.firefox) {
inherit runE2ETests;
});
devShells.default = pkgs.mkShell {
BuildInputs = with pkgs; [
rustc
cargo
] ++ (lib.optionals (!pkgs.stdenv.isDarwin) [
geckodriver
firefox
]);
};
});
}
To build a cargo project which depends on the SQLx crate, run the following in a fresh directory:
nix flake init -t github:ipetkov/crane#sqlx
Alternatively, if you have an existing project already, copy and paste the
following flake.nix
:
{
description = "Build a cargo project which uses SQLx";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, crane, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
inherit (pkgs) lib;
craneLib = crane.mkLib pkgs;
unfilteredRoot = ./.; # The original, unfiltered source
src = lib.fileset.toSource {
root = unfilteredRoot;
fileset = lib.fileset.unions [
# Default files from crane (Rust and cargo files)
(craneLib.fileset.commonCargoSources unfilteredRoot)
# Include all the .sql migrations as well
./migrations
];
};
# Common arguments can be set here to avoid repeating them later
commonArgs = {
inherit src;
strictDeps = true;
nativeBuildInputs = [
pkgs.pkg-config
];
buildInputs = [
# Add additional build inputs here
pkgs.openssl
] ++ lib.optionals pkgs.stdenv.isDarwin [
# Additional darwin specific inputs can be set here
pkgs.libiconv
pkgs.darwin.apple_sdk.frameworks.Security
];
# Additional environment variables can be set directly
# MY_CUSTOM_VAR = "some value";
};
# Build *just* the cargo dependencies, so we can reuse
# all of that work (e.g. via cachix) when running in CI
cargoArtifacts = craneLib.buildDepsOnly commonArgs;
# Build the actual crate itself, reusing the dependency
# artifacts from above.
my-crate = craneLib.buildPackage (commonArgs // {
inherit cargoArtifacts;
nativeBuildInputs = (commonArgs.nativeBuildInputs or [ ]) ++ [
pkgs.sqlx-cli
];
preBuild = ''
export DATABASE_URL=sqlite:./db.sqlite3
sqlx database create
sqlx migrate run
'';
});
in
{
checks = {
# Build the crate as part of `nix flake check` for convenience
inherit my-crate;
};
packages = {
default = my-crate;
inherit my-crate;
};
devShells.default = craneLib.devShell {
# Inherit inputs from checks.
checks = self.checks.${system};
# Additional dev-shell environment variables can be set directly
# MY_CUSTOM_DEVELOPMENT_VAR = "something else";
# Extra inputs can be added here; cargo and rustc are provided by default.
packages = [
pkgs.sqlx-cli
];
};
});
}
Source filtering
Nix considers that a derivation must be rebuilt whenever any of its inputs change, including all source files passed into the build. Unfortunately, this means that changes to any "irrelevant" files (such as the project README) would end up rebuilding the project even if the final outputs don't actually care about their contents!
Source filtering is a technique Nix employs that allows for better caching by programmatically filtering out files which are known to not apply to the build before the inputs are hashed.
A default source cleaner is available via craneLib.cleanCargoSource
: it cleans
a source tree to omit things like version control directories as well omit any
non-Rust/non-cargo related files. It can be used like so:
craneLib.buildPackage {
# other attributes omitted
src = craneLib.cleanCargoSource ./.;
}
It is possible to customize the filter to use when cleaning the source by
leveraging craneLib.filterCargoSources
. By default this filter will only keep
files whose names end with .rs
or .toml
. Though it is possible to compose it
with other filters, especially if it is necessary to include additional files
which it might otherwise omit:
let
# Only keeps markdown files
markdownFilter = path: _type: builtins.match ".*md$" path != null;
markdownOrCargo = path: type:
(markdownFilter path type) || (craneLib.filterCargoSources path type);
in
craneLib.buildPackage {
# other attributes omitted
src = lib.cleanSourceWith {
src = ./.; # The original, unfiltered source
filter = markdownOrCargo;
name = "source"; # Be reproducible, regardless of the directory name
};
}
Fileset filtering
A more composable alternative to source filtering is using filesets:
let
unfilteredRoot = ./.; # The original, unfiltered source
src = lib.fileset.toSource {
root = unfilteredRoot;
fileset = lib.fileset.unions [
# Default files from crane (Rust and cargo files)
(craneLib.fileset.commonCargoSources unfilteredRoot)
# Also keep any markdown files
(lib.fileset.fileFilter (file: file.hasExt == "md") unfilteredRoot)
# Example of a folder for images, icons, etc
(lib.fileset.maybeMissing ./assets)
];
};
in
craneLib.buildPackage {
# other attributes omitted
inherit src;
}
Local Development
Nix shells (or development shells) are extremely powerful when it comes to locally developing with the exact same dependencies used when building packages.
To get started, declare a default devShell
in flake.nix
using
craneLib.devShell
and run nix develop
in the
project directory. Then, you can use something like
direnv
or
nix-direnv
to automatically
enter and exit a development shell when you enter or exit the project
directory!
Sample flake.nix
:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, crane, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
craneLib = crane.mkLib pkgs;
my-crate = craneLib.buildPackage {
src = craneLib.cleanCargoSource ./.;
buildInputs = [
# Add additional build inputs here
] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [
# Additional darwin specific inputs can be set here
pkgs.libiconv
];
# Additional environment variables can be set directly
# MY_CUSTOM_VAR = "some value";
};
in
{
packages.default = my-crate;
devShells.default = craneLib.devShell {
# Additional dev-shell environment variables can be set directly
MY_CUSTOM_DEV_URL = "http://localhost:3000";
# Automatically inherit any build inputs from `my-crate`
inputsFrom = [ my-crate ];
# Extra inputs (only used for interactive development)
# can be added here; cargo and rustc are provided by default.
packages = [
pkgs.cargo-audit
pkgs.cargo-watch
];
};
});
}
Then, after integrating direnv into your shell:
echo "use flake" > .envrc
direnv allow
Custom cargo commands
Although it is possible to customize exactly what build commands and flags are
used by the provided functions like buildPackage
, or cargoBuild
, sometimes
it is useful to encapsulate a cargo invocation that crane does not know about.
Doing so allows that helper function to be used across different crates, or even
different Nix flakes without having to duplicate the logic in multiple build
definitions.
mkCargoDerivation
allows building such extensions. Below is a short example to
illustrate the approach. The reference also
explores the inputs and behavior of mkCargoDerivation
in greater depth.
{ pkgs, craneLib }:
# Let's assume we want to add a helper for a fictitious `cargo awesome` command
let
cargoAwesome = {
cargoArtifacts,
cargoAwesomeExtraArgs ? "", # Arguments that are generally useful default
cargoExtraArgs ? "" # Other cargo-general flags (e.g. for features or targets)
}@origArgs: let
# Clean the original arguments for good hygiene (i.e. so the flags specific
# to this helper don't pollute the environment variables of the derivation)
args = builtins.removeAttrs origArgs [
"cargoAwesomeExtraArgs"
"cargoExtraArgs"
];
in
craneLib.mkCargoDerivation (args // {
# Additional overrides we want to explicitly set in this helper
# Require the caller to specify cargoArtifacts we can use
inherit cargoArtifacts;
# A suffix name used by the derivation, useful for logging
pnameSuffix = "-awesome";
# Set the cargo command we will use and pass through the flags
buildPhaseCargoCommand = "cargo awesome ${cargoExtraArgs} ${cargoAwesomeExtraArgs}";
# Append the `cargo-awesome` package to the nativeBuildInputs set by the
# caller (or default to an empty list if none were set)
nativeBuildInputs = (args.nativeBuildInputs or [ ]) ++ [ pkgs.cargo-awesome ];
});
in
cargoAwesome {
src = craneLib.cleanCargoSource ./.;
}
Customizing builds
All derivations, whether they are configured through buildPackage
,
cargoBuild
, or even mkCargoDerivation
, eventually delegate to
mkDerivation
which is defined by
nixpkgs.
At its heart, mkDerivation
builds up a big bash
script which is executed by
the builder. Inputs are added to the execution $PATH
, libraries are added to
include paths, and all other variables are set as shell variables. But these
scripts also come with a small framework for running various different
phases. Many of
these phases also come with their own hooks which are shell functions which
can be subscribed to execute before and/or after a particular phase has run.
Although build phases and their hooks allow for easily extending and customizing the build instructions for a particular derivation, it can become difficult to identify exactly where a bit of logic should execute. The following are a good set of resources to consult when in doubt:
- The nixpkgs manual for describing the default set of build phases and their hooks
- The crane API reference for additional hooks it introduces
- Setting
NIX_DEBUG
to a non-zero value will cause the builder to print out various variables and commands it will run (increasing values will increase the verbosity). - When all else fails source for the generic build scripts themselves can be useful
All that out of the way, here's a quick example of how to use the build phases and hooks to customize a particular build:
craneLib.buildPackage {
src = craneLib.cleanCargoSource ./.;
# Define a list of function names to execute before the `configurePhase` runs
preConfigurePhases = [
"foo"
"bar"
];
# Define the functions themselves
foo = ''
# double the amount of rust test threads we can use
# Note that crane will set these defaults as a `postPatchHook` which
# should have already run by the time the preConfigurePhases are called
export RUST_TEST_THREADS=$((RUST_TEST_THREADS * 2))
'';
bar = ''
# decrement by one test thread if running in release mode
if [[ "${CARGO_PROFILE}" == "release" ]]; then
export RUST_TEST_THREADS=$((RUST_TEST_THREADS - 2))
fi
'';
# Lastly, add postInstall to install additional items after
# the default installPhase has run and installed the package binaries
postInstall = ''
echo "hello world" > $out/hello.txt
# also install the README.md for good measure
cp README.md $out/
'';
}
Overriding derivations
Sometimes it is useful for a downstream consumer of a derivation to override
portions of its behavior (such as swapping out a dependency with another
customized package, or to perhaps opt-in or opt-out of additional behavior).
There are two main techniques to achieve this defined by nixpkgs
: using
.override
and .overrideAttrs
.
Neither of these are specific to crane
, but are documented here as a general
primer.
.override
The .override
attribute comes from makeOverridable
from nixpkgs
, which is
automatically invoked by callPackage
. Normally using .override
only changes
the parameters made available to the function which prepares the derivation, but
does not alter the derivation's attributes directly:
# my-crate.nix
{ craneLib
, lib
, withFoo ? true
, withBar ? false
}:
craneLib.buildPackage {
src = craneLib.cleanCargoSource ./..;
strictDeps = true;
cargoExtraArgs =
(lib.optionalString withFoo "--features foo") +
(lib.optionalString withBar "--features bar");
}
# flake.nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, crane, fenix, flake-utils, advisory-db, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ (import rust-overlay) ];
};
craneLib = crane.mkLib pkgs;
my-crate = pkgs.callPackage ./my-crate.nix {
inherit craneLib;
};
in
{
packages = {
# The default definition
default = my-crate;
# Ensure all additional options are enabled
my-crate-all = my-crate.override {
withBar = true;
};
# Disable all optional functionality
my-crate-minimal = my-crate.override {
withFoo = false;
};
# Use a different `craneLib` instantiation: one with a nightly compiler
my-crate-nightly = my-crate.override {
craneLib = craneLib.overrideToolchain (p: p.rust-bin.nightly.latest.default);
};
};
});
}
.overrideAttrs
The .overrideAttrs
attribute comes from mkDerivation
(which all crane
APIs
eventually call) and it allows changing what is passed into mkDerivation
itself (i.e. this does change derivation attributes). It is a much more low
level operation, and although it can be used to achieve the same things
possible via .override
, it may be more cumbersome to plumb the changes
through.
Note that .overrideAttrs
will not change what inputs crane
APIs see,
as it affects the derivation produced after those APIs have finished running.
If you need to change behavior that way, consider using a combination of
callPackage
and .override
.
# flake.nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
};
outputs = { self, nixpkgs, crane, fenix, flake-utils, advisory-db, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
craneLib = crane.mkLib pkgs;
my-crate = craneLib.buildPackage {
src = craneLib.cleanCargoSource ./.;
strictDeps = true;
};
in
{
packages = {
# The default definition
default = my-crate;
# Perform a build with debug logging enabled
my-crate-debug = my-crate.overrideAttrs (old: {
NIX_DEBUG = 10;
});
};
});
}
Patching sources of dependency crates
Sometimes it is useful to patch the sources of dependency crates without needing to wait for an upstream release to include a necessary change, or without needing to use a custom git fork.
The vendorCargoDeps
, vendorCargoRegistries
, vendorGitDeps
, and
vendorMultipleCargoDeps
APIs support arbitrary overrides (i.e. patching) at
the individual crate/repo level when vendoring sources. Checkout their
respective API documentation for more details, but below is a short quick start
example:
let
baseArgs = {
src = craneLib.cleanCargoSource ./.;
};
isNumCpusRepo = p: lib.hasPrefix
"git+https://github.com/seanmonstar/num_cpus.git"
p.source;
isTag1_13_1 = p: lib.hasInfix
"tag=v1.13.1"
p.source;
cargoVendorDir = craneLib.vendorCargoDeps (baseArgs // {
# Use this function to override crates coming from git dependencies
overrideVendorGitCheckout = ps: drv:
# For example, patch a specific repository and tag, in this case num_cpus-1.13.1
if lib.any (p: (isNumCpusRepo p) && (isTag1_13_1 p)) ps then
drv.overrideAttrs (_old: {
# Specifying an arbitrary patch to apply
patches = [
./0001-patch-num-cpus.patch
];
# Similarly we can also run additional hooks to make changes
postPatch = ''
echo running some arbitrary command to make modifications
'';
})
else
# Nothing to change, leave the derivations as is
drv;
# Use this function to override crates coming from any registry checkout
overrideVendorCargoPackage = p: drv:
# For example, patch a specific crate, in this case byteorder-1.5.0
if p.name == "byteorder" && p.version == "1.5.0" then
drv.overrideAttrs (_old: {
# Specifying an arbitrary patch to apply
patches = [
./0001-patch-byteorder.patch
];
# Similarly we can also run additional hooks to make changes
postPatch = ''
echo running some arbitrary command to make modifications
'';
})
else
# Nothing to change, leave the derivations as is
drv;
});
commonArgs = baseArgs // {
inherit cargoVendorDir;
};
in
craneLib.buildPackage commonArgs
API Documentation
mkLib
mkLib :: pkgs -> set
Creates a lib
instance bound to the specified (and instantiated) pkgs
set.
This is a convenience escape hatch in case you want to use your own custom
instantiation of nixpkgs with the overlays you may need.
mkLib (import inputs.nixpkgs { system = "armv7l-linux"; })
Note that if you wish to override a particular package without having to overlay
it across all of nixpkgs, consider using overrideScope
:
(mkLib pkgs).overrideScope (final: prev: {
cargo-tarpaulin = myCustomCargoTarpaulinVersion;
})
To overlay an entire rust toolchain (e.g. cargo
, rustc
, clippy
, rustfmt
,
etc.) consider using overrideToolchain
.
craneLib
craneLib
represents an instantiated value crated by mkLib
above.
craneLib.appendCrateRegistries
appendCrateRegistries :: [registry mapping] -> new lib
Creates a new lib
instance which will make additional registries available for
use when downloading crate sources. Each entry can be defined using:
registryFromDownloadUrl
: if you know the exactdl
URL as defined in the registry'sconfig.json
fileregistryFromGitIndex
: if you would like the download URL to be inferred from the index's source directly.registryFromSparse
: if you would like the download URL to be inferred from the index's source directly, and the index is a sparse index.
See the documentation on each function for more specifics.
newLib = craneLib.appendCrateRegistries [
(craneLib.registryFromDownloadUrl {
indexUrl = "https://github.com/rust-lang/crates.io-index";
dl = "https://static.crates.io/crates";
fetchurlExtraArgs = {};
})
# Or, alternatively
(craneLib.registryFromGitIndex {
indexUrl = "https://github.com/Hirevo/alexandrie-index";
rev = "90df25daf291d402d1ded8c32c23d5e1498c6725";
fetchurlExtraArgs = {};
})
# Or even
(lib.registryFromSparse {
indexUrl = "https://index.crates.io/config.json";
configSha256 = "1cxgzdm1ipqmgwnq7kgym92axna7pfyhgfla63vl7dvydwn3m52v";
fetchurlExtraArgs = {};
})
];
craneLib.buildDepsOnly
buildDepsOnly :: set -> drv
Create a derivation which will only build all dependencies of a cargo workspace.
Useful for splitting up cargo projects into two derivations: one which only builds dependencies and needs to be rebuilt when a Cargo.lock file changes, and another which inherits the cargo artifacts from the first and (quickly) builds just the application itself.
The exact cargo commands being run (or the arguments passed into it) can be
easily updated to suit your needs. By default all artifacts from running cargo {check,build,test}
will be cached.
In addition to all default and overridden values being set as documented below,
all derivation attributes are delegated to mkCargoDerivation
, and can be used
to influence its behavior.
cargoArtifacts
: set tonull
since this is our entry point for generating cargo artifactsdoInstallCargoArtifacts
: set totrue
pnameSuffix
: set to"-deps"
src
: set to the result ofmkDummySrc
after applying the arguments set. This ensures that we do not need to rebuild the cargo artifacts derivation whenever the application source changes.CRANE_BUILD_DEPS_ONLY
is exported as an environment variable, in case this is handy for scripts or hooks which may want to customize how they run
Optional attributes
buildPhaseCargoCommand
: A command to run during the derivation's build phase. Pre and post build hooks will automatically be run.- Default value:
"${cargoCheckCommand} ${cargoExtraArgs}\n${cargoBuildCommand} ${cargoExtraArgs}"
- Default value:
cargoBuildCommand
: A cargo (build) invocation to run during the derivation's build phase- Default value:
"cargo build --profile release"
CARGO_PROFILE
can be set on the derivation to alter which cargo profile is selected; setting it to""
will omit specifying a profile altogether.
- Default value:
cargoCheckCommand
: A cargo (check) invocation to run during the derivation's build phase (in order to cache additional artifacts)- Default value:
"cargo check --profile release ${cargoCheckExtraArgs}"
CARGO_PROFILE
can be set on the derivation to alter which cargo profile is selected; setting it to""
will omit specifying a profile altogether.
- Default value:
cargoCheckExtraArgs
: additional flags to be passed in thecargoCheckCommand
invocation- Default value:
"--all-targets"
ifdoCheck
is set to true,""
otherwise
- Default value:
cargoExtraArgs
: additional flags to be passed in the cargo invocation (e.g. enabling specific features)- Default value:
"--locked"
- Default value:
cargoTestCommand
: A cargo invocation to run during the derivation's check phase- Default value:
"cargo test --profile release"
CARGO_PROFILE
can be set on the derivation to alter which cargo profile is selected; setting it to""
will omit specifying a profile altogether.
- Default value:
cargoTestExtraArgs
: additional flags to be passed in thecargoTestCommand
invocation (e.g. enabling specific tests)- Default value:
"--no-run"
- Default value:
cargoVendorDir
: A path (or derivation) of vendored cargo sources which can be consumed without network access. Directory structure should basically follow the output ofcargo vendor
.- Default value: the result of
vendorCargoDeps
after applying the arguments set (with the respective default values)
- Default value: the result of
checkPhaseCargoCommand
: A command to run during the derivation's check phase. Pre and post check hooks will automatically be run.- Default value:
"${cargoTestCommand} ${cargoExtraArgs}"
- Default value:
doCheck
: whether the derivation's check phase should be run- Default value:
true
- Default value:
dummySrc
: the "dummy" source to use when building this derivation. Automatically derived if not passed in- Default value:
mkDummySrc args.src
- Default value:
pname
: package name of the derivation- Default value: inherited from calling
crateNameFromCargoToml
- Default value: inherited from calling
version
: version of the derivation- Default value: inherited from calling
crateNameFromCargoToml
- Default value: inherited from calling
Remove attributes
The following attributes will be removed before being lowered to
mkCargoDerivation
. If you absolutely need these attributes present as
environment variables during the build, you can bring them back via
.overrideAttrs
.
cargoBuildCommand
cargoCheckCommand
cargoCheckExtraArgs
cargoExtraArgs
cargoTestCommand
cargoTestExtraArgs
dummySrc
outputHashes
outputs
craneLib.buildPackage
buildPackage :: set -> drv
A(n opinionated) version of mkCargoDerivation
which will install to the output
any binaries which were built by cargo in this invocation. All options
understood by mkCargoDerivation
apply here as well, with the only difference
being some additional book keeping necessary to log cargo's results and
subsequently install from that log.
Note that only bin
, cdylib
, dylib
, and staticlib
, targets will be installed by
default (namely rlib
targets will be ignored), though it is possible to adjust
the behavior by changing the installPhaseCommand
or registering additional
install hooks.
Optional attributes
buildPhaseCargoCommand
: A command to run during the derivation's build phase. Pre and post build hooks will automatically be run.- Default value:
cargoBuildCommand
will be invoked along withcargoExtraArgs
passed in, except cargo's build steps will also be captured and written to a log so that it can be used to find the build binaries. - Note that the default install hook assumes that the build phase will create
a log of cargo's build results. If you wish to customize this command
completely, make sure that cargo is run with
--message-format json-render-diagnostics
and the standard output captured and saved to a file. ThecargoBuildLog
shell variable should point to this log.
- Default value:
cargoArtifacts
: A path (or derivation) which contains an existing cargotarget
directory, which will be reused at the start of the derivation. Useful for caching incremental cargo builds.- Default value: the result of
buildDepsOnly
after applying the arguments set (with the respective default values). installPhase
andinstallPhaseCommand
will be removed, and no installation hooks will be run
- Default value: the result of
cargoBuildCommand
: A cargo invocation to run during the derivation's build phase- Default value:
"cargo build --profile release"
CARGO_PROFILE
can be set on the derivation to alter which cargo profile is selected; setting it to""
will omit specifying a profile altogether.
- Default value:
cargoExtraArgs
: additional flags to be passed in the cargo invocation (e.g. enabling specific features)- Default value:
"--locked"
- Default value:
cargoTestCommand
: A cargo invocation to run during the derivation's check phase- Default value:
"cargo test --profile release"
CARGO_PROFILE
can be set on the derivation to alter which cargo profile is selected; setting it to""
will omit specifying a profile altogether.
- Default value:
cargoTestExtraArgs
: additional flags to be passed in thecargoTestCommand
invocation (e.g. enabling specific tests)- Default value:
""
- Default value:
doCheck
: whether the derivation's check phase should be run- Default value:
true
- Default value:
doInstallCargoArtifacts
: controls whether cargo'starget
directory should be copied as an output- Default value:
false
- Default value:
installPhaseCommand
: the command(s) which are expected to install the derivation's outputs.- Default value: will look for a cargo build log and install all binary targets listed there
Remove attributes
The following attributes will be removed before being lowered to
mkCargoDerivation
. If you absolutely need these attributes present as
environment variables during the build, you can bring them back via
.overrideAttrs
.
cargoBuildCommand
cargoExtraArgs
cargoTestCommand
cargoTestExtraArgs
outputHashes
Native build dependencies and included hooks
The following hooks are automatically added as native build inputs:
installFromCargoBuildLogHook
removeReferencesToVendoredSourcesHook
craneLib.buildTrunkPackage
buildTrunkPackage :: set -> drv
Create a derivation which will build a distributable directory for a WASM application.
Except where noted below, all derivation attributes are delegated to
mkCargoDerivation
, and can be used to influence its behavior.
Optional attributes
buildPhaseCargoCommand
: A command to run during the derivation's build phase. Pre and post build hooks will automatically be run.- Default value:
trunk build
will be invoked along withtrunkExtraArgs
,trunkExtraBuildArgs
, andtrunkIndexpath
passed in. If$CARGO_PROFILE
is set torelease
then the--release
flag will also be set for the build
- Default value:
cargoArtifacts
: A path (or derivation) which contains an existing cargotarget
directory, which will be reused at the start of the derivation. Useful for caching incremental cargo builds.- Default value: the result of
buildDepsOnly
after applying the arguments set (with the respective default values). CARGO_BUILD_TARGET
will be set to"wasm32-unknown-unknown"
if not specified.doCheck
will be set tofalse
if not specified.installPhase
andinstallPhaseCommand
will be removed (in favor of their default values provided bybuildDepsOnly
)
- Default value: the result of
installPhaseCommand
: the command(s) which are expected to install the derivation's outputs.- Default value: will install trunk's
dist
output directory
- Default value: will install trunk's
trunkExtraArgs
pass additional arguments totrunk
- Default value:
""
- Default value:
trunkExtraBuildArgs
pass additional arguments totrunk build
- Default value:
""
- Default value:
trunkIndexPath
A path to the index.html of your trunk project- Default value:
"./index.html"
- Default value:
wasm-bindgen-cli
The package used to satisfy thewasm-bindgen-cli
dependency oftrunk
, the version used here must match the version ofwasm-bindgen
in theCargo.lock
file of your project exactly.- Default value:
pkgs.wasm-bindgen-cli
- Default value:
Remove attributes
The following attributes will be removed before being lowered to
mkCargoDerivation
. If you absolutely need these attributes present as
environment variables during the build, you can bring them back via
.overrideAttrs
.
trunkExtraArgs
trunkExtraBuildArgs
trunkIndexPath
Native build dependencies and included hooks
The following hooks are automatically added as native build inputs:
binaryen
dart-sass
trunk
craneLib.cargoAudit
cargoAudit :: set -> drv
Create a derivation which will run a cargo audit
invocation in a cargo
workspace.
Except where noted below, all derivation attributes are delegated to
mkCargoDerivation
, and can be used to influence its behavior.
buildPhaseCargoCommand
will be set to runcargo audit -n -d ${advisory-db}
in the workspace.cargoArtifacts
will be set tonull
as they are not neededcargoVendorDir
will be set tonull
as it is not neededdoInstallCargoArtifacts
is disabledpnameSuffix
will be set to"-audit"
src
will be filtered to only keepCargo.lock
files
Required attributes
advisory-db
: A path (or derivation) which contains the advisory database- It is possible to track the advisory database as a flake input and avoid having to manually update hashes or specific revisions to check out
src
: The project source to audit, it must contain aCargo.lock
file- Note that the source will internally be filtered to omit any files besides
Cargo.lock
. This avoids having to audit the project again until either the advisory database or the dependencies change.
- Note that the source will internally be filtered to omit any files besides
Optional attributes
cargoAuditExtraArgs
: additional flags to be passed in the cargo-audit invocation- Default value:
"--ignore yanked"
- Default value:
pname
: the name of the derivation; will not be introspected from aCargo.toml
file- Default value:
"crate"
- Default value:
version
: the version of the derivation, will not be introspected from aCargo.toml
file- Default value:
"0.0.0"
- Default value:
Native build dependencies
The cargo-audit
package is automatically appended as a native build input to any
other nativeBuildInputs
specified by the caller.
Remove attributes
The following attributes will be removed before being lowered to
mkCargoDerivation
. If you absolutely need these attributes present as
environment variables during the build, you can bring them back via
.overrideAttrs
.
cargoAuditExtraArgs
craneLib.cargoDeny
cargoDeny :: set -> drv
Create a derivation which will run a cargo deny
invocation in a cargo
workspace.
Note that although cargo deny
can serve as a replacement for cargo audit
,
craneLib.cargoDeny
does not expose this functionality because cargo deny
requires the full source tree, rather than working from just the Cargo.lock
file, meaning it will be re-run when any source file changes, rather than only
when dependencies change.
Except where noted below, all derivation attributes are delegated to
mkCargoDerivation
, and can be used to influence its behavior.
buildPhaseCargoCommand
will be set to runcargo --offline $cargoExtraArgs deny $cargoDenyExtraArgs check $cargoDenyChecks
in the workspace.cargoArtifacts
will be set tonull
doInstallCargoArtifacts
will be set tofalse
pnameSuffix
will be set to"-deny"
Optional attributes
cargoDenyChecks
: check types to run- Default value:
"bans licenses sources"
- Default value:
cargoDenyExtraArgs
: additional flags to be passed in the cargo-deny invocation- Default value:
""
- Default value:
cargoExtraArgs
: additional flags to be passed in the cargo invocation- Default value:
""
- Default value:
Native build dependencies
The cargo-deny
package is automatically appended as a native build input to any
other nativeBuildInputs
specified by the caller.
Remove attributes
The following attributes will be removed before being lowered to
mkCargoDerivation
. If you absolutely need these attributes present as
environment variables during the build, you can bring them back via
.overrideAttrs
.
cargoDenyExtraArgs
cargoExtraArgs
craneLib.cargoBuild
cargoBuild :: set -> drv
Create a derivation which will run a cargo build
invocation in a cargo
workspace. Consider using buildPackage
if all you need is to build the
workspace and install the resulting application binaries.
Except where noted below, all derivation attributes are delegated to
mkCargoDerivation
, and can be used to influence its behavior.
buildPhaseCargoCommand
will be set to runcargo build --profile release
for the workspace.CARGO_PROFILE
can be set on the derivation to alter which cargo profile is selected; setting it to""
will omit specifying a profile altogether.
pnameSuffix
will be set to"-build"
Required attributes
cargoArtifacts
: A path (or derivation) which contains an existing cargotarget
directory, which will be reused at the start of the derivation. Useful for caching incremental cargo builds.- This can be prepared via
buildDepsOnly
- Alternatively, any cargo-based derivation which was built with
doInstallCargoArtifacts = true
will work as well
- This can be prepared via
Optional attributes
cargoExtraArgs
: additional flags to be passed in the cargo invocation (e.g. enabling specific features)- Default value:
"--locked"
- Default value:
Remove attributes
The following attributes will be removed before being lowered to
mkCargoDerivation
. If you absolutely need these attributes present as
environment variables during the build, you can bring them back via
.overrideAttrs
.
cargoExtraArgs
craneLib.cargoClippy
cargoClippy :: set -> drv
Create a derivation which will run a cargo clippy
invocation in a cargo
workspace.
Except where noted below, all derivation attributes are delegated to
mkCargoDerivation
, and can be used to influence its behavior.
buildPhaseCargoCommand
will be set to runcargo clippy --profile release
for the workspace.CARGO_PROFILE
can be set on the derivation to alter which cargo profile is selected; setting it to""
will omit specifying a profile altogether.
pnameSuffix
will be set to"-clippy"
Required attributes
cargoArtifacts
: A path (or derivation) which contains an existing cargotarget
directory, which will be reused at the start of the derivation. Useful for caching incremental cargo builds.- This can be prepared via
buildDepsOnly
- Alternatively, any cargo-based derivation which was built with
doInstallCargoArtifacts = true
will work as well
- This can be prepared via
Optional attributes
cargoClippyExtraArgs
: additional flags to be passed in the clippy invocation (e.g. deny specific lints)- Default value:
"--all-targets"
- Default value:
cargoExtraArgs
: additional flags to be passed in the cargo invocation (e.g. enabling specific features)- Default value:
"--locked"
- Default value:
Native build dependencies
The clippy
package is automatically appended as a native build input to any
other nativeBuildInputs
specified by the caller.
Remove attributes
The following attributes will be removed before being lowered to
mkCargoDerivation
. If you absolutely need these attributes present as
environment variables during the build, you can bring them back via
.overrideAttrs
.
cargoClippyExtraArgs
cargoExtraArgs
craneLib.cargoDoc
cargoDoc :: set -> drv
Create a derivation which will run a cargo doc
invocation in a cargo
workspace.
Except where noted below, all derivation attributes are delegated to
mkCargoDerivation
, and can be used to influence its behavior.
buildPhaseCargoCommand
will be set to runcargo doc --profile release
for the workspace.CARGO_PROFILE
can be set on the derivation to alter which cargo profile is selected; setting it to""
will omit specifying a profile altogether.
doInstallCargoArtifacts
will default tofalse
if not specifiedpnameSuffix
will be set to"-doc"
Required attributes
cargoArtifacts
: A path (or derivation) which contains an existing cargotarget
directory, which will be reused at the start of the derivation. Useful for caching incremental cargo builds.- This can be prepared via
buildDepsOnly
- Alternatively, any cargo-based derivation which was built with
doInstallCargoArtifacts = true
will work as well
- This can be prepared via
Optional attributes
cargoDocExtraArgs
: additional flags to be passed in the rustdoc invocation (e.g. deny specific lints)- Default value:
"--no-deps"
- Default value:
cargoExtraArgs
: additional flags to be passed in the cargo invocation (e.g. enabling specific features)- Default value:
"--locked"
- Default value:
docInstallRoot
: defines the exact directory to install to$out/share
, useful for overriding when compiling different targets. By default will honor$CARGO_TARGET_DIR
(or default to./target
if not set) and$CARGO_BUILD_TARGET
(if set).- Default value:
"${CARGO_TARGET_DIR:-target}/${CARGO_BUILD_TARGET:-}/doc"
if such a directory exists, otherwise falls back to"${CARGO_TARGET_DIR:-target}/doc"
- Default value:
Remove attributes
The following attributes will be removed before being lowered to
mkCargoDerivation
. If you absolutely need these attributes present as
environment variables during the build, you can bring them back via
.overrideAttrs
.
cargoDocExtraArgs
cargoExtraArgs
craneLib.cargoDocTest
cargoDocTest :: set -> drv
Create a derivation which will run a cargo test --doc
invocation in a cargo
workspace. To run all or any tests for a workspace, consider cargoTest
.
Except where noted below, all derivation attributes are delegated to
buildPhaseCargoCommand
will be set to runcargo test --profile release
in the workspace.CARGO_PROFILE
can be set on the derivation to alter which cargo profile is selected; setting it to""
will omit specifying a profile altogether.
pnameSuffix
will be set to"-doctest"
Optional attributes
cargoExtraArgs
: additional flags to be passed in the cargo invocation- Default value:
"--locked"
- Default value:
cargoTestExtraArgs
: additional flags to be passed in the cargo invocation- Default value:
""
- Default value:
craneLib.cargoFmt
cargoFmt :: set -> drv
Create a derivation which will run a cargo fmt
invocation in a cargo
workspace.
Except where noted below, all derivation attributes are delegated to
mkCargoDerivation
, and can be used to influence its behavior.
buildPhaseCargoCommand
will be set to runcargo fmt
(in check mode) in the workspace.cargoArtifacts
is disabled/clearedcargoVendorDir
is disabled/clearedpnameSuffix
will be set to"-fmt"
Optional attributes
cargoExtraArgs
: additional flags to be passed in the cargo invocation- Default value:
""
- Default value:
rustFmtExtraArgs
: additional flags to be passed in the rustfmt invocation- Default value:
""
- Default value:
Native build dependencies
The rustfmt
package is automatically appended as a native build input to any
other nativeBuildInputs
specified by the caller.
Remove attributes
The following attributes will be removed before being lowered to
mkCargoDerivation
. If you absolutely need these attributes present as
environment variables during the build, you can bring them back via
.overrideAttrs
.
cargoExtraArgs
rustFmtExtraArgs
craneLib.taploFmt
taploFmt :: set -> drv
Create a derivation which will run a taplo fmt
invocation in a cargo
workspace.
Except where noted below, all derivation attributes are delegated to
mkCargoDerivation
, and can be used to influence its behavior.
buildPhaseCargoCommand
will be set to runtaplo fmt
(in check mode) in the workspace.cargoArtifacts
is disabled/clearedcargoVendorDir
is disabled/clearedpnameSuffix
will be set to"-tomlfmt"
Optional attributes
taploExtraArgs
: additional flags to be passed in the taplo invocation- Default value:
""
- Default value:
taplo
command line options for setting taploExtraArgs
and configuration options
for taplo.toml
config files can be found in the Command Line and Configuration
sections of the taplo documentation.
Native build dependencies
The taplo
package is automatically appended as a native build input to any
other nativeBuildInputs
specified by the caller.
Remove attributes
The following attributes will be removed before being lowered to
mkCargoDerivation
. If you absolutely need these attributes present as
environment variables during the build, you can bring them back via
.overrideAttrs
.
taploExtraArgs
craneLib.cargoLlvmCov
cargoLlvmCov :: set -> drv
Create a derivation which will run a cargo llvm-cov
invocation in a cargo
workspace.
Except where noted below, all derivation attributes are delegated to
mkCargoDerivation
, and can be used to influence its behavior.
buildPhaseCargoCommand
will be set to runcargo llvm-cov test --release
in the workspace.installPhaseCommand
will be set to""
, as the default settings creates a file instead of directory at$out
.doInstallCargoArtifacts
will be set tofalse
for the same reason asinstallPhaseCommand
pnameSuffix
will be set to"-llvm-cov"
Required attributes
cargoArtifacts
: A path (or derivation) which contains an existing cargotarget
directory, which will be reused at the start of the derivation. Useful for caching incremental cargo builds.- This can be prepared via
buildDepsOnly
- Alternatively, any cargo-based derivation which was built with
doInstallCargoArtifacts = true
will work as well
- This can be prepared via
Optional attributes
cargoExtraArgs
: additional flags to be passed in the cargo invocation- Default value:
"--locked"
- Default value:
cargoLlvmCovCommand
: cargo-llvm-cov command to run- Default value:
"test"
- Default value:
cargoLlvmCovExtraArgs
: additional flags to be passed in the cargo llvm-cov invocation- Default value:
"--lcov --output-path $out"
- Default value:
Native build dependencies
The cargo-llvm-cov
package is automatically appended as a native build input to any
other nativeBuildInputs
specified by the caller.
Note that this would require the llvm-tools-preview
component for the Rust toolchain,
which you would need to provide yourself using fenix or rust-overlay.
Remove attributes
The following attributes will be removed before being lowered to
mkCargoDerivation
. If you absolutely need these attributes present as
environment variables during the build, you can bring them back via
.overrideAttrs
.
cargoExtraArgs
cargoLlvmCovCommand
cargoLlvmCovExtraArgs
craneLib.cargoNextest
cargoNextest :: set -> drv
Create a derivation which will run a cargo nextest
invocation in a cargo
workspace. Note that cargo nextest
doesn't run
doctests, so you may also
want to build a cargoDocTest
derivation.
Except where noted below, all derivation attributes are delegated to
mkCargoDerivation
, and can be used to influence its behavior.
checkPhaseCargoCommand
will be set to runcargo nextest run --profile release
for the workspace.CARGO_PROFILE
can be set on the derivation to alter which cargo profile is selected; setting it to""
will omit specifying a profile altogether.
pnameSuffix
will be set to"-nextest"
and may include partition numbers
Required attributes
cargoArtifacts
: A path (or derivation) which contains an existing cargotarget
directory, which will be reused at the start of the derivation. Useful for caching incremental cargo builds.- This can be prepared via
buildDepsOnly
- Alternatively, any cargo-based derivation which was built with
doInstallCargoArtifacts = true
will work as well
- This can be prepared via
Optional attributes
buildPhaseCargoCommand
, unless specified, will be set to print the nextest versioncargoExtraArgs
: additional flags to be passed in the cargo invocation (e.g. enabling specific features)- Default value:
""
- Default value:
cargoLlvmCovExtraArgs
: additional flags to be passed in the cargo llvm-cov invocation- Default value:
"--lcov --output-path $out/coverage"
- Default value:
cargoNextestExtraArgs
: additional flags to be passed in the nextest invocation (e.g. specifying a profile)- Default value:
""
- Note that all flags from
cargo test
are supported.
- Default value:
partitions
: The number of separate nextest partitions to run. Useful if the test suite takes a long time and can be parallelized across multiple build nodes.- Default value:
1
- Default value:
partitionType
: The kind of nextest partition to run (e.g."count"
or"hash"
based).- Default value:
"count"
- Default value:
withLlvmCov
: Whether or not to run nextest throughcargo llvm-cov
- Default value:
false
- Note that setting
withLlvmCov = true;
is not currently supported ifpartitions > 1
.
- Default value:
Native build dependencies
The cargo-nextest
package is automatically appended as a native build input to any
other nativeBuildInputs
specified by the caller.
Remove attributes
The following attributes will be removed before being lowered to
mkCargoDerivation
. If you absolutely need these attributes present as
environment variables during the build, you can bring them back via
.overrideAttrs
.
cargoExtraArgs
cargoLlvmCovExtraArgs
cargoNextestExtraArgs
partitions
partitionType
withLlvmCov
craneLib.cargoTarpaulin
cargoTarpaulin :: set -> drv
Create a derivation which will run a cargo tarpaulin
invocation in a cargo
workspace.
Except where noted below, all derivation attributes are delegated to
mkCargoDerivation
, and can be used to influence its behavior.
buildPhaseCargoCommand
will be set to runcargo tarpaulin --profile release
in the workspace.CARGO_PROFILE
can be set on the derivation to alter which cargo profile is selected; setting it to""
will omit specifying a profile altogether.
pnameSuffix
will be set to"-tarpaulin"
Required attributes
cargoArtifacts
: A path (or derivation) which contains an existing cargotarget
directory, which will be reused at the start of the derivation. Useful for caching incremental cargo builds.- This can be prepared via
buildDepsOnly
- Alternatively, any cargo-based derivation which was built with
doInstallCargoArtifacts = true
will work as well
- This can be prepared via
Optional attributes
cargoExtraArgs
: additional flags to be passed in the cargo invocation- Default value:
""
- Default value:
cargoTarpaulinExtraArgs
: additional flags to be passed in the cargo tarpaulin invocation- Default value:
"--skip-clean --out xml --output-dir $out"
- Default value:
doNotLinkInheritedArtifacts
will be set totrue
if not specified.
Native build dependencies
The cargo-tarpaulin
package is automatically appended as a native build input to any
other nativeBuildInputs
specified by the caller.
Remove attributes
The following attributes will be removed before being lowered to
mkCargoDerivation
. If you absolutely need these attributes present as
environment variables during the build, you can bring them back via
.overrideAttrs
.
cargoExtraArgs
cargoTarpaulinExtraArgs
craneLib.cargoTest
cargoTest :: set -> drv
Create a derivation which will run a cargo test
invocation in a cargo
workspace.
Except where noted below, all derivation attributes are delegated to
buildPhaseCargoCommand
will be set to runcargo test --profile release
in the workspace.CARGO_PROFILE
can be set on the derivation to alter which cargo profile is selected; setting it to""
will omit specifying a profile altogether.
pnameSuffix
will be set to"-test"
Optional attributes
cargoExtraArgs
: additional flags to be passed in the cargo invocation- Default value:
"--locked"
- Default value:
cargoTestExtraArgs
: additional flags to be passed in the cargo invocation- Default value:
""
- Default value:
Remove attributes
The following attributes will be removed before being lowered to
mkCargoDerivation
. If you absolutely need these attributes present as
environment variables during the build, you can bring them back via
.overrideAttrs
.
cargoExtraArgs
cargoTestExtraArgs
Remove attributes
The following attributes will be removed before being lowered to
mkCargoDerivation
. If you absolutely need these attributes present as
environment variables during the build, you can bring them back via
.overrideAttrs
.
cargoExtraArgs
cargoTestExtraArgs
craneLib.cleanCargoSource
cleanCargoSource :: path or drv -> drv
Cleans a source tree to omit things like version control directories as well
omit any non-Rust/non-cargo related files. Useful to avoid rebuilding a project
when unrelated files are changed (e.g. flake.nix
or any other nix files).
The final output will be cleaned by both cleanSource
(from nixpkgs) and
craneLib.filterCargoSources
. See each of them for more details on which files are
kept.
If it is necessary to customize which files are kept, a custom filter can be
written (which may want to also call craneLib.filterCargoSources
) to achieve the
desired behavior.
craneLib.cleanCargoSource ./.
craneLib.cleanCargoToml
cleanCargoToml :: set -> set
Cleans all definitions from a Cargo.toml file which are irrelevant for a
minimal build of a package's dependencies. See mkDummySrc
for more information
on how the result is applied.
In general, the following types of attributes are kept from the original input:
- basic package definitions (like name and version)
- dependency definitions
- feature definitions
- workspace definitions
- anything pertaining to project structure (like bin/lib targets, tests, etc.)
craneLib.cleanCargoToml { cargoToml = ./Cargo.toml; }
# { dependencies = { byteorder = "*"; }; package = { edition = "2021"; name = "simple"; version = "0.1.0"; }; }
Input attributes
cargoToml
: a path to a Cargo.toml filecargoTomlContents
: the contents of a Cargo.toml file as a string
At least one of the above attributes must be specified, or an error will be raised during evaluation.
craneLib.crateNameFromCargoToml
crateNameFromCargoToml :: set -> set
Extract a crate's name and version from its Cargo.toml file.
The resulting pname
attribute will be populated with the value of the
Cargo.toml's (top-level) attributes in the following order, where the first
attribute (with a string value) will be chosen:
package.metadata.crane.name
package.name
workspace.metadata.crane.name
- (Deprecated)
workspace.package.name
- Otherwise a placeholder name will be used
The resulting version
attribute will be populated with the value of the
Cargo.toml's (top-level) attributes in the following order, where the first
attribute (with a string value) will be chosen:
package.version
workspace.package.version
- Otherwise a placeholder version will be used
Note that only the root Cargo.toml
of the specified source will be checked.
Directories will not be crawled to resolve potential workspace inheritance.
craneLib.crateNameFromCargoToml { cargoToml = ./Cargo.toml; }
# { pname = "simple"; version = "0.1.0"; }
craneLib.crateRegistries
crateRegistries :: set
A set of crate registries made available for use in downloading crate sources. The keys are registry URLs as used in the Cargo.lock file (e.g. "registry+https://...") and the values are the download URL for that registry, including any placeholder values cargo is expected to populate for downloads.
This definition can be updated via appendCrateRegistries
.
Input attributes
src
: a directory which includes a Cargo.toml file at its root.cargoToml
: a path to a Cargo.toml filecargoTomlContents
: the contents of a Cargo.toml file as a string
At least one of the above attributes must be specified, or an error will be raised during evaluation.
Output attributes
pname
: the name of the crate- Default value:
"cargo-package"
if the specified Cargo.toml file did not include a name
- Default value:
version
: the version of the crate- Default value:
"0.0.1"
if the specified Cargo.toml file did not include a version
- Default value:
craneLib.devShell
devShell :: set -> drv
A thin wrapper around
pkgs.mkShell
for
creating development shells for use with nix develop
(see “Local
Development”). Except where noted below, all derivation
attributes are passed straight through, so any mkShell
behavior can be used
as expected: namely, all key-value pairs other than those mkShell
consumes
will be set as environment variables in the resulting shell.
Note that the current toolchain's cargo
, clippy
, rustc
, and rustfmt
packages will automatically be added to the devShell.
Optional attributes
checks
: A set of checks to inherit inputs from, typicallyself.checks.${system}
. Build inputs from the values in this attribute set are added to the created shell environment for interactive use.inputsFrom
: A list of extra packages to inherit inputs from. Note that these packages are not added to the result environment; usepackages
for that.packages
: A list of extra packages to add to the created shell environment.shellHook
: A string of bash statements that will be executed when the shell is entered withnix develop
.
See the quick start example for usage in a
flake.nix
file.
craneLib.devShell {
checks = self.checks.${system};
packages = [
pkgs.ripgrep
];
# Set a `cargo-nextest` profile:
NEXTEST_PROFILE = "local";
}
craneLib.devShell {
checks = {
my-package-clippy = craneLib.cargoClippy commonArgs;
my-package-doc = craneLib.cargoDoc commonArgs;
my-package-nextest = craneLib.cargoNextest commonArgs;
};
}
Note that it is possible to override the underlying mkShell
(for example to
customize the build environment further) like so:
let
moldDevShell = craneLib.devShell.override {
# For example, use the mold linker
mkShell = pkgs.mkShell.override {
stdenv = pkgs.stdenvAdapters.useMoldLinker pkgs.stdenv;
};
};
in
moldDevShell {
packages = [
# etc...
];
}
craneLib.downloadCargoPackage
downloadCargoPackage :: set -> drv
Download a packaged cargo crate (e.g. from crates.io) and prepare it for vendoring.
The registry's fetchurlExtraArgs
will be passed through to fetchurl
when
downloading the crate, making it possible to influence interacting with the
registry's API if necessary.
Required input attributes
checksum
: the (sha256) checksum recorded in the Cargo.lock filename
: the name of the cratesource
: the source key recorded in the Cargo.lock fileversion
: the version of the crate
Attributes of the vendor-prep derivation
dontBuild
:true
dontConfigure
:true
dontFixup
:true
pname
:"cargo-package-"
suffixed by the package name inCargo.lock
sourceRoot
:"./crate"
version
: inherited from the package version inCargo.lock
unpackPhase
: This phase will:- run the
preUnpack
hook - create an empty directory named
./crate
- unpack the crate's tarball under
./crate
- run the
postUnpack
hook
- run the
installPhase
: This phase will:- run the
preInstall
hook - move the contents of the current directory (i.e.
./crate
by default) to$out
- populate
$out/.cargo-checksum.json
- run the
postInstall
hook
- run the
craneLib.downloadCargoPackageFromGit
downloadCargoPackageFromGit :: set -> drv
Download a git repository containing a cargo crate or workspace, and prepare it any crates it contains for vendoring.
Required input attributes
git
: the URL to the repositoryrev
: the exact revision to check out
Optional attributes
allRefs
: whether all git refs should be fetched in order to look for the specifiedrev
- Default value:
true
ifref
is set tonull
,false
otherwise
- Default value:
ref
: the ref (i.e. branch or tag) to whichrev
belongs to. For branches it should be"refs/head/${branch}"
and for tags it should be"refs/tags/${tag}"
- Default value:
null
- Default value:
sha256
: the sha256 hash of the (unpacked) download. If providedfetchgit
will be used (instead ofbuiltins.fetchGit
) which allows for offline evaluations.- Default value:
null
- Default value:
Attributes of the vendor-prep derivation
dontBuild
:true
dontConfigure
:true
dontFixup
:true
installPhase
: This phase will:- run the
preInstall
hook - Prepare the current directory for vendoring by:
- Searching for all
Cargo.toml
files - Copying their parent directory to
$out/$crate
(where$crate
is the package name and version as defined inCargo.toml
) - Populating
.cargo-checksum.json
- Running
crane-resolve-workspace-inheritance
on theCargo.toml
- Note that duplicate crates (whose name and version collide) are ignored
- Searching for all
- run the
postInstall
hook
- run the
nativeBuildInputs
: A list of thecargo
,craneUtils
, andjq
packagesname
: set to"cargo-git"
src
: the git repo checkout, as determined by the input parameters
craneLib.findCargoFiles
findCargoFiles :: path -> set of lists
Given a path, recursively search it for any Cargo.toml
, .cargo/config
or
.cargo/config.toml
files.
craneLib.findCargoFiles ./src
# { cargoTomls = [ "..." ]; cargoConfigs = [ "..." ]; }
craneLib.filterCargoSources
filterCargoSources :: path -> string -> bool
A source filter which when used with cleanSourceWith
(from nixpkgs's lib
)
will retain the following files from a given source:
- Cargo files (
Cargo.toml
,Cargo.lock
,.cargo/config.toml
,.cargo/config
) - Rust files (files whose name end with
.rs
) - TOML files (files whose name end with
.toml
)
cleanSourceWith {
src = ./.;
filter = craneLib.filterCargoSources;
name = "source"; # Be reproducible, regardless of the directory name
}
Note that it is possible to compose source filters, especially if
filterCargoSources
omits files which are relevant to the build. For example:
let
# Only keeps markdown files
markdownFilter = path: _type: builtins.match ".*md$" path != null;
markdownOrCargo = path: type:
(markdownFilter path type) || (craneLib.filterCargoSources path type);
in
cleanSourceWith {
src = ./.;
filter = markdownOrCargo;
name = "source"; # Be reproducible, regardless of the directory name
}
craneLib.fileset.cargoTomlAndLock
cargoTomlAndLock :: path -> fileset
A fileset helper which will only include any Cargo.toml
and Cargo.lock
files from the specified path.
craneLib.fileset.commonCargoSources
commonCargoSources :: path -> fileset
A fileset helper which will only include any files commonly used by cargo projects from the specified path. Essentially a union of:
craneLib.fileset.cargoTomlAndLock
craneLib.fileset.rust
craneLib.fileset.toml
craneLib.fileset.configToml
configToml :: path -> fileset
A fileset helper which will only include config.toml
files from the
specified path.
Note that cargo usually only pays attention to config.toml
files if they are
present inside of a directory named .cargo
. This fileset will contain any
config.toml
file, even if its parent directory is not named .cargo
.
craneLib.fileset.rust
rust :: path -> fileset
A fileset helper which will only include *.rs
files from the specified path.
craneLib.fileset.toml
toml :: path -> fileset
A fileset helper which will only include *.toml
files from the specified path.
craneLib.mkCargoDerivation
mkCargoDerivation :: set -> drv
A thin wrapper around stdenv.mkDerivation
which includes common hooks for
building a derivation using cargo. Except where noted below, all derivation
attributes are passed straight through, so any common derivation behavior can be
used as expected: namely all key-value pairs will be set as environment
variables for the derivation's build script.
This is a fairly low-level abstraction, so consider using buildPackage
or
cargoBuild
if they fit your needs.
Required attributes
buildPhaseCargoCommand
: A command (likely a cargo invocation) to run during the derivation's build phase. Pre and post build hooks will automatically be run.cargoArtifacts
: A path (or derivation) which contains an existing cargotarget
directory, which will be reused at the start of the derivation. Useful for caching incremental cargo builds.- This can be prepared via
buildDepsOnly
- Alternatively, any cargo-based derivation which was built with
doInstallCargoArtifacts = true
will work as well
- This can be prepared via
Optional attributes
buildPhase
: the commands used by the build phase of the derivation- Default value: the build phase will run
preBuild
hooks, print the cargo version, log and evaluatebuildPhaseCargoCommand
, and runpostBuild
hooks
- Default value: the build phase will run
cargoLock
: if set will be passed through to the derivation and the path it points to will be copied as the workspaceCargo.lock
- Unset by default
cargoLockContents
: if set andcargoLock
is missing or null, its value will be written as the workspaceCargo.lock
- Unset by default
cargoLockParsed
: if set and bothcargoLock
andcargoLockContents
are missing or null, its value will be serialized as TOML and the result written as the workspaceCargo.lock
- Unset by default
cargoVendorDir
: A path (or derivation) of vendored cargo sources which can be consumed without network access. Directory structure should basically follow the output ofcargo vendor
.- Default value: the result of
vendorCargoDeps
after applying the arguments set (with the respective default values)
- Default value: the result of
checkPhase
: the commands used by the check phase of the derivation- Default value: the check phase will run
preCheck
hooks, log and evaluatecheckPhaseCargoCommand
, and runpostCheck
hooks
- Default value: the check phase will run
checkPhaseCargoCommand
: A command (likely a cargo invocation) to run during the derivation's check phase. Pre and post check hooks will automatically be run.- Default value:
""
- Default value:
configurePhase
: the commands used by the configure phase of the derivation- Default value: the configure phase will run
preConfigureHooks
hooks, then runpostConfigure
hooks
- Default value: the configure phase will run
doInstallCargoArtifacts
: controls whether cargo'starget
directory should be copied as an output- Default value:
true
- Default value:
installPhase
: the commands used by the install phase of the derivation- Default value: the install phase will run
preInstall
hooks, log and evaluateinstallPhaseCommand
, and runpostInstall
hooks
- Default value: the install phase will run
installPhaseCommand
: the command(s) which are expected to install the derivation's outputs.- Default value:
"mkdir -p $out"
- By default an output directory is created such that any other
postInstall
hooks can successfully run. Consider overriding this value with an appropriate installation commands for the package being built.
- Default value:
pname
: the name of the derivation- Default value: the package name listed in
Cargo.toml
- Default value: the package name listed in
pnameSuffix
: a suffix appended topname
- Default value:
""
- Default value:
stdenv
: the standard build environment to use for this derivation- Default value:
pkgs.stdenv
- Default value:
version
: the version of the derivation- Default value: the version listed in
Cargo.toml
- Default value: the version listed in
Remove attributes
The following attributes will be removed before being lowered to
stdenv.mkDerivation
. If you absolutely need these attributes present as
environment variables during the build, you can bring them back via
.overrideAttrs
.
buildPhaseCargoCommand
cargoLock
cargoLockContents
cargoLockParsed
checkPhaseCargoCommand
installPhaseCommand
outputHashes
pnameSuffix
stdenv
Native build dependencies and included hooks
The cargo
package is automatically appended as a native build input to any
other nativeBuildInputs
specified by the caller, along with the following
hooks:
cargoHelperFunctionsHook
configureCargoCommonVarsHook
configureCargoVendoredDepsHook
inheritCargoArtifactsHook
installCargoArtifactsHook
replaceCargoLockHook
rsync
zstd
craneLib.mkDummySrc
mkDummySrc :: set -> drv
Converts a given source directory of a cargo workspace to the smallest, most trivial form needed to build all dependencies such that their artifacts can be cached.
The actual source files of the project itself are ignored/replaced with empty programs, such that changes to the source files does not invalidate any build caches. More specifically:
- The Cargo.lock file is kept as-is
- Any changes to it will invalidate the build cache
- Any cargo configuration files (i.e. files name
config
orconfig.toml
whose parent directory is named.cargo
) are kept as-is.- Any changes to these files will invalidate the build cache
- Any files named
Cargo.toml
are reduced viacleanCargoToml
and the result is kept. Only the following changes will result in invalidating the build cache:- Any changes to listed dependencies
- Any changes to feature definitions
- Any changes to the workspace member metadata
- Any changes to the
[package]
definition such as name and version - Any changes to the name or path of any target (such as benches, bins, examples, libs, or tests)
Required attributes
src
: a source directory which should be turned into a "dummy" form
Optional attributes
cargoLock
: a path to a Cargo.lock file- Default value:
src + /Cargo.lock
- Default value:
dummyrs
: a path to a file which will be used in place of all dummy rust files (e.g.main.rs
,lib.rs
, etc.). This can be useful to customize dummy source files (e.g. enable certain lang features for a given target).- Default value: an empty
fn main
declaration and conditionally enabled#![no_std]
if thetarget_os
cfg is set to"none"
or"uefi"
.
- Default value: an empty
extraDummyScript
: additional shell script which will be run inside the builder verbatim. Useful for customizing what the dummy sources include by running any arbitrary commands.- Default value:
""
- Note that this script will run in an environment where the original source is not present as doing so would cause a rebuild if any part of the source changed. Additional files can be copied to the derivation's result, but care must be taken that the derivation only depends on (i.e. is rebuilt if) the smallest subset of the original source as required.
- Here is an example of how to include an entire directory, in this case
.cargo
, but any other directory would work as well:let # The _entire_ source of the project. mkDummySrc will automatically # filter out irrelevant files as described above src = craneLib.path ./.; dotCargoOnly = lib.cleanSourceWith { inherit src; # Only keep `*/.cargo/*` filter = path: _type: lib.hasInfix ".cargo" path; }; in mkDummySrc { inherit src; # Note that here we scope the path to only contain any `.cargo` directory # and its contents and not any other directories which may exist at the # root of the project. Also note that the entire path is inside of the # `${ }` which ensures that the derivation only consumes that directory. # Writing `${./.}/.cargo` would incorrectly consume the entire source root, # and therefore rebuild everything when any file changes, which defeats # artifact caching. # # Also note the `--no-target-directory` flag which ensures the results are # copied to `$out/.cargo` instead of something like `$out/HASH-.cargo` extraDummyScript = '' cp -r ${dotCargoOnly} --no-target-directory $out/ ''; }
- Default value:
craneLib.overrideToolchain
overrideToolchain :: (set -> drv) -> set
overrideToolchain :: drv -> set
(legacy)
A convenience method to override and use tools (like cargo
, clippy
,
rustfmt
, rustc
, etc.) from one specific toolchain. The input should be a
single derivation which contains all the tools as binaries. For example, this
can be the output of oxalica/rust-overlay
.
Note that in order to best support cross compilation, overrideToolchain
should
be provided a function (whose argument is a cross-compilation aware version of
pkgs
) which constructs the toolchain:
craneLib.overrideToolchain (p: myCustomToolchainForPkgs p)
craneLib.path
path :: path -> drv
path :: set -> drv
A convenience wrapper around builtins.path
which will automatically set the
path's name
to the workspace's package name (or a placeholder value of
"source"
if a name cannot be determined).
It should be used anywhere a relative path like ./.
or ./..
is needed so
that the result is reproducible and caches can be reused. Otherwise the store
path will depend on the name of the parent
directory which may cause unnecessary rebuilds.
craneLib.path ./.
# "/nix/store/wbhf6c7wiw9z53hsn487a8wswivwdw81-source"
craneLib.path ./checks/simple
# "/nix/store/s9scn97c86kqskf7yv5n2k85in5y5cmy-simple"
It is also possible to use as a drop in replacement for builtins.path
:
craneLib.path {
path = ./.;
name = "asdf";
}
# "/nix/store/23zy3c68v789cg8sysgba0rbgbfcjfhn-asdf"
craneLib.registryFromDownloadUrl
registryFromDownloadUrl :: set -> set
Prepares a crate registry into a format that can be passed directly to
appendCrateRegistries
using the registry's download URL.
If the registry in question has a stable download URL (which either never
changes, or it does so very infrequently), then registryFromDownloadUrl
is a
great and lightweight choice for including the registry. To get started, look up
the
config.json
at the registry's root and copy the value of the dl
entry.
If the registry's download endpoint changes more frequently and you would like
to infer the configuration directly from a git revision, consider using
registryFromGitIndex
as an alternative.
If the registry needs a special way of accessing crate sources the
fetchurlExtraArgs
set can be used to influence the behavior of fetching the
crate sources (e.g. by setting curlOptsList
)
Required attributes
dl
: the value of thedl
entry in the registry'sconfig.json
fileindexUrl
: an HTTP URL to the index
Optional attributes
fetchurlExtraArgs
: a set of arguments which will be passed on to thefetchurl
for each crate being sourced from this registry
craneLib.registryFromDownloadUrl {
dl = "https://static.crates.io/crates";
indexUrl = "https://github.com/rust-lang/crates.io-index";
}
# {
# "registry+https://github.com/rust-lang/crates.io-index" = {
# downloadUrl = "https://static.crates.io/crates/{crate}/{version}/download";
# fetchurlExtraArgs = {};
# };
# }
craneLib.registryFromGitIndex
registryFromGitIndex :: set -> set
Prepares a crate registry into a format that can be passed directly to
appendCrateRegistries
using a revision of the registry index to infer the
download URL.
Note that the specified git revision does not need to track updates to the
index itself as long as the pinned revision contains the most recent version of
the config.json
file. In other words, this commit revision only needs to be
updated if the config.json
file changes.
Also note that this approach means that the contents of the entire index at the
specified revision will be added to the Nix store during evaluation time, and
that IFD will need to be enabled. If this is unsatisfactory, consider using
registryFromDownloadUrl
as a simpler alternative.
If the registry needs a special way of accessing crate sources the
fetchurlExtraArgs
set can be used to influence the behavior of fetching the
crate sources (e.g. by setting curlOptsList
)
Required attributes
indexUrl
: an HTTP URL to the indexrev
: any git revision which contains the latestconfig.json
definition
Optional attributes
fetchurlExtraArgs
: a set of arguments which will be passed on to thefetchurl
for each crate being sourced from this registry
craneLib.registryFromGitIndex {
url = "https://github.com/Hirevo/alexandrie-index";
rev = "90df25daf291d402d1ded8c32c23d5e1498c6725";
}
# {
# "registry+https://github.com/Hirevo/alexandrie-index" = {
# downloadUrl = "https://crates.polomack.eu/api/v1/crates/{crate}/{version}/download";
# fetchurlExtraArgs = {};
# };
# }
craneLib.registryFromSparse
registryFromSparse :: set -> set
Prepares a (sparse) crate registry into a format that can be passed directly to
appendCrateRegistries
using the registry's download URL.
If the registry in question has a stable download URL (which either never
changes, or it does so very infrequently), then registryFromDownloadUrl
is a
great and lightweight choice for including the registry. To get started,
download the registry's config.json
and copy the value of the dl
entry.
If the registry's download endpoint changes more frequently and you would like
to infer the configuration directly from a git revision, consider using
registryFromGitIndex
as an alternative.
If the registry needs a special way of accessing crate sources the
fetchurlExtraArgs
set can be used to influence the behavior of fetching the
crate sources (e.g. by setting curlOptsList
)
Required attributes
indexUrl
: an HTTP URL to the registry's config.jsonconfigSha256
: a sha256 hash of the contents of config.json
Optional attributes
fetchurlExtraArgs
: a set of arguments which will be passed on to thefetchurl
for each crate being sourced from this registry
craneLib.registryFromSparse {
indexUrl = "https://index.crates.io/config.json";
configSha256 = "1cxgzdm1ipqmgwnq7kgym92axna7pfyhgfla63vl7dvydwn3m52v";
}
# {
# "sparse+https://index.crates.io/config.json/" = {
# downloadUrl = "https://static.crates.io/crates/{crate}/{version}/download";
# fetchurlExtraArgs = { };
# };
# }
craneLib.urlForCargoPackage
urlForCargoPackage :: set -> set
Returns info pertaining to the URL for downloading a particular crate if the crate's registry is configured (an error will be thrown if it is not).
The result will contain two attributes:
url
: A string representing the URL at which the crate can be fetchedfetchurlExtraArgs
: A set of attributes specific to this registry which will be passed on to thefetchurl
invocation.
Required input attributes
name
: the name of the cratesource
: the source key recorded in the Cargo.lock fileversion
: the version of the crate
craneLib.vendorCargoDeps
vendorCargoDeps :: set -> drv
Creates a derivation which will download all crates referenced by a Cargo.lock file, and prepare a vendored directory which cargo can use for subsequent builds without needing network access.
Each unique crate index will be vendored as its own subdirectory within the
output of the derivation. A config.toml
file will also be placed at the root
of the output which will contain the necessary configurations to point cargo to
the vendored directories (i.e. this configuration can be appended to the
.cargo/config.toml
definition of the project).
Input attributes
src
: a directory which includes a Cargo.lock file at its root.cargoLock
: a path to a Cargo.lock filecargoLockContents
: the contents of a Cargo.lock file as a stringcargoLockParsed
: the parsed contents of Cargo.lock as an attribute set
At least one of the above attributes must be specified, or an error will be raised during evaluation.
Optional attributes
outputHashes
: a mapping of package-source to thehash
attribute of the (unpacked) download. Useful for supporting fully offline evaluations.- Default value:
[]
- Default value:
overrideVendorCargoPackage
: a function that will be called on every crate vendored from a cargo registry, which allows for modifying the derivation which will unpack the cargo tarball (e.g. to patch the crate source). It will be called with the following parameters:- The
Cargo.lock
entry for that package (to allow conditional overrides based on the package name/version/source, etc.) - The default
downloadCargoPackage
derivation
- Default value:
_p: drv: drv
- The
overrideVendorGitCheckout
: a function that will be called on every unique checkout vendored from a git repository, which allows for modifying the derivation which will unpack the cargo crates found in the checkout (e.g. to patch the crate sources). It will be called with the following parameters:- A list of the
Cargo.lock
entries for each package which shares the same repo URL and revision to checkout (to allow conditional overrides based on the repo/checkout etc.) - The default
downloadCargoPackageFromGit
derivation
- Default value:
_ps: drv: drv
- A list of the
craneLib.vendorCargoRegistries
vendorCargoRegistries :: set -> set
Creates the derivations necessary to download all crates from all registries
referenced by a Cargo.lock
file, and prepare the vendored directories which
cargo can use for subsequent builds without needing network access.
Input attributes
lockPackages
: a list of all[[package]]
entries found in the project'sCargo.lock
file (parsed viabuiltins.fromTOML
)
Optional attributes
cargoConfigs
: a list of paths to all.cargo/config.toml
files which may appear in the project. Ignored ifregistries
is set.- Default value:
[]
- Default value:
overrideVendorCargoPackage
: a function that will be called on every crate vendored from a cargo registry, which allows for modifying the derivation which will unpack the cargo tarball (e.g. to patch the crate source). It will be called with the following parameters:- The
Cargo.lock
entry for that package (to allow conditional overrides based on the package name/version/source, etc.) - The default
downloadCargoPackage
derivation
- Default value:
_p: drv: drv
- The
registries
: an attrset of registry names to their index URL. The default ("crates-io") registry need not be specified, as it will automatically be available, but it can be overridden if required.- Default value: if not specified,
cargoConfigs
will be used to identify any configured registries
- Default value: if not specified,
Output attributes
config
: the configuration entires needed to point cargo to the vendored crates. This is intended to be appended to$CARGO_HOME/config.toml
verbatimsources
: an attribute set of all the newly created cargo sources' names to their location in the Nix store
craneLib.vendorGitDeps
vendorGitDeps :: set -> set
Creates the derivations necessary to download all crates from all git
dependencies referenced by a Cargo.lock
file, and prepare the vendored
directories which cargo can use for subsequent builds without needing network
access.
Input attributes
lockPackages
: a list of all[[package]]
entries found in the project'sCargo.lock
file (parsed viabuiltins.fromTOML
)
Optional attributes
outputHashes
: a mapping of package-source to thehash
attribute of the (unpacked) download. Useful for supporting fully offline evaluations.- Default value:
[]
- Default value:
overrideVendorGitCheckout
: a function that will be called on every unique checkout vendored from a git repository, which allows for modifying the derivation which will unpack the cargo crates found in the checkout (e.g. to patch the crate sources). It will be called with the following parameters:- A list of the
Cargo.lock
entries for each package which shares the same repo URL and revision to checkout (to allow conditional overrides based on the repo/checkout etc.) - The default
downloadCargoPackageFromGit
derivation
- Default value:
_ps: drv: drv
- A list of the
Output attributes
config
: the configuration entires needed to point cargo to the vendored sources. This is intended to be appended to$CARGO_HOME/config.toml
verbatimsources
: an attribute set of all the newly created cargo sources' names to their location in the Nix store
craneLib.vendorMultipleCargoDeps
vendorMultipleCargoDeps :: set -> drv
Creates a derivation which will download all crates referenced by several
Cargo.lock
files, and prepare a vendored directory which cargo can use for
subsequent builds without needing network access. Duplicate packages listed in
different Cargo.lock
files will automatically be filtered out.
Each unique crate index will be vendored as its own subdirectory within the
output of the derivation. A config.toml
file will also be placed at the root
of the output which will contain the necessary configurations to point cargo to
the vendored directories (i.e. this configuration can be appended to the
.cargo/config.toml
definition of the project).
Optional attributes
cargoConfigs
: a list of paths to all.cargo/config.toml
files which may appear in the project. Ignored ifregistries
is set.- Default value:
[]
- Default value:
cargoLockContentsList
: a list of strings representing the contents of differentCargo.lock
files to be included while vendoring. The strings will automatically be parsed during evaluation.- Default value:
[]
- Default value:
cargoLockList
: a list of paths to differentCargo.lock
files to be included while vendoring. The paths will automatically be read and parsed during evaluation.- Default value:
[]
- Default value:
cargoLockParsedList
: a list of attrsets representing the parsed contents of differentCargo.lock
files to be included while vendoring.- Default value:
[]
- Default value:
outputHashes
: a mapping of package-source to thehash
attribute of the (unpacked) download. Useful for supporting fully offline evaluations.- Default value:
[]
- Default value:
overrideVendorCargoPackage
: a function that will be called on every crate vendored from a cargo registry, which allows for modifying the derivation which will unpack the cargo tarball (e.g. to patch the crate source). It will be called with the following parameters:- The
Cargo.lock
entry for that package (to allow conditional overrides based on the package name/version/source, etc.) - The default
downloadCargoPackage
derivation
- Default value:
_p: drv: drv
- The
overrideVendorGitCheckout
: a function that will be called on every unique checkout vendored from a git repository, which allows for modifying the derivation which will unpack the cargo crates found in the checkout (e.g. to patch the crate sources). It will be called with the following parameters:- A list of the
Cargo.lock
entries for each package which shares the same repo URL and revision to checkout (to allow conditional overrides based on the repo/checkout etc.) - The default
downloadCargoPackageFromGit
derivation
- Default value:
_ps: drv: drv
- A list of the
registries
: an attrset of registry names to their index URL. The default ("crates-io") registry need not be specified, as it will automatically be available, but it can be overridden if required.- Default value: if not specified,
cargoConfigs
will be used to identify any configured registries
- Default value: if not specified,
craneLib.writeTOML
writeTOML :: String -> String -> drv
Takes a file name and an attribute set, converts the set to a TOML document and writes it to a file with the given name.
craneLib.writeTOML "foo.toml" { foo.bar = "baz"; }
# «derivation /nix/store/...-foo.toml.drv»
Hooks
craneLib.cargoHelperFunctionsHook
Defines helper functions for internal use. It is probably not a great idea to depend on these directly as their behavior can change at any time, but it is worth documenting them just in case:
- Defines a
cargo()
function which will immediately invoke thecargo
command found on the$PATH
after echoing the exact arguments that were passed in. Useful for automatically logging all cargo invocations to the log. - Defines a
cargoWithProfile()
function which will invokecargo
with the provided arguments. If$CARGO_PROFILE
is set, then--profile $CARGO_PROFILE
will be injected into thecargo
invocation- Note: a default value of
$CARGO_PROFILE
is set viaconfigureCargoCommonVarsHook
. You can setCARGO_PROFILE = "something"
in your derivation to change which profile is used, or setCARGO_PROFILE = "";
to omit it altogether.
- Note: a default value of
craneLib.configureCargoCommonVarsHook
Defines configureCargoCommonVars()
which will set various common cargo-related
variables, such as honoring the amount of parallelism dictated by Nix, disabling
incremental artifacts, etc. More specifically:
CARGO_BUILD_INCREMENTAL
is set tofalse
if not already definedCARGO_BUILD_JOBS
is set to$NIX_BUILD_CORES
if not already definedCARGO_HOME
is set to$PWD/.cargo-home
if not already defined.- The directory that
CARGO_HOME
points to will be created
- The directory that
CARGO_PROFILE
is set torelease
if not already defined.- Note that this is is used internally specify a cargo profile (e.g.
cargo build --profile release
) and not something natively understood by cargo.
- Note that this is is used internally specify a cargo profile (e.g.
RUST_TEST_THREADS
is set to$NIX_BUILD_CORES
if not already defined
Automatic behavior: runs as a post-patch hook
craneLib.configureCargoVendoredDepsHook
Defines configureCargoVendoredDeps()
which will prepare cargo to use a
directory of vendored crate sources. It takes two positional arguments:
- a path to the vendored sources
- If not specified, the value of
$cargoVendorDir
will be used - If
cargoVendorDir
is not specified, an error will be raised
- If not specified, the value of
- a path to a cargo config file to modify
- If not specified, the value of
$CARGO_HOME/config.toml
will be used - This cargo config file will be appended with a stanza which will instruct
cargo to use the vendored sources (instead of downloading the sources
directly) as follows:
- If the vendored directory path contains a file named
config.toml
, then its contents will be appended to the specified cargo config path. - Otherwise the entire vendored directory path will be treated as if it only vendors the crates.io index and will be configured as such.
- If the vendored directory path contains a file named
- If not specified, the value of
Automatic behavior: if cargoVendorDir
is set, then
configureCargoVendoredDeps "$cargoVendorDir" "$CARGO_HOME/config.toml"
will be
run as a pre configure hook.
craneLib.inheritCargoArtifactsHook
Defines inheritCargoArtifacts()
which will pre-populate cargo's artifact
directory using a previous derivation. It takes two positional arguments:
- a path to the previously prepared artifacts
- If not specified, the value of
$cargoArtifacts
will be used - If
cargoArtifacts
is not specified, an error will be raised - If the specified path is a directory which contains a file called
target.tar.zst
, then that file will be used as specified below - If the specified path is a file (and not a directory) it is assumed that it contains a zstd compressed tarball and will be decompressed and unpacked into the specified cargo artifacts directory
- If the specified path is a directory which contains another directory
called
target
, then that directory will be used as specified below - If the specified path is a directory, its contents will be copied into the specified cargo artifacts directory
- The previously prepared artifacts are expected to be a zstd compressed tarball
- If not specified, the value of
- the path to cargo's artifact directory, where the previously prepared
artifacts should be unpacked
- If not specified, the value of
$CARGO_TARGET_DIR
will be used - If
CARGO_TARGET_DIR
is not set, cargo's default target location (i.e../target
) will be used.
- If not specified, the value of
Note that as an optimization, some dependency artifacts will be symlinked
instead of (deeply) copied to $CARGO_TARGET_DIR
. To disable this behavior set
doNotLinkInheritedArtifacts
, and all artifacts will be copied as plain,
writable files.
Automatic behavior: if cargoArtifacts
is set, then
inheritCargoArtifacts "$cargoArtifacts" "$CARGO_TARGET_DIR"
will be run as a
post patch hook.
Required nativeBuildInputs: assumes zstd
is available on the $PATH
craneLib.installCargoArtifactsHook
Defines compressAndInstallCargoArtifactsDir()
which handles installing
cargo's artifact directory to the derivation's output as a zstd compressed
tarball. It takes two positional arguments:
- the installation directory for the output.
- An error will be raised if not specified
- Cargo's artifact directory will be compressed as a reproducible tarball
with zstd compression. It will be written to this directory and named
target.tar.zstd
- the path to cargo's artifact directory
- An error will be raised if not specified
If $zstdCompressionExtraArgs
is set, compressAndInstallCargoArtifactsDir()
will pass its contents along to zstd
when compressing artifacts.
Defines dedupAndInstallCargoArtifactsDir()
which handles installing
cargo's artifact directory to the derivation's output after deduplicating
identical files against a directory of previously prepared cargo artifacts.
It takes three positional arguments:
- the installation directory for the output.
- An error will be raised if not specified
- If the specified path is a directory which exists then the current cargo artifacts will be compared with the contents of said directory. Any files whose contents and paths match will be symbolically linked together to reduce the size of the data stored in the Nix store.
- the path to cargo's artifact directory
- An error will be raised if not specified
- a path to the previously prepared cargo artifacts
- An error will be raised if not specified
/dev/null
can be specified here if there is no previous directory to deduplicate against
Defines prepareAndInstallCargoArtifactsDir()
which handles installing cargo's
artifact directory to the derivation's output. It takes three positional
arguments:
- the installation directory for the output.
- If not specified, the value of
$out
will be used - Cargo's artifact directory will be installed based on the installation mode selected below
- If not specified, the value of
- the path to cargo's artifact directory
- If not specified, the value of
$CARGO_TARGET_DIR
will be used - If
CARGO_TARGET_DIR
is not set, cargo's default target location (i.e../target
) will be used.
- If not specified, the value of
- the installation mode to apply
- If specified, the value of
$installCargoArtifactsMode
will be used, otherwise, a default value of"use-zstd"
will be used - If set to "use-symlink" then
dedupAndInstallCargoArtifactsDir()
will be used.- If
$cargoArtifacts
is defined and$cargoArtifacts/target
is a valid directory, it will be used during file deduplication
- If
- If set to "use-zstd" then
compressAndInstallCargoArtifactsDir()
will be used. - Otherwise an error will be raised if the mode is not recognized
- If specified, the value of
Automatic behavior: if doInstallCargoArtifacts
is set to 1
, then
prepareAndInstallCargoArtifactsDir "$out" "$CARGO_TARGET_DIR"
will be run as a
post install hook.
Required nativeBuildInputs: assumes zstd
is available on the $PATH
craneLib.installFromCargoBuildLogHook
Defines installFromCargoBuildLog()
which will use a build log produced by
cargo to find and install any binaries and libraries which have been built. It
takes two positional arguments:
- a path to where artifacts should be installed
- If not specified, the value of
$out
will be used - Binaries will be installed in a
bin
subdirectory - Libraries will be installed in a
lib
subdirectory- Note that only library targets with the
staticlib
andcdylib
crate-types will be installed. Library targets with therlib
crate-type will be ignored
- Note that only library targets with the
- If not specified, the value of
- a path to a JSON formatted build log written by cargo
- If not specified, the value of
$cargoBuildLog
will be used - If
cargoBuildLog
is not set, an error will be raised - This log can be captured, for example, via
cargo build --message-format json-render-diagnostics >cargo-build.json
- If not specified, the value of
Automatic behavior: none
Required nativeBuildInputs: assumes cargo
is available on the $PATH
craneLib.removeReferencesToVendoredSourcesHook
Defines removeReferencesToVendoredSources()
which handles removing all
references to vendored sources from the installed binaries, which ensures that
nix does not consider the binaries as having a (runtime) dependency on the
sources themselves. It takes two positional arguments:
- the installation directory for the output.
- If not specified, the value of
$out
will be used - If
out
is not specified, an error will be raised
- If not specified, the value of
- a path to the vendored sources
- If not specified, the value of
$cargoVendorDir
will be used - If
cargoVendorDir
is not specified, an error will be raised - Note: it is expected that this directory has the exact structure as would
be produced by
craneLib.vendorCargoDeps
- If not specified, the value of
Any patched binaries on aarch64-darwin
will be signed. You can disable this functionality by setting doNotSign
.
Automatic behavior: if cargoVendorDir
is set and
doNotRemoveReferencesToVendorDir
is not set, then
removeReferencesToVendoredSources "$out" "$cargoVendorDir"
will be run as a
post install hook.
craneLib.replaceCargoLockHook
Defines replaceCargoLock()
which handles replacing or inserting a specified
Cargo.lock
file in the current directory. It takes one positional argument:
- a file which will be copied to
Cargo.lock
in the current directory- If not specified, the value of
$cargoLock
will be used - If
$cargoLock
is not set, an error will be raised
- If not specified, the value of
Automatic behavior: if cargoLock
is set and
doNotReplaceCargoLock
is not set, then replaceCargoLock "$cargoLock"
will be
run as a pre patch hook.
Troubleshooting and Frequently Asked Questions
This chapter captures a list of common questions or issues and how to resolve them. If you happen to run into an issue that is not documented here, please consider submitting a pull request!
The crane library can be instantiated with a specific version of nixpkgs as
follows. For more information, see the API docs for mkLib
.
# Instantiating for a specific `system`
crane.mkLib (import nixpkgs {
system = "armv7l-linux";
})
# Instantiating for cross compiling
crane.mkLib (import nixpkgs {
localSystem = "x86_64-linux";
crossSystem = "aarch64-linux";
})
The crane library can also be instantiated with a particular rust toolchain:
# For example, using rust-overlay
let
system = "x86_64-linux";
pkgs = import nixpkgs {
inherit system;
overlays = [ (import rust-overlay) ];
};
in
(crane.mkLib pkgs).overrideToolchain (p: p.rust-bin.stable.latest.default.override {
targets = [ "wasm32-wasi" ];
})
Finally, specific inputs can be overridden for the entire library via the
overrideScope
API as follows. For more information, see the API
docs for mkLib
/overrideToolchain
, or checkout the
custom-toolchain example.
(crane.mkLib pkgs).overrideScope (final: prev: {
cargo-tarpaulin = myCustomCargoTarpaulinVersion;
})
Nix is complaining about IFD (import from derivation)
If a derivation's pname
and version
attributes are not explicitly set,
crane will inspect the project's Cargo.toml
file to set them as a convenience
to avoid duplicating that information by hand. This works well when the source
is a local path, but can cause issues if the source is being fetched remotely,
or flakes are not being used (since flakes have IFD enabled on by default).
One easy workaround for this issue (besides enabling the
allow-import-from-derivation
option in Nix) is to explicitly set
{ pname = "..."; version = "..."; }
in the derivation.
You'll know you've run into this issue if you see error messages along the lines of:
cannot build '/nix/store/...-source.drv' during evaluation because the option 'allow-import-from-derivation' is disabled
a 'aarch64-darwin' with features {} is required to build '/nix/store/...', but I am a 'x86_64-linux' with features {}
I'm getting rebuilds all of the time, especially when I change flake.nix
Nix will rebuild a derivation if any of its inputs change, which includes any
file contained by the source that is passed in. For example, if the build
expression specifies src = ./.;
then the crate will be rebuilt when any file
changes (including "unrelated" changes to flake.nix
)!
There are two main ways to avoid unnecessary builds:
- Use a source cleaning function which can omit any files know to not be
needed while building the crate (for example, all
*.nix
sources,flake.lock
, and so on). For examplecleanCargoSource
(see [API docs] for details) implements some good defaults for ignoring irrelevant files which are not needed by cargo. - Another option is to put the crate's source files into its own subdirectory
(e.g.
./mycrate
) and then set the build expression's source to that subdirectory (e.g.src = ./mycrate;
). Then, changes to files outside of that directory will be ignored and will not cause a rebuild
In certain (especially non-trivial) crane-based workflows, it's possible that a change to a given file might trigger rebuilds of certain seemingly unrelated derivations. This is most often caused by a subtle bug introducing undesired derivation inputs.
Debugging with nix-diff
An efficient way to debug such problems is to use nix-diff
to compare the derivation build plans:
# nix-diff does not support direct flake-urls so we'll need
# to get the actual derivation name
nix show-derivation .#affectedOutput | nix run nixpkgs#jq -- -r 'keys[0]' > before_drv
echo >> ./file/triggering/rebuild # cause a rebuild
nix show-derivation .#affectedOutput | nix run nixpkgs#jq -- -r 'keys[0]' > after_drv
nix run nixpkgs#nix-diff "$(cat before_drv)" "$(cat after_drv)"
Debugging with just nix
Another way to debug such problems is to use nix derivation show -r
to compare the derivation build plans:
nix derivation show -r .#affectedOutput > before
echo >> ./file/triggering/rebuild # cause a rebuild
nix derivation show -r .#affectedOutput > after
diff -u before after
The difference in the highest-level derivation should point to a direct cause of the rebuild (possibly a different lower-level input derivation which can be compared recursively).
I've used a source filter but cargo is still rebuilding all dependencies from scratch!
Another source of artifact invalidation is if
- A different set of dependency crates are being built between derivations
let
src = ...;
in
craneLib.buildPackage {
inherit src;
cargoArtifacts = craneLib.buildDepsOnly {
inherit src;
cargoExtraArgs = "-p foo"; # Only build the `foo` crate
};
# Oops, we're only building the `bar` crate now
# any dependency crates used by `bar` but not by `foo`
# will get built from scratch!
cargoExtraArgs = "-p bar";
}
- Another reason could be using different feature flags between derivations, which result in setting different feature flags for dependency crates themselves and causing a rebuild
let
src = ...;
in
craneLib.buildPackage {
inherit src;
cargoArtifacts = craneLib.buildDepsOnly {
inherit src;
cargoExtraArgs = "--no-default-features"; # Don't use any workspace features
};
# Oops, we're now building with an additional downstream feature flag which
# needs to build more crates which we do not have cached!
cargoExtraArgs = "--features feature-which-enables-downstream-feature";
}
If in doubt, double check that the same set of -p
/--package
and
--features
/--no-default-features
/--all-features
flags are used between all
buildDepsOnly
/cargoBuild
/cargoClippy
/buildPackage
derivations.
Mixing [package]
and [workspace]
definitions in the top-level Cargo.toml
Another potential pitfall is defining both [package]
and [workspace]
in the
project's top-level Cargo.toml
file. Although cargo allows both to be
defined, doing so results in cargo only operating on that package by default
(unless the --workspace
flag is passed in).
Any subsequent derivations which attempt to build with -p another-crate
might
not have their dependencies fully cached. Our recommendation is to only define
[package]
in the top-level Cargo.toml
if the workspace contains a single
crate; otherwise only [workspace]
should be defined.
Dependencies being rebuilt even with proper source filtering applied
If the dependency crates are being rebuilt even after proper source filtering
has been applied (i.e. the crate-depsOnly
derivation is NOT being rebuilt)
check that the same Rust/Cargo toolchain is being used when building artifacts
and vendoring crate sources.
The crate artifacts can only be used for the same compiler version, so if cargo sees artifacts for the wrong toolchain it will rebuild everything from scratch.
Note that each instance of crane
tied to a single Rust toolchain (by default
the one available in nixpkgs
, but this can be overridden by the caller). If
you are using multiple craneLib
instantiations and you see this occurring,
double check that they aren't being created with a different toolchain
(especially if cross-compilation is being used for the project).
Constantly rebuilding proc-macro dependencies dev
mode
A regression was introduced sometime around Rust 1.71.1 which changed how
debuginfo flags are passed to proc-macro crates when using a dev
profile.
If you are building with a dev
profile (i.e. not using release
builds),
you may want to set the following in .cargo/config.toml
:
[profile.dev.build-override]
debug = false
I see the pyo3
crate constantly rebuilding
The pyo3
crate uses checks $PYO3_PYTHON
for a path to the python
binary it
should use during the build. If this environment variable is not set, pyo3
will look for whatever version of python
is on the $PATH
, which
unfortunately results in the crate being rebuilt when $PATH
changes (i.e.
whenever the cargo artifacts are used in a derivation which may have different
build inputs).
The way to remedy this is to explicitly set PYO3_PYTHON
to point to the
version of python
that will be used by the derivation:
let
chosenPython = pkgs.python3;
in
craneLib.buildPackage {
env.PYO3_PYTHON = "${chosenPython}/bin/python";
nativeBuildInputs = [
chosenPython
];
# etc...
}
I see the bindgen
crate constantly rebuilding
If you are using rustPlatform.bindgenHook
it is worth noting that it will
propagate NIX_CFLAGS_COMPILE
via
BINDGEN_EXTRA_CLANG_ARGS
.
In order to support reproducible builds, this build hook will add
-frandom-seed=...
to
NIX_CFLAGS_COMPILE
based on the current derivation's hash.
Since dependencies are built in a separate derivation as the main package, each
derivation essentially gets a different value for -frandom-seed
. The bindgen
crate will observe this change and rebuild
itself.
A workaround for this is to set NIX_OUTPATH_USED_AS_RANDOM_SEED
to any
arbitrary 10 character string for all derivations which share artifacts
together.
buildPackage {
NIX_OUTPATH_USED_AS_RANDOM_SEED = "aaaaaaaaaa";
# other attributes omitted
}
I'm trying to build another cargo project from source which has no lock file
First consider if there is a release of this project available with a lock
file as it may be simpler and more consistent to use the exact dependencies
published by the project itself. Projects published on crates.io always come
with a lock file and nixpkgs
has a fetchCrate
fetcher which pulls straight
from crates.io.
If that is not an option, the next best thing is to generate your own
Cargo.lock
file and pass it in as an override by setting cargoLock = ./path/to/Cargo.lock
. If you are calling buildDepsOnly
or vendorCargoDeps
directly the value must be passed there; otherwise you can pass it into
buildPackage
or cargoBuild
and it will automatically passed through.
Note that the Cargo.lock
file must be accessible at evaluation time for the
dependency vendoring to work, meaning the file cannot be generated within the
same derivation that builds the project. It may come from another derivation,
but it may require enabling IFD if flakes are not used.
I need to patch Cargo.lock
but when I do the build fails
Dependency crates are vendored by reading Cargo.lock
at evaluation time and
not at build time. Thus using patches = [ ./patch-which-updates-lockfile.patch ];
may result in a situation where any new crates introduced by the patch cannot be
found by cargo.
It is possible to work around this limitation by patching Cargo.lock
in a
stand-alone derivation and passing that result to vendorCargoDeps
before
building the rest of the workspace.
let
patchedCargoLock = src = pkgs.stdenv.mkDerivation {
src = ./path/to/Cargo.lock;
patches = [
./update-cargo-lock.patch
];
installPhase = ''
runHook preInstall
mkdir -p $out
cp Cargo.lock $out
runHook postInstall
'';
};
in
craneLib.buildPackage {
cargoVendorDir = craneLib.vendorCargoDeps {
src = patchedCargoLock;
};
src = craneLib.cleanCargoSource ./.;
patches = [
./update-cargo-lock.patch
./some-other.patch
];
}
How can I build only a subset of a given cargo workspace?
By default, cargo will build the crate at the current directory when invoked; if the current directory holds a workspace, cargo will then build all crates within that workspace.
Sometimes it can be useful to only build a subset of a given workspace (e.g. only specific binaries are needed, or some crates cannot be built for certain platforms, etc.), and cargo can be instructed to do so.
Notably, it is possible to set:
cargoExtraArgs = "-p foo -p bar";
to only build thefoo
andbar
crates only, but nothing else in the workspacecargoExtraArgs = "--bin baz";
to only build thebaz
binary (from whatever crate defines it)cargoExtraArgs = "--workspace --exclude qux";
to build the entire cargo workspace except for thequx
crate.
Consider setting pname = "NAME_OF_THE_EXECUTABLE";
when building a single
executable from the workspace. Having the name of the package match the
executable name will allow the result to easily run via nix run
without
further configuration.
I'm having trouble building a project which uses include_str!
Double check if the source passed into the derivation is being cleaned or
filtered in anyway. Using craneLib.cleanCargoSource
(or
craneLib.filterCargoSources
directly) will omit any non-cargo and non-rust
files before trying to build the derivation. Thus if the project is trying to
use include_str!
, include_bytes!
, or any other attempt at accessing such a
file you may need to tweak the source filter to ensure the files are included.
Check out the source filtering section for more info!
Note that if the error is originating from a git-dependency, it may be a problem with the upstream source itself.
Dealing with sandbox-unfriendly build scripts
In general, most build scripts used by popular Rust projects are pretty good at only attempting to write to cargo's output directory. But every once in a while it is possible to find a build script somewhere deep in the dependency tree which assumes it can happily write to any directory it wants to (i.e. wherever its own sources happen to be present). For build scripts like these the best long term approach is almost always to fix them upstream; cargo's own documentation also warns against this:
In general, build scripts should not modify any files outside of OUT_DIR. It may seem fine on the first blush, but it does cause problems when you use such crate as a dependency, because there's an implicit invariant that sources in .cargo/registry should be immutable. cargo won't allow such scripts when packaging.
As a dire last resort it is possible to copy all vendored sources out of the (read-only) Nix store and into a writable directory. Keep in mind that doing so requires recursively copying all sources of all crates the project depends on during every single build; it comes with a performance and energy cost, and as such it is not recommended.
# You have been warned
buildPackage {
# other attributes omitted
postPatch = ''
mkdir -p "$TMPDIR/nix-vendor"
cp -Lr "$cargoVendorDir" -T "$TMPDIR/nix-vendor"
sed -i "s|$cargoVendorDir|$TMPDIR/nix-vendor/|g" "$TMPDIR/nix-vendor/config.toml"
chmod -R +w "$TMPDIR/nix-vendor"
cargoVendorDir="$TMPDIR/nix-vendor"
'';
}
Cargo workspace root (Cargo.toml) is not at the root of the derivation's source
Most cargo projects have their Cargo.toml
at the root of the source, but it's
still possible to build a project where the Cargo.toml
file is nested in a
deeper directory:
# Assuming that we have the following directory structure:
# ./flake.nix
# ./flake.lock
# ./nested
# ./nested/Cargo.toml
# ./nested/Cargo.lock
# ./nested/src/*.rs
craneLib.buildPackage {
src = myLib.cleanCargoSource ./.;
cargoLock = ./nested/Cargo.lock;
cargoToml = ./nested/Cargo.toml;
# Use a postUnpack hook to jump into our nested directory. This will work
# regardless of what the unpacked source is named (i.e. will avoid hashes
# when using the root path of a flake).
#
# The unpackPhase sets `$sourceRoot` to the directory that was unpacked
# but unfortunately `postUnpack` runs before the directory is actually
# changed so we'll do two things:
# 1. Jump into the directory we want (replace `nested` with your directory)
# 2. Overwrite the variable so when the default build scripts run they don't
# end up changing to a different directory again
postUnpack = ''
cd $sourceRoot/nested
sourceRoot="."
'';
}
found invalid metadata files for crate
errors
This error can occur when mixing components from two different Rust toolchains,
for example, using clippy
with artifacts produced from a different cargo
version. Check the configuration for specifying the exact Rust toolchain to be
used in the build:
let
rustToolchainForPkgs = p: ...;
rustToolchain = rustToolchainForPkgs pkgs;
in
# Incorrect usage, missing `clippy` override!
#(crane.mkLib pkgs).overrideScope (final: prev: {
# rustc = rustToolchain;
# cargo = rustToolchain;
# rustfmt = rustToolchain;
#});
# Correct usage (`overrideToolchain` handles the details for us)
(crane.mkLib pkgs).overrideToolchain rustToolchainForPkgs
A git dependency fails to find a file by a relative path
Sometimes various Rust projects are written in a way where a build script or
include_str!
invocation attempts to
read files outside of the crate's root, but this causes problems if such a
project is used as a git-dependency.
Normally when cargo downloads a package source from a registry like crates.io, it extracts each crate into its own separate directory (even if the upstream source is a workspace with multiple crates). This means that published crates usually do not suffer from this situation, however, cargo handles git dependencies in a different (i.e. inconsistent) manner: cargo will download the entire git directory but keep all files in place, which means that those crates happen to be able to rely on a file structure which matches the upstream repo.
Crane implements source fetching by following the behavior of the cargo vendor
command: each crate (whether it comes from a registry or a git repo) is extracted
in a separate directory. Thus the problem of trying to locate files outside of
the crate's (not the workspace's) root directory can also be demonstrated by
calling cargo vendor
and then following its instructions (normally copying
some configuration to .cargo/config.toml
) and then building as normal.
If building like this after running cargo vendor
succeeds but fails when
building with Crane please open an issue with a reproduction! However, if the
build fails even without Crane there are a few options to remedying the
problem:
- Consider reporting the situation to the upstream project and/or contributing a
change there. If the primary authors are not familiar with or users of either
Nix or Crane, consider explaining that their project cannot be used by anyone
who wants to vendor their sources (e.g. through using
cargo vendor
). - Consider forking the crate and remedying the problem there until it is accepted upstream
- Consider locally patching the dependency source while building with Nix
Controlling whether or not hooks run during buildDepsOnly
A typical project configuration will build a workspace's dependencies (without
the actual sources) during the buildDepsOnly
derivation, and later build the
project's sources in a second derivation. Sometimes this results in problems if
a build hook is accidentally configured to run in both derivations but expects
to use the real sources, for example.
Solution 1: explicitly configure the arguments to each derivation
let
# Explicitly split out common arguments
commonArgs = {
src = ./.;
# etc.
};
# Then explicitly define the arguments to `buildDepsOnly`
cargoArtifacts = craneLib.buildDepsOnly (commonArgs // {
postConfigure = ''
echo 'I am a hook which must only run during buildDepsOnly'
'';
});
};
in
craneLib.buildPackage (commonArgs // {
inherit cargoArtifacts;
preBuild = ''
echo 'I am a hook which must run with the real sources'
'';
})
Solution 2: check whether CRANE_BUILD_DEPS_ONLY
env var is set
Note that with this approach, changing the build hook will rebuild all dependencies, so consider the first solution above if possible.
craneLib.buildPackage {
src = ./.;
postConfigure = ''
# NB: use ''${var} to escape the ${...} so that Nix does not interpet it as
# an evaluation variable (since CRANE_BUILD_DEPS_ONLY is a shell variable)
if [ -n "''${CRANE_BUILD_DEPS_ONLY:-}"]; then
echo 'I am a hook which must only run during buildDepsOnly'
fi
'';
preBuild = ''
# NB: use ''${var} to escape the ${...} so that Nix does not interpet it as
# an evaluation variable (since CRANE_BUILD_DEPS_ONLY is a shell variable)
if [ -z "''${CRANE_BUILD_DEPS_ONLY:-}"]; then
echo 'I am a hook which must run with the real sources'
fi
'';
}
Advanced Techniques
This chapter contains various "advanced" techniques for configuring
and modifying the behaviors of crane
.
Most projects will likely not need to apply these patterns as they may require
extensive familiarity with Nix as well as crane
internals.
Overriding function behavior
At it's core, crane
is instantiated via pkgs.lib.newScope
which allows any
internal definition to be changed or replaced via .overrideScope
(which
behaves very much like applying overlays to nixpkgs). Although this mechanism is
incredibly powerful, care should be taken to avoid creating confusing or brittle
integrations built on undocumented details.
Note that crane
's stability guarantees (with respect to semantic versioning) only
apply to what has been documented at the API level. For example,
buildPackage
is documented to delegate to mkCargoDerivation
, so any changes
or overrides to mkCargoDerivation
's behavior will be observed by
buildPackage
. Other non-documented internal details, however, may change at
any time, so take care when reaching this deep into the internals.
Here is an example:
let
craneLib = (inputs.crane.mkLib pkgs).overrideScope (final: prev: {
# We override the behavior of `mkCargoDerivation` by adding a wrapper which
# will set a default value of `CARGO_PROFILE` when not set by the caller.
# This change will automatically be propagated to any other functions built
# on top of it (like `buildPackage`, `cargoBuild`, etc.)
mkCargoDerivation = args: prev.mkCargoDerivation ({
CARGO_PROFILE = "bench"; # E.g. always build in benchmark mode unless overridden
} // args);
});
in
{
# Build two different workspaces with the modified behavior above
foo = craneLib.buildPackage {
src = craneLib.cleanCargoSource ./foo;
};
bar = craneLib.buildPackage {
src = craneLib.cleanCargoSource ./bar;
};
}