C: Difference between revisions
imported>Mic92 minor readability improvement |
imported from old wiki |
||
(30 intermediate revisions by 13 users not shown) | |||
Line 1: | Line 1: | ||
This | This is a collection of recipes for working on C/C++ projects with Nix. | ||
They do not just apply to C but also C++. | |||
== Differences between nixpkgs and the rest == | == Differences between nixpkgs and the rest == | ||
The way nixpkgs and its stdenv handles compiling and linking is very different from other | The way nixpkgs and its stdenv handles compiling and linking is very different from other Linux distributions. | ||
In more conventional Linux distributions it's usual that header files are put into well known paths i.e. <code>/usr/include</code>, where the compiler will look for them. Same is true when linking against libraries, which are put in a few places, where the build-time linker will find them. Dynamically linked libraries will have a run-time linker (also known as <code>ld.so</code>) set as an interpreter. | |||
look for them. Same is true when linking against libraries, which are put in a few places, where the build-time | |||
linker will find them. Dynamically linked libraries will have a run-time linker (also known as <code>ld.so</code>) set as an interpreter. | |||
This linker reads <code>/etc/ld.so.conf</code> to figure out where to find libraries. | This linker reads <code>/etc/ld.so.conf</code> to figure out where to find libraries. | ||
In nixpkgs in contrast this information is provided by environment variables. | In nixpkgs in contrast this information is provided by environment variables. | ||
Line 40: | Line 38: | ||
<syntaxHighlight lang=console> | <syntaxHighlight lang=console> | ||
$ cat > shell.nix <<EOF | $ cat > shell.nix <<EOF ;nix-shell | ||
with import <nixpkgs> {}; | with import <nixpkgs> {}; | ||
stdenv.mkDerivation { | stdenv.mkDerivation { | ||
name = "myenv"; | name = "myenv"; | ||
buildInputs = [ zlib ]; | buildInputs = [ zlib ]; | ||
} | } | ||
EOF | EOF | ||
[nix-shell:~] $ echo $NIX_CFLAGS_COMPILE | [nix-shell:~] $ echo $NIX_CFLAGS_COMPILE | ||
Line 55: | Line 53: | ||
In <code>$NIX_CFLAGS_COMPILE</code> we see that the include search path is extended by appending new directories | In <code>$NIX_CFLAGS_COMPILE</code> we see that the include search path is extended by appending new directories | ||
using the <code>-isystem</code> flag. | using the <code>-isystem</code> flag. | ||
However, while the <code>$out/include</code> folder will be included by default, it may sometimes not be enough when the lib puts the header in a subfolder (for instance, gtk2 and gtk3 uses subdirectories like <code>$out/include/gtk-2.0</code> instead to avoid conflict between versions). To deal with this kind of libraries, one can use `pkg-config`: the idea is simply to add `pkg-config` in the <code>nativeBuildInputs</code>, and then to start the <code>buildPhase</code> with: | |||
<syntaxHighlight lang=nix> | |||
buildPhase = '' | |||
NIX_CFLAGS_COMPILE="$(pkg-config --cflags gtk+-3.0) $NIX_CFLAGS_COMPILE" | |||
# put the usual make/gcc code here | |||
''; | |||
</syntaxHighlight> | |||
For <code>$NIX_LDFLAGS</code> see that the library link path is extended | For <code>$NIX_LDFLAGS</code> see that the library link path is extended | ||
Line 97: | Line 103: | ||
== Hardening flags == | == Hardening flags == | ||
To improve the security of applications the wrapper also injects additional hardening compile flags into the application. | To improve the security of applications the wrapper also injects additional hardening compile flags into the application. These nix flags enable different compiler flags, as seen in the [https://nixos.org/nixpkgs/manual/#sec-hardening-in-nixpkgs manual]. | ||
Under some circumstances this can make programs fail to build or function. | Under some circumstances this can make programs fail to build or function. For example, the `fortify` flag enables the `-O2` optimization level -- if you want to change this, you need to disable the `fortify` flag and re-add the compiler flags manually (`env.NIX_CFLAGS_COMPILE = [ "-O" "....."]`). | ||
To disable all hardening options one can export the environment variable <code>hardeningDisable="all"</code>. | To disable all hardening options one can export the environment variable <code>hardeningDisable="all"</code>. | ||
This also works for derivations like that: | This also works for derivations like that: | ||
Line 109: | Line 115: | ||
</syntaxHighlight> | </syntaxHighlight> | ||
It is also possible to only disable certain parts, for example <code>-Werror=format-security</code>: | |||
<syntaxHighlight lang=nix> | <syntaxHighlight lang=nix> | ||
Line 136: | Line 142: | ||
stdenv.mkDerivation { | stdenv.mkDerivation { | ||
name = "env"; | name = "env"; | ||
nativeBuildInputs = [ | nativeBuildInputs = [ pkg-config ]; | ||
buildInputs = [ | buildInputs = [ | ||
cryptsetup | cryptsetup | ||
Line 150: | Line 156: | ||
[nix-shell:~] $ pkg-config --cflags libcryptsetup | [nix-shell:~] $ pkg-config --cflags libcryptsetup | ||
-I/nix/store/ypg1r7c8m0rkim7by4ikn68xc88bi53j-cryptsetup-2.0.6-dev/include | -I/nix/store/ypg1r7c8m0rkim7by4ikn68xc88bi53j-cryptsetup-2.0.6-dev/include | ||
</syntaxHighlight> | |||
When using <code>autoconf</code>, pkg-config is a required build input for providing the <code>AC_CHECK_HEADERS</code> m4 macro. | |||
=== pkg-config package names === | |||
To list all pkg-config package names of a Nix package: | |||
<syntaxHighlight lang=console> | |||
$ nix-shell -p pkgconfig libglvnd | |||
$ pkg-config --list-all | |||
egl egl - EGL library and headers | |||
libglvnd libglvnd - Vendor-neutral OpenGL dispatch library vendor interface | |||
glx glx - GLX library and headers | |||
glesv1_cm glesv1_cm - OpenGL ES-CM v1 library and headers | |||
opengl opengl - OpenGL (without GLX) headers and interface | |||
glesv2 gles2 - OpenGL ES v2/v3 library and headers | |||
gl gl - Legacy OpenGL and GLX library and headers | |||
</syntaxHighlight> | </syntaxHighlight> | ||
Line 167: | Line 191: | ||
<syntaxHighlight lang=console> | <syntaxHighlight lang=console> | ||
$ nix-shell | $ nix-shell | ||
[nix-shell:~] $ echo $CMAKE_PREFIX_PATH | [nix-shell:~] $ echo $CMAKE_PREFIX_PATH | ||
/nix/store/lw4xr0x2p6xyfgbk961lxh8vnnx7vn2r-cmake-3.12.1:/nix/store/j4x44bjjgwy7hm7lazj8xnr9mnlfiksh-patchelf-0.9:/nix/store/isg8rxaxkijl9x3hr2gzsf8pqfnqxg3k-gcc-wrapper-7.4.0:/nix/store/lwdkm354f3zzsvkf7pqmnc8w6r164b42-binutils-wrapper-2.30:/nix/store/biz7v9g4g6yrnp2h8wfn01d6pk3bj2m1-zeromq-4.3.0:/nix/store/lw4xr0x2p6xyfgbk961lxh8vnnx7vn2r-cmake-3.12.1:/nix/store/j4x44bjjgwy7hm7lazj8xnr9mnlfiksh-patchelf-0.9:/nix/store/isg8rxaxkijl9x3hr2gzsf8pqfnqxg3k-gcc-wrapper-7.4.0:/nix/store/lwdkm354f3zzsvkf7pqmnc8w6r164b42-binutils-wrapper-2.30:/nix/store/biz7v9g4g6yrnp2h8wfn01d6pk3bj2m1-zeromq-4.3.0 | /nix/store/lw4xr0x2p6xyfgbk961lxh8vnnx7vn2r-cmake-3.12.1:/nix/store/j4x44bjjgwy7hm7lazj8xnr9mnlfiksh-patchelf-0.9:/nix/store/isg8rxaxkijl9x3hr2gzsf8pqfnqxg3k-gcc-wrapper-7.4.0:/nix/store/lwdkm354f3zzsvkf7pqmnc8w6r164b42-binutils-wrapper-2.30:/nix/store/biz7v9g4g6yrnp2h8wfn01d6pk3bj2m1-zeromq-4.3.0:/nix/store/lw4xr0x2p6xyfgbk961lxh8vnnx7vn2r-cmake-3.12.1:/nix/store/j4x44bjjgwy7hm7lazj8xnr9mnlfiksh-patchelf-0.9:/nix/store/isg8rxaxkijl9x3hr2gzsf8pqfnqxg3k-gcc-wrapper-7.4.0:/nix/store/lwdkm354f3zzsvkf7pqmnc8w6r164b42-binutils-wrapper-2.30:/nix/store/biz7v9g4g6yrnp2h8wfn01d6pk3bj2m1-zeromq-4.3.0 | ||
[nix-shell:~] $ cat >CMakeLists.txt <<EOF | [nix-shell:~] $ cat >CMakeLists.txt <<EOF | ||
Line 177: | Line 201: | ||
[nix-shell:~] $ echo 'int main {}' > hello.c | [nix-shell:~] $ echo 'int main {}' > hello.c | ||
[nix-shell:~] $ cmake . | [nix-shell:~] $ cmake . | ||
</syntaxHighlight> | |||
== gcc multilib == | |||
<code>pkgs.gcc_multi</code> exports a <code>gcc</code> in a multilib variant, which can produce 32-bit and 64-bit x86 code at the same time. However, <code>gcc_multi</code> falls back to the gcc version coming from <code>pkgs.gcc</code>. To use a specific version of gcc, you might use something like that: | |||
<syntaxHighlight lang=nix> | |||
{ | |||
gcc11_multi = pkgs.wrapCCMulti pkgs.gcc11; | |||
// or | |||
gcc13_multi = pkgs.wrapCCMulti pkgs.gcc13; | |||
} | |||
</syntaxHighlight> | </syntaxHighlight> | ||
Line 225: | Line 261: | ||
This is described in the [https://nixos.org/nixpkgs/manual/#ssec-fixup-phase manual]. | This is described in the [https://nixos.org/nixpkgs/manual/#ssec-fixup-phase manual]. | ||
Also see [[Debug Symbols]] for further information about debug symbols. | Also see [[Debug Symbols]] for further information about debug symbols. | ||
== Editor/IDE integration == | |||
Tooling that provides autocompletion or refactoring support also needs to be aware of the environments variables | |||
to find C/C++ header files. Nixpkgs adds wrapper to all language server [https://clangd.llvm.org/ clangd] (recommend), [https://github.com/MaskRay/ccls ccls] and [https://github.com/cquery-project/cquery cquery] to extend the include path of these tools. [https://github.com/MaskRay/ccls/wiki CCLS] also provides extensive documentation on how to setup a project/editors to make use of it. | |||
== Use a different compiler version == | |||
Adding a different c compiler to <code>buildInputs</code> in a nix expression will not change the default compiler | |||
available in <code>$PATH</code>. Instead, nixpkgs provides a several alternative <code>stdenv</code> which you can search with <code>nix search stdenv</code> so for example: | |||
<syntaxHighlight lang=nix> | |||
gcc8Stdenv.mkDerivation { | |||
name = "env"; | |||
} | |||
</syntaxHighlight> | |||
<syntaxHighlight lang=console> | |||
$ nix-shell --command 'gcc --version' | |||
gcc (GCC) 8.3.0 | |||
</syntaxHighlight> | |||
* Available gcc based stdenv variants: gcc{49,6-12}Stdenv, gccMultiStdenv (32bit/64bit mixed support) | |||
* Available clang based stdenv variants: llvmPackages_[5-13].{stdenv,libcxxStdenv}, clangMultiStdenv (32bit/64bit mixed support) | |||
Those stdenv instances can be also constructed using the <code>overrideCC</code> function: | |||
Here we are creating a shell environment that will always have the latest available gcc: | |||
<syntaxHighlight lang=nix> | |||
(overrideCC stdenv gcc_latest).mkDerivation { | |||
name = "env"; | |||
} | |||
</syntaxHighlight> | |||
Note that this will only affect compiler and not the used linker. To overwrite the linker and maybe also the used libc | |||
Check out the <code>wrapCCWith</code> example in the next section. | |||
See also: [[Using Clang instead of GCC]] | |||
== Get a compiler without default libc == | |||
By default cc wrapper will include the libc headers (i.e. glibc). This can break for example projects that would bring their own libc (i.e. musl). However it is possible to get a cc wrapper that would include all build inputs without adding glibc: | |||
<syntaxHighlight lang=nix> | |||
let | |||
gcc_nolibc = wrapCCWith { | |||
cc = gcc.cc; | |||
bintools = wrapBintoolsWith { | |||
bintools = binutils-unwrapped; | |||
libc = null; | |||
}; | |||
}; | |||
in (overrideCC stdenv gcc_nolibc).mkDerivation { | |||
name = "env"; | |||
} | |||
</syntaxHighlight> | |||
== Override binutils == | |||
This example shows how to apply changes to the binutils package and than use the override binutils | |||
package to compose a new stdenv. | |||
<syntaxHighlight lang=nix> | |||
with import <nixpkgs> {}; | |||
let | |||
binutils-unwrapped' = binutils-unwrapped.overrideAttrs (old: { | |||
name = "binutils-2.37"; | |||
src = pkgs.fetchurl { | |||
url = "https://ftp.gnu.org/gnu/binutils/binutils-2.37.tar.xz"; | |||
sha256 = "sha256-gg2XJPAgo+acszeJOgtjwtsWHa3LDgb8Edwp6x6Eoyw="; | |||
}; | |||
patches = []; | |||
}); | |||
cc = wrapCCWith rec { | |||
cc = gcc-unwrapped; | |||
bintools = wrapBintoolsWith { | |||
bintools = binutils-unwrapped'; | |||
libc = glibc; | |||
}; | |||
}; | |||
in | |||
(overrideCC stdenv cc).mkDerivation { | |||
name = "env"; | |||
} | |||
</syntaxHighlight> | |||
== Faster GCC compiler == | |||
The default gcc compiler in nixpkgs disables profile-guided optimization in order to achieve deterministic builds. There is a faster version available in nixpkgs via the <code>fastStdenv</code> | |||
attribute (7-12% faster). | |||
<syntaxHighlight lang=nix> | |||
fastStdenv.mkDerivation { | |||
name = "env"; | |||
} | |||
</syntaxHighlight> | |||
== Use a clang compiled from source == | |||
Unwrapped compilers usually do not have any access to libraries/headers in nix. This is an issue if you work on the clang/llvm code base. Assuming you have built llvm/clang like this | |||
<syntaxHighlight lang=console> | |||
$ git clone https://github.com/llvm/llvm-project | |||
$ cd llvm-project | |||
$ nix-shell -p cmake --command 'mkdir build && cd build && cmake -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DCMAKE_INSTALL_PREFIX=../install -DCMAKE_BUILD_TYPE=Debug ../llvm && make -j$(nproc)' | |||
</syntaxHighlight> | |||
You can create a wrapper around your local build binaries like this: | |||
<syntaxHighlight lang=nix> | |||
# This file assumes that your llvm binaries are stored in ./build/bin | |||
# impure-clang.nix | |||
{ stdenv, wrapCC, runtimeShell }: | |||
wrapCC (stdenv.mkDerivation { | |||
name = "impure-clang"; | |||
dontUnpack = true; | |||
installPhase = '' | |||
mkdir -p $out/bin | |||
for bin in ${toString (builtins.attrNames (builtins.readDir ./build/bin))}; do | |||
cat > $out/bin/$bin <<EOF | |||
#!${runtimeShell} | |||
exec "${toString ./.}/build/bin/$bin" "\$@" | |||
EOF | |||
chmod +x $out/bin/$bin | |||
done | |||
''; | |||
passthru.isClang = true; | |||
}) | |||
</syntaxHighlight> | |||
Then you can create a <code>shell.nix</code> like this: | |||
<syntaxHighlight lang=nix> | |||
with import <nixpkgs> {}; | |||
pkgs.mkShell { | |||
nativeBuildInputs = [ | |||
cmake | |||
(callPackage ./impure-clang.nix {}) | |||
]; | |||
} | |||
</syntaxHighlight> | |||
And use your self-compiled clang in a nix-shell: | |||
<syntaxHighlight lang=console> | |||
$ nix-shell --command 'clang --version' | |||
</syntaxHighlight> | |||
== Further information == | |||
[https://www.youtube.com/watch?v=zEsT4fw1pL0 Nix Friday about C/CPP infrastructure in Nix] | |||
[[Category:Languages]] | |||
[[Category:Cookbook]] |