# HG changeset patch # User dfraser # Date 1236607197 0 # Node ID 4caa400abe8e0f0dc7b8012d7ef65be1920d2eb0 # Parent e2eef154f1afc0f76eb73038b4d296e2dba1cfbd This alters the `svn:checkout` command to take an extra parameter, `shared_path`, which will be used to share checkouts, preventing checking out the whole project for every revision that's tested. `shared_path` is relative to the project directory, so for standard usage set `shared_path` to something like `../trunk` Fixes #289 diff --git a/bitten/build/svntools.py b/bitten/build/svntools.py --- a/bitten/build/svntools.py +++ b/bitten/build/svntools.py @@ -13,12 +13,61 @@ import logging import posixpath import re +import shutil +import os log = logging.getLogger('bitten.build.svntools') __docformat__ = 'restructuredtext en' -def checkout(ctxt, url, path=None, revision=None, dir_='.', verbose=False): +class Error(EnvironmentError): + pass + +def copytree(src, dst, symlinks=False): + """Recursively copy a directory tree using copy2(). + + If exception(s) occur, an Error 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. + + Adapted from shtuil.copytree + + """ + names = os.listdir(src) + if not os.path.isdir(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): + copytree(srcname, dstname, symlinks) + else: + shutil.copy2(srcname, dstname) + except (IOError, os.error), why: + errors.append((srcname, dstname, str(why))) + # catch the Error from the recursive copytree so that we can + # continue with other files + except Error, 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 Error, errors + +def checkout(ctxt, url, path=None, revision=None, dir_='.', verbose=False, shared_path=None): """Perform a checkout from a Subversion repository. :param ctxt: the build context @@ -28,18 +77,27 @@ :param revision: the revision to check out :param dir_: the name of a local subdirectory to check out into :param verbose: whether to log the list of checked out files + :param shared_path: a shared directory to do the checkout in, before copying to dir_ """ args = ['checkout'] if revision: args += ['-r', revision] if path: - url = posixpath.join(url, path.lstrip('/')) - args += [url, dir_] + final_url = posixpath.join(url, path.lstrip('/')) + args += [final_url, dir_] cofilter = None if not verbose: cre = re.compile(r'^[AU]\s.*$') cofilter = lambda s: cre.sub('', s) + if shared_path is not None: + # run checkout on shared_path, then copy + shared_path = ctxt.resolve(shared_path) + checkout(ctxt, url, path, revision, dir_=shared_path, verbose=verbose) + try: + copytree(shared_path, ctxt.resolve(dir_)) + except Exception, e: + ctxt.log('error copying shared tree (%s)' % e) from bitten.build import shtools returncode = shtools.execute(ctxt, file_='svn', args=args, filter_=cofilter) diff --git a/doc/commands.txt b/doc/commands.txt --- a/doc/commands.txt +++ b/doc/commands.txt @@ -734,25 +734,30 @@ Parameters ---------- -+--------------+-------------------------------------------------------------+ -| Name | Description | -+==============+=============================================================+ -| ``url`` | URL of the repository. | -+--------------+-------------------------------------------------------------+ -| ``path`` | The path inside the repository that should be checked out. | -| | You should normally set this to ``${path}`` so that the | -| | path of the build configuration is used. | -+--------------+-------------------------------------------------------------+ -| ``revision`` | The revision that should be checked out. You should | -| | normally set this to ``${revision}`` so that the revision | -| | of the build is used. | -+--------------+-------------------------------------------------------------+ -| ``dir`` | Path specifying which directory the sources should be | -| | checked out to (defaults to '.'). | -+--------------+-------------------------------------------------------------+ -| ``verbose`` | Whether to log the list of checked out files (defaults to | -| | False). | -+--------------+-------------------------------------------------------------+ ++-----------------+-------------------------------------------------------------+ +| Name | Description | ++=================+=============================================================+ +| ``url`` | URL of the repository. | ++-----------------+-------------------------------------------------------------+ +| ``path`` | The path inside the repository that should be checked out. | +| | You should normally set this to ``${path}`` so that the | +| | path of the build configuration is used. | ++-----------------+-------------------------------------------------------------+ +| ``revision`` | The revision that should be checked out. You should | +| | normally set this to ``${revision}`` so that the revision | +| | of the build is used. | ++-----------------+-------------------------------------------------------------+ +| ``dir`` | Path specifying which directory the sources should be | +| | checked out to (defaults to '.'). | ++-----------------+-------------------------------------------------------------+ +| ``verbose`` | Whether to log the list of checked out files (defaults to | +| | False). | ++-----------------+-------------------------------------------------------------+ +| ``shared_path`` | An optional shared directory to check the sources out in, | +| | which will be reused for each subsequent build. This is | +| | relative to the project directory, so for standard usage | +| | set it to something like ``../trunk`` | ++-----------------+-------------------------------------------------------------+ Examples