:: commit d3201995301bcbc041ed90cd81f5f3b23421d87a

Philipp Schuster <philipp.schuster@cyberus-technology.de> — 2024-01-31 15:43

parents: 06e0e14fed

nix: build Limine in Nix

This packages Limine in Nix and exports it via the Nix Flake.
Due to the nature of the project, i.e., the self-hacked
"git submodule checkout with additional patches" in `./bootstrap`,
packing this in a Nix-way is very complicated.

The big advantage of packaging Limine in Nix directly as part
of the flake (additionally to nixpkgs) is that instead of using a
fixed version from nixpkgs coming from a Limine release, one can
use the current source and build everything in Nix.
diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
index f2dc0880..04beab37 100644
--- a/.github/workflows/check.yml
+++ b/.github/workflows/check.yml
@@ -29,3 +29,21 @@ jobs:
 
       - name: Build the bootloader (GNU, riscv64)
         run: ./bootstrap && ./configure TOOLCHAIN_FOR_TARGET=riscv64-linux-gnu --enable-werror --enable-uefi-riscv64 && make all && make maintainer-clean
+
+  build_nix:
+    name: Build with Nix
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - uses: cachix/install-nix-action@v26
+      - uses: DeterminateSystems/magic-nix-cache-action@main
+      - run: nix build .#limine
+
+  build_nix_shell:
+    name: Build with Nix shell toolchain
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - uses: cachix/install-nix-action@v26
+      - uses: DeterminateSystems/magic-nix-cache-action@main
+      - run: nix develop --command bash -c "./bootstrap && ./configure --enable-all && make -j $(nproc)"
\ No newline at end of file
diff --git a/README.md b/README.md
index 0703a2eb..13a92c13 100644
--- a/README.md
+++ b/README.md
@@ -84,6 +84,21 @@ Host utility binaries are provided for Windows.
 
 ### Prerequisites
 
+### Building with Nix
+
+This repository provides [Nix](https://nixos.org/)-based tooling for a convenient 
+development environment and building Limine using Nix.
+
+To use the regular build flow using a toolchain obtained by Nix, simply
+run `$ nix develop` to open a Nix shell. To build Limine completely in Nix 
+using the latest sources, you can run:
+
+- `$ nix build .#\limine` (build with clang and `--enable-all`)
+
+Limine is not yet in `nixpkgs`.
+
+### Regular build
+
 In order to build Limine, the following programs have to be installed:
 common UNIX tools (also known as `coreutils`),
 `GNU make`, `grep`, `sed`, `find`, `awk`, `gzip`, `nasm`, `mtools`
@@ -91,7 +106,7 @@ common UNIX tools (also known as `coreutils`),
 Furthermore, `gcc` or `llvm/clang` must also be installed, alongside
 the respective binutils.
 
-### Configure
+#### Configure
 
 If using a release tarball (recommended, see https://github.com/limine-bootloader/limine/releases),
 run `./configure` directly.
@@ -109,7 +124,7 @@ Limine supports both in-tree and out-of-tree builds. Simply run the `configure`
 script from the directory you wish to execute the build in. The following `make`
 commands are supposed to be run inside the build directory.
 
-### Building Limine
+#### Building Limine
 
 To build Limine, run:
 ```bash
diff --git a/flake.lock b/flake.lock
index fc8e038f..aa22c560 100644
--- a/flake.lock
+++ b/flake.lock
@@ -7,11 +7,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1709336216,
-        "narHash": "sha256-Dt/wOWeW6Sqm11Yh+2+t0dfEWxoMxGBvv3JpIocFl9E=",
+        "lastModified": 1712014858,
+        "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
         "owner": "hercules-ci",
         "repo": "flake-parts",
-        "rev": "f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2",
+        "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
         "type": "github"
       },
       "original": {
@@ -22,11 +22,11 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1710695816,
-        "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=",
+        "lastModified": 1711668574,
+        "narHash": "sha256-u1dfs0ASQIEr1icTVrsKwg2xToIpn7ZXxW3RHfHxshg=",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "614b4613980a522ba49f0d194531beddbb7220d3",
+        "rev": "219951b495fc2eac67b1456824cc1ec1fd2ee659",
         "type": "github"
       },
       "original": {
diff --git a/flake.nix b/flake.nix
index 87e3fbf4..18fc52ec 100644
--- a/flake.nix
+++ b/flake.nix
@@ -5,7 +5,7 @@
 # regarding the packaging in nixpkgs.
 
 {
-  description = "limine";
+  description = "Limine";
 
   inputs = {
     flake-parts.url = "github:hercules-ci/flake-parts";
@@ -20,6 +20,9 @@
       # will notice it soon enough.
       systems = nixpkgs.lib.systems.flakeExposed;
       perSystem = { config, pkgs, ... }:
+        let
+          limine = pkgs.callPackage ./nix/build.nix { };
+        in
         {
           devShells = {
             default = pkgs.mkShell {
@@ -38,6 +41,7 @@
                 # gcc toolchain (comes as default, here only for completness)
                 binutils
                 gcc
+                gnumake
 
                 # llvm toolchain (with TOOLCHAIN_FOR_TARGET=llvm)
                 llvmPackages.bintools
@@ -52,6 +56,12 @@
 
           # `$ nix fmt`
           formatter = pkgs.nixpkgs-fmt;
+
+          # `$ nix build .#<attr>`
+          packages = {
+            inherit limine;
+            default = limine;
+          };
         };
     };
 }
diff --git a/nix/build.nix b/nix/build.nix
new file mode 100644
index 00000000..813fe4b9
--- /dev/null
+++ b/nix/build.nix
@@ -0,0 +1,139 @@
+# Building Limine with all features in Nix.
+#
+# Independent of the packing in nixpkgs, This is convenient for prototyping and
+# local development.
+#
+# These derivations always builds Limine from the current src tree and not some
+# stable release. Further, unlike the nixpkgs derivation, this derivation runs
+# the ./bootstrap step which needs network access. Due to the nature of the
+# self-hacked Git submodules download approach, packaging this project in Nix
+# is especailly complicated. The complicated multi-derivation approach below
+# is the best I can get after multiple hours of trying. :).
+
+{
+  # Helpers
+  fd
+, lib
+, nix-gitignore
+, stdenv
+
+  # Actual derivation dependencies.
+, autoconf
+, automake
+, cacert
+, git
+, llvmPackages
+, mtools
+, nasm
+}:
+
+let
+  currentRepoSrc = nix-gitignore.gitignoreSource [
+    # Additional git ignores:
+    "flake.nix" # otherwise
+    "flake.lock"
+    "nix/"
+  ] ../.;
+
+  # Contains the sources downloaded by the Git submodule-like initialation done
+  # in ./bootstrap.
+  #
+  # ALWAYS update the hash when one of the network dependencies in ./bootstrap
+  # changes. Also, before updating, it is recommended to run "make clean"
+  # beforehand.
+  # bootstrappedSrcHash = lib.fakeHash;
+  #
+  # TODO: Unfortunately, currently this hash changes for almost every repository
+  # change. We need to strip down this derivation further to only contain the
+  # changed sources.
+  bootstrappedSrcHash = "sha256-UU5pkdbaKXPs/i/hnuk4vZcxiag1cTsTCcn2LGzPuMs=";
+  bootstrappedSrc = stdenvNoCC.mkDerivation {
+    pname = "limine-bootstrapped";
+    version = "0.0.0";
+    src = currentRepoSrc;
+    nativeBuildInputs = [
+      cacert
+      git
+      fd
+    ];
+    buildPhase = ''
+      runHook preBuild
+
+      # `true` refers to the binary/bash-builtin to prevent any configuration
+      # steps apart from downloading sources.
+      AUTOMAKE=true AUTORECONF=true ./bootstrap
+
+      # When cloning, Git automatically creates hooks. Unfortunately, in a Nix
+      # environment / on a NixOS system, this includes Nix store paths.
+      # However, me must prevent to have any Nix store path inside the final
+      # directory, as otherwise we get the error
+      # "illegal path references in fixed-output derivation"! Further, we must
+      # remove all git artifacts (.git dirs) as they affect the hash of the
+      # derivation in a non-deterministic way.
+      fd -u --type=d "^.git$" --min-depth=2 . --exec rm -rf {}
+
+      # This should report nothing. Othewise, the Nix build will fail.
+      # grep -r /nix/store .
+
+      runHook postBuild
+    '';
+    dontPatchShebangs = true;
+    installPhase = ''
+      runHook preInstall
+      mkdir $out
+
+      cp -r . $out
+
+      runHook postInstall
+    '';
+    doCheck = false;
+    dontFixup = true;
+    # See "fixed output derivation".
+    outputHashAlgo = "sha256";
+    outputHashMode = "recursive";
+    outputHash = bootstrappedSrcHash;
+  };
+
+  # Common build dependencies apart from the compiler toolchain.
+  commonBuildDeps = [
+    autoconf
+    automake
+
+    mtools
+    nasm
+  ];
+in
+stdenv.mkDerivation {
+  pname = "limine-dev";
+  version = "0.0.0";
+  src = bootstrappedSrc;
+  nativeBuildInputs = commonBuildDeps ++ [
+    # gcc is used to build the host tools and clang to (cross)compile all
+    # the bootloader files
+    llvmPackages.bintools
+    llvmPackages.clang
+    llvmPackages.lld
+  ];
+  enableParallelBuilding = true;
+  preConfigure = ''
+    # The default input source of this derivation is what we aggregated
+    # from `./bootstrap`. As this derivation holds all files but we are only
+    # interested in the ones that are not in `currentRepoSrc`, we just
+    # override all.
+    #
+    # This way we can use the actual current repo sources but still use the
+    # populated sources from the ./bootstrap script.
+    #
+    # It's very complicated, I know. But that way we can make it work, at
+    # least.
+    cp -RTf ${currentRepoSrc} .
+
+    # Extracted from ./bootstrap. To see why, check the `bootstrapedSrc`
+    # derivation.
+    #
+    # TODO, we could also do this in ./bootstrap but add a special flag.
+    autoreconf -fvi -Wall
+  '';
+  configureFlags = [ "--enable-all" ];
+  outputs = [ "out" "doc" "dev" "man" ];
+}
tab: 248 wrap: offon