Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(186)

Unified Diff: third_party/buildbot_7_12/buildbot/steps/shell.py

Issue 12207158: Bye bye buildbot 0.7.12. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/buildbot_7_12/buildbot/steps/shell.py
diff --git a/third_party/buildbot_7_12/buildbot/steps/shell.py b/third_party/buildbot_7_12/buildbot/steps/shell.py
deleted file mode 100644
index 0748a4bff4e3098deb8d7cd0cec801d794f1f306..0000000000000000000000000000000000000000
--- a/third_party/buildbot_7_12/buildbot/steps/shell.py
+++ /dev/null
@@ -1,713 +0,0 @@
-# -*- test-case-name: buildbot.test.test_steps,buildbot.test.test_properties -*-
-
-import re
-from twisted.python import log
-from twisted.spread import pb
-from buildbot.process.buildstep import LoggingBuildStep, RemoteShellCommand
-from buildbot.process.buildstep import RemoteCommand
-from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, STDOUT, STDERR
-
-# for existing configurations that import WithProperties from here. We like
-# to move this class around just to keep our readers guessing.
-from buildbot.process.properties import WithProperties
-_hush_pyflakes = [WithProperties]
-del _hush_pyflakes
-
-class ShellCommand(LoggingBuildStep):
- """I run a single shell command on the buildslave. I return FAILURE if
- the exit code of that command is non-zero, SUCCESS otherwise. To change
- this behavior, override my .evaluateCommand method.
-
- By default, a failure of this step will mark the whole build as FAILURE.
- To override this, give me an argument of flunkOnFailure=False .
-
- I create a single Log named 'log' which contains the output of the
- command. To create additional summary Logs, override my .createSummary
- method.
-
- The shell command I run (a list of argv strings) can be provided in
- several ways:
- - a class-level .command attribute
- - a command= parameter to my constructor (overrides .command)
- - set explicitly with my .setCommand() method (overrides both)
-
- @ivar command: a list of renderable objects (typically strings or
- WithProperties instances). This will be used by start()
- to create a RemoteShellCommand instance.
-
- @ivar logfiles: a dict mapping log NAMEs to workdir-relative FILENAMEs
- of their corresponding logfiles. The contents of the file
- named FILENAME will be put into a LogFile named NAME, ina
- something approximating real-time. (note that logfiles=
- is actually handled by our parent class LoggingBuildStep)
-
- @ivar lazylogfiles: Defaults to False. If True, logfiles will be tracked
- `lazily', meaning they will only be added when and if
- they are written to. Empty or nonexistent logfiles
- will be omitted. (Also handled by class
- LoggingBuildStep.)
-
- """
-
- name = "shell"
- description = None # set this to a list of short strings to override
- descriptionDone = None # alternate description when the step is complete
- command = None # set this to a command, or set in kwargs
- # logfiles={} # you can also set 'logfiles' to a dictionary, and it
- # will be merged with any logfiles= argument passed in
- # to __init__
-
- # override this on a specific ShellCommand if you want to let it fail
- # without dooming the entire build to a status of FAILURE
- flunkOnFailure = True
-
- def __init__(self, workdir=None,
- description=None, descriptionDone=None,
- command=None,
- usePTY="slave-config",
- **kwargs):
- # most of our arguments get passed through to the RemoteShellCommand
- # that we create, but first strip out the ones that we pass to
- # BuildStep (like haltOnFailure and friends), and a couple that we
- # consume ourselves.
-
- if description:
- self.description = description
- if isinstance(self.description, str):
- self.description = [self.description]
- if descriptionDone:
- self.descriptionDone = descriptionDone
- if isinstance(self.descriptionDone, str):
- self.descriptionDone = [self.descriptionDone]
- if command:
- self.setCommand(command)
-
- # pull out the ones that LoggingBuildStep wants, then upcall
- buildstep_kwargs = {}
- for k in kwargs.keys()[:]:
- if k in self.__class__.parms:
- buildstep_kwargs[k] = kwargs[k]
- del kwargs[k]
- LoggingBuildStep.__init__(self, **buildstep_kwargs)
- self.addFactoryArguments(workdir=workdir,
- description=description,
- descriptionDone=descriptionDone,
- command=command)
-
- # everything left over goes to the RemoteShellCommand
- kwargs['workdir'] = workdir # including a copy of 'workdir'
- kwargs['usePTY'] = usePTY
- self.remote_kwargs = kwargs
- # we need to stash the RemoteShellCommand's args too
- self.addFactoryArguments(**kwargs)
-
- def setStepStatus(self, step_status):
- LoggingBuildStep.setStepStatus(self, step_status)
-
- # start doesn't set text soon enough to capture our description in
- # the stepStarted status notification. Set text here so it's included.
- self.step_status.setText(self.describe(False))
-
- def setDefaultWorkdir(self, workdir):
- rkw = self.remote_kwargs
- rkw['workdir'] = rkw['workdir'] or workdir
-
- def setCommand(self, command):
- self.command = command
-
- def describe(self, done=False):
- """Return a list of short strings to describe this step, for the
- status display. This uses the first few words of the shell command.
- You can replace this by setting .description in your subclass, or by
- overriding this method to describe the step better.
-
- @type done: boolean
- @param done: whether the command is complete or not, to improve the
- way the command is described. C{done=False} is used
- while the command is still running, so a single
- imperfect-tense verb is appropriate ('compiling',
- 'testing', ...) C{done=True} is used when the command
- has finished, and the default getText() method adds some
- text, so a simple noun is appropriate ('compile',
- 'tests' ...)
- """
-
- if done and self.descriptionDone is not None:
- return list(self.descriptionDone)
- if self.description is not None:
- return list(self.description)
-
- properties = self.build.getProperties()
- words = self.command
- if isinstance(words, (str, unicode)):
- words = words.split()
- # render() each word to handle WithProperties objects
- words = properties.render(words)
- if len(words) < 1:
- return ["???"]
- if len(words) == 1:
- return ["'%s'" % words[0]]
- if len(words) == 2:
- return ["'%s" % words[0], "%s'" % words[1]]
- return ["'%s" % words[0], "%s" % words[1], "...'"]
-
- def setupEnvironment(self, cmd):
- # merge in anything from Build.slaveEnvironment
- # This can be set from a Builder-level environment, or from earlier
- # BuildSteps. The latter method is deprecated and superceded by
- # BuildProperties.
- # Environment variables passed in by a BuildStep override
- # those passed in at the Builder level.
- properties = self.build.getProperties()
- slaveEnv = self.build.slaveEnvironment
- if slaveEnv:
- if cmd.args['env'] is None:
- cmd.args['env'] = {}
- fullSlaveEnv = slaveEnv.copy()
- fullSlaveEnv.update(cmd.args['env'])
- cmd.args['env'] = properties.render(fullSlaveEnv)
- # note that each RemoteShellCommand gets its own copy of the
- # dictionary, so we shouldn't be affecting anyone but ourselves.
-
- def checkForOldSlaveAndLogfiles(self):
- if not self.logfiles:
- return # doesn't matter
- if not self.slaveVersionIsOlderThan("shell", "2.1"):
- return # slave is new enough
- # this buildslave is too old and will ignore the 'logfiles'
- # argument. You'll either have to pull the logfiles manually
- # (say, by using 'cat' in a separate RemoteShellCommand) or
- # upgrade the buildslave.
- msg1 = ("Warning: buildslave %s is too old "
- "to understand logfiles=, ignoring it."
- % self.getSlaveName())
- msg2 = "You will have to pull this logfile (%s) manually."
- log.msg(msg1)
- for logname,remotefilevalue in self.logfiles.items():
- remotefilename = remotefilevalue
- # check for a dictionary of options
- if type(remotefilevalue) == dict:
- remotefilename = remotefilevalue['filename']
-
- newlog = self.addLog(logname)
- newlog.addHeader(msg1 + "\n")
- newlog.addHeader(msg2 % remotefilename + "\n")
- newlog.finish()
- # now prevent setupLogfiles() from adding them
- self.logfiles = {}
-
- def start(self):
- # this block is specific to ShellCommands. subclasses that don't need
- # to set up an argv array, an environment, or extra logfiles= (like
- # the Source subclasses) can just skip straight to startCommand()
- properties = self.build.getProperties()
-
- warnings = []
-
- # create the actual RemoteShellCommand instance now
- kwargs = properties.render(self.remote_kwargs)
- kwargs['command'] = properties.render(self.command)
- kwargs['logfiles'] = self.logfiles
-
- # check for the usePTY flag
- if kwargs.has_key('usePTY') and kwargs['usePTY'] != 'slave-config':
- slavever = self.slaveVersion("shell", "old")
- if self.slaveVersionIsOlderThan("svn", "2.7"):
- warnings.append("NOTE: slave does not allow master to override usePTY\n")
-
- cmd = RemoteShellCommand(**kwargs)
- self.setupEnvironment(cmd)
- self.checkForOldSlaveAndLogfiles()
-
- self.startCommand(cmd, warnings)
-
-
-
-class TreeSize(ShellCommand):
- name = "treesize"
- command = ["du", "-s", "-k", "."]
- description = "measuring tree size"
- descriptionDone = "tree size measured"
- kib = None
-
- def __init__(self, *args, **kwargs):
- ShellCommand.__init__(self, *args, **kwargs)
-
- def commandComplete(self, cmd):
- out = cmd.logs['stdio'].getText()
- m = re.search(r'^(\d+)', out)
- if m:
- self.kib = int(m.group(1))
- self.setProperty("tree-size-KiB", self.kib, "treesize")
-
- def evaluateCommand(self, cmd):
- if cmd.rc != 0:
- return FAILURE
- if self.kib is None:
- return WARNINGS # not sure how 'du' could fail, but whatever
- return SUCCESS
-
- def getText(self, cmd, results):
- if self.kib is not None:
- return ["treesize", "%d KiB" % self.kib]
- return ["treesize", "unknown"]
-
-class SetProperty(ShellCommand):
- name = "setproperty"
-
- def __init__(self, **kwargs):
- self.property = None
- self.extract_fn = None
- self.strip = True
-
- if kwargs.has_key('property'):
- self.property = kwargs['property']
- del kwargs['property']
- if kwargs.has_key('extract_fn'):
- self.extract_fn = kwargs['extract_fn']
- del kwargs['extract_fn']
- if kwargs.has_key('strip'):
- self.strip = kwargs['strip']
- del kwargs['strip']
-
- ShellCommand.__init__(self, **kwargs)
-
- self.addFactoryArguments(property=self.property)
- self.addFactoryArguments(extract_fn=self.extract_fn)
- self.addFactoryArguments(strip=self.strip)
-
- assert self.property or self.extract_fn, \
- "SetProperty step needs either property= or extract_fn="
-
- self.property_changes = {}
-
- def commandComplete(self, cmd):
- if self.property:
- result = cmd.logs['stdio'].getText()
- if self.strip: result = result.strip()
- propname = self.build.getProperties().render(self.property)
- self.setProperty(propname, result, "SetProperty Step")
- self.property_changes[propname] = result
- else:
- log = cmd.logs['stdio']
- new_props = self.extract_fn(cmd.rc,
- ''.join(log.getChunks([STDOUT], onlyText=True)),
- ''.join(log.getChunks([STDERR], onlyText=True)))
- for k,v in new_props.items():
- self.setProperty(k, v, "SetProperty Step")
- self.property_changes = new_props
-
- def createSummary(self, log):
- props_set = [ "%s: %r" % (k,v) for k,v in self.property_changes.items() ]
- self.addCompleteLog('property changes', "\n".join(props_set))
-
- def getText(self, cmd, results):
- if self.property_changes:
- return [ "set props:" ] + self.property_changes.keys()
- else:
- return [ "no change" ]
-
-class Configure(ShellCommand):
-
- name = "configure"
- haltOnFailure = 1
- flunkOnFailure = 1
- description = ["configuring"]
- descriptionDone = ["configure"]
- command = ["./configure"]
-
-class StringFileWriter(pb.Referenceable):
- """
- FileWriter class that just puts received data into a buffer.
-
- Used to upload a file from slave for inline processing rather than
- writing into a file on master.
- """
- def __init__(self):
- self.buffer = ""
-
- def remote_write(self, data):
- self.buffer += data
-
- def remote_close(self):
- pass
-
-class SilentRemoteCommand(RemoteCommand):
- """
- Remote command subclass used to run an internal file upload command on the
- slave. We do not need any progress updates from such command, so override
- remoteUpdate() with an empty method.
- """
- def remoteUpdate(self, update):
- pass
-
-class WarningCountingShellCommand(ShellCommand):
- warnCount = 0
- warningPattern = '.*warning[: ].*'
- # The defaults work for GNU Make.
- directoryEnterPattern = "make.*: Entering directory [\"`'](.*)['`\"]"
- directoryLeavePattern = "make.*: Leaving directory"
- suppressionFile = None
-
- commentEmptyLineRe = re.compile(r"^\s*(\#.*)?$")
- suppressionLineRe = re.compile(r"^\s*(.+?)\s*:\s*(.+?)\s*(?:[:]\s*([0-9]+)(?:-([0-9]+))?\s*)?$")
-
- def __init__(self, workdir=None,
- warningPattern=None, warningExtractor=None,
- directoryEnterPattern=None, directoryLeavePattern=None,
- suppressionFile=None, **kwargs):
- self.workdir = workdir
- # See if we've been given a regular expression to use to match
- # warnings. If not, use a default that assumes any line with "warning"
- # present is a warning. This may lead to false positives in some cases.
- if warningPattern:
- self.warningPattern = warningPattern
- if directoryEnterPattern:
- self.directoryEnterPattern = directoryEnterPattern
- if directoryLeavePattern:
- self.directoryLeavePattern = directoryLeavePattern
- if suppressionFile:
- self.suppressionFile = suppressionFile
- if warningExtractor:
- self.warningExtractor = warningExtractor
- else:
- self.warningExtractor = WarningCountingShellCommand.warnExtractWholeLine
-
- # And upcall to let the base class do its work
- ShellCommand.__init__(self, workdir=workdir, **kwargs)
-
- self.addFactoryArguments(warningPattern=warningPattern,
- directoryEnterPattern=directoryEnterPattern,
- directoryLeavePattern=directoryLeavePattern,
- warningExtractor=warningExtractor,
- suppressionFile=suppressionFile)
- self.suppressions = []
- self.directoryStack = []
-
- def setDefaultWorkdir(self, workdir):
- if self.workdir is None:
- self.workdir = workdir
- ShellCommand.setDefaultWorkdir(self, workdir)
-
- def addSuppression(self, suppressionList):
- """
- This method can be used to add patters of warnings that should
- not be counted.
-
- It takes a single argument, a list of patterns.
-
- Each pattern is a 4-tuple (FILE-RE, WARN-RE, START, END).
-
- FILE-RE is a regular expression (string or compiled regexp), or None.
- If None, the pattern matches all files, else only files matching the
- regexp. If directoryEnterPattern is specified in the class constructor,
- matching is against the full path name, eg. src/main.c.
-
- WARN-RE is similarly a regular expression matched against the
- text of the warning, or None to match all warnings.
-
- START and END form an inclusive line number range to match against. If
- START is None, there is no lower bound, similarly if END is none there
- is no upper bound."""
-
- for fileRe, warnRe, start, end in suppressionList:
- if fileRe != None and isinstance(fileRe, str):
- fileRe = re.compile(fileRe)
- if warnRe != None and isinstance(warnRe, str):
- warnRe = re.compile(warnRe)
- self.suppressions.append((fileRe, warnRe, start, end))
-
- def warnExtractWholeLine(self, line, match):
- """
- Extract warning text as the whole line.
- No file names or line numbers."""
- return (None, None, line)
-
- def warnExtractFromRegexpGroups(self, line, match):
- """
- Extract file name, line number, and warning text as groups (1,2,3)
- of warningPattern match."""
- file = match.group(1)
- lineNo = match.group(2)
- if lineNo != None:
- lineNo = int(lineNo)
- text = match.group(3)
- return (file, lineNo, text)
-
- def maybeAddWarning(self, warnings, line, match):
- if self.suppressions:
- (file, lineNo, text) = self.warningExtractor(self, line, match)
-
- if file != None and file != "" and self.directoryStack:
- currentDirectory = self.directoryStack[-1]
- if currentDirectory != None and currentDirectory != "":
- file = "%s/%s" % (currentDirectory, file)
-
- # Skip adding the warning if any suppression matches.
- for fileRe, warnRe, start, end in self.suppressions:
- if ( (file == None or fileRe == None or fileRe.search(file)) and
- (warnRe == None or warnRe.search(text)) and
- lineNo != None and
- (start == None or start <= lineNo) and
- (end == None or end >= lineNo) ):
- return
-
- warnings.append(line)
- self.warnCount += 1
-
- def start(self):
- if self.suppressionFile == None:
- return ShellCommand.start(self)
-
- version = self.slaveVersion("uploadFile")
- if not version:
- m = "Slave is too old, does not know about uploadFile"
- raise BuildSlaveTooOldError(m)
-
- self.myFileWriter = StringFileWriter()
-
- properties = self.build.getProperties()
-
- args = {
- 'slavesrc': properties.render(self.suppressionFile),
- 'workdir': self.workdir,
- 'writer': self.myFileWriter,
- 'maxsize': None,
- 'blocksize': 32*1024,
- }
- cmd = SilentRemoteCommand('uploadFile', args)
- d = self.runCommand(cmd)
- d.addCallback(self.uploadDone)
- d.addErrback(self.failed)
-
- def uploadDone(self, dummy):
- lines = self.myFileWriter.buffer.split("\n")
- del(self.myFileWriter)
-
- list = []
- for line in lines:
- if self.commentEmptyLineRe.match(line):
- continue
- match = self.suppressionLineRe.match(line)
- if (match):
- file, test, start, end = match.groups()
- if (end != None):
- end = int(end)
- if (start != None):
- start = int(start)
- if end == None:
- end = start
- list.append((file, test, start, end))
-
- self.addSuppression(list)
- return ShellCommand.start(self)
-
- def createSummary(self, log):
- self.warnCount = 0
-
- # Now compile a regular expression from whichever warning pattern we're
- # using
- if not self.warningPattern:
- return
-
- wre = self.warningPattern
- if isinstance(wre, str):
- wre = re.compile(wre)
-
- directoryEnterRe = self.directoryEnterPattern
- if directoryEnterRe != None and isinstance(directoryEnterRe, str):
- directoryEnterRe = re.compile(directoryEnterRe)
-
- directoryLeaveRe = self.directoryLeavePattern
- if directoryLeaveRe != None and isinstance(directoryLeaveRe, str):
- directoryLeaveRe = re.compile(directoryLeaveRe)
-
- # Check if each line in the output from this command matched our
- # warnings regular expressions. If did, bump the warnings count and
- # add the line to the collection of lines with warnings
- warnings = []
- # TODO: use log.readlines(), except we need to decide about stdout vs
- # stderr
- for line in log.getText().split("\n"):
- if directoryEnterRe:
- match = directoryEnterRe.search(line)
- if match:
- self.directoryStack.append(match.group(1))
- if (directoryLeaveRe and
- self.directoryStack and
- directoryLeaveRe.search(line)):
- self.directoryStack.pop()
-
- match = wre.match(line)
- if match:
- self.maybeAddWarning(warnings, line, match)
-
- # If there were any warnings, make the log if lines with warnings
- # available
- if self.warnCount:
- self.addCompleteLog("warnings", "\n".join(warnings) + "\n")
-
- warnings_stat = self.step_status.getStatistic('warnings', 0)
- self.step_status.setStatistic('warnings', warnings_stat + self.warnCount)
-
- try:
- old_count = self.getProperty("warnings-count")
- except KeyError:
- old_count = 0
- self.setProperty("warnings-count", old_count + self.warnCount, "WarningCountingShellCommand")
-
-
- def evaluateCommand(self, cmd):
- if cmd.rc != 0:
- return FAILURE
- if self.warnCount:
- return WARNINGS
- return SUCCESS
-
-
-class Compile(WarningCountingShellCommand):
-
- name = "compile"
- haltOnFailure = 1
- flunkOnFailure = 1
- description = ["compiling"]
- descriptionDone = ["compile"]
- command = ["make", "all"]
-
- OFFprogressMetrics = ('output',)
- # things to track: number of files compiled, number of directories
- # traversed (assuming 'make' is being used)
-
- def createSummary(self, log):
- # TODO: grep for the characteristic GCC error lines and
- # assemble them into a pair of buffers
- WarningCountingShellCommand.createSummary(self, log)
-
-class Test(WarningCountingShellCommand):
-
- name = "test"
- warnOnFailure = 1
- description = ["testing"]
- descriptionDone = ["test"]
- command = ["make", "test"]
-
- def setTestResults(self, total=0, failed=0, passed=0, warnings=0):
- """
- Called by subclasses to set the relevant statistics; this actually
- adds to any statistics already present
- """
- total += self.step_status.getStatistic('tests-total', 0)
- self.step_status.setStatistic('tests-total', total)
- failed += self.step_status.getStatistic('tests-failed', 0)
- self.step_status.setStatistic('tests-failed', failed)
- warnings += self.step_status.getStatistic('tests-warnings', 0)
- self.step_status.setStatistic('tests-warnings', warnings)
- passed += self.step_status.getStatistic('tests-passed', 0)
- self.step_status.setStatistic('tests-passed', passed)
-
- def describe(self, done=False):
- description = WarningCountingShellCommand.describe(self, done)
- if done:
- if self.step_status.hasStatistic('tests-total'):
- total = self.step_status.getStatistic("tests-total", 0)
- failed = self.step_status.getStatistic("tests-failed", 0)
- passed = self.step_status.getStatistic("tests-passed", 0)
- warnings = self.step_status.getStatistic("tests-warnings", 0)
- if not total:
- total = failed + passed + warnings
-
- if total:
- description.append('%d tests' % total)
- if passed:
- description.append('%d passed' % passed)
- if warnings:
- description.append('%d warnings' % warnings)
- if failed:
- description.append('%d failed' % failed)
- return description
-
-class PerlModuleTest(Test):
- command=["prove", "--lib", "lib", "-r", "t"]
- total = 0
-
- def evaluateCommand(self, cmd):
- # Get stdio, stripping pesky newlines etc.
- lines = map(
- lambda line : line.replace('\r\n','').replace('\r','').replace('\n',''),
- self.getLog('stdio').readlines()
- )
-
- total = 0
- passed = 0
- failed = 0
- rc = cmd.rc
-
- # New version of Test::Harness?
- try:
- test_summary_report_index = lines.index("Test Summary Report")
-
- del lines[0:test_summary_report_index + 2]
-
- re_test_result = re.compile("^Result: (PASS|FAIL)$|Tests: \d+ Failed: (\d+)\)|Files=\d+, Tests=(\d+)")
-
- mos = map(lambda line: re_test_result.search(line), lines)
- test_result_lines = [mo.groups() for mo in mos if mo]
-
- for line in test_result_lines:
- if line[0] == 'PASS':
- rc = SUCCESS
- elif line[0] == 'FAIL':
- rc = FAILURE
- elif line[1]:
- failed += int(line[1])
- elif line[2]:
- total = int(line[2])
-
- except ValueError: # Nope, it's the old version
- re_test_result = re.compile("^(All tests successful)|(\d+)/(\d+) subtests failed|Files=\d+, Tests=(\d+),")
-
- mos = map(lambda line: re_test_result.search(line), lines)
- test_result_lines = [mo.groups() for mo in mos if mo]
-
- if test_result_lines:
- test_result_line = test_result_lines[0]
-
- success = test_result_line[0]
-
- if success:
- failed = 0
-
- test_totals_line = test_result_lines[1]
- total_str = test_totals_line[3]
- rc = SUCCESS
- else:
- failed_str = test_result_line[1]
- failed = int(failed_str)
-
- total_str = test_result_line[2]
-
- rc = FAILURE
-
- total = int(total_str)
-
- warnings = 0
- if self.warningPattern:
- wre = self.warningPattern
- if isinstance(wre, str):
- wre = re.compile(wre)
-
- warnings = len([l for l in lines if wre.search(l)])
-
- # Because there are two paths that are used to determine
- # the success/fail result, I have to modify it here if
- # there were warnings.
- if rc == SUCCESS and warnings:
- rc = WARNINGS
-
- if total:
- passed = total - failed
-
- self.setTestResults(total=total, failed=failed, passed=passed,
- warnings=warnings)
-
- return rc
« no previous file with comments | « third_party/buildbot_7_12/buildbot/steps/python_twisted.py ('k') | third_party/buildbot_7_12/buildbot/steps/source.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698