Merge branch 'features/sbang' into develop
Conflicts: lib/spack/llnl/util/filesystem.py
This commit is contained in:
commit
ada675dea2
3 changed files with 171 additions and 1 deletions
84
bin/sbang
Executable file
84
bin/sbang
Executable file
|
@ -0,0 +1,84 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# `sbang`: Run scripts with long shebang lines.
|
||||||
|
#
|
||||||
|
# Many operating systems limit the length of shebang lines, making it
|
||||||
|
# hard to use interpreters that are deep in the directory hierarchy.
|
||||||
|
# `sbang` can run such scripts, either as a shebang interpreter, or
|
||||||
|
# directly on the command line.
|
||||||
|
#
|
||||||
|
# Usage
|
||||||
|
# -----------------------------
|
||||||
|
# Suppose you have a script, long-shebang.sh, like this:
|
||||||
|
#
|
||||||
|
# 1 #!/very/long/path/to/some/interpreter
|
||||||
|
# 2
|
||||||
|
# 3 echo "success!"
|
||||||
|
#
|
||||||
|
# Invoking this script will result in an error on some OS's. On
|
||||||
|
# Linux, you get this:
|
||||||
|
#
|
||||||
|
# $ ./long-shebang.sh
|
||||||
|
# -bash: ./long: /very/long/path/to/some/interp: bad interpreter:
|
||||||
|
# No such file or directory
|
||||||
|
#
|
||||||
|
# On Mac OS X, the system simply assumes the interpreter is the shell
|
||||||
|
# and tries to run with it, which is likely not what you want.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# `sbang` on the command line
|
||||||
|
# -----------------------------
|
||||||
|
# You can use `sbang` in two ways. The first is to use it directly,
|
||||||
|
# from the command line, like this:
|
||||||
|
#
|
||||||
|
# $ sbang ./long-shebang.sh
|
||||||
|
# success!
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# `sbang` as the interpreter
|
||||||
|
# -----------------------------
|
||||||
|
# You can also use `sbang` *as* the interpreter for your script. Put
|
||||||
|
# `#!/bin/bash /path/to/sbang` on line 1, and move the original
|
||||||
|
# shebang to line 2 of the script:
|
||||||
|
#
|
||||||
|
# 1 #!/bin/bash /path/to/sbang
|
||||||
|
# 2 #!/long/path/to/real/interpreter with arguments
|
||||||
|
# 3
|
||||||
|
# 4 echo "success!"
|
||||||
|
#
|
||||||
|
# $ ./long-shebang.sh
|
||||||
|
# success!
|
||||||
|
#
|
||||||
|
# On Linux, you could shorten line 1 to `#!/path/to/sbang`, but other
|
||||||
|
# operating systems like Mac OS X require the interpreter to be a
|
||||||
|
# binary, so it's best to use `sbang` as a `bash` argument.
|
||||||
|
# Obviously, for this to work, `sbang` needs to have a short enough
|
||||||
|
# path that *it* will run without hitting OS limits.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# How it works
|
||||||
|
# -----------------------------
|
||||||
|
# `sbang` is a very simple bash script. It looks at the first two
|
||||||
|
# lines of a script argument and runs the last line starting with
|
||||||
|
# `#!`, with the script as an argument. It also forwards arguments.
|
||||||
|
#
|
||||||
|
|
||||||
|
# First argument is the script we want to actually run.
|
||||||
|
script="$1"
|
||||||
|
|
||||||
|
# Search the first two lines of script for interpreters.
|
||||||
|
lines=0
|
||||||
|
while read line && ((lines < 2)) ; do
|
||||||
|
if [[ "$line" = '#!'* ]]; then
|
||||||
|
interpreter="${line#\#!}"
|
||||||
|
fi
|
||||||
|
lines=$((lines+1))
|
||||||
|
done < "$script"
|
||||||
|
|
||||||
|
# Invoke any interpreter found, or raise an error if none was found.
|
||||||
|
if [ -n "$interpreter" ]; then
|
||||||
|
exec $interpreter "$@"
|
||||||
|
else
|
||||||
|
echo "error: sbang found no interpreter in $script"
|
||||||
|
exit 1
|
||||||
|
fi
|
|
@ -25,7 +25,8 @@
|
||||||
__all__ = ['set_install_permissions', 'install', 'install_tree', 'traverse_tree',
|
__all__ = ['set_install_permissions', 'install', 'install_tree', 'traverse_tree',
|
||||||
'expand_user', 'working_dir', 'touch', 'touchp', 'mkdirp',
|
'expand_user', 'working_dir', 'touch', 'touchp', 'mkdirp',
|
||||||
'force_remove', 'join_path', 'ancestor', 'can_access', 'filter_file',
|
'force_remove', 'join_path', 'ancestor', 'can_access', 'filter_file',
|
||||||
'FileFilter', 'change_sed_delimiter', 'is_exe', 'force_symlink', 'remove_dead_links', 'remove_linked_tree']
|
'FileFilter', 'change_sed_delimiter', 'is_exe', 'force_symlink',
|
||||||
|
'remove_dead_links', 'remove_linked_tree', 'copy_mode', 'unset_executable_mode']
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
@ -158,6 +159,14 @@ def copy_mode(src, dest):
|
||||||
os.chmod(dest, dest_mode)
|
os.chmod(dest, dest_mode)
|
||||||
|
|
||||||
|
|
||||||
|
def unset_executable_mode(path):
|
||||||
|
mode = os.stat(path).st_mode
|
||||||
|
mode &= ~stat.S_IXUSR
|
||||||
|
mode &= ~stat.S_IXGRP
|
||||||
|
mode &= ~stat.S_IXOTH
|
||||||
|
os.chmod(path, mode)
|
||||||
|
|
||||||
|
|
||||||
def install(src, dest):
|
def install(src, dest):
|
||||||
"""Manually install a file to a particular location."""
|
"""Manually install a file to a particular location."""
|
||||||
tty.debug("Installing %s to %s" % (src, dest))
|
tty.debug("Installing %s to %s" % (src, dest))
|
||||||
|
|
77
lib/spack/spack/hooks/sbang.py
Normal file
77
lib/spack/spack/hooks/sbang.py
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
##############################################################################
|
||||||
|
# Copyright (c) 2013-2015, Lawrence Livermore National Security, LLC.
|
||||||
|
# Produced at the Lawrence Livermore National Laboratory.
|
||||||
|
#
|
||||||
|
# This file is part of Spack.
|
||||||
|
# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
|
||||||
|
# LLNL-CODE-647188
|
||||||
|
#
|
||||||
|
# For details, see https://github.com/llnl/spack
|
||||||
|
# Please also see the LICENSE file for our notice and the LGPL.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License (as published by
|
||||||
|
# the Free Software Foundation) version 2.1 dated February 1999.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
|
||||||
|
# conditions of the GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
##############################################################################
|
||||||
|
import os
|
||||||
|
|
||||||
|
from llnl.util.filesystem import *
|
||||||
|
import llnl.util.tty as tty
|
||||||
|
|
||||||
|
import spack
|
||||||
|
import spack.modules
|
||||||
|
|
||||||
|
# Character limit for shebang line. Using Linux's 127 characters
|
||||||
|
# here, as it is the shortest I could find on a modern OS.
|
||||||
|
shebang_limit = 127
|
||||||
|
|
||||||
|
def shebang_too_long(path):
|
||||||
|
"""Detects whether an file has a shebang line that is too long."""
|
||||||
|
with open(path, 'r') as script:
|
||||||
|
bytes = script.read(2)
|
||||||
|
if bytes != '#!':
|
||||||
|
return False
|
||||||
|
|
||||||
|
line = bytes + script.readline()
|
||||||
|
return len(line) > shebang_limit
|
||||||
|
|
||||||
|
|
||||||
|
def filter_shebang(path):
|
||||||
|
"""Adds a second shebang line, using sbang, at the beginning of a file."""
|
||||||
|
backup = path + ".shebang.bak"
|
||||||
|
os.rename(path, backup)
|
||||||
|
|
||||||
|
with open(backup, 'r') as bak_file:
|
||||||
|
original = bak_file.read()
|
||||||
|
|
||||||
|
with open(path, 'w') as new_file:
|
||||||
|
new_file.write('#!/bin/bash %s/bin/sbang\n' % spack.spack_root)
|
||||||
|
new_file.write(original)
|
||||||
|
|
||||||
|
copy_mode(backup, path)
|
||||||
|
unset_executable_mode(backup)
|
||||||
|
|
||||||
|
tty.warn("Patched overly long shebang in %s" % path)
|
||||||
|
|
||||||
|
|
||||||
|
def post_install(pkg):
|
||||||
|
"""This hook edits scripts so that they call /bin/bash
|
||||||
|
$spack_prefix/bin/sbang instead of something longer than the
|
||||||
|
shebang limit."""
|
||||||
|
if not os.path.isdir(pkg.prefix.bin):
|
||||||
|
return
|
||||||
|
|
||||||
|
for file in os.listdir(pkg.prefix.bin):
|
||||||
|
path = os.path.join(pkg.prefix.bin, file)
|
||||||
|
if shebang_too_long(path):
|
||||||
|
filter_shebang(path)
|
||||||
|
|
Loading…
Reference in a new issue