C++ and libstdc++ not available?

I have a hard time to figure out what toolchain I need to compile with clang 16 and libstdc++ from GCC.

I have the following flake.nix:

{
  description = "Cpp Playground";

  nixConfig = {
    substituters = [
      # Add here some other mirror if needed.
      "https://cache.nixos.org/"
    ];
    extra-substituters = [
      # Nix community's cache server
      "https://nix-community.cachix.org"
    ];
    extra-trusted-public-keys = [
      "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
    ];
  };

  inputs = {
    # Nixpkgs
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";

    # You can access packages and modules from different nixpkgs revs
    # at the same time. Here's an working example:
    nixpkgsStable.url = "github:nixos/nixpkgs/nixos-23.11";
    # Also see the 'stable-packages' overlay at 'overlays/default.nix'.

    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = {
    self,
    nixpkgs,
    nixpkgsStable,
    flake-utils,
    ...
  } @ inputs:
    flake-utils.lib.eachDefaultSystem
    # Creates an attribute map `{ devShells.<system>.default = ...}`
    # by calling this function:
    (
      system: let
        overlays = [];

        # Import nixpkgs and load it into pkgs.
        pkgs = import nixpkgs {
          inherit system overlays;
        };

        nodePackages = pkgs.callPackage ./tools/nix/node-packages/default.nix {};

        clangVersion = "16";
        llvmPkgs = pkgs."llvmPackages_${clangVersion}";

        compilerLinks = pkgs.runCommand "clang-links" {} ''
          mkdir -p $out/bin
          ln -s ${llvmPkgs.libstdcxxClang}/bin/clang $out/bin/clang-${clangVersion}
          ln -s ${llvmPkgs.libstdcxxClang}/bin/clang++ $out/bin/clang++-${clangVersion}
          ln -s ${llvmPkgs.llvm}/bin/llvm-as $out/bin/llvm-as-${clangVersion}
        '';

        buildInputs = with pkgs; [
          # Other tools
          just
          parallel
          docker

          entr
          ninja
          cmake
          ccache

          conan

          valgrind
          cppcheck

          node2nix
          nodePackages."@devcontainers/cli"

          # C++
          # Clangd from clang-tools must come first.
          (hiPrio clang-tools.override {
            llvmPackages = llvmPkgs;
            enableLibcxx = false;
          })
          # Do not use the clangd from this package as it does not work correctly with
          # stdlib headers.
          llvmPkgs.lld
          # llvmPkgs.lldb
          # llvmPkgs.libraries.libcxxStdenv
          # llvmPkgs.libraries.openmp
          # llvmPkgs.libraries.libunwind
          # llvmPkgs.libraries.libcxxabi
          # llvmPkgs.libraries.compiler-rt
          pkgs."clang${clangVersion}Stdenv".cc.cc.lib

          compilerLinks

          # Libs
          fmt
          grpc
          protobuf
        ];
      in {
        devShells = {
          default = pkgs.mkShell.override {stdenv = pkgs."clang${clangVersion}Stdenv";} rec {
            inherit buildInputs;
          };
        };
      }
    );
}

which works and compiles my simple program with cmake but does not execute it because libstdc++.so.6 is not found.

  • How can I add this libraray libstdc++ as a package?
  • Why is clang finding the library and linking to it, but it cannot be found when executing the binary?
  • How is llvmPackage_16.libstdcxxClang different from pkgs.clang16Stdenv?

Thanks for shining light into the arkane package names for C++ toolchains…

I dont think a hack with LD_LIBRARY_PATH is the right way to do this… or is it?

A simple

devShells = {
          default = pkgs.mkShell.override {stdenv = llvmPkgs.libstdcxxClang.stdenv;} rec {
            inherit nativeBuildInputs;
            LD_LIBRARY_PATH = nixpkgs.lib.makeLibraryPath [pkgs.clang16Stdenv.cc.cc.lib];
          };
        };

does not work, because the libstdc++ is not located in pkgs.clang16Stdenv.cc.cc.lib, but then where is it?
It is certainly in the GCC package but how do I know which libstdc++ is used in pkgs.clang16Stdenv ??

libstdc++.so.6 is in stdenv.cc.cc.lib

But how does stdenv.cc.cc.lib relate to the GCC version clang version 16 is using? Isnt this the default stdenv… which might be using some other GCC version then pkgs.clang16Stdenv is using (therefore having another libstdc++)

Clang’s and gcc’s libstdc++'s are different. You have some binary that needs libstdc++.so.6 in runpath to run. You say it’s your binary, but your post does not have enough information how you actually construct the binary.

There is libc++ from LLVM toolchain and there is libstdc++ from gcc. I want do use libstdc++ which is standardt in NixOS. I am building a simple hello world c++ main.cpp -o out which works and links the libstdc++.so.6 but obviously its not in my shell environment, so I need to set LD_LIBRARY_PATH to execute it in the nix-shell. However I dont know how to construct this path.

I came up with the following solution which constructs a clang stdenv, (not sure if everything is correct here)


{
  description = "Cpp Playground";

  nixConfig = {
    substituters = [
      # Add here some other mirror if needed.
      "https://cache.nixos.org/"
    ];
    extra-substituters = [
      # Nix community's cache server
      "https://nix-community.cachix.org"
    ];
    extra-trusted-public-keys = [
      "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
    ];
  };

  inputs = {
    # Nixpkgs (take the systems nixpkgs version)
    nixpkgs.url = "nixpkgs";

    # You can access packages and modules from different nixpkgs revs
    # at the same time. Here's an working example:
    nixpkgsStable.url = "github:nixos/nixpkgs/nixos-23.11";
    # Also see the 'stable-packages' overlay at 'overlays/default.nix'.
  };

  outputs = {
    self,
    nixpkgs,
    nixpkgsStable,
    flake-utils,
    ...
  } @ inputs: let
    # Supported systems for your flake packages, shell, etc.
    systems = [
      "x86_64-linux"
      "aarch64-darwin"
    ];

    # This is a function that generates an attribute by calling a function you
    # pass to it, with the correct `system` and `pkgs` as arguments.
    forAllSystems = func: nixpkgs.lib.genAttrs systems (system: func system nixpkgs.legacyPackages.${system});
  in {
    # Formatter for your nix files, available through 'nix fmt'
    # Other options beside 'alejandra' include 'nixpkgs-fmt'
    formatter = forAllSystems (system: pkgs: pkgs.alejandra);

    devShells =
      forAllSystems
      (
        system: pkgs: let
          # Some Node packages for development.
          nodePackages = pkgs.callPackage ./tools/nix/node-packages/default.nix {};

          # Toolchain.
          gccVersion = "13";
          gccPkg = pkgs."gcc${gccVersion}";
          llvmVersion = "16";
          llvmPkgs = pkgs."llvmPackages_${llvmVersion}";
          clangStdEnv = pkgs.stdenvAdapters.overrideCC llvmPkgs.stdenv (llvmPkgs.clang.override {gccForLibs = gccPkg;});

          compilerLinks = pkgs.runCommand "clang-links" {} ''
            mkdir -p $out/bin
            ln -s ${llvmPkgs.clang}/bin/clang $out/bin/clang-${llvmVersion}
            ln -s ${llvmPkgs.clang}/bin/clang++ $out/bin/clang++-${llvmVersion}
            ln -s ${llvmPkgs.llvm}/bin/llvm-as $out/bin/llvm-as-${llvmVersion}
          '';

          dependencies = with pkgs; [
            # Dependencies
            fmt
            grpc
            protobuf
          ];

          nativeBuildInputs = with pkgs;
            [
              ninja
              cmake

              # Toolchain
              # Clangd from clang-tools must come first.
              (hiPrio clang-tools.override {
                llvmPackages = llvmPkgs;
                enableLibcxx = false;
              })
              # Do not use the clangd from this package as it does not work correctly with
              # stdlib headers.
              llvmPkgs.lld
              llvmPkgs.lldb

              # Compiler Links
              compilerLinks
            ]
            ++ dependencies;
        in {
          default = pkgs.mkShell.override {stdenv = clangStdEnv;} rec {
            inherit nativeBuildInputs;
            LD_LIBRARY_PATH = nixpkgs.lib.makeLibraryPath [gccPkg.cc.lib];
          };
        }
      );
  };
}

A better question here:

Is this what you want to do?

~/d/p/asd 52.1s ❱ cat main.cpp
#include <iostream>
int main() {
    std::cout << "hello world" << std::endl;
    return 0;
}
~/d/p/asd ❱ nix shell nixpkgs#llvmPackages_17.libstdcxxClang
Welcome to fish, the friendly interactive shell
Type help for instructions on how to use fish
~/d/p/asd ❱ clang++ main.cpp
~/d/p/asd ❱ ldd a.out
	linux-vdso.so.1 (0x00007fff9e3dc000)
	libstdc++.so.6 => /nix/store/4zp05qkzh9s3c3k7pp9d36vhiz3x8h9l-gcc-13.2.0-lib/lib/libstdc++.so.6 (0x00007fceeec00000)
	libm.so.6 => /nix/store/p3jshbwxiwifm1py0yq544fmdyy98j8a-glibc-2.38-27/lib/libm.so.6 (0x00007fceeef1f000)
	libgcc_s.so.1 => /nix/store/4zp05qkzh9s3c3k7pp9d36vhiz3x8h9l-gcc-13.2.0-lib/lib/libgcc_s.so.1 (0x00007fceeeefa000)
	libc.so.6 => /nix/store/p3jshbwxiwifm1py0yq544fmdyy98j8a-glibc-2.38-27/lib/libc.so.6 (0x00007fceeea17000)
	/nix/store/p3jshbwxiwifm1py0yq544fmdyy98j8a-glibc-2.38-27/lib/ld-linux-x86-64.so.2 => /nix/store/p3jshbwxiwifm1py0yq544fmdyy98j8a-gli
bc-2.38-27/lib64/ld-linux-x86-64.so.2 (0x00007fceef008000)
~/d/p/asd ❱ ./a.out
hello world

If you instead want clang with GNU’s libc++ in mkDerivation, you use clangStdenv, if you want clang’s libc++ then libcxxStdenv

1 Like

Ah strange, whats the difference between the above and nix shell -p nixpkgs#llvmPackages_17.libstdcxxClang, for the flake somehow, libstdc++ does not get found after compiling, but with your thing it works… Is LD_LIBRARY_PATH set?

The difference is that libstdcxxClang sets the runpath

 0x000000000000001d (RUNPATH)            Library runpath: [/nix/store/7jiqcrg061xi5clniy7z5pvkc4jiaqav-glibc-2.38-27/lib:/nix/store/np3cndfk53miqg2cilv7vfdxckga665h-gcc-13.2.0-lib/lib:/nix/store/kbsb6s8djydiawgswnm8j5bwn1yxaqb7-gcc-13.2.0-libgcc/lib]

If you want to use clang like this in mkShell, you have to use libstdcxxClang.

stdenv’s are only really meant for mkDerivations when you override mkShell’s stdenv it overrides the mkDerivation’s stdenv for the mkShell but it does not have much meaning inside the shell itself.

Proper way to do your flake would be like this:

{
   description = "flake";

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

   outputs = { self, nixpkgs, flake-utils }: let
      outputs = (flake-utils.lib.eachDefaultSystem (system: let
         pkgs = nixpkgs.outputs.legacyPackages.${system};
      in {
         packages.default = pkgs.clangStdenv.mkDerivation {
            name = "hello";
            src = ./.;
            installPhase = "mkdir -p $out/bin; $CXX main.cpp -o $out/bin/hello";
         };
      }));
   in outputs;
}

You don’t need devShells.default here, because nix develop will also look for packages.default and makes shell suitable for building the package.

2 Likes