Rust: Difference between revisions

Artturin (talk | contribs)
FAQ: from a rustup toolchain file
Ind E (talk | contribs)
m fix typo bindgen misspelled as bindegen
 
(12 intermediate revisions by 5 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">
<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.cargo pkgs.rustc ];
pkgs.callPackage (
}
  {
</syntaxHighlight>
    mkShell,
    cargo,
    rustc,
  }:
  mkShell {
    strictDeps = true;
    nativeBuildInputs = [
      cargo
      rustc
    ];
  }
) { }
</syntaxhighlight>


== Installating with bindgen support ==
== Installating with bindgen support ==
By default crates using <code>bindgen</code> will not compile. To add bindegen support add the <code>rustPlatform.bindegenHook</code> to your <code>nativeBuildInputs</code>.
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>:
Here's an example <code>shell.nix</code>:


<syntaxHighlight lang="nix">
<syntaxhighlight lang="nix">
{ pkgs ? import <nixpkgs> {} }:
{
pkgs.mkShell {
  pkgs ? import <nixpkgs> { },
  nativeBuildInputs = [  
}:
    pkgs.cargo
pkgs.callPackage (
    pkgs.rustc
  {
    pkgs.rustPlatform.bindgenHook
    mkShell,
    # optional: add pkg-config support
    cargo,
    pkgs.pkg-config
    rustc,
  ];
    rustPlatform,
  buildInputs = [
    pkg-config,
    # add desired native packages
  }:
  mkShell {
    strictDeps = true;
    nativeBuildInputs = [
      cargo
      rustc
      rustPlatform.bindgenHook
      # optional: add pkg-config support
      pkg-config
    ];
    buildInputs = [
      # add desired native packages
      # ...
    ];
     # ...
     # ...
   ];
   }
  # ...
) { }
}
</syntaxhighlight>
</syntaxHighlight>


This also works, when compiling rust crates:
This also works, when compiling rust crates:


<syntaxHighlight lang="nix">
<syntaxHighlight lang="nix">
{  
{
   rustPlatform,
   rustPlatform,
   pkg-config,
   pkg-config,
Line 72: 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">
<syntaxhighlight lang="nix">
{ pkgs ? import <nixpkgs> {} }:
{
  let
  pkgs ? import <nixpkgs> { },
    overrides = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml));
}:
    libPath = with pkgs; lib.makeLibraryPath [
let
      # load external libraries that you need in your rust project here
  overrides = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml));
    ];
in
in
  pkgs.mkShell rec {
pkgs.callPackage (
     buildInputs = with pkgs; [
  {
      clang
     stdenv,
      # Replace llvmPackages with llvmPackages_X, where X is the latest LLVM version (at the time of writing, 16)
    mkShell,
      llvmPackages.bintools
    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
    LIBCLANG_PATH = pkgs.lib.makeLibraryPath [ pkgs.llvmPackages_latest.libclang.lib ];
     shellHook = ''
     shellHook = ''
       export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin
       export PATH="''${CARGO_HOME:-~/.cargo}/bin":"$PATH"
       export PATH=$PATH:''${RUSTUP_HOME:-~/.rustup}/toolchains/$RUSTC_VERSION-x86_64-unknown-linux-gnu/bin/
       export PATH="''${RUSTUP_HOME:-~/.rustup}/toolchains/$RUSTC_VERSION-${stdenv.hostPlatform.rust.rustcTarget}/bin":"$PATH"
      '';
    '';
    # Add precompiled library to rustc search path
    RUSTFLAGS = (builtins.map (a: ''-L ${a}/lib'') [
      # add libraries here (e.g. pkgs.libvmi)
    ]);
    LD_LIBRARY_PATH = libPath;
    # Add glibc, clang, glib, and other headers to bindgen search path
    BINDGEN_EXTRA_CLANG_ARGS =
    # Includes normal include path
    (builtins.map (a: ''-I"${a}/include"'') [
      # add dev libraries here (e.g. pkgs.libvmi.dev)
      pkgs.glibc.dev
    ])
    # Includes with special directory paths
    ++ [
      ''-I"${pkgs.llvmPackages_latest.libclang.lib}/lib/clang/${pkgs.llvmPackages_latest.libclang.version}/include"''
      ''-I"${pkgs.glib.dev}/include/glib-2.0"''
      ''-I${pkgs.glib.out}/lib/glib-2.0/include/''
    ];
   }
   }
</syntaxHighlight>
) { }
 
</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 125: Line 141:
                   # or nightly-20XX-XX-XX for a specific nightly.
                   # or nightly-20XX-XX-XX for a specific nightly.
</syntaxhighlight>
</syntaxhighlight>
The important part is that this also works with complex setups using bindgen and precompiled C libraries. To add a new C library in the search path of bindgen and rustc edit the variables <code>BINDGEN_EXTRA_CLANG_ARGS</code> and <code>RUSTFLAGS</code>


== Cross-compiling ==
== Cross-compiling ==
Line 134: 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 152: Line 171:


== Using overrideAttrs with Rust Packages ==
== Using overrideAttrs with Rust Packages ==
There are two ways to use <code>overrideAttrs</code> with Rust packages:


[https://discourse.nixos.org/t/is-it-possible-to-override-cargosha256-in-buildrustpackage/4393/7 This does not seem to be possible.]
* Using [[Import From Derivation]]:
 
<p>
== Using overrideArgs with Rust Packages ==
<syntaxhighlight lang="nix">
 
nil = pkgs.nil.overrideAttrs (
This is a bit tricky, you can't just use <code>overrideArgs</code>. [https://discourse.nixos.org/t/is-it-possible-to-override-cargosha256-in-buildrustpackage/4393/3 Here] is one example of how to do it. The trick is to use two nested calls to <code>overrideAttrs</code>; the outer call overrides the <code>cargoDeps</code> attribute, the inner call rebuilds the vendored tarball and provides the updated hash:
  finalAttrs: previousAttrs: {
 
    version = "unstable-2024-09-19";
<syntaxHighlight lang="nix">
                                                                         
overlays = [
    src = pkgs.fetchFromGitHub {
   (final: prev: {
      owner = "oxalica";
     some-nixpkgs-package = prev.some-nixpkgs-package.overrideAttrs (oldAttrs: {
      repo = "nil";
       cargoDeps = oldAttrs.cargoDeps.overrideAttrs (_: {
      rev = "c8e8ce72442a164d89d3fdeaae0bcc405f8c015a";
        # ...
      hash = "sha256-mIuOP4I51eFLquRaxMKx67pHmhatZrcVPjfHL98v/M8=";
       });
    };
     });
                                                                         
   })
    # Requires IFD
];
    cargoDeps = pkgs.rustPlatform.importCargoLock {
</syntaxHighlight>
      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 241: Line 293:


== Shell.nix example ==
== Shell.nix example ==
<syntaxHighlight lang="nix">
<syntaxhighlight lang="nix">
{ pkgs ? import <nixpkgs> {} }:
{
pkgs.mkShell {
  pkgs ? import <nixpkgs> { },
  nativeBuildInputs = with pkgs; [ rustc cargo gcc rustfmt clippy ];
}:
 
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
    # Certain Rust tools won't work without this
  # This can also be fixed by using oxalica/rust-overlay and specifying the rust-src extension
    # rust-analyzer from nixpkgs does not need this.
  # 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.
    # This can also be fixed by using oxalica/rust-overlay and specifying the rust-src extension
  RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
    # 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>
  }
) { }
</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">
<syntaxhighlight lang="nix">
/*
based on
https://discourse.nixos.org/t/how-can-i-set-up-my-rust-programming-environment/4501/9
*/
let
let
  rust_overlay = import (builtins.fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz");
  pkgs = import <nixpkgs> { overlays = [ rust_overlay ]; };
   rustVersion = "latest";
   rustVersion = "latest";
   #rustVersion = "1.62.0";
   #rustVersion = "1.62.0";
   rust = pkgs.rust-bin.stable.${rustVersion}.default.override {
   rust_overlay = import (
    extensions = [
    builtins.fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz"
      "rust-src" # for rust-analyzer
  );
      "rust-analyzer"
  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 (
  buildInputs = [
  {
     rust
    mkShell,
  ] ++ (with pkgs; [
    hello,
    pkg-config
    my-rust,
     # other dependencies
    pkg-config,
     #gtk3
    openssl,
     #wrapGAppsHook
  }:
  ]);
  mkShell {
  RUST_BACKTRACE = 1;
    strictDeps = true;
}
    # host/target agnostic programs
</syntaxHighlight>
    depsBuildBuild = [
      hello
     ];
    # compilers & linkers & dependecy finding programs
    nativeBuildInputs = [
      my-rust
      pkg-config
     ];
     # libraries
     buildInputs = [
      openssl
    ];
    RUST_BACKTRACE = 1;
  }
) { }
 
</syntaxhighlight>


=== VSCode integration ===
=== VSCode integration ===
The [https://marketplace.visualstudio.com/items?itemName=rust-lang.rust rust-lang.rust] and [https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer rust-lang.rust-analyzer] VSCode extensions offer Rust support.  
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.
Line 298: Line 395:
   rust-overlay = builtins.fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz";
   rust-overlay = builtins.fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz";
   pkgs = import <nixpkgs> {
   pkgs = import <nixpkgs> {
     overlays = [(import rust-overlay)];
     overlays = [
      (import rust-overlay)
      (_: prev: {
        my-rust-toolchain = prev.rust-bin.fromRustupToolchainFile ./toolchain.toml;
      })
    ];
   };
   };
  toolchain = pkgs.rust-bin.fromRustupToolchainFile ./toolchain.toml;
in
in
  pkgs.mkShell {
pkgs.callPackage (
  {
    mkShell,
    my-rust-toolchain,
  }:
  mkShell {
    strictDeps = true;
     nativeBuildInputs = [
     nativeBuildInputs = [
       toolchain
       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
) { }
</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 ===
Line 369: Line 479:


[[Category:Languages]]
[[Category:Languages]]
[[Category:Rust]]