1# Rust {#rust}
2
3To install the rust compiler and cargo put
4
5```nix
6{
7 environment.systemPackages = [
8 rustc
9 cargo
10 ];
11}
12```
13
14into your `configuration.nix` or bring them into scope with `nix-shell -p rustc cargo`.
15
16For other versions such as daily builds (beta and nightly),
17use either `rustup` from nixpkgs (which will manage the rust installation in your home directory),
18or use [community maintained Rust toolchains](#using-community-maintained-rust-toolchains).
19
20## `buildRustPackage`: Compiling Rust applications with Cargo {#compiling-rust-applications-with-cargo}
21
22Rust applications are packaged by using the `buildRustPackage` helper from `rustPlatform`:
23
24```nix
25{ lib, fetchFromGitHub, rustPlatform }:
26
27rustPlatform.buildRustPackage rec {
28 pname = "ripgrep";
29 version = "12.1.1";
30
31 src = fetchFromGitHub {
32 owner = "BurntSushi";
33 repo = pname;
34 rev = version;
35 hash = "sha256-+s5RBC3XSgb8omTbUNLywZnP6jSxZBKSS1BmXOjRF8M=";
36 };
37
38 cargoHash = "sha256-jtBw4ahSl88L0iuCXxQgZVm1EcboWRJMNtjxLVTtzts=";
39
40 meta = {
41 description = "A fast line-oriented regex search tool, similar to ag and ack";
42 homepage = "https://github.com/BurntSushi/ripgrep";
43 license = lib.licenses.unlicense;
44 maintainers = [];
45 };
46}
47```
48
49`buildRustPackage` requires either a `cargoHash` (preferred) or a
50`cargoSha256` attribute, computed over all crate sources of this package.
51`cargoHash` supports [SRI](https://www.w3.org/TR/SRI/) hashes and should be
52preferred over `cargoSha256` which was used for traditional Nix SHA-256 hashes.
53For example:
54
55```nix
56{
57 cargoHash = "sha256-l1vL2ZdtDRxSGvP0X/l3nMw8+6WF67KPutJEzUROjg8=";
58}
59```
60
61Exception: If the application has cargo `git` dependencies, the `cargoHash`/`cargoSha256`
62approach will not work, and you will need to copy the `Cargo.lock` file of the application
63to nixpkgs and continue with the next section for specifying the options of the `cargoLock`
64section.
65
66
67Both types of hashes are permitted when contributing to nixpkgs. The
68Cargo hash is obtained by inserting a fake checksum into the
69expression and building the package once. The correct checksum can
70then be taken from the failed build. A fake hash can be used for
71`cargoHash` as follows:
72
73```nix
74{
75 cargoHash = lib.fakeHash;
76}
77```
78
79For `cargoSha256` you can use:
80
81```nix
82{
83 cargoSha256 = lib.fakeSha256;
84}
85```
86
87Per the instructions in the [Cargo Book](https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html)
88best practices guide, Rust applications should always commit the `Cargo.lock`
89file in git to ensure a reproducible build. However, a few packages do not, and
90Nix depends on this file, so if it is missing you can use `cargoPatches` to
91apply it in the `patchPhase`. Consider sending a PR upstream with a note to the
92maintainer describing why it's important to include in the application.
93
94The fetcher will verify that the `Cargo.lock` file is in sync with the `src`
95attribute, and fail the build if not. It will also will compress the vendor
96directory into a tar.gz archive.
97
98The tarball with vendored dependencies contains a directory with the
99package's `name`, which is normally composed of `pname` and
100`version`. This means that the vendored dependencies hash
101(`cargoHash`/`cargoSha256`) is dependent on the package name and
102version. The `cargoDepsName` attribute can be used to use another name
103for the directory of vendored dependencies. For example, the hash can
104be made invariant to the version by setting `cargoDepsName` to
105`pname`:
106
107```nix
108rustPlatform.buildRustPackage rec {
109 pname = "broot";
110 version = "1.2.0";
111
112 src = fetchCrate {
113 inherit pname version;
114 hash = "sha256-aDQA4A5mScX9or3Lyiv/5GyAehidnpKKE0grhbP1Ctc=";
115 };
116
117 cargoHash = "sha256-tbrTbutUs5aPSV+yE0IBUZAAytgmZV7Eqxia7g+9zRs=";
118 cargoDepsName = pname;
119
120 # ...
121}
122```
123
124### Importing a `Cargo.lock` file {#importing-a-cargo.lock-file}
125
126Using a vendored hash (`cargoHash`/`cargoSha256`) is tedious when using
127`buildRustPackage` within a project, since it requires that the hash
128is updated after every change to `Cargo.lock`. Therefore,
129`buildRustPackage` also supports vendoring dependencies directly from
130a `Cargo.lock` file using the `cargoLock` argument. For example:
131
132```nix
133rustPlatform.buildRustPackage {
134 pname = "myproject";
135 version = "1.0.0";
136
137 cargoLock = {
138 lockFile = ./Cargo.lock;
139 };
140
141 # ...
142}
143```
144
145This will retrieve the dependencies using fixed-output derivations from
146the specified lockfile.
147
148One caveat is that `Cargo.lock` cannot be patched in the `patchPhase`
149because it runs after the dependencies have already been fetched. If
150you need to patch or generate the lockfile you can alternatively set
151`cargoLock.lockFileContents` to a string of its contents:
152
153```nix
154rustPlatform.buildRustPackage {
155 pname = "myproject";
156 version = "1.0.0";
157
158 cargoLock = let
159 fixupLockFile = path: f (builtins.readFile path);
160 in {
161 lockFileContents = fixupLockFile ./Cargo.lock;
162 };
163
164 # ...
165}
166```
167
168Note that setting `cargoLock.lockFile` or `cargoLock.lockFileContents`
169doesn't add a `Cargo.lock` to your `src`, and a `Cargo.lock` is still
170required to build a rust package. A simple fix is to use:
171
172```nix
173{
174 postPatch = ''
175 ln -s ${./Cargo.lock} Cargo.lock
176 '';
177}
178```
179
180The output hash of each dependency that uses a git source must be
181specified in the `outputHashes` attribute. For example:
182
183```nix
184rustPlatform.buildRustPackage rec {
185 pname = "myproject";
186 version = "1.0.0";
187
188 cargoLock = {
189 lockFile = ./Cargo.lock;
190 outputHashes = {
191 "finalfusion-0.14.0" = "17f4bsdzpcshwh74w5z119xjy2if6l2wgyjy56v621skr2r8y904";
192 };
193 };
194
195 # ...
196}
197```
198
199If you do not specify an output hash for a git dependency, building
200the package will fail and inform you of which crate needs to be
201added. To find the correct hash, you can first use `lib.fakeSha256` or
202`lib.fakeHash` as a stub hash. Building the package (and thus the
203vendored dependencies) will then inform you of the correct hash.
204
205For usage outside nixpkgs, `allowBuiltinFetchGit` could be used to
206avoid having to specify `outputHashes`. For example:
207
208```nix
209rustPlatform.buildRustPackage rec {
210 pname = "myproject";
211 version = "1.0.0";
212
213 cargoLock = {
214 lockFile = ./Cargo.lock;
215 allowBuiltinFetchGit = true;
216 };
217
218 # ...
219}
220```
221
222### Cargo features {#cargo-features}
223
224You can disable default features using `buildNoDefaultFeatures`, and
225extra features can be added with `buildFeatures`.
226
227If you want to use different features for check phase, you can use
228`checkNoDefaultFeatures` and `checkFeatures`. They are only passed to
229`cargo test` and not `cargo build`. If left unset, they default to
230`buildNoDefaultFeatures` and `buildFeatures`.
231
232For example:
233
234```nix
235rustPlatform.buildRustPackage rec {
236 pname = "myproject";
237 version = "1.0.0";
238
239 buildNoDefaultFeatures = true;
240 buildFeatures = [ "color" "net" ];
241
242 # disable network features in tests
243 checkFeatures = [ "color" ];
244
245 # ...
246}
247```
248
249### Cross compilation {#cross-compilation}
250
251By default, Rust packages are compiled for the host platform, just like any
252other package is. The `--target` passed to rust tools is computed from this.
253By default, it takes the `stdenv.hostPlatform.config` and replaces components
254where they are known to differ. But there are ways to customize the argument:
255
256 - To choose a different target by name, define
257 `stdenv.hostPlatform.rustc.config` as that name (a string), and that
258 name will be used instead.
259
260 For example:
261
262 ```nix
263 import <nixpkgs> {
264 crossSystem = (import <nixpkgs/lib>).systems.examples.armhf-embedded // {
265 rustc.config = "thumbv7em-none-eabi";
266 };
267 }
268 ```
269
270 will result in:
271
272 ```shell
273 --target thumbv7em-none-eabi
274 ```
275
276 - To pass a completely custom target, define
277 `stdenv.hostPlatform.rustc.config` with its name, and
278 `stdenv.hostPlatform.rustc.platform` with the value. The value will be
279 serialized to JSON in a file called
280 `${stdenv.hostPlatform.rustc.config}.json`, and the path of that file
281 will be used instead.
282
283 For example:
284
285 ```nix
286 import <nixpkgs> {
287 crossSystem = (import <nixpkgs/lib>).systems.examples.armhf-embedded // {
288 rustc.config = "thumb-crazy";
289 rustc.platform = { foo = ""; bar = ""; };
290 };
291 }
292 ```
293
294 will result in:
295
296 ```shell
297 --target /nix/store/asdfasdfsadf-thumb-crazy.json # contains {"foo":"","bar":""}
298 ```
299
300Note that currently custom targets aren't compiled with `std`, so `cargo test`
301will fail. This can be ignored by adding `doCheck = false;` to your derivation.
302
303### Running package tests {#running-package-tests}
304
305When using `buildRustPackage`, the `checkPhase` is enabled by default and runs
306`cargo test` on the package to build. To make sure that we don't compile the
307sources twice and to actually test the artifacts that will be used at runtime,
308the tests will be ran in the `release` mode by default.
309
310However, in some cases the test-suite of a package doesn't work properly in the
311`release` mode. For these situations, the mode for `checkPhase` can be changed like
312so:
313
314```nix
315rustPlatform.buildRustPackage {
316 /* ... */
317 checkType = "debug";
318}
319```
320
321Please note that the code will be compiled twice here: once in `release` mode
322for the `buildPhase`, and again in `debug` mode for the `checkPhase`.
323
324Test flags, e.g., `--package foo`, can be passed to `cargo test` via the
325`cargoTestFlags` attribute.
326
327Another attribute, called `checkFlags`, is used to pass arguments to the test
328binary itself, as stated
329[here](https://doc.rust-lang.org/cargo/commands/cargo-test.html).
330
331#### Tests relying on the structure of the `target/` directory {#tests-relying-on-the-structure-of-the-target-directory}
332
333Some tests may rely on the structure of the `target/` directory. Those tests
334are likely to fail because we use `cargo --target` during the build. This means that
335the artifacts
336[are stored in `target/<architecture>/release/`](https://doc.rust-lang.org/cargo/guide/build-cache.html),
337rather than in `target/release/`.
338
339This can only be worked around by patching the affected tests accordingly.
340
341#### Disabling package-tests {#disabling-package-tests}
342
343In some instances, it may be necessary to disable testing altogether (with `doCheck = false;`):
344
345* If no tests exist -- the `checkPhase` should be explicitly disabled to skip
346 unnecessary build steps to speed up the build.
347* If tests are highly impure (e.g. due to network usage).
348
349There will obviously be some corner-cases not listed above where it's sensible to disable tests.
350The above are just guidelines, and exceptions may be granted on a case-by-case basis.
351
352However, please check if it's possible to disable a problematic subset of the
353test suite and leave a comment explaining your reasoning.
354
355This can be achieved with `--skip` in `checkFlags`:
356
357```nix
358rustPlatform.buildRustPackage {
359 /* ... */
360 checkFlags = [
361 # reason for disabling test
362 "--skip=example::tests:example_test"
363 ];
364}
365```
366
367#### Using `cargo-nextest` {#using-cargo-nextest}
368
369Tests can be run with [cargo-nextest](https://github.com/nextest-rs/nextest)
370by setting `useNextest = true`. The same options still apply, but nextest
371accepts a different set of arguments and the settings might need to be
372adapted to be compatible with cargo-nextest.
373
374```nix
375rustPlatform.buildRustPackage {
376 /* ... */
377 useNextest = true;
378}
379```
380
381#### Setting `test-threads` {#setting-test-threads}
382
383`buildRustPackage` will use parallel test threads by default,
384sometimes it may be necessary to disable this so the tests run consecutively.
385
386```nix
387rustPlatform.buildRustPackage {
388 /* ... */
389 dontUseCargoParallelTests = true;
390}
391```
392
393### Building a package in `debug` mode {#building-a-package-in-debug-mode}
394
395By default, `buildRustPackage` will use `release` mode for builds. If a package
396should be built in `debug` mode, it can be configured like so:
397
398```nix
399rustPlatform.buildRustPackage {
400 /* ... */
401 buildType = "debug";
402}
403```
404
405In this scenario, the `checkPhase` will be ran in `debug` mode as well.
406
407### Custom `build`/`install`-procedures {#custom-buildinstall-procedures}
408
409Some packages may use custom scripts for building/installing, e.g. with a `Makefile`.
410In these cases, it's recommended to override the `buildPhase`/`installPhase`/`checkPhase`.
411
412Otherwise, some steps may fail because of the modified directory structure of `target/`.
413
414### Building a crate with an absent or out-of-date Cargo.lock file {#building-a-crate-with-an-absent-or-out-of-date-cargo.lock-file}
415
416`buildRustPackage` needs a `Cargo.lock` file to get all dependencies in the
417source code in a reproducible way. If it is missing or out-of-date one can use
418the `cargoPatches` attribute to update or add it.
419
420```nix
421rustPlatform.buildRustPackage rec {
422 # ...
423 cargoPatches = [
424 # a patch file to add/update Cargo.lock in the source code
425 ./add-Cargo.lock.patch
426 ];
427}
428```
429
430### Compiling non-Rust packages that include Rust code {#compiling-non-rust-packages-that-include-rust-code}
431
432Several non-Rust packages incorporate Rust code for performance- or
433security-sensitive parts. `rustPlatform` exposes several functions and
434hooks that can be used to integrate Cargo in non-Rust packages.
435
436#### Vendoring of dependencies {#vendoring-of-dependencies}
437
438Since network access is not allowed in sandboxed builds, Rust crate
439dependencies need to be retrieved using a fetcher. `rustPlatform`
440provides the `fetchCargoTarball` fetcher, which vendors all
441dependencies of a crate. For example, given a source path `src`
442containing `Cargo.toml` and `Cargo.lock`, `fetchCargoTarball`
443can be used as follows:
444
445```nix
446{
447 cargoDeps = rustPlatform.fetchCargoTarball {
448 inherit src;
449 hash = "sha256-BoHIN/519Top1NUBjpB/oEMqi86Omt3zTQcXFWqrek0=";
450 };
451}
452```
453
454The `src` attribute is required, as well as a hash specified through
455one of the `hash` attribute. The following optional attributes can
456also be used:
457
458* `name`: the name that is used for the dependencies tarball. If
459 `name` is not specified, then the name `cargo-deps` will be used.
460* `sourceRoot`: when the `Cargo.lock`/`Cargo.toml` are in a
461 subdirectory, `sourceRoot` specifies the relative path to these
462 files.
463* `patches`: patches to apply before vendoring. This is useful when
464 the `Cargo.lock`/`Cargo.toml` files need to be patched before
465 vendoring.
466
467If a `Cargo.lock` file is available, you can alternatively use the
468`importCargoLock` function. In contrast to `fetchCargoTarball`, this
469function does not require a hash (unless git dependencies are used)
470and fetches every dependency as a separate fixed-output derivation.
471`importCargoLock` can be used as follows:
472
473```nix
474{
475 cargoDeps = rustPlatform.importCargoLock {
476 lockFile = ./Cargo.lock;
477 };
478}
479```
480
481If the `Cargo.lock` file includes git dependencies, then their output
482hashes need to be specified since they are not available through the
483lock file. For example:
484
485```nix
486{
487 cargoDeps = rustPlatform.importCargoLock {
488 lockFile = ./Cargo.lock;
489 outputHashes = {
490 "rand-0.8.3" = "0ya2hia3cn31qa8894s3av2s8j5bjwb6yq92k0jsnlx7jid0jwqa";
491 };
492 };
493}
494```
495
496If you do not specify an output hash for a git dependency, building
497`cargoDeps` will fail and inform you of which crate needs to be
498added. To find the correct hash, you can first use `lib.fakeSha256` or
499`lib.fakeHash` as a stub hash. Building `cargoDeps` will then inform
500you of the correct hash.
501
502#### Hooks {#hooks}
503
504`rustPlatform` provides the following hooks to automate Cargo builds:
505
506* `cargoSetupHook`: configure Cargo to use dependencies vendored
507 through `fetchCargoTarball`. This hook uses the `cargoDeps`
508 environment variable to find the vendored dependencies. If a project
509 already vendors its dependencies, the variable `cargoVendorDir` can
510 be used instead. When the `Cargo.toml`/`Cargo.lock` files are not in
511 `sourceRoot`, then the optional `cargoRoot` is used to specify the
512 Cargo root directory relative to `sourceRoot`.
513* `cargoBuildHook`: use Cargo to build a crate. If the crate to be
514 built is a crate in e.g. a Cargo workspace, the relative path to the
515 crate to build can be set through the optional `buildAndTestSubdir`
516 environment variable. Features can be specified with
517 `cargoBuildNoDefaultFeatures` and `cargoBuildFeatures`. Additional
518 Cargo build flags can be passed through `cargoBuildFlags`.
519* `maturinBuildHook`: use [Maturin](https://github.com/PyO3/maturin)
520 to build a Python wheel. Similar to `cargoBuildHook`, the optional
521 variable `buildAndTestSubdir` can be used to build a crate in a
522 Cargo workspace. Additional Maturin flags can be passed through
523 `maturinBuildFlags`.
524* `cargoCheckHook`: run tests using Cargo. The build type for checks
525 can be set using `cargoCheckType`. Features can be specified with
526 `cargoCheckNoDefaultFeatures` and `cargoCheckFeatures`. Additional
527 flags can be passed to the tests using `checkFlags` and
528 `checkFlagsArray`. By default, tests are run in parallel. This can
529 be disabled by setting `dontUseCargoParallelTests`.
530* `cargoNextestHook`: run tests using
531 [cargo-nextest](https://github.com/nextest-rs/nextest). The same
532 options for `cargoCheckHook` also applies to `cargoNextestHook`.
533* `cargoInstallHook`: install binaries and static/shared libraries
534 that were built using `cargoBuildHook`.
535* `bindgenHook`: for crates which use `bindgen` as a build dependency, lets
536 `bindgen` find `libclang` and `libclang` find the libraries in `buildInputs`.
537
538#### Examples {#examples}
539
540#### Python package using `setuptools-rust` {#python-package-using-setuptools-rust}
541
542For Python packages using `setuptools-rust`, you can use
543`fetchCargoTarball` and `cargoSetupHook` to retrieve and set up Cargo
544dependencies. The build itself is then performed by
545`buildPythonPackage`.
546
547The following example outlines how the `tokenizers` Python package is
548built. Since the Python package is in the `source/bindings/python`
549directory of the `tokenizers` project's source archive, we use
550`sourceRoot` to point the tooling to this directory:
551
552```nix
553{ fetchFromGitHub
554, buildPythonPackage
555, cargo
556, rustPlatform
557, rustc
558, setuptools-rust
559}:
560
561buildPythonPackage rec {
562 pname = "tokenizers";
563 version = "0.10.0";
564
565 src = fetchFromGitHub {
566 owner = "huggingface";
567 repo = pname;
568 rev = "python-v${version}";
569 hash = "sha256-rQ2hRV52naEf6PvRsWVCTN7B1oXAQGmnpJw4iIdhamw=";
570 };
571
572 cargoDeps = rustPlatform.fetchCargoTarball {
573 inherit src sourceRoot;
574 name = "${pname}-${version}";
575 hash = "sha256-miW//pnOmww2i6SOGbkrAIdc/JMDT4FJLqdMFojZeoY=";
576 };
577
578 sourceRoot = "${src.name}/bindings/python";
579
580 nativeBuildInputs = [
581 cargo
582 rustPlatform.cargoSetupHook
583 rustc
584 setuptools-rust
585 ];
586
587 # ...
588}
589```
590
591In some projects, the Rust crate is not in the main Python source
592directory. In such cases, the `cargoRoot` attribute can be used to
593specify the crate's directory relative to `sourceRoot`. In the
594following example, the crate is in `src/rust`, as specified in the
595`cargoRoot` attribute. Note that we also need to specify the correct
596path for `fetchCargoTarball`.
597
598```nix
599
600{ buildPythonPackage
601, fetchPypi
602, rustPlatform
603, setuptools-rust
604, openssl
605}:
606
607buildPythonPackage rec {
608 pname = "cryptography";
609 version = "3.4.2"; # Also update the hash in vectors.nix
610
611 src = fetchPypi {
612 inherit pname version;
613 hash = "sha256-xGDilsjLOnls3MfVbGKnj80KCUCczZxlis5PmHzpNcQ=";
614 };
615
616 cargoDeps = rustPlatform.fetchCargoTarball {
617 inherit src;
618 sourceRoot = "${pname}-${version}/${cargoRoot}";
619 name = "${pname}-${version}";
620 hash = "sha256-PS562W4L1NimqDV2H0jl5vYhL08H9est/pbIxSdYVfo=";
621 };
622
623 cargoRoot = "src/rust";
624
625 # ...
626}
627```
628
629#### Python package using `maturin` {#python-package-using-maturin}
630
631Python packages that use [Maturin](https://github.com/PyO3/maturin)
632can be built with `fetchCargoTarball`, `cargoSetupHook`, and
633`maturinBuildHook`. For example, the following (partial) derivation
634builds the `retworkx` Python package. `fetchCargoTarball` and
635`cargoSetupHook` are used to fetch and set up the crate dependencies.
636`maturinBuildHook` is used to perform the build.
637
638```nix
639{ lib
640, buildPythonPackage
641, rustPlatform
642, fetchFromGitHub
643}:
644
645buildPythonPackage rec {
646 pname = "retworkx";
647 version = "0.6.0";
648
649 src = fetchFromGitHub {
650 owner = "Qiskit";
651 repo = "retworkx";
652 rev = version;
653 hash = "sha256-11n30ldg3y3y6qxg3hbj837pnbwjkqw3nxq6frds647mmmprrd20=";
654 };
655
656 cargoDeps = rustPlatform.fetchCargoTarball {
657 inherit src;
658 name = "${pname}-${version}";
659 hash = "sha256-heOBK8qi2nuc/Ib+I/vLzZ1fUUD/G/KTw9d7M4Hz5O0=";
660 };
661
662 format = "pyproject";
663
664 nativeBuildInputs = with rustPlatform; [ cargoSetupHook maturinBuildHook ];
665
666 # ...
667}
668```
669
670#### Rust package built with `meson` {#rust-package-built-with-meson}
671
672Some projects, especially GNOME applications, are built with the Meson Build System instead of calling Cargo directly. Using `rustPlatform.buildRustPackage` may successfully build the main program, but related files will be missing. Instead, you need to set up Cargo dependencies with `fetchCargoTarball` and `cargoSetupHook` and leave the rest to Meson. `rust` and `cargo` are still needed in `nativeBuildInputs` for Meson to use.
673
674```nix
675{ lib
676, stdenv
677, fetchFromGitLab
678, meson
679, ninja
680, pkg-config
681, rustPlatform
682, rustc
683, cargo
684, wrapGAppsHook4
685, blueprint-compiler
686, libadwaita
687, libsecret
688, tracker
689}:
690
691stdenv.mkDerivation rec {
692 pname = "health";
693 version = "0.95.0";
694
695 src = fetchFromGitLab {
696 domain = "gitlab.gnome.org";
697 owner = "World";
698 repo = "health";
699 rev = version;
700 hash = "sha256-PrNPprSS98yN8b8yw2G6hzTSaoE65VbsM3q7FVB4mds=";
701 };
702
703 cargoDeps = rustPlatform.fetchCargoTarball {
704 inherit src;
705 name = "${pname}-${version}";
706 hash = "sha256-8fa3fa+sFi5H+49B5sr2vYPkp9C9s6CcE0zv4xB8gww=";
707 };
708
709 nativeBuildInputs = [
710 meson
711 ninja
712 pkg-config
713 rustPlatform.cargoSetupHook
714 rustc
715 cargo
716 wrapGAppsHook4
717 blueprint-compiler
718 ];
719
720 buildInputs = [
721 libadwaita
722 libsecret
723 tracker
724 ];
725
726 # ...
727}
728```
729
730## `buildRustCrate`: Compiling Rust crates using Nix instead of Cargo {#compiling-rust-crates-using-nix-instead-of-cargo}
731
732### Simple operation {#simple-operation}
733
734When run, `cargo build` produces a file called `Cargo.lock`,
735containing pinned versions of all dependencies. Nixpkgs contains a
736tool called `crate2Nix` (`nix-shell -p crate2nix`), which can be
737used to turn a `Cargo.lock` into a Nix expression. That Nix
738expression calls `rustc` directly (hence bypassing Cargo), and can
739be used to compile a crate and all its dependencies.
740
741See [`crate2nix`'s documentation](https://github.com/kolloch/crate2nix#known-restrictions)
742for instructions on how to use it.
743
744### Handling external dependencies {#handling-external-dependencies}
745
746Some crates require external libraries. For crates from
747[crates.io](https://crates.io), such libraries can be specified in
748`defaultCrateOverrides` package in nixpkgs itself.
749
750Starting from that file, one can add more overrides, to add features
751or build inputs by overriding the hello crate in a separate file.
752
753```nix
754with import <nixpkgs> {};
755((import ./hello.nix).hello {}).override {
756 crateOverrides = defaultCrateOverrides // {
757 hello = attrs: { buildInputs = [ openssl ]; };
758 };
759}
760```
761
762Here, `crateOverrides` is expected to be a attribute set, where the
763key is the crate name without version number and the value a function.
764The function gets all attributes passed to `buildRustCrate` as first
765argument and returns a set that contains all attribute that should be
766overwritten.
767
768For more complicated cases, such as when parts of the crate's
769derivation depend on the crate's version, the `attrs` argument of
770the override above can be read, as in the following example, which
771patches the derivation:
772
773```nix
774with import <nixpkgs> {};
775((import ./hello.nix).hello {}).override {
776 crateOverrides = defaultCrateOverrides // {
777 hello = attrs: lib.optionalAttrs (lib.versionAtLeast attrs.version "1.0") {
778 postPatch = ''
779 substituteInPlace lib/zoneinfo.rs \
780 --replace-fail "/usr/share/zoneinfo" "${tzdata}/share/zoneinfo"
781 '';
782 };
783 };
784}
785```
786
787Another situation is when we want to override a nested
788dependency. This actually works in the exact same way, since the
789`crateOverrides` parameter is forwarded to the crate's
790dependencies. For instance, to override the build inputs for crate
791`libc` in the example above, where `libc` is a dependency of the main
792crate, we could do:
793
794```nix
795with import <nixpkgs> {};
796((import hello.nix).hello {}).override {
797 crateOverrides = defaultCrateOverrides // {
798 libc = attrs: { buildInputs = []; };
799 };
800}
801```
802
803### Options and phases configuration {#options-and-phases-configuration}
804
805Actually, the overrides introduced in the previous section are more
806general. A number of other parameters can be overridden:
807
808- The version of `rustc` used to compile the crate:
809
810 ```nix
811 (hello {}).override { rust = pkgs.rust; }
812 ```
813
814- Whether to build in release mode or debug mode (release mode by
815 default):
816
817 ```nix
818 (hello {}).override { release = false; }
819 ```
820
821- Whether to print the commands sent to `rustc` when building
822 (equivalent to `--verbose` in cargo:
823
824 ```nix
825 (hello {}).override { verbose = false; }
826 ```
827
828- Extra arguments to be passed to `rustc`:
829
830 ```nix
831 (hello {}).override { extraRustcOpts = "-Z debuginfo=2"; }
832 ```
833
834- Phases, just like in any other derivation, can be specified using
835 the following attributes: `preUnpack`, `postUnpack`, `prePatch`,
836 `patches`, `postPatch`, `preConfigure` (in the case of a Rust crate,
837 this is run before calling the "build" script), `postConfigure`
838 (after the "build" script),`preBuild`, `postBuild`, `preInstall` and
839 `postInstall`. As an example, here is how to create a new module
840 before running the build script:
841
842 ```nix
843 (hello {}).override {
844 preConfigure = ''
845 echo "pub const PATH=\"${hi.out}\";" >> src/path.rs"
846 '';
847 }
848 ```
849
850### Setting Up `nix-shell` {#setting-up-nix-shell}
851
852Oftentimes you want to develop code from within `nix-shell`. Unfortunately
853`buildRustCrate` does not support common `nix-shell` operations directly
854(see [this issue](https://github.com/NixOS/nixpkgs/issues/37945))
855so we will use `stdenv.mkDerivation` instead.
856
857Using the example `hello` project above, we want to do the following:
858
859- Have access to `cargo` and `rustc`
860- Have the `openssl` library available to a crate through it's _normal_
861 compilation mechanism (`pkg-config`).
862
863A typical `shell.nix` might look like:
864
865```nix
866with import <nixpkgs> {};
867
868stdenv.mkDerivation {
869 name = "rust-env";
870 nativeBuildInputs = [
871 rustc cargo
872
873 # Example Build-time Additional Dependencies
874 pkg-config
875 ];
876 buildInputs = [
877 # Example Run-time Additional Dependencies
878 openssl
879 ];
880
881 # Set Environment Variables
882 RUST_BACKTRACE = 1;
883}
884```
885
886You should now be able to run the following:
887
888```ShellSession
889$ nix-shell --pure
890$ cargo build
891$ cargo test
892```
893
894## Using community maintained Rust toolchains {#using-community-maintained-rust-toolchains}
895
896::: {.note}
897The following projects cannot be used within Nixpkgs since [Import From Derivation](https://nixos.org/manual/nix/unstable/language/import-from-derivation) (IFD) is disallowed in Nixpkgs.
898To package things that require Rust nightly, `RUSTC_BOOTSTRAP = true;` can sometimes be used as a hack.
899:::
900
901There are two community maintained approaches to Rust toolchain management:
902- [oxalica's Rust overlay](https://github.com/oxalica/rust-overlay)
903- [fenix](https://github.com/nix-community/fenix)
904
905Despite their names, both projects provides a similar set of packages and overlays under different APIs.
906
907Oxalica's overlay allows you to select a particular Rust version without you providing a hash or a flake input,
908but comes with a larger git repository than fenix.
909
910Fenix also provides rust-analyzer nightly in addition to the Rust toolchains.
911
912Both oxalica's overlay and fenix better integrate with nix and cache optimizations.
913Because of this and ergonomics, either of those community projects
914should be preferred to the Mozilla's Rust overlay ([nixpkgs-mozilla](https://github.com/mozilla/nixpkgs-mozilla)).
915
916The following documentation demonstrates examples using fenix and oxalica's Rust overlay
917with `nix-shell` and building derivations. More advanced usages like flake usage
918are documented in their own repositories.
919
920### Using Rust nightly with `nix-shell` {#using-rust-nightly-with-nix-shell}
921
922Here is a simple `shell.nix` that provides Rust nightly (default profile) using fenix:
923
924```nix
925with import <nixpkgs> { };
926let
927 fenix = callPackage
928 (fetchFromGitHub {
929 owner = "nix-community";
930 repo = "fenix";
931 # commit from: 2023-03-03
932 rev = "e2ea04982b892263c4d939f1cc3bf60a9c4deaa1";
933 hash = "sha256-AsOim1A8KKtMWIxG+lXh5Q4P2bhOZjoUhFWJ1EuZNNk=";
934 })
935 { };
936in
937mkShell {
938 name = "rust-env";
939 nativeBuildInputs = [
940 # Note: to use stable, just replace `default` with `stable`
941 fenix.default.toolchain
942
943 # Example Build-time Additional Dependencies
944 pkg-config
945 ];
946 buildInputs = [
947 # Example Run-time Additional Dependencies
948 openssl
949 ];
950
951 # Set Environment Variables
952 RUST_BACKTRACE = 1;
953}
954```
955
956Save this to `shell.nix`, then run:
957
958```ShellSession
959$ rustc --version
960rustc 1.69.0-nightly (13471d3b2 2023-03-02)
961```
962
963To see that you are using nightly.
964
965Oxalica's Rust overlay has more complete examples of `shell.nix` (and cross compilation) under its
966[`examples` directory](https://github.com/oxalica/rust-overlay/tree/e53e8853aa7b0688bc270e9e6a681d22e01cf299/examples).
967
968### Using Rust nightly in a derivation with `buildRustPackage` {#using-rust-nightly-in-a-derivation-with-buildrustpackage}
969
970You can also use Rust nightly to build rust packages using `makeRustPlatform`.
971The below snippet demonstrates invoking `buildRustPackage` with a Rust toolchain from oxalica's overlay:
972
973```nix
974with import <nixpkgs>
975{
976 overlays = [
977 (import (fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz"))
978 ];
979};
980let
981 rustPlatform = makeRustPlatform {
982 cargo = rust-bin.selectLatestNightlyWith (toolchain: toolchain.default);
983 rustc = rust-bin.selectLatestNightlyWith (toolchain: toolchain.default);
984 };
985in
986
987rustPlatform.buildRustPackage rec {
988 pname = "ripgrep";
989 version = "12.1.1";
990
991 src = fetchFromGitHub {
992 owner = "BurntSushi";
993 repo = "ripgrep";
994 rev = version;
995 hash = "sha256-+s5RBC3XSgb8omTbUNLywZnP6jSxZBKSS1BmXOjRF8M=";
996 };
997
998 cargoHash = "sha256-l1vL2ZdtDRxSGvP0X/l3nMw8+6WF67KPutJEzUROjg8=";
999
1000 doCheck = false;
1001
1002 meta = {
1003 description = "A fast line-oriented regex search tool, similar to ag and ack";
1004 homepage = "https://github.com/BurntSushi/ripgrep";
1005 license = with lib.licenses; [ mit unlicense ];
1006 maintainers = with lib.maintainers; [];
1007 };
1008}
1009```
1010
1011Follow the below steps to try that snippet.
10121. save the above snippet as `default.nix` in that directory
10132. cd into that directory and run `nix-build`
1014
1015Fenix also has examples with `buildRustPackage`,
1016[crane](https://github.com/ipetkov/crane),
1017[naersk](https://github.com/nix-community/naersk),
1018and cross compilation in its [Examples](https://github.com/nix-community/fenix#examples) section.
1019
1020## Using `git bisect` on the Rust compiler {#using-git-bisect-on-the-rust-compiler}
1021
1022Sometimes an upgrade of the Rust compiler (`rustc`) will break a
1023downstream package. In these situations, being able to `git bisect`
1024the `rustc` version history to find the offending commit is quite
1025useful. Nixpkgs makes it easy to do this.
1026
1027First, roll back your nixpkgs to a commit in which its `rustc` used
1028*the most recent one which doesn't have the problem.* You'll need
1029to do this because of `rustc`'s extremely aggressive
1030version-pinning.
1031
1032Next, add the following overlay, updating the Rust version to the
1033one in your rolled-back nixpkgs, and replacing `/git/scratch/rust`
1034with the path into which you have `git clone`d the `rustc` git
1035repository:
1036
1037```nix
1038 (final: prev: /*lib.optionalAttrs prev.stdenv.targetPlatform.isAarch64*/ {
1039 rust_1_72 =
1040 lib.updateManyAttrsByPath [{
1041 path = [ "packages" "stable" ];
1042 update = old: old.overrideScope(final: prev: {
1043 rustc-unwrapped = prev.rustc-unwrapped.overrideAttrs (_: {
1044 src = lib.cleanSource /git/scratch/rust;
1045 # do *not* put passthru.isReleaseTarball=true here
1046 });
1047 });
1048 }]
1049 prev.rust_1_72;
1050 })
1051```
1052
1053If the problem you're troubleshooting only manifests when
1054cross-compiling you can uncomment the `lib.optionalAttrs` in the
1055example above, and replace `isAarch64` with the target that is
1056having problems. This will speed up your bisect quite a bit, since
1057the host compiler won't need to be rebuilt.
1058
1059Now, you can start a `git bisect` in the directory where you checked
1060out the `rustc` source code. It is recommended to select the
1061endpoint commits by searching backwards from `origin/master` for the
1062*commits which added the release notes for the versions in
1063question.* If you set the endpoints to commits on the release
1064branches (i.e. the release tags), git-bisect will often get confused
1065by the complex merge-commit structures it will need to traverse.
1066
1067The command loop you'll want to use for bisecting looks like this:
1068
1069```bash
1070git bisect {good,bad} # depending on result of last build
1071git submodule update --init
1072CARGO_NET_OFFLINE=false cargo vendor \
1073 --sync ./src/tools/cargo/Cargo.toml \
1074 --sync ./src/tools/rust-analyzer/Cargo.toml \
1075 --sync ./compiler/rustc_codegen_cranelift/Cargo.toml \
1076 --sync ./src/bootstrap/Cargo.toml
1077nix-build $NIXPKGS -A package-broken-by-rust-changes
1078```
1079
1080The `git submodule update --init` and `cargo vendor` commands above
1081require network access, so they can't be performed from within the
1082`rustc` derivation, unfortunately.