Building Handmade Hero with Nix Flakes
Lately, I'm following along Handmade Hero on NixOS using SDL2 (thank you
Handmade Penguin), and wanted to setup Nix Flakes to build the derivation. I
made something work for C/C++ with external dependencies such as SDL2.
If you are unfamiliar with the nix ecosystem, I recommend reading my How-To Nix guide, but it's not a requirement. I'm still learning about these concepts myself, so don't worry.
My requirements:
- ability to use
nix runto display the project (via NixOS, or Nix on it's own) - place C++ source files in
./src - the
Language Server Protocol(LSP) should pick up theSDL2library when using direnv in your editor of choice (I use Emacs btw 😎)
Show the flake.nix already!
For my own system configuration I depend on nixos-unstable, but for projects I
like to depend on more stable version nixos-23.05. I need the flake-utils,
since it makes working with flakes easier when building derivations for multiple
platforms.
{
description = "Handmade Hero on NixOS";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-23.05";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let pkgs = nixpkgs.legacyPackages.${system};
in {
# TODO(Kevin): What goes in here?
});
}I use clangd, not sure why, but it's the one I chose. If you want to use
gcc, go for it. I also use gf to provide me with a simple debugger as
RemedyBG is not available on Linux yet.
The executable depends on SDL2 being available at runtime, which is why we add
it to the buildInputs. Usually you would read mkDerivation, but this
environment provides gcc by default, not clang++. We have to call the
derivation helper from a clangStenv.
packages = {
default = let inherit (pkgs) clangStdenv;
in clangStdenv.mkDerivation {
name = "my-handmade-hero";
src = ./.;
buildInputs = with pkgs; [ SDL2 ];
buildPhase = with pkgs;
"clang++ ./src/sdl_handmade.cpp -o handmade_hero -lSDL2";
installPhase = ''
mkdir -p $out/bin
cp handmade_hero $out/bin/my-handmade-hero
'';
};
};The isolated development environment loads the same buildInputs and
nativeBuildInputs, from the earlier mention package declaration via
inputsFrom. The shell reports the installed clang++ version as a sanity
check.
devShells = {
default = pkgs.mkShell.override { stdenv = pkgs.clangStdenv; } {
packages = with pkgs; [ clang-tools gf SDL2.dev ];
inputsFrom = [ self.packages.${system}.default ];
shellHook = with pkgs; ''
echo "`clang++ --version`"
'';
};
};Now, by bringing all these puzzles pieces together, you end up with the
following flake.nix.
{
description = "Handmade Hero on NixOS";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-23.05";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
name = "my-handmade-hero";
src = ./.;
pkgs = nixpkgs.legacyPackages.${system};
buildInputs = with pkgs; [ SDL2 SDL2.dev ];
nativeBuildInputs = with pkgs; [ clang-tools gf ];
in {
<<package>>
<<dev-shell>>
});
}(ansi-color-apply text)Generate the lock file, and check if the flake is has been setup correctly.
nix flake lock
nix flake showgit+file:///home/venikx/code/venikx.com?dir=src/content/blog/handmade-hero-nixos
├───devShells
│ ├───aarch64-darwin
│ │ └───default omitted (use '--all-systems' to show)
│ ├───aarch64-linux
│ │ └───default omitted (use '--all-systems' to show)
│ ├───x86_64-darwin
│ │ └───default omitted (use '--all-systems' to show)
│ └───x86_64-linux
│ └───default: development environment 'nix-shell'
└───packages
├───aarch64-darwin
│ └───default omitted (use '--all-systems' to show)
├───aarch64-linux
│ └───default omitted (use '--all-systems' to show)
├───x86_64-darwin
│ └───default omitted (use '--all-systems' to show)
└───x86_64-linux
└───default: package 'my-handmade-hero'
Verify the SDL2 Headers with a Message Box
The simplest way to check if C/C++ code properly links with the SDL2 library
is by showing a message box. Your editor (I use Emacs btw 😎) should now also be
able to complete SDL functions when you type SDL_.
#include <SDL2/SDL.h>
#include <stdio.h>
int main(int arc, char **argv) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Handmade Hero",
"This is Handmade Hero", 0);
return 0;
}The hardest parts are behind us now. Running nix build creates an executable
file inside result. After running ./result/bin/handmade_hero you should see
a message box. Or equivalently run nix run.
nix run