2000-01-28 04:20:49 +03:00
|
|
|
#!/usr/bin/python
|
|
|
|
#
|
|
|
|
# The contents of this file are subject to the Mozilla Public
|
|
|
|
# License Version 1.1 (the "License"); you may not use this file
|
|
|
|
# except in compliance with the License. You may obtain a copy of
|
|
|
|
# the License at http://www.mozilla.org/MPL/
|
|
|
|
#
|
|
|
|
# Software distributed under the License is distributed on an "AS
|
|
|
|
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
|
|
# implied. See the License for the specific language governing
|
|
|
|
# rights and limitations under the License.
|
|
|
|
#
|
|
|
|
# The Original Code is a Mozilla build tool.
|
|
|
|
#
|
|
|
|
# The Initial Developer of the Original Code is Netscape Communications
|
|
|
|
# Corporation. Portions created by Netscape are
|
|
|
|
# Copyright (C) 2000 Netscape Communications Corportation. All
|
|
|
|
# Rights Reserved.
|
|
|
|
#
|
|
|
|
# Contributor(s): Stephen Lamm <slamm@netscape.com>
|
|
|
|
#
|
|
|
|
|
2000-01-28 09:13:41 +03:00
|
|
|
# Version: $Id: cvs.py,v 1.2 2000/01/28 06:13:41 slamm%netscape.com Exp $
|
2000-01-28 04:20:49 +03:00
|
|
|
|
2000-01-28 09:13:41 +03:00
|
|
|
# module cvs -- Add multithreading to the cvs client.
|
|
|
|
|
|
|
|
"""A multithreading wrapper around the cvs client.
|
|
|
|
|
|
|
|
Exports:
|
|
|
|
class CVS(module='<cvs_module>')
|
|
|
|
CVS.module_files -- Array of module files and directories.
|
|
|
|
Files that need to be grouped together are space separated
|
|
|
|
within the same array element. For example,
|
|
|
|
['mozilla/xpcom','mozilla/js !mozilla/js/extra']
|
|
|
|
class ModuleFileQueue(): A queue of cvs module files and directories.
|
|
|
|
xargs() - Emulate xargs, but use threads to draw from common queue.
|
|
|
|
class XArgsThread() - Helper class for xargs()
|
|
|
|
parallel_cvs_co() - Use xargs and ModuleFileQueue to checkout. Time it too.
|
|
|
|
|
|
|
|
Three threads should be enough to get a decent pull time. In fact,
|
|
|
|
any more threads than that will probably not help.
|
|
|
|
|
|
|
|
"""
|
2000-01-28 04:20:49 +03:00
|
|
|
|
|
|
|
import os
|
|
|
|
import string
|
|
|
|
import sys
|
|
|
|
import threading
|
|
|
|
import time
|
|
|
|
import Queue
|
|
|
|
|
|
|
|
|
|
|
|
class CVS:
|
|
|
|
"""Handle basic cvs operations like reading the modules file.
|
|
|
|
|
|
|
|
Example usage:
|
|
|
|
cvs_cmd = cvs(module='SeaMonkeyAll')
|
|
|
|
modules = cvs_cmd.module_files
|
|
|
|
|
|
|
|
(Doesn't do much else yet.)
|
|
|
|
|
|
|
|
"""
|
|
|
|
def __init__(self, **params):
|
|
|
|
self.cvsroot = os.environ["CVSROOT"]
|
|
|
|
self.params = params
|
|
|
|
|
|
|
|
# The magic function that makes cvs.module_files a function call
|
|
|
|
def __getattr__(self, attr):
|
|
|
|
if attr == "module_files":
|
|
|
|
# Parse the module and safe the result for later reference
|
|
|
|
self.module_files = self.parse_module_file()
|
|
|
|
return self.module_files
|
|
|
|
elif self.params.has_key(attr):
|
|
|
|
return self.params[attr]
|
|
|
|
|
|
|
|
def parse_module_file(self):
|
|
|
|
import re
|
|
|
|
mod_pat = re.compile(r'\s+-a\s+')
|
|
|
|
spc_pat = re.compile(r'\s+')
|
|
|
|
module = ''
|
|
|
|
members = {}
|
|
|
|
|
|
|
|
for line in os.popen("cvs co -c").readlines():
|
|
|
|
line = line[:-1]
|
|
|
|
module_split = re.split(mod_pat, line)
|
|
|
|
if len(module_split) > 1:
|
|
|
|
(module, line) = module_split
|
|
|
|
members[module] = re.split(spc_pat, line)
|
|
|
|
else:
|
|
|
|
space_split = re.split(spc_pat, line)[1:]
|
|
|
|
members[module].extend(space_split)
|
|
|
|
|
2000-01-28 09:13:41 +03:00
|
|
|
members = self._flatten_module(members, self.module)
|
2000-01-28 04:20:49 +03:00
|
|
|
members.sort()
|
2000-01-28 09:13:41 +03:00
|
|
|
members = self._get_checkout_groups(members)
|
2000-01-28 04:20:49 +03:00
|
|
|
|
|
|
|
return members
|
|
|
|
|
2000-01-28 09:13:41 +03:00
|
|
|
def _flatten_module(self, members, module):
|
2000-01-28 04:20:49 +03:00
|
|
|
result = []
|
|
|
|
for member in members[module]:
|
|
|
|
if members.has_key(member):
|
2000-01-28 09:13:41 +03:00
|
|
|
result.extend(self._flatten_module(members,member))
|
2000-01-28 04:20:49 +03:00
|
|
|
else:
|
|
|
|
result.append(member)
|
|
|
|
return result
|
|
|
|
|
2000-01-28 09:13:41 +03:00
|
|
|
def _get_checkout_groups(self, members):
|
2000-01-28 04:20:49 +03:00
|
|
|
import string
|
|
|
|
ignore = []
|
|
|
|
checkout_groups = []
|
|
|
|
for member in members:
|
|
|
|
if member[0] == "!":
|
|
|
|
ignore.append(member)
|
|
|
|
else:
|
|
|
|
group = [member]
|
|
|
|
for file in ignore:
|
|
|
|
if file[1:len(member)+1] == member:
|
|
|
|
group.append(file)
|
|
|
|
#ignore.remove(file)
|
|
|
|
group = string.join(group)
|
|
|
|
checkout_groups.append(group)
|
|
|
|
return checkout_groups
|
|
|
|
|
|
|
|
|
|
|
|
class ModuleFileQueue(Queue.Queue):
|
|
|
|
"""Use a thread-safe Queue for listing module files and directories."""
|
|
|
|
|
|
|
|
def __init__(self, cvs_module):
|
|
|
|
mozcvs = CVS(module=cvs_module)
|
|
|
|
queue_size = len(mozcvs.module_files)
|
|
|
|
Queue.Queue.__init__(self, queue_size)
|
|
|
|
for file in mozcvs.module_files:
|
|
|
|
self.put(file)
|
|
|
|
|
|
|
|
|
2000-01-28 09:13:41 +03:00
|
|
|
class XArgsThread(threading.Thread):
|
|
|
|
"""An individual thread for the XArgs class"""
|
2000-01-28 04:20:49 +03:00
|
|
|
|
|
|
|
def __init__(self, shell_command, queue, **params):
|
|
|
|
self.shell_command = shell_command
|
|
|
|
self.queue = queue
|
|
|
|
self.max_args = params.get('max_args')
|
|
|
|
self.error_event = params.get('error_event')
|
|
|
|
self.verbose = params.get('verbose')
|
|
|
|
self.status = 0
|
|
|
|
threading.Thread.__init__(self)
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
while not self.queue.empty():
|
|
|
|
files = []
|
|
|
|
while (self.max_args and len(files) < self.max_args):
|
|
|
|
try:
|
|
|
|
next_file_group = self.queue.get_nowait()
|
|
|
|
except Queue.Empty:
|
|
|
|
break
|
|
|
|
next_files = string.split(next_file_group)
|
|
|
|
files.extend(next_files)
|
|
|
|
if self.error_event and self.error_event.isSet():
|
2000-01-28 09:13:41 +03:00
|
|
|
sys.exit()
|
2000-01-28 04:20:49 +03:00
|
|
|
if files:
|
|
|
|
if self.verbose:
|
|
|
|
print "%s: %s %s" % (self.getName(), self.shell_command,
|
|
|
|
string.join(files))
|
|
|
|
def add_quotes(xx): return '"' + xx + '"'
|
|
|
|
quoted_files = map(add_quotes, files)
|
|
|
|
quoted_files = string.join(files)
|
|
|
|
error = os.system(self.shell_command + " " + quoted_files)
|
|
|
|
|
|
|
|
if error:
|
|
|
|
# Stop this thread
|
|
|
|
self.status = error
|
|
|
|
if self.error_event: self.error_event.set()
|
|
|
|
sys.exit()
|
|
|
|
|
2000-01-28 09:13:41 +03:00
|
|
|
def xargs(command, queue, **params):
|
|
|
|
"""Similar to shell's xargs.
|
2000-01-28 04:20:49 +03:00
|
|
|
|
2000-01-28 09:13:41 +03:00
|
|
|
Requires a shell command and a queue for arguments.
|
|
|
|
"""
|
|
|
|
num_threads = params.get('num_threads')
|
|
|
|
max_args = params.get('max_args')
|
|
|
|
verbose = params.get('verbose')
|
|
|
|
if not num_threads: num_threads = 1
|
|
|
|
if not max_args: max_args = 10
|
2000-01-28 04:20:49 +03:00
|
|
|
|
2000-01-28 09:13:41 +03:00
|
|
|
# Use an event to stop all the threads if one fails
|
|
|
|
error_event = threading.Event()
|
2000-01-28 04:20:49 +03:00
|
|
|
|
|
|
|
# Create the threads
|
|
|
|
threads = []
|
2000-01-28 09:13:41 +03:00
|
|
|
for ii in range(0,num_threads):
|
|
|
|
thread = XArgsThread(command, queue, max_args=max_args,
|
|
|
|
error_event=error_event, verbose=verbose)
|
2000-01-28 04:20:49 +03:00
|
|
|
threads.append(thread)
|
|
|
|
thread.start()
|
|
|
|
# Wait until the threads finish
|
|
|
|
while threading.activeCount() > 1:
|
|
|
|
time.sleep(1)
|
|
|
|
pass
|
2000-01-28 09:13:41 +03:00
|
|
|
for thread in threads:
|
|
|
|
if thread.status:
|
|
|
|
return thread.status
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
def parallel_cvs_co(co_cmd, modules):
|
|
|
|
"""Checkout a cvs module using multiple threads."""
|
|
|
|
start_time = time.time() # Track the time to pull
|
|
|
|
|
|
|
|
# Create a Queue of the modules individual files and directories
|
|
|
|
module_queue = ModuleFileQueue(modules)
|
|
|
|
|
|
|
|
status = xargs(co_cmd, module_queue, num_threads=3, max_args=10,
|
|
|
|
verbose=1)
|
2000-01-28 04:20:49 +03:00
|
|
|
|
|
|
|
# Compute and print time spent
|
2000-01-28 09:13:41 +03:00
|
|
|
total_time = time.time() - start_time
|
2000-01-28 04:20:49 +03:00
|
|
|
total_time = time.strftime("%H:%M:%S", time.gmtime(total_time))
|
|
|
|
print "Total time to pull: " + total_time
|
|
|
|
|
2000-01-28 09:13:41 +03:00
|
|
|
if status: # Did xargs return an error?
|
|
|
|
sys.exit(status)
|
2000-01-28 04:20:49 +03:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
# Check for module on the command-line
|
|
|
|
# XXX Need more general cvs argument handling
|
|
|
|
if len(sys.argv) < 2:
|
|
|
|
print "cvs.py: must specity at least one module or directory"
|
|
|
|
sys.exit(1)
|
|
|
|
modules = string.join(sys.argv[1:])
|
|
|
|
parallel_cvs_co('cvs -z3 -q co -P', modules)
|