new builder interface:

- dream lock interface via utils.readDreamLock
  - improve granular builder for nodejs
  - support fetching urls with sha1 hash
  - add jsonschema for source type path
This commit is contained in:
DavHau 2021-10-23 18:50:16 +07:00
parent 2db704be87
commit 70cf6c2723
13 changed files with 424 additions and 170 deletions

View File

@ -316,9 +316,12 @@ class PackageCommand(Command):
removed_cycles_text = 'Removed Cyclic dependencies:'
for node, removed_node in removed_edges:
removed_cycles_text += f"\n {node} -> {removed_node}"
if node not in lock['generic']['dependenciesRemoved']:
lock['generic']['dependenciesRemoved'][node] = []
lock['generic']['dependenciesRemoved'][node].append(removed_node)
n_name, n_ver = node.split('#')
if n_name not in lock['generic']['dependenciesRemoved']:
lock['generic']['dependenciesRemoved'][n_name] = {}
if n_ver not in lock['generic']['dependenciesRemoved'][n_name]:
lock['generic']['dependenciesRemoved'][n_name][n_ver] = []
lock['generic']['dependenciesRemoved'][n_name][n_ver].append(removed_node)
print(removed_cycles_text)
# calculate combined hash if --combined was specified

View File

@ -14,59 +14,41 @@
}:
{
fetchedSources,
dreamLock,
# funcs
getDependencies,
getSource,
buildPackageWithOtherBuilder,
# attributes
buildSystemAttrs,
dependenciesRemoved,
mainPackageName,
mainPackageVersion,
packageVersions,
# overrides
packageOverrides ? {},
# custom opts:
standalonePackageNames ? [],
...
}@args:
let
b = builtins;
dreamLock = utils.readDreamLock { inherit (args) dreamLock; };
inherit (dreamLock.generic) mainPackageName mainPackageVersion;
dependencyGraph = dreamLock.generic.dependencyGraph;
standAlonePackages =
let
standaloneNames = standalonePackageNames ++ (lib.attrNames dreamLock.generic.dependenciesRemoved);
standaloneKeys =
lib.filter
(key: lib.any (sName: lib.hasPrefix sName key) standaloneNames)
(lib.attrNames dependencyGraph);
in
lib.genAttrs standaloneKeys
(key:
(builders.nodejs.node2nix (args // {
dreamLock =
let
nameVer = lib.splitString "#" key;
name = b.elemAt nameVer 0;
version = b.elemAt nameVer 1;
in
lib.recursiveUpdate dreamLock
{
generic.mainPackagenName = name;
generic.mainPackagenVersion = version;
# re-introduce removed dependencies
generic.dependencyGraph."${key}" =
# b.trace "re-introduce removed ${b.toString dreamLock.generic.dependenciesRemoved."${key}" or []}"
dreamLock.generic.dependencyGraph."${key}"
++ dreamLock.generic.dependenciesRemoved."${key}" or [];
};
})).package
);
isCyclic = name: version:
b.elem name standalonePackageNames
||
(dependenciesRemoved ? "${name}"
&& dependenciesRemoved."${name}" ? "${version}");
mainPackageKey =
"${mainPackageName}#${mainPackageVersion}";
nodejsVersion = dreamLock.buildSystem.nodejsVersion;
nodejsVersion = buildSystemAttrs.nodejsVersion;
nodejs =
pkgs."nodejs-${builtins.toString nodejsVersion}_x"
@ -78,20 +60,31 @@ let
'';
allPackages =
lib.genAttrs
(lib.attrNames fetchedSources)
(key:
let
split = lib.splitString "#" key;
name = b.elemAt split 0;
version = b.elemAt split 1;
in
makePackage name version);
lib.mapAttrs
(name: versions:
lib.genAttrs
versions
(version:
if isCyclic name version then
makeCombinedPackage name version
else
makePackage name version))
packageVersions;
makeCombinedPackage = name: version:
# b.trace name
(buildPackageWithOtherBuilder
builders.nodejs.node2nix
name
version).package;
makePackage = name: version:
let
pkgKey = "${name}#${version}";
deps = getDependencies name version;
pkg =
# b.trace (lib.attrNames allPackages)
(stdenv.mkDerivation rec {
packageName = name;
@ -100,7 +93,7 @@ let
inherit version;
src = fetchedSources."${pkgKey}";
src = getSource name version;
buildInputs = [ nodejs nodejs.python ];
@ -108,28 +101,17 @@ let
inherit nodeSources;
dependencies_json = writeText "dependencies.json"
dependencies_json = writeText "dependencies.json"
(b.toJSON
(lib.listToAttrs
(b.map
(pKey:
let
split = lib.splitString "#" pKey;
pname = b.elemAt split 0;
version = b.elemAt split 1;
in
lib.nameValuePair pname version)
dreamLock.generic.dependencyGraph."${pkgKey}" or [])));
(dep: lib.nameValuePair dep.name dep.version)
deps)));
nodeDeps =
lib.forEach
(dependencyGraph."${pkgKey}" or [])
(depKey:
allPackages."${depKey}"
)
++
lib.forEach (dreamLock.generic.dependenciesRemoved."${pkgKey}" or [])
(removedDep: standAlonePackages."${pkgKey}");
deps
(dep: allPackages."${dep.name}"."${dep.version}" );
dontUnpack = true;
@ -196,7 +178,7 @@ let
cd "$nodeModules/${packageName}"
# fix package.json malformed dependency versions
# fix malformed dependency versions in package.json
python ${./fix-package-lock.py} $dependencies_json package.json
export HOME=$TMPDIR
@ -232,11 +214,9 @@ let
'';
});
in
standAlonePackages."${name}#${version}"
or
(utils.applyOverridesToPackage packageOverrides pkg name);
package = makePackage mainPackageName mainPackageVersion;
package = allPackages."${mainPackageName}"."${mainPackageVersion}";
in
{

View File

@ -12,52 +12,55 @@
}:
{
fetchedSources,
dreamLock,
buildSystemAttrs,
dependenciesRemoved,
mainPackageName,
mainPackageVersion,
getDependencies,
getSource,
...
}@args:
let
b = builtins;
dreamLock = utils.readDreamLock { inherit (args) dreamLock; };
getDependencies = name: version:
if dependenciesRemoved ? "${name}" && dependenciesRemoved."${name}" ? "${version}" then
dependenciesRemoved."${name}"."${version}" ++ (args.getDependencies name version)
else
args.getDependencies name version;
mainPackageName = dreamLock.generic.mainPackageName;
mainPackageVersion = dreamLock.generic.mainPackageVersion;
mainPackageKey = "${mainPackageName}#${mainPackageVersion}";
nodejsVersion = dreamLock.buildSystem.nodejsVersion;
mainPackageDependencies = getDependencies mainPackageName mainPackageVersion;
nodejsVersion = buildSystemAttrs.nodejsVersion;
nodejs =
pkgs."nodejs-${builtins.toString nodejsVersion}_x"
or (throw "Could not find nodejs version '${nodejsVersion}' in pkgs");
node2nixEnv = node2nix nodejs;
node2nixDependencies =
let
makeSource = name:
let
nameVer = lib.splitString "#" name;
packageName = lib.elemAt nameVer 0;
version = lib.elemAt nameVer 1;
in
{
inherit packageName version;
name = utils.sanitizeDerivationName packageName;
src = fetchedSources."${name}";
dependencies =
lib.forEach
(lib.filter
(depName: ! builtins.elem depName dreamLock.generic.dependencyGraph."${mainPackageKey}")
(dreamLock.generic.dependencyGraph."${name}" or []))
(dependency:
makeSource dependency
);
};
makeSource = packageName: version:
{
inherit packageName version;
name = utils.sanitizeDerivationName packageName;
src = getSource packageName version;
dependencies =
lib.forEach
(lib.filter
(dep: ! builtins.elem dep mainPackageDependencies)
(getDependencies packageName version))
(dep:
makeSource dep.name dep.version
);
};
in
lib.forEach
dreamLock.generic.dependencyGraph."${mainPackageKey}"
(dependency: makeSource dependency);
mainPackageDependencies
(dep: makeSource dep.name dep.version);
callNode2Nix = funcName: args:
node2nixEnv."${funcName}" rec {
@ -76,7 +79,7 @@ let
production = true;
bypassCache = true;
reconstructLock = true;
src = fetchedSources."${mainPackageKey}";
src = getSource mainPackageName mainPackageVersion;
}
// args;

View File

@ -94,48 +94,39 @@ rec {
else
fetchers.defaultFetcher;
# automatically parse dream.lock if passed as file
parseLock = lock:
if builtins.isPath lock || builtins.isString lock then
builtins.fromJSON (builtins.readFile lock)
else
lock;
# fetch only sources and do not build
fetchSources =
{
dreamLock,
fetcher ? findFetcher (parseLock dreamLock),
sourceOverrides ? oldSources: {},
}:
fetcher ? null,
extract ? false,
}@args:
let
# if generic lock is a file, read and parse it
dreamLock' = (parseLock dreamLock);
dreamLock' = (utils.readDreamLock { inherit dreamLock; }).lock;
fetcher =
if args.fetcher == null then
findFetcher dreamLock'
else
args.fetcher;
fetched = fetcher {
sources = dreamLock'.sources;
sourcesCombinedHash = dreamLock'.generic.sourcesCombinedHash;
};
sourcesToReplace = sourceOverrides fetched.fetchedSources;
sourcesOverridden = lib.mapAttrs (pname: source:
sourcesToReplace."${pname}" or source
) fetched.fetchedSources;
sourcesEnsuredOverridden = lib.mapAttrs (pname: source:
if source == "unknown" then throw ''
Source '${pname}' is unknown. Please override using:
dream2nix.buildPackage {
...
sourceOverrides = oldSources: {
"${pname}" = ...;
};
...
};
''
else source
) sourcesOverridden;
fetchedSources = fetched.fetchedSources;
in
fetched // {
fetchedSources = sourcesEnsuredOverridden;
fetchedSources =
if extract then
lib.mapAttrs
(key: source: utils.extractSource { inherit source; })
fetchedSources
else
fetchedSources;
};
@ -178,31 +169,103 @@ rec {
} // argsForRise)).package;
# build a dream lock via a specific builder
callBuilder =
{
builder,
builderArgs,
fetchedSources,
dreamLock,
dreamLockInterface,
sourceOverrides,
}@args:
let
fetchedSources =
args.fetchedSources // (sourceOverrides args.fetchedSources);
in
builder {
inherit dreamLock;
inherit (dreamLockInterface)
buildSystemAttrs
dependenciesRemoved
getDependencies
mainPackageName
mainPackageVersion
packageVersions
;
getSource = utils.dreamLock.getSource fetchedSources;
buildPackageWithOtherBuilder = builder: name: version:
let
subDreamLockLoaded =
utils.readDreamLock {
dreamLock =
utils.dreamLock.getSubDreamLock dreamLock name version;
};
filteredFetchedSources =
utils.dreamLock.filterFetchedSources
args.fetchedSources subDreamLockLoaded.lock;
newFetchedSources =
filteredFetchedSources // (sourceOverrides filteredFetchedSources);
in
callBuilder rec {
inherit builder builderArgs sourceOverrides;
dreamLock =
subDreamLockLoaded.lock;
dreamLockInterface = subDreamLockLoaded.interface;
fetchedSources = newFetchedSources;
};
};
# build package defined by dream.lock
riseAndShine =
{
dreamLock,
builder ? findBuilder (parseLock dreamLock),
fetcher ? findFetcher (parseLock dreamLock),
builder ? null,
fetcher ? null,
sourceOverrides ? oldSources: {},
packageOverrides ? {},
builderArgs ? {},
}@args:
let
# if generic lock is a file, read and parse it
dreamLock' = (parseLock dreamLock);
dreamLockLoaded = utils.readDreamLock { inherit (args) dreamLock; };
dreamLock = dreamLockLoaded.lock;
dreamLockInterface = dreamLockLoaded.interface;
builderOutputs = builder (
{
dreamLock = dreamLock';
fetchedSources = (fetchSources {
inherit dreamLock fetcher sourceOverrides;
}).fetchedSources;
}
// builderArgs
// lib.optionalAttrs (packageOverrides != {}) {
builder' =
if builder == null then
findBuilder dreamLock
else
builder;
fetcher' =
if fetcher == null then
findFetcher dreamLock
else
fetcher;
fetchedSources = (fetchSources {
inherit dreamLock;
fetcher = fetcher';
}).fetchedSources;
builderOutputs = callBuilder {
inherit dreamLock dreamLockInterface fetchedSources sourceOverrides;
builder = builder';
builderArgs = args.builderArgs // {
inherit packageOverrides;
});
};
};
in
builderOutputs;

View File

@ -12,8 +12,10 @@
...
}:
{
# attrset: pname -> path of downloaded source
let
b = builtins;
fetchedSources =
lib.listToAttrs
(lib.flatten
@ -32,6 +34,10 @@
)
versions
)
sources))
;
sources));
in
{
# attrset: pname -> path of downloaded source
inherit fetchedSources;
}

View File

@ -1,4 +1,5 @@
{
lib,
fetchurl,
utils,
@ -21,9 +22,15 @@
});
fetched = hash:
fetchurl {
inherit url hash;
};
if lib.stringLength hash == 40 then
fetchurl {
inherit url;
sha1 = hash;
}
else
fetchurl {
inherit url hash;
};
};
}

View File

@ -20,6 +20,7 @@
"git",
"github",
"gitlab",
"path",
"pypi-sdist"
]
}
@ -75,6 +76,19 @@
"additionalProperties": false
}
},
{
"if": {
"properties": { "type": { "const": "path" } }
},
"then": {
"properties": {
"path": { "type": "string" },
"type": { "type": "string" }
},
"required": ["type", "path"],
"additionalProperties": false
}
},
{
"if": {
"properties": { "type": { "const": "pypi-sdist" } }

View File

@ -120,7 +120,7 @@
};
};
getDependencies = dependencyObject: getDepByNameVer: getDepByOriginalID:
getDependencies = dependencyObject: getDepByNameVer: dependenciesByOriginalID:
dependencyObject.depsExact;
};

View File

@ -39,8 +39,8 @@
utils.simpleTranslate translatorName rec {
inputData = parsedLock;
mainPackageName = packageJSON.name;
mainPackageVersion = packageJSON.version;
mainPackageName = packageJSON.name or "unknown";
mainPackageVersion = packageJSON.version or "unknown";
buildSystemName = "nodejs";
buildSystemAttrs = {
@ -88,7 +88,7 @@
getVersion = dependencyObject:
dependencyObject.version;
getDependencies = dependencyObject: getDepByNameVer: getDepByOriginalID:
getDependencies = dependencyObject: getDepByNameVer: dependenciesByOriginalID:
let
dependencies =
dependencyObject.dependencies or []
@ -102,10 +102,19 @@
(name: value:
let
yarnName = "${name}@${value}";
depObject = getDepByOriginalID yarnName;
depObject = dependenciesByOriginalID."${yarnName}";
version = depObject.version;
in
{ inherit name version; }
if ! dependenciesByOriginalID ? ${yarnName} then
let
yarnNameSplit = lib.splitString "@" yarnName;
in
{
name = b.elemAt yarnNameSplit 0;
version = b.elemAt yarnNameSplit 1;
}
else
{ inherit name version; }
)
dependency
)
@ -113,7 +122,9 @@
getSourceType = dependencyObject:
if lib.hasInfix "@github:" dependencyObject.yarnName
|| lib.hasInfix "codeload.github.com/" dependencyObject.resolved then
||
(dependencyObject ? resolved
&& lib.hasInfix "codeload.github.com/" dependencyObject.resolved ) then
if dependencyObject ? integrity then
b.trace "Warning: Using git despite integrity exists for ${getName dependencyObject}"
"git"
@ -152,6 +163,11 @@
hash =
if dependencyObject ? integrity then
dependencyObject.integrity
else
let
hash = lib.last (lib.splitString "#" dependencyObject.resolved);
in
if lib.stringLength hash == 40 then hash
else
throw "Missing integrity for ${dependencyObject.yarnName}";
url = lib.head (lib.splitString "#" dependencyObject.resolved);

View File

@ -23,7 +23,7 @@ let
dreamLock,
}:
let
lock = utils.readDreamLock { inherit dreamLock; };
lock = (utils.readDreamLock { inherit dreamLock; }).lock;
source = lockUtils.getMainPackageSource lock;
in
lock.updater
@ -36,7 +36,7 @@ let
updater ? getUpdaterName { inherit dreamLock; },
}:
let
lock = utils.readDreamLock { inherit dreamLock; };
lock = (utils.readDreamLock { inherit dreamLock; }).lock;
source = lockUtils.getMainPackageSource lock;
updater' = updaters."${updater}";
in

View File

@ -94,7 +94,7 @@ rec {
}:
# fetchzip can extract tarballs as well
(fetchzip { url="file:${source}"; }).overrideAttrs (old: {
name = "${source.name}-extracted";
name = "${(source.name or "")}extracted";
outputHash = null;
postFetch =
''

View File

@ -11,23 +11,188 @@ let
dreamLock,
}@args:
let
lock =
if b.isPath dreamLock || b.isString dreamLock then
b.fromJSON (b.readFile dreamLock)
else
dreamLock;
mainPackage = lock.generic.mainPackage;
buildSystemAttrs = lock.buildSystem;
sources = lock.sources;
dependencyGraph = lock.generic.dependencyGraph;
packageVersions =
lib.mapAttrs
(pname: versions: lib.attrNames versions)
sources;
dependenciesRemoved =
lib.mapAttrs
(name: versions:
lib.mapAttrs
(version: removedKeys:
lib.forEach removedKeys
(rKey:
let
split = lib.splitString "#" rKey;
name = b.elemAt split 0;
version = b.elemAt split 1;
in
{ inherit name version; }))
versions)
lock.generic.dependenciesRemoved;
# Format:
# {
# "{name}#{version}": [
# { name=...; version=...; }
# { name=...; version=...; }
# ]
# }
dependenciesAttrs =
lib.mapAttrs
(key: deps:
lib.forEach deps
(dep:
let
split = lib.splitString "#" dep;
name = b.elemAt split 0;
version = b.elemAt split 1;
in
{ inherit name version; }))
dependencyGraph;
getDependencies = pname: version:
if dependenciesAttrs ? "${pname}#${version}" then
dependenciesAttrs."${pname}#${version}"
else
[];
in
lock;
{
inherit lock;
interface = rec {
inherit (lock.generic)
mainPackageName
mainPackageVersion
;
inherit
buildSystemAttrs
dependenciesRemoved
getDependencies
packageVersions
;
};
};
getMainPackageSource = dreamLock:
dreamLock.sources
."${dreamLock.generic.mainPackageName}"
."${dreamLock.generic.mainPackageVersion}";
getSource = fetchedSources: pname: version:
let
key = "${pname}#${version}";
in
if fetchedSources ? "${key}" then
fetchedSources."${key}"
else
throw ''
The source for ${key} is not defined.
This can be fixed via an override. Example:
```
dream2nix.riseAndShine {
...
sourceOverrides = oldSources: {
"${key}" = builtins.fetchurl { ... };
};
...
}
```
'';
# generate standalone dreamLock for a depenndency of an existing dreamLock
getSubDreamLock = dreamLock: name: version:
let
lock = (readDreamLock { inherit dreamLock; }).lock;
allDependneciesOf = key:
let
next = lock.generic.dependencyGraph."${key}" or [];
in
next
++
lib.flatten (b.map allDependneciesOf next);
# use set for efficient lookups
allDependenciesOfMainPackage =
lib.genAttrs (allDependneciesOf "${name}#${version}") (x: null);
newSources =
lib.filterAttrs
(p_name: versions: versions != {})
(lib.mapAttrs
(p_name: versions:
lib.filterAttrs
(p_version: source:
allDependenciesOfMainPackage ? "${p_name}#${p_version}"
|| (name == p_name && version == p_version))
versions)
lock.sources);
newDependencyGraph =
lib.filterAttrs
(key: deps:
let
split = lib.splitString "#" key;
p_name = b.elemAt split 0;
p_version = b.elemAt split 1;
in
allDependenciesOfMainPackage ? "${key}"
||
(name == p_name && version == p_version))
lock.generic.dependencyGraph;
in
lock // {
generic = lock.generic // {
mainPackageName = name;
mainPackageVersion = version;
dependencyGraph = newDependencyGraph;
};
sources = newSources;
};
filterFetchedSources = fetchedSources: dreamLock:
let
lockInterface = (readDreamLock { inherit dreamLock; }).interface;
allDependencyKeys =
lib.flatten
(lib.mapAttrsToList
(name: versions: lib.forEach versions
(v: "${name}#${v}"))
lockInterface.packageVersions);
allDependencyKeysSet = lib.genAttrs allDependencyKeys (x: null);
in
lib.filterAttrs
(key: source: allDependencyKeysSet ? "${key}")
fetchedSources;
in
{
inherit getMainPackageSource readDreamLock;
inherit
getMainPackageSource
filterFetchedSources
getSource
getSubDreamLock
readDreamLock
;
}

View File

@ -42,7 +42,7 @@ let
{}
serializedPackagesList;
allDependenciesByOriginalID = b.foldl'
dependenciesByOriginalID = b.foldl'
(result: pkgData: lib.recursiveUpdate result {
"${getOriginalID pkgData}" = pkgData;
})
@ -79,10 +79,7 @@ let
"${getName pkgData}#${getVersion pkgData}"
(b.map
(depNameVer: "${depNameVer.name}#${depNameVer.version}")
(getDependencies pkgData getDepByNameVer getDepByOriginalID))));
getDepByOriginalID = id:
allDependenciesByOriginalID."${id}";
(getDependencies pkgData getDepByNameVer dependenciesByOriginalID))));
getDepByNameVer = name: version:
allDependencies."${name}"."${version}";