How to override stdenv for all packages in mkShell

(I am a nix newbie, mostly trying to setup my first project)

I am trying to ensure that all dependencies/buildInputs for my mkShell derivation (in a shell.nix) are built using a specific stdenv – in my case gcc10Stdenv. I can seem to create a new stdenv but I’m not sure how to use a named one like gcc10Stdenv, so I can take advantage of the binary cache.

I am currently using this:
shell.nix

{ pkgs ? import <nixpkgs> {
  overlays = [
    (import ./nix/host-gcc10Stdenv.nix)
  ];
} }:
pkgs.mkShell {
  buildInputs = [
    pkgs.libxmlxx # I want this and its dependencies built with GCC10
    # and so on ...
  ];
}

nix/host-gcc10Stdenv.nix

(self: super:
  let
    stdenv =
      let stdenv = super.overrideCC super.stdenv super.gcc10;
      in
        stdenv.override { cc = super.stdenv.cc; };
  in {
    inherit stdenv;
  }
)

This rebuilds a whole new stdenv with gcc10, how can I simply specify to use the gcc10Stdenv as stdenv, ideally directly in shell.nix? I’ve tried using pkgs.gcc10Stdenv.mkDerivation instead of mkShell, but it seems my buildInputs do not get compiled with that specified stdenv.

2 Likes

You can override the shell used for mkShell like

pkgs.mkShell.override {
  stdenv = gcc10Stdenv;
} ...

Not sure if that does what you want though…

2 Likes

Instead of importing, you can just include the expression inline. You should also be able to override the stdenv directly:

{ pkgs ? import <nixpkgs> {
  overlays = [
    (final: prev: { stdenv = prev.gcc10Stdenv; })
  ];
} }:
pkgs.mkShell {
  buildInputs = [
    pkgs.libxmlxx # I want this and its dependencies built with GCC10
    # and so on ...
  ];
}

Though I that should be basically the same as what you have – you will have to build everything yourself since we do not generally build stuff with other stdenvs except for some subsets of packages for selected ones.

1 Like

@jtojnar When I try that (that was similar to my initial attempt to override stdenv via overlays), I get an infinite recursion error:

> nix-shell --pure --show-trace
error: while evaluating anonymous function at ./shell.nix:1:1, called from undefined position:
while evaluating anonymous function at /Users/paulreimer/Development/nixos/nixpkgs/pkgs/top-level/impure.nix:15:1, called from ./shell.nix:1:10:
while evaluating anonymous function at /Users/paulreimer/Development/nixos/nixpkgs/pkgs/top-level/default.nix:20:1, called from /Users/paulreimer/Development/nixos/nixpkgs/pkgs/top-level/impure.nix:84:1:
while evaluating anonymous function at /Users/paulreimer/Development/nixos/nixpkgs/pkgs/stdenv/booter.nix:42:1, called from /Users/paulreimer/Development/nixos/nixpkgs/pkgs/top-level/default.nix:126:10:
while evaluating 'dfold' at /Users/paulreimer/Development/nixos/nixpkgs/pkgs/stdenv/booter.nix:60:27, called from /Users/paulreimer/Development/nixos/nixpkgs/pkgs/stdenv/booter.nix:136:4:
while evaluating 'go' at /Users/paulreimer/Development/nixos/nixpkgs/pkgs/stdenv/booter.nix:63:18, called from /Users/paulreimer/Development/nixos/nixpkgs/pkgs/stdenv/booter.nix:72:13:
while evaluating 'folder' at /Users/paulreimer/Development/nixos/nixpkgs/pkgs/stdenv/booter.nix:89:33, called from /Users/paulreimer/Development/nixos/nixpkgs/pkgs/stdenv/booter.nix:68:18:
while evaluating 'allPackages' at /Users/paulreimer/Development/nixos/nixpkgs/pkgs/top-level/default.nix:116:17, called from /Users/paulreimer/Development/nixos/nixpkgs/pkgs/stdenv/booter.nix:101:12:
while evaluating anonymous function at /Users/paulreimer/Development/nixos/nixpkgs/pkgs/top-level/stage.nix:12:1, called from /Users/paulreimer/Development/nixos/nixpkgs/pkgs/top-level/default.nix:116:26:
while evaluating 'fix' at /Users/paulreimer/Development/nixos/nixpkgs/lib/fixed-points.nix:19:9, called from /Users/paulreimer/Development/nixos/nixpkgs/pkgs/top-level/stage.nix:252:3:
while evaluating 'extends' at /Users/paulreimer/Development/nixos/nixpkgs/lib/fixed-points.nix:69:24, called from /Users/paulreimer/Development/nixos/nixpkgs/lib/fixed-points.nix:19:20:
while evaluating 'stdenvOverrides' at /Users/paulreimer/Development/nixos/nixpkgs/pkgs/top-level/stage.nix:130:27, called from /Users/paulreimer/Development/nixos/nixpkgs/lib/fixed-points.nix:69:67:
while evaluating the attribute 'stdenv.overrides' at ./shell.nix:3:21:
while evaluating the attribute 'gcc10Stdenv' at /Users/paulreimer/Development/nixos/nixpkgs/pkgs/top-level/all-packages.nix:9014:3:
infinite recursion encountered, at /Users/paulreimer/Development/nixos/nixpkgs/pkgs/top-level/all-packages.nix:11:6

@abbec When I try that, I get a weird linker error [while compiling my application which depends on libxmlxx], which I think is because the packages listed in my shell.nix buildInputs are not themselves built with the specified stdenv. I am currently using those lines you have suggested, but I also need this additional overlay hack on my dependencies to set their stdenv, too. (which I’m lucky only goes 2 levels deep so I can override them manually, as below), which I am trying to get rid of:

# ./nix/overlays/host-libxmlxx.nix (really wish I didn't need this file at all)
self: {pkgs, libxmlxx, ...}: {
  libxmlxx = (
    libxmlxx.override{ stdenv = pkgs.gcc10Stdenv; }
  ).overrideAttrs (oldAttrs: rec {
    propagatedBuildInputs = [
      pkgs.libxml2
      (pkgs.glibmm.override{ stdenv = pkgs.gcc10Stdenv; })
    ];
  });
}

So I think somehow the new stdenv is not being used by the dependencies, hence why this hack is required? [somehow the linker error only fires for C++ code using libxml++, so I don’t need to override stdenv on any C-only dependencies (like libxml2 above), but I think that’s just lazy luck and in theory I should be overriding stdenv on that one, too, in my overlay hack.]

I don’t know how to fix your issue, but the difference between the C and C++ dependency is probably due to ABI compatibility. C has a stable ABI while C++ has not, so it’s not just luck in this case. Good luck on fixing this issue!

I’m having an issue with this as well. Overriding stdenv = super.gcc9stdenv; in an overlay causes an infinite recursion error which I’m not sure how to resolve. Any clues? Here’s the script:

let
  pkgs = import <nixpkgs> {
    crossSystem = (import <nixpkgs/lib>).systems.examples.riscv64-embedded;
    overlays = [ (self: super: {
           stdenv = super.gcc9Stdenv; 
      })
    ];
  };

  shell = { mkShell, qemu, gdb, dtc }: mkShell {
    nativeBuildInputs = [ qemu gdb dtc ];
  };

in pkgs.callPackage shell {}

Bumping again. I’m also hitting the infinite recursion when trying to set stdenv in an overlay.

2 Likes

I don’t know if it’s possible to change stdenv in the reqular package set, but it’s possible to add a nested attribute set with an extra indirection:

{ nixpkgsFun ? import <nixpkgs> }:
let
  pkgs = nixpkgsFun {
    overlays = [ (final: prev: {
      pkgs11 = nixpkgsFun {
        overlays = [
          (final': prev': {
            stdenv = prev.gcc11Stdenv;
          })
        ];
      };
    })];
  };
in
pkgs.mkShell {
  buildInputs = [
    pkgs.pkgs11.libxmlxx
    # ...
  ];
}

I found this technique in stage.nix.

The drawback of this approach is that it’s a big hammer. It will rebuild all inputs recursively, even the ones that are only used at build time, i.e. nativeBuildInputs.

1 Like

Necrobumping again, is there still a solution to eschew infinite recursion or rebuilding all inputs?

The correct thing to do has always been replaceStdenv:

{ pkgs ? import <nixpkgs> {
  config.replaceStdenv = { pkgs, ... }: pkgs.gcc12Stdenv;
} }:

pkgs.hello
3 Likes

Thank you very much! I just gave it a try and it did all the inputs. The purpose was to avoid gcc. But, clang-wrapper ironically still relies on gcc. I tested with both llvmPackages_latest.{stdenv,libcxxStdenv}.

Giving up :woman_shrugging:. but thanks anyway and happy new year :grinning:

1 Like

Oh, yea, on Linux I don’t think there’s a way to avoid bootstrapping with GCC. Once stdenv is built, nothing else will need to rely on previous stages though. Also FYI, pkgs.pkgsLLVM is a package set that uses clang for stdenv, but again it still gets bootstrapped via GCC.

2 Likes

pkgs.pkgsLLVM is the hidden gem. Finally my project is gcc free. Again, thank you very much :grinning:

1 Like

I guess I don’t understand what you meant by this then? It depends on GCC in the sense that it’s needed for bootstrapping. But the same is true for pkgsLLVM. It also bootstraps llvm/clang with GCC.

1 Like

key information was left out so there came to your confusion. To contextualize it, I’m using the following flake.nix for a project that requires some features from libc++. The is no problem with building the project. The problem is clangd always points to headers file during my daily editing job. Then, I found those unwanted header files come from gcc which results from stdenv.

Without using pkgs.pkgsLLVM, the clang dependency always searches at least from gcc’s c++/v1/include directory. And, this extra unwanted gcc causes a problem. Those gcc dependencies actually have no such an impact. I guess this ambiguity of different gcc dependencies caused confusion :sweat_smile:

Still I have no idea why config.replaceStdenv won’t solve the problem. :sweat_smile: And it’s not needed for my flake.nix; pkgs.pkgsLLVM suffices.

{
  description = "cracking the coding interview";

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

  outputs = { self, nixpkgs, flake-utils, ... }:
    flake-utils.lib.eachDefaultSystem (
      system:
        let
          pkgs = import nixpkgs {
            inherit system;
            # XXX: There is no easy way to avoid libstdc++ include files. See
            # https://discourse.nixos.org/t/use-clang-without-gccs-c-standard-library/9884
            # https://discourse.nixos.org/t/how-to-override-stdenv-for-all-packages-in-mkshell/10368/10
            # https://discourse.nixos.org/t/gcc11stdenv-and-clang/17734
            # overlays = [ (_: super: { stdenvNoCC = super.llvmPackages_latest.libcxxStdenv; }) ];
            # config.replaceStdenv = { pkgs, ...}: pkgs.llvmPackages_latest.stdenv;
          };
          llvm = pkgs.pkgsLLVM.llvmPackages_latest;
          lib = nixpkgs.lib;

        in
          {
            devShell = llvm.stdenv.mkDerivation {
              name = "shell";
              nativeBuildInputs = [
                # builder
                # p.gnumake
                # p.bear
                pkgs.cmake  # for discovering libraries
                pkgs.pkg-config
                pkgs.meson
                pkgs.ninja
                # debugger
                # llvm.lldb
                # pkgs.gdb

                pkgs.gtest
                pkgs.fmt

                # pkgs.leetcode-cli

                llvm.bintools
                pkgs.clang-tools_14  # don't use clangd from llvm.clang
              ] ++ lib.optionals pkgs.stdenv.isLinux [ llvm.lld ]
              ;
              shellHook = lib.optionalString pkgs.stdenv.isLinux ''
                export CC_LD="lld"
                export CXX_LD="lld"
              '';
              LD_LIBRARY_PATH = lib.strings.makeLibraryPath [ pkgs.fmt pkgs.gtest llvm.libcxx ];
              LLVM_PROFILE_FILE="/tmp/test.profraw";
            };
          }
    );
}
2 Likes