Development on GCC on nixos

I am a beginner to nixos.

I would like to make some patches to gcc. So I need to build it from source. As expected recommended build from the gcc wiki does not work on nixos.

The directory that should contain system headers does not exist:
  /usr/include

Is the latest error I encountered.

What is the recommended way to develop gcc on nixos?

1 Like

This is how gcc11 is built in nixpkgs:

You can reproduce the build environment with nix-shell -p gcc11. Now simply change src to your local sources.

If you expect a lot of recompilations during your changes, you might prefer to apply the patches from nixpkgs to your source, launch the appropriate nix shell and then build as you would do on other systems.

Another option would be using e.g. Debian in a Container for the hack&test cycles and once you seem satisfied you can easily rebuild gcc and your test code with nix.

1 Like

It would probably be best to do nix-shell '<nixpkgs>' -A gcc11.cc which should give you the build environment to create gcc.

Some issues may occur with patching though. :frowning:

2 Likes

I agree building vanilla gcc is not trivial on NixOS. And doing upstream development using nixpkgs derivations is even harder. I suggest a simple hack: use buildFHSEnv to get development environment. Something like the following:

# shell.nix:
{ pkgs ? import <nixpkgs> {} }:

let e =
  pkgs.buildFHSEnv {
    name = "gcc-git-build-env";
    targetPkgs = ps: with ps; [
      # library depends
      gmp gmp.dev
      isl
      libffi libffi.dev
      libmpc
      libxcrypt
      mpfr mpfr.dev
      xz xz.dev
      zlib zlib.dev

      # git checkout need flex as they are not complete release tarballs
      m4
      bison
      flex
      texinfo

      # test harness
      dejagnu
      autogen

      # valgrind annotations
      valgrind valgrind.dev

      # toolchain itself
      gcc
      stdenv.cc
      stdenv.cc.libc stdenv.cc.libc_dev
    ];
  };
in e.env

The full session would look like that:

$ nix-shell

# fetch
$$ git clone --depth 1 https://gcc.gnu.org/git/gcc.git

# build / install
$$ mkdir gcc-build
$$ cd gcc-build
$$ ../gcc/configure --disable-multilib --prefix=$PWD/../gcc-installed
$$ make -j $(nproc)
$$ make install

# test basics
$$ printf '#include <iostream>\nint main() { std::cout << "Hello!" << std::endl ; }' > a.cc
$$ ../gcc-installed/bin/g++ a.cc -o a -static-libstdc++
$$ ./a
Hello!

# run testsuite
$$ make check
...
                === gcc Summary ===
# of expected passes            191743
# of unexpected failures        107

I hope it will help you get started.

4 Likes

Hello! I tried to reproduce your method with flakes, and have the following flake:

{
  description = "A very basic flake";
  
  outputs = { nixpkgs, self }: {
    devShells.riscv64-linux.default = (nixpkgs.legacyPackages.riscv64-linux.buildFHSEnv {
      name = "gcc-git-build-env";

      targetPkgs = ps: with ps; [
        # library depends
        gmp gmp.dev
        isl
        libffi libffi.dev
        libmpc
        libxcrypt
        mpfr mpfr.dev
        xz xz.dev
        zlib zlib.dev

        # git checkout need flex as they are not complete release tarballs
        m4
        bison
        flex
        texinfo
        
        # test harness
        dejagnu
        autogen
        
        # toolchain itself
        gcc
        stdenv.cc
        stdenv.cc.libc stdenv.cc.libc_dev
      ];
    }).env;
  };
}

However, when I try to build GCC in nix develop with this flake, I get an error from ld that it can’t find -lmpfr, -lmpc and so on. I can compile program with them, though, configure passes succesfully. In shell files are also present on their intended places. How can I debug this?

That’s on riscv64-linux host, right? Your flake looks reasonable it works for me if I replace riscv64-linux with x86_64-linux. I suspect it has to do with the lib/lib64 symlinks and linker search paths. Should not be too hard to fix.

Try adding -Wl,--verbose to the build command you see failing and check why -lmpc is not searched in usual /lib/libmpc.so locations.

I’ll try to reproduce it as is on riscv64-linux using qemu-user and look closer. But it will take quite a bit to build the environment without the binary cache.

DId not succeed reproducing the failure on riscv64-linux (grep test fails in qemu-user), but I did on i686-linux. I think x86_64-linux works by accident because multilib layout always injects -L /usr/lib path while i686 just relies on linker defaults. For some reason NIX_LDFLAGS does not get read by the ld wrapper.

I’ll try to figure out why and come up with a workaround.

Proposed the fix in buildFHSEnv to make it Just Work: buildFHSEnv: fix `NIX_LDFLAGS` propagation to `ld` wrapper by trofi · Pull Request #250500 · NixOS/nixpkgs · GitHub

Meanwhile you can apply a local workaround to get NIX_LDFLAGS to work:

--- a/flake.nix
+++ b/flake.nix
@@ -32,5 +32,11 @@
         stdenv.cc.libc stdenv.cc.libc_dev
       ];
+
+    profile = ''
+      # A workaround needed until the fix is merged to `nixpkgs` proper:
+      #   https://github.com/NixOS/nixpkgs/pull/250500
+      export NIX_BINTOOLS_WRAPPER_TARGET_HOST_${nixpkgs.legacyPackages.riscv64-linux.stdenv.cc.suffixSalt}=1
+    '';
     }).env;
   };
 }

It helped my on i686-linux. I hope it’s enough for you on riscv64-linux.

1 Like

I applied this patch to my flake, but it didn’t seem to help. :frowning: What is strange, I can’t see this variable in env also:

$ env | grep NIX
NIX_CC_WRAPPER_TARGET_HOST_riscv64_unknown_linux_gnu=1
NIX_BUILD_CORES=4
NIX_GCROOT=/nix/store/awqrssnina49p3hg2zc755vgmddc6s71-gcc-git-build-env-shell-env-env
NIX_PROFILES=/run/current-system/sw /nix/var/nix/profiles/default /etc/profiles/per-user/whaleahead /home/whaleahead/.nix-profile
NIX_ENFORCE_NO_NATIVE=1
NIX_PATH=nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels
NIXPKGS_CONFIG=/etc/nix/nixpkgs-config.nix
IN_NIX_SHELL=impure
NIX_STORE=/nix/store
NIX_USER_PROFILE_DIR=/nix/var/nix/profiles/per-user/whaleahead
NIX_CFLAGS_LINK=-L/usr/lib -L/usr/lib32
NIX_BUILD_TOP=/tmp/nix-shell.uRN1fN/nix-shell.uWYY4d/nix-shell.FEo3r1
NIX_CFLAGS_COMPILE=-idirafter /usr/include
__NIXOS_SET_ENVIRONMENT_DONE=1
NIX_LDFLAGS=-L/usr/lib -L/usr/lib32

Maybe I am missing something?

UPD: it’s just an incorrectly applied patch, after more careful examination and fix of patched flake variable did show up. Running the build to check if problem is fixed.

UPD2: it looks like problem is fixed, compilations using mpc pass successfully. Hope this would be the only problem.

1 Like

Ah, all my fault. profile should have been added within ({ ... }).env block, not the outside. Too much hand-editing on my side. Updated the patch above.

Yeah, with fixed patch I was able to build GCC successfully. Many thanks for your help!

1 Like

Just for the record, with a few modifications is it possible to build basically any GCC from its vanilla sources on NixOS. We recently ran into this issue with our bootstrappable builds toolchain project, which supports bootstrapping toolchains from only a minimal system, like NixOS.

Assuming you have the following paths (or similar):

MY_HDRDIR="/nix/store/y8wfrgk7br5rfz4221lfb9v8w3n0cnyd-glibc-2.37-8-dev/include"
MY_LIBDIR="/nix/store/ld03l52xq2ssn4x0g5asypsxqls40497-glibc-2.37-8/lib"
MY_LINKER="/nix/store/ld03l52xq2ssn4x0g5asypsxqls40497-glibc-2.37-8/lib/ld-linux-x86-64.so.2"

We were able to successfully build GCC 4.7.4, GCC 10.2.0, and GCC 13.2.0 with this:

# patch the dynamic linker path
for hdr in gcc/config/*/*.h; do
  sed -i 's|"/lib.*/ld-linux.*so.2"|"'$MY_LINKER'"|g' "$hdr"
done

export LIBRARY_PATH="$MY_LIBDIR"
./configure --with-sysroot=/ --with-naive-system-header-dir="$MY_HDRDIR" # ... other configure args
make && make install

Maybe someone finds this information helpful.

2 Likes