Rust-src not found, and other misadventures of developing rust on NixOS

I’m trying to set up a shell.nix for rust development:

let
  # Last updated: 2/12/21
  pkgs = import (fetchTarball("https://github.com/NixOS/nixpkgs/archive/a58a0b5098f0c2a389ee70eb69422a052982d990.tar.gz")) {};

  # Rolling updates, not deterministic.
  # pkgs = import (fetchTarball("channel:nixpkgs-unstable")) {};
in pkgs.mkShell {
  buildInputs = [
    pkgs.cargo
    pkgs.rustc

    # Necessary for the openssl-sys crate:
    pkgs.openssl
    pkgs.pkg-config
  ];
}

This is great in that it gives me cargo and rustc in the path, and I can build my project, but I’m not able to use rust-analyzer in vscode:

rust-analyzer failed to load workspace: Failed to find sysroot for Cargo.toml file /home/skainswo/dev/cuddlefish/api/Cargo.toml. Is rust-src installed?: can't load standard library from sysroot /nix/store/l3icq4dzpk5cwq3mf4459cd7ydrxvy0i-rustc-1.49.0 (discovered via `rustc --print sysroot`) try installing the Rust source the same way you installed rustc

Ok, so I’m missing some kind of package called rust-src, which I’m assuming contains extra goodies. But a quick search reveals no nix packages: NixOS Search.

I did notice that https://github.com/NixOS/nixpkgs/blob/8199872bba4fdb836c093b58ee4092266fec5592/pkgs/development/compilers/rust/rust-src.nix exists, although I have no idea how to “use” it. How is this exposed to users?

Also, our wiki entry on rust (Rust - NixOS Wiki) looks a bit sad atm. I’d be more than happy to update it, although I still haven’t figured out how to get things working for my own purposes yet…

Anyone know how to use rust-analyzer with the nixpkgs versions of rustc and cargo?

3 Likes

Update: just found the pkgs.rust.packages.stable.rustPlatform.rustcSrc package, but unfortunately adding it does not seem to change anything…

Many Rust analysis tools will inspect RUST_SRC_PATH, so you should be able to do something like (untested):

pkgs.mkShell {
   # ...

   RUST_SRC_PATH = "${rustPlatform.rustLibSrc}";
}
5 Likes

Using the rust-overlay provided by @oxalica I was able solve the issue you’re facing with the following in my shell.nix:

{}:
let
  rust-overlay = (import (builtins.fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz"));
  pkgs = (import <nixpkgs> {
    overlays = [ rust-overlay ];
  });
in
pkgs.mkShell {
  buildInputs = [
    (pkgs.rust-bin.stable.latest.rust.override {
      extensions = ["rust-src"];
    })

    # keep this line if you use bash
    pkgs.bashInteractive
  ];
}

You can additionally pick your preferred rust channel in with this overlay as well.

3 Likes

This did the trick for me, thank you!! Out of curiosity, why doesn’t cargo or rustc set up this env var automatically? I feel like it would be convenient for users to have a single derivation they could include and get a full, working rust development environment.

EDIT: The full bit that worked for me was

RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";

for anyone that comes across this issue in the future…

5 Likes

This would add the library source to the closure of cargo/rustc.

Perhaps we could include a new convenience derivation like rust-everything (or rust-moar or just-let-me-rust) or something to bring in rustc, cargo, rustLibSrc, rustfmt, etc?

3 Likes

I’m facing the same issue right now :smiley: , I’m using flakes, and here what I got from prev comments:

            pkgs.devShell."${system}" = pkgs.mkShell {
              buildInputs = [
                (
                  pkgs.rust-bin.stable.latest.rust.override {
                    extensions = [ "rust-src" ];
                  }
                )
              ];
              RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
              nativeBuildInputs = with pkgs; [
                cargo
                clippy
                rust-analyzer
                rustc
              ];
            };

Working on it right now

1 Like

This worked for me last time I tried

{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = [
    pkgs.latest.rustChannels.stable.rust
    pkgs.latest.rustChannels.stable.rust-src
  ];
  RUST_SRC_PATH="${pkgs.latest.rustChannels.stable.rust-src}/lib/rustlib/src/rust/library/";
}
2 Likes

Thanks a lot Richard.

This worked for me with the nixpkgs-mozilla overlay!

This is my way of setting RUST_SRC_PATH using GitHub - oxalica/rust-overlay: Pure and reproducible nix overlay of binary distributed rust toolchains only:

{
  description = "Rust broker-v2 dev environment";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    rust-overlay.url = "github:oxalica/rust-overlay";
    flake-utils = {
      url = "github:numtide/flake-utils";
      inputs.nixpkgs.follows = "nixkgs";
    };
  };

  outputs = {
    self,
    nixpkgs,
    rust-overlay,
    flake-utils,
    ...
  }:
    flake-utils.lib.eachDefaultSystem (
      system: let
        pkgs = import nixpkgs {
          inherit system;
          overlays = [
            (import rust-overlay)
          ];
        };
      in {
        devShell = pkgs.mkShell {
          nativeBuildInputs = with pkgs; [
            (pkgs.rust-bin.stable.latest.default.override {
                  extensions = [ "rust-src" "cargo" "rustc" ];
            })
            gcc
          ];

          RUST_SRC_PATH = "${pkgs.rust-bin.stable.latest.default.override {
              extensions = [ "rust-src" ];
          }}/lib/rustlib/src/rust/library";


          buildInputs = with pkgs; [
            openssl.dev
            glib.dev
            pkg-config

            clippy
            rust-analyzer
            just
          ];
        };
      }
    );
}

➜ echo $RUST_SRC_PATH
/nix/store/w9w1b0lxgj9x1n40vba0jkzwh0hc8g5z-rust-default-1.61.0/lib/rustlib/src/rust/library
3 Likes

This is how I use rust-analyzer in my editor:

I initially create a wrapper that puts the fenix toolchain in PATH, so that the editor has the required tools and the src:

  environment.systemPackages = [
    (nixpkgs.writeShellScriptBin "editor" ''
      export PATH="${fenix.latest.toolchain}/bin:$PATH"
      exec ${bin} "$@"
    '')
  ];

This toolchain is used only by the editor, but it’s not globally present. Then I use a regular devShell in my rust projects

Then I use the fenix.rust-analyzer-vscode-extension, which will use the toolchain from the previous step

This is the complete module:

My projects use rust from nixpkgs, and only my editor uses the Fenix toolchains, for example:

1 Like

Note that if you do that, running cargo commands in a shell that does not share an exact env with your editor may end up rebuilding things from scratch (because rust assumes certain changes in environment mean it needs to do so).

This can make rust-analyzer as well as your normal builds extremely slow, so try to stick to compiling with the editor.

1 Like

Another option if you want to go the dream2nix route:

Is it just me or did something break mid december? Now rust won’t pick up that it’s added the wasm target any more.

Anybody know how to fix this if your rust is installed like this?

/Users/alpercugun/.nix-profile/bin/rustc

I’m a bit miffed that the packaging here broke something fundamental like rust-analyzer. Isn’t this a bug?

In general I’d recommend using rustc and cargo on a per-project basis. Mixing globally installed Rust stuff with local environments is likely to be un-fun.

3 Likes

Well yeah, I tried figuring out how to add this to the flake.nix that I have but that was also non-obvious.

{
  inputs = {
    crane = {
      url = "github:ipetkov/crane";
      inputs = {
        nixpkgs.follows = "nixpkgs";
        rust-overlay.follows = "rust-overlay";
        flake-utils.follows = "flake-utils";
      };
    };
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
    rust-overlay.url = "github:oxalica/rust-overlay";
  };
  outputs = { self, nixpkgs, flake-utils, rust-overlay, crane }:
    flake-utils.lib.eachDefaultSystem
      (system:
        let
          overlays = [ (import rust-overlay) ];
          pkgs = import nixpkgs {
            inherit system overlays;
          };

          rustToolchain = pkgs.pkgsBuildHost.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;

          craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain;
          src = craneLib.path ./.;

          nativeBuildInputs = with pkgs; [
            rustToolchain
            pkg-config
          ];
          # pkgs.darwin.apple_sdk.frameworks.Security
          buildInputs = with pkgs; [ openssl ];

          commonArgs = {
            inherit src buildInputs nativeBuildInputs;
          };
          cargoArtifacts = craneLib.buildDepsOnly commonArgs;

          database = pkgs.stdenv.mkDerivation {
            name = "database";
            src = ./.;
            phases = [ "unpackPhase" "installPhase" ];
            installPhase = ''
              mkdir -p $out
              cp $src/.env $out
              cp $src/20221002.sqlite $out
            '';
          };

          bin = craneLib.buildPackage (commonArgs // {
            inherit cargoArtifacts;
          });

          dockerImage = pkgs.dockerTools.buildImage {
            name = "cuppings";
            tag = "latest";
            copyToRoot = [ bin database ];
            config = {
              Cmd = [ "${bin}/bin/cuppings"];
              # WorkingDir = "${bin}/bin";
            };
          };
        in
        with pkgs;
        {
          packages =
          {
            inherit bin database dockerImage;
            default = bin;
          };

          devShells.default = mkShell {
            inputsFrom = [ bin ];

            # buildInputs = with pkgs; [ dive ];
          };
        }
      );
}

These are side projects where I have a couple of hours every so many weeks to work on them. I really can’t afford spending most of that time fighting with things that are broken in Nix.

Switching to Rustup also fixed the thing where Rust would rebuild my entire project on every change.

1 Like