nodePackages: fix builder
- reuse unpack and patch phase - patch shebangs of source code - properly patch depdency versions (thanks @svanderburg) - add patching for github version names - add some inline docs - fix recursive depdencies runtime handling by copying files instead of symlinking if package has recursive dependencies
This commit is contained in:
parent
58c283f0dd
commit
86744fef91
|
@ -28,93 +28,188 @@ let
|
||||||
}) (filter (nm: !(elem nm (args.passthru.names or []))) dep.names)) (peerDependencies));
|
}) (filter (nm: !(elem nm (args.passthru.names or []))) dep.names)) (peerDependencies));
|
||||||
|
|
||||||
self = let
|
self = let
|
||||||
# Pass resolved dependencies to dependencies of this package
|
# Pass resolved dependencies to dependencies of this package
|
||||||
deps = map (
|
deps = map (
|
||||||
dep: dep.override {
|
dep: dep.override {
|
||||||
resolvedDeps = resolvedDeps // { "${name}" = self; };
|
resolvedDeps = resolvedDeps // { "${name}" = self; };
|
||||||
}
|
}
|
||||||
) (attrValues requiredDeps);
|
) (attrValues requiredDeps);
|
||||||
|
|
||||||
in stdenv.mkDerivation ({
|
patchShebangs = dir: ''
|
||||||
unpackPhase = "true";
|
node=`type -p node`
|
||||||
|
coffee=`type -p coffee || true`
|
||||||
|
find -L ${dir} -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"'@' || true
|
||||||
|
'';
|
||||||
|
|
||||||
|
in stdenv.mkDerivation ({
|
||||||
inherit src;
|
inherit src;
|
||||||
|
|
||||||
|
postPatch = ''
|
||||||
|
${patchShebangs "./"}
|
||||||
|
|
||||||
|
# Some version specifiers (latest, unstable, URLs, file paths) force NPM
|
||||||
|
# to make remote connections or consult paths outside the Nix store.
|
||||||
|
# The following JavaScript replaces these by * to prevent that:
|
||||||
|
|
||||||
|
(
|
||||||
|
cat <<EOF
|
||||||
|
var fs = require('fs');
|
||||||
|
var url = require('url');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Replaces an impure version specification by *
|
||||||
|
*/
|
||||||
|
function replaceImpureVersionSpec(versionSpec) {
|
||||||
|
var parsedUrl = url.parse(versionSpec);
|
||||||
|
|
||||||
|
if(versionSpec == "latest" || versionSpec == "unstable" ||
|
||||||
|
versionSpec.substr(0, 2) == ".." || dependency.substr(0, 2) == "./" || dependency.substr(0, 2) == "~/" || dependency.substr(0, 1) == '/' || /^[^/]+\/[^/]+$/.test(versionSpec))
|
||||||
|
return '*';
|
||||||
|
else if(parsedUrl.protocol == "git:" || parsedUrl.protocol == "git+ssh:" || parsedUrl.protocol == "git+http:" || parsedUrl.protocol == "git+https:" ||
|
||||||
|
parsedUrl.protocol == "http:" || parsedUrl.protocol == "https:")
|
||||||
|
return '*';
|
||||||
|
else
|
||||||
|
return versionSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
var packageObj = JSON.parse(fs.readFileSync('./package.json'));
|
||||||
|
|
||||||
|
/* Replace dependencies */
|
||||||
|
if(packageObj.dependencies !== undefined) {
|
||||||
|
for(var dependency in packageObj.dependencies) {
|
||||||
|
var versionSpec = packageObj.dependencies[dependency];
|
||||||
|
packageObj.dependencies[dependency] = replaceImpureVersionSpec(versionSpec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Replace development dependencies */
|
||||||
|
if(packageObj.devDependencies !== undefined) {
|
||||||
|
for(var dependency in packageObj.devDependencies) {
|
||||||
|
var versionSpec = packageObj.devDependencies[dependency];
|
||||||
|
packageObj.devDependencies[dependency] = replaceImpureVersionSpec(versionSpec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Replace optional dependencies */
|
||||||
|
if(packageObj.optionalDependencies !== undefined) {
|
||||||
|
for(var dependency in packageObj.optionalDependencies) {
|
||||||
|
var versionSpec = packageObj.optionalDependencies[dependency];
|
||||||
|
packageObj.optionalDependencies[dependency] = replaceImpureVersionSpec(versionSpec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write the fixed JSON file */
|
||||||
|
fs.writeFileSync("package.json", JSON.stringify(packageObj));
|
||||||
|
EOF
|
||||||
|
) | node
|
||||||
|
'';
|
||||||
|
|
||||||
configurePhase = ''
|
configurePhase = ''
|
||||||
runHook preConfigure
|
runHook preConfigure
|
||||||
mkdir node_modules
|
|
||||||
|
|
||||||
# Symlink dependencies for node modules
|
mkdir build-dir
|
||||||
${concatStrings (concatMap (dep: map (name: ''
|
(
|
||||||
ln -sv ${dep}/lib/node_modules/${name} node_modules/
|
cd build-dir
|
||||||
'') dep.names) deps)}
|
mkdir node_modules
|
||||||
|
|
||||||
# Symlink peer dependencies
|
# Symlink or copy dependencies for node modules
|
||||||
${concatStrings (mapAttrsToList (name: dep: ''
|
# copy is needed if dependency has recursive dependencies,
|
||||||
ln -sv ${dep}/lib/node_modules/${name} node_modules/
|
# because node can't follow symlinks while resolving recursive deps.
|
||||||
'') peerDeps)}
|
${concatStrings (concatMap (dep: map (name:
|
||||||
|
if dep.recursiveDeps == [] then ''
|
||||||
|
ln -sv ${dep}/lib/node_modules/${name} node_modules/
|
||||||
|
'' else ''
|
||||||
|
cp -R ${dep}/lib/node_modules/${name} node_modules/
|
||||||
|
''
|
||||||
|
) dep.names) deps)}
|
||||||
|
|
||||||
# Create shims for recursive dependenceies
|
# Symlink peer dependencies
|
||||||
${concatStrings (concatMap (dep: map (name: ''
|
${concatStrings (mapAttrsToList (name: dep: ''
|
||||||
mkdir -p node_modules/${name}
|
ln -sv ${dep}/lib/node_modules/${name} node_modules/
|
||||||
cat > node_modules/${name}/package.json <<EOF
|
'') peerDeps)}
|
||||||
{
|
|
||||||
"name": "${name}",
|
|
||||||
"version": "${(builtins.parseDrvName dep.name).version}"
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
'') dep.names) (attrValues recursiveDeps))}
|
|
||||||
|
|
||||||
export HOME=$(pwd)
|
# Create shims for recursive dependenceies
|
||||||
|
${concatStrings (concatMap (dep: map (name: ''
|
||||||
|
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/build-dir
|
||||||
runHook postConfigure
|
runHook postConfigure
|
||||||
'';
|
'';
|
||||||
|
|
||||||
buildPhase = ''
|
buildPhase = ''
|
||||||
runHook preBuild
|
runHook preBuild
|
||||||
npm --registry http://www.example.com --nodedir=${sources} install $src ${npmFlags}
|
|
||||||
|
# If source was a file, repackage it, so npm pre/post publish hooks are not triggered,
|
||||||
|
if [[ -f $src ]]; then
|
||||||
|
tar --exclude='build-dir' -czf build-dir/package.tgz ./
|
||||||
|
export src=$HOME/package.tgz
|
||||||
|
else
|
||||||
|
export src=$PWD
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install package
|
||||||
|
(cd $HOME && npm --registry http://www.example.com --nodedir=${sources} install $src ${npmFlags})
|
||||||
|
|
||||||
runHook postBuild
|
runHook postBuild
|
||||||
'';
|
'';
|
||||||
|
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
runHook preInstall
|
runHook preInstall
|
||||||
|
|
||||||
# Remove shims
|
(
|
||||||
${concatStrings (concatMap (dep: map (name: ''
|
cd $HOME
|
||||||
rm node_modules/${name}/package.json
|
|
||||||
rmdir node_modules/${name}
|
|
||||||
'') dep.names) (attrValues recursiveDeps))}
|
|
||||||
|
|
||||||
mkdir -p $out/lib/node_modules
|
# Remove shims
|
||||||
${concatStrings (map (name: ''
|
${concatStrings (concatMap (dep: map (name: ''
|
||||||
mv node_modules/${name} $out/lib/node_modules
|
rm node_modules/${name}/package.json
|
||||||
rm -fR $out/lib/node_modules/${name}/node_modules
|
rmdir node_modules/${name}
|
||||||
ln -sv $out/.dependent-node-modules $out/lib/node_modules/${name}/node_modules
|
'') dep.names) (attrValues recursiveDeps))}
|
||||||
if [ -e "$out/lib/node_modules/${name}/man" ]; then
|
|
||||||
mkdir -p $out/share
|
mkdir -p $out/lib/node_modules
|
||||||
for dir in "$out/lib/node_modules/${name}/man/"*; do
|
|
||||||
mkdir -p $out/share/man/$(basename "$dir")
|
# Install manual
|
||||||
for page in "$dir"/*; do
|
${concatStrings (map (name: ''
|
||||||
ln -sv $page $out/share/man/$(basename "$dir")
|
mv node_modules/${name} $out/lib/node_modules
|
||||||
|
rm -fR $out/lib/node_modules/${name}/node_modules
|
||||||
|
cp -r 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)}
|
||||||
|
|
||||||
|
# Symlink dependencies
|
||||||
|
${concatStrings (mapAttrsToList (name: dep: ''
|
||||||
|
mv node_modules/${name} $out/lib/node_modules
|
||||||
|
'') peerDeps)}
|
||||||
|
|
||||||
|
# Install binaries and patch shebangs
|
||||||
|
mv node_modules/.bin $out/lib/node_modules 2>/dev/null || true
|
||||||
|
if [ -d "$out/lib/node_modules/.bin" ]; then
|
||||||
|
ln -sv $out/lib/node_modules/.bin $out/bin
|
||||||
|
${patchShebangs "$out/lib/node_modules/.bin/*"}
|
||||||
fi
|
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
|
|
||||||
runHook postInstall
|
runHook postInstall
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
@ -141,6 +236,10 @@ let
|
||||||
|
|
||||||
# Make buildNodePackage useful with --run-env
|
# Make buildNodePackage useful with --run-env
|
||||||
nativeBuildInputs = (args.nativeBuildInputs or []) ++ deps ++ peerDependencies ++ neededNatives;
|
nativeBuildInputs = (args.nativeBuildInputs or []) ++ deps ++ peerDependencies ++ neededNatives;
|
||||||
|
|
||||||
|
# Expose list of recursive dependencies upstream, up to the package that
|
||||||
|
# caused recursive dependency
|
||||||
|
recursiveDeps = (flatten (map (d: remove name d.recursiveDeps) deps)) ++ (attrNames recursiveDeps);
|
||||||
});
|
});
|
||||||
|
|
||||||
in self
|
in self
|
||||||
|
|
|
@ -14,16 +14,7 @@ rec {
|
||||||
inherit (pkgs) runCommand;
|
inherit (pkgs) runCommand;
|
||||||
};
|
};
|
||||||
|
|
||||||
patchSource = fn: srcAttrs:
|
patchSource = fn: srcAttrs: fn srcAttrs;
|
||||||
let src = fn srcAttrs; in pkgs.runCommand src.name {} ''
|
|
||||||
mkdir unpack
|
|
||||||
cd unpack
|
|
||||||
unpackFile ${src}
|
|
||||||
chmod -R +w */
|
|
||||||
mv */ package 2>/dev/null || true
|
|
||||||
sed -i -e "s/:\s*\"latest\"/: \"*\"/" -e "s/:\s\+\"[A-Za-z0-9_-]\+\/[A-Za-z0-9_-]\+\"/: \"*\"/" -e "s/:\s*\"\(https\?\|git\(\+\(ssh\|http\|https\)\)\?\):\/\/[^\"]*\"/: \"*\"/" package/package.json
|
|
||||||
mv */ $out
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Backwards compat
|
# Backwards compat
|
||||||
patchLatest = patchSource fetchurl;
|
patchLatest = patchSource fetchurl;
|
||||||
|
|
Loading…
Reference in New Issue