You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@gump.apache.org by le...@apache.org on 2005/11/13 19:36:57 UTC

svn commit: r333089 - in /gump/branches/Gump3/pygump/python: ./ gump/ gump/config.py gump/engine/persistence.py gump/model/util.py gump/plugins/updater.py gump/util/ gump/util/sync.py main.py

Author: leosimons
Date: Sun Nov 13 10:36:43 2005
New Revision: 333089

URL: http://svn.apache.org/viewcvs?rev=333089&view=rev
Log:
Implement GUMP-146, by creating a custom rsync-like implementation of sync functionality which is tailored to gump its needs. Considerably simpler than sync.py from gump2, considerably more efficient, and more aligned with the needs of the last-continuous-build functionality from GUMP-105. Again, since this is code heavy on 'side effects' and low on actual logic, most of it is tested through the integration test and not through unit tests. Shame on me. But it is cool stuff otherwise...

Added:
    gump/branches/Gump3/pygump/python/gump/util/sync.py
Modified:
    gump/branches/Gump3/pygump/python/   (props changed)
    gump/branches/Gump3/pygump/python/gump/   (props changed)
    gump/branches/Gump3/pygump/python/gump/config.py
    gump/branches/Gump3/pygump/python/gump/engine/persistence.py
    gump/branches/Gump3/pygump/python/gump/model/util.py
    gump/branches/Gump3/pygump/python/gump/plugins/updater.py
    gump/branches/Gump3/pygump/python/gump/util/   (props changed)
    gump/branches/Gump3/pygump/python/main.py

Propchange: gump/branches/Gump3/pygump/python/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Sun Nov 13 10:36:43 2005
@@ -1,4 +1,5 @@
 *.pyc
+*.pyo
 x.*
 test
 bogus

Propchange: gump/branches/Gump3/pygump/python/gump/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Sun Nov 13 10:36:43 2005
@@ -1,3 +1,4 @@
 *.pyc
+*.pyo
 tmp
 cache

Modified: gump/branches/Gump3/pygump/python/gump/config.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/pygump/python/gump/config.py?rev=333089&r1=333088&r2=333089&view=diff
==============================================================================
--- gump/branches/Gump3/pygump/python/gump/config.py (original)
+++ gump/branches/Gump3/pygump/python/gump/config.py Sun Nov 13 10:36:43 2005
@@ -98,8 +98,8 @@
     if config.do_update:    
         plugins.append(TimerPlugin("update_start"))
         from gump.plugins.updater import CvsUpdater, SvnUpdater
-        plugins.append(CvsUpdater())
-        plugins.append(SvnUpdater())
+        plugins.append(CvsUpdater(log=log, cleanup=config.repo_cleanup))
+        plugins.append(SvnUpdater(log=log, cleanup=config.repo_cleanup))
         plugins.append(TimerPlugin("update_end"))
     else:
         log.info("Not running updates! (pass --do-updates to enable them)")
@@ -235,6 +235,8 @@
         if name == 'paths_metadata':
             return os.path.dirname(self.paths_workspace)
             # nononono....return os.path.join(self.paths_home, "metadata")
+        if name == 'repo_cleanup':
+            return False
         if name == 'do_mail':
             return self.mail_server and self.mail_server_port and self.mail_to and self.mail_from
         

Modified: gump/branches/Gump3/pygump/python/gump/engine/persistence.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/pygump/python/gump/engine/persistence.py?rev=333089&r1=333088&r2=333089&view=diff
==============================================================================
--- gump/branches/Gump3/pygump/python/gump/engine/persistence.py (original)
+++ gump/branches/Gump3/pygump/python/gump/engine/persistence.py Sun Nov 13 10:36:43 2005
@@ -53,36 +53,18 @@
 gump system. While the parts of this plugin that don't interact with the algorithm.py
 code very closely could be split off, keeping it "out" as a special case hopefully keeps
 the program flow a little more understandable.
-
-Note that the file-copying code is not very efficient. Ideally we can do something using
-hard links, falling back to efficient rsync. What we're doing here is doubling disk space
-usage. When using SVN and doing a 'keep local copy checked out and duplicate before build'
-(which gump2 does but gump3 does not at the moment), that means we have 3 checkouts of the
-same stuff, which means 6 times the disk space, since SVN keeps around the .svn
-directories). Ouch!
 """
 
 from gump.model import Workspace, Repository, Module, Project, Jar, Path
 from gump.model.util import get_project_directory, check_failure, check_skip
 from gump.model.util import mark_previous_build
+from gump.util.sync import smart_sync
 import os
 import shutil
 import copy
 
 GUMP_STORAGE_DIRNAME=".gump-persist"
 
-def copytree(source=None, target=None, excludes=[]):
-    """Naive non-performant copytree utility.
-    
-    TODO: replace with something efficient (like rsync.py from gump2)."""
-    if os.path.exists(target):
-        shutil.rmtree(target)
-    shutil.copytree(source, target)
-    for x in excludes:
-        fullpath = os.path.join(target, x)
-        if os.path.exists(fullpath):
-            shutil.rmtree(fullpath)
-        
 class ShelfBasedPersistenceHelper:
     def __init__(self, shelf, log):
         self.shelf = shelf
@@ -175,8 +157,8 @@
     
             self.log.debug("Saving %s files into %s..." % (project, storage_path))
             
-            copytree(source=currentpath, target=storage_path, excludes=[GUMP_STORAGE_DIRNAME])
-            
+            smart_sync(currentpath, storage_path, excludes=[GUMP_STORAGE_DIRNAME], cleanup=False)
+
             project.original_path = project.path
             project.path = os.path.join(project.path, GUMP_STORAGE_DIRNAME)
         else:

Modified: gump/branches/Gump3/pygump/python/gump/model/util.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/pygump/python/gump/model/util.py?rev=333089&r1=333088&r2=333089&view=diff
==============================================================================
--- gump/branches/Gump3/pygump/python/gump/model/util.py (original)
+++ gump/branches/Gump3/pygump/python/gump/model/util.py Sun Nov 13 10:36:43 2005
@@ -42,19 +42,21 @@
     """Determine the base directory for a project."""
     return abspath(join(get_module_directory(project.module),project.path))
 
-def get_module_directory(module):
+def get_module_directory(module, location="builds"):
     """Determine the base directory for a module."""
-    return abspath(join(get_repository_directory(module.repository),module.name))
+    return abspath(join(get_repository_directory(module.repository, location=location),module.name))
 
-def get_repository_directory(repository):
+def get_repository_directory(repository, location="builds"):
     """Determine the base directory for a repository."""
-    return abspath(join(get_workspace_directory(repository.workspace),repository.name))
+    return abspath(join(get_workspace_directory(repository.workspace, location=location),repository.name))
 
-def get_workspace_directory(workspace):
+def get_workspace_directory(workspace, location="builds"):
     """Determine the base directory for a workspace."""
     # the below join() now happens in objectifier._create_workspace!
     #return abspath(join(workspace.workdir,workspace.name))
-    return abspath(workspace.workdir)
+    if not location:
+        location = "builds"
+    return abspath(join(workspace.workdir, location))
 
 def mark_stale_prereq(model_element, stale_prereq):
     """Mark a project with "stale prereq"."""

Modified: gump/branches/Gump3/pygump/python/gump/plugins/updater.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/pygump/python/gump/plugins/updater.py?rev=333089&r1=333088&r2=333089&view=diff
==============================================================================
--- gump/branches/Gump3/pygump/python/gump/plugins/updater.py (original)
+++ gump/branches/Gump3/pygump/python/gump/plugins/updater.py Sun Nov 13 10:36:43 2005
@@ -30,26 +30,52 @@
 from gump.util.executor import Popen
 from gump.util.executor import PIPE
 from gump.util.executor import STDOUT
+from gump.util.sync import smart_sync
 
 from gump.model.util import UPDATE_TYPE_CHECKOUT, UPDATE_TYPE_UPDATE
 
+SYNC_EXCLUDES=[".gump-persist"]
+
 class ModuleUpdater(AbstractPlugin):
-    def __init__(self):
-        pass
+    def __init__(self, log=None, cleanup=False):
+        self.log = log
+        self.cleanup = cleanup
 
     def visit_repository(self, repository):
-        repopath = get_repository_directory(repository)
-        if not os.path.exists(repopath):
-            os.makedirs(repopath)
+        checkoutrepopath = get_repository_directory(repository, location="checkouts")
+        buildrepopath = get_repository_directory(repository)
+        
+        if self.cleanup:
+            if self.log:
+                self.log.debug(
+"Removing %s so that new checkouts will be fresh" % checkoutrepopath)
+            shutil.rmtree(checkoutrepopath)
+        
+        if not os.path.exists(checkoutrepopath):
+            os.makedirs(checkoutrepopath)
+        if not os.path.exists(buildrepopath):
+            os.makedirs(buildrepopath)
 
     def visit_module(self, module):
-        modulepath = get_module_directory(module)
-        if not os.path.exists(modulepath):
-            os.makedirs(modulepath)
+        checkoutmodulepath = get_module_directory(module, location="checkouts")
+        buildmodulepath = get_module_directory(module)
+        if not os.path.exists(checkoutmodulepath):
+            os.makedirs(checkoutmodulepath)
+        #DONT -- breaks "smart" sync... if not os.path.exists(buildmodulepath):
+        #    os.makedirs(buildmodulepath)
+        
+        def onerror(*args, **kwargs):
+            if self.log:
+                self.log.error(
+"Problem while syncing %s -> %s" % (checkoutmodulepath, buildmodulepath))
+        
+        smart_sync(checkoutmodulepath, buildmodulepath,
+                   excludes=SYNC_EXCLUDES, cleanup=self.cleanup, onerror=onerror)
+        
 
 class CvsUpdater(ModuleUpdater):
-    def __init__(self):
-        ModuleUpdater.__init__(self)
+    def __init__(self, log=None, cleanup=False):
+        ModuleUpdater.__init__(self, log=log, cleanup=cleanup)
     
     def visit_repository(self, repository):
         if isinstance(repository, CvsRepository):
@@ -58,16 +84,21 @@
     def visit_module(self, module):
         if not isinstance(module.repository, CvsRepository): return
 
-        ModuleUpdater.visit_module(self, module)
-
-        repopath = get_repository_directory(module.repository)
-        current = os.path.curdir
+        repopath = get_repository_directory(module.repository, location="checkouts")
         modulepath = os.path.join(repopath, module.name)
+        if not os.path.exists(modulepath):
+            os.makedirs(modulepath)
+
         cvsdir = os.path.join(modulepath, 'CVS')
         if not os.path.exists(cvsdir):
+            if self.log: self.log.debug("New CVS checkout in %s" % modulepath)
             self.checkout(module, repopath)
         else:
+            if self.log: self.log.debug("CVS update in %s" % modulepath)
             self.update(module, modulepath)
+
+        ModuleUpdater.visit_module(self, module)
+
     
     def checkout(self, module, cwd):
         # no 'CVS', but there is a module dir. That could cause cvs to fail.
@@ -77,7 +108,8 @@
             shutil.rmtree(targetdir)
 
         repository = module.repository.to_url()
-        cvs = Popen(['cvs', '-q', '-d', repository, 'checkout', module.name], cwd=cwd, stdout=PIPE, stderr=STDOUT)
+        cvs = Popen(['cvs', '-q', '-d', repository, 'checkout', module.name], \
+                    cwd=cwd, stdout=PIPE, stderr=STDOUT)
         module.update_log = cvs.communicate()[0]
         module.update_exit_status = cvs.wait()
         module.update_type = UPDATE_TYPE_CHECKOUT
@@ -89,8 +121,8 @@
         module.update_type = UPDATE_TYPE_UPDATE
 
 class SvnUpdater(ModuleUpdater):
-    def __init__(self):
-        ModuleUpdater.__init__(self)
+    def __init__(self, log=None, cleanup=False):
+        ModuleUpdater.__init__(self, log=log, cleanup=cleanup)
     
     def visit_repository(self, repository):
         if isinstance(repository, SvnRepository):
@@ -99,24 +131,35 @@
     def visit_module(self, module):
         if not isinstance(module.repository, SvnRepository): return
 
-        ModuleUpdater.visit_module(self, module)
-
-        modulepath = get_module_directory(module)
-        current = os.path.curdir
-        os.chdir(modulepath)
+        modulepath = get_module_directory(module, location="checkouts")
+        if not os.path.exists(modulepath):
+            os.makedirs(modulepath)
         svndir = os.path.join(modulepath, '.svn')
         if not os.path.exists(svndir):
             self.checkout(module, modulepath)
         else:
-            self.update(module, modulepath)
-        os.chdir(current)
+            svninfo = Popen(['svn', 'info', modulepath], stdout=PIPE,\
+                            stderr=STDOUT).communicate()[0]
+            currurl = None
+            for line in svninfo.split('\n'):
+                if line.startswith("URL: "):
+                    currurl = line[5:].strip()
+                    break
+            if not currurl == module.repository.url + '/' + module.path:
+                shutil.rmtree(modulepath)
+                self.checkout(module, modulepath)
+            else:
+                self.update(module, modulepath)
     
+        ModuleUpdater.visit_module(self, module)
+
     def checkout(self, module, cwd):
-        repository = module.repository.url + '/' + module.path
         if not os.path.exists(cwd):
             os.makedirs(cwd)
         
-        svn = Popen(['svn', 'checkout', repository, '.'], cwd=cwd, stdout=PIPE, stderr=STDOUT)
+        repository = module.repository.url + '/' + module.path
+        svn = Popen(['svn', 'checkout', repository, '.'], cwd=cwd, \
+                    stdout=PIPE, stderr=STDOUT)
         module.update_log = svn.communicate()[0]
         module.update_exit_status = svn.wait()
         module.update_type = UPDATE_TYPE_CHECKOUT

Propchange: gump/branches/Gump3/pygump/python/gump/util/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Sun Nov 13 10:36:43 2005
@@ -1 +1,2 @@
 *.pyc
+*.pyo

Added: gump/branches/Gump3/pygump/python/gump/util/sync.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/pygump/python/gump/util/sync.py?rev=333089&view=auto
==============================================================================
--- gump/branches/Gump3/pygump/python/gump/util/sync.py (added)
+++ gump/branches/Gump3/pygump/python/gump/util/sync.py Sun Nov 13 10:36:43 2005
@@ -0,0 +1,283 @@
+# being picky about the imports since any '.' in python code slows things down
+from gump.util.executor import Popen
+from gump.util.executor import PIPE
+from os import name as osname
+from os.path import isdir
+from os.path import join
+from os.path import exists
+from os.path import abspath
+from os.path import normcase
+from os import makedirs
+from os import mkdir
+from os import walk
+from os import link
+from os import unlink
+from os import stat
+
+from re import search
+from shutil import copyfile
+from shutil import copystat
+from shutil import rmtree
+
+"""
+This module provides a very efficient implementation of the specific rsync-like functionality
+that gump needs. The basic idea is that gump needs three copies of roughly the same directory
+tree:
+
+  work/checkouts/mymodule/myproject
+  work/builds/mymodule/myproject
+  work/builds/mymodule/myproject/.gump-persistence
+
+the steps that gump takes are as follows:
+
+  - if possible, "revert" the checkout location
+    (eg svn revert -R work/checkouts/mymodule/myproject)
+  - update the checkout location (eg svn up work/checkouts/mymodule/myproject)
+  - take a big sledgehammer and make the build location have exactly the same
+    content as the checkout location, *except* for the .gump-persistence directory,
+    which is left intact
+    (eg rsync -a --delete work/checkouts/mymodule/myproject \
+          work/builds/mymodule/myproject \
+          --delete-excludes=.gump-persistence)
+  - when a build is successful, take a big sledgehammer and make the persistence location
+    have exactly the same content as the build location, *except* for the .gump-persistence
+    directory, which we don't want to "recursively keep around"
+    (eg rsync -a --delete work/builds/mymodule/myproject \
+          work/builds/mymodule/myproject/.gump-persistence \
+          --excludes=.gump-persistence)
+
+The associated calls into this module look roughly like
+
+  sync('work/checkouts/mymodule/myproject', 'work/builds/mymodule/myproject',
+       excludes=[".gump-persist"])
+  sync('ant/ant', 'ant/ant/.gump-persist', excludes=[".gump-persist"])
+
+On mac and linux, this module makes use of hardlinks for files, which saves a considerable
+amount of space, avoids a lot of copying stuff around, and is faster than native rsync if
+the target to copy to is not yet around or way-out-of-date.
+
+Native rsync is still about 10 times faster on updates of existing stuff. A good tradeoff
+between disk space use and performance is the smart_sync command, which picks the rsync
+mode to use based on this speed/disk space difference, periodically cleaning out and
+re-doing everything using hard links. WARNING: native_sync assumes shell patterns, sync
+assumes regular expressions for the exclude filtering. Use only a sensible subset of the two!
+
+Also note that memory usage briefly grows sharply, as we have to keep arond a hash in memory
+with all the file and directory entries inside the source location so we can figure out what
+bits to delete later. Rsync probably does this a little differently, visiting source and
+target directories at the same time and deleting whatever is in the target location as it
+lists the source directory and sees what "should go". The reason for not doing things like
+that is that it feels a little safer.
+
+I've tested subversion 1.2.3 on the mac and when it updates a file it really does replace it,
+meaning hardlinks can probably also be safely used here without nasty side effects when we
+have multiple gumps running concurrently using the same checkout base. This may not be true
+for CVS or other versioning systems; I don't know.
+
+Finally, the use of hard links means that all three 'copies' of the different files need to
+be on the same physical filesystem. That means partitioning of the gump working copies will
+have to be a per-module or per-repository basis, eg you can't split the work dir in two
+partitions (eg one for checkouts and one for builds).
+"""
+
+def __should_update(src, dst):
+    """Compare file attributes and decide whether an update is needed."""
+    sstat = stat(src)
+    dstat = stat(dst)
+
+    # compare file size
+    ss = sstat.st_size
+    ds = dstat.st_size
+    if ss != ds: return True
+    
+    # compare modification time
+    st = sstat.st_mtime
+    dt = dstat.st_mtime
+    if abs(st - dt) > 5: return True
+    
+    # same same
+    return False
+    
+
+# figure out how to do the file copies. No symlinks, they may break
+# some builds...
+__hardlinks = osname == "posix" or osname == "mac"
+if __hardlinks:
+    def __copy_file(src, dst):
+        """Get rid of dst if it is weird, or create new hard link if update is needed."""
+        if isdir(dst):
+            rmtree(dst)
+        elif exists(dst):
+            if __should_update(src,dst):
+                unlink(dst)
+            else:
+                return
+        link(src, dst)
+else:
+    def __copy_file(src, dst):
+        """Get rid of dst if its a directory, then copy over file if update is needed."""
+        if isdir(dst):
+            rmtree(dst)
+        elif exists(dst) and __should_update(src, dst):
+            return
+        copyfile(src, dst)
+
+
+def __strip_slash_prefix(path):
+    if len(path) == 0:
+        return path
+    
+    if path[0] == "/" or path[0] == "\\":
+        return path[1:]
+    
+
+def sync(source, target, excludes=[], onerror=None):
+    """Big sledgehammer to sync source and target locations. See module documentation."""
+    assert isinstance(source, basestring)
+    assert isinstance(target, basestring)
+    assert isdir(source)
+    assert hasattr(excludes, "__iter__")
+    for i in excludes:
+        assert isinstance(i, basestring)
+    
+    source = normcase(abspath(source))
+    target = normcase(abspath(target))
+
+    if not isdir(target):
+        makedirs(target)
+        
+    # this is where the memory usage grows -- we keep a rather big array in memory here...
+    # ...do_delete does modify it in-place, but still, executing on the 'smallest subset
+    # possible' (and not a whole source tree at a time) seems prudent
+    keep = __do_copy(source, target, excludes, onerror)
+    __do_delete(target, keep, excludes, onerror)
+
+
+def __do_copy(source, target, excludes, onerror):
+    """Recursive directory copy using os.walk."""
+    plen = len(source)
+    keep = {}
+    for (root, dirs, files) in walk(source, topdown=True, onerror=onerror):
+        __filter_walk_stack(root, dirs, excludes)
+        __filter_walk_stack(root, files, excludes)
+        relative_root = __strip_slash_prefix(root[plen:])
+        
+        for idir in dirs:
+            spath = join(root, idir)
+            tpath = join(target, relative_root, idir)
+            if not isdir(tpath):
+                mkdir(tpath)
+            copystat(spath, tpath)
+        
+        for ifile in files:
+            spath = join(root, ifile)
+            tpath = join(target, relative_root, ifile)
+            __copy_file(spath, tpath)
+        
+        keep[relative_root] = (dirs, files)
+    return keep
+
+
+def __do_delete(target, dontdel, excludes, onerror):
+    """Recursive deletes of unknown files using os.walk."""
+    plen = len(target)
+    for (root, dirs, files) in walk(target, topdown=True, onerror=onerror):
+        relative_root = __strip_slash_prefix(root[plen:])
+
+        if not dontdel.has_key(relative_root):
+            rmtree(normcase(abspath(root)), onerror=onerror)
+            continue
+        
+        # note we modify the dontdel hash so its starts shrinking as we
+        # go through it...
+        (dontdel_dirs, dontdel_files) = dontdel.pop(relative_root)
+        
+        dontwalkdirs = []
+        for idir in dirs:
+            if not idir in dontdel_dirs:
+                dontwalkdirs.append(idir)
+                rmtree(join(root, idir), onerror=onerror)
+        for x in dontwalkdirs:
+            dirs.remove(x)
+        
+        for ifile in files:
+            if not ifile in dontdel_files:
+                unlink(join(root, ifile))
+
+
+def __filter_walk_stack(root, paths, excludes):
+    """Apply regexps to a bunch of paths and modify the array in place if there's a match."""
+    if len(excludes) == 0:
+        return
+    
+    ignore_paths = []
+    for ipath in paths:
+        iabspath = abspath(join(root, ipath))
+        for x in excludes:
+            if search(x, iabspath):
+                ignore_paths.append(ipath)
+                break
+    for ipath in ignore_paths:
+        paths.remove(ipath)
+
+
+from tempfile import mkdtemp
+have_native = not Popen(["rsync", "--version"], stdout=PIPE, shell=True).wait()
+from time import localtime
+
+if have_native:
+    def native_sync(source, target, excludes):
+        assert isinstance(source, basestring)
+        assert isinstance(target, basestring)
+        assert isdir(source)
+        assert hasattr(excludes, "__iter__")
+        for i in excludes:
+            assert isinstance(i, basestring)
+        
+        source = normcase(abspath(source))
+        target = normcase(abspath(target))
+        if not source[-1] == "/" and not source[-1] == "\\":
+            source = source + "/"
+        if not target[-1] == "/" and not target[-1] == "\\":
+            target = target + "/"
+
+        if not isdir(target):
+            makedirs(target)
+
+        cmd = ["rsync", "-a", "--delete"]
+        for x in excludes:
+            cmd.append("--exclude='" + x + "'")
+        cmd.append(source)
+        cmd.append(target)
+        
+        result = Popen(cmd, shell=True).wait()
+        if result:
+            raise "rsync command %s failed with exit status %s!" % (" ".join(cmd), result)
+
+if __hardlinks and have_native:
+    def smart_sync(source, target, excludes=[], onerror=None, cleanup=None):
+        if not exists(target):
+            sync(source, target, excludes=excludes, onerror=onerror)
+        else:
+            if cleanup == None:
+                currtime = localtime()
+                currday = currtime.tm_mday
+                docleanup = currday in [1, 14]
+            else:
+                docleanup = cleanup
+            
+            if docleanup:
+                # periodically clean out completely to start with new hard links and save space
+                emptydir = mkdtemp()
+                sync(emptydir, target, excludes=excludes, onerror=onerror)
+                rmtree(emptydir)
+                sync(source, target, excludes=excludes, onerror=onerror)
+            else:
+                native_sync(source, target, excludes)
+elif have_native:
+    def smart_sync(source, target, excludes=[], onerror=None, cleanup="ignored"):
+        native_sync(source, target, excludes)
+else:
+    def smart_sync(source, target, excludes=[], onerror=None, cleanup="ignored"):
+        sync(source, target, excludes, onerror=onerror)
+

Modified: gump/branches/Gump3/pygump/python/main.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/pygump/python/main.py?rev=333089&r1=333088&r2=333089&view=diff
==============================================================================
--- gump/branches/Gump3/pygump/python/main.py (original)
+++ gump/branches/Gump3/pygump/python/main.py Sun Nov 13 10:36:43 2005
@@ -119,6 +119,11 @@
                       dest="do_update",
                       default=False, # Default to NOT. At least during initial development...
                       help="run cvs and svn updates")
+    parser.add_option("--do-repo-clean",
+                      action="store_true",
+                      dest="repo_cleanup",
+                      default=False,
+                      help="Get rid of the svn and cvs checkout directories (and so do fresh checkouts)")
     parser.add_option("-b",
                       "--do-builds",
                       action="store_true",
@@ -475,6 +480,11 @@
     options.start_time = time.strftime('%d %b %Y %H:%M:%S', time.gmtime())
 
     options.version = GUMP_VERSION
+    
+    if options.repo_cleanup and not options.do_update:
+        print "ERROR: don't clean out the repositories if you will not update!"
+        print "       re-run gump with the '-u' option set."
+        sys.exit(3)
     
     # check for debug info
     if options.attach_wing: