| 1 | # -*- coding: utf-8 -*- |
---|
| 2 | # |
---|
| 3 | # Copyright (C) 2007 Christopher Lenz <cmlenz@gmx.de> |
---|
| 4 | # Copyright (C) 2007-2010 Edgewall Software |
---|
| 5 | # All rights reserved. |
---|
| 6 | # |
---|
| 7 | # This software is licensed as described in the file COPYING, which |
---|
| 8 | # you should have received as part of this distribution. The terms |
---|
| 9 | # are also available at http://bitten.edgewall.org/wiki/License. |
---|
| 10 | |
---|
| 11 | """Recipe commands for Subversion.""" |
---|
| 12 | |
---|
| 13 | import logging |
---|
| 14 | import posixpath |
---|
| 15 | import re |
---|
| 16 | import shutil |
---|
| 17 | import os |
---|
| 18 | |
---|
| 19 | log = logging.getLogger('bitten.build.svntools') |
---|
| 20 | |
---|
| 21 | __docformat__ = 'restructuredtext en' |
---|
| 22 | |
---|
| 23 | class Error(EnvironmentError): |
---|
| 24 | pass |
---|
| 25 | |
---|
| 26 | def copytree(src, dst, symlinks=False): |
---|
| 27 | """Recursively copy a directory tree using copy2(). |
---|
| 28 | |
---|
| 29 | If exception(s) occur, an Error is raised with a list of reasons. |
---|
| 30 | |
---|
| 31 | If the optional symlinks flag is true, symbolic links in the |
---|
| 32 | source tree result in symbolic links in the destination tree; if |
---|
| 33 | it is false, the contents of the files pointed to by symbolic |
---|
| 34 | links are copied. |
---|
| 35 | |
---|
| 36 | Adapted from shtuil.copytree |
---|
| 37 | |
---|
| 38 | """ |
---|
| 39 | names = os.listdir(src) |
---|
| 40 | if not os.path.isdir(dst): |
---|
| 41 | os.makedirs(dst) |
---|
| 42 | errors = [] |
---|
| 43 | for name in names: |
---|
| 44 | srcname = os.path.join(src, name) |
---|
| 45 | dstname = os.path.join(dst, name) |
---|
| 46 | try: |
---|
| 47 | if symlinks and os.path.islink(srcname): |
---|
| 48 | linkto = os.readlink(srcname) |
---|
| 49 | os.symlink(linkto, dstname) |
---|
| 50 | elif os.path.isdir(srcname): |
---|
| 51 | copytree(srcname, dstname, symlinks) |
---|
| 52 | else: |
---|
| 53 | shutil.copy2(srcname, dstname) |
---|
| 54 | except (IOError, os.error), why: |
---|
| 55 | errors.append((srcname, dstname, str(why))) |
---|
| 56 | # catch the Error from the recursive copytree so that we can |
---|
| 57 | # continue with other files |
---|
| 58 | except Error, err: |
---|
| 59 | errors.extend(err.args[0]) |
---|
| 60 | try: |
---|
| 61 | shutil.copystat(src, dst) |
---|
| 62 | except WindowsError: |
---|
| 63 | # can't copy file access times on Windows |
---|
| 64 | pass |
---|
| 65 | except OSError, why: |
---|
| 66 | errors.extend((src, dst, str(why))) |
---|
| 67 | if errors: |
---|
| 68 | raise Error, errors |
---|
| 69 | |
---|
| 70 | def checkout(ctxt, url, path=None, revision=None, dir_='.', verbose='false', shared_path=None, |
---|
| 71 | username=None, password=None, no_auth_cache='false'): |
---|
| 72 | """Perform a checkout from a Subversion repository. |
---|
| 73 | |
---|
| 74 | :param ctxt: the build context |
---|
| 75 | :type ctxt: `Context` |
---|
| 76 | :param url: the URL of the repository |
---|
| 77 | :param path: the path inside the repository |
---|
| 78 | :param revision: the revision to check out |
---|
| 79 | :param dir\_: the name of a local subdirectory to check out into |
---|
| 80 | :param verbose: whether to log the list of checked out files |
---|
| 81 | :param shared_path: a shared directory to do the checkout in, before copying to dir\_ |
---|
| 82 | :param username: a username of the repository |
---|
| 83 | :param password: a password of the repository |
---|
| 84 | :param no\_auth\_cache: do not cache authentication tokens |
---|
| 85 | """ |
---|
| 86 | args = ['checkout'] |
---|
| 87 | if revision: |
---|
| 88 | args += ['-r', revision] |
---|
| 89 | if path: |
---|
| 90 | final_url = posixpath.join(url, path.lstrip('/')) |
---|
| 91 | else: |
---|
| 92 | final_url = url |
---|
| 93 | if username: |
---|
| 94 | args += ['--username', username] |
---|
| 95 | if password: |
---|
| 96 | args += ['--password', password] |
---|
| 97 | if no_auth_cache.lower() == 'true': |
---|
| 98 | args += ['--no-auth-cache'] |
---|
| 99 | args += [final_url, dir_] |
---|
| 100 | |
---|
| 101 | cofilter = None |
---|
| 102 | if verbose.lower() == 'false': |
---|
| 103 | cre = re.compile(r'^[AU]\s.*$') |
---|
| 104 | cofilter = lambda s: cre.sub('', s) |
---|
| 105 | if shared_path is not None: |
---|
| 106 | # run checkout on shared_path, then copy |
---|
| 107 | shared_path = ctxt.resolve(shared_path) |
---|
| 108 | checkout(ctxt, url, path, revision, dir_=shared_path, verbose=verbose) |
---|
| 109 | try: |
---|
| 110 | copytree(shared_path, ctxt.resolve(dir_)) |
---|
| 111 | except Exception, e: |
---|
| 112 | ctxt.log('error copying shared tree (%s)' % e) |
---|
| 113 | from bitten.build import shtools |
---|
| 114 | returncode = shtools.execute(ctxt, file_='svn', args=args, |
---|
| 115 | filter_=cofilter) |
---|
| 116 | if returncode != 0: |
---|
| 117 | ctxt.error('svn checkout failed (%s)' % returncode) |
---|
| 118 | |
---|
| 119 | def export(ctxt, url, path=None, revision=None, dir_='.', |
---|
| 120 | username=None, password=None, no_auth_cache='false'): |
---|
| 121 | """Perform an export from a Subversion repository. |
---|
| 122 | |
---|
| 123 | :param ctxt: the build context |
---|
| 124 | :type ctxt: `Context` |
---|
| 125 | :param url: the URL of the repository |
---|
| 126 | :param path: the path inside the repository |
---|
| 127 | :param revision: the revision to check out |
---|
| 128 | :param dir\_: the name of a local subdirectory to export out into |
---|
| 129 | :param username: a username of the repository |
---|
| 130 | :param password: a password of the repository |
---|
| 131 | :param no\_auth\_cache: do not cache authentication tokens |
---|
| 132 | """ |
---|
| 133 | args = ['export', '--force'] |
---|
| 134 | if revision: |
---|
| 135 | args += ['-r', revision] |
---|
| 136 | if path: |
---|
| 137 | url = posixpath.join(url, path) |
---|
| 138 | if username: |
---|
| 139 | args += ['--username', username] |
---|
| 140 | if password: |
---|
| 141 | args += ['--password', password] |
---|
| 142 | if no_auth_cache.lower() == 'true': |
---|
| 143 | args += ['--no-auth-cache'] |
---|
| 144 | args += [url, dir_] |
---|
| 145 | |
---|
| 146 | from bitten.build import shtools |
---|
| 147 | returncode = shtools.execute(ctxt, file_='svn', args=args) |
---|
| 148 | if returncode != 0: |
---|
| 149 | ctxt.error('svn export failed (%s)' % returncode) |
---|
| 150 | |
---|
| 151 | def update(ctxt, revision=None, dir_='.', |
---|
| 152 | username=None, password=None, no_auth_cache='false'): |
---|
| 153 | """Update the local working copy from the Subversion repository. |
---|
| 154 | |
---|
| 155 | :param ctxt: the build context |
---|
| 156 | :type ctxt: `Context` |
---|
| 157 | :param revision: the revision to check out |
---|
| 158 | :param dir\_: the name of a local subdirectory containing the working copy |
---|
| 159 | :param username: a username of the repository |
---|
| 160 | :param password: a password of the repository |
---|
| 161 | :param no\_auth\_cache: do not cache authentication tokens |
---|
| 162 | """ |
---|
| 163 | args = ['update'] |
---|
| 164 | if revision: |
---|
| 165 | args += ['-r', revision] |
---|
| 166 | if username: |
---|
| 167 | args += ['--username', username] |
---|
| 168 | if password: |
---|
| 169 | args += ['--password', password] |
---|
| 170 | if no_auth_cache.lower() == 'true': |
---|
| 171 | args += ['--no-auth-cache'] |
---|
| 172 | args += [dir_] |
---|
| 173 | |
---|
| 174 | from bitten.build import shtools |
---|
| 175 | returncode = shtools.execute(ctxt, file_='svn', args=args) |
---|
| 176 | if returncode != 0: |
---|
| 177 | ctxt.error('svn update failed (%s)' % returncode) |
---|