Add basic directed acyclic graph data structure
Also make use of this instead of Nixpkgs's strings-with-deps library in activation script generation.
This commit is contained in:
parent
62a9a8fa3c
commit
8fab2a5d9b
|
@ -1,6 +1,7 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
with import ./lib/dag.nix;
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
||||||
|
@ -241,7 +242,11 @@ in
|
||||||
//
|
//
|
||||||
(maybeSet "LC_TIME" cfg.language.time);
|
(maybeSet "LC_TIME" cfg.language.time);
|
||||||
|
|
||||||
home.activation.linkGeneration =
|
# A dummy entry acting as a boundary between the activation
|
||||||
|
# script's "check" and the "write" phases.
|
||||||
|
home.activation.writeBoundary = dagEntryAnywhere "";
|
||||||
|
|
||||||
|
home.activation.linkGeneration = dagEntryAfter ["writeBoundary"] (
|
||||||
let
|
let
|
||||||
link = pkgs.writeText "link" ''
|
link = pkgs.writeText "link" ''
|
||||||
newGenFiles="$1"
|
newGenFiles="$1"
|
||||||
|
@ -303,27 +308,26 @@ in
|
||||||
else
|
else
|
||||||
echo "Same home files as previous generation ... doing nothing"
|
echo "Same home files as previous generation ... doing nothing"
|
||||||
fi
|
fi
|
||||||
'';
|
''
|
||||||
|
);
|
||||||
|
|
||||||
home.activation.installPackages =
|
home.activation.installPackages = dagEntryAfter ["writeBoundary"] ''
|
||||||
''
|
$DRY_RUN_CMD nix-env -i ${cfg.path}
|
||||||
$DRY_RUN_CMD nix-env -i ${cfg.path}
|
'';
|
||||||
'';
|
|
||||||
|
|
||||||
home.activationPackage =
|
home.activationPackage =
|
||||||
let
|
let
|
||||||
addHeader = n: v:
|
mkCmd = res: ''
|
||||||
v // {
|
echo Activating ${res.name}
|
||||||
text = ''
|
${res.data}
|
||||||
echo Activating ${n}
|
'';
|
||||||
${v.text}
|
sortedCommands = dagTopoSort cfg.activation;
|
||||||
'';
|
|
||||||
};
|
|
||||||
toDepString = n: v: if isString v then noDepEntry v else v;
|
|
||||||
activationWithDeps =
|
|
||||||
mapAttrs addHeader (mapAttrs toDepString cfg.activation);
|
|
||||||
activationCmds =
|
activationCmds =
|
||||||
textClosureMap id activationWithDeps (attrNames activationWithDeps);
|
if sortedCommands ? result then
|
||||||
|
concatStringsSep "\n" (map mkCmd sortedCommands.result)
|
||||||
|
else
|
||||||
|
abort ("Dependency cycle in activation script: "
|
||||||
|
+ builtins.toJSON sortedCommands);
|
||||||
|
|
||||||
sf = pkgs.writeText "activation-script" ''
|
sf = pkgs.writeText "activation-script" ''
|
||||||
#!${pkgs.stdenv.shell}
|
#!${pkgs.stdenv.shell}
|
||||||
|
|
124
modules/lib/dag.nix
Normal file
124
modules/lib/dag.nix
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
# 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,
|
||||||
|
#
|
||||||
|
# - the addition of the function `dagEntryBefore` indicating a
|
||||||
|
# "wanted by" relationship.
|
||||||
|
|
||||||
|
with import <nixpkgs/lib/strings.nix>;
|
||||||
|
with import <nixpkgs/lib/attrsets.nix>;
|
||||||
|
with import <nixpkgs/lib/lists.nix>;
|
||||||
|
|
||||||
|
rec {
|
||||||
|
|
||||||
|
emptyDag = {};
|
||||||
|
|
||||||
|
isDag = dag:
|
||||||
|
let
|
||||||
|
isEntry = e: (e ? data) && (e ? after) && (e ? before);
|
||||||
|
in
|
||||||
|
builtins.isAttrs dag && all (x: x) (mapAttrsToList (n: isEntry) dag);
|
||||||
|
|
||||||
|
# Takes an attribute set containing entries built by
|
||||||
|
# dagEntryAnywhere, dagEntryAfter, and dagEntryBefore to a
|
||||||
|
# topologically sorted list of entries.
|
||||||
|
#
|
||||||
|
# Internally this function uses the `toposort` function in
|
||||||
|
# `<nixpkgs/lib/lists.nix>` and its value is accordingly.
|
||||||
|
#
|
||||||
|
# Specifically, the result on success is
|
||||||
|
#
|
||||||
|
# { result = [{name = ?; data = ?;} …] }
|
||||||
|
#
|
||||||
|
# For example
|
||||||
|
#
|
||||||
|
# nix-repl> dagTopoSort {
|
||||||
|
# a = dagEntryAnywhere "1";
|
||||||
|
# b = dagEntryAfter ["a" "c"] "2";
|
||||||
|
# c = dagEntryBefore ["d"] "3";
|
||||||
|
# d = dagEntryBefore ["e"] "4";
|
||||||
|
# e = dagEntryAnywhere "5";
|
||||||
|
# } == {
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
# {
|
||||||
|
# cycle = [ {after = ?; name = ?; data = ?} … ];
|
||||||
|
# loops = [ {after = ?; name = ?; data = ?} … ];
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# For example
|
||||||
|
#
|
||||||
|
# nix-repl> dagTopoSort {
|
||||||
|
# a = dagEntryAnywhere "1";
|
||||||
|
# b = dagEntryAfter ["a" "c"] "2";
|
||||||
|
# c = dagEntryAfter ["d"] "3";
|
||||||
|
# d = dagEntryAfter ["b"] "4";
|
||||||
|
# e = dagEntryAnywhere "5";
|
||||||
|
# } == {
|
||||||
|
# cycle = [
|
||||||
|
# { after = ["a" "c"]; data = "2"; name = "b"; }
|
||||||
|
# { after = ["d"]; data = "3"; name = "c"; }
|
||||||
|
# { after = ["b"]; data = "4"; name = "d"; }
|
||||||
|
# ];
|
||||||
|
# loops = [
|
||||||
|
# { after = ["a" "c"]; data = "2"; name = "b"; }
|
||||||
|
# ];
|
||||||
|
# } == {}
|
||||||
|
# true
|
||||||
|
dagTopoSort = dag:
|
||||||
|
let
|
||||||
|
dagBefore = dag: name:
|
||||||
|
mapAttrsToList (n: v: n) (
|
||||||
|
filterAttrs (n: v: any (a: a == name) v.before) dag
|
||||||
|
);
|
||||||
|
normalizedDag =
|
||||||
|
mapAttrs (n: v: {
|
||||||
|
name = n;
|
||||||
|
data = v.data;
|
||||||
|
after = v.after ++ dagBefore dag n;
|
||||||
|
}) dag;
|
||||||
|
before = a: b: any (c: a.name == c) b.after;
|
||||||
|
sorted = toposort before (mapAttrsToList (n: v: v) normalizedDag);
|
||||||
|
in
|
||||||
|
if sorted ? result then
|
||||||
|
{ result = map (v: { inherit (v) name data; }) sorted.result; }
|
||||||
|
else
|
||||||
|
sorted;
|
||||||
|
|
||||||
|
# Applies a function to each element of the given DAG.
|
||||||
|
dagMap = f: dag: mapAttrs (n: v: v // { data = f n v.data; }) dag;
|
||||||
|
|
||||||
|
# Create a DAG entry with no particular dependency information.
|
||||||
|
dagEntryAnywhere = data: {
|
||||||
|
inherit data;
|
||||||
|
before = [];
|
||||||
|
after = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
dagEntryBetween = before: after: data: {
|
||||||
|
inherit data before after;
|
||||||
|
};
|
||||||
|
|
||||||
|
dagEntryAfter = after: data: {
|
||||||
|
inherit data after;
|
||||||
|
before = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
dagEntryBefore = before: data: {
|
||||||
|
inherit data before;
|
||||||
|
after = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
with import ../lib/dag.nix;
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
||||||
|
@ -177,18 +178,19 @@ in
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
home.packages = [ pkgs.gnome3.gnome_terminal ];
|
home.packages = [ pkgs.gnome3.gnome_terminal ];
|
||||||
|
|
||||||
home.activation.gnomeTerminal =
|
# The dconf service needs to be installed and prepared.
|
||||||
|
home.activation.gnomeTerminal = dagEntryAfter ["installPackages"] (
|
||||||
let
|
let
|
||||||
sf = pkgs.writeText "gnome-terminal.ini" (toINI (buildIniSet cfg));
|
sf = pkgs.writeText "gnome-terminal.ini" (toINI (buildIniSet cfg));
|
||||||
dconfPath = "/org/gnome/terminal/legacy/";
|
dconfPath = "/org/gnome/terminal/legacy/";
|
||||||
in
|
in
|
||||||
# The dconf service needs to be installed and prepared.
|
''
|
||||||
stringAfter [ "installPackages" ] ''
|
|
||||||
if [[ -v DRY_RUN ]]; then
|
if [[ -v DRY_RUN ]]; then
|
||||||
echo ${pkgs.gnome3.dconf}/bin/dconf load ${dconfPath} "<" ${sf}
|
echo ${pkgs.gnome3.dconf}/bin/dconf load ${dconfPath} "<" ${sf}
|
||||||
else
|
else
|
||||||
${pkgs.gnome3.dconf}/bin/dconf load ${dconfPath} < ${sf}
|
${pkgs.gnome3.dconf}/bin/dconf load ${dconfPath} < ${sf}
|
||||||
fi
|
fi
|
||||||
'';
|
''
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
with import ./lib/dag.nix;
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
||||||
|
@ -69,7 +70,7 @@ in
|
||||||
(buildServices "timer" config.systemd.user.timers)
|
(buildServices "timer" config.systemd.user.timers)
|
||||||
);
|
);
|
||||||
|
|
||||||
home.activation.reloadSystemD = ''
|
home.activation.reloadSystemD = dagEntryAfter ["linkGeneration"] ''
|
||||||
function systemdPostReload() {
|
function systemdPostReload() {
|
||||||
local workDir
|
local workDir
|
||||||
workDir="$(mktemp -d)"
|
workDir="$(mktemp -d)"
|
||||||
|
|
Loading…
Reference in a new issue