(*─────────────────────────────────────────────────────────────────────────────┐
│ SPDX-FileCopyrightText: 2025–2026 toastal │
│ SPDX-License-Identifier: LGPL-2.1-or-later WITH OCaml-LGPL-linking-exception │
└─────────────────────────────────────────────────────────────────────────────*)
(* Loads the lockfile for Nix usage broadly *)
let filename = "default.nix"
module Features = struct
type t = int [@@deriving show]
let empty = 0
(* only build features needed *)
let file = 1 lsl 0
let archive = 1 lsl 1
let git = 1 lsl 2
let darcs = 1 lsl 3
let pijul = 1 lsl 4
let nilla = 1 lsl 5
let fossil = 1 lsl 6
let [@inline]has mask v = (mask land v) <> 0
let [@inline]add mask v = mask lor v
let [@inline]drop mask v = mask land (lnot v)
let value = ref empty
let add_input (input : Input.t) : t -> t =
match input.kind with
| `File _ -> add file
| `Archive _ -> add archive
| `Git _ -> add git
| `Darcs _ -> add darcs
| `Pijul _ -> add pijul
| `Nilla _ -> add nilla
| `Fossil _ -> add fossil
let drop_input (input : Input.t) : t -> t =
match input.kind with
| `File _ -> drop file
| `Archive _ -> drop archive
| `Git _ -> drop git
| `Darcs _ -> drop darcs
| `Pijul _ -> drop pijul
| `Nilla _ -> drop nilla
| `Fossil _ -> drop fossil
end
open Fmt
let pp_banner (ppf : Format.formatter) =
let maker = "toastal"
and year_range =
let first = 2025 in
(* replaced by Nix *)
match int_of_string_opt "@release_year@" with
| Some last when last > first -> Fmt.str "%a–%a" Fmt.int first Fmt.int last
| _ -> Fmt.str "%a" Fmt.int first
and margin = Format.pp_get_margin ppf ()
in
let hr =
(*──────────────────────────────*)
let uchar = Uchar.of_int 0x2500
and buf = Buffer.create (margin * 3)
in
for _ = 1 to margin do
Buffer.add_utf_8_uchar buf uchar
done;
Buffer.contents buf
in
pf ppf "/*@.";
pf ppf "SPDX-FileCopyrightText: %a %a@." Fmt.string year_range Fmt.string maker;
pf ppf "SPDX-License-Identifier: ISC@.";
pf ppf "@.";
pf ppf "@[Permission@ to@ use,@ copy,@ modify,@ and/or@ distribute@ ";
pf ppf "this@ software@ for@ any@ purpose@ with@ or@ without@ fee@ is@ ";
pf ppf "hereby@ granted,@ provided@ that@ the@ above@ copyright@ notice@ &@ ";
pf ppf "this@ permission@ notice@ appear@ in@ all@ copies.@]@.";
pf ppf "@.";
pf ppf "@[THE@ SOFTWARE@ IS@ PROVIDED@ “AS@ IS”@ &@ ISC@ DISCLAIMS@ ";
pf ppf "ALL@ WARRANTIES@ WITH@ REGARD@ TO@ THIS@ SOFTWARE@ INCLUDING@ ALL@ ";
pf ppf "IMPLIED@ WARRANTIES@ OF@ MERCHANTABILITY@ &@ FITNESS.@ IN@ NO@ ";
pf ppf "EVENT@ SHALL@ ISC@ BE@ LIABLE@ FOR@ ANY@ SPECIAL,@ DIRECT,@ ";
pf ppf "INDIRECT,@ OR@ CONSEQUENTIAL@ DAMAGES@ OR@ ANY@ DAMAGES@ WHATSOEVER@ ";
pf ppf "RESULTING@ FROM@ LOSS@ OF@ USE,@ DATA@ OR@ PROFITS,@ WHETHER@ IN@ ";
pf ppf "AN@ ACTION@ OF@ CONTRACT,@ NEGLIGENCE@ OR@ OTHER@ TORTIOUS@ ACTION,@ ";
pf ppf "ARISING@ OUT@ OF@ OR@ IN@ CONNECTION@ WITH@ THE@ USE@ OR@ ";
pf ppf "PERFORMANCE@ OF@ THIS@ SOFTWARE.@]@.";
pf ppf "@.";
pf ppf "%a@." Fmt.string hr;
pf ppf "┏┓╻+╻ ╱┏┳┓┏┓┏┳┓┏┓╻@.";
pf ppf "┃┃┃┃┗━┓╹┃╹┣┫┃┃┃┣┫┃ This file was generated by Nixtamal.@.";
pf ppf "╹┗┛╹╱ ╹ ╹ ╹╹╹ ╹╹╹┗┛ Do not edit as it will be overwritten.@.";
pf ppf "%a@." Fmt.string hr;
pf ppf "*/@."
let pp_nix_named_arg (ppf : Format.formatter) ((name, default): (string * string option)) =
pf ppf "%a%a" string name (option (fun ppf v -> pf ppf " ? %s" v)) default
let pp_nix_named_args fmt args =
let pp_args = list ~sep: (any ",@;") pp_nix_named_arg in
let break = Format.pp_print_custom_break ~fits: ("", 0, "") ~breaks: (",", 0, "") in
pf fmt "@[{@;<0 1>@[@[%a@]@]%t@]}:" pp_args args break
let pp_cfg (ppf : Format.formatter) =
pp_nix_named_args ppf [
("system", Some "builtins.currentSystem");
("nixpkgs", Some "null");
("bootstrap-nixpkgs-lock-name", Some "null");
]
(* TODO: consider *not* doing manually as this is ugly AF, but would probably
involve building a Nix AST to do properly *)
let pp_body ~version (ppf : Format.formatter) () =
let feats = !Features.value in
pf ppf {|let lock = builtins.fromJSON (builtins.readFile ./lock.json); in@.|};
pf ppf {|assert (lock.v == "%a");@.|} string version;
pf ppf {|let@.|};
pf ppf {| try-fetch = name: fetcher:@.|};
pf ppf {| let@.|};
pf ppf {| try-fetch' = failed-urls: url: urls:@.|};
pf ppf {| let result = builtins.tryEval (fetcher url); in@.|};
pf ppf {| if result.success then@.|};
pf ppf {| result.value@.|};
pf ppf {| else@.|};
pf ppf {| let failed-urls' = [ url ] ++ failed-urls; in@.|};
pf ppf {| if builtins.length urls <= 0 then@.|};
pf ppf {| let fus = builtins.concatStringsSep " " failed-urls'; in@.|};
pf ppf {| throw "Input 「${name}」fetchable @@ [ ${fus} ]"@.|};
pf ppf {| else@.|};
pf ppf {| try-fetch' failed-urls' (builtins.head urls) (builtins.tail urls);@.|};
pf ppf {| in@.|};
pf ppf {| try-fetch' [ ];@.|};
pf ppf {|@.|};
if Features.has Features.file feats then
begin
pf ppf {| builtin-fetch-url = {name, kind, hash}:@.|};
pf ppf {| try-fetch name (url:@.|};
pf ppf {| builtins.fetchurl {@.|};
pf ppf {| inherit url name;@.|};
pf ppf {| ${hash.al} = hash.vl;@.|};
pf ppf {| }@.|};
pf ppf {| ) kind.ur kind.ms;@.|};
pf ppf {|@.|};
end;
if Features.has Features.archive feats then
begin
pf ppf {| builtin-fetch-tarball = {name, kind, hash}:@.|};
pf ppf {| try-fetch name (url:@.|};
pf ppf {| builtins.fetchTarball {@.|};
pf ppf {| inherit url;@.|};
pf ppf {| ${hash.al} = hash.vl;@.|};
pf ppf {| }@.|};
pf ppf {| ) kind.ur kind.ms;@.|};
pf ppf {|@.|}
end;
if Features.has Features.git feats then
begin
pf ppf {| builtin-fetch-git = {name, kind}:@.|};
pf ppf {| try-fetch name (url:@.|};
pf ppf {| builtins.fetchGit {@.|};
pf ppf {| inherit url;@.|};
pf ppf {| rev = kind.lr;@.|};
pf ppf {| submodules = kind.sm;@.|};
pf ppf {| lfs = kind.lf;@.|};
pf ppf {| shallow = true;@.|};
pf ppf {| }@.|};
pf ppf {| ) kind.rp kind.ms;@.|};
pf ppf {|@.|}
end;
pf ppf {| builtin-to-input = name: input:@.|};
pf ppf {| let k = builtins.head input.kd; in@.|};
pf ppf {| |};
let builtin_fetch_ifs = Dynarray.create () in
if Features.has Features.file feats then
Dynarray.add_last builtin_fetch_ifs (
Fmt.str "@[%a@]" (list ~sep: (any "@.") string) [
{|if k == 0 then|};
{| builtin-fetch-url {|};
{| inherit name;|};
{| kind = builtins.elemAt input.kd 1;|};
{| hash = input.ha;|};
{| }|};
]
);
if Features.has Features.archive feats then
Dynarray.add_last builtin_fetch_ifs (
Fmt.str "@[%a@]" (list ~sep: (any "@.") string) [
{|if k == 1 then|};
{| builtin-fetch-tarball {|};
{| inherit name;|};
{| kind = builtins.elemAt input.kd 1;|};
{| hash = input.ha;|};
{| }|};
]
);
if Features.has Features.git feats then
Dynarray.add_last builtin_fetch_ifs (
Fmt.str "@[%a@]" (list ~sep: (any "@.") string) [
{|if k == 2 then|};
{| builtin-fetch-git {|};
{| inherit name;|};
{| kind = builtins.elemAt input.kd 1;|};
{| }|};
]
);
pf ppf "@[%a@]@." (list ~sep: (any "@. else ") string) (Dynarray.to_list builtin_fetch_ifs);
Dynarray.clear builtin_fetch_ifs;
pf ppf {| else@.|};
pf ppf {| throw "Unsupported input kind “${builtins.toString k}”.";@.|};
pf ppf {|@.|};
pf ppf {| nixpkgs' = if builtins.isNull nixpkgs then@.|};
pf ppf {| builtin-to-input "nixpkgs-for-nixtamal" (@.|};
pf ppf {| if builtins.isString bootstrap-nixpkgs-lock-name then@.|};
pf ppf {| lock.i.${bootstrap-nixpkgs-lock-name}@.|};
pf ppf {| else@.|};
pf ppf {| lock.i.nixpkgs-nixtamal or lock.i.nixpkgs@.|};
pf ppf {| )@.|};
pf ppf {| else@.|};
pf ppf {| nixpkgs;@.|};
pf ppf {|@.|};
pf ppf {| pkgs = import nixpkgs' {inherit system;};@.|};
pf ppf {|@.|};
pf ppf {| inherit (pkgs) lib;@.|};
pf ppf {|@.|};
if Features.has Features.file feats then
begin
pf ppf {| fetch-url = {name, kind, hash}: pkgs.fetchurl {@.|};
pf ppf {| inherit name;@.|};
pf ppf {| url = kind.ur;@.|};
pf ppf {| ${hash.al} = hash.vl;@.|};
pf ppf {| } // lib.optionalAttrs (builtins.length kind.ms > 0) { urls = kind.ms; };@.|};
pf ppf {|@.|}
end;
if Features.has Features.archive feats then
begin
pf ppf {| fetch-zip = {name, kind, hash}: pkgs.fetchzip {@.|};
pf ppf {| inherit name;@.|};
pf ppf {| url = kind.ur;@.|};
pf ppf {| ${hash.al} = hash.vl;@.|};
pf ppf {| } // lib.optionalAttrs (builtins.length kind.ms > 0) { urls = kind.ms; };@.|};
pf ppf {|@.|}
end;
if Features.has Features.git feats then
begin
pf ppf {| fetch-git = {name, kind, hash}:@.|};
pf ppf {| let@.|};
pf ppf {| using-mirrors = kind ? ms && (builtins.length kind.ms) > 0;@.|};
pf ppf {| mirror-support = pkgs.fetchgit.__functionArgs ? "mirrors";@.|};
pf ppf {| in@.|};
pf ppf {| lib.warnIf (using-mirrors && !mirror-support)@.|};
pf ppf {| "Upstream pkgs.fetchgit doesn’t yet support mirrors for 「${name}」"@.|};
pf ppf {| pkgs.fetchgit {@.|};
pf ppf {| url = kind.rp;@.|};
pf ppf {| rev = kind.lr;@.|};
pf ppf {| fetchSubmodules = kind.sm;@.|};
pf ppf {| fetchLFS = kind.lf;@.|};
pf ppf {| deepClone = false;@.|};
pf ppf {| ${hash.al} = hash.vl;@.|};
pf ppf {| } // lib.optionalAttrs (using-mirror && mirror-support) {@.|};
pf ppf {| mirrors = kind.ms;@.|};
pf ppf {| };@.|};
pf ppf {|@.|}
end;
if Features.has Features.darcs feats then
begin
pf ppf {| fetch-darcs = {name, kind, hash}:@.|};
pf ppf {| let@.|};
pf ppf {| using-mirrors = kind ? ms && (builtins.length kind.ms) > 0;@.|};
pf ppf {| mirror-support = pkgs.fetchdarcs.__functionArgs ? "mirrors";@.|};
pf ppf {| reference =@.|};
pf ppf {| let@.|};
pf ppf {| type = builtins.elemAt kind.rf 0;@.|};
pf ppf {| value = builtins.elemAt kind.rf 1;@.|};
pf ppf {| in@.|};
pf ppf {| if type == 0 then@.|};
pf ppf {| let ctx_path = builtins.elemAt value 1; in@.|};
pf ppf {| assert (lib.hasSuffix ".txt" ctx_path);@.|};
pf ppf {| let@.|};
pf ppf {| txt-files = lib.sourceFilesBySuffices ./. [ ".txt" ];@.|};
pf ppf {| dir = lib.fileset.toSource {@.|};
pf ppf {| root = ./.;@.|};
pf ppf {| fileset = lib.fileset.fromSource txt-files;@.|};
pf ppf {| };@.|};
pf ppf {| in@.|};
pf ppf {| {context = "${dir}/${ctx_path}";}@.|};
pf ppf {| else if type == 1 then@.|};
pf ppf {| {rev = value;}@.|};
pf ppf {| else@.|};
pf ppf {| throw "Invalid Darcs reference";@.|};
pf ppf {| in@.|};
pf ppf {| lib.warnIf (using-mirrors && !mirror-support)@.|};
pf ppf {| "Upstream pkgs.fetchdarcs doesn’t yet support mirrors for 「${name}」"@.|};
pf ppf {| pkgs.fetchdarcs ({@.|};
pf ppf {| url = kind.rp;@.|};
pf ppf {| ${hash.al} = hash.vl;@.|};
pf ppf {| } // reference // lib.optionalAttrs (using-mirrors && mirror-support){@.|};
pf ppf {| mirrors = kind.ms;@.|};
pf ppf {| });@.|};
pf ppf {|@.|}
end;
if Features.has Features.pijul feats then
begin
pf ppf {| fetch-pijul = {name, kind, hash}:@.|};
pf ppf {| let@.|};
pf ppf {| using-mirrors = kind ? ms && (builtins.length kind.ms) > 0;@.|};
pf ppf {| mirror-support = pkgs.fetchpijul.__functionArgs ? "mirrors";@.|};
pf ppf {| in@.|};
pf ppf {| lib.warnIf (using-mirrors && !mirror-support)@.|};
pf ppf {| "Upstream pkgs.fetchpijul doesn’t yet support mirrors for 「${name}」"@.|};
pf ppf {| pkgs.fetchpijul {@.|};
pf ppf {| url = kind.rm;@.|};
pf ppf {| state = kind.ls;@.|};
pf ppf {| ${hash.al} = hash.vl;@.|};
pf ppf {| } // lib.optionalAttrs (using-mirrors && mirror-support) {@.|};
pf ppf {| mirrors = kind.ms;@.|};
pf ppf {| };@.|};
pf ppf {|@.|}
end;
pf ppf {| to-input = name: input:@.|};
pf ppf {| let k = builtins.head input.kd; in@.|};
pf ppf {| |};
let pkgs_fetch_ifs = Dynarray.create () in
if Features.has Features.file feats then
Dynarray.add_last pkgs_fetch_ifs (
Fmt.str "@[%a@]" (list ~sep: (any "@.") string) [
{|if k == 0 then|};
{| fetch-url {|};
{| inherit name;|};
{| kind = builtins.elemAt input.kd 1;|};
{| hash = input.ha;|};
{| }|};
]
);
if Features.has Features.archive feats then
Dynarray.add_last pkgs_fetch_ifs (
Fmt.str "@[%a@]" (list ~sep: (any "@.") string) [
{|if k == 1 then|};
{| fetch-zip {|};
{| inherit name;|};
{| kind = builtins.elemAt input.kd 1;|};
{| hash = input.ha;|};
{| }|};
]
);
if Features.has Features.git feats then
Dynarray.add_last pkgs_fetch_ifs (
Fmt.str "@[%a@]" (list ~sep: (any "@.") string) [
{|if k == 2 then|};
{| fetch-git {|};
{| inherit name;|};
{| kind = builtins.elemAt input.kd 1;|};
{| hash = input.ha;|};
{| }|};
]
);
if Features.has Features.darcs feats then
Dynarray.add_last pkgs_fetch_ifs (
Fmt.str "@[%a@]" (list ~sep: (any "@.") string) [
{|if k == 3 then|};
{| fetch-darcs {|};
{| inherit name;|};
{| kind = builtins.elemAt input.kd 1;|};
{| hash = input.ha;|};
{| }|};
]
);
if Features.has Features.pijul feats then
Dynarray.add_last pkgs_fetch_ifs (
Fmt.str "@[%a@]" (list ~sep: (any "@.") string) [
{|if k == 4 then|};
{| fetch-pijul {|};
{| inherit name;|};
{| kind = builtins.elemAt input.kd 1;|};
{| hash = input.ha;|};
{| }|};
]
);
pf ppf "@[%a@]@." (list ~sep: (any "@. else ") string) (Dynarray.to_list pkgs_fetch_ifs);
Dynarray.clear pkgs_fetch_ifs;
pf ppf {| else@.|};
pf ppf {| throw "Unsupported input kind “${builtins.toString k}”.";@.|};
pf ppf {|in@.|};
pf ppf {|builtins.mapAttrs to-input lock.i@.|}
let pp ~version (ppf : Format.formatter) =
set_utf_8 ppf true;
let custom_formatter_functions : Format.formatter_out_functions =
let sf = Format.pp_get_formatter_out_functions ppf () in
{sf with out_indent = fun n -> sf.out_string (String.make n '\t') 0 n}
in
Format.pp_set_formatter_out_functions ppf custom_formatter_functions;
Format.pp_set_margin Format.std_formatter 80;
Format.pp_set_formatter_out_functions ppf custom_formatter_functions;
pp_banner ppf;
pp_cfg ppf;
pf ppf "@.@.";
pp_body ~version ppf ()
let write ?(version = "0.1.1") () =
let working_dir = Working_directory.get () in
let filepath = Eio.Path.(working_dir / filename) in
let () =
Input_foreman.inputs
|> Saturn.Htbl.to_seq
|> Seq.iter (fun (_name, input) ->
let () = Features.value := Features.add_input input !Features.value in ()
)
in
Logs.info (fun m -> m "Writing lock loader @@ %s …" filename);
let () =
Eio.Path.with_open_out ~create: (`Or_truncate 0o644) filepath @@ fun flow ->
Util.Formatter.to_flow (pp ~version) flow
in
Logs.info (fun m -> m "Lock loader written.")