add shortenPerlShebang function

This setup hook modifies a Perl script so that any "-I" flags in its shebang
line are rewritten into a "use lib ..." statement on the next line. This gets
around a limitation in Darwin, which will not properly handle a script whose
shebang line exceeds 511 characters.
This commit is contained in:
Benjamin Esham 2019-08-17 19:12:14 -07:00
parent b7d04d6e6b
commit 3a9b0bd634
3 changed files with 124 additions and 1 deletions

View file

@ -75,7 +75,8 @@ foo = import ../path/to/foo.nix {
It adds the contents of the <envar>PERL5LIB</envar> environment variable
to <literal>#! .../bin/perl</literal> line of Perl scripts as
<literal>-I<replaceable>dir</replaceable></literal> flags. This ensures
that a script can find its dependencies.
that a script can find its dependencies. (This can cause this shebang line
to become too long for Darwin to handle; see the note below.)
</para>
</listitem>
<listitem>
@ -137,6 +138,36 @@ ClassC3Componentised = buildPerlPackage rec {
</programlisting>
</para>
<para>
On Darwin, if a script has too many
<literal>-I<replaceable>dir</replaceable></literal> flags in its first line
(its “shebang line”), it will not run. This can be worked around by calling
the <literal>shortenPerlShebang</literal> function from the
<literal>postInstall</literal> phase:
<programlisting>
{ stdenv, buildPerlPackage, fetchurl, shortenPerlShebang }:
ImageExifTool = buildPerlPackage {
pname = "Image-ExifTool";
version = "11.50";
src = fetchurl {
url = "https://www.sno.phy.queensu.ca/~phil/exiftool/Image-ExifTool-11.50.tar.gz";
sha256 = "0d8v48y94z8maxkmw1rv7v9m0jg2dc8xbp581njb6yhr7abwqdv3";
};
buildInputs = stdenv.lib.optional stdenv.isDarwin shortenPerlShebang;
postInstall = stdenv.lib.optional stdenv.isDarwin ''
shortenPerlShebang $out/bin/exiftool
'';
};
</programlisting>
This will remove the <literal>-I</literal> flags from the shebang line,
rewrite them in the <literal>use lib</literal> form, and put them on the next
line instead. This function can be given any number of Perl scripts as
arguments; it will modify them in-place.
</para>
<section xml:id="ssec-generation-from-CPAN">
<title>Generation from CPAN</title>

View file

@ -0,0 +1,88 @@
# This setup hook modifies a Perl script so that any "-I" flags in its shebang
# line are rewritten into a "use lib ..." statement on the next line. This gets
# around a limitation in Darwin, which will not properly handle a script whose
# shebang line exceeds 511 characters.
#
# Each occurrence of "-I /path/to/lib1" or "-I/path/to/lib2" is removed from
# the shebang line, along with the single space that preceded it. These library
# paths are placed into a new line of the form
#
# use lib "/path/to/lib1", "/path/to/lib2";
#
# immediately following the shebang line. If a library appeared in the original
# list more than once, only its first occurrence will appear in the output
# list. In other words, the libraries are deduplicated, but the ordering of the
# first appearance of each one is preserved.
#
# Any flags other than "-I" in the shebang line are left as-is, and the
# interpreter is also left alone (although the script will abort if the
# interpreter does not seem to be either "perl" or else "env" with "perl" as
# its argument). Each line after the shebang line is left unchanged. Each file
# is modified in place.
#
# Usage:
# shortenPerlShebang SCRIPT...
shortenPerlShebang() {
while [ $# -gt 0 ]; do
_shortenPerlShebang "$1"
shift
done
}
_shortenPerlShebang() {
local program="$1"
echo "shortenPerlShebang: rewriting shebang line in $program"
if ! isScript "$program"; then
die "shortenPerlShebang: refusing to modify $program because it is not a script"
fi
local temp="$(mktemp)"
gawk '
(NR == 1) {
if (!($0 ~ /\/(perl|env +perl)\>/)) {
print "shortenPerlShebang: script does not seem to be a Perl script" > "/dev/stderr"
exit 1
}
idx = 0
while (match($0, / -I ?([^ ]+)/, pieces)) {
matches[idx] = pieces[1]
idx++
$0 = gensub(/ -I ?[^ ]+/, "", 1, $0)
}
print $0
if (idx > 0) {
prefix = "use lib "
for (idx in matches) {
path = matches[idx]
if (!(path in seen)) {
printf "%s\"%s\"", prefix, path
seen[path] = 1
prefix = ", "
}
}
print ";"
}
}
(NR > 1 ) {
print
}
' "$program" > "$temp" || die
# Preserve the mode of the original file
cp --preserve=mode --attributes-only "$program" "$temp"
mv "$temp" "$program"
# Measure the new shebang line length and make sure it's okay. We subtract
# one to account for the trailing newline that "head" included in its
# output.
local new_length=$(( $(head -n 1 "$program" | wc -c) - 1 ))
# Darwin is okay when the shebang line contains 511 characters, but not
# when it contains 512 characters.
if [ $new_length -ge 512 ]; then
die "shortenPerlShebang: shebang line is $new_length characters--still too long for Darwin!"
fi
}

View file

@ -384,6 +384,10 @@ in
setupSystemdUnits = callPackage ../build-support/setup-systemd-units.nix { };
shortenPerlShebang = makeSetupHook
{ deps = [ dieHook ]; }
../build-support/setup-hooks/shorten-perl-shebang.sh;
singularity-tools = callPackage ../build-support/singularity-tools { };
srcOnly = args: callPackage ../build-support/src-only args;