summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorHarmen Stoppels <harmenstoppels@gmail.com>2022-03-23 10:31:23 +0100
committerGitHub <noreply@github.com>2022-03-23 10:31:23 +0100
commit773da7ceba116154e999187e0d9bcef5d58bebd4 (patch)
treee749f12fd66fb9b0279863455a97c1e17434588b /lib
parent487b1c369098c050aa04f8fd99bf3a6e578847c1 (diff)
downloadspack-773da7ceba116154e999187e0d9bcef5d58bebd4.tar.gz
spack-773da7ceba116154e999187e0d9bcef5d58bebd4.tar.bz2
spack-773da7ceba116154e999187e0d9bcef5d58bebd4.tar.xz
spack-773da7ceba116154e999187e0d9bcef5d58bebd4.zip
python: drop dependency on `file` for script check (#29513)
`file` was used to detect Python scripts with shebangs, so that the interpreter could be changed from <python prefix> to <view path>. With this change, we detect shebangs using Python instead, so that `file` is no longer required.
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/llnl/util/filesystem.py23
-rw-r--r--lib/spack/spack/build_systems/python.py4
-rw-r--r--lib/spack/spack/test/llnl/util/filesystem.py24
3 files changed, 49 insertions, 2 deletions
diff --git a/lib/spack/llnl/util/filesystem.py b/lib/spack/llnl/util/filesystem.py
index 22ca97c347..179e1703b9 100644
--- a/lib/spack/llnl/util/filesystem.py
+++ b/lib/spack/llnl/util/filesystem.py
@@ -617,6 +617,29 @@ def get_filetype(path_name):
return output.strip()
+@system_path_filter
+def is_nonsymlink_exe_with_shebang(path):
+ """
+ Returns whether the path is an executable script with a shebang.
+ Return False when the path is a *symlink* to an executable script.
+ """
+ try:
+ st = os.lstat(path)
+ # Should not be a symlink
+ if stat.S_ISLNK(st.st_mode):
+ return False
+
+ # Should be executable
+ if not st.st_mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH):
+ return False
+
+ # Should start with a shebang
+ with open(path, 'rb') as f:
+ return f.read(2) == b'#!'
+ except (IOError, OSError):
+ return False
+
+
@system_path_filter(arg_slice=slice(1))
def chgrp_if_not_world_writable(path, group):
"""chgrp path to group if path is not world writable"""
diff --git a/lib/spack/spack/build_systems/python.py b/lib/spack/spack/build_systems/python.py
index b3552f0337..6aa225ba6c 100644
--- a/lib/spack/spack/build_systems/python.py
+++ b/lib/spack/spack/build_systems/python.py
@@ -11,7 +11,7 @@ import llnl.util.tty as tty
from llnl.util.filesystem import (
filter_file,
find,
- get_filetype,
+ is_nonsymlink_exe_with_shebang,
path_contains_subdirectory,
same_path,
working_dir,
@@ -230,7 +230,7 @@ class PythonPackage(PackageBase):
view.link(src, dst)
elif not os.path.islink(src):
shutil.copy2(src, dst)
- is_script = 'script' in get_filetype(src)
+ is_script = is_nonsymlink_exe_with_shebang(src)
if is_script and not python_is_external:
filter_file(
python_prefix, os.path.abspath(
diff --git a/lib/spack/spack/test/llnl/util/filesystem.py b/lib/spack/spack/test/llnl/util/filesystem.py
index 2bd6994754..b5a4594efb 100644
--- a/lib/spack/spack/test/llnl/util/filesystem.py
+++ b/lib/spack/spack/test/llnl/util/filesystem.py
@@ -674,3 +674,27 @@ def test_temporary_dir_context_manager():
with fs.temporary_dir() as tmp_dir:
assert previous_dir != os.path.realpath(os.getcwd())
assert os.path.realpath(str(tmp_dir)) == os.path.realpath(os.getcwd())
+
+
+@pytest.mark.skipif(sys.platform == 'win32', reason="No shebang on Windows")
+def test_is_nonsymlink_exe_with_shebang(tmpdir):
+ with tmpdir.as_cwd():
+ # Create an executable with shebang.
+ with open('executable_script', 'wb') as f:
+ f.write(b'#!/interpreter')
+ os.chmod('executable_script', 0o100775)
+
+ with open('executable_but_not_script', 'wb') as f:
+ f.write(b'#/not-a-shebang')
+ os.chmod('executable_but_not_script', 0o100775)
+
+ with open('not_executable_with_shebang', 'wb') as f:
+ f.write(b'#!/interpreter')
+ os.chmod('not_executable_with_shebang', 0o100664)
+
+ os.symlink('executable_script', 'symlink_to_executable_script')
+
+ assert fs.is_nonsymlink_exe_with_shebang('executable_script')
+ assert not fs.is_nonsymlink_exe_with_shebang('executable_but_not_script')
+ assert not fs.is_nonsymlink_exe_with_shebang('not_executable_with_shebang')
+ assert not fs.is_nonsymlink_exe_with_shebang('symlink_to_executable_script')