mirror of
https://github.com/nix-community/dream2nix.git
synced 2024-12-23 14:31:55 +03:00
297 lines
7.1 KiB
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
|