Commits

Gary Oberbrunner committed 04c48a1

Fix Install() when src and target are dirs and target dir already exists, by using our own copytree implementation.

Comments (0)

Files changed (3)

 
 RELEASE 2.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE
 
+  From Gary Oberbrunner:
+
+    - Fix Install() when the source and target are directories and the
+      target directory exists.
+
   From Jean-Baptiste Lab:
 
     - Fix problems with appending CPPDEFINES that contain

src/engine/SCons/Tool/install.py

 _INSTALLED_FILES = []
 _UNIQUE_INSTALLED_FILES = None
 
+class CopytreeError(EnvironmentError):
+    pass
+                
+# This is a patched version of shutil.copytree from python 2.5.  It
+# doesn't fail if the dir exists, which regular copytree does
+# (annoyingly).  Note the XXX comment in the docstring.
+def scons_copytree(src, dst, symlinks=False):
+    """Recursively copy a directory tree using copy2().
+
+    The destination directory must not already exist.
+    If exception(s) occur, an CopytreeError is raised with a list of reasons.
+
+    If the optional symlinks flag is true, symbolic links in the
+    source tree result in symbolic links in the destination tree; if
+    it is false, the contents of the files pointed to by symbolic
+    links are copied.
+
+    XXX Consider this example code rather than the ultimate tool.
+
+    """
+    names = os.listdir(src)
+    # garyo@genarts.com fix: check for dir before making dirs.
+    if not os.path.exists(dst):
+        os.makedirs(dst)
+    errors = []
+    for name in names:
+        srcname = os.path.join(src, name)
+        dstname = os.path.join(dst, name)
+        try:
+            if symlinks and os.path.islink(srcname):
+                linkto = os.readlink(srcname)
+                os.symlink(linkto, dstname)
+            elif os.path.isdir(srcname):
+                scons_copytree(srcname, dstname, symlinks)
+            else:
+                shutil.copy2(srcname, dstname)
+            # XXX What about devices, sockets etc.?
+        except (IOError, os.error), why:
+            errors.append((srcname, dstname, str(why)))
+        # catch the CopytreeError from the recursive copytree so that we can
+        # continue with other files
+        except CopytreeError, err:
+            errors.extend(err.args[0])
+    try:
+        shutil.copystat(src, dst)
+    except WindowsError:
+        # can't copy file access times on Windows
+        pass
+    except OSError, why:
+        errors.extend((src, dst, str(why)))
+    if errors:
+        raise CopytreeError, errors
+
+
 #
 # Functions doing the actual work of the Install Builder.
 #
             parent = os.path.split(dest)[0]
             if not os.path.exists(parent):
                 os.makedirs(parent)
-        shutil.copytree(source, dest)
+        scons_copytree(source, dest)
     else:
         shutil.copy2(source, dest)
         st = os.stat(source)

test/Install/dir-exists.py

+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Test using Install() on directories that exist.
+"""
+
+import os.path
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+Execute(Mkdir('a'))
+Execute(Mkdir('b'))
+f=Command('a/f', None, 'echo hi > $TARGET')
+AlwaysBuild(f)
+Install('b', 'a')
+""")
+
+expect="""\
+Mkdir("a")
+Mkdir("b")
+echo hi > a%sf
+Install directory: "a" as "b%sa"
+"""%(os.sep, os.sep)
+test.run(arguments = ["-Q"], stdout = expect)
+
+test.must_exist(test.workpath('a', 'f'))
+test.must_exist(test.workpath('b', 'a', 'f'))
+
+# this run used to fail on Windows with an OS error before the copytree fix
+test.run(arguments=["-Q"])
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: