C Nix boilerplate
Last modified on 18 Jun 2022 by Gabriel Fontes
Tags: #nix
Looking to start out a C (or C++) project and wanna some cool and nifty commands to manage it through both make
and nix
? As a plus, you’ll also get nice out of tree builds.
Then look no further, here’s all the boilerplate you need.
If you just want it fast without an explanation, it’s all available on my repository.
You can just run nix flake init -t github:misterio77/nix-config#templates.c
, to quickly init a flake with this template.
We’re gonna start by building a flexible (yet simple) Makefile:
# Set default prefix and bin dirs, for non-nixos
PREFIX ?= /usr/local
BIN_DIR ?= $(PREFIX)/bin
# Executable name
TARGET_EXEC ?= foo-bar
# Build directory
BUILD_DIR ?= ./build
# Source directory(ies)
SRC_DIRS ?= ./src
# Use find to get all .c and .cpp files
SRCS := $(shell find $(SRC_DIRS) -name *.cpp -or -name *.c)
# Map those to their respective .o files
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
# Dependency files generated by gcc
DEPS := $(OBJS:.o=.d)
# Directories with includes
INC_DIRS := $(shell find $(SRC_DIRS) -type d)
# Flags for including those
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
# Flags for both C and C++
CPPFLAGS ?= $(INC_FLAGS) -MMD -MP
# Link executable
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
$(CXX) $(OBJS) -o $@ $(LDFLAGS)
# C source files
$(BUILD_DIR)/%.c.o: %.c
$(MKDIR_P) $(dir $@)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
# C++ source files
$(BUILD_DIR)/%.cpp.o: %.cpp
$(MKDIR_P) $(dir $@)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
# Clean built artifacts
clean:
$(RM) -r $(BUILD_DIR)
# Install to prefix/bin path
install: $(BUILD_DIR)/$(TARGET_EXEC)
install -d $(BIN_DIR)
install -t $(BIN_DIR) $<
# Include generated dependencies
-include $(DEPS)
MKDIR_P ?= mkdir -p
This is heavily based on this post, with the main change being the install directive.
Okay, now we just need a flake for building with nix:
{
description = "Foo Bar C/C++ Project";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
# For each system
flake-utils.lib.eachDefaultSystem (system:
let
# Derivation and executable name
name = "foo-bar";
pkgs = (import nixpkgs { inherit system; });
in
rec {
# nix build
packages.${name} = pkgs.stdenv.mkDerivation rec {
inherit name;
src = ./.;
# Set prefix, so stuff is installed to $out
makeFlags = [ "PREFIX=$(out)" ];
};
defaultPackage = packages.${name};
# nix run
apps.${name} = {
type = "app";
program = "${packages.${name}}/bin/${name}";
};
defaultApp = apps.${name};
# nix develop
devShell = pkgs.mkShell {
# Add clang and clang-tools for LSP support while editing
buildInputs = with pkgs; [ gnumake clang clang-tools ];
};
});
}
And there you go!