From ca9059c7a6cbcff94e379e048d5166e77d606283 Mon Sep 17 00:00:00 2001 From: stevenknight Date: Sat, 20 Aug 2005 23:10:35 +0000 Subject: [PATCH] Handle IOError exceptions when pushing files to CacheDir (and elsewhere). git-svn-id: http://scons.tigris.org/svn/scons/trunk@1339 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- src/engine/SCons/Node/FS.py | 18 ++++++++++----- src/engine/SCons/SConsign.py | 15 +++++++++++-- src/engine/SCons/Scanner/Dir.py | 2 +- src/engine/SCons/Script/Main.py | 38 ++++++++++++++++++++++++++++++-- src/engine/SCons/Tool/hpc++.py | 4 +++- src/engine/SCons/Tool/hplink.py | 4 +++- src/engine/SCons/Tool/sunlink.py | 4 +++- src/engine/SCons/Util.py | 36 ++++-------------------------- src/engine/SCons/UtilTests.py | 33 --------------------------- 9 files changed, 76 insertions(+), 78 deletions(-) diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index c7905b5b..6eb61d99 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -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)) diff --git a/src/engine/SCons/SConsign.py b/src/engine/SCons/SConsign.py index d99df4e4..67b2affa 100644 --- a/src/engine/SCons/SConsign.py +++ b/src/engine/SCons/SConsign.py @@ -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 diff --git a/src/engine/SCons/Scanner/Dir.py b/src/engine/SCons/Scanner/Dir.py index 7c39f26b..e722230f 100644 --- a/src/engine/SCons/Scanner/Dir.py +++ b/src/engine/SCons/Scanner/Dir.py @@ -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) diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index e5a8cb2d..67b3308a 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -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 diff --git a/src/engine/SCons/Tool/hpc++.py b/src/engine/SCons/Tool/hpc++.py index a1b3e974..3276412d 100644 --- a/src/engine/SCons/Tool/hpc++.py +++ b/src/engine/SCons/Tool/hpc++.py @@ -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: diff --git a/src/engine/SCons/Tool/hplink.py b/src/engine/SCons/Tool/hplink.py index f3d03c92..5921b39d 100644 --- a/src/engine/SCons/Tool/hplink.py +++ b/src/engine/SCons/Tool/hplink.py @@ -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: diff --git a/src/engine/SCons/Tool/sunlink.py b/src/engine/SCons/Tool/sunlink.py index 66dd7c0f..665d6566 100644 --- a/src/engine/SCons/Tool/sunlink.py +++ b/src/engine/SCons/Tool/sunlink.py @@ -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: diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index 084975a9..2859807a 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -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 diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py index ce9fd560..19ec7487 100644 --- a/src/engine/SCons/UtilTests.py +++ b/src/engine/SCons/UtilTests.py @@ -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 -- 2.26.2