💾 Archived View for misterio.me › 2021 › 09 › 14 › c-nix-boilerplate.gmi captured on 2022-06-04 at 00:03:24. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2022-03-01)

-=-=-=-=-=-=-

Gabriel Fontes

About

Blog

Portfolio

C Nix boilerplate

14 Sep 2021 - Gabriel Fontes

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.

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 {body}lt; -o $@

# C++ source files
$(BUILD_DIR)/%.cpp.o: %.cpp
	$(MKDIR_P) $(dir $@)
	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c {body}lt; -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) {body}lt;

# Include generated dependencies
-include $(DEPS)

MKDIR_P ?= mkdir -p

This is heavily based on this post, with the main change being the install directive.

https://spin.atomicobject.com/2016/08/26/makefile-c-projects/

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!