396 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			396 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
---
 | 
						|
title: Rust
 | 
						|
author: Matthias Beyer
 | 
						|
date: 2017-03-05
 | 
						|
---
 | 
						|
 | 
						|
# User's Guide to the Rust Infrastructure
 | 
						|
 | 
						|
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`.
 | 
						|
 | 
						|
> If you are using NixOS and you want to use rust without a nix expression you
 | 
						|
> probably want to add the following in your `configuration.nix` to build
 | 
						|
> crates with C dependencies.
 | 
						|
>
 | 
						|
>     environment.systemPackages = [binutils gcc gnumake openssl pkgconfig]
 | 
						|
 | 
						|
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 {
 | 
						|
  name = "ripgrep-${version}";
 | 
						|
  version = "0.4.0";
 | 
						|
 | 
						|
  src = fetchFromGitHub {
 | 
						|
    owner = "BurntSushi";
 | 
						|
    repo = "ripgrep";
 | 
						|
    rev = "${version}";
 | 
						|
    sha256 = "0y5d1n6hkw85jb3rblcxqas2fp82h3nghssa4xqrhqnz25l799pj";
 | 
						|
  };
 | 
						|
 | 
						|
  cargoSha256 = "0q68qyl2h6i0qsz82z840myxlnjay8p1w5z7hfyr8fqp7wgwa9cx";
 | 
						|
 | 
						|
  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.
 | 
						|
 | 
						|
## 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 <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 <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 <nixpkgs> {};
 | 
						|
((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 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 <nixpkgs> {};
 | 
						|
((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 <nixpkgs> {};
 | 
						|
((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 <nixpkgs> {};
 | 
						|
 | 
						|
stdenv.mkDerivation {
 | 
						|
  name = "rust-env";
 | 
						|
  buildInputs = [
 | 
						|
    rustc cargo
 | 
						|
 | 
						|
    # Example Additional Dependencies
 | 
						|
    pkgconfig 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 <nixpkgs> {};
 | 
						|
let src = fetchFromGitHub {
 | 
						|
      owner = "mozilla";
 | 
						|
      repo = "nixpkgs-mozilla";
 | 
						|
      # commit from: 2018-03-27
 | 
						|
      rev = "2945b0b6b2fd19e7d23bac695afd65e320efcebe";
 | 
						|
      sha256 = "034m1dryrzh2lmjvk3c0krgip652dql46w5yfwpvh7gavd3iypyw";
 | 
						|
   };
 | 
						|
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/).
 |