Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions doc/stdenv/stdenv.chapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -1409,3 +1409,35 @@ If the libraries lack `-fPIE`, you will get the error `recompile with -fPIE`.
[^footnote-stdenv-sys-lib-search-path]: It clears the `sys_lib_*search_path` variables in the Libtool script to prevent Libtool from using libraries in `/usr/lib` and such.
[^footnote-stdenv-build-time-guessing-impurity]: Eventually these will be passed building natively as well, to improve determinism: build-time guessing, as is done today, is a risk of impurity.
[^footnote-stdenv-per-platform-wrapper]: Each wrapper targets a single platform, so if binaries for multiple platforms are needed, the underlying binaries must be wrapped multiple times. As this is a property of the wrapper itself, the multiple wrappings are needed whether or not the same underlying binaries can target multiple platforms.

### CC wrapper post-wrapper-hook {#post-wrapper-hook}

The post wrapper hook can be used to inject code into the [compiler wrapper](#cc-wrapper).
If the file `${cc}/nix-support/post-wrapper-hook.sh` exists, it will be sourced by the compiler wrapper just before the unwrapped compiler is invoked.
The post wrapper hook executes in the same environment as the compiler wrapper, so it has access to all the environment variables used by the wrapper, for example `extraBefore`, `params` and `extraAfter` which store all the command line arguments passed to the compiler.

### Generating compile_commands.json files {#compile-commands-json}

A `compile_commands.json` is a record of all the compiler invocations which occoured when building a C or C++ project.
The specification can be found [here](https://clang.llvm.org/docs/JSONCompilationDatabase.html).
A `compile_commands.json` is used by lsp servers like `clangd` to provide IDE features to editors for C and C++.
The most popular tool for generating a `compile_commands.json` is [bear](https://github.com/rizsotto/Bear).
In nixpkgs standard environments, [compilers are wrapped](#cc-wrapper), and bear can have trouble distinguishing between invocations of the compiler wrapper and invocations of the unwrapped compiler.

The mini-compile-commands package is a nixpkgs specific tool for generating a `compile_commands.json`. The package consists of two files: `mini_compile_commands_server.py` and `mini_compile_commands_client.py`.
The basic idea is that we inject the `mini_compile_commands_client.py` into the compiler wrapper using a [post-wrapper-hook](#post-wrapper-hook).
Whenever the wrapper is executed, it sends the unwrapped compile command to a running `mini_compile_commands_server.py`, which collects them and when shut down, writes a `compile_commands.json`.
The client server architecture is required to handle parallel builds.

The `mini-compile-commands.wrap` function takes a nixpkgs standard environment, and returns a new standard environment with the `mini_compile_commands_client.py` insert into the compiler wrapper as a post wrapper hook and `mini_compile_commands_server.py` in scope.

For example, we can use the following environment to build the linux kernel:

```
nix-shell -E "with (import <nixpkgs> {}); linux.override { stdenv = (mini-compile-commands.wrap stdenv); }"
```

Clone the linux kernel source tree, and spawn two shells with the above command.
In the first run `mini_compile_commands_server.py compile_commands.json`.
In the second run `make $makeFlags defconfig -j $(nproc); make $makeFlags -j $(nproc)`.
Once the build has finished, close `mini_compile_commands_server.py` and a `compile_commands.json` will be generated.
6 changes: 6 additions & 0 deletions pkgs/build-support/cc-wrapper/cc-wrapper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ fi
PATH="$path_backup"
# Old bash workaround, see above.

# if a post-wrapper-hook exists, run it.
if [[ -e @out@/nix-support/post-wrapper-hook.sh ]]; then
compiler=@prog@
source @out@/nix-support/post-wrapper-hook.sh
fi

if (( "${NIX_CC_USE_RESPONSE_FILE:-@use_response_file_by_default@}" >= 1 )); then
exec @prog@ @<(printf "%q\n" \
${extraBefore+"${extraBefore[@]}"} \
Expand Down
56 changes: 56 additions & 0 deletions pkgs/development/tools/mini-compile-commands/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{ lib
, stdenv
, fetchFromGitHub
, python3Minimal
}:

rec {
# function for wrapping a standard environment to include the
# compile commands generation functionality
wrap = env:
let
hook = "ln -s ${package}/bin/post-wrapper-hook.sh $out/nix-support/post-wrapper-hook.sh\n";
in
env.override (old: {
cc = old.cc.overrideAttrs (final: previous: {
installPhase = previous.installPhase or "" + hook;
});
extraBuildInputs = old.extraBuildInputs or [] ++ [ package ];
allowedRequisites = null;
});

# mini compile commands package. You probably don't want to use this directly.
# instead, wrap your standard environment: ( mini-compile-commands.wrap stdenv )
package = stdenv.mkDerivation rec {
pname = "mini_compile_commands";
version = "0.1";

src = fetchFromGitHub {
owner = "danielbarter";
repo = "mini_compile_commands";
rev = "v${version}";
sha256 = "0yDocHY4+DsakFXDnrCa/d4ZTGxjFCW68zB8F5GsRuo=";
};

# specifying python environment variable so that it gets substituted
# during install phase
python = python3Minimal;

installPhase = ''
mkdir -p $out/bin
export mini_compile_commands_client=$out/bin/mini_compile_commands_client.py
for file in $(ls $src); do
substituteAll $src/$file $out/bin/$file
chmod +x $out/bin/$file
done
'';

meta = with lib; {
homepage = "https://github.com/danielbarter/mini_compile_commands";
description = "Generate compile_commands.json using nixpkgs infrastructure";
license = licenses.gpl3;
maintainers = with maintainers; [ danielbarter ];
platforms = platforms.linux;
};
};
}
2 changes: 2 additions & 0 deletions pkgs/top-level/all-packages.nix
Original file line number Diff line number Diff line change
Expand Up @@ -8381,6 +8381,8 @@ with pkgs;

minica = callPackage ../tools/security/minica { };

mini-compile-commands = callPackage ../development/tools/mini-compile-commands { };

minidlna = callPackage ../tools/networking/minidlna { };

miniplayer = callPackage ../applications/audio/miniplayer { };
Expand Down