dream2nix/lib/internal/graphUtils.nix
2024-01-19 12:02:18 +00:00

173 lines
4.1 KiB
Nix

/*
A collection of tools needed to interact with graphs (i.e. A dependencyTree)
*/
{lib, ...}: let
l = lib // builtins;
# debugMsg = msg: val: builtins.trace "${msg} ${(builtins.toJSON val)}" val;
/*
Internal function. sanitize a potentially cyclic graph. Returns all items interconnected but with at least connections as possible.
# Params
- Graph :: {
${name}.${version} :: {
dependencies = {
${dep.name}.version = String;
};
dev :: Bool;
};
};
GenericGraph; An AttrSet of nodeIds, pointing to neighboring nodes. (could be cyclic).
- roots :: [ String ]
A list of root ids
# Returns
(inverted) GenericGraph without cycles.
GenericGraph :: [ { key :: String; parent :: String; } ]
*/
# returns
/*
[
{
name :: String;
version :: String;
// True if the parent requires as dev dependency
dev :: Bool;
isRoot? :: true;
// Needed for generic closure
key :: [ name version];
}
]
*/
# TODO(hsjobeki): inherit dev attribute lazy
sanitizeGraph = {
graph,
root,
pred ? null,
}:
l.genericClosure {
startSet = [
{
key = [root.name root.version];
inherit (root) name version;
isRoot = true;
}
];
operator = {key, ...} @ prev: let
parentName = builtins.elemAt prev.key 0;
parentVersion = builtins.elemAt prev.key 1;
results =
l.mapAttrsToList (
name: depEntry: {
key = [name depEntry.version];
inherit name;
inherit (depEntry) version;
inherit (graph.${name}.${depEntry.version}) dev;
parent = {
name = parentName;
version = parentVersion;
};
}
)
graph.${parentName}.${parentVersion}.dependencies;
in
l.filter (
entry:
if l.isFunction pred
then pred entry && true
else true
)
results;
# if l.isFunction pred
# then l.filter (entry: pred entry) results
# else results;
};
/*
Function that returns instructions to create the file system (aka. node_modules directory)
Every `source` entry here is created. Bins are symlinked to their target.
This behavior is implemented via the prepared-builder script.
@argument pdefs'
# The filtered and sanititized pdefs containing no cycles.
# Only pdefs required by the current root and environment.
# e.g. all buildtime dependencies of top-level package.
[]
->
fileSystem :: {
"node_modules/typescript": {
source: <derivation typescript-dist>
bins: {
"node_modules/.bin/tsc": "node_modules/typescript/bin/tsc"
}
}
}
*/
getFileSystem = graph: sanitizedGraph:
l.foldl' (
/*
sanitziedGraphEntry :: {
name ::
version ::
dev ::
isRoot ? :: true;
key :: [];
}
*/
res: sanitizedGraphEntry: let
/*
filteredSet :: graph
Example:
All versions of "next" that are in the dist state.
*/
distVersions = l.filterAttrs (_version: e: e.info.initialState == "dist") graph.${sanitizedGraphEntry.name};
in
res
// l.foldlAttrs (
acc: version: entry: let
pdef = graph.${sanitizedGraphEntry.name}.${version};
in
acc
// l.foldlAttrs (fileSystem: path: pathInfo:
if pathInfo
then
fileSystem
// getFileSystemInfo path pdef entry
else fileSystem) {} (entry.info.allPaths)
) {}
distVersions
) {}
sanitizedGraph;
getFileSystemInfo = path: pdef: entry: let
info = {
${path} = {
source = entry.dist;
bins =
l.mapAttrs' (name: target: {
name = (builtins.dirOf path) + "/.bin/" + name;
value = path + "/" + target;
})
pdef.bins;
};
};
in
info;
in {
inherit
sanitizeGraph
getFileSystem
;
}