2017-05-03 22:36:39 +00:00
|
|
|
# A generalization of Nixpkgs's `strings-with-deps.nix`.
|
|
|
|
#
|
|
|
|
# The main differences from the Nixpkgs version are
|
|
|
|
#
|
|
|
|
# - not specific to strings, i.e., any payload is OK,
|
|
|
|
#
|
2022-01-13 17:21:32 +00:00
|
|
|
# - the addition of the function `entryBefore` indicating a "wanted
|
|
|
|
# by" relationship.
|
2017-05-03 22:36:39 +00:00
|
|
|
|
2017-09-21 11:19:29 +00:00
|
|
|
{ lib }:
|
|
|
|
|
2023-06-04 08:53:26 +00:00
|
|
|
let inherit (lib) all filterAttrs head hm mapAttrs length tail toposort;
|
2022-01-13 17:21:32 +00:00
|
|
|
in {
|
|
|
|
empty = { };
|
2017-05-03 22:36:39 +00:00
|
|
|
|
2022-01-13 17:21:32 +00:00
|
|
|
isEntry = e: e ? data && e ? after && e ? before;
|
2017-05-03 22:36:39 +00:00
|
|
|
isDag = dag:
|
2022-01-13 17:21:32 +00:00
|
|
|
builtins.isAttrs dag && all hm.dag.isEntry (builtins.attrValues dag);
|
2017-05-03 22:36:39 +00:00
|
|
|
|
2022-01-13 17:21:32 +00:00
|
|
|
# Takes an attribute set containing entries built by entryAnywhere,
|
|
|
|
# entryAfter, and entryBefore to a topologically sorted list of
|
|
|
|
# entries.
|
2017-05-03 22:36:39 +00:00
|
|
|
#
|
|
|
|
# Internally this function uses the `toposort` function in
|
|
|
|
# `<nixpkgs/lib/lists.nix>` and its value is accordingly.
|
|
|
|
#
|
|
|
|
# Specifically, the result on success is
|
|
|
|
#
|
2022-01-13 17:21:32 +00:00
|
|
|
# { result = [ { name = ?; data = ?; } … ] }
|
2017-05-03 22:36:39 +00:00
|
|
|
#
|
|
|
|
# For example
|
|
|
|
#
|
2022-01-13 17:21:32 +00:00
|
|
|
# nix-repl> topoSort {
|
|
|
|
# a = entryAnywhere "1";
|
|
|
|
# b = entryAfter [ "a" "c" ] "2";
|
|
|
|
# c = entryBefore [ "d" ] "3";
|
|
|
|
# d = entryBefore [ "e" ] "4";
|
|
|
|
# e = entryAnywhere "5";
|
2017-05-03 22:36:39 +00:00
|
|
|
# } == {
|
|
|
|
# result = [
|
|
|
|
# { data = "1"; name = "a"; }
|
|
|
|
# { data = "3"; name = "c"; }
|
|
|
|
# { data = "2"; name = "b"; }
|
|
|
|
# { data = "4"; name = "d"; }
|
|
|
|
# { data = "5"; name = "e"; }
|
|
|
|
# ];
|
|
|
|
# }
|
|
|
|
# true
|
|
|
|
#
|
|
|
|
# And the result on error is
|
|
|
|
#
|
|
|
|
# {
|
2022-01-13 17:21:32 +00:00
|
|
|
# cycle = [ { after = ?; name = ?; data = ? } … ];
|
|
|
|
# loops = [ { after = ?; name = ?; data = ? } … ];
|
2017-05-03 22:36:39 +00:00
|
|
|
# }
|
|
|
|
#
|
|
|
|
# For example
|
|
|
|
#
|
2022-01-13 17:21:32 +00:00
|
|
|
# nix-repl> topoSort {
|
|
|
|
# a = entryAnywhere "1";
|
|
|
|
# b = entryAfter [ "a" "c" ] "2";
|
|
|
|
# c = entryAfter [ "d" ] "3";
|
|
|
|
# d = entryAfter [ "b" ] "4";
|
|
|
|
# e = entryAnywhere "5";
|
2017-05-03 22:36:39 +00:00
|
|
|
# } == {
|
|
|
|
# cycle = [
|
2022-01-13 17:21:32 +00:00
|
|
|
# { after = [ "a" "c" ]; data = "2"; name = "b"; }
|
|
|
|
# { after = [ "d" ]; data = "3"; name = "c"; }
|
|
|
|
# { after = [ "b" ]; data = "4"; name = "d"; }
|
2017-05-03 22:36:39 +00:00
|
|
|
# ];
|
|
|
|
# loops = [
|
2022-01-13 17:21:32 +00:00
|
|
|
# { after = [ "a" "c" ]; data = "2"; name = "b"; }
|
2017-05-03 22:36:39 +00:00
|
|
|
# ];
|
2022-01-13 17:21:32 +00:00
|
|
|
# }
|
2017-05-03 22:36:39 +00:00
|
|
|
# true
|
2022-01-13 17:21:32 +00:00
|
|
|
topoSort = dag:
|
2017-05-03 22:36:39 +00:00
|
|
|
let
|
|
|
|
dagBefore = dag: name:
|
2022-01-13 17:21:32 +00:00
|
|
|
builtins.attrNames
|
|
|
|
(filterAttrs (n: v: builtins.elem name v.before) dag);
|
2020-02-01 23:39:17 +00:00
|
|
|
normalizedDag = mapAttrs (n: v: {
|
|
|
|
name = n;
|
|
|
|
data = v.data;
|
|
|
|
after = v.after ++ dagBefore dag n;
|
|
|
|
}) dag;
|
2022-01-13 17:21:32 +00:00
|
|
|
before = a: b: builtins.elem a.name b.after;
|
|
|
|
sorted = toposort before (builtins.attrValues normalizedDag);
|
2020-02-01 23:39:17 +00:00
|
|
|
in if sorted ? result then {
|
|
|
|
result = map (v: { inherit (v) name data; }) sorted.result;
|
|
|
|
} else
|
|
|
|
sorted;
|
2017-05-03 22:36:39 +00:00
|
|
|
|
|
|
|
# Applies a function to each element of the given DAG.
|
2022-01-13 17:21:32 +00:00
|
|
|
map = f: mapAttrs (n: v: v // { data = f n v.data; });
|
2017-05-03 22:36:39 +00:00
|
|
|
|
2022-01-13 17:21:32 +00:00
|
|
|
entryBetween = before: after: data: { inherit data before after; };
|
2017-05-03 22:36:39 +00:00
|
|
|
|
2022-01-13 17:21:32 +00:00
|
|
|
# Create a DAG entry with no particular dependency information.
|
|
|
|
entryAnywhere = hm.dag.entryBetween [ ] [ ];
|
2017-05-03 22:36:39 +00:00
|
|
|
|
2022-01-13 17:21:32 +00:00
|
|
|
entryAfter = hm.dag.entryBetween [ ];
|
|
|
|
entryBefore = before: hm.dag.entryBetween before [ ];
|
2023-06-04 08:53:26 +00:00
|
|
|
|
|
|
|
# Given a list of entries, this function places them in order within the DAG.
|
|
|
|
# Each entry is labeled "${tag}-${entry index}" and other DAG entries can be
|
|
|
|
# added with 'before' or 'after' referring these indexed entries.
|
|
|
|
#
|
|
|
|
# The entries as a whole can be given a relation to other DAG nodes. All
|
|
|
|
# generated nodes are then placed before or after those dependencies.
|
|
|
|
entriesBetween = tag:
|
|
|
|
let
|
|
|
|
go = i: before: after: entries:
|
|
|
|
let
|
|
|
|
name = "${tag}-${toString i}";
|
|
|
|
i' = i + 1;
|
|
|
|
in if entries == [ ] then
|
|
|
|
hm.dag.empty
|
|
|
|
else if length entries == 1 then {
|
|
|
|
"${name}" = hm.dag.entryBetween before after (head entries);
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
"${name}" = hm.dag.entryAfter after (head entries);
|
|
|
|
} // go (i + 1) before [ name ] (tail entries);
|
|
|
|
in go 0;
|
|
|
|
|
|
|
|
entriesAnywhere = tag: hm.dag.entriesBetween tag [ ] [ ];
|
|
|
|
entriesAfter = tag: hm.dag.entriesBetween tag [ ];
|
|
|
|
entriesBefore = tag: before: hm.dag.entriesBetween tag before [ ];
|
2017-05-03 22:36:39 +00:00
|
|
|
}
|