diff options
Diffstat (limited to 'nixpkgs/doc/languages-frameworks/rust.section.md')
-rw-r--r-- | nixpkgs/doc/languages-frameworks/rust.section.md | 410 |
1 files changed, 410 insertions, 0 deletions
diff --git a/nixpkgs/doc/languages-frameworks/rust.section.md b/nixpkgs/doc/languages-frameworks/rust.section.md new file mode 100644 index 000000000000..97dc9e2ff531 --- /dev/null +++ b/nixpkgs/doc/languages-frameworks/rust.section.md @@ -0,0 +1,410 @@ +--- +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. + +Unless `legacyCargoFetcher` is set to `true`, the fetcher will also verify that +the `Cargo.lock` file is in sync with the `src` attribute, and 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/). |