dream2nix/lib/internal/simpleTranslate2.nix

297 lines
7.1 KiB
Nix

{lib, ...}: let
l = builtins // lib;
nameVersionPair = name: version: {inherit name version;};
expectedFields = [
"name"
"version"
"sourceSpec"
"dependencies"
];
mkFinalObjects = rawObjects: extractors:
l.map
(rawObj: let
finalObj =
{inherit rawObj;}
// l.mapAttrs
(key: extractFunc: extractFunc rawObj finalObj)
extractors;
in
finalObj)
rawObjects;
# checks validity of all objects by iterating over them
mkValidatedFinalObjects = finalObjects: translatorName: extraObjects:
l.map
(finalObj:
if l.any (field: ! finalObj ? "${field}") expectedFields
then
throw
''
Translator ${translatorName} failed.
The following object does not contain all required fields:
Object:
${l.toJSON finalObj}
Missing fields:
${l.subtractLists expectedFields (l.attrNames finalObj)}
''
# TODO: validate sourceSpec as well
else finalObj)
(finalObjects ++ extraObjects);
mkExportedFinalObjects = finalObjects: exportedPackages:
l.filter
(finalObj:
exportedPackages.${finalObj.name} or null == finalObj.version)
finalObjects;
mkRelevantFinalObjects = exportedFinalObjects: allDependencies:
l.genericClosure {
startSet =
l.map
(finalObj:
finalObj
// {key = "${finalObj.name}#${finalObj.version}";})
exportedFinalObjects;
operator = finalObj:
l.map
(c:
allDependencies.${c.name}.${c.version}
// {key = "${c.name}#${c.version}";})
finalObj.dependencies;
};
/*
format:
{
foo = {
"1.0.0" = finalObj
}
}
*/
makeDependencies = finalObjects:
l.foldl'
(result: finalObj:
l.recursiveUpdate
result
{
"${finalObj.name}" = {
"${finalObj.version}" = finalObj;
};
})
{}
finalObjects;
translate = func: let
final =
func
{
inherit objectsByKey;
};
rawObjects = final.serializedRawObjects;
finalObjects' = mkFinalObjects rawObjects final.extractors;
objectsByKey =
l.mapAttrs
(key: keyFunc:
l.foldl'
(merged: finalObj:
merged
// {"${keyFunc finalObj.rawObj finalObj}" = finalObj;})
{}
finalObjects')
final.keys;
dreamLockData = magic final;
magic = {
defaultPackage,
exportedPackages,
extractors,
extraObjects ? [],
keys ? {},
location ? "",
serializedRawObjects,
subsystemName,
subsystemAttrs ? {},
translatorName,
}: let
inputs = {
inherit
defaultPackage
exportedPackages
extractors
extraObjects
keys
location
serializedRawObjects
subsystemName
subsystemAttrs
translatorName
;
};
finalObjects =
mkValidatedFinalObjects
finalObjects'
translatorName
(final.extraObjects or []);
allDependencies = makeDependencies finalObjects;
exportedFinalObjects =
mkExportedFinalObjects finalObjects exportedPackages;
relevantFinalObjects =
mkRelevantFinalObjects exportedFinalObjects allDependencies;
relevantDependencies = makeDependencies relevantFinalObjects;
sources =
l.mapAttrs
(name: versions: let
# Filter out all `path` sources which link to store paths.
# The removed sources can be added back via source override later
filteredObjects =
l.filterAttrs
(version: finalObj:
(finalObj.sourceSpec.type != "path")
|| ! l.isStorePath (l.removeSuffix "/" finalObj.sourceSpec.path))
versions;
in
l.mapAttrs
(version: finalObj: finalObj.sourceSpec)
filteredObjects)
relevantDependencies;
dependencyGraph =
l.mapAttrs
(name: versions:
l.mapAttrs
(version: finalObj: finalObj.dependencies)
versions)
relevantDependencies;
cyclicDependencies =
if dependencyGraph == {}
then {}
else cyclicDependencies';
cyclicDependencies' =
# TODO: inefficient! Implement some kind of early cutoff
let
depGraphWithFakeRoot =
l.recursiveUpdate
dependencyGraph
{
__fake-entry.__fake-version =
l.mapAttrsToList
nameVersionPair
exportedPackages;
};
findCycles = node: prevNodes: cycles: let
children =
depGraphWithFakeRoot."${node.name}"."${node.version}";
cyclicChildren =
l.filter
(child: prevNodes ? "${child.name}#${child.version}")
children;
nonCyclicChildren =
l.filter
(child: ! prevNodes ? "${child.name}#${child.version}")
children;
cycles' =
cycles
++ (l.map (child: {
from = node;
to = child;
})
cyclicChildren);
# use set for efficient lookups
prevNodes' =
prevNodes
// {"${node.name}#${node.version}" = null;};
in
if nonCyclicChildren == []
then cycles'
else
l.flatten
(l.map
(child: findCycles child prevNodes' cycles')
nonCyclicChildren);
cyclesList =
findCycles
(
nameVersionPair
"__fake-entry"
"__fake-version"
)
{}
[];
in
l.foldl'
(cycles: cycle: (
let
existing =
cycles."${cycle.from.name}"."${cycle.from.version}"
or [];
reverse =
cycles."${cycle.to.name}"."${cycle.to.version}"
or [];
in
# if edge or reverse edge already in cycles, do nothing
if
l.elem cycle.from reverse
|| l.elem cycle.to existing
then cycles
else
l.recursiveUpdate
cycles
{
"${cycle.from.name}"."${cycle.from.version}" =
existing ++ [cycle.to];
}
))
{}
cyclesList;
data =
{
decompressed = true;
_generic = {
inherit
defaultPackage
location
;
packages = exportedPackages;
subsystem = subsystemName;
sourcesAggregatedHash = null;
};
# build system specific attributes
_subsystem = subsystemAttrs;
inherit cyclicDependencies sources;
}
// {dependencies = dependencyGraph;};
in {
inherit data;
inherit inputs;
};
in
dreamLockData.data;
in
translate