Rust: Difference between revisions
Console4852 (talk | contribs) Remove Rust Nightlies section. This section had no information that wasn't found on the rest of the page (only having two bullet points referring to other sections of the page). Instead, modify the page to elaborate more in the respective sections. |
m Format table so the header is clearly distinguished from the rest of the rows. |
||
| (18 intermediate revisions by 9 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 best way to use Rust, but there are valid reasons to use any approach. | 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 switch between multiple toolchains then this approach may not be for you. | 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> | ||
| Line 28: | Line 96: | ||
If you want the most "normal" Rust experience I recommend using rustup with the following example shell.nix: | 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 ? import <nixpkgs> { }, | |||
}: | |||
let | |||
overrides = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml)); | |||
in | in | ||
pkgs.callPackage ( | |||
{ | |||
stdenv, | |||
mkShell, | |||
rustup, | |||
rustPlatform, | |||
}: | |||
mkShell { | |||
strictDeps = true; | |||
nativeBuildInputs = [ | |||
rustup | rustup | ||
rustPlatform.bindgenHook | |||
]; | ]; | ||
# libraries here | |||
buildInputs = | |||
[ | |||
]; | |||
RUSTC_VERSION = overrides.toolchain.channel; | 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.toml</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. | ||
| Line 81: | Line 141: | ||
# or nightly-20XX-XX-XX for a specific nightly. | # or nightly-20XX-XX-XX for a specific nightly. | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Cross-compiling == | == Cross-compiling == | ||
| Line 90: | 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 108: | Line 171: | ||
== 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 131: | Line 227: | ||
At the time of writing, there are now no less than 8 different solutions for building Rust code with Nix. In the following table they are compared: | At the time of writing, there are now no less than 8 different solutions for building Rust code with Nix. In the following table they are compared: | ||
{| | {| class="wikitable" style="margin:auto" | ||
|- | |- | ||
| [https://github.com/NixOS/nixpkgs/blob/ | ! Name !! Cargo.lock solution !! Derivations !! Build logic !! Supports cross !! Notes | ||
|- | |||
| <code>[https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/rust.section.md buildRustPackage]</code> | |||
| Checksum | | Checksum | ||
| 1 | | 1 | ||
| Line 181: | Line 273: | ||
| 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 197: | Line 289: | ||
== 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 === | === Custom Rust version or targets === | ||
< | <syntaxhighlight lang="nix"> | ||
let | let | ||
rustVersion = "latest"; | rustVersion = "latest"; | ||
#rustVersion = "1.62.0"; | #rustVersion = "1.62.0"; | ||
rust = | 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 | in | ||
pkgs.mkShell { | 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 == | == 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 308: | Line 475: | ||
[[Category:Languages]] | [[Category:Languages]] | ||
[[Category:Rust]] | |||