Rust: Difference between revisions
imported>Amjoseph-nixpkgs delete "nobody is maintaining it" comment -- crate2nix has had commits at least weekly for several months now |
Musicmatze (talk | contribs) Build to documentation on master rather than a specific commit |
||
| (44 intermediate revisions by 29 users not shown) | |||
| Line 1: | Line 1: | ||
This article is about the [https://www.rust-lang.org Rust programming language]. There are 3 methods to use the Rust compiler and toolchain in Nix/NixOS: | This article is about the [https://www.rust-lang.org Rust programming language]. There are 3 methods to use the Rust compiler and toolchain in Nix/NixOS: | ||
# via nixpkgs, | # via nixpkgs, | ||
# via rustup, | # via rustup, | ||
# or with unofficial overlays on nixpkgs. | # or with unofficial overlays on nixpkgs. | ||
Installing via nixpkgs is the | Installing via nixpkgs is the best way to use Rust, but there are valid reasons to use any approach. | ||
== Installing via nixpkgs == | == Installing via nixpkgs == | ||
The <code>cargo</code> and <code>rustc</code> derivations provide the Rust toolchain in nixpkgs. An advantage of using nixpkgs is that it's dead simple and you get pinned versions, deterministic builds in nix-shell, etc. However, nixpkgs only maintains a single version of the Rust stable toolchain, so if you require a nightly toolchain or | The <code>cargo</code> and <code>rustc</code> derivations provide the Rust toolchain in nixpkgs. An advantage of using nixpkgs is that it's dead simple and you get pinned versions, deterministic builds in nix-shell, etc. However, nixpkgs only maintains a single version of the Rust stable toolchain, so if you require a nightly toolchain or switch between multiple toolchains then this approach may not be for you. | ||
Here's an example <code>shell.nix</code>: | Here's an example <code>shell.nix</code>: | ||
< | <syntaxhighlight lang="nix"> | ||
let | let | ||
# Pinned nixpkgs, deterministic. Last updated: 2/12/21. | # Pinned nixpkgs, deterministic. Last updated: 2/12/21. | ||
| Line 19: | Line 19: | ||
# Rolling updates, not deterministic. | # Rolling updates, not deterministic. | ||
# pkgs = import (fetchTarball("channel:nixpkgs-unstable")) {}; | # pkgs = import (fetchTarball("channel:nixpkgs-unstable")) {}; | ||
in pkgs.mkShell { | in | ||
buildInputs = [ | pkgs.callPackage ( | ||
{ | |||
mkShell, | |||
cargo, | |||
rustc, | |||
}: | |||
mkShell { | |||
strictDeps = true; | |||
nativeBuildInputs = [ | |||
cargo | |||
rustc | |||
]; | |||
} | |||
) { } | |||
</syntaxhighlight> | |||
== Installating with bindgen support == | |||
By default crates using <code>bindgen</code> will not compile. To add bindgen support add the <code>rustPlatform.bindgenHook</code> to your <code>nativeBuildInputs</code>. | |||
Here's an example <code>shell.nix</code>: | |||
<syntaxhighlight lang="nix"> | |||
{ | |||
pkgs ? import <nixpkgs> { }, | |||
}: | |||
pkgs.callPackage ( | |||
{ | |||
mkShell, | |||
cargo, | |||
rustc, | |||
rustPlatform, | |||
pkg-config, | |||
}: | |||
mkShell { | |||
strictDeps = true; | |||
nativeBuildInputs = [ | |||
cargo | |||
rustc | |||
rustPlatform.bindgenHook | |||
# optional: add pkg-config support | |||
pkg-config | |||
]; | |||
buildInputs = [ | |||
# add desired native packages | |||
# ... | |||
]; | |||
# ... | |||
} | |||
) { } | |||
</syntaxhighlight> | |||
This also works, when compiling rust crates: | |||
<syntaxHighlight lang="nix"> | |||
{ | |||
rustPlatform, | |||
pkg-config, | |||
... | |||
}: | |||
rustPlatform.buildRustPackage { | |||
# ... | |||
nativeBuildInputs = [ | |||
rustPlatform.bindgenHook | |||
pkg-config | |||
]; | |||
buildInputs = [ | |||
# add desired native packages | |||
# ... | |||
]; | |||
} | } | ||
</syntaxHighlight> | </syntaxHighlight> | ||
== Installation via rustup == | == Installation via rustup == | ||
The rustup tool is maintained by the Rust community and offers | The rustup tool is maintained by the Rust community and offers an interface to install and switch between Rust toolchains. In this scenario, rustup handles the "package management" of Rust toolchains and places them in <code>$PATH</code>. Nixpkgs offers rustup via the <code>rustup</code> derivation. More info on using rustup can be found on their official website: https://rustup.rs/. | ||
If you want | If you want the most "normal" Rust experience I recommend using rustup with the following example shell.nix: | ||
< | <syntaxhighlight lang="nix"> | ||
{ pkgs ? import <nixpkgs> {} }: | { | ||
pkgs.mkShell | pkgs ? import <nixpkgs> { }, | ||
}: | |||
let | |||
overrides = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml)); | |||
in | |||
pkgs.callPackage ( | |||
{ | |||
stdenv, | |||
mkShell, | |||
rustup, | |||
rustPlatform, | |||
}: | |||
mkShell { | |||
strictDeps = true; | |||
nativeBuildInputs = [ | |||
rustup | rustup | ||
rustPlatform.bindgenHook | |||
]; | ]; | ||
RUSTC_VERSION = | # libraries here | ||
buildInputs = | |||
[ | |||
]; | |||
RUSTC_VERSION = overrides.toolchain.channel; | |||
# https://github.com/rust-lang/rust-bindgen#environment-variables | # https://github.com/rust-lang/rust-bindgen#environment-variables | ||
shellHook = '' | shellHook = '' | ||
export PATH= | export PATH="''${CARGO_HOME:-~/.cargo}/bin":"$PATH" | ||
export PATH= | export PATH="''${RUSTUP_HOME:-~/.rustup}/toolchains/$RUSTC_VERSION-${stdenv.hostPlatform.rust.rustcTarget}/bin":"$PATH" | ||
''; | |||
} | |||
) { } | |||
</syntaxhighlight> | |||
</ | |||
It's important to have a file named <code>rust-toolchain</code> lying in the same directory as the shell.nix. | It's important to have a file named <code>rust-toolchain.toml</code> lying in the same directory as the shell.nix. | ||
Rust already has a standardized way of pinning a toolchain version for a workspace or a project. | |||
See [https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file the Rustup book] for its syntax. | |||
A minimal example of the <code>rust-toolchain.toml</code>: | |||
<syntaxhighlight lang="toml"> | |||
[toolchain] | |||
channel = "stable" # This can also be "nightly" if you want a nightly rust | |||
# or nightly-20XX-XX-XX for a specific nightly. | |||
</syntaxhighlight> | |||
== Cross-compiling == | == Cross-compiling == | ||
| Line 86: | Line 148: | ||
* [https://github.com/jraygauthier/jrg-rust-cross-experiment/tree/master/simple-static-rustup-target-windows simple-static-rustup-target-windows] | * [https://github.com/jraygauthier/jrg-rust-cross-experiment/tree/master/simple-static-rustup-target-windows simple-static-rustup-target-windows] | ||
** [https://github.com/jraygauthier/jrg-rust-cross-experiment/blob/master/simple-static-rustup-target-windows/shell.nix shell.nix] | ** [https://github.com/jraygauthier/jrg-rust-cross-experiment/blob/master/simple-static-rustup-target-windows/shell.nix shell.nix] | ||
=== To Windows via a cargo plugin: === | |||
* use [https://crates.io/crates/cargo-xwin cargo-xwin] with rustup install or the [https://search.nixos.org/packages?show=cargo-xwin&type=packages&query=cargo+windows nix cargo plugin] | |||
* run cargo commands prefixed by xwin, e.g. <code>cargo xwin run --target x86_64-pc-windows-msvc</code> | |||
== Unofficial overlays == | == Unofficial overlays == | ||
| Line 93: | Line 160: | ||
# https://github.com/mozilla/nixpkgs-mozilla (Flake support, Nightly & Stable) | # https://github.com/mozilla/nixpkgs-mozilla (Flake support, Nightly & Stable) | ||
== | == devenv.sh support == | ||
# | # https://github.com/cachix/devenv/blob/main/examples/rust/devenv.nix and <code>devenv shell</code> | ||
== Developing Rust projects using Nix == | == Developing Rust projects using Nix == | ||
| Line 102: | Line 168: | ||
The [https://nixos.org/manual/nixpkgs/stable/#rust Nixpkgs manual] uses <code>buildRustPackage</code>. | The [https://nixos.org/manual/nixpkgs/stable/#rust Nixpkgs manual] uses <code>buildRustPackage</code>. | ||
[https:// | [https://srid.ca/rust-nix This] blog post shows how to do it using <code>dream2nix</code>. A template repo is available here: https://github.com/srid/rust-nix-template | ||
== Using overrideAttrs with Rust Packages == | == Using overrideAttrs with Rust Packages == | ||
There are two ways to use <code>overrideAttrs</code> with Rust packages: | |||
[ | * Using [[Import From Derivation]]: | ||
<p> | |||
== | <syntaxhighlight lang="nix"> | ||
nil = pkgs.nil.overrideAttrs ( | |||
finalAttrs: previousAttrs: { | |||
version = "unstable-2024-09-19"; | |||
< | |||
src = pkgs.fetchFromGitHub { | |||
owner = "oxalica"; | |||
repo = "nil"; | |||
rev = "c8e8ce72442a164d89d3fdeaae0bcc405f8c015a"; | |||
hash = "sha256-mIuOP4I51eFLquRaxMKx67pHmhatZrcVPjfHL98v/M8="; | |||
}; | |||
} | |||
}) | # Requires IFD | ||
cargoDeps = pkgs.rustPlatform.importCargoLock { | |||
</ | lockFile = finalAttrs.src + "/Cargo.lock"; | ||
allowBuiltinFetchGit = true; | |||
}; | |||
cargoHash = null; | |||
} | |||
); | |||
</syntaxhighlight> | |||
</p> | |||
* Overriding <code>cargoDeps</code>: | |||
<p> | |||
<syntaxhighlight lang="nix"> | |||
nil = pkgs.nil.overrideAttrs ( | |||
finalAttrs: previousAttrs: { | |||
version = "unstable-2024-09-19"; | |||
src = pkgs.fetchFromGitHub { | |||
owner = "oxalica"; | |||
repo = "nil"; | |||
rev = "c8e8ce72442a164d89d3fdeaae0bcc405f8c015a"; | |||
hash = "sha256-mIuOP4I51eFLquRaxMKx67pHmhatZrcVPjfHL98v/M8="; | |||
}; | |||
# Doesn't require IFD | |||
cargoDeps = previousAttrs.cargoDeps.overrideAttrs { | |||
name = "nil-vendor.tar.gz"; | |||
inherit (finalAttrs) src; | |||
#outputHash = pkgs.lib.fakeHash; | |||
outputHash = "sha256-RWgknkeGNfP2wH1X6nc+b10Qg1QX3UeewDdeWG0RIE8="; | |||
#}; | |||
} | |||
); | |||
</syntaxhighlight> | |||
</p> | |||
== Packaging Rust projects with nix == | == Packaging Rust projects with nix == | ||
| Line 136: | Line 235: | ||
| Notes | | Notes | ||
|- | |- | ||
| [https://github.com/NixOS/nixpkgs/blob/ | | <code>[https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/rust.section.md buildRustPackage]</code> | ||
| Checksum | | Checksum | ||
| 1 | | 1 | ||
| Line 142: | Line 241: | ||
| Yes | | Yes | ||
| Built into nixpkgs | | Built into nixpkgs | ||
|- | |- | ||
| [https://github.com/kolloch/crate2nix <code>crate2nix</code>] | | [https://github.com/kolloch/crate2nix <code>crate2nix</code>] | ||
| Line 154: | Line 246: | ||
| Many | | Many | ||
| <code>buildRustCrate</code> | | <code>buildRustCrate</code> | ||
| | | [https://github.com/kolloch/crate2nix/commit/8bfeb42bda097e0bdf5452691a5e157aad3cc11f experimental] | ||
| Spiritual successor to carnix | | Spiritual successor to [https://github.com/nix-community/carnix <code>carnix</code>] | ||
|- | |- | ||
| [https://github.com/nmattia/naersk/ <code>naersk</code>] | | [https://github.com/nmattia/naersk/ <code>naersk</code>] | ||
| Line 161: | Line 253: | ||
| 2 | | 2 | ||
| cargo | | cargo | ||
| | | Yes | ||
| [https://github.com/nmattia/naersk/blob/22b96210b2433228d42bce460f3befbdcfde7520/rust/rustc.nix#L22-L29 Seems to only support building on x86] | | [https://github.com/nmattia/naersk/blob/22b96210b2433228d42bce460f3befbdcfde7520/rust/rustc.nix#L22-L29 Seems to only support building on x86] | ||
|- | |- | ||
| Line 185: | Line 277: | ||
| Inspired by naersk, with [https://discourse.nixos.org/t/introducing-crane-composable-and-cacheable-builds-with-cargo/17275/4 better support for composing Cargo invocations as completely separate derivations] | | Inspired by naersk, with [https://discourse.nixos.org/t/introducing-crane-composable-and-cacheable-builds-with-cargo/17275/4 better support for composing Cargo invocations as completely separate derivations] | ||
|- | |- | ||
| [https:// | | [https://dream2nix.dev/reference/rust-crane <code>dream2nix</code>] | ||
| Codegen | | Codegen | ||
| 1 or 2 | | 1 or 2 | ||
| cargo (via <code>buildRustPackage</code> or <code>crane</code>) | | cargo (via <code>buildRustPackage</code> or <code>crane</code>) | ||
| Yes | | Yes | ||
| A framework for unifying 2nix converters across languages | | A framework for unifying 2nix converters across languages (Experimental) | ||
|} | |} | ||
| Line 201: | Line 293: | ||
== Shell.nix example == | == Shell.nix example == | ||
< | <syntaxhighlight lang="nix"> | ||
{ pkgs ? import <nixpkgs> {} }: | { | ||
pkgs.mkShell { | pkgs ? import <nixpkgs> { }, | ||
}: | |||
pkgs.callPackage ( | |||
{ | |||
mkShell, | |||
rustc, | |||
cargo, | |||
rustPlatform, | |||
rustfmt, | |||
clippy, | |||
rust-analyzer, | |||
}: | |||
mkShell { | |||
strictDeps = true; | |||
nativeBuildInputs = [ | |||
rustc | |||
cargo | |||
rustfmt | |||
clippy | |||
rust-analyzer | |||
]; | |||
# Certain Rust tools won't work without this | |||
# rust-analyzer from nixpkgs does not need this. | |||
# This can also be fixed by using oxalica/rust-overlay and specifying the rust-src extension | |||
# See https://discourse.nixos.org/t/rust-src-not-found-and-other-misadventures-of-developing-rust-on-nixos/11570/3?u=samuela. for more details. | |||
} | RUST_SRC_PATH = "${rustPlatform.rustLibSrc}"; | ||
</ | } | ||
) { } | |||
</syntaxhighlight> | |||
This will have the stable Rust compiler + the official formatter and linter inside the ephemeral shell. It'll also set the RUST_SRC_PATH environment variable to point to the right location, which tools, such as rust-analyzer, require to be set. | This will have the stable Rust compiler + the official formatter and linter inside the ephemeral shell. It'll also set the RUST_SRC_PATH environment variable to point to the right location, which tools, such as rust-analyzer, require to be set. | ||
=== Custom Rust version or targets === | |||
<syntaxhighlight lang="nix"> | |||
let | |||
rustVersion = "latest"; | |||
#rustVersion = "1.62.0"; | |||
rust_overlay = import ( | |||
builtins.fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz" | |||
); | |||
pkgs = import <nixpkgs> { | |||
overlays = [ | |||
rust_overlay | |||
(_: prev: { | |||
my-rust = prev.rust-bin.stable.${rustVersion}.default.override { | |||
extensions = [ | |||
"rust-src" # for rust-analyzer | |||
"rust-analyzer" | |||
]; | |||
# Or import nixpkgs with `crossSystem` | |||
#targets = [ "arm-unknown-linux-gnueabihf" ]; | |||
}; | |||
}) | |||
]; | |||
}; | |||
in | |||
pkgs.callPackage ( | |||
{ | |||
mkShell, | |||
hello, | |||
my-rust, | |||
pkg-config, | |||
openssl, | |||
}: | |||
mkShell { | |||
strictDeps = true; | |||
# host/target agnostic programs | |||
depsBuildBuild = [ | |||
hello | |||
]; | |||
# compilers & linkers & dependecy finding programs | |||
nativeBuildInputs = [ | |||
my-rust | |||
pkg-config | |||
]; | |||
# libraries | |||
buildInputs = [ | |||
openssl | |||
]; | |||
RUST_BACKTRACE = 1; | |||
} | |||
) { } | |||
</syntaxhighlight> | |||
=== VSCode integration === | === VSCode integration === | ||
The | The [https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer rust-lang.rust-analyzer] VSCode extension offers Rust support. | ||
You can use the [https://marketplace.visualstudio.com/items?itemName=arrterian.nix-env-selector arrterian.nix-env-selector] extension to enable your nix-shell inside VSCode and have these settings picked up by other extensions. | You can use the [https://marketplace.visualstudio.com/items?itemName=arrterian.nix-env-selector arrterian.nix-env-selector] extension to enable your nix-shell inside VSCode and have these settings picked up by other extensions. | ||
== | == FAQ == | ||
=== Rust from a rustup toolchain file === | |||
<syntaxhighlight lang="nix"> | |||
< | let | ||
rust-overlay = builtins.fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz"; | |||
pkgs = import <nixpkgs> { | |||
overlays = [ | |||
(import rust-overlay) | |||
(_: prev: { | |||
my-rust-toolchain = prev.rust-bin.fromRustupToolchainFile ./toolchain.toml; | |||
}) | |||
]; | ]; | ||
}; | }; | ||
}) | in | ||
</ | pkgs.callPackage ( | ||
{ | |||
mkShell, | |||
my-rust-toolchain, | |||
}: | |||
mkShell { | |||
strictDeps = true; | |||
nativeBuildInputs = [ | |||
my-rust-toolchain | |||
]; | |||
} | |||
) { } | |||
</syntaxhighlight> | |||
From https://ayats.org/blog/nix-rustup and https://github.com/oxalica/rust-overlay?tab=readme-ov-file#cheat-sheet-common-usage-of-rust-bin | |||
=== Building Rust crates that require external system libraries === | === Building Rust crates that require external system libraries === | ||
For example, the <code>openssl-sys</code> crate needs the OpenSSL static libraries and searches for the library path with <code>pkg-config</code>. That's why you need to have the Nix derivatives <code>openssl</code> and <code>pkg-config</code> in order to build that crate. You'll need to start a shell providing these packages: | For example, the <code>openssl-sys</code> crate needs the OpenSSL static libraries and searches for the library path with <code>pkg-config</code>. That's why you need to have the Nix derivatives <code>openssl</code> and <code>pkg-config</code> in order to build that crate. You'll need to start a shell providing these packages: | ||
| Line 269: | Line 441: | ||
</syntaxHighlight> | </syntaxHighlight> | ||
Note that you need to use a <code>nix-shell</code> environment. Installing the Nix packages <code>openssl</code> or <code>sqlite</code> globally under <code>systemPackages</code> in NixOS or in <code>nix-env</code> [ | Note that you need to use a <code>nix-shell</code> environment. Installing the Nix packages <code>openssl</code> or <code>sqlite</code> globally under <code>systemPackages</code> in NixOS or in <code>nix-env</code> [[FAQ/I installed a library but my compiler is not finding it. Why? | is discouraged]] and doesn't always work (<code>pkg-config</code> may not be able to locate the libraries). | ||
=== Building with a different Rust version than the one in Nixpkgs === | === Building with a different Rust version than the one in Nixpkgs === | ||
The following uses the [https://github.com/nix-community/fenix fenix] overlay and <code>makeRustPlatform</code> to build a crate with Rust | The following uses the [https://github.com/nix-community/fenix fenix] overlay and <code>makeRustPlatform</code> to build a crate with Rust Nightly: | ||
<syntaxHighlight lang="nix"> | <syntaxHighlight lang="nix"> | ||
| Line 301: | Line 473: | ||
} | } | ||
</syntaxHighlight> | </syntaxHighlight> | ||
=== Using LLD instead of LD === | |||
If you want to use <code>lld</code>, then the correct way to do this is to use <code>pkgs.llvmPackages.bintools</code>, <em>not</em> <code>pkgs.lld</code>. This is because the former uses a wrapper script that correctly sets <code>rpath</code>. You can find more information about this [https://matklad.github.io/2022/03/14/rpath-or-why-lld-doesnt-work-on-nixos.html here]. | |||
[[Category:Languages]] | |||
[[Category:Rust]] | |||