Nixnative
Minimal, incremental clang compilation library for nix
Install / Use
/learn @tom-lubenow/NixnativeREADME
nixnative
Incremental C/C++ builds using Nix dynamic derivations and nix-ninja.
Overview
nixnative provides a composable API for building C/C++ projects with dynamic-derivation-driven incremental builds. It uses nix-ninja as the build driver, which generates one derivation per source file at build time using RFC 92 dynamic derivations.
This project requires Nix with dynamic derivations support. All builds use nix-ninja for incremental compilation—there is no fallback to traditional builds.
Requirements
Nix with dynamic derivations support is mandatory. The recommended version is pinned in flake.nix:
# Nix with full dynamic derivations support (commit d904921)
inputs.nix.url = "github:NixOS/nix/d904921eecbc17662fef67e8162bd3c7d1a54ce0";
Enable the required experimental features in your Nix configuration (~/.config/nix/nix.conf):
experimental-features = nix-command dynamic-derivations ca-derivations recursive-nix
Language Scope
nixnative currently compiles C and C++ sources.
Other languages can still participate through normal library composition (for example prebuilt/static/shared libraries produced by external toolchains), but the native compilation pipeline in nixnative is intentionally C/C++ only for now.
Why Dynamic Derivations?
Traditional approaches to incremental C++ builds in Nix face a fundamental tradeoff:
- IFD-based scanning: Evaluation blocks while scanning dependencies, breaking
nix flake checkand slowing CI. - Checked-in manifests: Requires manual synchronization, adding maintenance burden.
Dynamic derivations solve this by deferring derivation creation to build time while keeping evaluation instant.
How It Works
nixnative generates a ninja build file at Nix evaluation time, then uses nix-ninja to execute it with per-file derivations:
EVALUATION TIME (instant):
proj = native.project { root = ./.; ... }
app = proj.executable { name = "app"; sources = [...]; }
→ Generate build.ninja content (pure Nix)
→ Create wrapper derivation that invokes nix-ninja
→ builtins.outputOf → placeholder for final output
BUILD TIME (nix-ninja):
1. nix-ninja parses build.ninja
2. Scans headers per-source (deps = gcc)
3. Creates one derivation per source file
4. Compiles each source → .o files
5. Links final executable/library
Key insight: nix-ninja handles all the complexity of creating per-file derivations and tracking header dependencies. nixnative just generates the ninja build file.
This architecture gives you:
- Instant evaluation: No IFD blocking during
nix evalornix flake check - Incrementality gate: validate one-file-change behavior with
nix run .#incrementality-gate - Parallel compilation: Each source compiles in its own derivation
- Full toolchain control: Compilers, linkers, and flags are explicit inputs
- Content-addressed caching: Identical compilations are deduplicated across projects
Quick Start
# flake.nix
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
inputs.nixnative.url = "github:your/nixnative";
outputs = { nixpkgs, nixnative, ... }:
let
pkgs = import nixpkgs { system = "x86_64-linux"; };
system = "x86_64-linux";
nixPackage = nixnative.inputs.nix.packages.${system}.default;
ninjaPackages = nixnative.inputs.nix-ninja.packages.${system};
native = nixnative.lib.native {
inherit pkgs nixPackage;
inherit (ninjaPackages) nix-ninja nix-ninja-task;
};
# Create a project with shared defaults
proj = native.project {
root = ./.;
compileFlags = [ "-Wall" "-Wextra" ];
};
# Build targets - real values, not string references
hello = proj.executable {
name = "hello";
sources = [ "src/main.cc" ];
};
in {
packages.x86_64-linux.default = hello;
};
}
Build and run:
# Build and get the output path in one command
nix build --print-out-paths
# Output: /nix/store/xxx-hello
# Run directly from the store path
$(nix build --print-out-paths)/hello
Note: Dynamic derivations don't create the traditional
./resultsymlink. Use--print-out-pathsto get the store path, ornix path-info .#targetafter building.
Features
- Modular toolchains: Compilers and linkers are independent, composable pieces. Use clang with mold, gcc with lld, or define your own.
- Explicit flags: Use raw
compileFlags,languageFlags, andlinkFlagsfor precise and predictable control. - Tool plugins: Code generators (templates, etc.) integrate cleanly—generated sources and headers flow through automatically.
- Structured libraries: Static, shared, and header-only libraries propagate their public interface to dependents.
- IDE integration: Every target exports
compile_commands.jsonfor clangd/LSP.
Recommended Pattern: System Link Dependencies
When multiple targets share system linker flags (for example -lpthread/-ldl/-lm), define local "system library" objects and compose them through libraries instead of repeating linkFlags everywhere:
let
mkSystemLibrary = { name, compileFlags ? [ ], linkFlags ? [ ] }: {
inherit name;
public = {
includeDirs = [ ];
defines = [ ];
inherit compileFlags linkFlags;
};
};
sys = {
threads = mkSystemLibrary { name = "threads"; linkFlags = [ "-lpthread" ]; };
dl = mkSystemLibrary { name = "dl"; linkFlags = [ "-ldl" ]; };
m = mkSystemLibrary { name = "m"; linkFlags = [ "-lm" ]; };
};
commonSystemLibraries = [ sys.threads sys.dl sys.m ];
in
proj.executable {
name = "app";
sources = [ "src/main.c" ];
libraries = [ myLib ] ++ commonSystemLibraries;
}
This keeps link policy explicit, reusable, and local to your project without requiring a dedicated framework abstraction.
Examples
See the examples/ directory for working examples:
examples/executable– Minimal executableexamples/library– Static library with public headersexamples/header-only– Header-only libraryexamples/library-chain– Transitive library dependenciesexamples/app-with-library– Executable depending on a static libraryexamples/multi-toolchain– Different compiler/linker combinations (clang/gcc + lld/mold)examples/testing– Unit tests with project-defined checksexamples/test-libraries– GoogleTest, Catch2, and doctest integrationexamples/coverage– Code coverage with gcov/llvm-covexamples/plugins– Shared library plugins with dlopenexamples/multi-binary– Multiple executables from one projectexamples/pkg-config– Using external libraries via pkg-configexamples/c-and-cpp– Mixed C and C++ sourcesexamples/devshell– Development shell with clangd supportexamples/simple-tool– Custom code generation tool pluginexamples/python-extension– Python C++ extension with pybind11
Build and run an example:
nix build .#executableExample --print-out-paths
# Output: /nix/store/xxx-executable-example
# Or in one line:
$(nix build .#executableExample --print-out-paths)/executable-example
Run all checks:
nix flake check
Run the incrementality/cache behavior gate:
nix run .#incrementality-gate
# or
./scripts/incrementality-gate.sh
The gate enforces:
- no recompilation on a no-op rebuild
- exactly one recompilation after editing one source file in a two-source target
- stable object derivation identity for unchanged sources
Platform Support
- Linux (x86_64, aarch64): Fully supported
Repository Layout
.
├── flake.nix # Top-level flake exposing native.lib and examples
├── nix/native/
│ ├── default.nix # Main entry point, assembles all modules
│ ├── core/ # Compiler, linker, toolchain, and tool plugin abstractions
│ ├── compilers/ # Compiler implementations (clang, gcc)
│ ├── linkers/ # Linker implementations (lld, mold, ld)
│ ├── ninja/ # nix-ninja integration (build file generation)
│ ├── modules/ # Shared schema and module internals
│ ├── builders/ # High-level API (executable, staticLib, etc.)
│ ├── tools/ # Built-in tool plugins (protobuf, jinja, binary-blob)
│ ├── testlibs/ # Test framework integrations (gtest, catch2, doctest)
│ ├── lsps/ # LSP/IDE support (clangd)
│ └── utils/ # Shared utilities
└── examples/ # Working example projects
Current Status
This project builds on experimental Nix features. The dynamic derivations implementation is based on John Ericson's work on RFC 92.
Key references:
License
MIT
Related Skills
node-connect
354.3kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
112.3kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
354.3kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
354.3kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
