Crane override toolchain fromManifestFile

direnv allow produces this error on a flake that I think was working fine before:

error: attempt to call something which is not a function but a set: { appendCrateRegistries = «thunk»; buildDepsOnly = «thunk»; buildPackage = «thunk»; buildTrunkPackage = «thunk»; callPackage = «thunk»; cargo = «thunk»; cargoAudit = «thunk»; cargoBuild = «thunk»; cargoClippy = «thunk»; cargoDeny = «thunk»; «48 attributes elided» }
       at /nix/store/7h196hqmxizdbiidmnbjwydy5x4kszlq-source/flake.nix:36:22:
           35|           inherit (pkgs) lib;
           36|           craneLib = (crane.mkLib pkgs).overrideToolchain
             |                      ^
           37|             fenix.packages.${system}.fromManifestFile ./rust-toolchain.toml;
direnv: nix-direnv: Evaluating current devShell failed. Falling back to previous environment!
direnv: export +NIX_DIRENV_DID_FALLBACK ~PATH

Same error on my other nix machine. I am quite inexperienced with nix, so I’m questioning how I ever got my environment working in the first place since I can compile to wasm32 targets just fine. It’s possible I got my environment working with a valid flake but committed unverified changes… but that has me questioning my sanity a bit :upside_down_face:. Or maybe something did change outside of my control.

This is the commit that breaks things: Add wasm32-unknown-unknown target

I remembered struggling getting crane.mkLib to work in that commit. Specifically I was having some errors related to lib which I fixed by adding inherit (pkgs) lib as shown in the crane quick start example.

I assume updates to my inputs were to blame for the latter issue I fixed, but what is to blame for my current issue?

Here’s my full flake:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    flake-utils.url = "github:numtide/flake-utils";

    crane = {
      url = "github:ipetkov/crane";
      inputs = {
        flake-utils.follows = "flake-utils";
        nixpkgs.follows = "nixpkgs";
      };
    };
    rust-overlay = {
      url = "github:oxalica/rust-overlay";
      inputs = {
        nixpkgs.follows = "nixpkgs";
        flake-utils.follows = "flake-utils";
      };
    };
    fenix = {
      url = "github:nix-community/fenix";
      inputs.nixpkgs.follows = "nixpkgs";
      inputs.rust-analyzer-src.follows = "";
    };
  };

  outputs = { self, nixpkgs, crane, fenix, flake-utils, rust-overlay }:
    flake-utils.lib.eachDefaultSystem
      (system:
        let
          pkgs = import nixpkgs {
            inherit system;
            overlays = [ (import rust-overlay) ];
          };
          inherit (pkgs) lib;
          craneLib = (crane.mkLib pkgs).overrideToolchain
            fenix.packages.${system}.fromManifestFile ./rust-toolchain.toml;
          buildInputs = with pkgs; [
            # Dev tools
            nixd
            
            # Build tools
            pkg-config
          ] ++ lib.optionals stdenv.isLinux [
            alsa-lib
            libxkbcommon
            udev
            vulkan-loader
            wayland
            xorg.libX11
            xorg.libXcursor
            xorg.libXi
            xorg.libXrandr
          ] ++ lib.optionals stdenv.isDarwin [
            darwin.apple_sdk_11_0.frameworks.Cocoa
            rustPlatform
          ];
        in
        {
          devShells.default = craneLib.devShell {
            inherit buildInputs;

            LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs;
            RUSTFLAGS="-Z crate-attr=feature(const_trait_impl)";
          };
        }
      );
}
1 Like

I think the original error was because you needed to add parenthesis for overrideToolchain:

craneLib = (crane.mkLib pkgs).overrideToolchain (
  fenix.packages.${system}.fromManifestFile ./rust-toolchain.toml
);

Without them fromManifestFile (which is a function) will be passed to overrideToolchain, but it expects the result of running fromManifestFile on the rust-toolchain.toml file (which is a set) instead of a function.

That said, even if we fix this, we get another error:

 error: attribute 'pkg' missing
 at /nix/store/qpbr4mw34wjhm60r64hk09hnqlkq90xp-source/default.nix:46:13:
     45|             (_: pkg: pkg.target."*" or pkg.target.${target} or null)
     46|             manifest.pkg));
       |             ^
     47|       };

This is because we need to use fromToolchainFile and not fromManifestFile to read the toolchain file:

craneLib = (crane.mkLib pkgs).overrideToolchain (
  fenix.packages.${system}.fromToolchainFile {
    file = ./rust-toolchain.toml;
    sha256 = "sha256-3fl/ceyskbvb1AUB1gvWttN4Yc4InP+Yu7wG7VeuP1g=";
  }
);

This all works fine, but there is no need to mix fenix and rust-overlay in my opinion, so I suggest you only pick one of them and remove the other.

For rust-overlay, this is how you’d set up craneLib:

craneLib = (crane.mkLib pkgs).overrideToolchain (
  p: p.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml
);
1 Like

Thanks so much for the explanation!

I misunderstood that rust-overlay was necessary for making the override possible, and fenix itself provided the nightly toolchain. Since relying on one of the two is all that is necessary that’s great!

I’m having trouble understanding what p: p is. It’s seems like such a unique syntax I havn’t seen in many places in nix. There’s also an interesting example here where p: is used seemingly out of nowhere.

Why am I able to use fenix like so: (crane.mkLib pkgs).overrideToolchain (fenix.packages.${system}.fromToolchainFile { ... }) but when using rust overlay, I have to use this special p?

That’s correct, (p: ...) is a function that takes a pkgs set (the one with the rust-overlay applied in this case).

The name p doesn’t really matter as it can be called anything, really. What matters is that we pass a function to overrideToolchain. Why do we do that, though? If you try to use pkgs directly, that will still work:

craneLib = (crane.mkLib pkgs).overrideToolchain (
  pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml
);

Well, according to the crane docs:

Note that in order to best support cross compilation, overrideToolchain should be provided a function (whose argument is a cross-compilation aware version of pkgs) which constructs the toolchain:

craneLib.overrideToolchain (p: myCustomToolchainForPkgs p)

So it’s just something good to have by default for better cross compilation.

PS: see Custom toolchain - crane for a full wasm example using rust-overlay

With fenix, you will probably need to add it to the overlay first as per the Usage - As an overlay section, then you could probably pass a function. In the Quick start - crane example, it’s just passed directly, though.

1 Like