summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGary Oberbrunner <garyo@oberbrunner.com>2010-11-18 13:48:37 (GMT)
committerGary Oberbrunner <garyo@oberbrunner.com>2010-11-18 13:48:37 (GMT)
commit3f478c8dbe11dbb4eabb77cf6443a10ed039af0d (patch)
tree16d9c6c9ebd7647d7cd8bb2b4e5567a68983fbe0
parent9de4764cf8bdfb9da9565e3c38878eb1c1b03377 (diff)
downloadSCons-3f478c8dbe11dbb4eabb77cf6443a10ed039af0d.zip
SCons-3f478c8dbe11dbb4eabb77cf6443a10ed039af0d.tar.gz
SCons-3f478c8dbe11dbb4eabb77cf6443a10ed039af0d.tar.bz2
Fix Install() when src and target are dirs and target dir already exists, by using our own copytree implementation.
-rw-r--r--src/CHANGES.txt5
-rw-r--r--src/engine/SCons/Tool/install.py56
-rw-r--r--test/Install/dir-exists.py65
3 files changed, 125 insertions, 1 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 29cb719..3cfc76b 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -7,6 +7,11 @@
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
diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py
index f328248..7e5fed5 100644
--- a/src/engine/SCons/Tool/install.py
+++ b/src/engine/SCons/Tool/install.py
@@ -44,6 +44,60 @@ from SCons.Util import make_path_relative
_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.
#
@@ -59,7 +113,7 @@ def copyFunc(dest, source, env):
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)
diff --git a/test/Install/dir-exists.py b/test/Install/dir-exists.py
new file mode 100644
index 0000000..f981a35
--- /dev/null
+++ b/test/Install/dir-exists.py
@@ -0,0 +1,65 @@
+#!/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: