diff options
| author | OpenEmbedded Project <openembedded-devel@lists.openembedded.org> | 2007-10-18 16:35:01 +0000 |
|---|---|---|
| committer | OpenEmbedded Project <openembedded-devel@lists.openembedded.org> | 2007-10-18 16:35:01 +0000 |
| commit | 2b94d96062ac7eb6a15445f74e424b7b08e69a63 (patch) | |
| tree | a4ceb58e4985908f2dd574d498e1e17f38061f5e /contrib | |
| parent | 51d88fa521229df09a9d624618d7e861d765d21a (diff) | |
| parent | 0ed630ee92d26828b61f1de72dc0b33d2a056123 (diff) | |
merge of '1d0e13e8c6528e7a7037276e3c01cb21dfa085e4'
and '86581f7de9bc72fe5e21778b4442cc0e59e96fbf'
Diffstat (limited to 'contrib')
| -rw-r--r-- | contrib/armmon_xfer/.mtn2git_empty | 0 | ||||
| -rwxr-xr-x | contrib/armmon_xfer/armmon_xfer.py | 150 | ||||
| -rw-r--r-- | contrib/mono/.mtn2git_empty | 0 | ||||
| -rw-r--r-- | contrib/mono/collect-files.py | 118 | ||||
| -rw-r--r-- | contrib/mtn2git/.mtn2git_empty | 0 | ||||
| -rwxr-xr-x | contrib/mtn2git/git2mtn.py | 50 | ||||
| -rw-r--r-- | contrib/mtn2git/mtn/.mtn2git_empty | 0 | ||||
| -rw-r--r-- | contrib/mtn2git/mtn/__init__.py | 6 | ||||
| -rw-r--r-- | contrib/mtn2git/mtn/authors.py | 11 | ||||
| -rw-r--r-- | contrib/mtn2git/mtn/common.py | 49 | ||||
| -rwxr-xr-x | contrib/mtn2git/mtn/genproxy.py | 25 | ||||
| -rw-r--r-- | contrib/mtn2git/mtn/mtn.py | 419 | ||||
| -rw-r--r-- | contrib/mtn2git/mtn/utility.py | 100 | ||||
| -rwxr-xr-x | contrib/mtn2git/mtn2git.py | 610 | ||||
| -rw-r--r-- | contrib/mtn2git/status.py | 47 | ||||
| -rw-r--r-- | contrib/source-checker/.mtn2git_empty | 0 | ||||
| -rw-r--r-- | contrib/source-checker/oe-source-checker.py | 113 |
17 files changed, 1698 insertions, 0 deletions
diff --git a/contrib/armmon_xfer/.mtn2git_empty b/contrib/armmon_xfer/.mtn2git_empty new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/contrib/armmon_xfer/.mtn2git_empty diff --git a/contrib/armmon_xfer/armmon_xfer.py b/contrib/armmon_xfer/armmon_xfer.py new file mode 100755 index 0000000000..d7a9b7281d --- /dev/null +++ b/contrib/armmon_xfer/armmon_xfer.py @@ -0,0 +1,150 @@ +#!/usr/bin/python + +# Small utility to upload kernel and/or ramdisk to +# the compulab board through the ARMmon firmware. +# Requires pyserial : http://pyserial.sourceforge.net +# author: Tobias Pflug + +import sys, os, serial, getopt, atexit, re + +# regular expression to match valid IPv4 addresses +ipv4_regex = r"\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b" + +def wait_flash_complete(): + while 1: + data = s.readline() + if quiet != 1: + print data, + if data.split(" ")[0] == "Finished": + break + +def test_armmon(): + # write some nonsense to the monitor and + # check if armmon replies accordingly + s.write("nonsense\n") + s.timeout=2 + s.read(len("nonsense\n")+1) + data = s.read(len("Illegal")) + if data.split(" ")[0] != "Illegal": + sys.exit("Error: ARMmon not responding. Linux running maybe?") + s.timeout=None + s.flushInput() + s.flushOutput() + + +def usage(): + sys.stderr.write("""USAGE: %s [options] <server ip> + armmon_xfer - data upload tool for cmx270/ARMmon + + options: + -p, --port=PORT: serial port, default='/dev/ttyUSB0' + -k, --kernel=KERNEL: upload and flash kernel image KERNEL + -r, --ramdisk=RAMDISK: upload and flash ramdisk image RAMDISK + -b, --bootos: boot Linux + -R, --reboot: reboot cmx270 + -q, --quiet be quiet, little output + -h, --help: print this help screen + +""" % (sys.argv[0],)) + +if __name__ == '__main__': + + port = "/dev/ttyUSB0" + baud = 38400 + kernel = None + ramdisk = None + boot = None + reboot = None + quiet = None + tftp_ip = None + + if len(sys.argv) < 2: + usage() + sys.exit(0) + + # parse command line + + try: + opts, args = getopt.getopt(sys.argv[1:], + "p:k:r:bRhq", + ["port=", "kernel=","ramdisk=","bootos","reboot","help","quiet"] + ) + except getopt.GetoptError: + usage() + sys.exit(2) + + for o, a in opts: + if o in ("-h","--help"): + usage() + sys.exit() + elif o in ("-k","--kernel"): + kernel = a + elif o in ("-r","--ramdisk"): + ramdisk = a + elif o in ("-b","--bootos"): + boot = 1 + elif o in ("-R","--reboot"): + reboot = 1 + elif o in ("-p","--port"): + port = o + elif o in ("-q","--quiet"): + quiet = 1 + + # check for arguments and validate IP address + # when required by commands + if len(args)>0 and re.match(ipv4_regex,args[0]): + tftp_ip = args[0] + else: + if kernel or ramdisk: + sys.exit("Error: invalid IP address!") + + try: + s = serial.Serial('/dev/ttyUSB0',38400) + except: + sys.stderr.write("Error: Could not open port: %s\n" % (port)) + sys.exit(1) + + # test if armmon is responding on bogus input + test_armmon() + + # carry out commands + + if reboot: + print "Rebooting compulab ..." + s.write("reboot\n") + s.write("y\n") + s.write("y\n") + s.close() + sys.exit(0) + + if kernel: + print "Downloading kernel ..." + cmd = "download kernel tftp %s %s\n" % (kernel,tftp_ip) + s.write(cmd) + data = s.readline() + data = s.readline() + + print "Flashing kernel ..." + cmd = "flash kernel\n" + s.write(cmd) + wait_flash_complete() + + + if ramdisk: + print "Downloading ramdisk ..." + cmd = "download ramdisk tftp %s %s\n" % (ramdisk,tftp_ip) + s.write(cmd) + s.readline() + s.readline() + + print "Flashing ramdisk ..." + cmd = "flash ramdisk\n" + s.write(cmd) + wait_flash_complete() + + if boot: + print "Booting Linux ..." + s.write("bootos\n") + + s.close() + sys.exit(0) diff --git a/contrib/mono/.mtn2git_empty b/contrib/mono/.mtn2git_empty new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/contrib/mono/.mtn2git_empty diff --git a/contrib/mono/collect-files.py b/contrib/mono/collect-files.py new file mode 100644 index 0000000000..c353667b19 --- /dev/null +++ b/contrib/mono/collect-files.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +## This utility takes the debian directory from an unpacked debian mono source +## tree (e.g. apt-get source mono), parses the *.install files and generates a +## bitbake include file with the file and package lists. It tries to handle -dbg +## packages by creating additional glob patterns for *.mdb and */.debug/*. Most +## of these will not match, but that's fine (I think). +## -- Henryk Plötz <henryk@openmoko.org> +## +##The output looks like: +##FILES_mono-jit-dbg = "/usr/bin/mono*.mdb \ +## /usr/bin/mono*/*.mdb" +##FILES_mono-jit = "/usr/bin/mono" +##FILES_mono-gac-dbg = "/usr/bin/gacutil*.mdb \ +## /usr/bin/gacutil*/*.mdb \ +## /usr/lib/mono/1.0/gacutil.exe*.mdb \ +## /usr/lib/mono/1.0/gacutil.exe*/*.mdb" +##FILES_mono-gac = "/usr/bin/gacutil \ +## /usr/lib/mono/1.0/gacutil.exe" +## ... +##PACKAGES = "mono-jit-dbg \ +## mono-jit \ +## mono-gac-dbg \ +## mono-gac \ +## ... + +import os, sys, re + +def collect_paths(dir): + paths = {} + + os.chdir(dir) + for filename in os.listdir("."): + if filename.endswith(".install"): + fp = file(filename, "r") + lines = fp.readlines() + fp.close() + + contents = [] + for line in lines: + line = line.strip() + if line.startswith("#"): continue + if line == "": continue + + lineparts = line.split() + if lineparts[0].startswith("debian/tmp"): + pattern = lineparts[0][ len("debian/tmp"): ] + if len(lineparts) == 2: + if not pattern.startswith(lineparts[1]): + print >>sys.stderr, "Warning: Apparently I don't fully understand the format in file %s" % filename + elif len(lineparts) > 2: + print >>sys.stderr, "Warning: Apparently I don't fully understand the format in file %s" % filename + + contents.append( pattern ) + else: + print >>sys.stderr, "Note: Ignoring %s in %s" % (lineparts, filename) + + paths[ filename[ :-len(".install") ] ] = contents + + return paths + +def collect_packages(paths): + # These packages should be populated first (e.g. because their files will otherwise end up + # in other packages) + PACKAGES_FIRST = ("mono-jit", "mono-gac", "mono-mjs", "mono-gmcs", "mono-utils", "mono-doc") + # These should be populated last (because their spec is very broad) + PACKAGES_LAST = ("mono-mcs", "libmono-system1.0-cil", "libmono-system2.0-cil", "libmono1.0-cil", "libmono2.0-cil") + first = [] + last = [] + packages = paths.keys() + for packagename in PACKAGES_FIRST + PACKAGES_LAST: + if packagename in packages: + packages.remove(packagename) + if packagename in PACKAGES_FIRST: + first.append(packagename) + else: + last.append(packagename) + packagenames = first + packages + last + + return packagenames, paths + +def debugify(packagenames, paths): + pnames = [] + for pkg in packagenames: + if not pkg.endswith("-dbg"): + result = [] + for path in paths[pkg]: + if not path.endswith("*"): + result.append(path + "*.mdb") + result.append(path + "*/*.mdb") + else: + result.append(path + ".mdb") + result.append(path + "/*.mdb") + if path.endswith("/"): + result.append(path + ".debug/") + result.append(path + "../.debug/") + paths[pkg + "-dbg"] = result + pnames.append(pkg + "-dbg") + pnames.append(pkg) + return pnames, paths + +if __name__ == "__main__": + packagenames, paths = collect_packages( collect_paths(".") ) + packagenames, paths = debugify(packagenames, paths) + + print "# This is a generated file, please do not edit directly" + print "# Use collect-files.py instead. -- Henryk <henryk@openmoko.org>" + + packages = [] + for pkg in packagenames: + if not paths[pkg]: continue + + print 'FILES_%s = "%s"' % (pkg, " \\\n\t".join(paths[pkg])) + packages.append(pkg) + + print + print 'PACKAGES = "%s"' % (" \\\n\t".join(packages)) diff --git a/contrib/mtn2git/.mtn2git_empty b/contrib/mtn2git/.mtn2git_empty new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/contrib/mtn2git/.mtn2git_empty diff --git a/contrib/mtn2git/git2mtn.py b/contrib/mtn2git/git2mtn.py new file mode 100755 index 0000000000..99681a9ca2 --- /dev/null +++ b/contrib/mtn2git/git2mtn.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +""" + Copyright (C) 2006, 2007 Holger Hans Peter Freyther + + 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. +""" + +############# +# +# Use: This tool can merge one git-branch back to one branch in monotone +# +# Discussion: +# Merging from git to a monotone branch. Currently I see two modes which +# should be supported. +# +# a) linear development. Only a couple of changes are done on top of the +# branch and nothing get merged. In this case we can merge everything +# back and each rev gets a cert with the branch. +# This should be possible using programs like git-rebase. +# b) we have merges inside our git-rev-list history. This means we need to +# merge every revision and can't attach any branch certs to the revision. +# And once we are done with this we will create a propagate like commit +# entry and we can give that new revision a cert with the branch name. +# +# This means working in git is treated like a branch! +# +# One difficulty is with git. This propagate like commit will create a new revision +# in monotone but none in git as both trees/manifests are the same. So what we have +# to make sure is to use the latest mtn revision for a given mark/git revision. This +# is where mtn2git.py needs to help. We will save a list of mtn revisions that have the +# same git version and then will read every of them and check the branch certs and will +# use the one matching our target branch! +############# diff --git a/contrib/mtn2git/mtn/.mtn2git_empty b/contrib/mtn2git/mtn/.mtn2git_empty new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/contrib/mtn2git/mtn/.mtn2git_empty diff --git a/contrib/mtn2git/mtn/__init__.py b/contrib/mtn2git/mtn/__init__.py new file mode 100644 index 0000000000..e7424ccd3d --- /dev/null +++ b/contrib/mtn2git/mtn/__init__.py @@ -0,0 +1,6 @@ +from mtn import Automate, Operations + +__all__ = [ + "Automate", + "Operations" + ] diff --git a/contrib/mtn2git/mtn/authors.py b/contrib/mtn2git/mtn/authors.py new file mode 100644 index 0000000000..90542a02da --- /dev/null +++ b/contrib/mtn2git/mtn/authors.py @@ -0,0 +1,11 @@ +authors='''Authors: +Grahame Bowland <grahame@angrygoats.net> + +Contributors: +Matt Johnston <matt@ucc.asn.au> +Nathaniel Smith <njs@pobox.com> +Bruce Stephens <monotone@cenderis.demon.co.uk> +Lapo Luchini <lapo@lapo.it> +David Reiss <davidn@gmail.com> + +''' diff --git a/contrib/mtn2git/mtn/common.py b/contrib/mtn2git/mtn/common.py new file mode 100644 index 0000000000..1bbf6031c9 --- /dev/null +++ b/contrib/mtn2git/mtn/common.py @@ -0,0 +1,49 @@ + +import datetime +import time +import fcntl +import os +import signal +import traceback +import sys + +def parse_timecert(value): + return apply(datetime.datetime, time.strptime(value, "%Y-%m-%dT%H:%M:%S")[:6]) + +def set_nonblocking(fd): + fl = fcntl.fcntl(fd, fcntl.F_GETFL) + fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NDELAY) + +def terminate_popen3(process): + print >> sys.stderr, ("[%s] stopping process: %s" % (os.getpid(), process.pid)) + try: + process.tochild.close() + process.fromchild.close() + process.childerr.close() + if process.poll() == -1: + # the process is still running, so kill it. + os.kill(process.pid, signal.SIGKILL) + process.wait() + except: + print >> sys.stderr, ("%s failed_to_stop %s (%s)" % (os.getpid(), process.pid, traceback.format_exc())) + +def ago(event): + def plural(v, singular, plural): + if v == 1: + return "%d %s" % (v, singular) + else: + return "%d %s" % (v, plural) + now = datetime.datetime.utcnow() + ago = now - event + if ago.days > 0: + rv = "%s" % (plural(ago.days, "day", "days")) + elif ago.seconds > 3600: + hours = ago.seconds / 3600 + minutes = (ago.seconds - (hours * 3600)) / 60 + rv = "%s" % (plural(hours, "hour", "hours")) + else: + minutes = ago.seconds / 60 + seconds = (ago.seconds - (minutes * 60)) + rv = "%s" % (plural(minutes, "minute", "minutes")) + return rv + diff --git a/contrib/mtn2git/mtn/genproxy.py b/contrib/mtn2git/mtn/genproxy.py new file mode 100755 index 0000000000..5ba67eefd6 --- /dev/null +++ b/contrib/mtn2git/mtn/genproxy.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +class GeneratorProxy(object): + def __init__(self, generator): + self.generator = generator + def __iter__(self): + return self + def next(self): + return self.generator.next() + +class Seedy(GeneratorProxy): + def __del__(self): + print "testing" + +def test(): + yield 2 + yield 3 + yield 4 + +if __name__ == '__main__': + a = test() + b = Seedy(test()) + for i in b: + print i +
\ No newline at end of file diff --git a/contrib/mtn2git/mtn/mtn.py b/contrib/mtn2git/mtn/mtn.py new file mode 100644 index 0000000000..aa086d78d4 --- /dev/null +++ b/contrib/mtn2git/mtn/mtn.py @@ -0,0 +1,419 @@ + +import os +import re +import fcntl +import pipes +import select +import threading +import popen2 +from common import set_nonblocking, terminate_popen3 +from traceback import format_exc +import genproxy +import sys + +# regular expressions that are of general use when +# validating monotone output +def group_compile(r): + return re.compile('('+r+')') + +hex_re = r'[A-Fa-f0-9]*' +hex_re_c = group_compile(hex_re) +revision_re = r'[A-Fa-f0-9]{40}' +revision_re_c = group_compile(revision_re) +name_re = r'^[\S]+' +name_re_c = group_compile(name_re) + +class MonotoneException(Exception): + pass + +class Revision(str): + def __init__(self, v): + # special case that must be handled: empty (initial) revision ID '' + str.__init__(v) + self.obj_type = "revision" + if v != '' and not revision_re_c.match(self): + raise MonotoneException("Not a valid revision ID: %s" % (v)) + def abbrev(self): + return '[' + self[:8] + '..]' + +class Author(str): + def __init__(self, v): + str.__init__(v) + self.obj_type = "author" + +class Runner: + def __init__(self, monotone, database): + self.base_command = [monotone, "--db=%s" % pipes.quote(database)] + +packet_header_re = re.compile(r'^(\d+):(\d+):([lm]):(\d+):') + +class Automate(Runner): + """Runs commands via a particular monotone process. This + process is started the first time run() is called, and + stopped when this class instance is deleted or the stop() + method is called. + + If an error occurs, the monotone process may need to be + stopped and a new one created. + """ + def __init__(self, *args, **kwargs): + Runner.__init__(*[self] + list(args), **kwargs) + self.lock = threading.Lock() + self.process = None + + def stop(self): + if not self.process: + return + terminate_popen3(self.process) + self.process = None + + def __process_required(self): + if self.process != None: + return + to_run = self.base_command + ['automate', 'stdio'] + self.process = popen2.Popen3(to_run, capturestderr=True) + # breaks down with toposort and a lot of input + #map (set_nonblocking, [ self.process.fromchild, + # self.process.tochild, + # self.process.childerr ]) + map (set_nonblocking, [ self.process.fromchild, + self.process.childerr ]) + + def run(self, *args, **kwargs): + print >> sys.stderr, (("automate is running:", args, kwargs)) + + lock = self.lock + stop = self.stop + class CleanRequest(genproxy.GeneratorProxy): + def __init__(self, *args, **kwargs): + genproxy.GeneratorProxy.__init__(self, *args, **kwargs) + + # nb; this used to be False, but True seems to behave more sensibly. + # in particular, if someone holds down Refresh sometimes the code + # gets here before __del__ is called on the previous iterator, + # causing a pointless error to occur + if not lock.acquire(True): + # I've checked; this exception does _not_ cause __del__ to run, so + # we don't accidentally unlock a lock below + raise MonotoneException("Automate request cannot be called: it is already locked! This indicates a logic error in ViewMTN; please report.") + + def __del__(self): + def read_any_unread_output(): + try: + # this'll raise StopIteration if we're done + self.next() + # okay, we're not done.. + print >> sys.stderr, ("warning: Automate output not completely read; reading manually.") + for stanza in self: + pass + except StopIteration: + pass + + try: + read_any_unread_output() + lock.release() + except: + print >> sys.stderr, ("exception cleaning up after Automation; calling stop()!") + stop() + + return CleanRequest(self.__run(*args, **kwargs)) + + def __run(self, command, args): + enc = "l%d:%s" % (len(command), command) + enc += ''.join(["%d:%s" % (len(x), x) for x in args]) + 'e' + + # number of tries to get a working mtn going.. + for i in xrange(2): + self.__process_required() + try: + self.process.tochild.write(enc) + self.process.tochild.flush() + break + except: + # mtn has died underneath the automate; restart it + print >> sys.stderr, ("exception writing to child process; attempting restart: %s" % format_exc()) + self.stop() + + import sys + def read_result_packets(): + buffer = "" + while True: + r_stdin, r_stdout, r_stderr = select.select([self.process.fromchild], [], [], None) + if not r_stdin and not r_stdout and not r_stderr: + break + + if self.process.fromchild in r_stdin: + data = self.process.fromchild.read() + if data == "": + break + buffer += data + + # loop, trying to get complete packets out of our buffer + complete, in_packet = False, False + while not complete and buffer != '': + if not in_packet: + m = packet_header_re.match(buffer) + if not m: + break + in_packet = True + cmdnum, errnum, pstate, length = m.groups() + errnum = int(errnum) + length = int(length) + header_length = m.end(m.lastindex) + 1 # the '1' is the colon + + if len(buffer) < length + header_length: + # not enough data read from client yet; go round + break + else: + result = buffer[header_length:header_length+length] + buffer = buffer[header_length+length:] + complete = pstate == 'l' + in_packet = False + yield errnum, complete, result + + if complete: + break + + # get our response, and yield() it back one line at a time + code_max = -1 + data_buf = '' + for code, is_last, data in read_result_packets(): + if code and code > code_max: + code_max = code + data_buf += data + while True: + nl_idx = data_buf.find('\n') + if nl_idx == -1: + break + yield data_buf[:nl_idx+1] + data_buf = data_buf[nl_idx+1:] + # left over data? + if data_buf: + yield data_buf + if code_max > 0: + raise MonotoneException("error code %d in automate packet." % (code_max)) + +class Standalone(Runner): + """Runs commands by running monotone. One monotone process + per command""" + + def run(self, command, args): + # as we pass popen3 as sequence, it executes monotone with these + # arguments - and does not pass them through the shell according + # to help(os.popen3) +# print(("standalone is running:", command, args)) + to_run = self.base_command + [command] + args + process = popen2.Popen3(to_run, capturestderr=True) + for line in process.fromchild: + yield line + stderr_data = process.childerr.read() + if len(stderr_data) > 0: + raise MonotoneException("data on stderr for command '%s': %s" % (command, + stderr_data)) + terminate_popen3(process) + +class MtnObject: + def __init__(self, obj_type): + self.obj_type = obj_type + +class Tag(MtnObject): + def __init__(self, name, revision, author, branches): + MtnObject.__init__(self, "tag") + self.name, self.revision, self.author, self.branches = name, Revision(revision), author, branches + +class Branch(MtnObject): + def __init__(self, name): + MtnObject.__init__(self, "branch") + self.name = name + +class File(MtnObject): + def __init__(self, name, in_revision): + MtnObject.__init__(self, "file") + self.name = name + self.in_revision = in_revision + +class Dir(MtnObject): + def __init__(self, name, in_revision): + MtnObject.__init__(self, "dir") + self.name = name + self.in_revision = in_revision + +basic_io_name_tok = re.compile(r'^(\S+)') + +def basic_io_from_stream(gen): + # all of these x_consume functions return parsed string + # token to add to stanza, name of next consume function to call + # new value of line (eg. with consumed tokens removed) + + def hex_consume(line): + m = hex_re_c.match(line[1:]) + if line[0] != '[' or not m: + raise MonotoneException("This is not a hex token: %s" % line) + end_of_match = m.end(m.lastindex) + if line[end_of_match+1] != ']': + raise MonotoneException("Hex token ends in character other than ']': %s" % line) + return Revision(m.groups()[0]), choose_consume, line[end_of_match+2:] + + def name_consume(line): + m = name_re_c.match(line) + if not m: + raise MonotoneException("Not a name: %s" % line) + end_of_match = m.end(m.lastindex) + return m.groups()[0], choose_consume, line[end_of_match:] + + def choose_consume(line): + line = line.lstrip() + if line == '': + consumer = choose_consume + elif line[0] == '[': + consumer = hex_consume + elif line[0] == '"': + consumer = string_consume + else: + consumer = name_consume + return None, consumer, line + + class StringState: + def __init__(self): + self.in_escape = False + self.has_started = False + self.has_ended = False + self.value = '' + + def string_consume(line, state=None): + if not state: + state = StringState() + + if not state.has_started: + if line[0] != '"': + raise MonotoneException("Not a string: %s" % line) + line = line[1:] + state.has_started = True + + idx = 0 + for idx, c in enumerate(line): + if state.in_escape: + if c != '\\' and c != '"': + raise MonotoneException("Invalid escape code: %s in %s\n" % (c, line)) + state.value += c + state.in_escape = False + else: + if c == '\\': + state.in_escape = True + elif c == '"': + state.has_ended = True + break + else: + state.value += c + + if state.has_ended: + return state.value, choose_consume, line[idx+1:] + else: + return (None, + lambda s: string_consume(s, state), + line[idx+1:]) + + consumer = choose_consume + current_stanza = [] + for line in gen: + # if we're not in an actual consumer (which we shouldn't be, unless + # we're parsing some sort of multi-line token) and we have a blank + # line, it indicates the end of any current stanza + if (consumer == choose_consume) and (line == '' or line == '\n') and current_stanza: + yield current_stanza + current_stanza = [] + continue + + while line != '' and line != '\n': + new_token, consumer, line = consumer(line) + if new_token != None: + current_stanza.append(new_token) + if current_stanza: + yield current_stanza + +class Operations: + def __init__(self, runner_args): + self.standalone = apply(Standalone, runner_args) + self.automate = apply(Automate, runner_args) + + def tags(self): + for stanza in basic_io_from_stream(self.automate.run('tags', [])): + if stanza[0] == 'tag': + branches = [] + for branch in stanza[7:]: + branches.append(Branch(branch)) + yield Tag(stanza[1], stanza[3], stanza[5], branches) + + def branches(self): + for line in (t.strip() for t in self.automate.run('branches', [])): + if not line: + continue + yield apply(Branch, (line,)) + + def graph(self): + for line in self.automate.run('graph', []): + yield line + + def parents(self, revision): + if revision != "": + for line in (t.strip() for t in self.automate.run('parents', [revision])): + if not line: + continue + yield apply(Revision, (line,)) + + def ancestry_difference(self, new_rev, old_revs): + """ + new_rev a single new revision number + old_revs a list of revisions + """ + if new_rev != "": + for line in (t.strip() for t in self.automate.run('ancestry_difference', [new_rev]+old_revs)): + if not line: + continue + yield apply(Revision, (line,)) + + def children(self, revision): + if revision != "": + for line in (t.strip() for t in self.automate.run('children', [revision])): + if not line: + continue + yield apply(Revision, (line,)) + + def toposort(self, revisions): + for line in (t.strip() for t in self.automate.run('toposort', revisions)): + if not line: + continue + yield apply(Revision, (line,)) + + def heads(self, branch): + for line in (t.strip() for t in self.automate.run('heads', [branch])): + if not line: + continue + yield apply(Revision, (line,)) + + def get_content_changed(self, revision, path): + for stanza in basic_io_from_stream(self.automate.run('get_content_changed', [revision, path])): + yield stanza + + def get_revision(self, revision): + for stanza in basic_io_from_stream(self.automate.run('get_revision', [revision])): + yield stanza + + def get_manifest_of(self, revision): + for stanza in basic_io_from_stream(self.automate.run('get_manifest_of', [revision])): + yield stanza + + def get_file(self, fileid): + for stanza in self.automate.run('get_file', [fileid]): + yield stanza + + def certs(self, revision): + for stanza in basic_io_from_stream(self.automate.run('certs', [revision])): + yield stanza + + def diff(self, revision_from, revision_t |
