Trouble building a C++ project in a nix development shell

I am trying to set up a reproducible C++ development environment with nix-shell for an existing project, and I have run into build issues even though the clang versions in and outside of the nix environment should match. The first issue was compiler warnings, which I was able to resolve by adding the appropriate -Wno- compiler flag.

Now I am stuck on a linker error. The linker is complaining about multiple definitions of __cxa_throw, one in libstdc++.a(eh_throw.o) in the nix store, the other in one of my project files. It’s entirely possible that the project is doing something funny that it should not be doing, but why is this only an issue in the nix shell?

Unfortunately, the build system is so complex that I have been unable to come up with a minimal reproduction of the build errors only occurring under the nix shell, so I instead have to ask for general advice on what to look for, and why clang from the llvm.org Ubuntu packages behaves differently to the corresponding version from nixpkgs?

Finally, is this even the best approach for what I am trying to achieve? I have a shell.nix with just this:

let
  pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/refs/tags/22.05.zip") {};
in pkgs.mkShell {
  packages = [
    clang_13
    ninja
    # etc.
];

Here’s an outline of something that has been working for me. Haven’t touched it since putting it into place a couple of years ago, so the details are hazy in my mind. I’m pretty sure I based this on a very complete setup explained by someone else, somewhere. But this simplified version might be easier to start with.

I have a shell.nix that looks like this (stripped down):

let
  pkgs = import ...;
  derivation = pkgs.callPackage (import ./nix/derivation.nix) {};
in

pkgs.llvmPackages_13.stdenv.mkDerivation {
  inherit (derivation) name;
  nativeBuildInputs = derivation.nativeBuildInputs ++ [
    pkgs.clang_13
  ];

  buildInputs = derivation.buildInputs ++ [
    pkgs.clang_13
    pkgs.cmake
    pkgs.catch2
    pkgs.cmake-language-server
    pkgs.gdb
    # more development dependencies ...
  ];
}

with a corresponding nix/derivation.nix:

# Fundamental derivation of how to build the project: a function from
# dependencies to build-result. Picking the specific versions of the
# dependencies is Someone Else's Problem: `default.nix`, `release.nix` get to
# pick these.

# geant4 is just an example of a dependency, replace with your own 

{ geant4, stdenv }:

stdenv.mkDerivation {
  name = "my-package-name";
  src = ../.;

  # build-time dependencies
  nativeBuildInputs = [
    geant4
    geant4.data.G4PhotonEvaporation
    geant4.data.G4EMLOW
    geant4.data.G4RadioactiveDecay
    geant4.data.G4ENSDFSTATE
    geant4.data.G4SAIDDATA
    geant4.data.G4PARTICLEXS
    geant4.data.G4NDL

  ];

  # run-time dependencies
  buildInputs = [

  ];

  # buildPhase = ...
  # installPhase = ...

}

I’ve commented out the build/installPhase as I don’t actually use them: it seems that I have a justfile in which I’ve encoded the cmake build directory song-and-dance, as I don’t actually use Nix to publish this project, only to allow me to work on it. Which sounds similar to what you want.

Hosted by Flying Circus.