about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBenjamin Esham <benjamin@esham.io>2019-08-17 19:12:14 -0700
committerBenjamin Esham <benjamin@esham.io>2019-08-18 16:22:23 -0700
commit3a9b0bd6344816e166170bceef0064a22684d9ab (patch)
tree17d23ae1f81d63f613919e2112f67ab98d3ce2f6
parentb7d04d6e6b048b600c7b614c19ee033cac157eeb (diff)
downloadnixlib-3a9b0bd6344816e166170bceef0064a22684d9ab.tar
nixlib-3a9b0bd6344816e166170bceef0064a22684d9ab.tar.gz
nixlib-3a9b0bd6344816e166170bceef0064a22684d9ab.tar.bz2
nixlib-3a9b0bd6344816e166170bceef0064a22684d9ab.tar.lz
nixlib-3a9b0bd6344816e166170bceef0064a22684d9ab.tar.xz
nixlib-3a9b0bd6344816e166170bceef0064a22684d9ab.tar.zst
nixlib-3a9b0bd6344816e166170bceef0064a22684d9ab.zip
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.
-rw-r--r--doc/languages-frameworks/perl.xml33
-rw-r--r--pkgs/build-support/setup-hooks/shorten-perl-shebang.sh88
-rw-r--r--pkgs/top-level/all-packages.nix4
3 files changed, 124 insertions, 1 deletions
diff --git a/doc/languages-frameworks/perl.xml b/doc/languages-frameworks/perl.xml
index d0f124f29d42..065212a0e180 100644
--- a/doc/languages-frameworks/perl.xml
+++ b/doc/languages-frameworks/perl.xml
@@ -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>
 
diff --git a/pkgs/build-support/setup-hooks/shorten-perl-shebang.sh b/pkgs/build-support/setup-hooks/shorten-perl-shebang.sh
new file mode 100644
index 000000000000..4bf7c0ff1af4
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/shorten-perl-shebang.sh
@@ -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
+}
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index 3bb3c5a358af..f5d87f28f734 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -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;