/doc/languages-frameworks/rust.section.md
Markdown | 410 lines | 331 code | 79 blank | 0 comment | 0 complexity | 8f3de2e390905ca6693e04d45139ed76 MD5 | raw file
- ---
- 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";
- 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.
- Per the instructions in the [Cargo Book](https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html)
- best practices guide, Rust applications should always commit the `Cargo.lock`
- file in git to ensure a reproducible build. However, a few packages do not, and
- Nix depends on this file, so if it missing you can use `cargoPatches` to apply
- it in the `patchPhase`. Consider sending a PR upstream with a note to the
- maintainer describing why it's important to include in the application.
- The fetcher will verify that the `Cargo.lock` file is in sync with the `src`
- attribute, and fail the build if not. It will also will compress the vendor
- directory into a tar.gz archive.
- ### 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 <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 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";
- 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 <nixpkgs> {};
- 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/).