Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
at release-19.03 399 lines 12 kB view raw view rendered
1--- 2title: Rust 3author: Matthias Beyer 4date: 2017-03-05 5--- 6 7# User's Guide to the Rust Infrastructure 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 19> If you are using NixOS and you want to use rust without a nix expression you 20> probably want to add the following in your `configuration.nix` to build 21> crates with C dependencies. 22> 23> environment.systemPackages = [binutils gcc gnumake openssl pkgconfig] 24 25For daily builds (beta and nightly) use either rustup from 26nixpkgs or use the [Rust nightlies 27overlay](#using-the-rust-nightlies-overlay). 28 29## Compiling Rust applications with Cargo 30 31Rust applications are packaged by using the `buildRustPackage` helper from `rustPlatform`: 32 33``` 34rustPlatform.buildRustPackage rec { 35 name = "ripgrep-${version}"; 36 version = "0.4.0"; 37 38 src = fetchFromGitHub { 39 owner = "BurntSushi"; 40 repo = "ripgrep"; 41 rev = "${version}"; 42 sha256 = "0y5d1n6hkw85jb3rblcxqas2fp82h3nghssa4xqrhqnz25l799pj"; 43 }; 44 45 cargoSha256 = "0q68qyl2h6i0qsz82z840myxlnjay8p1w5z7hfyr8fqp7wgwa9cx"; 46 47 meta = with stdenv.lib; { 48 description = "A fast line-oriented regex search tool, similar to ag and ack"; 49 homepage = https://github.com/BurntSushi/ripgrep; 50 license = licenses.unlicense; 51 maintainers = [ maintainers.tailhook ]; 52 platforms = platforms.all; 53 }; 54} 55``` 56 57`buildRustPackage` requires a `cargoSha256` attribute which is computed over 58all crate sources of this package. Currently it is obtained by inserting a 59fake checksum into the expression and building the package once. The correct 60checksum can be then take from the failed build. 61 62When the `Cargo.lock`, provided by upstream, is not in sync with the 63`Cargo.toml`, it is possible to use `cargoPatches` to update it. All patches 64added in `cargoPatches` will also be prepended to the patches in `patches` at 65build-time. 66 67## Compiling Rust crates using Nix instead of Cargo 68 69### Simple operation 70 71When run, `cargo build` produces a file called `Cargo.lock`, 72containing pinned versions of all dependencies. Nixpkgs contains a 73tool called `carnix` (`nix-env -iA nixos.carnix`), which can be used 74to turn a `Cargo.lock` into a Nix expression. 75 76That Nix expression calls `rustc` directly (hence bypassing Cargo), 77and can be used to compile a crate and all its dependencies. Here is 78an example for a minimal `hello` crate: 79 80 81 $ cargo new hello 82 $ cd hello 83 $ cargo build 84 Compiling hello v0.1.0 (file:///tmp/hello) 85 Finished dev [unoptimized + debuginfo] target(s) in 0.20 secs 86 $ carnix -o hello.nix --src ./. Cargo.lock --standalone 87 $ nix-build hello.nix -A hello_0_1_0 88 89Now, the file produced by the call to `carnix`, called `hello.nix`, looks like: 90 91``` 92# Generated by carnix 0.6.5: carnix -o hello.nix --src ./. Cargo.lock --standalone 93{ lib, stdenv, buildRustCrate, fetchgit }: 94let kernel = stdenv.buildPlatform.parsed.kernel.name; 95 # ... (content skipped) 96in 97rec { 98 hello = f: hello_0_1_0 { features = hello_0_1_0_features { hello_0_1_0 = f; }; }; 99 hello_0_1_0_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { 100 crateName = "hello"; 101 version = "0.1.0"; 102 authors = [ "pe@pijul.org <pe@pijul.org>" ]; 103 src = ./.; 104 inherit dependencies buildDependencies features; 105 }; 106 hello_0_1_0 = { features?(hello_0_1_0_features {}) }: hello_0_1_0_ {}; 107 hello_0_1_0_features = f: updateFeatures f (rec { 108 hello_0_1_0.default = (f.hello_0_1_0.default or true); 109 }) [ ]; 110} 111``` 112 113In particular, note that the argument given as `--src` is copied 114verbatim to the source. If we look at a more complicated 115dependencies, for instance by adding a single line `libc="*"` to our 116`Cargo.toml`, we first need to run `cargo build` to update the 117`Cargo.lock`. Then, `carnix` needs to be run again, and produces the 118following nix file: 119 120``` 121# Generated by carnix 0.6.5: carnix -o hello.nix --src ./. Cargo.lock --standalone 122{ lib, stdenv, buildRustCrate, fetchgit }: 123let kernel = stdenv.buildPlatform.parsed.kernel.name; 124 # ... (content skipped) 125in 126rec { 127 hello = f: hello_0_1_0 { features = hello_0_1_0_features { hello_0_1_0 = f; }; }; 128 hello_0_1_0_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { 129 crateName = "hello"; 130 version = "0.1.0"; 131 authors = [ "pe@pijul.org <pe@pijul.org>" ]; 132 src = ./.; 133 inherit dependencies buildDependencies features; 134 }; 135 libc_0_2_36_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { 136 crateName = "libc"; 137 version = "0.2.36"; 138 authors = [ "The Rust Project Developers" ]; 139 sha256 = "01633h4yfqm0s302fm0dlba469bx8y6cs4nqc8bqrmjqxfxn515l"; 140 inherit dependencies buildDependencies features; 141 }; 142 hello_0_1_0 = { features?(hello_0_1_0_features {}) }: hello_0_1_0_ { 143 dependencies = mapFeatures features ([ libc_0_2_36 ]); 144 }; 145 hello_0_1_0_features = f: updateFeatures f (rec { 146 hello_0_1_0.default = (f.hello_0_1_0.default or true); 147 libc_0_2_36.default = true; 148 }) [ libc_0_2_36_features ]; 149 libc_0_2_36 = { features?(libc_0_2_36_features {}) }: libc_0_2_36_ { 150 features = mkFeatures (features.libc_0_2_36 or {}); 151 }; 152 libc_0_2_36_features = f: updateFeatures f (rec { 153 libc_0_2_36.default = (f.libc_0_2_36.default or true); 154 libc_0_2_36.use_std = 155 (f.libc_0_2_36.use_std or false) || 156 (f.libc_0_2_36.default or false) || 157 (libc_0_2_36.default or false); 158 }) []; 159} 160``` 161 162Here, the `libc` crate has no `src` attribute, so `buildRustCrate` 163will fetch it from [crates.io](https://crates.io). A `sha256` 164attribute is still needed for Nix purity. 165 166### Handling external dependencies 167 168Some crates require external libraries. For crates from 169[crates.io](https://crates.io), such libraries can be specified in 170`defaultCrateOverrides` package in nixpkgs itself. 171 172Starting from that file, one can add more overrides, to add features 173or build inputs by overriding the hello crate in a seperate file. 174 175``` 176with import <nixpkgs> {}; 177((import ./hello.nix).hello {}).override { 178 crateOverrides = defaultCrateOverrides // { 179 hello = attrs: { buildInputs = [ openssl ]; }; 180 }; 181} 182``` 183 184Here, `crateOverrides` is expected to be a attribute set, where the 185key is the crate name without version number and the value a function. 186The function gets all attributes passed to `buildRustCrate` as first 187argument and returns a set that contains all attribute that should be 188overwritten. 189 190For more complicated cases, such as when parts of the crate's 191derivation depend on the the crate's version, the `attrs` argument of 192the override above can be read, as in the following example, which 193patches the derivation: 194 195``` 196with import <nixpkgs> {}; 197((import ./hello.nix).hello {}).override { 198 crateOverrides = defaultCrateOverrides // { 199 hello = attrs: lib.optionalAttrs (lib.versionAtLeast attrs.version "1.0") { 200 postPatch = '' 201 substituteInPlace lib/zoneinfo.rs \ 202 --replace "/usr/share/zoneinfo" "${tzdata}/share/zoneinfo" 203 ''; 204 }; 205 }; 206} 207``` 208 209Another situation is when we want to override a nested 210dependency. This actually works in the exact same way, since the 211`crateOverrides` parameter is forwarded to the crate's 212dependencies. For instance, to override the build inputs for crate 213`libc` in the example above, where `libc` is a dependency of the main 214crate, we could do: 215 216``` 217with import <nixpkgs> {}; 218((import hello.nix).hello {}).override { 219 crateOverrides = defaultCrateOverrides // { 220 libc = attrs: { buildInputs = []; }; 221 }; 222} 223``` 224 225### Options and phases configuration 226 227Actually, the overrides introduced in the previous section are more 228general. A number of other parameters can be overridden: 229 230- The version of rustc used to compile the crate: 231 232 ``` 233 (hello {}).override { rust = pkgs.rust; }; 234 ``` 235 236- Whether to build in release mode or debug mode (release mode by 237 default): 238 239 ``` 240 (hello {}).override { release = false; }; 241 ``` 242 243- Whether to print the commands sent to rustc when building 244 (equivalent to `--verbose` in cargo: 245 246 ``` 247 (hello {}).override { verbose = false; }; 248 ``` 249 250- Extra arguments to be passed to `rustc`: 251 252 ``` 253 (hello {}).override { extraRustcOpts = "-Z debuginfo=2"; }; 254 ``` 255 256- Phases, just like in any other derivation, can be specified using 257 the following attributes: `preUnpack`, `postUnpack`, `prePatch`, 258 `patches`, `postPatch`, `preConfigure` (in the case of a Rust crate, 259 this is run before calling the "build" script), `postConfigure` 260 (after the "build" script),`preBuild`, `postBuild`, `preInstall` and 261 `postInstall`. As an example, here is how to create a new module 262 before running the build script: 263 264 ``` 265 (hello {}).override { 266 preConfigure = '' 267 echo "pub const PATH=\"${hi.out}\";" >> src/path.rs" 268 ''; 269 }; 270 ``` 271 272### Features 273 274One can also supply features switches. For example, if we want to 275compile `diesel_cli` only with the `postgres` feature, and no default 276features, we would write: 277 278``` 279(callPackage ./diesel.nix {}).diesel { 280 default = false; 281 postgres = true; 282} 283``` 284 285Where `diesel.nix` is the file generated by Carnix, as explained above. 286 287 288## Setting Up `nix-shell` 289Oftentimes you want to develop code from within `nix-shell`. Unfortunately 290`buildRustCrate` does not support common `nix-shell` operations directly 291(see [this issue](https://github.com/NixOS/nixpkgs/issues/37945)) 292so we will use `stdenv.mkDerivation` instead. 293 294Using the example `hello` project above, we want to do the following: 295- Have access to `cargo` and `rustc` 296- Have the `openssl` library available to a crate through it's _normal_ 297 compilation mechanism (`pkg-config`). 298 299A typical `shell.nix` might look like: 300 301``` 302with import <nixpkgs> {}; 303 304stdenv.mkDerivation { 305 name = "rust-env"; 306 nativeBuildInputs = [ 307 rustc cargo 308 309 # Example Build-time Additional Dependencies 310 pkgconfig 311 ]; 312 buildInputs = [ 313 # Example Run-time Additional Dependencies 314 openssl 315 ]; 316 317 # Set Environment Variables 318 RUST_BACKTRACE = 1; 319} 320``` 321 322You should now be able to run the following: 323``` 324$ nix-shell --pure 325$ cargo build 326$ cargo test 327``` 328 329### Controlling Rust Version Inside `nix-shell` 330To control your rust version (i.e. use nightly) from within `shell.nix` (or 331other nix expressions) you can use the following `shell.nix` 332 333``` 334# Latest Nightly 335with import <nixpkgs> {}; 336let src = fetchFromGitHub { 337 owner = "mozilla"; 338 repo = "nixpkgs-mozilla"; 339 # commit from: 2019-05-15 340 rev = "9f35c4b09fd44a77227e79ff0c1b4b6a69dff533"; 341 sha256 = "18h0nvh55b5an4gmlgfbvwbyqj91bklf1zymis6lbdh75571qaz0"; 342 }; 343in 344with import "${src.out}/rust-overlay.nix" pkgs pkgs; 345stdenv.mkDerivation { 346 name = "rust-env"; 347 buildInputs = [ 348 # Note: to use use stable, just replace `nightly` with `stable` 349 latest.rustChannels.nightly.rust 350 351 # Add some extra dependencies from `pkgs` 352 pkgconfig openssl 353 ]; 354 355 # Set Environment Variables 356 RUST_BACKTRACE = 1; 357} 358``` 359 360Now run: 361``` 362$ rustc --version 363rustc 1.26.0-nightly (188e693b3 2018-03-26) 364``` 365 366To see that you are using nightly. 367 368 369## Using the Rust nightlies overlay 370 371Mozilla provides an overlay for nixpkgs to bring a nightly version of Rust into scope. 372This overlay can _also_ be used to install recent unstable or stable versions 373of Rust, if desired. 374 375To use this overlay, clone 376[nixpkgs-mozilla](https://github.com/mozilla/nixpkgs-mozilla), 377and create a symbolic link to the file 378[rust-overlay.nix](https://github.com/mozilla/nixpkgs-mozilla/blob/master/rust-overlay.nix) 379in the `~/.config/nixpkgs/overlays` directory. 380 381 $ git clone https://github.com/mozilla/nixpkgs-mozilla.git 382 $ mkdir -p ~/.config/nixpkgs/overlays 383 $ ln -s $(pwd)/nixpkgs-mozilla/rust-overlay.nix ~/.config/nixpkgs/overlays/rust-overlay.nix 384 385The latest version can be installed with the following command: 386 387 $ nix-env -Ai nixos.latest.rustChannels.stable.rust 388 389Or using the attribute with nix-shell: 390 391 $ nix-shell -p nixos.latest.rustChannels.stable.rust 392 393To install the beta or nightly channel, "stable" should be substituted by 394"nightly" or "beta", or 395use the function provided by this overlay to pull a version based on a 396build date. 397 398The overlay automatically updates itself as it uses the same source as 399[rustup](https://www.rustup.rs/).