--- title: Rust author: Matthias Beyer date: 2017-03-05 --- # Rust To install the rust compiler and cargo put ``` rustc cargo ``` into the `environment.systemPackages` or bring them into scope with `nix-shell -p rustc cargo`. For daily builds (beta and nightly) use either rustup from nixpkgs or use the [Rust nightlies overlay](#using-the-rust-nightlies-overlay). ## Compiling Rust applications with Cargo Rust applications are packaged by using the `buildRustPackage` helper from `rustPlatform`: ``` rustPlatform.buildRustPackage rec { pname = "ripgrep"; version = "11.0.2"; src = fetchFromGitHub { owner = "BurntSushi"; repo = pname; rev = version; sha256 = "1iga3320mgi7m853la55xip514a3chqsdi1a1rwv25lr9b1p7vd3"; }; cargoSha256 = "17ldqr3asrdcsh4l29m3b5r37r5d0b3npq1lrgjmxb6vlx6a36qh"; verifyCargoDeps = true; meta = with stdenv.lib; { description = "A fast line-oriented regex search tool, similar to ag and ack"; homepage = https://github.com/BurntSushi/ripgrep; license = licenses.unlicense; maintainers = [ maintainers.tailhook ]; platforms = platforms.all; }; } ``` `buildRustPackage` requires a `cargoSha256` attribute which is computed over all crate sources of this package. Currently it is obtained by inserting a fake checksum into the expression and building the package once. The correct checksum can be then take from the failed build. When the `Cargo.lock`, provided by upstream, is not in sync with the `Cargo.toml`, it is possible to use `cargoPatches` to update it. All patches added in `cargoPatches` will also be prepended to the patches in `patches` at build-time. When `verifyCargoDeps` is set to `true`, the build will also verify that the `cargoSha256` is not out of date by comparing the `Cargo.lock` file in both the `cargoDeps` and `src`. Note that this option changes the value of `cargoSha256` since it also copies the `Cargo.lock` in it. To avoid breaking backward-compatibility this option is not enabled by default but hopefully will be in the future. ### Building a crate for a different target To build your crate with a different cargo `--target` simply specify the `target` attribute: ```nix pkgs.rustPlatform.buildRustPackage { (...) target = "x86_64-fortanix-unknown-sgx"; } ``` ## Compiling Rust crates using Nix instead of Cargo ### Simple operation When run, `cargo build` produces a file called `Cargo.lock`, containing pinned versions of all dependencies. Nixpkgs contains a tool called `carnix` (`nix-env -iA nixos.carnix`), which can be used to turn a `Cargo.lock` into a Nix expression. That Nix expression calls `rustc` directly (hence bypassing Cargo), and can be used to compile a crate and all its dependencies. Here is an example for a minimal `hello` crate: $ cargo new hello $ cd hello $ cargo build Compiling hello v0.1.0 (file:///tmp/hello) Finished dev [unoptimized + debuginfo] target(s) in 0.20 secs $ carnix -o hello.nix --src ./. Cargo.lock --standalone $ nix-build hello.nix -A hello_0_1_0 Now, the file produced by the call to `carnix`, called `hello.nix`, looks like: ``` # Generated by carnix 0.6.5: carnix -o hello.nix --src ./. Cargo.lock --standalone { lib, stdenv, buildRustCrate, fetchgit }: let kernel = stdenv.buildPlatform.parsed.kernel.name; # ... (content skipped) in rec { hello = f: hello_0_1_0 { features = hello_0_1_0_features { hello_0_1_0 = f; }; }; hello_0_1_0_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { crateName = "hello"; version = "0.1.0"; authors = [ "pe@pijul.org " ]; src = ./.; inherit dependencies buildDependencies features; }; hello_0_1_0 = { features?(hello_0_1_0_features {}) }: hello_0_1_0_ {}; hello_0_1_0_features = f: updateFeatures f (rec { hello_0_1_0.default = (f.hello_0_1_0.default or true); }) [ ]; } ``` In particular, note that the argument given as `--src` is copied verbatim to the source. If we look at a more complicated dependencies, for instance by adding a single line `libc="*"` to our `Cargo.toml`, we first need to run `cargo build` to update the `Cargo.lock`. Then, `carnix` needs to be run again, and produces the following nix file: ``` # Generated by carnix 0.6.5: carnix -o hello.nix --src ./. Cargo.lock --standalone { lib, stdenv, buildRustCrate, fetchgit }: let kernel = stdenv.buildPlatform.parsed.kernel.name; # ... (content skipped) in rec { hello = f: hello_0_1_0 { features = hello_0_1_0_features { hello_0_1_0 = f; }; }; hello_0_1_0_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { crateName = "hello"; version = "0.1.0"; authors = [ "pe@pijul.org " ]; src = ./.; inherit dependencies buildDependencies features; }; libc_0_2_36_ = { dependencies?[], buildDependencies?[], features?[] }: buildRustCrate { crateName = "libc"; version = "0.2.36"; authors = [ "The Rust Project Developers" ]; sha256 = "01633h4yfqm0s302fm0dlba469bx8y6cs4nqc8bqrmjqxfxn515l"; inherit dependencies buildDependencies features; }; hello_0_1_0 = { features?(hello_0_1_0_features {}) }: hello_0_1_0_ { dependencies = mapFeatures features ([ libc_0_2_36 ]); }; hello_0_1_0_features = f: updateFeatures f (rec { hello_0_1_0.default = (f.hello_0_1_0.default or true); libc_0_2_36.default = true; }) [ libc_0_2_36_features ]; libc_0_2_36 = { features?(libc_0_2_36_features {}) }: libc_0_2_36_ { features = mkFeatures (features.libc_0_2_36 or {}); }; libc_0_2_36_features = f: updateFeatures f (rec { libc_0_2_36.default = (f.libc_0_2_36.default or true); libc_0_2_36.use_std = (f.libc_0_2_36.use_std or false) || (f.libc_0_2_36.default or false) || (libc_0_2_36.default or false); }) []; } ``` Here, the `libc` crate has no `src` attribute, so `buildRustCrate` will fetch it from [crates.io](https://crates.io). A `sha256` attribute is still needed for Nix purity. ### Handling external dependencies Some crates require external libraries. For crates from [crates.io](https://crates.io), such libraries can be specified in `defaultCrateOverrides` package in nixpkgs itself. Starting from that file, one can add more overrides, to add features or build inputs by overriding the hello crate in a seperate file. ``` with import {}; ((import ./hello.nix).hello {}).override { crateOverrides = defaultCrateOverrides // { hello = attrs: { buildInputs = [ openssl ]; }; }; } ``` Here, `crateOverrides` is expected to be a attribute set, where the key is the crate name without version number and the value a function. The function gets all attributes passed to `buildRustCrate` as first argument and returns a set that contains all attribute that should be overwritten. For more complicated cases, such as when parts of the crate's derivation depend on the crate's version, the `attrs` argument of the override above can be read, as in the following example, which patches the derivation: ``` with import {}; ((import ./hello.nix).hello {}).override { crateOverrides = defaultCrateOverrides // { hello = attrs: lib.optionalAttrs (lib.versionAtLeast attrs.version "1.0") { postPatch = '' substituteInPlace lib/zoneinfo.rs \ --replace "/usr/share/zoneinfo" "${tzdata}/share/zoneinfo" ''; }; }; } ``` Another situation is when we want to override a nested dependency. This actually works in the exact same way, since the `crateOverrides` parameter is forwarded to the crate's dependencies. For instance, to override the build inputs for crate `libc` in the example above, where `libc` is a dependency of the main crate, we could do: ``` with import {}; ((import hello.nix).hello {}).override { crateOverrides = defaultCrateOverrides // { libc = attrs: { buildInputs = []; }; }; } ``` ### Options and phases configuration Actually, the overrides introduced in the previous section are more general. A number of other parameters can be overridden: - The version of rustc used to compile the crate: ``` (hello {}).override { rust = pkgs.rust; }; ``` - Whether to build in release mode or debug mode (release mode by default): ``` (hello {}).override { release = false; }; ``` - Whether to print the commands sent to rustc when building (equivalent to `--verbose` in cargo: ``` (hello {}).override { verbose = false; }; ``` - Extra arguments to be passed to `rustc`: ``` (hello {}).override { extraRustcOpts = "-Z debuginfo=2"; }; ``` - Phases, just like in any other derivation, can be specified using the following attributes: `preUnpack`, `postUnpack`, `prePatch`, `patches`, `postPatch`, `preConfigure` (in the case of a Rust crate, this is run before calling the "build" script), `postConfigure` (after the "build" script),`preBuild`, `postBuild`, `preInstall` and `postInstall`. As an example, here is how to create a new module before running the build script: ``` (hello {}).override { preConfigure = '' echo "pub const PATH=\"${hi.out}\";" >> src/path.rs" ''; }; ``` ### Features One can also supply features switches. For example, if we want to compile `diesel_cli` only with the `postgres` feature, and no default features, we would write: ``` (callPackage ./diesel.nix {}).diesel { default = false; postgres = true; } ``` Where `diesel.nix` is the file generated by Carnix, as explained above. ## Setting Up `nix-shell` Oftentimes you want to develop code from within `nix-shell`. Unfortunately `buildRustCrate` does not support common `nix-shell` operations directly (see [this issue](https://github.com/NixOS/nixpkgs/issues/37945)) so we will use `stdenv.mkDerivation` instead. Using the example `hello` project above, we want to do the following: - Have access to `cargo` and `rustc` - Have the `openssl` library available to a crate through it's _normal_ compilation mechanism (`pkg-config`). A typical `shell.nix` might look like: ``` with import {}; stdenv.mkDerivation { name = "rust-env"; nativeBuildInputs = [ rustc cargo # Example Build-time Additional Dependencies pkgconfig ]; buildInputs = [ # Example Run-time Additional Dependencies openssl ]; # Set Environment Variables RUST_BACKTRACE = 1; } ``` You should now be able to run the following: ``` $ nix-shell --pure $ cargo build $ cargo test ``` ### Controlling Rust Version Inside `nix-shell` To control your rust version (i.e. use nightly) from within `shell.nix` (or other nix expressions) you can use the following `shell.nix` ``` # Latest Nightly with import {}; let src = fetchFromGitHub { owner = "mozilla"; repo = "nixpkgs-mozilla"; # commit from: 2019-05-15 rev = "9f35c4b09fd44a77227e79ff0c1b4b6a69dff533"; sha256 = "18h0nvh55b5an4gmlgfbvwbyqj91bklf1zymis6lbdh75571qaz0"; }; in with import "${src.out}/rust-overlay.nix" pkgs pkgs; stdenv.mkDerivation { name = "rust-env"; buildInputs = [ # Note: to use use stable, just replace `nightly` with `stable` latest.rustChannels.nightly.rust # Add some extra dependencies from `pkgs` pkgconfig openssl ]; # Set Environment Variables RUST_BACKTRACE = 1; } ``` Now run: ``` $ rustc --version rustc 1.26.0-nightly (188e693b3 2018-03-26) ``` To see that you are using nightly. ## Using the Rust nightlies overlay Mozilla provides an overlay for nixpkgs to bring a nightly version of Rust into scope. This overlay can _also_ be used to install recent unstable or stable versions of Rust, if desired. To use this overlay, clone [nixpkgs-mozilla](https://github.com/mozilla/nixpkgs-mozilla), and create a symbolic link to the file [rust-overlay.nix](https://github.com/mozilla/nixpkgs-mozilla/blob/master/rust-overlay.nix) in the `~/.config/nixpkgs/overlays` directory. $ git clone https://github.com/mozilla/nixpkgs-mozilla.git $ mkdir -p ~/.config/nixpkgs/overlays $ ln -s $(pwd)/nixpkgs-mozilla/rust-overlay.nix ~/.config/nixpkgs/overlays/rust-overlay.nix The latest version can be installed with the following command: $ nix-env -Ai nixos.latest.rustChannels.stable.rust Or using the attribute with nix-shell: $ nix-shell -p nixos.latest.rustChannels.stable.rust To install the beta or nightly channel, "stable" should be substituted by "nightly" or "beta", or use the function provided by this overlay to pull a version based on a build date. The overlay automatically updates itself as it uses the same source as [rustup](https://www.rustup.rs/).