`env.NIX_CFLAGS_COMPILE` vs `CXXFLAGS`

Preface: I don’t know C/C++ (one of the several reasons I hope nix eventually sees some rust in it – as then perhaps I could contribute!).

In searching nixpkgs for example uses of std=c++17, I see 3 matches for CXXFLAGS = .*"-std=c\+\+17";' and two for env\.NIX_CFLAGS_COMPILE = .*"-std=c\+\+17";

I’ve seen the env attrset a few times but don’t really understand it use. I thought most arbitrary keys passed into mkDerivation ended up as environment variables in the build context.

Document attribute ordering in package expressions - #3 by jtojnar mentions something about __structuredAttrs being related to the existence of env.

  • Why does the env attrset exist?
  • When would one use something like env.NIX_CFLAGS_COMPILE as opposed to CXXFLAGS?

If you’re aware of a good blog post or somewhere I’ve missed in the documentation, a link will do! Many thanks.

NIX_C(XX)FLAGS are guaranteed to be passed to compiler because nix’s stdenv’s wrap the compiler binaries and mangles the flags. CFLAGS and CXXFLAGS are convention used by most build systems and C/C++ projects, but there are times when they do not work (usually when project uses bad build system).

Preferred way to pass these flags:

  1. Use the projects build system way
  2. C(XX)FLAGS
  3. NIX_C(XX)FLAGS as last resort
1 Like

Thanks for your time! I still have some questions if you have time (and patience).

  • What is the significance of the env attrset?
  • Is it only required with NIX_*?
    • If so, why?

It is required by the Nixpkgs generic builder with __structuredAttrs. With that enabled, the generic builder will no longer export the attributes passed to stdenv.mkDerivation as environment variables – it will just set them as regular shell variables.

If you actually want environment variables, you will need to move the attributes under the env attribute. __structuredAttrs are not currently enabled by default in Nixpkgs so env attribute is not yet necessary. But we still recommend people to use it to make the eventual global enabling of __structuredAttrs easier.


If you like examples, you can build the following Nix expression:

let
  pkgs = import <nixpkgs> {};
in
pkgs.runCommand
  "foo"
  {
    __structuredAttrs = true;
    MY_VARIABLE = "foo";
    env = {
      MY_ENVIRONMENT_VARIABLE = "foo";
    };
  }
  ''
    echo directly in the builder MY_VARIABLE=$MY_VARIABLE
    sh -c 'echo in a subprocess MY_VARIABLE=$MY_VARIABLE'
    echo directly in the builder MY_ENVIRONMENT_VARIABLE=$MY_ENVIRONMENT_VARIABLE
    sh -c 'echo in a subprocess MY_ENVIRONMENT_VARIABLE=$MY_ENVIRONMENT_VARIABLE'
  ''

It will log the following output during build:

structuredAttrs is enabled
directly in the builder MY_VARIABLE=foo
in a subprocess MY_VARIABLE=
directly in the builder MY_ENVIRONMENT_VARIABLE=foo
in a subprocess MY_ENVIRONMENT_VARIABLE=foo
3 Likes

Fantastic. Thank you both!

Unfortunately, cc-wrapper does not have a C++ equivalent to NIX_CFLAGS_COMPILE and NIX_CFLAGS_LINK. Setting NIX_CXXFLAGS_COMPILE has no effect.

That caused some pain during the LLVM 16 upgrade with packages that weren’t compatible with C++17 and used both C and C++ (because compiling C code with, e.g., -std=c++14 would fail). I had to figure out other ways to effect same result. :confused:

1 Like

Is there any chance you could elaborate on the workarounds?

I’m a junior member of darwin-maintainers, a self-taught hobbyist that knows a little bit of several relatively modern languages (well that and bash / python), but as I had no need (and rust already existed) I ended up skpping C-family languages completely. I’m roughly 50/50 darwin / linux, and I’d really like to help out more. I’ve loosely followed your work on the darwin stdenv revamp (with great admiration!), but most of it is far above my head.

I feel like I’m learning over time, but then I run into issues and end up endlessly spinning my wheels.

What workarounds did you need to use in order to pass -std=c++14 to codebases using both C and C++ with LLVM 16? Isn’t cc-wrapper being used under the covers – or are there situations that I need to specifically call / interact with it?

EDIT: Also, as we speak I’m trying to figure out how to compile GitHub - casadi/casadi: CasADi is a symbolic framework for numeric optimization implementing automatic differentiation in forward and reverse modes on sparse matrix-valued computational graphs. It supports self-contained C-code generation and interfaces state-of-the-art codes such as SUNDIALS, IPOPT etc. It can be used from C++, Python or Matlab/Octave., which seems to need "-std=c++17" or else I get error: no type named 'set_unexpected' in namespace 'std', but CXXFLAGS seemed to make no difference, and C barfed with env.NIX_CFLAGS_COMPILE = "-std=c++17";.

It depends on the package. If they respect CXXFLAGS, you can just set that. That’s a common convention, so it usually works. However, I’m pretty sure there was at least one where it wasn’t respected. In that case, you have to dig into the build system and see where you can patch it to add the required flags or if it has some other way of specifying it.

It does use cc-wrapper, but cc-wrapper doesn’t provide a way to specify C+±only flags. That’s the problem (which you’re hitting with CasADi).

Compilation failure with C++17 and clang 16 on macOS · Issue #3458 · casadi/casadi · GitHub ?

Can you update SWIG? You might need to cherry-pick Support swig-4.2.0 · casadi/casadi@8055ed3 · GitHub.

1 Like

Thanks for your time. Yes, I saw that thread (which is how I got the hint about trying the c++17 flag.

Will tinker around some more!