Handle IOError exceptions when pushing files to CacheDir (and elsewhere).
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 20 Aug 2005 23:10:35 +0000 (23:10 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 20 Aug 2005 23:10:35 +0000 (23:10 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@1339 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/engine/SCons/Node/FS.py
src/engine/SCons/SConsign.py
src/engine/SCons/Scanner/Dir.py
src/engine/SCons/Script/Main.py
src/engine/SCons/Tool/hpc++.py
src/engine/SCons/Tool/hplink.py
src/engine/SCons/Tool/sunlink.py
src/engine/SCons/Util.py
src/engine/SCons/UtilTests.py

index c7905b5bcb6eb9ae49b24bb71573967b87571cdb..6eb61d9974b63597ff90af008ea71ecd91e42ed6 100644 (file)
@@ -156,7 +156,13 @@ def LinkFunc(target, source, env):
         try:
             func(src,dest)
             break
-        except OSError:
+        except (IOError, OSError):
+            # An OSError indicates something happened like a permissions
+            # problem or an attempt to symlink across file-system
+            # boundaries.  An IOError indicates something like the file
+            # not existing.  In either case, keeping trying additional
+            # functions in the list and only raise an error if the last
+            # one failed.
             if func == Link_Funcs[-1]:
                 # exception of the last link method (copy) are fatal
                 raise
@@ -240,10 +246,12 @@ def CachePushFunc(target, source, env):
         fs.rename(tempfile, cachefile)
         st = fs.stat(t.path)
         fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
-    except OSError:
-        # It's possible someone else tried writing the file at the same
-        # time we did.  Print a warning but don't stop the build, since
-        # it doesn't affect the correctness of the build.
+    except (IOError, OSError):
+        # It's possible someone else tried writing the file at the
+        # same time we did, or else that there was some problem like
+        # the CacheDir being on a separate file system that's full.
+        # In any case, inability to push a file to cache doesn't affect
+        # the correctness of the build, so just print a warning.
         SCons.Warnings.warn(SCons.Warnings.CacheWriteErrorWarning,
                             "Unable to copy %s to cache. Cache file is %s"
                                 % (str(target), cachefile))
index d99df4e407f192c8e016f08d925d70c73b9b85d0..67b2affab7c81d824c26e9d9a260bf85960147d6 100644 (file)
@@ -297,16 +297,27 @@ class DirFile(Dir):
                     mode = os.stat(self.sconsign)[0]
                     os.chmod(self.sconsign, 0666)
                     os.unlink(self.sconsign)
-                except OSError:
+                except (IOError, OSError):
+                    # Try to carry on in the face of either OSError
+                    # (things like permission issues) or IOError (disk
+                    # or network issues).  If there's a really dangerous
+                    # issue, it should get re-raised by the calls below.
                     pass
                 try:
                     os.rename(fname, self.sconsign)
                 except OSError:
+                    # An OSError failure to rename may indicate something
+                    # like the directory has no write permission, but
+                    # the .sconsign file itself might still be writable,
+                    # so try writing on top of it directly.  An IOError
+                    # here, or in any of the following calls, would get
+                    # raised, indicating something like a potentially
+                    # serious disk or network issue.
                     open(self.sconsign, 'wb').write(open(fname, 'rb').read())
                     os.chmod(self.sconsign, mode)
             try:
                 os.unlink(temp)
-            except OSError:
+            except (IOError, OSError):
                 pass
 
 ForDirectory = DB
index 7c39f26b399ed44c33fba8cac595ec261e39c81c..e722230f381dfcc998e4514751efb9c559463fdb 100644 (file)
@@ -53,7 +53,7 @@ def scan(node, env, path=()):
     """
     try:
         flist = node.fs.listdir(node.abspath)
-    except OSError:
+    except (IOError, OSError):
         return []
     dont_scan = lambda k: not skip_entry.has_key(k)
     flist = filter(dont_scan, flist)
index e5a8cb2d5414545b11e869578adb72bf64aac9f0..67b3308a77072461c1050c9d7517fd49c6290ff9 100644 (file)
@@ -190,6 +190,35 @@ class BuildTask(SCons.Taskmaster.Task):
 
 class CleanTask(SCons.Taskmaster.Task):
     """An SCons clean task."""
+    def dir_index(self, directory):
+        dirname = lambda f, d=directory: os.path.join(d, f)
+        files = map(dirname, os.listdir(directory))
+
+        # os.listdir() isn't guaranteed to return files in any specific order,
+        # but some of the test code expects sorted output.
+        files.sort()
+        return files
+
+    def fs_delete(self, path, remove=1):
+        try:
+            if os.path.exists(path):
+                if os.path.isfile(path):
+                    if remove: os.unlink(path)
+                    display("Removed " + path)
+                elif os.path.isdir(path) and not os.path.islink(path):
+                    # delete everything in the dir
+                    for p in self.dir_index(path):
+                        if os.path.isfile(p):
+                            if remove: os.unlink(p)
+                            display("Removed " + p)
+                        else:
+                            self.fs_delete(p, remove)
+                    # then delete dir itself
+                    if remove: os.rmdir(path)
+                    display("Removed directory " + path)
+        except (IOError, OSError), e:
+            print "scons: Could not remove '%s':" % str(path), e.strerror
+
     def show(self):
         target = self.targets[0]
         if (target.has_builder() or target.side_effect) and not target.isdir():
@@ -197,7 +226,7 @@ class CleanTask(SCons.Taskmaster.Task):
         if SCons.Environment.CleanTargets.has_key(target):
             files = SCons.Environment.CleanTargets[target]
             for f in files:
-                SCons.Util.fs_delete(str(f), 0)
+                self.fs_delete(str(f), 0)
 
     def remove(self):
         target = self.targets[0]
@@ -206,6 +235,11 @@ class CleanTask(SCons.Taskmaster.Task):
                 try:
                     removed = t.remove()
                 except OSError, e:
+                    # An OSError may indicate something like a permissions
+                    # issue, an IOError would indicate something like
+                    # the file not existing.  In either case, print a
+                    # message and keep going to try to remove as many
+                    # targets aa possible.
                     print "scons: Could not remove '%s':" % str(t), e.strerror
                 else:
                     if removed:
@@ -213,7 +247,7 @@ class CleanTask(SCons.Taskmaster.Task):
         if SCons.Environment.CleanTargets.has_key(target):
             files = SCons.Environment.CleanTargets[target]
             for f in files:
-                SCons.Util.fs_delete(str(f))
+                self.fs_delete(str(f))
 
     execute = remove
 
index a1b3e97482a35b7659cf780fa4a179fa1c8c2633..3276412dacd41016a70e5d105507bc37b16ed1d3 100644 (file)
@@ -46,7 +46,9 @@ acc = None
 
 try:
     dirs = os.listdir('/opt')
-except OSError:
+except (IOError, OSError):
+    # Not being able to read the directory because it doesn't exist
+    # (IOError) or isn't readable (OSError) is okay.
     dirs = []
 
 for dir in dirs:
index f3d03c924ef9ce7a5fcec149313918944053c229..5921b39d64ef0a4f1fcd818945e973a5c77cf44c 100644 (file)
@@ -45,7 +45,9 @@ ccLinker = None
 
 try:
     dirs = os.listdir('/opt')
-except OSError:
+except (IOError, OSError):
+    # Not being able to read the directory because it doesn't exist
+    # (IOError) or isn't readable (OSError) is okay.
     dirs = []
 
 for dir in dirs:
index 66dd7c0f1f5939eeb342ceb6458046fb86ba1212..665d6566725e52f33127900598f5a7dd3f0b9e50 100644 (file)
@@ -45,7 +45,9 @@ ccLinker = None
 
 try:
     dirs = os.listdir('/opt')
-except OSError:
+except (IOError, OSError):
+    # Not being able to read the directory because it doesn't exist
+    # (IOError) or isn't readable (OSError) is okay.
     dirs = []
 
 for d in dirs:
index 084975a9846444f786939b6aeb2fef0a71f2ec7e..2859807a7437ae79eb4357eeeffc4d0e7dd92561 100644 (file)
@@ -1380,6 +1380,10 @@ else:
                 try:
                     st = os.stat(f)
                 except OSError:
+                    # os.stat() raises OSError, not IOError if the file
+                    # doesn't exist, so in this case we let IOError get
+                    # raised so as to not mask possibly serious disk or
+                    # network issues.
                     continue
                 if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
                     try:
@@ -1478,38 +1482,6 @@ def AppendPath(oldpath, newpath, sep = os.pathsep):
     else:
         return string.join(paths, sep)
 
-
-def dir_index(directory):
-    files = []
-    for f in os.listdir(directory):
-        fullname = os.path.join(directory, f)
-        files.append(fullname)
-
-    # os.listdir() isn't guaranteed to return files in any specific order,
-    # but some of the test code expects sorted output.
-    files.sort()
-    return files
-
-def fs_delete(path, remove=1):
-    try:
-        if os.path.exists(path):
-            if os.path.isfile(path):
-                if remove: os.unlink(path)
-                display("Removed " + path)
-            elif os.path.isdir(path) and not os.path.islink(path):
-                # delete everything in the dir
-                for p in dir_index(path):
-                    if os.path.isfile(p):
-                        if remove: os.unlink(p)
-                        display("Removed " + p)
-                    else:
-                        fs_delete(p, remove)
-                # then delete dir itself
-                if remove: os.rmdir(path)
-                display("Removed directory " + path)
-    except OSError, e:
-        print "scons: Could not remove '%s':" % str(path), e.strerror
-
 if sys.platform == 'cygwin':
     def get_native_path(path):
         """Transforms an absolute path into a native path for the system.  In
index ce9fd5600f4f01444229bbd7fb031516f4b3b515..19ec7487deae2c6c75328a1dd5402af681213acb 100644 (file)
@@ -1388,39 +1388,6 @@ class UtilTestCase(unittest.TestCase):
         assert sys.stdout.buffer == "line1\nline3\nline4\n"
         sys.stdout = old_stdout
 
-    def test_fs_delete(self):
-        test = TestCmd.TestCmd(workdir = '')
-        base = test.workpath('')
-        xxx = test.workpath('xxx.xxx')
-        ZZZ = test.workpath('ZZZ.ZZZ')
-        sub1_yyy = test.workpath('sub1', 'yyy.yyy')
-
-        test.subdir('sub1')
-        test.write(xxx, "\n")
-        test.write(ZZZ, "\n")
-        test.write(sub1_yyy, "\n")
-
-        old_stdout = sys.stdout
-        sys.stdout = OutBuffer()
-
-        exp = "Removed " + os.path.join(base, ZZZ) + "\n" + \
-              "Removed " + os.path.join(base, sub1_yyy) + '\n' + \
-              "Removed directory " + os.path.join(base, 'sub1') + '\n' + \
-              "Removed " + os.path.join(base, xxx) + '\n' + \
-              "Removed directory " + base + '\n'
-
-        fs_delete(base, remove=0)
-        assert sys.stdout.buffer == exp, sys.stdout.buffer
-        assert os.path.exists(sub1_yyy)
-
-        sys.stdout.buffer = ""
-        fs_delete(base, remove=1)
-        assert sys.stdout.buffer == exp
-        assert not os.path.exists(base)
-
-        test._dirlist = None
-        sys.stdout = old_stdout
-
     def test_get_native_path(self):
         """Test the get_native_path() function."""
         import tempfile