sbang: vendor sbang
`sbang` now lives at https://github.com/spack/sbang, and it has its own test suite that's more extensive than what's in Spack. We'll leave sbang tests to sbang from now on, and just vendor `bin/sbang` directly. Remaining `sbang` tests have to do with patching files, not with `sbang`'s functionality. This update also fixes a bug with `sbang` and multiple command line arguments that was introduced in #19529. See: * https://github.com/spack/sbang/pull/1 * https://github.com/spack/sbang/pull/2 - [x] include latest `sbang` from https://github.com/spack/sbang - [x] remove old `sbang` tests from Spack - [x] update `COPYRIGHT` and `cmd/license.py`
This commit is contained in:
parent
44bacefb27
commit
aebf20ebdc
4 changed files with 24 additions and 164 deletions
12
COPYRIGHT
12
COPYRIGHT
|
@ -28,9 +28,11 @@ text in the license header:
|
||||||
|
|
||||||
External Packages
|
External Packages
|
||||||
-------------------
|
-------------------
|
||||||
Spack bundles its external dependencies in lib/spack/external. These
|
|
||||||
packages are covered by various permissive licenses. A summary listing
|
Spack bundles most external dependencies in lib/spack/external. It also
|
||||||
follows. See the license included with each package for full details.
|
includes the sbang tool directly in bin/sbang. These packages are covered
|
||||||
|
by various permissive licenses. A summary listing follows. See the
|
||||||
|
license included with each package for full details.
|
||||||
|
|
||||||
PackageName: argparse
|
PackageName: argparse
|
||||||
PackageHomePage: https://pypi.python.org/pypi/argparse
|
PackageHomePage: https://pypi.python.org/pypi/argparse
|
||||||
|
@ -76,6 +78,10 @@ PackageName: ruamel.yaml
|
||||||
PackageHomePage: https://yaml.readthedocs.io/
|
PackageHomePage: https://yaml.readthedocs.io/
|
||||||
PackageLicenseDeclared: MIT
|
PackageLicenseDeclared: MIT
|
||||||
|
|
||||||
|
PackageName: sbang
|
||||||
|
PackageHomePage: https://github.com/spack/sbang
|
||||||
|
PackageLicenseDeclared: Apache-2.0 OR MIT
|
||||||
|
|
||||||
PackageName: six
|
PackageName: six
|
||||||
PackageHomePage: https://pypi.python.org/pypi/six
|
PackageHomePage: https://pypi.python.org/pypi/six
|
||||||
PackageLicenseDeclared: MIT
|
PackageLicenseDeclared: MIT
|
||||||
|
|
110
bin/sbang
110
bin/sbang
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
#
|
#
|
||||||
# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other
|
# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other
|
||||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
# sbang project developers. See the top-level COPYRIGHT file for details.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
|
||||||
|
@ -12,85 +12,16 @@
|
||||||
# arguments in shebang lines, making it hard to use interpreters that are
|
# arguments in shebang lines, making it hard to use interpreters that are
|
||||||
# deep in the directory hierarchy or require special arguments.
|
# deep in the directory hierarchy or require special arguments.
|
||||||
#
|
#
|
||||||
# `sbang` can run such scripts, either as a shebang interpreter, or
|
# To use, put the long shebang on the second line of your script, and
|
||||||
# directly on the command line.
|
# make sbang the interpreter, like this:
|
||||||
#
|
#
|
||||||
# Usage
|
# #!/bin/sh /path/to/sbang
|
||||||
# -----
|
# #!/long/path/to/real/interpreter with arguments
|
||||||
# Suppose you have a script, long-shebang.sh, like this:
|
|
||||||
#
|
#
|
||||||
# 1 #!/very/long/path/to/some/interp
|
# `sbang` will run the real interpreter with the script as its argument.
|
||||||
# 2
|
|
||||||
# 3 echo "success!"
|
|
||||||
#
|
#
|
||||||
# Invoking this script will result in an error on some OS's. On
|
# See https://github.com/spack/sbang for more details.
|
||||||
# Linux, you get this:
|
|
||||||
#
|
#
|
||||||
# $ ./longshebang.sh
|
|
||||||
# -bash: ./longshebang.sh: /very/long/path/to/some/interp: bad interpreter:
|
|
||||||
# No such file or directory
|
|
||||||
#
|
|
||||||
# On macOS, the system simply assumes the interpreter is the shell and
|
|
||||||
# tries to run with it, which is not likely 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/sh /path/to/sbang` on line 1, and move the original
|
|
||||||
# shebang to line 2 of the script:
|
|
||||||
#
|
|
||||||
# 1 #!/bin/sh /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 an argument to `/bin/sh`. Obviously, for
|
|
||||||
# this to work, `sbang` needs to have a short enough path that *it* will
|
|
||||||
# run without hitting OS limits.
|
|
||||||
#
|
|
||||||
# For Lua, node, and php scripts, the second line can't start with #!, as
|
|
||||||
# # is not the comment character in these languages (though they all
|
|
||||||
# ignore #! on the *first* line of a script). So, instrument such scripts
|
|
||||||
# like this, using --, //, or <?php ... ?> instead of # on the second
|
|
||||||
# line, e.g.:
|
|
||||||
#
|
|
||||||
# 1 #!/bin/sh /path/to/sbang
|
|
||||||
# 2 --!/long/path/to/lua with arguments
|
|
||||||
# 3 print "success!"
|
|
||||||
#
|
|
||||||
# 1 #!/bin/sh /path/to/sbang
|
|
||||||
# 2 //!/long/path/to/node with arguments
|
|
||||||
# 3 print "success!"
|
|
||||||
#
|
|
||||||
# 1 #!/bin/sh /path/to/sbang
|
|
||||||
# 2 <?php #/long/path/to/php with arguments ?>
|
|
||||||
# 3 <?php echo "success!\n"; ?>
|
|
||||||
#
|
|
||||||
# How it works
|
|
||||||
# ------------
|
|
||||||
# `sbang` is a very simple posix shell 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.
|
|
||||||
#
|
|
||||||
|
|
||||||
# We disable two shellcheck errors below:
|
|
||||||
# SC2124: when saving arguments, we intentionally assign as an array
|
|
||||||
# SC2086: when splitting $shebang_line and exec args, we want to expand args
|
|
||||||
|
|
||||||
# Generic error handling
|
# Generic error handling
|
||||||
die() {
|
die() {
|
||||||
|
@ -130,27 +61,16 @@ while read -r line && [ $lines -ne 2 ]; do
|
||||||
lines=$((lines+1))
|
lines=$((lines+1))
|
||||||
done < "$script"
|
done < "$script"
|
||||||
|
|
||||||
# shellcheck disable=SC2124
|
|
||||||
# this saves arguments for later and intentionally assigns as an array
|
|
||||||
args="$@"
|
|
||||||
|
|
||||||
# handle scripts with sbang parameters, e.g.:
|
|
||||||
#
|
|
||||||
# #!/<spack-long-path>/perl -w
|
|
||||||
#
|
|
||||||
# put the shebang line with all the parameters in the $@ array and get
|
|
||||||
# the first element.
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
set $shebang_line
|
|
||||||
set -- "$@"
|
|
||||||
interpreter="$1"
|
|
||||||
arg1="$2"
|
|
||||||
|
|
||||||
# error if we did not find any interpreter
|
# error if we did not find any interpreter
|
||||||
if [ -z "$interpreter" ]; then
|
if [ -z "$shebang_line" ]; then
|
||||||
die "error: sbang found no interpreter in $script"
|
die "error: sbang found no interpreter in $script"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# parse out the interpreter and first argument
|
||||||
|
IFS=' ' read -r interpreter arg1 rest <<EOF
|
||||||
|
$shebang_line
|
||||||
|
EOF
|
||||||
|
|
||||||
# Determine if the interpreter is a particular program, accounting for the
|
# Determine if the interpreter is a particular program, accounting for the
|
||||||
# '#!/usr/bin/env PROGRAM' convention. So:
|
# '#!/usr/bin/env PROGRAM' convention. So:
|
||||||
#
|
#
|
||||||
|
@ -176,8 +96,8 @@ fi
|
||||||
#
|
#
|
||||||
if interpreter_is perl || interpreter_is ruby; then
|
if interpreter_is perl || interpreter_is ruby; then
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
$exec $shebang_line -x "$args"
|
$exec $shebang_line -x "$@"
|
||||||
else
|
else
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
$exec $shebang_line "$args"
|
$exec $shebang_line "$@"
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -32,7 +32,6 @@
|
||||||
# spack scripts
|
# spack scripts
|
||||||
r'^bin/spack$',
|
r'^bin/spack$',
|
||||||
r'^bin/spack-python$',
|
r'^bin/spack-python$',
|
||||||
r'^bin/sbang$',
|
|
||||||
|
|
||||||
# all of spack core
|
# all of spack core
|
||||||
r'^lib/spack/spack/.*\.py$',
|
r'^lib/spack/spack/.*\.py$',
|
||||||
|
|
|
@ -217,68 +217,3 @@ def test_install_sbang(install_mockery):
|
||||||
# install again and make sure sbang is still fine
|
# install again and make sure sbang is still fine
|
||||||
sbang.install_sbang()
|
sbang.install_sbang()
|
||||||
check_sbang_installation()
|
check_sbang_installation()
|
||||||
|
|
||||||
|
|
||||||
def test_sbang_fails_without_argument():
|
|
||||||
sbang = which(spack.paths.sbang_script)
|
|
||||||
sbang(fail_on_error=False)
|
|
||||||
assert sbang.returncode == 1
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("shebang,returncode,expected", [
|
|
||||||
# perl, with and without /usr/bin/env
|
|
||||||
("#!/path/to/perl", 0, "/path/to/perl -x"),
|
|
||||||
("#!/usr/bin/env perl", 0, "/usr/bin/env perl -x"),
|
|
||||||
|
|
||||||
# perl -w, with and without /usr/bin/env
|
|
||||||
("#!/path/to/perl -w", 0, "/path/to/perl -w -x"),
|
|
||||||
("#!/usr/bin/env perl -w", 0, "/usr/bin/env perl -w -x"),
|
|
||||||
|
|
||||||
# ruby, with and without /usr/bin/env
|
|
||||||
("#!/path/to/ruby", 0, "/path/to/ruby -x"),
|
|
||||||
("#!/usr/bin/env ruby", 0, "/usr/bin/env ruby -x"),
|
|
||||||
|
|
||||||
# python, with and without /usr/bin/env
|
|
||||||
("#!/path/to/python", 0, "/path/to/python"),
|
|
||||||
("#!/usr/bin/env python", 0, "/usr/bin/env python"),
|
|
||||||
|
|
||||||
# php with one-line php comment
|
|
||||||
("<?php #!/usr/bin/php ?>", 0, "/usr/bin/php"),
|
|
||||||
|
|
||||||
# simple shell scripts
|
|
||||||
("#!/bin/sh", 0, "/bin/sh"),
|
|
||||||
("#!/bin/bash", 0, "/bin/bash"),
|
|
||||||
|
|
||||||
# error case: sbang as infinite loop
|
|
||||||
("#!/path/to/sbang", 1, None),
|
|
||||||
("#!/usr/bin/env sbang", 1, None),
|
|
||||||
|
|
||||||
# lua
|
|
||||||
("--!/path/to/lua", 0, "/path/to/lua"),
|
|
||||||
|
|
||||||
# node
|
|
||||||
("//!/path/to/node", 0, "/path/to/node"),
|
|
||||||
])
|
|
||||||
def test_sbang_with_specific_shebang(
|
|
||||||
tmpdir, shebang, returncode, expected):
|
|
||||||
|
|
||||||
script = str(tmpdir.join("script"))
|
|
||||||
|
|
||||||
# write a script out with <shebang> on second line
|
|
||||||
with open(script, "w") as f:
|
|
||||||
f.write("#!/bin/sh {sbang}\n{shebang}\n".format(
|
|
||||||
sbang=spack.paths.sbang_script,
|
|
||||||
shebang=shebang
|
|
||||||
))
|
|
||||||
fs.set_executable(script)
|
|
||||||
|
|
||||||
# test running the script in debug, which prints what would be executed
|
|
||||||
exe = which(script)
|
|
||||||
out = exe(output=str, fail_on_error=False, env={"SBANG_DEBUG": "1"})
|
|
||||||
|
|
||||||
# check error status and output vs. expected
|
|
||||||
assert exe.returncode == returncode
|
|
||||||
|
|
||||||
if expected is not None:
|
|
||||||
expected += " " + script
|
|
||||||
assert expected == out.strip()
|
|
||||||
|
|
Loading…
Reference in a new issue