Setting up a nix-env that can compile C libraries

Hi,

My general workflow is to create a default.nix and shell.nix for each git repo I work in, configured with all of the tools I need to work in that repo. Then, I run exec nix-shell --run zsh to get into a shell environment usable for development. This let’s me use cargo commands (to build Rust projects) exactly as a developer would when not using Nix.

Now I’m trying to build cloudflare/boring, which needs to compile C code at build-time.

I’ve got a default.nix as follows:

{ pkgs ? import <nixos-unstable> { } }:
with pkgs;

buildEnv {
  name = "boring-dev";
  paths = [
    binutils
    boringssl
    cacert
    clang
    cmake
    git
    glibc
    (glibcLocales.override { locales = [ "en_US.UTF-8" ]; })
    libclang
    lldb
    openssl
    pkg-config
    rustup
    stdenv
  ];

  passthru = with pkgs; {
    LC_ALL = "en_US.UTF-8";
    LOCALE_ARCHIVE = "${glibcLocales}/lib/locale/locale-archive";

    CARGO_TERM_COLOR = "always";
    RUST_BACKTRACE = "short";

    CURL_CA_BUNDLE = "${cacert}/etc/ssl/certs/ca-bundle.crt";
    GIT_SSL_CAINFO = "${cacert}/etc/ssl/certs/ca-bundle.crt";
    SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt";

    LIBCLANG_PATH = "${libclang}/lib";

    OPENSSL_DIR = "${openssl.dev}";
    OPENSSL_LIB_DIR = "${openssl.out}/lib";
  };

And a shell.nix:

{ pkgs ? import <nixos-unstable> { } }:
with pkgs;

let env = (import ./default.nix scope);

in mkShell {
  LC_ALL = "en_US.UTF-8";
  LOCALE_ARCHIVE = "${glibcLocales}/lib/locale/locale-archive";

  CARGO_TERM_COLOR = "always";
  RUST_BACKTRACE = "1";

  CURL_CA_BUNDLE = "${cacert}/etc/ssl/certs/ca-bundle.crt";
  GIT_SSL_CAINFO = "${cacert}/etc/ssl/certs/ca-bundle.crt";
  SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt";

  OPENSSL_DIR = "${openssl.dev}";
  OPENSSL_LIB_DIR = "${openssl.out}/lib";

  PROTOC = "${protobuf}/bin/protoc";
  PROTOC_INCLUDE = "${protobuf}/include";

  buildInputs = [ (import ./default.nix { inherit pkgs; }) ];

  BORING_BSSL_PATH = "${boringssl}/lib";
  BORING_BSSL_INCLUDE_PATH = "${boringssl}/include";

  nativeBuildInputs = [ llvmPackages.libclang ];
  LIBCLANG_PATH = "${llvmPackages.libclang.lib}/lib";
}

When I try to run cargo check, I quickly get an error indicating that the compiler is unable to locate stddef.h:

:; cargo check
   Compiling boring-sys v1.1.1 (/home/ver/b/boring/boring-sys)
error: failed to run custom build command for `boring-sys v1.1.1 (/home/ver/b/boring/boring-sys)`

Caused by:
  process didn't exit successfully: `/home/ver/b/boring/target/debug/build/boring-sys-32685ef162b1efe4/build-script-build` (exit status: 101)
  --- stdout
  cargo:rustc-link-search=native=/nix/store/zinsp0lqhfbl819kra3v36ax5m96abdp-boringssl-2021-07-09/lib/build/
  cargo:rustc-link-lib=static=crypto
  cargo:rustc-link-lib=static=ssl

  --- stderr
  /nix/store/zinsp0lqhfbl819kra3v36ax5m96abdp-boringssl-2021-07-09/include/openssl/base.h:59:10: fatal error: 'stddef.h' file not found
  /nix/store/zinsp0lqhfbl819kra3v36ax5m96abdp-boringssl-2021-07-09/include/openssl/base.h:59:10: fatal error: 'stddef.h' file not found, err: true
  thread 'main' panicked at 'Unable to generate bindings: ()', boring-sys/build.rs:274:39

I’ve pieced together that this header should be provided by the compiler:

:; find /nix -name stddef.h                     
/nix/store/5qjycalzb9sqzvqg65kf5zimqwjabm9g-gcc-10.3.0/lib/gcc/x86_64-unknown-linux-gnu/10.3.0/include/stddef.h
/nix/store/kwwnrbkzs6qfb32dmbsgbdj2hacn1csk-clang-7.1.0-lib/lib/clang/7.1.0/include/stddef.h
...

Is there a straightforward way to make this nix-env be configured so that these headers can be discovered? What am I missing? If you encountered this how would you go about debugging it?

Please don’t hesitate to point me at docs if this is explained somewhere – I’ve cobbled together this configuration by copy/pasting and I barely know what I’m doing here :wink: So any resources I can learn from are appreciated.

Thanks!

1 Like

Did you consider trying that option ?

My colleague hawkw (Eliza Weisman) · GitHub was able to come up with a config that fixed this specific issue:

I think the important bit is including something like the following in shell.nix:

  shellHook = ''
    export BINDGEN_EXTRA_CLANG_ARGS="$(< ${stdenv.cc}/nix-support/libc-crt1-cflags) \
          $(< ${stdenv.cc}/nix-support/libc-cflags) \
          $(< ${stdenv.cc}/nix-support/cc-cflags) \
          $(< ${stdenv.cc}/nix-support/libcxx-cxxflags) \
          ${
            lib.optionalString stdenv.cc.isClang
            "-idirafter ${stdenv.cc.cc}/lib/clang/${
              lib.getVersion stdenv.cc.cc
            }/include"
          } \
          ${
            lib.optionalString stdenv.cc.isGNU
            "-isystem ${stdenv.cc.cc}/include/c++/${
              lib.getVersion stdenv.cc.cc
            } -isystem ${stdenv.cc.cc}/include/c++/${
              lib.getVersion stdenv.cc.cc
            }/${stdenv.hostPlatform.config} -idirafter ${stdenv.cc.cc}/lib/gcc/${stdenv.hostPlatform.config}/${
              lib.getVersion stdenv.cc.cc
            }/include"
          } \
        "
        '';

I think this was inspired by Using rust-bindgen in Nix

1 Like