{ lib, config, ... }: { options = { chk.bootDiskUsage = lib.mkOption { description = '' Choose boot.*.configurationLimit based on disk usage: Keep adding entries up to this limit of disk space, in megabytes. This allows the boot menu length to shrink and grow depending upon how much kernel/initrd overlap there is between system profiles. Note: This implementation doesn't yet support: * Named profiles / specialisations * Devicetrees * Grub / refind / limine (systemd-boot only) ''; type = with lib.types; nullOr int; default = null; }; boot.loader.systemd-boot.configurationLimit = lib.mkOption { # Normally, this is inserted into systemd-boot-builder.py like this: # # CONFIGURATION_LIMIT = int("@configurationLimit@") # # When chk.bootDiskUsage is set, instead of a simple integer constant, # we instead insert a program snippet to dynamically calculate a # configurationLimit (boot list length) as large as possible without # going over the allowed disk space consumption. # # * We're stuck in expression context here, so we can't have nice things # like names; it has to be one giant expression. :( # * This declaration appears early in systemd-boot-builder.py, so we # can't use the various helper functions it defines below this point. # * We can't get access to the output of 'nix-env --list-generations' # that it runs later, so we have to also run it here. Running it twice # introduces a TOCTOU race risk, but systemd-boot-builder.py is already # full of those, so one more doesn't change much. apply = prev: if builtins.isNull (config.chk.bootDiskUsage) then prev else '' " + ( "%d" % ( __import__('functools').reduce( lambda acc, gen: ( gen[0] if sum(x[0] for x in acc[1]) < (${toString config.chk.bootDiskUsage} * 1024 * 1024) else acc[0], acc[1].union(gen[1]) ), enumerate( [[(os.stat(f).st_size, f) for f in gen] for gen in map( lambda j: [j["kernel"], j["initrd"]], reversed( [json.load( open(f'/nix/var/nix/profiles/system-{line.split()[0]}-link/boot.json', 'r') )["org.nixos.bootspec.v1"] for line in subprocess.run( ['nix-env', '--list-generations', '-p', '/nix/var/nix/profiles/system'], check=True, text=True, stdout=subprocess.PIPE, stderr=sys.stderr ).stdout.split('\n')[:-1] ] ) ) ], start=1 ), (0, set()) )[0] ) ) + "''; }; }; }