Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
at 20.09 493 lines 16 kB view raw view rendered
1--- 2title: Rust 3author: Matthias Beyer 4date: 2017-03-05 5--- 6 7# Rust 8 9To install the rust compiler and cargo put 10 11``` 12rustc 13cargo 14``` 15 16into the `environment.systemPackages` or bring them into 17scope with `nix-shell -p rustc cargo`. 18 19For daily builds (beta and nightly) use either rustup from 20nixpkgs or use the [Rust nightlies 21overlay](#using-the-rust-nightlies-overlay). 22 23## Compiling Rust applications with Cargo 24 25Rust applications are packaged by using the `buildRustPackage` helper from `rustPlatform`: 26 27``` 28rustPlatform.buildRustPackage rec { 29 pname = "ripgrep"; 30 version = "11.0.2"; 31 32 src = fetchFromGitHub { 33 owner = "BurntSushi"; 34 repo = pname; 35 rev = version; 36 sha256 = "1iga3320mgi7m853la55xip514a3chqsdi1a1rwv25lr9b1p7vd3"; 37 }; 38 39 cargoSha256 = "17ldqr3asrdcsh4l29m3b5r37r5d0b3npq1lrgjmxb6vlx6a36qh"; 40 41 meta = with stdenv.lib; { 42 description = "A fast line-oriented regex search tool, similar to ag and ack"; 43 homepage = "https://github.com/BurntSushi/ripgrep"; 44 license = licenses.unlicense; 45 maintainers = [ maintainers.tailhook ]; 46 }; 47} 48``` 49 50`buildRustPackage` requires a `cargoSha256` attribute which is computed over 51all crate sources of this package. Currently it is obtained by inserting a 52fake checksum into the expression and building the package once. The correct 53checksum can be then take from the failed build. 54 55Per the instructions in the [Cargo Book](https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html) 56best practices guide, Rust applications should always commit the `Cargo.lock` 57file in git to ensure a reproducible build. However, a few packages do not, and 58Nix depends on this file, so if it missing you can use `cargoPatches` to apply 59it in the `patchPhase`. Consider sending a PR upstream with a note to the 60maintainer describing why it's important to include in the application. 61 62The fetcher will verify that the `Cargo.lock` file is in sync with the `src` 63attribute, and fail the build if not. It will also will compress the vendor 64directory into a tar.gz archive. 65 66### Building a crate for a different target 67 68To build your crate with a different cargo `--target` simply specify the `target` attribute: 69 70```nix 71pkgs.rustPlatform.buildRustPackage { 72 (...) 73 target = "x86_64-fortanix-unknown-sgx"; 74} 75``` 76 77### Running package tests 78 79When using `buildRustPackage`, the `checkPhase` is enabled by default and runs 80`cargo test` on the package to build. To make sure that we don't compile the 81sources twice and to actually test the artifacts that will be used at runtime, 82the tests will be ran in the `release` mode by default. 83 84However, in some cases the test-suite of a package doesn't work properly in the 85`release` mode. For these situations, the mode for `checkPhase` can be changed like 86so: 87 88```nix 89rustPlatform.buildRustPackage { 90 /* ... */ 91 checkType = "debug"; 92} 93``` 94 95Please note that the code will be compiled twice here: once in `release` mode 96for the `buildPhase`, and again in `debug` mode for the `checkPhase`. 97 98#### Tests relying on the structure of the `target/` directory 99 100Some tests may rely on the structure of the `target/` directory. Those tests 101are likely to fail because we use `cargo --target` during the build. This means that 102the artifacts 103[are stored in `target/<architecture>/release/`](https://doc.rust-lang.org/cargo/guide/build-cache.html), 104rather than in `target/release/`. 105 106This can only be worked around by patching the affected tests accordingly. 107 108#### Disabling package-tests 109 110In some instances, it may be necessary to disable testing altogether (with `doCheck = false;`): 111 112* If no tests exist -- the `checkPhase` should be explicitly disabled to skip 113 unnecessary build steps to speed up the build. 114* If tests are highly impure (e.g. due to network usage). 115 116There will obviously be some corner-cases not listed above where it's sensible to disable tests. 117The above are just guidelines, and exceptions may be granted on a case-by-case basis. 118 119However, please check if it's possible to disable a problematic subset of the 120test suite and leave a comment explaining your reasoning. 121 122### Building a package in `debug` mode 123 124By default, `buildRustPackage` will use `release` mode for builds. If a package 125should be built in `debug` mode, it can be configured like so: 126 127```nix 128rustPlatform.buildRustPackage { 129 /* ... */ 130 buildType = "debug"; 131} 132``` 133 134In this scenario, the `checkPhase` will be ran in `debug` mode as well. 135 136### Custom `build`/`install`-procedures 137 138Some packages may use custom scripts for building/installing, e.g. with a `Makefile`. 139In these cases, it's recommended to override the `buildPhase`/`installPhase`/`checkPhase`. 140 141Otherwise, some steps may fail because of the modified directory structure of `target/`. 142 143### Building a crate with an absent or out-of-date Cargo.lock file 144 145`buildRustPackage` needs a `Cargo.lock` file to get all dependencies in the 146source code in a reproducible way. If it is missing or out-of-date one can use 147the `cargoPatches` attribute to update or add it. 148 149``` 150{ lib, rustPlatform, fetchFromGitHub }: 151 152rustPlatform.buildRustPackage rec { 153 (...) 154 cargoPatches = [ 155 # a patch file to add/update Cargo.lock in the source code 156 ./add-Cargo.lock.patch 157 ]; 158} 159``` 160 161## Compiling Rust crates using Nix instead of Cargo 162 163### Simple operation 164 165When run, `cargo build` produces a file called `Cargo.lock`, 166containing pinned versions of all dependencies. Nixpkgs contains a 167tool called `carnix` (`nix-env -iA nixos.carnix`), which can be used 168to turn a `Cargo.lock` into a Nix expression. 169 170That Nix expression calls `rustc` directly (hence bypassing Cargo), 171and can be used to compile a crate and all its dependencies. Here is 172an example for a minimal `hello` crate: 173 174 175 $ cargo new hello 176 $ cd hello 177 $ cargo build 178 Compiling hello v0.1.0 (file:///tmp/hello) 179 Finished dev [unoptimized + debuginfo] target(s) in 0.20 secs 180 $ carnix -o hello.nix --src ./. Cargo.lock --standalone 181 $ nix-build hello.nix -A hello_0_1_0 182 183Now, the file produced by the call to `carnix`, called `hello.nix`, looks like: 184 185``` 186# Generated by carnix 0.6.5: carnix -o hello.nix --src ./. Cargo.lock --standalone 187{ lib, stdenv, buildRustCrate, fetchgit }: 188let kernel = stdenv.buildPlatform.parsed.kernel.name; 189 # ... (content skipped) 190in 191rec { 192 hello = f: hello_0_1_0 { features = hello_0_1_0_features { hello_0_1_0 = f; }; }; 193 hello_0_1_0_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { 194 crateName = "hello"; 195 version = "0.1.0"; 196 authors = [ "pe@pijul.org <pe@pijul.org>" ]; 197 src = ./.; 198 inherit dependencies buildDependencies features; 199 }; 200 hello_0_1_0 = { features?(hello_0_1_0_features {}) }: hello_0_1_0_ {}; 201 hello_0_1_0_features = f: updateFeatures f (rec { 202 hello_0_1_0.default = (f.hello_0_1_0.default or true); 203 }) [ ]; 204} 205``` 206 207In particular, note that the argument given as `--src` is copied 208verbatim to the source. If we look at a more complicated 209dependencies, for instance by adding a single line `libc="*"` to our 210`Cargo.toml`, we first need to run `cargo build` to update the 211`Cargo.lock`. Then, `carnix` needs to be run again, and produces the 212following nix file: 213 214``` 215# Generated by carnix 0.6.5: carnix -o hello.nix --src ./. Cargo.lock --standalone 216{ lib, stdenv, buildRustCrate, fetchgit }: 217let kernel = stdenv.buildPlatform.parsed.kernel.name; 218 # ... (content skipped) 219in 220rec { 221 hello = f: hello_0_1_0 { features = hello_0_1_0_features { hello_0_1_0 = f; }; }; 222 hello_0_1_0_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { 223 crateName = "hello"; 224 version = "0.1.0"; 225 authors = [ "pe@pijul.org <pe@pijul.org>" ]; 226 src = ./.; 227 inherit dependencies buildDependencies features; 228 }; 229 libc_0_2_36_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { 230 crateName = "libc"; 231 version = "0.2.36"; 232 authors = [ "The Rust Project Developers" ]; 233 sha256 = "01633h4yfqm0s302fm0dlba469bx8y6cs4nqc8bqrmjqxfxn515l"; 234 inherit dependencies buildDependencies features; 235 }; 236 hello_0_1_0 = { features?(hello_0_1_0_features {}) }: hello_0_1_0_ { 237 dependencies = mapFeatures features ([ libc_0_2_36 ]); 238 }; 239 hello_0_1_0_features = f: updateFeatures f (rec { 240 hello_0_1_0.default = (f.hello_0_1_0.default or true); 241 libc_0_2_36.default = true; 242 }) [ libc_0_2_36_features ]; 243 libc_0_2_36 = { features?(libc_0_2_36_features {}) }: libc_0_2_36_ { 244 features = mkFeatures (features.libc_0_2_36 or {}); 245 }; 246 libc_0_2_36_features = f: updateFeatures f (rec { 247 libc_0_2_36.default = (f.libc_0_2_36.default or true); 248 libc_0_2_36.use_std = 249 (f.libc_0_2_36.use_std or false) || 250 (f.libc_0_2_36.default or false) || 251 (libc_0_2_36.default or false); 252 }) []; 253} 254``` 255 256Here, the `libc` crate has no `src` attribute, so `buildRustCrate` 257will fetch it from [crates.io](https://crates.io). A `sha256` 258attribute is still needed for Nix purity. 259 260### Handling external dependencies 261 262Some crates require external libraries. For crates from 263[crates.io](https://crates.io), such libraries can be specified in 264`defaultCrateOverrides` package in nixpkgs itself. 265 266Starting from that file, one can add more overrides, to add features 267or build inputs by overriding the hello crate in a seperate file. 268 269``` 270with import <nixpkgs> {}; 271((import ./hello.nix).hello {}).override { 272 crateOverrides = defaultCrateOverrides // { 273 hello = attrs: { buildInputs = [ openssl ]; }; 274 }; 275} 276``` 277 278Here, `crateOverrides` is expected to be a attribute set, where the 279key is the crate name without version number and the value a function. 280The function gets all attributes passed to `buildRustCrate` as first 281argument and returns a set that contains all attribute that should be 282overwritten. 283 284For more complicated cases, such as when parts of the crate's 285derivation depend on the crate's version, the `attrs` argument of 286the override above can be read, as in the following example, which 287patches the derivation: 288 289``` 290with import <nixpkgs> {}; 291((import ./hello.nix).hello {}).override { 292 crateOverrides = defaultCrateOverrides // { 293 hello = attrs: lib.optionalAttrs (lib.versionAtLeast attrs.version "1.0") { 294 postPatch = '' 295 substituteInPlace lib/zoneinfo.rs \ 296 --replace "/usr/share/zoneinfo" "${tzdata}/share/zoneinfo" 297 ''; 298 }; 299 }; 300} 301``` 302 303Another situation is when we want to override a nested 304dependency. This actually works in the exact same way, since the 305`crateOverrides` parameter is forwarded to the crate's 306dependencies. For instance, to override the build inputs for crate 307`libc` in the example above, where `libc` is a dependency of the main 308crate, we could do: 309 310``` 311with import <nixpkgs> {}; 312((import hello.nix).hello {}).override { 313 crateOverrides = defaultCrateOverrides // { 314 libc = attrs: { buildInputs = []; }; 315 }; 316} 317``` 318 319### Options and phases configuration 320 321Actually, the overrides introduced in the previous section are more 322general. A number of other parameters can be overridden: 323 324- The version of rustc used to compile the crate: 325 326 ``` 327 (hello {}).override { rust = pkgs.rust; }; 328 ``` 329 330- Whether to build in release mode or debug mode (release mode by 331 default): 332 333 ``` 334 (hello {}).override { release = false; }; 335 ``` 336 337- Whether to print the commands sent to rustc when building 338 (equivalent to `--verbose` in cargo: 339 340 ``` 341 (hello {}).override { verbose = false; }; 342 ``` 343 344- Extra arguments to be passed to `rustc`: 345 346 ``` 347 (hello {}).override { extraRustcOpts = "-Z debuginfo=2"; }; 348 ``` 349 350- Phases, just like in any other derivation, can be specified using 351 the following attributes: `preUnpack`, `postUnpack`, `prePatch`, 352 `patches`, `postPatch`, `preConfigure` (in the case of a Rust crate, 353 this is run before calling the "build" script), `postConfigure` 354 (after the "build" script),`preBuild`, `postBuild`, `preInstall` and 355 `postInstall`. As an example, here is how to create a new module 356 before running the build script: 357 358 ``` 359 (hello {}).override { 360 preConfigure = '' 361 echo "pub const PATH=\"${hi.out}\";" >> src/path.rs" 362 ''; 363 }; 364 ``` 365 366### Features 367 368One can also supply features switches. For example, if we want to 369compile `diesel_cli` only with the `postgres` feature, and no default 370features, we would write: 371 372``` 373(callPackage ./diesel.nix {}).diesel { 374 default = false; 375 postgres = true; 376} 377``` 378 379Where `diesel.nix` is the file generated by Carnix, as explained above. 380 381 382## Setting Up `nix-shell` 383Oftentimes you want to develop code from within `nix-shell`. Unfortunately 384`buildRustCrate` does not support common `nix-shell` operations directly 385(see [this issue](https://github.com/NixOS/nixpkgs/issues/37945)) 386so we will use `stdenv.mkDerivation` instead. 387 388Using the example `hello` project above, we want to do the following: 389- Have access to `cargo` and `rustc` 390- Have the `openssl` library available to a crate through it's _normal_ 391 compilation mechanism (`pkg-config`). 392 393A typical `shell.nix` might look like: 394 395``` 396with import <nixpkgs> {}; 397 398stdenv.mkDerivation { 399 name = "rust-env"; 400 nativeBuildInputs = [ 401 rustc cargo 402 403 # Example Build-time Additional Dependencies 404 pkgconfig 405 ]; 406 buildInputs = [ 407 # Example Run-time Additional Dependencies 408 openssl 409 ]; 410 411 # Set Environment Variables 412 RUST_BACKTRACE = 1; 413} 414``` 415 416You should now be able to run the following: 417``` 418$ nix-shell --pure 419$ cargo build 420$ cargo test 421``` 422 423### Controlling Rust Version Inside `nix-shell` 424To control your rust version (i.e. use nightly) from within `shell.nix` (or 425other nix expressions) you can use the following `shell.nix` 426 427``` 428# Latest Nightly 429with import <nixpkgs> {}; 430let src = fetchFromGitHub { 431 owner = "mozilla"; 432 repo = "nixpkgs-mozilla"; 433 # commit from: 2019-05-15 434 rev = "9f35c4b09fd44a77227e79ff0c1b4b6a69dff533"; 435 sha256 = "18h0nvh55b5an4gmlgfbvwbyqj91bklf1zymis6lbdh75571qaz0"; 436 }; 437in 438with import "${src.out}/rust-overlay.nix" pkgs pkgs; 439stdenv.mkDerivation { 440 name = "rust-env"; 441 buildInputs = [ 442 # Note: to use use stable, just replace `nightly` with `stable` 443 latest.rustChannels.nightly.rust 444 445 # Add some extra dependencies from `pkgs` 446 pkgconfig openssl 447 ]; 448 449 # Set Environment Variables 450 RUST_BACKTRACE = 1; 451} 452``` 453 454Now run: 455``` 456$ rustc --version 457rustc 1.26.0-nightly (188e693b3 2018-03-26) 458``` 459 460To see that you are using nightly. 461 462 463## Using the Rust nightlies overlay 464 465Mozilla provides an overlay for nixpkgs to bring a nightly version of Rust into scope. 466This overlay can _also_ be used to install recent unstable or stable versions 467of Rust, if desired. 468 469To use this overlay, clone 470[nixpkgs-mozilla](https://github.com/mozilla/nixpkgs-mozilla), 471and create a symbolic link to the file 472[rust-overlay.nix](https://github.com/mozilla/nixpkgs-mozilla/blob/master/rust-overlay.nix) 473in the `~/.config/nixpkgs/overlays` directory. 474 475 $ git clone https://github.com/mozilla/nixpkgs-mozilla.git 476 $ mkdir -p ~/.config/nixpkgs/overlays 477 $ ln -s $(pwd)/nixpkgs-mozilla/rust-overlay.nix ~/.config/nixpkgs/overlays/rust-overlay.nix 478 479The latest version can be installed with the following command: 480 481 $ nix-env -Ai nixos.latest.rustChannels.stable.rust 482 483Or using the attribute with nix-shell: 484 485 $ nix-shell -p nixos.latest.rustChannels.stable.rust 486 487To install the beta or nightly channel, "stable" should be substituted by 488"nightly" or "beta", or 489use the function provided by this overlay to pull a version based on a 490build date. 491 492The overlay automatically updates itself as it uses the same source as 493[rustup](https://www.rustup.rs/).