nodePackages: add support for recursive dependencies

This is a drop-in replacement that handles nodejs recursive dependencies by
propagating already visited nodejs packages and creating shims for recursive
ones. This implementation handles both, new and old npm2nix output format.
This commit is contained in:
Jaka Hudoklin 2014-09-09 22:46:11 +02:00
parent 7d6a27a780
commit 7ff4ffd998

View File

@ -1,6 +1,6 @@
{ stdenv, runCommand, nodejs, neededNatives}: { stdenv, runCommand, nodejs, neededNatives}:
args @ { name, src, deps ? [], peerDependencies ? [], flags ? [], preShellHook ? "", postShellHook ? "", ... }: args @ { name, src, deps ? {}, peerDependencies ? [], flags ? [], preShellHook ? "", postShellHook ? "", resolvedDeps ? {}, bin ? null, ... }:
with stdenv.lib; with stdenv.lib;
@ -12,88 +12,135 @@ let
mv *node* $out mv *node* $out
''; '';
# Convert deps to attribute set
attrDeps = if isAttrs deps then deps else
(listToAttrs (map (dep: nameValuePair dep.name dep) deps));
# All required node modules, without already resolved dependencies
requiredDeps = removeAttrs attrDeps (attrNames resolvedDeps);
# Recursive dependencies that we want to avoid with shim creation
recursiveDeps = removeAttrs attrDeps (attrNames requiredDeps);
peerDeps = listToAttrs (concatMap (dep: map (name: { peerDeps = listToAttrs (concatMap (dep: map (name: {
inherit name; inherit name;
value = dep; value = dep;
}) (filter (nm: !(elem nm (args.passthru.names or []))) dep.names)) (peerDependencies)); }) (filter (nm: !(elem nm (args.passthru.names or []))) dep.names)) (peerDependencies));
in
stdenv.mkDerivation ({
unpackPhase = "true";
inherit src; self = let
# Pass resolved dependencies to dependencies of this package
deps = map (
dep: dep.override {
resolvedDeps = resolvedDeps // { "${name}" = self; };
}
) (attrValues requiredDeps);
configurePhase = '' in stdenv.mkDerivation ({
runHook preConfigure unpackPhase = "true";
mkdir node_modules
${concatStrings (concatMap (dep: map (name: ''
ln -sv ${dep}/lib/node_modules/${name} node_modules/
'') dep.names) deps)}
${concatStrings (mapAttrsToList (name: dep: ''
ln -sv ${dep}/lib/node_modules/${name} node_modules/
'') peerDeps)}
export HOME=$(pwd)
runHook postConfigure
'';
buildPhase = '' inherit src;
runHook preBuild
npm --registry http://www.example.com --nodedir=${sources} install $src ${npmFlags} configurePhase = ''
runHook postBuild runHook preConfigure
''; mkdir node_modules
installPhase = '' # Symlink dependencies for node modules
runHook preInstall ${concatStrings (concatMap (dep: map (name: ''
mkdir -p $out/lib/node_modules ln -sv ${dep}/lib/node_modules/${name} node_modules/
${concatStrings (map (name: '' '') dep.names) deps)}
mv node_modules/${name} $out/lib/node_modules
rm -fR $out/lib/node_modules/${name}/node_modules # Symlink peer dependencies
ln -sv $out/.dependent-node-modules $out/lib/node_modules/${name}/node_modules ${concatStrings (mapAttrsToList (name: dep: ''
if [ -e "$out/lib/node_modules/${name}/man" ]; then ln -sv ${dep}/lib/node_modules/${name} node_modules/
mkdir -p $out/share '') peerDeps)}
for dir in "$out/lib/node_modules/${name}/man/"*; do
mkdir -p $out/share/man/$(basename "$dir") # Create shims for recursive dependenceies
for page in "$dir"/*; do ${concatStrings (concatMap (dep: map (name: ''
ln -sv $page $out/share/man/$(basename "$dir") mkdir -p node_modules/${name}
cat > node_modules/${name}/package.json <<EOF
{
"name": "${name}",
"version": "${(builtins.parseDrvName dep.name).version}"
}
EOF
'') dep.names) (attrValues recursiveDeps))}
export HOME=$(pwd)
runHook postConfigure
'';
buildPhase = ''
runHook preBuild
npm --registry http://www.example.com --nodedir=${sources} install $src ${npmFlags}
runHook postBuild
'';
installPhase = ''
runHook preInstall
# Remove shims
${concatStrings (concatMap (dep: map (name: ''
rm node_modules/${name}/package.json
rmdir node_modules/${name}
'') dep.names) (attrValues recursiveDeps))}
mkdir -p $out/lib/node_modules
${concatStrings (map (name: ''
mv node_modules/${name} $out/lib/node_modules
rm -fR $out/lib/node_modules/${name}/node_modules
ln -sv $out/.dependent-node-modules $out/lib/node_modules/${name}/node_modules
if [ -e "$out/lib/node_modules/${name}/man" ]; then
mkdir -p $out/share
for dir in "$out/lib/node_modules/${name}/man/"*; do
mkdir -p $out/share/man/$(basename "$dir")
for page in "$dir"/*; do
ln -sv $page $out/share/man/$(basename "$dir")
done
done done
done fi
'') args.passthru.names)}
${concatStrings (mapAttrsToList (name: dep: ''
mv node_modules/${name} $out/lib/node_modules
'') peerDeps)}
mv node_modules/.bin $out/lib/node_modules 2>/dev/null || true
mv node_modules $out/.dependent-node-modules
if [ -d "$out/lib/node_modules/.bin" ]; then
ln -sv $out/lib/node_modules/.bin $out/bin
node=`type -p node`
coffee=`type -p coffee || true`
find -L $out/lib/node_modules/.bin/* -type f -print0 | \
xargs -0 sed --follow-symlinks -i \
-e 's@#!/usr/bin/env node@#!'"$node"'@' \
-e 's@#!/usr/bin/env coffee@#!'"$coffee"'@' \
-e 's@#!/.*/node@#!'"$node"'@' \
-e 's@#!/.*/coffee@#!'"$coffee"'@'
fi fi
'') args.passthru.names)} runHook postInstall
${concatStrings (mapAttrsToList (name: dep: '' '';
mv node_modules/${name} $out/lib/node_modules
'') peerDeps)}
mv node_modules/.bin $out/lib/node_modules 2>/dev/null || true
mv node_modules $out/.dependent-node-modules
if [ -d "$out/lib/node_modules/.bin" ]; then
ln -sv $out/lib/node_modules/.bin $out/bin
node=`type -p node`
coffee=`type -p coffee || true`
find -L $out/lib/node_modules/.bin/* -type f -print0 | \
xargs -0 sed --follow-symlinks -i \
-e 's@#!/usr/bin/env node@#!'"$node"'@' \
-e 's@#!/usr/bin/env coffee@#!'"$coffee"'@' \
-e 's@#!/.*/node@#!'"$node"'@' \
-e 's@#!/.*/coffee@#!'"$coffee"'@'
fi
runHook postInstall
'';
preFixup = concatStringsSep "\n" (map (src: '' preFixup = concatStringsSep "\n" (map (src: ''
find $out -type f -print0 | xargs -0 sed -i 's|${src}|${src.name}|g' find $out -type f -print0 | xargs -0 sed -i 's|${src}|${src.name}|g'
'') src); '') src);
shellHook = '' shellHook = ''
${preShellHook} ${preShellHook}
export PATH=${nodejs}/bin:$(pwd)/node_modules/.bin:$PATH export PATH=${nodejs}/bin:$(pwd)/node_modules/.bin:$PATH
mkdir -p node_modules mkdir -p node_modules
${concatStrings (concatMap (dep: map (name: '' ${concatStrings (concatMap (dep: map (name: ''
ln -sfv ${dep}/lib/node_modules/${name} node_modules/ ln -sfv ${dep}/lib/node_modules/${name} node_modules/
'') dep.names) deps)} '') dep.names) deps)}
${postShellHook} ${postShellHook}
''; '';
} // args // { } // (filterAttrs (n: v: n != "deps" && n != "resolvedDeps") args) // {
# Run the node setup hook when this package is a build input name = "${
propagatedNativeBuildInputs = (args.propagatedNativeBuildInputs or []) ++ [ nodejs ]; if bin == true then "bin-" else if bin == false then "node-" else ""
}${name}";
# Make buildNodePackage useful with --run-env # Run the node setup hook when this package is a build input
nativeBuildInputs = (args.nativeBuildInputs or []) ++ deps ++ peerDependencies ++ neededNatives; propagatedNativeBuildInputs = (args.propagatedNativeBuildInputs or []) ++ [ nodejs ];
} )
# Make buildNodePackage useful with --run-env
nativeBuildInputs = (args.nativeBuildInputs or []) ++ deps ++ peerDependencies ++ neededNatives;
});
in self