diff --git a/doc/stdenv/stdenv.chapter.md b/doc/stdenv/stdenv.chapter.md index 93351ce1bdb3c..3a50ac560b331 100644 --- a/doc/stdenv/stdenv.chapter.md +++ b/doc/stdenv/stdenv.chapter.md @@ -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 {}); 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. diff --git a/pkgs/build-support/cc-wrapper/cc-wrapper.sh b/pkgs/build-support/cc-wrapper/cc-wrapper.sh index 78759f2cfbbc4..50e961e5dd5f3 100644 --- a/pkgs/build-support/cc-wrapper/cc-wrapper.sh +++ b/pkgs/build-support/cc-wrapper/cc-wrapper.sh @@ -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[@]}"} \ diff --git a/pkgs/development/tools/mini-compile-commands/default.nix b/pkgs/development/tools/mini-compile-commands/default.nix new file mode 100644 index 0000000000000..690e415165a7f --- /dev/null +++ b/pkgs/development/tools/mini-compile-commands/default.nix @@ -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; + }; + }; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 8e6891b07f4cd..6c24299d9b587 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -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 { };