From 6ab78eb88e940cfe38b5fa8353639b574c6ee65f Mon Sep 17 00:00:00 2001
From: Todd Gamblin <tgamblin@llnl.gov>
Date: Thu, 4 Aug 2016 10:46:45 -0700
Subject: [PATCH] sbang filtering now works on non-writable files. (#1445)

- sbang now changes mode to writable and restores mode if a file is not
  writable.
---
 lib/spack/spack/hooks/sbang.py | 12 ++++++++++++
 lib/spack/spack/test/sbang.py  | 17 ++++++++++++++---
 2 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/lib/spack/spack/hooks/sbang.py b/lib/spack/spack/hooks/sbang.py
index 3a957c6e0e..02c1ce3816 100644
--- a/lib/spack/spack/hooks/sbang.py
+++ b/lib/spack/spack/hooks/sbang.py
@@ -23,6 +23,7 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##############################################################################
 import os
+import stat
 import re
 
 import llnl.util.tty as tty
@@ -62,10 +63,21 @@ def filter_shebang(path):
     if re.search(r'^#!(/[^/]*)*lua\b', original):
         original = re.sub(r'^#', '--', original)
 
+    # Change non-writable files to be writable if needed.
+    saved_mode = None
+    if not os.access(path, os.W_OK):
+        st = os.stat(path)
+        saved_mode = st.st_mode
+        os.chmod(path, saved_mode | stat.S_IWRITE)
+
     with open(path, 'w') as new_file:
         new_file.write(new_sbang_line)
         new_file.write(original)
 
+    # Restore original permissions.
+    if saved_mode is not None:
+        os.chmod(path, saved_mode)
+
     tty.warn("Patched overlong shebang in %s" % path)
 
 
diff --git a/lib/spack/spack/test/sbang.py b/lib/spack/spack/test/sbang.py
index ed54ff90b0..4ce854a1d8 100644
--- a/lib/spack/spack/test/sbang.py
+++ b/lib/spack/spack/test/sbang.py
@@ -26,6 +26,7 @@
 Test that Spack's shebang filtering works correctly.
 """
 import os
+import stat
 import unittest
 import tempfile
 import shutil
@@ -41,6 +42,7 @@
 sbang_line       = '#!/bin/bash %s/bin/sbang\n' % spack.spack_root
 last_line        = "last!\n"
 
+
 class SbangTest(unittest.TestCase):
     def setUp(self):
         self.tempdir = tempfile.mkdtemp()
@@ -74,10 +76,8 @@ def setUp(self):
             f.write(long_line)
             f.write(last_line)
 
-
     def tearDown(self):
-         shutil.rmtree(self.tempdir, ignore_errors=True)
-
+        shutil.rmtree(self.tempdir, ignore_errors=True)
 
     def test_shebang_handling(self):
         filter_shebangs_in_directory(self.tempdir)
@@ -104,3 +104,14 @@ def test_shebang_handling(self):
             self.assertEqual(f.readline(), sbang_line)
             self.assertEqual(f.readline(), long_line)
             self.assertEqual(f.readline(), last_line)
+
+    def test_shebang_handles_non_writable_files(self):
+        # make a file non-writable
+        st = os.stat(self.long_shebang)
+        not_writable_mode = st.st_mode & ~stat.S_IWRITE
+        os.chmod(self.long_shebang, not_writable_mode)
+
+        self.test_shebang_handling()
+
+        st = os.stat(self.long_shebang)
+        self.assertEqual(oct(not_writable_mode), oct(st.st_mode))
-- 
GitLab