Rust: Difference between revisions

Artturin (talk | contribs)
Make everything more cross-compatible
m Update that buildRustPackage can now import Cargo.lock
 
(16 intermediate revisions by 11 users not shown)
Line 12: Line 12:
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 27: Line 27:
   }:
   }:
   mkShell {
   mkShell {
    strictDeps = true;
     nativeBuildInputs = [
     nativeBuildInputs = [
       cargo
       cargo
Line 33: Line 34:
   }
   }
) { }
) { }
</syntaxHighlight>
</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 ? import <nixpkgs> { },
Line 53: Line 54:
   }:
   }:
   mkShell {
   mkShell {
    strictDeps = true;
     nativeBuildInputs = [
     nativeBuildInputs = [
       cargo
       cargo
Line 67: Line 69:
   }
   }
) { }
) { }
</syntaxHighlight>
</syntaxhighlight>


This also works, when compiling rust crates:
This also works, when compiling rust crates:
Line 94: 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 147: 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 156: 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 171: 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://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
[https://web.archive.org/web/20260108165619/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:


[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 197: 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"
| Name
| Cargo.lock solution
| Derivations
| Build logic
| Supports cross
| Notes
|-
|-
| [https://github.com/NixOS/nixpkgs/blob/4fc53b59aecbc25c0e173163d60155f8fca14bfd/doc/languages-frameworks/rust.section.md <code>buildRustPackage</code>]
! Name !! Cargo.lock solution !! Derivations !! Build logic !! Supports cross !! Notes
| Checksum
|-
| <code>[https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/rust.section.md buildRustPackage]</code>
| Checksum or <code>[https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/rust.section.md#importing-a-cargolock-file-importing-a-cargolock-file Import]</code>
| 1
| 1
| cargo
| cargo
Line 263: Line 289:


== Shell.nix example ==
== Shell.nix example ==
<syntaxHighlight lang="nix">
<syntaxhighlight lang="nix">
{
{
   pkgs ? import <nixpkgs> { },
   pkgs ? import <nixpkgs> { },
Line 279: Line 305:
   }:
   }:
   mkShell {
   mkShell {
    strictDeps = true;
     nativeBuildInputs = [
     nativeBuildInputs = [
       rustc
       rustc
Line 294: Line 321:
   }
   }
) { }
) { }
</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 <code>RUST_SRC_PATH</code> environment variable to point to the right location, which tools, such as rust-analyzer, require to be set.


=== Custom Rust version or targets ===
=== Custom Rust version or targets ===
Line 332: Line 359:
   }:
   }:
   mkShell {
   mkShell {
    strictDeps = true;
     # host/target agnostic programs
     # host/target agnostic programs
     depsBuildBuild = [
     depsBuildBuild = [
Line 351: Line 379:
</syntaxhighlight>
</syntaxhighlight>


=== VSCode integration ===
=== VS Code 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-analyzer] VS Code extension offers Rust support.
 
If you get the error <code>can't load standard library, try installing rust-src</code>, either configure <code>RUST_SRC_PATH</code> as shown above, or set <code>"rust-analyzer.server.path": "rust-analyzer"</code> in your VS Code settings to use rust-analyzer from nixpkgs rather than the one bundled with the extension.


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 377: Line 407:
   }:
   }:
   mkShell {
   mkShell {
    strictDeps = true;
     nativeBuildInputs = [
     nativeBuildInputs = [
       my-rust-toolchain
       my-rust-toolchain
Line 446: Line 477:


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