<html>
  <head>
    <meta content="text/html; charset=windows-1252"
      http-equiv="Content-Type">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    <div class="moz-cite-prefix">On 16/09/16 20:25, Chris Matthews
      wrote:<br>
    </div>
    <blockquote cite="mid:etPan.57dc3919.29c0aa5b.2baa@apple.com"
      type="cite">
      <style>body{font-family:Helvetica,Arial;font-size:13px}</style>
      <div id="bloop_customfont"
        style="font-family:Helvetica,Arial;font-size:13px; color:
        rgba(0,0,0,1.0); margin: 0px; line-height: auto;">Yes those are
        good changes! Please do add them.</div>
    </blockquote>
    Thanks, done in r281890. The tool is great! Are there plans to
    upload unix builds, too? It'd be very helpful to us.
    <meta http-equiv="content-type" content="text/html;
      charset=windows-1252">
    <blockquote cite="mid:etPan.57dc3919.29c0aa5b.2baa@apple.com"
      type="cite"> <br>
      <br>
      <p class="airmail_on">On September 16, 2016 at 3:10:27 AM, Vassil
        Vassilev (<a moz-do-not-send="true"
          href="mailto:v.g.vassilev@gmail.com">v.g.vassilev@gmail.com</a>)
        wrote:</p>
      <blockquote type="cite" class="clean_bq"><span>
          <div>
            <div>On 08/10/15 23:52, Chris Matthews via llvm-commits
              wrote:
              <br>
              > Author: cmatthews
              <br>
              > Date: Thu Oct 8 16:52:50 2015
              <br>
              > New Revision: 249757
              <br>
              >
              <br>
              > URL:
              <a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project?rev=249757&view=rev">http://llvm.org/viewvc/llvm-project?rev=249757&view=rev</a>
              <br>
              > Log:
              <br>
              > llvmlab bisect tool
              <br>
              >
              <br>
              > Added:
              <br>
              > zorg/trunk/llvmbisect/
              <br>
              > zorg/trunk/llvmbisect/bin/
              <br>
              > zorg/trunk/llvmbisect/bin/llvmlab (with props)
              <br>
              > zorg/trunk/llvmbisect/docs/
              <br>
              > zorg/trunk/llvmbisect/docs/Makefile
              <br>
              > zorg/trunk/llvmbisect/docs/builders.rst
              <br>
              > zorg/trunk/llvmbisect/docs/conf.py
              <br>
              > zorg/trunk/llvmbisect/docs/index.rst
              <br>
              > zorg/trunk/llvmbisect/docs/llvmlab_bisect.rst
              <br>
              > zorg/trunk/llvmbisect/llvmlab/
              <br>
              > zorg/trunk/llvmbisect/llvmlab/__init__.py
              <br>
              > zorg/trunk/llvmbisect/llvmlab/algorithm.py
              <br>
              > zorg/trunk/llvmbisect/llvmlab/ci.py
              <br>
              > zorg/trunk/llvmbisect/llvmlab/clang_link (with props)
              <br>
              > zorg/trunk/llvmbisect/llvmlab/gcs.py
              <br>
              > zorg/trunk/llvmbisect/llvmlab/llvmlab.py
              <br>
              > zorg/trunk/llvmbisect/llvmlab/scripts.py
              <br>
              > zorg/trunk/llvmbisect/llvmlab/shell.py
              <br>
              > zorg/trunk/llvmbisect/llvmlab/test_llvmlab.py
              <br>
              > zorg/trunk/llvmbisect/llvmlab/util.py
              <br>
              > zorg/trunk/llvmbisect/setup.py
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/bin/llvmlab
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/bin/llvmlab?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/bin/llvmlab?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/bin/llvmlab (added)
              <br>
              > +++ zorg/trunk/llvmbisect/bin/llvmlab Thu Oct 8
              16:52:50 2015
              <br>
              > @@ -0,0 +1,27 @@
              <br>
              > +#!/usr/bin/env python
              <br>
              > +
              <br>
              > +import sys
              <br>
              > +import errno
              <br>
              > +
              <br>
              > +from llvmlab.ci import action_fetch, action_ls,
              action_bisect, action_exec
              <br>
              > +from llvmlab.ci import action_test
              <br>
              > +from llvmlab import scripts
              <br>
              > +
              <br>
              > +
              <br>
              > +tool = scripts.Tool(locals())
              <br>
              > +main = tool.main
              <br>
              > +
              <br>
              > +if __name__ == '__main__':
              <br>
              > + rc = None
              <br>
              > + # Execute the main function in a try block to catch
              EPIPE exceptions.
              <br>
              > + try:
              <br>
              > + rc = main(sys.argv)
              <br>
              > +
              <br>
              > + # Force a flush on the output pipe to ensure EPIPE
              shows up here (prior
              <br>
              > + # to sys.stdout shutdown).
              <br>
              > + sys.stdout.flush()
              <br>
              > + sys.stderr.flush()
              <br>
              > + except IOError as e:
              <br>
              > + if e.errno != errno.EPIPE:
              <br>
              > + raise
              <br>
              > + sys.exit(0)
              <br>
              >
              <br>
              > Propchange: zorg/trunk/llvmbisect/bin/llvmlab
              <br>
              >
------------------------------------------------------------------------------<br>
              > svn:executable = *
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/docs/Makefile
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/Makefile?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/Makefile?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/docs/Makefile (added)
              <br>
              > +++ zorg/trunk/llvmbisect/docs/Makefile Thu Oct 8
              16:52:50 2015
              <br>
              > @@ -0,0 +1,177 @@
              <br>
              > +# Makefile for Sphinx documentation
              <br>
              > +#
              <br>
              > +
              <br>
              > +# You can set these variables from the command line.
              <br>
              > +SPHINXOPTS =
              <br>
              > +SPHINXBUILD = sphinx-build
              <br>
              > +PAPER =
              <br>
              > +BUILDDIR = _build
              <br>
              > +
              <br>
              > +# User-friendly check for sphinx-build
              <br>
              > +ifeq ($(shell which $(SPHINXBUILD) >/dev/null
              2>&1; echo $$?), 1)
              <br>
              > +$(error The '$(SPHINXBUILD)' command was not found.
              Make sure you have Sphinx installed, then set the
              SPHINXBUILD environment variable to point to the full path
              of the '$(SPHINXBUILD)' executable. Alternatively you can
              add the directory with the executable to your PATH. If you
              don't have Sphinx installed, grab it from
              <a class="moz-txt-link-freetext" href="http://sphinx-doc.org/">http://sphinx-doc.org/</a>)
              <br>
              > +endif
              <br>
              > +
              <br>
              > +# Internal variables.
              <br>
              > +PAPEROPT_a4 = -D latex_paper_size=a4
              <br>
              > +PAPEROPT_letter = -D latex_paper_size=letter
              <br>
              > +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees
              $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
              <br>
              > +# the i18n builder cannot share the environment and
              doctrees with the others
              <br>
              > +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS)
              .
              <br>
              > +
              <br>
              > +.PHONY: help clean html dirhtml singlehtml pickle
              json htmlhelp qthelp devhelp epub latex latexpdf text man
              changes linkcheck doctest gettext
              <br>
              > +
              <br>
              > +help:
              <br>
              > + @echo "Please use \`make <target>' where
              <target> is one of"
              <br>
              > + @echo " html to make standalone HTML files"
              <br>
              > + @echo " dirhtml to make HTML files named index.html
              in directories"
              <br>
              > + @echo " singlehtml to make a single large HTML
              file"
              <br>
              > + @echo " pickle to make pickle files"
              <br>
              > + @echo " json to make JSON files"
              <br>
              > + @echo " htmlhelp to make HTML files and a HTML help
              project"
              <br>
              > + @echo " qthelp to make HTML files and a qthelp
              project"
              <br>
              > + @echo " devhelp to make HTML files and a Devhelp
              project"
              <br>
              > + @echo " epub to make an epub"
              <br>
              > + @echo " latex to make LaTeX files, you can set
              PAPER=a4 or PAPER=letter"
              <br>
              > + @echo " latexpdf to make LaTeX files and run them
              through pdflatex"
              <br>
              > + @echo " latexpdfja to make LaTeX files and run them
              through platex/dvipdfmx"
              <br>
              > + @echo " text to make text files"
              <br>
              > + @echo " man to make manual pages"
              <br>
              > + @echo " texinfo to make Texinfo files"
              <br>
              > + @echo " info to make Texinfo files and run them
              through makeinfo"
              <br>
              > + @echo " gettext to make PO message catalogs"
              <br>
              > + @echo " changes to make an overview of all
              changed/added/deprecated items"
              <br>
              > + @echo " xml to make Docutils-native XML files"
              <br>
              > + @echo " pseudoxml to make pseudoxml-XML files for
              display purposes"
              <br>
              > + @echo " linkcheck to check all external links for
              integrity"
              <br>
              > + @echo " doctest to run all doctests embedded in the
              documentation (if enabled)"
              <br>
              > +
              <br>
              > +clean:
              <br>
              > + rm -rf $(BUILDDIR)/*
              <br>
              > +
              <br>
              > +html:
              <br>
              > + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS)
              $(BUILDDIR)/html
              <br>
              > + @echo
              <br>
              > + @echo "Build finished. The HTML pages are in
              $(BUILDDIR)/html."
              <br>
              > +
              <br>
              > +dirhtml:
              <br>
              > + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS)
              $(BUILDDIR)/dirhtml
              <br>
              > + @echo
              <br>
              > + @echo "Build finished. The HTML pages are in
              $(BUILDDIR)/dirhtml."
              <br>
              > +
              <br>
              > +singlehtml:
              <br>
              > + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS)
              $(BUILDDIR)/singlehtml
              <br>
              > + @echo
              <br>
              > + @echo "Build finished. The HTML page is in
              $(BUILDDIR)/singlehtml."
              <br>
              > +
              <br>
              > +pickle:
              <br>
              > + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS)
              $(BUILDDIR)/pickle
              <br>
              > + @echo
              <br>
              > + @echo "Build finished; now you can process the
              pickle files."
              <br>
              > +
              <br>
              > +json:
              <br>
              > + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS)
              $(BUILDDIR)/json
              <br>
              > + @echo
              <br>
              > + @echo "Build finished; now you can process the JSON
              files."
              <br>
              > +
              <br>
              > +htmlhelp:
              <br>
              > + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS)
              $(BUILDDIR)/htmlhelp
              <br>
              > + @echo
              <br>
              > + @echo "Build finished; now you can run HTML Help
              Workshop with the" \
              <br>
              > + ".hhp project file in $(BUILDDIR)/htmlhelp."
              <br>
              > +
              <br>
              > +qthelp:
              <br>
              > + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS)
              $(BUILDDIR)/qthelp
              <br>
              > + @echo
              <br>
              > + @echo "Build finished; now you can run
              "qcollectiongenerator" with the" \
              <br>
              > + ".qhcp project file in $(BUILDDIR)/qthelp, like
              this:"
              <br>
              > + @echo "# qcollectiongenerator
              $(BUILDDIR)/qthelp/LLVMLabTools.qhcp"
              <br>
              > + @echo "To view the help file:"
              <br>
              > + @echo "# assistant -collectionFile
              $(BUILDDIR)/qthelp/LLVMLabTools.qhc"
              <br>
              > +
              <br>
              > +devhelp:
              <br>
              > + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS)
              $(BUILDDIR)/devhelp
              <br>
              > + @echo
              <br>
              > + @echo "Build finished."
              <br>
              > + @echo "To view the help file:"
              <br>
              > + @echo "# mkdir -p
              $$HOME/.local/share/devhelp/LLVMLabTools"
              <br>
              > + @echo "# ln -s $(BUILDDIR)/devhelp
              $$HOME/.local/share/devhelp/LLVMLabTools"
              <br>
              > + @echo "# devhelp"
              <br>
              > +
              <br>
              > +epub:
              <br>
              > + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS)
              $(BUILDDIR)/epub
              <br>
              > + @echo
              <br>
              > + @echo "Build finished. The epub file is in
              $(BUILDDIR)/epub."
              <br>
              > +
              <br>
              > +latex:
              <br>
              > + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS)
              $(BUILDDIR)/latex
              <br>
              > + @echo
              <br>
              > + @echo "Build finished; the LaTeX files are in
              $(BUILDDIR)/latex."
              <br>
              > + @echo "Run \`make' in that directory to run these
              through (pdf)latex" \
              <br>
              > + "(use \`make latexpdf' here to do that
              automatically)."
              <br>
              > +
              <br>
              > +latexpdf:
              <br>
              > + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS)
              $(BUILDDIR)/latex
              <br>
              > + @echo "Running LaTeX files through pdflatex..."
              <br>
              > + $(MAKE) -C $(BUILDDIR)/latex all-pdf
              <br>
              > + @echo "pdflatex finished; the PDF files are in
              $(BUILDDIR)/latex."
              <br>
              > +
              <br>
              > +latexpdfja:
              <br>
              > + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS)
              $(BUILDDIR)/latex
              <br>
              > + @echo "Running LaTeX files through platex and
              dvipdfmx..."
              <br>
              > + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
              <br>
              > + @echo "pdflatex finished; the PDF files are in
              $(BUILDDIR)/latex."
              <br>
              > +
              <br>
              > +text:
              <br>
              > + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS)
              $(BUILDDIR)/text
              <br>
              > + @echo
              <br>
              > + @echo "Build finished. The text files are in
              $(BUILDDIR)/text."
              <br>
              > +
              <br>
              > +man:
              <br>
              > + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS)
              $(BUILDDIR)/man
              <br>
              > + @echo
              <br>
              > + @echo "Build finished. The manual pages are in
              $(BUILDDIR)/man."
              <br>
              > +
              <br>
              > +texinfo:
              <br>
              > + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS)
              $(BUILDDIR)/texinfo
              <br>
              > + @echo
              <br>
              > + @echo "Build finished. The Texinfo files are in
              $(BUILDDIR)/texinfo."
              <br>
              > + @echo "Run \`make' in that directory to run these
              through makeinfo" \
              <br>
              > + "(use \`make info' here to do that automatically)."
              <br>
              > +
              <br>
              > +info:
              <br>
              > + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS)
              $(BUILDDIR)/texinfo
              <br>
              > + @echo "Running Texinfo files through makeinfo..."
              <br>
              > + make -C $(BUILDDIR)/texinfo info
              <br>
              > + @echo "makeinfo finished; the Info files are in
              $(BUILDDIR)/texinfo."
              <br>
              > +
              <br>
              > +gettext:
              <br>
              > + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS)
              $(BUILDDIR)/locale
              <br>
              > + @echo
              <br>
              > + @echo "Build finished. The message catalogs are in
              $(BUILDDIR)/locale."
              <br>
              > +
              <br>
              > +changes:
              <br>
              > + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS)
              $(BUILDDIR)/changes
              <br>
              > + @echo
              <br>
              > + @echo "The overview file is in
              $(BUILDDIR)/changes."
              <br>
              > +
              <br>
              > +linkcheck:
              <br>
              > + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS)
              $(BUILDDIR)/linkcheck
              <br>
              > + @echo
              <br>
              > + @echo "Link check complete; look for any errors in
              the above output " \
              <br>
              > + "or in $(BUILDDIR)/linkcheck/output.txt."
              <br>
              > +
              <br>
              > +doctest:
              <br>
              > + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS)
              $(BUILDDIR)/doctest
              <br>
              > + @echo "Testing of doctests in the sources finished,
              look at the " \
              <br>
              > + "results in $(BUILDDIR)/doctest/output.txt."
              <br>
              > +
              <br>
              > +xml:
              <br>
              > + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS)
              $(BUILDDIR)/xml
              <br>
              > + @echo
              <br>
              > + @echo "Build finished. The XML files are in
              $(BUILDDIR)/xml."
              <br>
              > +
              <br>
              > +pseudoxml:
              <br>
              > + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS)
              $(BUILDDIR)/pseudoxml
              <br>
              > + @echo
              <br>
              > + @echo "Build finished. The pseudo-XML files are in
              $(BUILDDIR)/pseudoxml."
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/docs/builders.rst
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/builders.rst?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/builders.rst?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/docs/builders.rst (added)
              <br>
              > +++ zorg/trunk/llvmbisect/docs/builders.rst Thu Oct 8
              16:52:50 2015
              <br>
              > @@ -0,0 +1,19 @@
              <br>
              > +.. _builders:
              <br>
              > +
              <br>
              > +Adding your builder to llvmlab bisect
              <br>
              > +=====================================
              <br>
              > +
              <br>
              > +llvmlab bisect compilers are stored on Google Cloud
              Storage. There is a common
              <br>
              > +bucket called llvm-build-artifacts, within that
              there is a directory for each
              <br>
              > +build. Builds can be uploaded in two ways, with
              authorized credentials with
              <br>
              > +the gsutil tool, or if the builder is in
              lab.llvm.org, from the labmaster2
              <br>
              > +stageing server.
              <br>
              > +
              <br>
              > +On the labmaster2 staging server any builds uploaded
              to:
              <br>
              >
              +``/Library/WebServer/Documents/artifacts/<buildername>/``
              will be uploaded via
              <br>
              > +a cron job. Your builders public key will need to be
              added to that machine.
              <br>
              > +Rsync or scp can be used to upload the files.
              <br>
              > +
              <br>
              > +llvmbisect uses some regexes in llvmlab.py to parse
              the comiler information.
              <br>
              > +The tar file you upload will need to match those
              regexes. For example:
              <br>
              > +``clang-r249497-t13154-b13154.tar.gz``
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/docs/conf.py
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/conf.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/conf.py?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/docs/conf.py (added)
              <br>
              > +++ zorg/trunk/llvmbisect/docs/conf.py Thu Oct 8
              16:52:50 2015
              <br>
              > @@ -0,0 +1,258 @@
              <br>
              > +# -*- coding: utf-8 -*-
              <br>
              > +#
              <br>
              > +# LLVM Lab Tools documentation build configuration
              file, created by
              <br>
              > +# sphinx-quickstart on Tue Oct 6 18:29:53 2015.
              <br>
              > +#
              <br>
              > +# This file is execfile()d with the current
              directory set to its
              <br>
              > +# containing dir.
              <br>
              > +#
              <br>
              > +# Note that not all possible configuration values
              are present in this
              <br>
              > +# autogenerated file.
              <br>
              > +#
              <br>
              > +# All configuration values have a default; values
              that are commented out
              <br>
              > +# serve to show the default.
              <br>
              > +
              <br>
              > +import sys
              <br>
              > +import os
              <br>
              > +
              <br>
              > +# If extensions (or modules to document with
              autodoc) are in another directory,
              <br>
              > +# add these directories to sys.path here. If the
              directory is relative to the
              <br>
              > +# documentation root, use os.path.abspath to make it
              absolute, like shown here.
              <br>
              > +#sys.path.insert(0, os.path.abspath('.'))
              <br>
              > +
              <br>
              > +# -- General configuration
              ------------------------------------------------
              <br>
              > +
              <br>
              > +# If your documentation needs a minimal Sphinx
              version, state it here.
              <br>
              > +#needs_sphinx = '1.0'
              <br>
              > +
              <br>
              > +# Add any Sphinx extension module names here, as
              strings. They can be
              <br>
              > +# extensions coming with Sphinx (named
              'sphinx.ext.*') or your custom
              <br>
              > +# ones.
              <br>
              > +extensions = []
              <br>
              > +
              <br>
              > +# Add any paths that contain templates here,
              relative to this directory.
              <br>
              > +templates_path = ['_templates']
              <br>
              > +
              <br>
              > +# The suffix of source filenames.
              <br>
              > +source_suffix = '.rst'
              <br>
              > +
              <br>
              > +# The encoding of source files.
              <br>
              > +#source_encoding = 'utf-8-sig'
              <br>
              > +
              <br>
              > +# The master toctree document.
              <br>
              > +master_doc = 'index'
              <br>
              > +
              <br>
              > +# General information about the project.
              <br>
              > +project = u'LLVM Lab Tools'
              <br>
              > +copyright = u'2015, Daniel Dunbar, Chris Matthews'
              <br>
              > +
              <br>
              > +# The version info for the project you're
              documenting, acts as replacement for
              <br>
              > +# |version| and |release|, also used in various
              other places throughout the
              <br>
              > +# built documents.
              <br>
              > +#
              <br>
              > +# The short X.Y version.
              <br>
              > +version = '1.0'
              <br>
              > +# The full version, including alpha/beta/rc tags.
              <br>
              > +release = '1.0'
              <br>
              > +
              <br>
              > +# The language for content autogenerated by Sphinx.
              Refer to documentation
              <br>
              > +# for a list of supported languages.
              <br>
              > +#language = None
              <br>
              > +
              <br>
              > +# There are two options for replacing |today|:
              either, you set today to some
              <br>
              > +# non-false value, then it is used:
              <br>
              > +#today = ''
              <br>
              > +# Else, today_fmt is used as the format for a
              strftime call.
              <br>
              > +#today_fmt = '%B %d, %Y'
              <br>
              > +
              <br>
              > +# List of patterns, relative to source directory,
              that match files and
              <br>
              > +# directories to ignore when looking for source
              files.
              <br>
              > +exclude_patterns = ['_build']
              <br>
              > +
              <br>
              > +# The reST default role (used for this markup:
              `text`) to use for all
              <br>
              > +# documents.
              <br>
              > +#default_role = None
              <br>
              > +
              <br>
              > +# If true, '()' will be appended to :func: etc.
              cross-reference text.
              <br>
              > +#add_function_parentheses = True
              <br>
              > +
              <br>
              > +# If true, the current module name will be prepended
              to all description
              <br>
              > +# unit titles (such as .. function::).
              <br>
              > +#add_module_names = True
              <br>
              > +
              <br>
              > +# If true, sectionauthor and moduleauthor directives
              will be shown in the
              <br>
              > +# output. They are ignored by default.
              <br>
              > +#show_authors = False
              <br>
              > +
              <br>
              > +# The name of the Pygments (syntax highlighting)
              style to use.
              <br>
              > +pygments_style = 'sphinx'
              <br>
              > +
              <br>
              > +# A list of ignored prefixes for module index
              sorting.
              <br>
              > +#modindex_common_prefix = []
              <br>
              > +
              <br>
              > +# If true, keep warnings as "system message"
              paragraphs in the built documents.
              <br>
              > +#keep_warnings = False
              <br>
              > +
              <br>
              > +
              <br>
              > +# -- Options for HTML output
              ----------------------------------------------
              <br>
              > +
              <br>
              > +# The theme to use for HTML and HTML Help pages. See
              the documentation for
              <br>
              > +# a list of builtin themes.
              <br>
              > +html_theme = 'default'
              <br>
              > +
              <br>
              > +# Theme options are theme-specific and customize the
              look and feel of a theme
              <br>
              > +# further. For a list of options available for each
              theme, see the
              <br>
              > +# documentation.
              <br>
              > +#html_theme_options = {}
              <br>
              > +
              <br>
              > +# Add any paths that contain custom themes here,
              relative to this directory.
              <br>
              > +#html_theme_path = []
              <br>
              > +
              <br>
              > +# The name for this set of Sphinx documents. If
              None, it defaults to
              <br>
              > +# "<project> v<release> documentation".
              <br>
              > +#html_title = None
              <br>
              > +
              <br>
              > +# A shorter title for the navigation bar. Default is
              the same as html_title.
              <br>
              > +#html_short_title = None
              <br>
              > +
              <br>
              > +# The name of an image file (relative to this
              directory) to place at the top
              <br>
              > +# of the sidebar.
              <br>
              > +#html_logo = None
              <br>
              > +
              <br>
              > +# The name of an image file (within the static path)
              to use as favicon of the
              <br>
              > +# docs. This file should be a Windows icon file
              (.ico) being 16x16 or 32x32
              <br>
              > +# pixels large.
              <br>
              > +#html_favicon = None
              <br>
              > +
              <br>
              > +# Add any paths that contain custom static files
              (such as style sheets) here,
              <br>
              > +# relative to this directory. They are copied after
              the builtin static files,
              <br>
              > +# so a file named "default.css" will overwrite the
              builtin "default.css".
              <br>
              > +html_static_path = ['_static']
              <br>
              > +
              <br>
              > +# Add any extra paths that contain custom files
              (such as robots.txt or
              <br>
              > +# .htaccess) here, relative to this directory. These
              files are copied
              <br>
              > +# directly to the root of the documentation.
              <br>
              > +#html_extra_path = []
              <br>
              > +
              <br>
              > +# If not '', a 'Last updated on:' timestamp is
              inserted at every page bottom,
              <br>
              > +# using the given strftime format.
              <br>
              > +#html_last_updated_fmt = '%b %d, %Y'
              <br>
              > +
              <br>
              > +# If true, SmartyPants will be used to convert
              quotes and dashes to
              <br>
              > +# typographically correct entities.
              <br>
              > +#html_use_smartypants = True
              <br>
              > +
              <br>
              > +# Custom sidebar templates, maps document names to
              template names.
              <br>
              > +#html_sidebars = {}
              <br>
              > +
              <br>
              > +# Additional templates that should be rendered to
              pages, maps page names to
              <br>
              > +# template names.
              <br>
              > +#html_additional_pages = {}
              <br>
              > +
              <br>
              > +# If false, no module index is generated.
              <br>
              > +#html_domain_indices = True
              <br>
              > +
              <br>
              > +# If false, no index is generated.
              <br>
              > +#html_use_index = True
              <br>
              > +
              <br>
              > +# If true, the index is split into individual pages
              for each letter.
              <br>
              > +#html_split_index = False
              <br>
              > +
              <br>
              > +# If true, links to the reST sources are added to
              the pages.
              <br>
              > +#html_show_sourcelink = True
              <br>
              > +
              <br>
              > +# If true, "Created using Sphinx" is shown in the
              HTML footer. Default is True.
              <br>
              > +#html_show_sphinx = True
              <br>
              > +
              <br>
              > +# If true, "(C) Copyright ..." is shown in the HTML
              footer. Default is True.
              <br>
              > +#html_show_copyright = True
              <br>
              > +
              <br>
              > +# If true, an OpenSearch description file will be
              output, and all pages will
              <br>
              > +# contain a <link> tag referring to it. The
              value of this option must be the
              <br>
              > +# base URL from which the finished HTML is served.
              <br>
              > +#html_use_opensearch = ''
              <br>
              > +
              <br>
              > +# This is the file name suffix for HTML files (e.g.
              ".xhtml").
              <br>
              > +#html_file_suffix = None
              <br>
              > +
              <br>
              > +# Output file base name for HTML help builder.
              <br>
              > +htmlhelp_basename = 'LLVMLabToolsdoc'
              <br>
              > +
              <br>
              > +
              <br>
              > +# -- Options for LaTeX output
              ---------------------------------------------
              <br>
              > +
              <br>
              > +latex_elements = {
              <br>
              > +# The paper size ('letterpaper' or 'a4paper').
              <br>
              > +#'papersize': 'letterpaper',
              <br>
              > +
              <br>
              > +# The font size ('10pt', '11pt' or '12pt').
              <br>
              > +#'pointsize': '10pt',
              <br>
              > +
              <br>
              > +# Additional stuff for the LaTeX preamble.
              <br>
              > +#'preamble': '',
              <br>
              > +}
              <br>
              > +
              <br>
              > +# Grouping the document tree into LaTeX files. List
              of tuples
              <br>
              > +# (source start file, target name, title,
              <br>
              > +# author, documentclass [howto, manual, or own
              class]).
              <br>
              > +latex_documents = [
              <br>
              > + ('index', 'LLVMLabTools.tex', u'LLVM Lab Tools
              Documentation',
              <br>
              > + u'Daniel Dunbar, Chris Matthews', 'manual'),
              <br>
              > +]
              <br>
              > +
              <br>
              > +# The name of an image file (relative to this
              directory) to place at the top of
              <br>
              > +# the title page.
              <br>
              > +#latex_logo = None
              <br>
              > +
              <br>
              > +# For "manual" documents, if this is true, then
              toplevel headings are parts,
              <br>
              > +# not chapters.
              <br>
              > +#latex_use_parts = False
              <br>
              > +
              <br>
              > +# If true, show page references after internal
              links.
              <br>
              > +#latex_show_pagerefs = False
              <br>
              > +
              <br>
              > +# If true, show URL addresses after external links.
              <br>
              > +#latex_show_urls = False
              <br>
              > +
              <br>
              > +# Documents to append as an appendix to all manuals.
              <br>
              > +#latex_appendices = []
              <br>
              > +
              <br>
              > +# If false, no module index is generated.
              <br>
              > +#latex_domain_indices = True
              <br>
              > +
              <br>
              > +
              <br>
              > +# -- Options for manual page output
              ---------------------------------------
              <br>
              > +
              <br>
              > +# One entry per manual page. List of tuples
              <br>
              > +# (source start file, name, description, authors,
              manual section).
              <br>
              > +man_pages = [
              <br>
              > + ('index', 'llvmlabtools', u'LLVM Lab Tools
              Documentation',
              <br>
              > + [u'Daniel Dunbar, Chris Matthews'], 1)
              <br>
              > +]
              <br>
              > +
              <br>
              > +# If true, show URL addresses after external links.
              <br>
              > +#man_show_urls = False
              <br>
              > +
              <br>
              > +
              <br>
              > +# -- Options for Texinfo output
              -------------------------------------------
              <br>
              > +
              <br>
              > +# Grouping the document tree into Texinfo files.
              List of tuples
              <br>
              > +# (source start file, target name, title, author,
              <br>
              > +# dir menu entry, description, category)
              <br>
              > +texinfo_documents = [
              <br>
              > + ('index', 'LLVMLabTools', u'LLVM Lab Tools
              Documentation',
              <br>
              > + u'Daniel Dunbar, Chris Matthews', 'LLVMLabTools',
              'One line description of project.',
              <br>
              > + 'Miscellaneous'),
              <br>
              > +]
              <br>
              > +
              <br>
              > +# Documents to append as an appendix to all manuals.
              <br>
              > +#texinfo_appendices = []
              <br>
              > +
              <br>
              > +# If false, no module index is generated.
              <br>
              > +#texinfo_domain_indices = True
              <br>
              > +
              <br>
              > +# How to display URL addresses: 'footnote', 'no', or
              'inline'.
              <br>
              > +#texinfo_show_urls = 'footnote'
              <br>
              > +
              <br>
              > +# If true, do not generate a @detailmenu in the
              "Top" node's menu.
              <br>
              > +#texinfo_no_detailmenu = False
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/docs/index.rst
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/index.rst?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/index.rst?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/docs/index.rst (added)
              <br>
              > +++ zorg/trunk/llvmbisect/docs/index.rst Thu Oct 8
              16:52:50 2015
              <br>
              > @@ -0,0 +1,23 @@
              <br>
              > +.. LLVM Lab Tools documentation master file, created
              by
              <br>
              > + sphinx-quickstart on Tue Oct 6 18:29:53 2015.
              <br>
              > + You can adapt this file completely to your liking,
              but it should at least
              <br>
              > + contain the root `toctree` directive.
              <br>
              > +
              <br>
              > +Welcome to LLVM Lab Tools's documentation!
              <br>
              > +==========================================
              <br>
              > +
              <br>
              > +Contents:
              <br>
              > +
              <br>
              > +.. toctree::
              <br>
              > + :maxdepth: 2
              <br>
              > +
              <br>
              > + llvmlab_bisect
              <br>
              > + builders
              <br>
              > +
              <br>
              > +
              <br>
              > +Indices and tables
              <br>
              > +==================
              <br>
              > +
              <br>
              > +* :ref:`genindex`
              <br>
              > +* :ref:`modindex`
              <br>
              > +* :ref:`search`
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/docs/llvmlab_bisect.rst
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/llvmlab_bisect.rst?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/llvmlab_bisect.rst?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/docs/llvmlab_bisect.rst
              (added)
              <br>
              > +++ zorg/trunk/llvmbisect/docs/llvmlab_bisect.rst Thu
              Oct 8 16:52:50 2015
              <br>
              > @@ -0,0 +1,569 @@
              <br>
              > +.. _llvmlab-bisect:
              <br>
              > +
              <br>
              > +Automatic compiler bisecting with llvmlab bisect
              <br>
              > +==================================================
              <br>
              > +
              <br>
              > +``llvmlab bisect`` is a tool for automatically
              identifying
              <br>
              > +when a regression was introduced in any build of
              Clang, or LLVM
              <br>
              > +produced by one of our Buildbots or Jenkins jobs.
              <br>
              > +
              <br>
              > +The basics of the tool are very simple, you must
              provided it with a "test case"
              <br>
              > +which will reproduce the problem you are seeing.
              Once you have done that, the
              <br>
              > +tool will automatically download compiler packages
              from the cloud
              <br>
              > +(typically produced by our continuous integration
              system) and will check whether
              <br>
              > +the problem reproduces with that compiler or not.
              The tool will then attempt to
              <br>
              > +narrow down on the first compiler which broke the
              test, and will report the last
              <br>
              > +compiler which worked and the first compiler that
              failed.
              <br>
              > +
              <br>
              > +Getting the tool
              <br>
              > +~~~~~~~~~~~~~~~~
              <br>
              > +
              <br>
              > +The tool is in our tools repo::
              <br>
              > + $ svn checkout
              <a class="moz-txt-link-freetext" href="https://llvm.org/svn/llvm-project/zorg">https://llvm.org/svn/llvm-project/zorg</a>
              <br>
              Did you mean <a class="moz-txt-link-freetext" href="https://llvm.org/svn/llvm-project/zorg/trunk">https://llvm.org/svn/llvm-project/zorg/trunk</a>
              zorg ? <br>
              Otherwise the next command doesn't work.
              <br>
              <br>
              Also I ended up working around the 'sudo' requirement by:
              <br>
              <br>
              svn co <a class="moz-txt-link-freetext" href="https://llvm.org/svn/llvm-project/zorg/trunk/">https://llvm.org/svn/llvm-project/zorg/trunk/</a> zorg
              <br>
              cd zorg/llvmbisect
              <br>
LOCAL_PYTHON_INSTALL_PATH=$(pwd)/local_python_packages/lib/python2.7/site-packages/
              <br>
              mkdir -p $LOCAL_PYTHON_INSTALL_PATH
              <br>
              export PYTHONPATH=$LOCAL_PYTHON_INSTALL_PATH:$PYTHONPATH
              <br>
              python setup.py install
              --prefix=$(pwd)/local_python_packages
              <br>
              export PATH=$(pwd)/bin:$PATH
              <br>
              <br>
              Do you think it is worth mentioning in the docs?
              <br>
              <br>
              --Vassil
              <br>
              > + $ cd zorg/llvmbisect
              <br>
              > + $ sudo python setup.py install
              <br>
              > + $ llvmlab ls
              <br>
              > +
              <br>
              > +
              <br>
              > +The Bisection Process
              <br>
              > +~~~~~~~~~~~~~~~~~~~~~
              <br>
              > +
              <br>
              > +There are several parts of the bisection process you
              should understand to use
              <br>
              > +``llvmlab bisect`` effectively:
              <br>
              > +
              <br>
              > + * How the tool gets compiler builds.
              <br>
              > +
              <br>
              > + * How tests (bisection predicates) are run.
              <br>
              > +
              <br>
              > + * How the bisect process sandboxes tests.
              <br>
              > +
              <br>
              > + * Test filters.
              <br>
              > +
              <br>
              > +Compiler Packages
              <br>
              > ++++++++++++++++++
              <br>
              > +
              <br>
              > +Bisection uses packages produced by the continuous
              integration
              <br>
              > +system. Currently, it will only consider packages
              which are produced by one
              <br>
              > +particular "builder". The default is to use the
              ``clang-stage1-configure-RA_build``
              <br>
              > +line of packages, because that is produced by our
              mini farm and has a very high
              <br>
              > +granularity and a long history.
              <br>
              > +
              <br>
              > +You can tell the tool to use a particular line of
              builds using the ``-b`` or
              <br>
              > +``--build`` command line option.
              <br>
              > +
              <br>
              > +You can see a list of the kinds of builds which are
              published using::
              <br>
              > +
              <br>
              > + $ ./llvmlab ls
              <br>
              > + clang-stage1-configure-RA_build
              <br>
              > + clang-stage2-foo
              <br>
              > +
              <br>
              > +Each of these corresponds to a particular
              buildbot/Jenkins builder
              <br>
              > +which is constantly building new revisions and
              uploading them to
              <br>
              > +the cloud.
              <br>
              > +
              <br>
              > +The important thing to understand is that the
              particular compiler package in use
              <br>
              > +may impact your test. For example,
              ``clang-stage1-configure-RA_build`` builds
              <br>
              > +are x86_64 compilers built on Yosemite in release
              asserts mode. Generally, you
              <br>
              > +should make sure your test explicitly sets anything
              which could impact the test
              <br>
              > +(like the architecture).
              <br>
              > +
              <br>
              > +The other way this impacts your tests is that some
              packages are layed out
              <br>
              > +differently than others. Most compiler packages are
              layed out in a "Unix" style,
              <br>
              > +with ``bin`` and ``lib`` subdirectories. One easy
              way to see the package layout
              <br>
              > +is to use ``llvmlab fetch`` to grab a build from the
              particular builder you
              <br>
              > +are using and poke at it. For example::
              <br>
              > +
              <br>
              > + $ llvmlab fetch clang-i386-darwin9-snt
              <br>
              > + downloaded root: clang-r128299-b6960.tgz
              <br>
              > + extracted path :
              <br>
              > + $ ls clang-r128299-b6960
              <br>
              > + bin docs lib share
              <br>
              > +
              <br>
              > +See ``llvmlab fetch --help`` for more information on
              the ``fetch`` tool.
              <br>
              > +
              <br>
              > +The main exception to remember is that Apple style
              builds generally will have
              <br>
              > +"root" style layouts, where the package is meant to
              be installed directly into
              <br>
              > +``/``, and will be layed out with ``usr/bin`` and
              ``Developer/usr/bin``
              <br>
              > +subdirectories.
              <br>
              > +
              <br>
              > +
              <br>
              > +The Build Cache
              <br>
              > ++++++++++++++++
              <br>
              > +
              <br>
              > +``llvmlab bisect`` can be configured to cache
              downloaded archives. This is
              <br>
              > +useful for users who frequently bisect things and
              want the command to run as
              <br>
              > +fast as possible. Note that the tool doesn't try and
              do anything smart about
              <br>
              > +minimizing the amount of disk space the cache uses,
              so use this at your own
              <br>
              > +risk.
              <br>
              > +
              <br>
              > +To enable the cache::
              <br>
              > + $ mkdir -p ~/.llvmlab
              <br>
              > + $ echo "[ci]" > ~/.llvmlab/config
              <br>
              > + $ echo "cache_builds = True" > ~/.llvmlab/config
              <br>
              > +
              <br>
              > +
              <br>
              > +Bisection Predicates
              <br>
              > +++++++++++++++++++++
              <br>
              > +
              <br>
              > +Like most bisection tools, ``llvmlab bisect`` needs
              to have a way to test
              <br>
              > +whether a particular build "passes" or "fails".
              ``llvmlab bisect`` uses a
              <br>
              > +format which allows writing most bisection commands
              on a single command line
              <br>
              > +without having to write extra shell scripts.
              <br>
              > +
              <br>
              > +``llvmlab ci exec`` is an invaluable tool for
              checking bisection
              <br>
              > +predicates. It accepts the exact same syntax as
              llvmlab bisect, but prints a
              <br>
              > +bit more information by default and only runs a
              single command. This is useful
              <br>
              > +for vetting bisection predicates before running a
              full bisection process.
              <br>
              > +
              <br>
              > +Predicates are written as commands which are
              expected to exit successfully
              <br>
              > +(i.e., return 0 as the exit code) when the test
              succeeds
              <br>
              > +[#predicate_tense]_. The command will be run once
              for each downloaded package to
              <br>
              > +determine if the test passes or fails on that
              particular build.
              <br>
              > +
              <br>
              > +``llvmlab bisect`` treats all non-optional command
              line arguments as the
              <br>
              > +command to be run. Each argument will be rewritten
              to possibly substitute
              <br>
              > +variables, and then the entire command line will be
              run (i.e., ``exec()``'d) to
              <br>
              > +determine whether the test passes or fails.
              <br>
              > +
              <br>
              > +.. _string:
              <a class="moz-txt-link-freetext" href="http://docs.python.org/library/stdtypes.html#string-formatting">http://docs.python.org/library/stdtypes.html#string-formatting</a>
              <br>
              > +
              <br>
              > +Bisection downloads each package into a separate
              directory inside a sandbox and
              <br>
              > +provides a mechanism for substituting the path to
              package into the command to be
              <br>
              > +run. Variables are substituted using the Python
              syntax with string_ formatting
              <br>
              > +named keys. For the most part, the syntax is like
              ``printf`` but variable names
              <br>
              > +are written in parentheses before the format
              specifier [#sh_parens]_.
              <br>
              > +
              <br>
              > +The most important variable is "path", which will be
              set to the path to the
              <br>
              > +downloaded package. For example::
              <br>
              > +
              <br>
              > + %(path)s/bin/clang
              <br>
              > +
              <br>
              > +would typically be expanded to something like::
              <br>
              > +
              <br>
              > + .../<sandbox>/clang-r128289-b6957/bin/clang
              <br>
              > +
              <br>
              > +before the command is run. You can use the ``-v``
              (``--verbose``) command line
              <br>
              > +option to have ``llvmlab bisect`` print the command
              lines it is running after
              <br>
              > +substitution.
              <br>
              > +
              <br>
              > +The tool provides a few other variables but "path"
              is the only one needed for
              <br>
              > +all but the rarest bisections. You can see the
              others in ``llvmlab bisect
              <br>
              > +--help``.
              <br>
              > +
              <br>
              > +The tool optimizes for the situation where
              downloaded packages include command
              <br>
              > +line executable which are going to be used in the
              tests, by automatically
              <br>
              > +extending the PATH and DYLD_LIBRARY_PATH variables
              to point into the downloaded
              <br>
              > +build directory whenever it sees that the downloaded
              package has ``bin`` or
              <br>
              > +``lib`` directories (the tool will also look for
              ``/Developer/usr/...``
              <br>
              > +directories). This environment extensions mean that
              it is usually possible to
              <br>
              > +write simple test commands without requiring any
              substitutions.
              <br>
              > +
              <br>
              > +For some bisection scenarios, it is easier to write
              a test script than to try
              <br>
              > +and come up with a single predicate command. For
              these scenarioes, ``llvmlab
              <br>
              > +bisect`` also makes all of the substitution
              variables available in the command's
              <br>
              > +environment. Each variable is injected into the
              environment as
              <br>
              > +``TEST_<variable>``. As an example, the
              following script could be used as a test
              <br>
              > +predicate which just checks that the compile
              succeeds::
              <br>
              > +
              <br>
              > + #!/bin/sh
              <br>
              > +
              <br>
              > + $TEST_PATH/bin/clang -c t.c
              <br>
              > +
              <br>
              > +Even though llvmlab bisect itself will only run one
              individual command per
              <br>
              > +build, you can write arbitrarily complicated test
              predicates by either (a)
              <br>
              > +writing external test scripts, or (b) writing shell
              "one-liners" and using
              <br>
              > +``/bin/sh -c`` to execute them. For example, the
              following bisect will test that
              <br>
              > +a particular source file both compiles and executes
              successfully::
              <br>
              > +
              <br>
              > + llvmlab bisect /bin/sh -c '%(path)s/bin/clang t.c
              && ./a.out'
              <br>
              > +
              <br>
              > +llvmlab bisect also supports a shortcut for this
              particular pattern. Separate
              <br>
              > +test commands can be separated on the command by a
              literal "----" command line
              <br>
              > +argument. Each command will be substituted as usual,
              but will they will be run
              <br>
              > +separately in order and if any command fails the
              entire test will fail.
              <br>
              > +
              <br>
              > +.. [#predicate_tense] Note that ``llvmlab bisect``
              always looks for the latest
              <br>
              > + build where a predicate *passes*. This means that
              it
              <br>
              > + generally expects the predicate to fail on any
              recent
              <br>
              > + build. If you are used to using tools like
              ``delta`` you
              <br>
              > + may be used to the predicate having the opposite
              tense --
              <br>
              > + however, for regression analysis usually one is
              <br>
              > + investigating a failure, and so one expects the
              test to
              <br>
              > + currently fail.
              <br>
              > +
              <br>
              > +.. [#sh_parens] Most shells will assign a syntax to
              (foo) so you generally have
              <br>
              > + to quote arguments which require substitution. One
              day I'll
              <br>
              > + think of a clever way I like to commands even
              easier to
              <br>
              > + write. Until then, quote away!
              <br>
              > +
              <br>
              > +
              <br>
              > +The Bisection Sandbox
              <br>
              > ++++++++++++++++++++++
              <br>
              > +
              <br>
              > +``llvmlab bisect`` tries to be very lightweight and
              not modify your working
              <br>
              > +directory or leave stray files around unless asked
              to. For that reason, it
              <br>
              > +downloads all of the packages and runs all of the
              tests inside a sandbox. By
              <br>
              > +default, the tool uses a sandbox inside ``/tmp`` and
              will destroy the sandbox
              <br>
              > +when it is done running tests.
              <br>
              > +
              <br>
              > +The tool also tries to be quiet and minimize command
              output, so the output of
              <br>
              > +each individual test run is also stored inside the
              sandbox. Unfortunately, this
              <br>
              > +means when the sandbox is destroyed you will no
              longer have access to the log
              <br>
              > +files if you think the predicate was not working
              correctly.
              <br>
              > +
              <br>
              > +For long running or complicated bisects, it is
              recommended to use the ``-s`` or
              <br>
              > +``--sandbox`` to tell the tool where to put the
              sandbox. If this option is used,
              <br>
              > +the sandbox will not be destroyed and you can
              investigate the log files for each
              <br>
              > +predicate run and the downloaded packages at your
              leisure.
              <br>
              > +
              <br>
              > +Predicates commands themselves are **NOT** run
              inside the sandbox, they are
              <br>
              > +always run in the current working directory. This is
              useful for referring to
              <br>
              > +test input files, but may be a problem if you wish
              to store the outputs of each
              <br>
              > +individual test run (for example, to analyze later).
              For that case, one method
              <br>
              > +is to store the test outputs inside the download
              package directories. The
              <br>
              > +following example will store each generated
              executable inside the build
              <br>
              > +directory for testing later::
              <br>
              > +
              <br>
              > + llvmlab bisect /bin/sh -c '%(path)s/bin/clang t.c
              -o %(path)s/foo && %(path)s/foo'
              <br>
              > +
              <br>
              > +
              <br>
              > +Environment Extensions
              <br>
              > +++++++++++++++++++++++
              <br>
              > +
              <br>
              > +``llvmlab bisect`` tries to optimize for the common
              case where build product
              <br>
              > +have executables or libraries to test, by
              automatically extending the ``PATH``
              <br>
              > +and ``DYLD_LIBRARY_PATH`` variables when it
              recognizes that the build package
              <br>
              > +has ``bin`` or ``lib`` subdirectories.
              <br>
              > +
              <br>
              > +For almost all common bisection tasks, this makes it
              possible to run the tool
              <br>
              > +without having to explicitly specify the
              substitution variables.
              <br>
              > +
              <br>
              > +For example::
              <br>
              > +
              <br>
              > + llvmlab bisect '%(path)s/bin/clang' -c t.c
              <br>
              > +
              <br>
              > +could just be written as::
              <br>
              > +
              <br>
              > + llvmlab bisect clang -c t.c
              <br>
              > +
              <br>
              > +because the ``clang`` binary in the downloaded
              package will be found first in
              <br>
              > +the environment lookup.
              <br>
              > +
              <br>
              > +
              <br>
              > +Test Filters
              <br>
              > +++++++++++++
              <br>
              > +
              <br>
              > +For more advanced uses, llvmlab bisect has a syntax
              for specifying "filters"
              <br>
              > +on individual commands. The syntax for filters is
              that they should be specified
              <br>
              > +at the start of the command using arguments like
              "%%<filter expression>%%".
              <br>
              > +
              <br>
              > +The filters are used as a way to specify additional
              parameters which only apply
              <br>
              > +to particular test commands. The expressions
              themselves are just Python
              <br>
              > +expressions which should evaluate to a boolean
              result, which becomes the result
              <br>
              > +of the test.
              <br>
              > +
              <br>
              > +The Python expressions are evaluate in an
              environment which contains the
              <br>
              > +following predefined variables:
              <br>
              > +
              <br>
              > +``result``
              <br>
              > +
              <br>
              > + The current boolean result of the test predicate
              (that is, true if the test is
              <br>
              > + "passing"). This may have been modified by
              preceeding filters.
              <br>
              > +
              <br>
              > +``user_time``, ``sys_time``, ``wall_time``
              <br>
              > +
              <br>
              > + The user, system, and wall time the command took to
              execute, respectively.
              <br>
              > +
              <br>
              > +These variables can be used to easily construct
              predicates which fail based on
              <br>
              > +more complex criterion. For example, here is a
              filter to look for the latest
              <br>
              > +build where the compiler succeeds in less than .5
              seconds::
              <br>
              > +
              <br>
              > + llvmlab bisect "%% result and user_time < .5 %%"
              clang -c t.c
              <br>
              > +
              <br>
              > +
              <br>
              > +Using ``llvmlab bisect``
              <br>
              > +~~~~~~~~~~~~~~~~~~~~~~~~~~
              <br>
              > +
              <br>
              > +``llvmlab bisect`` is very flexible but takes some
              getting used to. The
              <br>
              > +following section has example bisection commands for
              many common scenarios.
              <br>
              > +
              <br>
              > +Compiler Crashes
              <br>
              > +++++++++++++++++
              <br>
              > +
              <br>
              > +This is the simplest case, a bisection for a
              compiler crash or assertion failure
              <br>
              > +usually looks like::
              <br>
              > +
              <br>
              > + $ llvmlab bisect '%(path)s'/bin/clang -c t.c ...
              compiler flags ...
              <br>
              > +
              <br>
              > +because when the compiler crashes it will have a
              non-zero exit code. *For
              <br>
              > +bisecting assertion failures, you should make sure
              the build being tested has
              <br>
              > +assertions compiled in!*
              <br>
              > +
              <br>
              > +Suppose you are investigating a crash which has been
              fixed, and you want to know
              <br>
              > +where. Just use the LLVM ``not`` tool to reverse the
              test:
              <br>
              > +
              <br>
              > + $ llvmlab bisect not '%(path)s'/bin/clang -c t.c
              ... compiler flags ...
              <br>
              > +
              <br>
              > +By looking for the latest build where ``not clang
              ...`` *passes* we are
              <br>
              > +effectively looking for the latest broken build. The
              next build will generally
              <br>
              > +be the one which fixed the problem.
              <br>
              > +
              <br>
              > +
              <br>
              > +Miscompiles
              <br>
              > ++++++++++++
              <br>
              > +
              <br>
              > +Miscompiles usually involve compiling and running
              the output.
              <br>
              > +
              <br>
              > +The simplest scenario is when the program crashes
              when run. In that case the
              <br>
              > +simplest method is to use the ``/bin/sh -c "...
              arguments ..."`` trick to
              <br>
              > +combine the compile and execute steps into one
              command line::
              <br>
              > +
              <br>
              > + $ llvmlab bisect /bin/sh -c '%(path)s/bin/clang t.c
              && ./a.out'
              <br>
              > +
              <br>
              > +Note that because we are already quoting the shell
              command, we can just move the
              <br>
              > +quotes around the entire line and not worry about
              quoting individual arguments
              <br>
              > +(unless they have spaces!).
              <br>
              > +
              <br>
              > +A more complex scenario is when the program runs but
              has bad output. Usually
              <br>
              > +this just means you need to grep the output for
              correct output. For example, to
              <br>
              > +bisect a program which is supposed to print "OK"
              (but isn't currently) we could
              <br>
              > +use::
              <br>
              > +
              <br>
              > + $ llvmlab bisect /bin/sh -c '%(path)s/bin/clang t.c
              && ./a.out | grep "OK"'
              <br>
              > +
              <br>
              > +Beware the pitfalls of exit codes and pipes, and use
              temporary files if you
              <br>
              > +aren't sure of what you are doing!
              <br>
              > +
              <br>
              > +
              <br>
              > +Overlapped Failures
              <br>
              > ++++++++++++++++++++
              <br>
              > +
              <br>
              > +If you are used to using a test case reduction tool
              like ``delta`` or
              <br>
              > +``bugpoint``, you are probably familiar with the
              problem of running the tool for
              <br>
              > +hours, only to find that it found a very nice test
              case for a different problem
              <br>
              > +than what you were looking for.
              <br>
              > +
              <br>
              > +The same problem happens when bisecting a program
              which was previously broken
              <br>
              > +for a different reason. If you run the tool but the
              results don't seem to make
              <br>
              > +sense, I recommend saving the sandbox (e.g.,
              ``llvmlab bisect -s /tmp/foo
              <br>
              > +...``) and investigating the log files to make sure
              bisection looked for the
              <br>
              > +problem you are interested in. If it didn't, usually
              you should make your
              <br>
              > +predicate more precise, for example by using
              ``grep`` to search the output for a
              <br>
              > +more precise failure message (like an assertion
              failure string).
              <br>
              > +
              <br>
              > +
              <br>
              > +Infinite Loops
              <br>
              > +++++++++++++++
              <br>
              > +
              <br>
              > +On occasion, you will want to bisect something that
              infinite loops or takes
              <br>
              > +much longer than usual. This is a problem because
              you usually don't want to wait
              <br>
              > +for a long time (or infinity) for the predicate to
              complete.
              <br>
              > +
              <br>
              > +One simple trick which can work is to use the
              ``ulimit`` command to set a time
              <br>
              > +limit. The following command will look for the
              latest build where the compiler
              <br>
              > +runs in less than 10 seconds on the given input::
              <br>
              > +
              <br>
              > + $ llvmlab bisect /bin/sh -c 'ulimit -t 10;
              %(path)s/bin/clang -c t.c'
              <br>
              > +
              <br>
              > +
              <br>
              > +Performance Regressions
              <br>
              > ++++++++++++++++++++++++
              <br>
              > +
              <br>
              > +Bisecting performance regressions is done most
              easily using the filter
              <br>
              > +expressions. Usually you would start by determining
              what an approximate upper
              <br>
              > +bound on the expected time of the command is. Then,
              use a ``max_time`` filter
              <br>
              > +with that time to cause any test running longer than
              that to fail.
              <br>
              > +
              <br>
              > +For example, the following example shows a real
              bisection of a performance
              <br>
              > +regression on the ``telecom-gsm`` benchmark::
              <br>
              > +
              <br>
              > + llvmlab bisect \
              <br>
              > + '%(path)s/bin/clang' -o telecomm-gsm.exe -w -arch
              x86_64 -O3 \
              <br>
              > +
              ~/llvm-test-suite/MultiSource/Benchmarks/MiBench/telecomm-gsm/*.c
              \
              <br>
              > + -lm -DSTUPID_COMPILER -DNeedFunctionPrototypes=1
              -DSASR \
              <br>
              > + ---- \
              <br>
              > + "%% user_time < 0.25 %%" ./telecomm-gsm.exe -fps
              -c \
              <br>
              > +
              ~/llvm-test-suite/MultiSource/Benchmarks/MiBench/telecomm-gsm/large.au
              <br>
              > +
              <br>
              > +
              <br>
              > +Nightly Test Failures
              <br>
              > ++++++++++++++++++++++
              <br>
              > +
              <br>
              > +If you are bisecting a nightly test failure, it
              commonly helps to leverage the
              <br>
              > +existing nightly test Makefiles rather than try to
              write your own step to build
              <br>
              > +or test an executable against the expected output.
              In particular, the Makefiles
              <br>
              > +generate report files which say whether the test
              passed or failed.
              <br>
              > +
              <br>
              > +For example, if you are using LNT to run your
              nightly tests, then the top line
              <br>
              > +the ``test.log`` file shows the exact command used
              to run the tests. You can
              <br>
              > +always rerun this command in any subdirectory. For
              example, here is an example
              <br>
              > +from an i386 Clang run::
              <br>
              > +
              <br>
              > + 2010-10-12 08:54:39: running: "make" "-k" "-j" "1"
              "report" "report.simple.csv" \
              <br>
              > +
"TARGET_LLVMGCC=/Users/ddunbar/llvm.ref/2010-10-12_00-01.install/bin/clang"
              \
              <br>
              > + "CC_UNDER_TEST_TARGET_IS_I386=1"
              "ENABLE_HASHED_PROGRAM_OUTPUT=1" "TARGET_CXX=None" \
              <br>
              > + "LLI_OPTFLAGS=-O0" "TARGET_CC=None" \
              <br>
              > +
"TARGET_LLVMGXX=/Users/ddunbar/llvm.ref/2010-10-12_00-01.install/bin/clang++"
              \
              <br>
              > + "TEST=simple" "CC_UNDER_TEST_IS_CLANG=1"
              "TARGET_LLCFLAGS=" "TARGET_FLAGS=-g -arch i386" \
              <br>
              > + "USE_REFERENCE_OUTPUT=1" "OPTFLAGS=-O0"
              "SMALL_PROBLEM_SIZE=1" "LLC_OPTFLAGS=-O0" \
              <br>
              > + "ENABLE_OPTIMIZED=1" "ARCH=x86" "DISABLE_CBE=1"
              "DISABLE_JIT=1"
              <br>
              > +
              <br>
              > +Suppose we wanted to bisect a test failure on
              something complicated, like
              <br>
              > +``254.gap``. The "easiest" thing to do is:
              <br>
              > +
              <br>
              > + #. Replace the compiler paths with "%(path)s" so
              that we use the right compiler to test.
              <br>
              > +
              <br>
              > + #. Change into the test directory, in this case
              ``External/SPEC/CINT2000/254.gap``.
              <br>
              > +
              <br>
              > + #. Each test produces a ``<test
              name>.simple.execute.report.txt`` text file which will
              have a line that looks like::
              <br>
              > +
              <br>
              > + TEST-FAIL: exec
/Users/ddunbar/nt/clang.i386.O0.g/test-2011-03-25_06-35-35/External/SPEC/CINT2000/254.gap/254.gap<br>
              > +
              <br>
              > + because the tests are make driven, we can tell make
              to only build this
              <br>
              > + file. In SingleSource directories, this would make
              sure we don't run any
              <br>
              > + tests we don't need to.
              <br>
              > +
              <br>
              > + In this case, replace the "report" and
              "report.simple.csv" make targest on
              <br>
              > + the command line with
              "Output/254.gap.simple.exec.txt".
              <br>
              > +
              <br>
              > + #. Make sure your test predicate removes the Output
              directory and any ``report...`` files (if
              <br>
              > + you forget this, you won't end up rebuilding the
              test with the right compiler).
              <br>
              > +
              <br>
              > + #. Add a grep for "TEST-PASS" of the report file.
              <br>
              > +
              <br>
              > +An example of what the final bisect command might
              look like::
              <br>
              > +
              <br>
              > + $ llvmlab bisect /bin/sh -c \
              <br>
              > + 'rm -rf report.* Output && \
              <br>
              > + "make" "-k" "-j" "1"
              "Output/254.gap.simple.exec.txt" \
              <br>
              > + "TARGET_LLVMGCC=%(path)s/bin/clang" \
              <br>
              > + "CC_UNDER_TEST_TARGET_IS_I386=1"
              "ENABLE_HASHED_PROGRAM_OUTPUT=1" "TARGET_CXX=None" \
              <br>
              > + "LLI_OPTFLAGS=-O0" "TARGET_CC=None" \
              <br>
              > + "TARGET_LLVMGXX=%(path)s/bin/clang++" \
              <br>
              > + "TEST=simple" "CC_UNDER_TEST_IS_CLANG=1"
              "TARGET_LLCFLAGS=" "TARGET_FLAGS=-g -arch i386" \
              <br>
              > + "USE_REFERENCE_OUTPUT=1" "OPTFLAGS=-O0"
              "SMALL_PROBLEM_SIZE=1" "LLC_OPTFLAGS=-O0" \
              <br>
              > + "ENABLE_OPTIMIZED=1" "ARCH=x86" "DISABLE_CBE=1"
              "DISABLE_JIT=1" && \
              <br>
              > + grep "TEST-PASS" "Output/254.gap.simple.exec.txt"'
              <br>
              > +
              <br>
              > +
              <br>
              > +Nightly Test Performance Regressions
              <br>
              > +++++++++++++++++++++++++++++++++++++
              <br>
              > +
              <br>
              > +This is similar to the problem of bisecting nightly
              test above, but made more
              <br>
              > +complicated because the test predicate needs to do a
              comparison on the
              <br>
              > +performance result.
              <br>
              > +
              <br>
              > +One way to do this is to extract a script which
              reproduces the performance
              <br>
              > +regression, and use a filter expression as described
              previously. However, this
              <br>
              > +requires extracting the exact commands which are run
              by the ``test-suite``
              <br>
              > +Makefiles.
              <br>
              > +
              <br>
              > +A simpler way is to use the
              ``test-suite/tools/get-report-time`` script in
              <br>
              > +conjunction with a standard Unix command line tool
              like ``expr`` to do the
              <br>
              > +performance comparison.
              <br>
              > +
              <br>
              > +The basic process is similar to the one above, the
              differences are that instead
              <br>
              > +of just using ``grep`` to check the output, we use
              the ``get-report-time`` tool
              <br>
              > +and a quick script using ``bc`` to compare the
              result. Here is an example::
              <br>
              > +
              <br>
              > + $ llvmlab bisect -s sandbox /bin/sh -c \
              <br>
              > + 'set -ex; \
              <br>
              > + rm -rf Output && \
              <br>
              > + "make" "-k" "-j" "1"
              "Output/security-rijndael.simple.compile.report.txt" \
              <br>
              > + "TARGET_LLVMGCC=%(path)s/bin/clang"
              "ENABLE_HASHED_PROGRAM_OUTPUT=1" "TARGET_CXX=None" \
              <br>
              > + "LLI_OPTFLAGS=-O0" "TARGET_CC=None" \
              <br>
              > + "TARGET_LLVMGXX=%(path)s/bin/clang++" \
              <br>
              > + "TEST=simple" "CC_UNDER_TEST_IS_CLANG=1"
              "ENABLE_PARALLEL_REPORT=1" "TARGET_FLAGS=-g" \
              <br>
              > + "USE_REFERENCE_OUTPUT=1"
              "CC_UNDER_TEST_TARGET_IS_X86_64=1" "OPTFLAGS=-O0" \
              <br>
              > + "LLC_OPTFLAGS=-O0" "ENABLE_OPTIMIZED=1"
              "ARCH=x86_64" "DISABLE_CBE=1" "DISABLE_JIT=1" && \
              <br>
              > + ./check-value.sh'
              <br>
              > +
              <br>
              > +Where ``check-value.sh`` looks like this::
              <br>
              > +
              <br>
              > + #!/bin/sh -x
              <br>
              > +
              <br>
              > +
cmd1=`/Volumes/Data/sources/llvm/projects/test-suite/tools/get-report-time
              \
              <br>
              > + Output/security-rijndael.simple.compile.report.txt`
              <br>
              > + cmd2=`echo "$cmd1 < 0.42" | bc -l`
              <br>
              > +
              <br>
              > + if [ $cmd2 == '1' ]; then
              <br>
              > + exit 0
              <br>
              > + fi
              <br>
              > +
              <br>
              > + exit 1
              <br>
              > +
              <br>
              > +Another trick this particular example uses is using
              the bash ``set -x`` command
              <br>
              > +to log the commands which get run. In this case,
              this allows us to inspect the
              <br>
              > +log files in the ``sandbox`` directory and see what
              the time used in the
              <br>
              > +``expr`` comparison was. This is handy in case we
              aren't exactly sure if the
              <br>
              > +comparison time we used is correct.
              <br>
              > +
              <br>
              > +
              <br>
              > +Tests With Interactive Steps
              <br>
              > +++++++++++++++++++++++++++++
              <br>
              > +
              <br>
              > +Sometimes test predicates require some steps that
              must be performed
              <br>
              > +interactively or are too hard to automate in a test
              script.
              <br>
              > +
              <br>
              > +In such cases its still possible to use llvmlab
              bisect by writing the test
              <br>
              > +script in such a way that it will wait for the user
              to inform it whether the
              <br>
              > +test passed or failed. For example, here is a real
              test script that was used to
              <br>
              > +bisect where I was running a GUI app to check for
              distorted colors as part of
              <br>
              > +the test step.
              <br>
              > +
              <br>
              > +After each step, the GUI app would be launched, I
              would check the colors, and
              <br>
              > +then type in "yes" or "no" based on whether the app
              worked or not. Note that
              <br>
              > +because llvmlab bisect hides the test output by
              default, the prompt itself
              <br>
              > +doesn't show up, but the command still can read
              stdin.
              <br>
              > +
              <br>
              > +Here is the test script::
              <br>
              > +
              <br>
              > + #!/bin/sh
              <br>
              > +
              <br>
              > + git reset --hard
              <br>
              > +
              <br>
              > + CC=clang
              <br>
              > + COMPILE HERE
              <br>
              > + sudo ditto built_files/ /
              <br>
              > +
              <br>
              > + open /Applications/GUIApp
              <br>
              > +
              <br>
              > + while true; do
              <br>
              > + read -p "OK?" is_ok
              <br>
              > + if [ "$is_ok" == "yes" ]; then
              <br>
              > + echo "OK!"
              <br>
              > + exit 0
              <br>
              > + elif [ "$is_ok" == "no" ]; then
              <br>
              > + echo "FAILED!"
              <br>
              > + exit 1
              <br>
              > + else
              <br>
              > + echo "Answer yes or no you!";
              <br>
              > + fi
              <br>
              > + done
              <br>
              > +
              <br>
              > +And here is log showing the transcript of the
              bisect::
              <br>
              > +
              <br>
              > + bash-3.2# ~admin/zorg/utils/llvmlab bisect
              --max-rev 131837 ./test.sh
              <br>
              > + no
              <br>
              > + FAIL: clang-r131837-b8165
              <br>
              > + no
              <br>
              > + FAIL: clang-r131835-b8164
              <br>
              > + no
              <br>
              > + FAIL: clang-r131832-b8162
              <br>
              > + no
              <br>
              > + FAIL: clang-r131828-b8158
              <br>
              > + yes
              <br>
              > + PASS: clang-r131795-b8146
              <br>
              > + no
              <br>
              > + FAIL: clang-r131809-b8151
              <br>
              > + no
              <br>
              > + FAIL: clang-r131806-b8149
              <br>
              > + no
              <br>
              > + FAIL: clang-r131801-b8147
              <br>
              > + clang-r131795-b8146: first working build
              <br>
              > + clang-r131801-b8147: next failing build
              <br>
              > +
              <br>
              > +Note that it is very easy to make a mistake and type
              the wrong answer when
              <br>
              > +following this process, in which case the bisect
              will come up with the wrong
              <br>
              > +answer. It's always worth sanity checking the
              results (e.g., using ``llvmlab
              <br>
              > +ci exec``) after the bisect is complete.
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/llvmlab/__init__.py
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/__init__.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/__init__.py?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/llvmlab/__init__.py (added)
              <br>
              > +++ zorg/trunk/llvmbisect/llvmlab/__init__.py Thu Oct
              8 16:52:50 2015
              <br>
              > @@ -0,0 +1 @@
              <br>
              > +""""""
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/llvmlab/algorithm.py
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/algorithm.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/algorithm.py?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/llvmlab/algorithm.py
              (added)
              <br>
              > +++ zorg/trunk/llvmbisect/llvmlab/algorithm.py Thu
              Oct 8 16:52:50 2015
              <br>
              > @@ -0,0 +1,85 @@
              <br>
              > +"""Handy algorithms."""
              <br>
              > +
              <br>
              > +
              <br>
              > +def bisect(predicate, list):
              <br>
              > + """
              <br>
              > + bisect(predicate, list) -> item or None
              <br>
              > +
              <br>
              > + Given a test predicate and a list of items, search
              return the first item in
              <br>
              > + the list for which the predicate succeeds, or None
              if no such item is
              <br>
              > + found.
              <br>
              > +
              <br>
              > + The list is assumed to be ordered such that
              (predicate(i) for i in list) is
              <br>
              > + monotonic. If this condition is not met, the
              returned item is guaranteed to
              <br>
              > + satisfy the predicate and the item preceeding it is
              guaranteed to fail the
              <br>
              > + predicate, but that is all. Additionally, if the
              last item does not pass
              <br>
              > + the predicate, such an item might not be found.
              <br>
              > +
              <br>
              > + This function is optimized for the case where the
              searched for item is near
              <br>
              > + the beginning of the list.
              <br>
              > + """
              <br>
              > +
              <br>
              > + if not list:
              <br>
              > + return None
              <br>
              > +
              <br>
              > + lo = 0
              <br>
              > + hi = len(list)-1
              <br>
              > +
              <br>
              > + # Check first item immediately.
              <br>
              > + if predicate(list[lo]):
              <br>
              > + return list[lo]
              <br>
              > +
              <br>
              > + # Invariants:
              <br>
              > + # not predicate(list[lo])
              <br>
              > + # predicate(list[hi])
              <br>
              > +
              <br>
              > + # Binary search region.
              <br>
              > + while lo + 1 != hi:
              <br>
              > + mid = (lo + hi) // 2
              <br>
              > + if predicate(list[mid]):
              <br>
              > + hi = mid
              <br>
              > + else:
              <br>
              > + lo = mid
              <br>
              > +
              <br>
              > + return list[hi]
              <br>
              > +
              <br>
              > +
              <br>
              > +def gallop(predicate, list):
              <br>
              > + """
              <br>
              > + gallop(predicate, list) -> list or None
              <br>
              > +
              <br>
              > + Given a test predicate and a list of items, reduce
              the search space
              <br>
              > + assuming the searched for item is near the
              beginning of the list.
              <br>
              > +
              <br>
              > + The list is assumed to be ordered such that
              (predicate(i) for i in list) is
              <br>
              > + monotonic. If this condition is not met, the
              returned item is guaranteed to
              <br>
              > + satisfy the predicate and the item preceeding it is
              guaranteed to fail the
              <br>
              > + predicate, but that is all. Additionally, if the
              last item does not pass
              <br>
              > + the predicate, such an item might not be found.
              <br>
              > + """
              <br>
              > +
              <br>
              > + if not list:
              <br>
              > + return None
              <br>
              > +
              <br>
              > + # Check first item immediately.
              <br>
              > + if predicate(list[0]):
              <br>
              > + return list[0:1]
              <br>
              > +
              <br>
              > + # Invariants:
              <br>
              > + # not predicate(list[lo])
              <br>
              > +
              <br>
              > + # Gallop to find initial search range, under the
              assumption that we are
              <br>
              > + # most likely looking for something at the head of
              this list.
              <br>
              > + lo = 0
              <br>
              > + hi = 1
              <br>
              > + while hi < len(list):
              <br>
              > + if predicate(list[hi]):
              <br>
              > + break
              <br>
              > + lo, hi = hi, hi + (hi - lo)*2
              <br>
              > +
              <br>
              > + # If we galloped past the end, limit the hi range.
              <br>
              > + if hi >= len(list):
              <br>
              > + hi = len(list) - 1
              <br>
              > + if hi == lo or not predicate(list[hi]):
              <br>
              > + return None
              <br>
              > + return list[lo:hi+1]
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/llvmlab/ci.py
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/ci.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/ci.py?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/llvmlab/ci.py (added)
              <br>
              > +++ zorg/trunk/llvmbisect/llvmlab/ci.py Thu Oct 8
              16:52:50 2015
              <br>
              > @@ -0,0 +1,650 @@
              <br>
              > +"""
              <br>
              > +Tools for working with llvmlab CI infrastructure.
              <br>
              > +"""
              <br>
              > +
              <br>
              > +import errno
              <br>
              > +import os
              <br>
              > +import resource
              <br>
              > +import shutil
              <br>
              > +import subprocess
              <br>
              > +import sys
              <br>
              > +import tempfile
              <br>
              > +import time
              <br>
              > +
              <br>
              > +
              <br>
              > +from . import shell
              <br>
              > +from . import algorithm
              <br>
              > +from . import llvmlab
              <br>
              > +from . import util
              <br>
              > +from util import warning, fatal, note
              <br>
              > +from . import scripts
              <br>
              > +from . import util
              <br>
              > +
              <br>
              > +from optparse import OptionParser
              <br>
              > +
              <br>
              > +
              <br>
              > +class Command(object):
              <br>
              > + class Filter(object):
              <br>
              > + def __init__(self):
              <br>
              > + pass
              <br>
              > +
              <br>
              > + def evaluate(self, command):
              <br>
              > + raise RuntimeError("Abstract method.")
              <br>
              > +
              <br>
              > + class NotFilter(Filter):
              <br>
              > + def evaluate(self, command):
              <br>
              > + warning("'negate' filter is deprecated, use 'not
              result' "
              <br>
              > + "filter expression")
              <br>
              > + command.result = not command.result
              <br>
              > +
              <br>
              > + class MaxTimeFilter(Filter):
              <br>
              > + def __init__(self, value):
              <br>
              > + try:
              <br>
              > + self.value = float(value)
              <br>
              > + except:
              <br>
              > + fatal("invalid argument: %r" % time)
              <br>
              > + warning("'max_time' filter is deprecated, use "
              <br>
              > + "'user_time < %.4f' filter expression" %
              self.value)
              <br>
              > +
              <br>
              > + def evaluate(self, command):
              <br>
              > + if command.metrics["user_time"] >= self.value:
              <br>
              > + command.result = False
              <br>
              > +
              <br>
              > + available_filters = {"negate": NotFilter(), # note
              this is an instance.
              <br>
              > + "max_time": MaxTimeFilter}
              <br>
              > +
              <br>
              > + def __init__(self, command, stdout_path,
              stderr_path, env):
              <br>
              > + self.command = command
              <br>
              > + self.stdout_path = stdout_path
              <br>
              > + self.stderr_path = stderr_path
              <br>
              > + self.env = env
              <br>
              > +
              <br>
              > + # Test data.
              <br>
              > + self.metrics = {}
              <br>
              > + self.result = None
              <br>
              > +
              <br>
              > + def execute(self, verbose=False):
              <br>
              > + if verbose:
              <br>
              > + note('executing: %s' % ' '.join("'%s'" % arg
              <br>
              > + for arg in self.command))
              <br>
              > +
              <br>
              > + start_rusage =
              resource.getrusage(resource.RUSAGE_CHILDREN)
              <br>
              > + start_time = time.time()
              <br>
              > +
              <br>
              > + p = subprocess.Popen(self.command,
              <br>
              > + stdout=open(self.stdout_path, 'w'),
              <br>
              > + stderr=open(self.stderr_path, 'w'),
              <br>
              > + env=self.env)
              <br>
              > + self.result = p.wait() == 0
              <br>
              > +
              <br>
              > + end_time = time.time()
              <br>
              > + end_rusage =
              resource.getrusage(resource.RUSAGE_CHILDREN)
              <br>
              > + self.metrics["user_time"] = end_rusage.ru_utime -
              start_rusage.ru_utime
              <br>
              > + self.metrics["sys_time"] = end_rusage.ru_stime -
              start_rusage.ru_stime
              <br>
              > + self.metrics["wall_time"] = end_time - start_time
              <br>
              > +
              <br>
              > + if verbose:
              <br>
              > + note("command executed in -- "
              <br>
              > + "user: %.4fs, wall: %.4fs, sys: %.4fs" % (
              <br>
              > + self.metrics["user_time"],
              self.metrics["wall_time"],
              <br>
              > + self.metrics["sys_time"]))
              <br>
              > +
              <br>
              > + def evaluate_filter_spec(self, spec):
              <br>
              > + # Run the filter in an environment with the builtin
              filters and the
              <br>
              > + # metrics.
              <br>
              > + env = {"result": self.result}
              <br>
              > + env.update(self.available_filters)
              <br>
              > + env.update(self.metrics)
              <br>
              > + result = eval(spec, {}, env)
              <br>
              > +
              <br>
              > + # If the result is a filter object, evaluate it.
              <br>
              > + if isinstance(result, Command.Filter):
              <br>
              > + result.evaluate(self)
              <br>
              > + return
              <br>
              > +
              <br>
              > + # Otherwise, treat the result as a boolean
              predicate.
              <br>
              > + self.result = bool(result)
              <br>
              > +
              <br>
              > +
              <br>
              > +def execute_sandboxed_test(sandbox, builder, build,
              args,
              <br>
              > + verbose=False, very_verbose=False,
              <br>
              > + add_path_variables=True,
              <br>
              > + show_command_output=False,
              <br>
              > + reuse_sandbox=False):
              <br>
              > +
              <br>
              > + def split_command_filters(command):
              <br>
              > + for i, arg in enumerate(command):
              <br>
              > + if arg[:2] != "%%" or arg[-2:] != "%%":
              <br>
              > + break
              <br>
              > + else:
              <br>
              > + fatal("invalid command: %s, only contains filter "
              <br>
              > + "specifications" % ("".join('"%s"' % a for a in
              command)))
              <br>
              > +
              <br>
              > + return ([a[2:-2] for a in command[:i]],
              <br>
              > + command[i:])
              <br>
              > +
              <br>
              > + path = build.tobasename(include_suffix=False)
              <br>
              > + fullpath = build.tobasename()
              <br>
              > +
              <br>
              > + if verbose:
              <br>
              > + note('testing %r' % path)
              <br>
              > +
              <br>
              > + # Create the sandbox directory, if it doesn't
              exist.
              <br>
              > + is_temp = False
              <br>
              > + if sandbox is None:
              <br>
              > + sandbox = tempfile.mkdtemp()
              <br>
              > + is_temp = True
              <br>
              > + else:
              <br>
              > + # Make absolute.
              <br>
              > + sandbox = os.path.abspath(sandbox)
              <br>
              > + if not os.path.exists(sandbox):
              <br>
              > + os.mkdir(sandbox)
              <br>
              > +
              <br>
              > + # Compute paths and make sure sandbox is clean.
              <br>
              > + root_path = os.path.join(sandbox, fullpath)
              <br>
              > + builddir_path = os.path.join(sandbox, path)
              <br>
              > + need_build = True
              <br>
              > + if reuse_sandbox and (os.path.exists(root_path) and
              <br>
              > + os.path.exists(builddir_path)):
              <br>
              > + need_build = False
              <br>
              > + else:
              <br>
              > + for p in (root_path, builddir_path):
              <br>
              > + if os.path.exists(p):
              <br>
              > + fatal('sandbox is not clean, %r exists' % p)
              <br>
              > +
              <br>
              > + # Fetch and extract the build.
              <br>
              > + if need_build:
              <br>
              > + start_time = time.time()
              <br>
              > + llvmlab.fetch_build_to_path(builder, build,
              root_path, builddir_path)
              <br>
              > + if very_verbose:
              <br>
              > + note("extracted build in %.2fs" % (time.time() -
              start_time,))
              <br>
              > +
              <br>
              > + # Attempt to find clang/clang++ in the downloaded
              build.
              <br>
              > + def find_binary(name):
              <br>
              > + x = subprocess.check_output(['find', builddir_path,
              '-name', name])\
              <br>
              > + .strip().split("\n")[0]
              <br>
              > + if x == '':
              <br>
              > + x = None
              <br>
              > + return x
              <br>
              > +
              <br>
              > + clang_path = find_binary('clang')
              <br>
              > + clangpp_path = find_binary('clang++')
              <br>
              > + liblto_path = find_binary('libLTO.dylib')
              <br>
              > + if liblto_path is not None:
              <br>
              > + liblto_dir = os.path.dirname(liblto_path)
              <br>
              > + else:
              <br>
              > + liblto_dir = None
              <br>
              > +
              <br>
              > + # Construct the interpolation variables.
              <br>
              > + options = {'sandbox': sandbox,
              <br>
              > + 'path': builddir_path,
              <br>
              > + 'revision': build.revision,
              <br>
              > + 'build': build.build,
              <br>
              > + 'clang': clang_path,
              <br>
              > + 'clang++': clangpp_path,
              <br>
              > + 'libltodir': liblto_dir}
              <br>
              > +
              <br>
              > + # Inject environment variables.
              <br>
              > + env = os.environ.copy()
              <br>
              > + for key, value in options.items():
              <br>
              > + env['TEST_%s' % key.upper()] = str(value)
              <br>
              > +
              <br>
              > + # Extend the environment to include the path to the
              extracted build.
              <br>
              > + #
              <br>
              > + # FIXME: Ideally, we would be able to read some
              kind of configuration
              <br>
              > + # notermation about a builder so that we could just
              set this up, it doesn't
              <br>
              > + # necessarily here as hard-coded notermation.
              <br>
              > + if add_path_variables:
              <br>
              > + path_extensions = []
              <br>
              > + dyld_library_path_extensions = []
              <br>
              > + toolchains_dir = os.path.join(builddir_path,
              <br>
              > + ('Applications/Xcode.app/Contents/'
              <br>
              > + 'Developer/Toolchains'))
              <br>
              > + toolchain_paths = []
              <br>
              > + if os.path.exists(toolchains_dir):
              <br>
              > + toolchain_paths = [os.path.join(toolchains_dir,
              name, 'usr')
              <br>
              > + for name in os.listdir(toolchains_dir)]
              <br>
              > + for package_root in ['', 'Developer/usr/'] +
              toolchain_paths:
              <br>
              > + p = os.path.join(builddir_path, package_root,
              'bin')
              <br>
              > + if os.path.exists(p):
              <br>
              > + path_extensions.append(p)
              <br>
              > + p = os.path.join(builddir_path, package_root,
              'lib')
              <br>
              > + if os.path.exists(p):
              <br>
              > + dyld_library_path_extensions.append(p)
              <br>
              > + if path_extensions:
              <br>
              > + env['PATH'] = os.pathsep.join(
              <br>
              > + path_extensions + [os.environ.get('PATH', '')])
              <br>
              > + if dyld_library_path_extensions:
              <br>
              > + env['DYLD_LIBRARY_PATH'] = os.pathsep.join(
              <br>
              > + dyld_library_path_extensions + [
              <br>
              > + os.environ.get('DYLD_LIBRARY_PATH', '')])
              <br>
              > +
              <br>
              > + # Split the arguments into distinct commands.
              <br>
              > + #
              <br>
              > + # Extended command syntax allows running multiple
              commands by separating
              <br>
              > + # them with '----'.
              <br>
              > + test_commands = util.list_split(args, "----")
              <br>
              > +
              <br>
              > + # Split command specs into filters and commands.
              <br>
              > + test_commands = [split_command_filters(spec) for
              spec in test_commands]
              <br>
              > +
              <br>
              > + # Execute the test.
              <br>
              > + command_objects = []
              <br>
              > + interpolated_variables = False
              <br>
              > + for i, (filters, command) in
              enumerate(test_commands):
              <br>
              > + # Interpolate arguments.
              <br>
              > + old_command = command
              <br>
              > + command = [a % options for a in command]
              <br>
              > + if old_command != command:
              <br>
              > + interpolated_variables = True
              <br>
              > +
              <br>
              > + # Create the command object...
              <br>
              > + stdout_log_path = os.path.join(sandbox,
              '%s.%d.stdout' % (path, i))
              <br>
              > + stderr_log_path = os.path.join(sandbox,
              '%s.%d.stderr' % (path, i))
              <br>
              > + cmd_object = Command(command, stdout_log_path,
              stderr_log_path, env)
              <br>
              > + command_objects.append(cmd_object)
              <br>
              > +
              <br>
              > + # Execute the command.
              <br>
              > + try:
              <br>
              > + cmd_object.execute(verbose=verbose)
              <br>
              > + except OSError, e:
              <br>
              > + # Python's exceptions are horribly to read, and
              this one is
              <br>
              > + # incredibly common when people don't use the right
              syntax (or
              <br>
              > + # misspell something) when writing a predicate.
              Detect this and
              <br>
              > + # notify the user.
              <br>
              > + if e.errno == errno.ENOENT:
              <br>
              > + fatal("invalid command, executable doesn't exist:
              %r" % (
              <br>
              > + cmd_object.command[0],))
              <br>
              > + elif e.errno == errno.ENOEXEC:
              <br>
              > + fatal("invalid command, executable has a bad
              format. Did you "
              <br>
              > + "forget to put a #! at the top of a script?: %r"
              <br>
              > + % (cmd_object.command[0],))
              <br>
              > + else:
              <br>
              > + # Otherwise raise the error again.
              <br>
              > + raise e
              <br>
              > +
              <br>
              > + # Evaluate the filters.
              <br>
              > + for filter in filters:
              <br>
              > + cmd_object.evaluate_filter_spec(filter)
              <br>
              > +
              <br>
              > + if show_command_output:
              <br>
              > + for p, type in ((stdout_log_path, "stdout"),
              <br>
              > + (stderr_log_path, "stderr")):
              <br>
              > + if not os.path.exists(p):
              <br>
              > + continue
              <br>
              > +
              <br>
              > + f = open(p)
              <br>
              > + data = f.read()
              <br>
              > + f.close()
              <br>
              > + if data:
              <br>
              > + print ("-- command %s (note: suppressed by default,
              "
              <br>
              > + "see sandbox dir for log files) --" % (type))
              <br>
              > + print "--\n%s--\n" % data
              <br>
              > +
              <br>
              > + test_result = cmd_object.result
              <br>
              > + if not test_result:
              <br>
              > + break
              <br>
              > + if not interpolated_variables:
              <br>
              > + warning('no substitutions found. Fetched root
              ignored?')
              <br>
              > +
              <br>
              > + # Remove the temporary directory.
              <br>
              > + if is_temp:
              <br>
              > + if shell.execute(['rm', '-rf', sandbox]) != 0:
              <br>
              > + note('unable to remove sandbox dir %r' % path)
              <br>
              > +
              <br>
              > + return test_result, command_objects
              <br>
              > +
              <br>
              > +
              <br>
              > +def get_best_match(builds, name, key=lambda x: x):
              <br>
              > + builds = list(builds)
              <br>
              > + builds.sort(key=key)
              <br>
              > +
              <br>
              > + if name is None and builds:
              <br>
              > + return builds[-1]
              <br>
              > +
              <br>
              > + to_find = llvmlab.Build.frombasename(name, None)
              <br>
              > +
              <br>
              > + best = None
              <br>
              > + for item in builds:
              <br>
              > + build = key(item)
              <br>
              > + # Check for a prefix match.
              <br>
              > + path = build.tobasename()
              <br>
              > + if path.startswith(name):
              <br>
              > + return item
              <br>
              > +
              <br>
              > + # Check for a revision match.
              <br>
              > + if build.revision == to_find.revision and
              build.revision is not None:
              <br>
              > + return item
              <br>
              > +
              <br>
              > + # Otherwise, stop when we aren't getting closer.
              <br>
              > + if build > to_find:
              <br>
              > + break
              <br>
              > + best = item
              <br>
              > +
              <br>
              > + return best
              <br>
              > +
              <br>
              > +
              <br>
              > +def action_fetch(name, args):
              <br>
              > + """fetch a build from the server"""
              <br>
              > +
              <br>
              > + parser = OptionParser("""\
              <br>
              > +usage: %%prog %(name)s [options] builder
              [build-name]
              <br>
              > +
              <br>
              > +Fetch the build from the named builder which matchs
              build-name. If no match is
              <br>
              > +found, get the first build before the given name. If
              no build name is given,
              <br>
              > +the most recent build is fetched.
              <br>
              > +
              <br>
              > +The available builders can be listed using:
              <br>
              > +
              <br>
              > + %%prog ci ls""" % locals())
              <br>
              > + parser.add_option("-f", "--force", dest="force",
              <br>
              > + help=("always download and extract, overwriting
              any"
              <br>
              > + "existing files"),
              <br>
              > + action="store_true", default=False)
              <br>
              > + parser.add_option("", "--update-link",
              dest="update_link", metavar="PATH",
              <br>
              > + help=("update a symbolic link at PATH to point to
              the "
              <br>
              > + "fetched build (on success)"),
              <br>
              > + action="store", default=None)
              <br>
              > + parser.add_option("-d", "--dry-run",
              dest='dry_run',
              <br>
              > + help=("Perform all operations except the actual "
              <br>
              > + "downloading and extracting of any files"),
              <br>
              > + action="store_true", default=False)
              <br>
              > +
              <br>
              > + (opts, args) = parser.parse_args(args)
              <br>
              > +
              <br>
              > + if len(args) == 0:
              <br>
              > + parser.error("please specify a builder name")
              <br>
              > + elif len(args) == 1:
              <br>
              > + builder, = args
              <br>
              > + build_name = None
              <br>
              > + elif len(args) == 2:
              <br>
              > + builder, build_name = args
              <br>
              > + else:
              <br>
              > + parser.error("invalid number of arguments")
              <br>
              > +
              <br>
              > + builds = list(llvmlab.fetch_builds(builder))
              <br>
              > + if not builds:
              <br>
              > + parser.error("no builds for builder: %r" % builder)
              <br>
              > +
              <br>
              > + build = get_best_match(builds, build_name)
              <br>
              > + if not build:
              <br>
              > + parser.error("no match for build %r" % build_name)
              <br>
              > +
              <br>
              > + path = build.tobasename()
              <br>
              > + if build_name is not None and not
              path.startswith(build_name):
              <br>
              > + note('no exact match, fetching %r' % path)
              <br>
              > +
              <br>
              > + # Get the paths to extract to.
              <br>
              > + root_path = path
              <br>
              > + builddir_path =
              build.tobasename(include_suffix=False)
              <br>
              > +
              <br>
              > + if not opts.dry_run:
              <br>
              > + # Check that the download and extract paths are
              clean.
              <br>
              > + for p in (root_path, builddir_path):
              <br>
              > + if os.path.exists(p):
              <br>
              > + # If we are using --force, then clean the path.
              <br>
              > + if opts.force:
              <br>
              > + shutil.rmtree(p, ignore_errors=True)
              <br>
              > + continue
              <br>
              > + fatal('current directory is not clean, %r exists' %
              p)
              <br>
              > + llvmlab.fetch_build_to_path(builder, build,
              root_path, builddir_path)
              <br>
              > +
              <br>
              > + print 'downloaded root: %s' % root_path
              <br>
              > + print 'extracted path : %s' % builddir_path
              <br>
              > +
              <br>
              > + # Update the symbolic link, if requested.
              <br>
              > + if not opts.dry_run and opts.update_link:
              <br>
              > + # Remove the existing path.
              <br>
              > + try:
              <br>
              > + os.unlink(opts.update_link)
              <br>
              > + except OSError as e:
              <br>
              > + if e.errno != errno.ENOENT:
              <br>
              > + fatal('unable to update symbolic link at %r, cannot
              unlink' % (
              <br>
              > + opts.update_link))
              <br>
              > +
              <br>
              > + # Create the symbolic link.
              <br>
              > + os.symlink(os.path.abspath(builddir_path),
              opts.update_link)
              <br>
              > + print 'updated link at: %s' % opts.update_link
              <br>
              > + return os.path.abspath(builddir_path)
              <br>
              > +
              <br>
              > +
              <br>
              > +def action_ls(name, args):
              <br>
              > + """list available build names or builds"""
              <br>
              > +
              <br>
              > + parser = OptionParser("""\
              <br>
              > +usage: %%prog %s [build-name]
              <br>
              > +
              <br>
              > +With no arguments, list the available build names on
              'llvmlab'. With a build
              <br>
              > +name, list the available builds for that builder.\
              <br>
              > +""" % name)
              <br>
              > +
              <br>
              > + (opts, args) = parser.parse_args(args)
              <br>
              > +
              <br>
              > + if not len(args):
              <br>
              > + available_buildnames = llvmlab.fetch_builders()
              <br>
              > + available_buildnames.sort()
              <br>
              > + for item in available_buildnames:
              <br>
              > + print item
              <br>
              > + return available_buildnames
              <br>
              > +
              <br>
              > + for name in args:
              <br>
              > + if len(args) > 1:
              <br>
              > + if name is not args[0]:
              <br>
              > + print
              <br>
              > + print '%s:' % name
              <br>
              > + available_builds = list(llvmlab.fetch_builds(name))
              <br>
              > + available_builds.sort()
              <br>
              > + available_builds.reverse()
              <br>
              > + for build in available_builds:
              <br>
              > + print build.tobasename(include_suffix=False)
              <br>
              > + min_rev = min([x.revision for x in
              available_builds])
              <br>
              > + max_rev = max([x.revision for x in
              available_builds])
              <br>
              > + note("Summary: found {} builds:
              r{}-r{}".format(len(available_builds),
              <br>
              > + min_rev, max_rev))
              <br>
              > + return available_builds
              <br>
              > +
              <br>
              > +DEFAULT_BUILDER = "clang-stage1-configure-RA_build"
              <br>
              > +
              <br>
              > +
              <br>
              > +def action_bisect(name, args):
              <br>
              > + """find first failing build using binary search"""
              <br>
              > +
              <br>
              > + parser = OptionParser("""\
              <br>
              > +usage: %%prog %(name)s [options] ... test command
              args ...
              <br>
              > +
              <br>
              > +Look for the first published build where a test
              failed, using the builds on
              <br>
              > +llvmlab. The command arguments are executed once per
              build tested, but each
              <br>
              > +argument is first subject to string interpolation.
              The syntax is
              <br>
              > +"%%(VARIABLE)FORMAT" where FORMAT is a standard
              printf format, and VARIABLE is
              <br>
              > +one of:
              <br>
              > +
              <br>
              > + 'sandbox' - the path to the sandbox directory.
              <br>
              > + 'path' - the path to the build under test.
              <br>
              > + 'revision' - the revision number of the build.
              <br>
              > + 'build' - the build number of the build under test.
              <br>
              > + 'clang' - the path to the clang binary of the build
              if it exists.
              <br>
              > + 'clang++' - the path to the clang++ binary of the
              build if it exists.
              <br>
              > + 'libltodir' - the path to the directory containing
              libLTO.dylib, if it
              <br>
              > + exists.
              <br>
              > +
              <br>
              > +Each test is run in a sandbox directory. By default,
              sandbox directories are
              <br>
              > +temporary directories which are created and
              destroyed for each test (see
              <br>
              > +--sandbox).
              <br>
              > +
              <br>
              > +For use in auxiliary test scripts, each test is also
              run with each variable
              <br>
              > +available in the environment as TEST_<variable
              name> (variables are converted
              <br>
              > +to uppercase). For example, a test script could use
              "TEST_PATH" to find the
              <br>
              > +path to the build under test.
              <br>
              > +
              <br>
              > +The stdout and stderr of the command are logged to
              files inside the sandbox
              <br>
              > +directory. Use an explicit sandbox directory if you
              would like to look at
              <br>
              > +them.
              <br>
              > +
              <br>
              > +It is possible to run multiple distinct commands for
              each test by separating
              <br>
              > +them in the command line arguments by '----'. The
              failure of any command causes
              <br>
              > +the entire test to fail.\
              <br>
              > +""" % locals())
              <br>
              > +
              <br>
              > + parser.add_option("-b", "--build",
              dest="build_name", metavar="STR",
              <br>
              > + help="name of build to fetch",
              <br>
              > + action="store", default=DEFAULT_BUILDER)
              <br>
              > + parser.add_option("-s", "--sandbox",
              dest="sandbox",
              <br>
              > + help="directory to use as a sandbox",
              <br>
              > + action="store", default=None)
              <br>
              > + parser.add_option("-v", "--verbose",
              dest="verbose",
              <br>
              > + help="output more test notermation",
              <br>
              > + action="store_true", default=False)
              <br>
              > + parser.add_option("-V", "--very-verbose",
              dest="very_verbose",
              <br>
              > + help="output even more test notermation",
              <br>
              > + action="store_true", default=False)
              <br>
              > + parser.add_option("", "--show-output",
              dest="show_command_output",
              <br>
              > + help="display command output",
              <br>
              > + action="store_true", default=False)
              <br>
              > + parser.add_option("", "--single-step",
              dest="single_step",
              <br>
              > + help="single step instead of binary stepping",
              <br>
              > + action="store_true", default=False)
              <br>
              > + parser.add_option("", "--min-rev", dest="min_rev",
              <br>
              > + help="minimum revision to test",
              <br>
              > + type="int", action="store", default=None)
              <br>
              > + parser.add_option("", "--max-rev", dest="max_rev",
              <br>
              > + help="maximum revision to test",
              <br>
              > + type="int", action="store", default=None)
              <br>
              > +
              <br>
              > + parser.disable_interspersed_args()
              <br>
              > +
              <br>
              > + (opts, args) = parser.parse_args(args)
              <br>
              > +
              <br>
              > + if opts.build_name is None:
              <br>
              > + parser.error("no build name given (see --build)")
              <br>
              > +
              <br>
              > + # Very verbose implies verbose.
              <br>
              > + opts.verbose |= opts.very_verbose
              <br>
              > +
              <br>
              > + start_time = time.time()
              <br>
              > + available_builds =
              list(llvmlab.fetch_builds(opts.build_name))
              <br>
              > + available_builds.sort()
              <br>
              > + available_builds.reverse()
              <br>
              > + if opts.very_verbose:
              <br>
              > + note("fetched builds in %.2fs" % (time.time() -
              start_time,))
              <br>
              > +
              <br>
              > + if opts.min_rev is not None:
              <br>
              > + available_builds = [b for b in available_builds
              <br>
              > + if b.revision >= opts.min_rev]
              <br>
              > + if opts.max_rev is not None:
              <br>
              > + available_builds = [b for b in available_builds
              <br>
              > + if b.revision <= opts.max_rev]
              <br>
              > +
              <br>
              > + def predicate(item):
              <br>
              > + # Run the sandboxed test.
              <br>
              > + test_result, _ = execute_sandboxed_test(
              <br>
              > + opts.sandbox, opts.build_name, item, args,
              verbose=opts.verbose,
              <br>
              > + very_verbose=opts.very_verbose,
              <br>
              > + show_command_output=opts.show_command_output or
              opts.very_verbose)
              <br>
              > +
              <br>
              > + # Print status.
              <br>
              > + print '%s: %s' % (('FAIL', 'PASS')[test_result],
              <br>
              > + item.tobasename(include_suffix=False))
              <br>
              > +
              <br>
              > + return test_result
              <br>
              > +
              <br>
              > + if opts.single_step:
              <br>
              > + for item in available_builds:
              <br>
              > + if predicate(item):
              <br>
              > + break
              <br>
              > + else:
              <br>
              > + item = None
              <br>
              > + else:
              <br>
              > + if opts.min_rev is None or opts.max_rev is None:
              <br>
              > + # Gallop to find initial search range, under the
              assumption that we
              <br>
              > + # are most likely looking for something at the head
              of this list.
              <br>
              > + search_space = algorithm.gallop(predicate,
              available_builds)
              <br>
              > + else:
              <br>
              > + # If both min and max revisions are specified,
              <br>
              > + # don't gallop - bisect the given range.
              <br>
              > + search_space = available_builds
              <br>
              > + item = algorithm.bisect(predicate, search_space)
              <br>
              > +
              <br>
              > + if item is None:
              <br>
              > + fatal('unable to find any passing build!')
              <br>
              > +
              <br>
              > + print '%s: first working build' %
              item.tobasename(include_suffix=False)
              <br>
              > + index = available_builds.index(item)
              <br>
              > + if index == 0:
              <br>
              > + print 'no failing builds!?'
              <br>
              > + else:
              <br>
              > + print '%s: next failing build' %
              available_builds[index-1].tobasename(
              <br>
              > + include_suffix=False)
              <br>
              > +
              <br>
              > +
              <br>
              > +def action_exec(name, args):
              <br>
              > + """execute a command against a published root"""
              <br>
              > +
              <br>
              > + parser = OptionParser("""\
              <br>
              > +usage: %%prog %(name)s [options] ... test command
              args ...
              <br>
              > +
              <br>
              > +Executes the given command against the latest
              published build. The syntax for
              <br>
              > +commands (and exit code) is exactly the same as for
              the 'bisect' tool, so this
              <br>
              > +command is useful for testing bisect test commands.
              <br>
              > +
              <br>
              > +See 'bisect' for more notermation on the exact test
              syntax.\
              <br>
              > +""" % locals())
              <br>
              > +
              <br>
              > + parser.add_option("-b", "--build",
              dest="build_name", metavar="STR",
              <br>
              > + help="name of build to fetch",
              <br>
              > + action="store", default=DEFAULT_BUILDER)
              <br>
              > + parser.add_option("-s", "--sandbox",
              dest="sandbox",
              <br>
              > + help="directory to use as a sandbox",
              <br>
              > + action="store", default=None)
              <br>
              > + parser.add_option("", "--min-rev", dest="min_rev",
              <br>
              > + help="minimum revision to test",
              <br>
              > + type="int", action="store", default=None)
              <br>
              > + parser.add_option("", "--max-rev", dest="max_rev",
              <br>
              > + help="maximum revision to test",
              <br>
              > + type="int", action="store", default=None)
              <br>
              > + parser.add_option("", "--near", dest="near_build",
              <br>
              > + help="use a build near NAME",
              <br>
              > + type="str", action="store", metavar="NAME",
              default=None)
              <br>
              > +
              <br>
              > + parser.disable_interspersed_args()
              <br>
              > +
              <br>
              > + (opts, args) = parser.parse_args(args)
              <br>
              > +
              <br>
              > + if opts.build_name is None:
              <br>
              > + parser.error("no build name given (see --build)")
              <br>
              > +
              <br>
              > + available_builds =
              list(llvmlab.fetch_builds(opts.build_name))
              <br>
              > + available_builds.sort()
              <br>
              > + available_builds.reverse()
              <br>
              > +
              <br>
              > + if opts.min_rev is not None:
              <br>
              > + available_builds = [b for b in available_builds
              <br>
              > + if b.revision >= opts.min_rev]
              <br>
              > + if opts.max_rev is not None:
              <br>
              > + available_builds = [b for b in available_builds
              <br>
              > + if b.revision <= opts.max_rev]
              <br>
              > +
              <br>
              > + if len(available_builds) == 0:
              <br>
              > + fatal("No builds available for builder name: %s" %
              opts.build_name)
              <br>
              > +
              <br>
              > + # Find the best match, if requested.
              <br>
              > + if opts.near_build:
              <br>
              > + build = get_best_match(available_builds,
              opts.near_build)
              <br>
              > + if not build:
              <br>
              > + parser.error("no match for build %r" %
              opts.near_build)
              <br>
              > + else:
              <br>
              > + # Otherwise, take the latest build.
              <br>
              > + build = available_builds[0]
              <br>
              > +
              <br>
              > + test_result, _ = execute_sandboxed_test(
              <br>
              > + opts.sandbox, opts.build_name, build, args,
              verbose=True,
              <br>
              > + show_command_output=True)
              <br>
              > +
              <br>
              > + print '%s: %s' % (('FAIL', 'PASS')[test_result],
              <br>
              > + build.tobasename(include_suffix=False))
              <br>
              > +
              <br>
              > + raise SystemExit(test_result != True)
              <br>
              > +
              <br>
              > +
              <br>
              > +def action_test(name, args):
              <br>
              > + from . import test_llvmlab
              <br>
              > + test_llvmlab.run_tests()
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/llvmlab/clang_link
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/clang_link?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/clang_link?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/llvmlab/clang_link (added)
              <br>
              > +++ zorg/trunk/llvmbisect/llvmlab/clang_link Thu Oct
              8 16:52:50 2015
              <br>
              > @@ -0,0 +1 @@
              <br>
              > +link
/Users/cmatthews/src/zorg/llvmbisect/llvmlab/clang-r219899-t2014-10-15_20-42-53-b808<br>
              > \ No newline at end of file
              <br>
              >
              <br>
              > Propchange: zorg/trunk/llvmbisect/llvmlab/clang_link
              <br>
              >
------------------------------------------------------------------------------<br>
              > svn:special = *
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/llvmlab/gcs.py
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/gcs.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/gcs.py?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/llvmlab/gcs.py (added)
              <br>
              > +++ zorg/trunk/llvmbisect/llvmlab/gcs.py Thu Oct 8
              16:52:50 2015
              <br>
              > @@ -0,0 +1,53 @@
              <br>
              > +"""Integration with Google Cloud Storage.
              <br>
              > +
              <br>
              > +"""
              <br>
              > +import os
              <br>
              > +import requests
              <br>
              > +
              <br>
              > +# Root URL to use for our queries.
              <br>
              > +GCS = <a class="moz-txt-link-rfc2396E" href="https://www.googleapis.com/storage/v1/">"https://www.googleapis.com/storage/v1/"</a>
              <br>
              > +
              <br>
              > +DEFAULT_BUCKET = "llvm-build-artifacts"
              <br>
              > +
              <br>
              > +BUCKET = os.getenv("BUCKET", DEFAULT_BUCKET)
              <br>
              > +
              <br>
              > +
              <br>
              > +def fetch_builders():
              <br>
              > + """Each build kind is stored as a folder in the GCS
              bucket.
              <br>
              > + List all the folders in the bucket, which is our
              list of possible
              <br>
              > + compilers.
              <br>
              > + """
              <br>
              > + params = {'delimiter': "/", 'fields': "prefixes"}
              <br>
              > + r = requests.get(GCS + "b/" + BUCKET + "/o",
              params=params)
              <br>
              > + r.raise_for_status()
              <br>
              > + reply_data = r.json()
              <br>
              > + folders = reply_data['prefixes']
              <br>
              > + no_slashes = [x.replace("/", "") for x in folders]
              <br>
              > + return no_slashes
              <br>
              > +
              <br>
              > +
              <br>
              > +def fetch_builds(project):
              <br>
              > + """Given a builder name, get the list of all the
              files stored for that
              <br>
              > + builder.
              <br>
              > + """
              <br>
              > + assert project is not None
              <br>
              > + params = {'delimiter': "/",
              <br>
              > + "fields": "kind,items(name, mediaLink)",
              <br>
              > + 'prefix': project + "/"}
              <br>
              > + r = requests.get(GCS + "b/" + BUCKET + "/o",
              params=params)
              <br>
              > + r.raise_for_status()
              <br>
              > + reply_data = r.json()
              <br>
              > + return reply_data
              <br>
              > +
              <br>
              > +# Dunno what this could be moved up to?
              <br>
              > +CHUNK_SIZE = 5124288
              <br>
              > +
              <br>
              > +
              <br>
              > +def get_compiler(url, filename):
              <br>
              > + """Get the compiler at the url, and save to
              filename."""
              <br>
              > + r = requests.get(url)
              <br>
              > + r.raise_for_status()
              <br>
              > + with open(filename, 'wb') as fd:
              <br>
              > + for chunk in r.iter_content(CHUNK_SIZE):
              <br>
              > + fd.write(chunk)
              <br>
              > + return filename
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/llvmlab/llvmlab.py
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/llvmlab.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/llvmlab.py?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/llvmlab/llvmlab.py (added)
              <br>
              > +++ zorg/trunk/llvmbisect/llvmlab/llvmlab.py Thu Oct
              8 16:52:50 2015
              <br>
              > @@ -0,0 +1,272 @@
              <br>
              > +"""Utilities for accessing stuff from llvmlab."""
              <br>
              > +
              <br>
              > +import json
              <br>
              > +import os
              <br>
              > +import re
              <br>
              > +import shutil
              <br>
              > +import time
              <br>
              > +
              <br>
              > +from . import shell
              <br>
              > +from . import util
              <br>
              > +from . import gcs
              <br>
              > +
              <br>
              > +from util import fatal
              <br>
              > +
              <br>
              > +
              <br>
              > +class BuilderMap(object):
              <br>
              > + # Expire the buildermap after 24 hours.
              <br>
              > + expiration_time = 24 * 60 * 60
              <br>
              > +
              <br>
              > + @classmethod
              <br>
              > + def frompath(klass, path):
              <br>
              > + with open(path) as f:
              <br>
              > + data = json.load(f)
              <br>
              > + return klass(data['builders'], data['timestamp'])
              <br>
              > +
              <br>
              > + def __init__(self, builders, timestamp):
              <br>
              > + self.builders = builders
              <br>
              > + self.timestamp = timestamp
              <br>
              > +
              <br>
              > + def topath(self, path):
              <br>
              > + with open(path, 'w') as f:
              <br>
              > + data = {'builders': self.builders,
              <br>
              > + 'timestamp': self.timestamp}
              <br>
              > + json.dump(data, f, indent=2)
              <br>
              > +
              <br>
              > + def is_expired(self):
              <br>
              > + return time.time() > self.timestamp +
              self.expiration_time
              <br>
              > +
              <br>
              > +BUILD_NAME_REGEX = re.compile(
              <br>
              > +
              r"((apple-)?clang)-([0-9]+)(\.([0-9]+))?(\.([0-9]+))?"
              <br>
              > + r"-([A-Z][A-Za-z]+)(\.(.*))?")
              <br>
              > +
              <br>
              > +
              <br>
              > +class Build(object):
              <br>
              > + @staticmethod
              <br>
              > + def frombasename(str, url=None):
              <br>
              > +
              <br>
              > + str = os.path.basename(str)
              <br>
              > + revision = timestamp = build = None
              <br>
              > +
              <br>
              > + # Check if this is a BNI style build.
              <br>
              > + m = BUILD_NAME_REGEX.match(str)
              <br>
              > + if m:
              <br>
              > + name, _, major_str, _, minor_str, _, micro_str,
              build, \
              <br>
              > + _, suffix = m.groups()
              <br>
              > + revision = [int(major_str)]
              <br>
              > + if minor_str:
              <br>
              > + revision.append(int(minor_str))
              <br>
              > + if micro_str:
              <br>
              > + revision.append(int(micro_str))
              <br>
              > + return Build(name, tuple(revision), None, build,
              suffix)
              <br>
              > +
              <br>
              > + if '.' in str:
              <br>
              > + str, suffix = str.split('.', 1)
              <br>
              > + else:
              <br>
              > + suffix = None
              <br>
              > +
              <br>
              > + m = re.match(r'(.*)-b([0-9]+)', str)
              <br>
              > + if m:
              <br>
              > + str, build = m.groups()
              <br>
              > + build = int(build)
              <br>
              > +
              <br>
              > + m = re.match(r'(.*)-t([0-9-]{8,10}_[0-9-]{6,8})',
              str)
              <br>
              > + if m:
              <br>
              > + str, timestamp = m.groups()
              <br>
              > +
              <br>
              > + m = re.match(r'(.*)-r([0-9]+)', str)
              <br>
              > + if m:
              <br>
              > + str, revision = m.groups()
              <br>
              > + revision = int(revision)
              <br>
              > +
              <br>
              > + return Build(str, revision, timestamp, build,
              suffix, url)
              <br>
              > +
              <br>
              > + @staticmethod
              <br>
              > + def fromdata(data):
              <br>
              > + return Build(data['name'], data['revision'],
              data['timestamp'],
              <br>
              > + data['build'], data['suffix'])
              <br>
              > +
              <br>
              > + def todata(self):
              <br>
              > + return {'name': self.name,
              <br>
              > + 'revision': self.revision,
              <br>
              > + 'timestamp': self.timestamp,
              <br>
              > + 'build': self.build,
              <br>
              > + 'suffix': self.suffix}
              <br>
              > +
              <br>
              > + def __init__(self, name, revision, timestamp,
              build, suffix, url):
              <br>
              > + self.name = name
              <br>
              > + self.revision = revision
              <br>
              > + self.timestamp = timestamp
              <br>
              > + self.build = build
              <br>
              > + self.suffix = suffix
              <br>
              > + self.url = url
              <br>
              > +
              <br>
              > + def tobasename(self, include_suffix=True):
              <br>
              > + basename = self.name
              <br>
              > + if self.revision is not None:
              <br>
              > + if isinstance(self.revision, (tuple, list)):
              <br>
              > + basename += '-' + '.'.join(str(r) for r in
              self.revision)
              <br>
              > + else:
              <br>
              > + assert isinstance(self.revision, int)
              <br>
              > + basename += '-r%d' % self.revision
              <br>
              > + if self.timestamp is not None:
              <br>
              > + basename += '-t%s' % self.timestamp
              <br>
              > + if self.build is not None:
              <br>
              > + if isinstance(self.build, str):
              <br>
              > + basename += '-' + self.build
              <br>
              > + else:
              <br>
              > + basename += '-b%d' % self.build
              <br>
              > + if include_suffix and self.suffix is not None:
              <br>
              > + basename += '.%s' % self.suffix
              <br>
              > + return basename
              <br>
              > +
              <br>
              > + def __repr__(self):
              <br>
              > + return "%s%r" % (self.__class__.__name__,
              <br>
              > + (self.name, self.revision, self.timestamp,
              self.build,
              <br>
              > + self.suffix))
              <br>
              > +
              <br>
              > + def __cmp__(self, other):
              <br>
              > + return cmp((self.revision, self.timestamp,
              <br>
              > + self.build, self.suffix, self.name),
              <br>
              > + ((other.revision, other.timestamp,
              <br>
              > + other.build, other.suffix, other.name)))
              <br>
              > +
              <br>
              > +
              <br>
              > +def load_builder_map(reload=False):
              <br>
              > + """
              <br>
              > + load_builder_map() -> BuilderMap
              <br>
              > +
              <br>
              > + Load a map of builder names to the server url that
              holds those artifacts.
              <br>
              > + """
              <br>
              > +
              <br>
              > + prefs = util.get_prefs()
              <br>
              > +
              <br>
              > + # Load load the builder map if present (and not
              reloading)
              <br>
              > + data_path = os.path.join(prefs.path, "ci")
              <br>
              > + buildermap_path = os.path.join(data_path,
              "build_map.json")
              <br>
              > + if not reload and os.path.exists(buildermap_path):
              <br>
              > + buildermap = BuilderMap.frompath(buildermap_path)
              <br>
              > +
              <br>
              > + # If the buildermap is not out-of-date, return it.
              <br>
              > + if not buildermap.is_expired():
              <br>
              > + return buildermap
              <br>
              > +
              <br>
              > + # Otherwise, we didn't have a buildermap or it is
              out of date, compute it.
              <br>
              > + builders = {}
              <br>
              > + for build in gcs.fetch_builders():
              <br>
              > + builders[build] = build
              <br>
              > +
              <br>
              > + # Create the buildermap and save it.
              <br>
              > + buildermap = BuilderMap(builders, time.time())
              <br>
              > + if not os.path.exists(data_path):
              <br>
              > + shell.mkdir_p(data_path)
              <br>
              > + buildermap.topath(buildermap_path)
              <br>
              > +
              <br>
              > + return buildermap
              <br>
              > +
              <br>
              > +
              <br>
              > +def fetch_builders():
              <br>
              > + """
              <br>
              > + fetch_builders() -> [builder-name, ...]
              <br>
              > +
              <br>
              > + Get a list of available builders.
              <br>
              > + """
              <br>
              > +
              <br>
              > + # Handle only_use_cache setting.
              <br>
              > + prefs = util.get_prefs()
              <br>
              > + if prefs.getboolean("ci", "only_use_cache"):
              <br>
              > + cache_path = os.path.join(prefs.path, "ci",
              "build_cache")
              <br>
              > + return sorted(os.listdir(cache_path))
              <br>
              > +
              <br>
              > + # Otherwise, fetch the builder map.
              <br>
              > + return sorted(load_builder_map().builders.keys())
              <br>
              > +
              <br>
              > +
              <br>
              > +def fetch_builds(name):
              <br>
              > + """
              <br>
              > + fetch_builds(name) -> [(path, revision, build),
              ...]
              <br>
              > +
              <br>
              > + Get a list of available builds for the named
              builder.
              <br>
              > + """
              <br>
              > + # Handle only_use_cache setting.
              <br>
              > + prefs = util.get_prefs()
              <br>
              > + if prefs.getboolean("ci", "only_use_cache"):
              <br>
              > + cache_path = os.path.join(prefs.path, "ci",
              "build_cache")
              <br>
              > + cache_build_path = os.path.join(cache_path, name)
              <br>
              > + items = os.listdir(cache_build_path)
              <br>
              > + assert False, "Unimplemented?" + str(items)
              <br>
              > + # Otherwise, load the builder map.
              <br>
              > + buildermap = load_builder_map()
              <br>
              > +
              <br>
              > + # If the builder isn't in the builder map, do a
              forced load of the builder
              <br>
              > + # map.
              <br>
              > + if name not in buildermap.builders:
              <br>
              > + buildermap = load_builder_map(reload=True)
              <br>
              > +
              <br>
              > + # If the builder doesn't exist, report an error.
              <br>
              > + builder_artifacts = buildermap.builders.get(name)
              <br>
              > + if builder_artifacts is None:
              <br>
              > + fatal("unknown builder name: %r" % (name,))
              <br>
              > +
              <br>
              > + # Otherwise, load the builder list.
              <br>
              > + server_builds = gcs.fetch_builds(builder_artifacts)
              <br>
              > + builds = []
              <br>
              > + for path in server_builds['items']:
              <br>
              > + build = Build.frombasename(path['name'],
              path['mediaLink'])
              <br>
              > +
              <br>
              > + # Ignore any links which don't at least have a
              revision component.
              <br>
              > + if build.revision is not None:
              <br>
              > + builds.append(build)
              <br>
              > +
              <br>
              > + # If there were no builds, report an error.
              <br>
              > + if not builds:
              <br>
              > + fatal("builder %r may be misconfigured (no items)"
              % (name,))
              <br>
              > +
              <br>
              > + # Sort the builds, to make sure we return them
              ordered properly.
              <br>
              > + builds.sort()
              <br>
              > +
              <br>
              > + return builds
              <br>
              > +
              <br>
              > +
              <br>
              > +def fetch_build_to_path(builder, build, root_path,
              builddir_path):
              <br>
              > + path = build.tobasename()
              <br>
              > +
              <br>
              > + # Check whether we are using a build cache and get
              the cached build path if
              <br>
              > + # so.
              <br>
              > + prefs = util.get_prefs()
              <br>
              > + cache_build_path = None
              <br>
              > + if prefs.getboolean("ci", "cache_builds"):
              <br>
              > + cache_path = os.path.join(prefs.path, "ci",
              "build_cache")
              <br>
              > + cache_build_path = os.path.join(cache_path,
              builder, path)
              <br>
              > +
              <br>
              > + # Copy the build from the cache or download it.
              <br>
              > + if cache_build_path and
              os.path.exists(cache_build_path):
              <br>
              > + shutil.copy(cache_build_path, root_path)
              <br>
              > + else:
              <br>
              > + # Load the builder map.
              <br>
              > + buildermap = load_builder_map()
              <br>
              > +
              <br>
              > + # If the builder isn't in the builder map, do a
              forced reload of the
              <br>
              > + # builder map.
              <br>
              > + if builder not in buildermap.builders:
              <br>
              > + buildermap = load_builder_map(reload=True)
              <br>
              > +
              <br>
              > + # If the builder doesn't exist, report an error.
              <br>
              > + builder_artifacts =
              buildermap.builders.get(builder)
              <br>
              > + if builder_artifacts is None:
              <br>
              > + fatal("unknown builder name: %r" % (builder,))
              <br>
              > +
              <br>
              > + # Otherwise create the build url.
              <br>
              > + gcs.get_compiler(build.url, root_path)
              <br>
              > +
              <br>
              > + # Copy the build into the cache, if enabled.
              <br>
              > + if cache_build_path is not None:
              <br>
              > + shell.mkdir_p(os.path.dirname(cache_build_path))
              <br>
              > + shutil.copy(root_path, cache_build_path)
              <br>
              > +
              <br>
              > + # Create the directory for the build.
              <br>
              > + os.mkdir(builddir_path)
              <br>
              > +
              <br>
              > + # Extract the build.
              <br>
              > + if shell.execute(['tar', '-xf', root_path, '-C',
              builddir_path]):
              <br>
              > + fatal('unable to extract %r to %r' % (root_path,
              builddir_path))
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/llvmlab/scripts.py
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/scripts.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/scripts.py?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/llvmlab/scripts.py (added)
              <br>
              > +++ zorg/trunk/llvmbisect/llvmlab/scripts.py Thu Oct
              8 16:52:50 2015
              <br>
              > @@ -0,0 +1,60 @@
              <br>
              > +"""
              <br>
              > +Utilities for building the llvmlab multi-tool.
              <br>
              > +"""
              <br>
              > +
              <br>
              > +import os
              <br>
              > +import sys
              <br>
              > +
              <br>
              > +
              <br>
              > +class Tool(object):
              <br>
              > + """
              <br>
              > + This object defines a generic command line tool
              instance, which dynamically
              <br>
              > + builds its commands from a module dictionary.
              <br>
              > +
              <br>
              > + Example usage::
              <br>
              > +
              <br>
              > + import scripts
              <br>
              > +
              <br>
              > + def action_foo(name, args):
              <br>
              > + "the foo command"
              <br>
              > +
              <br>
              > + ...
              <br>
              > +
              <br>
              > + tool = scripts.Tool(locals())
              <br>
              > + if __name__ == '__main__':
              <br>
              > + tool.main(sys.argv)
              <br>
              > +
              <br>
              > + Any function beginning with "action_" is considered
              a tool command. It's
              <br>
              > + name is defined by the function name suffix.
              Underscores in the function
              <br>
              > + name are converted to '-' in the command line
              syntax. Actions ending ith
              <br>
              > + "-debug" are not listed in the help.
              <br>
              > + """
              <br>
              > +
              <br>
              > + def __init__(self, locals):
              <br>
              > + # Create the list of commands.
              <br>
              > + self.commands = dict((name[7:].replace('_', '-'),
              f)
              <br>
              > + for name, f in locals.items()
              <br>
              > + if name.startswith('action_'))
              <br>
              > +
              <br>
              > + def usage(self, name):
              <br>
              > + print >>sys.stderr, "Usage: %s command
              [options]" % (
              <br>
              > + os.path.basename(name))
              <br>
              > + print >>sys.stderr
              <br>
              > + print >>sys.stderr, "Available commands:"
              <br>
              > + cmds_width = max(map(len, self.commands))
              <br>
              > + for name, func in sorted(self.commands.items()):
              <br>
              > + if name.endswith("-debug"):
              <br>
              > + continue
              <br>
              > +
              <br>
              > + print >>sys.stderr, " %-*s - %s" %
              (cmds_width, name,
              <br>
              > + func.__doc__)
              <br>
              > + sys.exit(1)
              <br>
              > +
              <br>
              > + def main(self, args):
              <br>
              > + if len(args) < 2 or args[1] not in
              self.commands:
              <br>
              > + if len(args) >= 2:
              <br>
              > + print >>sys.stderr, "error: invalid command
              %r\n" % args[1]
              <br>
              > + self.usage(args[0])
              <br>
              > +
              <br>
              > + cmd = args[1]
              <br>
              > + return self.commands[cmd]('%s %s' % (args[0], cmd),
              args[2:])
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/llvmlab/shell.py
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/shell.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/shell.py?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/llvmlab/shell.py (added)
              <br>
              > +++ zorg/trunk/llvmbisect/llvmlab/shell.py Thu Oct 8
              16:52:50 2015
              <br>
              > @@ -0,0 +1,44 @@
              <br>
              > +"""
              <br>
              > +shell like utilities
              <br>
              > +"""
              <br>
              > +
              <br>
              > +import os
              <br>
              > +
              <br>
              > +
              <br>
              > +def execute(args):
              <br>
              > + import subprocess
              <br>
              > + """execute(command) - Run the given command (or
              argv list) in a shell and
              <br>
              > + return the exit code."""
              <br>
              > + return subprocess.Popen(args).wait()
              <br>
              > +
              <br>
              > +
              <br>
              > +def capture(args, include_stderr=False):
              <br>
              > + import subprocess
              <br>
              > + """capture(command) - Run the given command (or
              argv list) in a shell and
              <br>
              > + return the standard output."""
              <br>
              > + stderr = subprocess.PIPE
              <br>
              > + if include_stderr:
              <br>
              > + stderr = subprocess.STDOUT
              <br>
              > + p = subprocess.Popen(args, stdout=subprocess.PIPE,
              stderr=stderr)
              <br>
              > + out, _ = p.communicate()
              <br>
              > + return p.wait(), out
              <br>
              > +
              <br>
              > +
              <br>
              > +def mkdir_p(path):
              <br>
              > + """mkdir_p(path) - Make the "path" directory, if it
              does not exist; this
              <br>
              > + will also make directories for any missing parent
              directories."""
              <br>
              > + import errno
              <br>
              > +
              <br>
              > + if not path or os.path.exists(path):
              <br>
              > + return
              <br>
              > +
              <br>
              > + parent = os.path.dirname(path)
              <br>
              > + if parent != path:
              <br>
              > + mkdir_p(parent)
              <br>
              > +
              <br>
              > + try:
              <br>
              > + os.mkdir(path)
              <br>
              > + except OSError as e:
              <br>
              > + # Ignore EEXIST, which may occur during a race
              condition.
              <br>
              > + if e.errno != errno.EEXIST:
              <br>
              > + raise
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/llvmlab/test_llvmlab.py
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/test_llvmlab.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/test_llvmlab.py?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/llvmlab/test_llvmlab.py
              (added)
              <br>
              > +++ zorg/trunk/llvmbisect/llvmlab/test_llvmlab.py Thu
              Oct 8 16:52:50 2015
              <br>
              > @@ -0,0 +1,50 @@
              <br>
              > +# RUN: pythong test_llvmlab.py
              <br>
              > +import unittest
              <br>
              > +
              <br>
              > +import os
              <br>
              > +import shutil
              <br>
              > +import tempfile
              <br>
              > +from . import ci
              <br>
              > +
              <br>
              > +class TestLLVMLabCI(unittest.TestCase):
              <br>
              > +
              <br>
              > + def setUp(self):
              <br>
              > + self.workdir = tempfile.mkdtemp()
              <br>
              > + print self.workdir
              <br>
              > + os.chdir(self.workdir)
              <br>
              > +
              <br>
              > + def tearDown(self):
              <br>
              > + shutil.rmtree(self.workdir)
              <br>
              > +
              <br>
              > + def test_bisect(self):
              <br>
              > + ci.action_bisect("llvmlab", ["--min-rev", "219719",
              <br>
              > + "--max-rev", "219899",
              <br>
              > + "bash", "-c",
              <br>
              > + "%(path)s/bin/clang -v | grep b700"])
              <br>
              > +
              <br>
              > + def test_ls(self):
              <br>
              > + """Check that you can """
              <br>
              > + builds = ci.action_ls("llvmlab", [])
              <br>
              > + self.assertIn("clang-stage1-configure-RA_build",
              builds)
              <br>
              > + compilers = ci.action_ls("llvmlab",
              <br>
              > + ["clang-stage1-configure-RA_build"])
              <br>
              > + compiler_revs = [x.revision for x in compilers]
              <br>
              > + self.assertIn(219899, compiler_revs)
              <br>
              > +
              <br>
              > + def test_fetch_noargs(self):
              <br>
              > + """ """
              <br>
              > + path = ci.action_fetch("llvmlab",
              ["clang-stage1-configure-RA_build"])
              <br>
              > + self.assertTrue(os.path.isdir(path), "Fetch did not
              get a compiler?")
              <br>
              > +
              <br>
              > + def test_fetch_arg(self):
              <br>
              > + """ """
              <br>
              > + path = ci.action_fetch("llvmlab",
              <br>
              > + ["--update-link", "clang_link",
              <br>
              > + "clang-stage1-configure-RA_build",
              <br>
              > + "clang-r219899-t2014-10-15_20-42-53-b808"])
              <br>
              > + self.assertTrue(os.path.isdir(path), "Fetch did not
              get a compiler?")
              <br>
              > +
              <br>
              > +
              <br>
              > +def run_tests():
              <br>
              > + suite =
              unittest.TestLoader().loadTestsFromTestCase(TestLLVMLabCI)
              <br>
              > + unittest.TextTestRunner(verbosity=2).run(suite)
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/llvmlab/util.py
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/util.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/util.py?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/llvmlab/util.py (added)
              <br>
              > +++ zorg/trunk/llvmbisect/llvmlab/util.py Thu Oct 8
              16:52:50 2015
              <br>
              > @@ -0,0 +1,286 @@
              <br>
              > +import ConfigParser
              <br>
              > +import datetime
              <br>
              > +import inspect
              <br>
              > +import os
              <br>
              > +import sys
              <br>
              > +import traceback
              <br>
              > +
              <br>
              > +__all__ = []
              <br>
              > +
              <br>
              > +def _write_message(kind, message):
              <br>
              > + # Get the file/line where this message was
              generated.
              <br>
              > + f = inspect.currentframe()
              <br>
              > + # Step out of _write_message, and then out of
              wrapper.
              <br>
              > + f = f.f_back.f_back
              <br>
              > + file,line,_,_,_ = inspect.getframeinfo(f)
              <br>
              > + location = '%s:%d' % (os.path.basename(file), line)
              <br>
              > +
              <br>
              > + print >>sys.stderr, '%s: %s: %s' % (location,
              kind, message)
              <br>
              > +
              <br>
              > +note = lambda message: _write_message('note',
              message)
              <br>
              > +warning = lambda message: _write_message('warning',
              message)
              <br>
              > +error = lambda message: _write_message('error',
              message)
              <br>
              > +fatal = lambda message: (_write_message('fatal
              error', message), sys.exit(1))
              <br>
              > +
              <br>
              > +
              <br>
              > +def sorted(l, **kwargs):
              <br>
              > + l = list(l)
              <br>
              > + l.sort(**kwargs)
              <br>
              > + return l
              <br>
              > +
              <br>
              > +def list_split(list, item):
              <br>
              > + parts = []
              <br>
              > + while item in list:
              <br>
              > + index = list.index(item)
              <br>
              > + parts.append(list[:index])
              <br>
              > + list = list[index+1:]
              <br>
              > + parts.append(list)
              <br>
              > + return parts
              <br>
              > +
              <br>
              > +def pairs(l):
              <br>
              > + return zip(l, l[1:])
              <br>
              > +
              <br>
              > +###
              <br>
              > +
              <br>
              > +class EnumVal(object):
              <br>
              > + def __init__(self, enum, name, value):
              <br>
              > + self.enum = enum
              <br>
              > + self.name = name
              <br>
              > + self.value = value
              <br>
              > +
              <br>
              > + def __repr__(self):
              <br>
              > + return '%s.%s' % (self.enum._name, self.name)
              <br>
              > +
              <br>
              > +class Enum(object):
              <br>
              > + def __init__(self, name, **kwargs):
              <br>
              > + self._name = name
              <br>
              > + self.__items = dict((name, EnumVal(self, name,
              value))
              <br>
              > + for name,value in kwargs.items())
              <br>
              > + self.__reverse_map = dict((e.value,e.name)
              <br>
              > + for e in self.__items.values())
              <br>
              > + self.__dict__.update(self.__items)
              <br>
              > +
              <br>
              > + def get_value(self, name):
              <br>
              > + return self.__items.get(name)
              <br>
              > +
              <br>
              > + def get_name(self, value):
              <br>
              > + return self.__reverse_map.get(value)
              <br>
              > +
              <br>
              > + def get_by_value(self, value):
              <br>
              > + return
              self.__items.get(self.__reverse_map.get(value))
              <br>
              > +
              <br>
              > + def contains(self, item):
              <br>
              > + if not isinstance(item, EnumVal):
              <br>
              > + return False
              <br>
              > + return item.enum == self
              <br>
              > +
              <br>
              > +class multidict:
              <br>
              > + def __init__(self, elts=()):
              <br>
              > + self.data = {}
              <br>
              > + for key,value in elts:
              <br>
              > + self[key] = value
              <br>
              > +
              <br>
              > + def __contains__(self, item):
              <br>
              > + return item in self.data
              <br>
              > + def __getitem__(self, item):
              <br>
              > + return self.data[item]
              <br>
              > + def __setitem__(self, key, value):
              <br>
              > + if key in self.data:
              <br>
              > + self.data[key].append(value)
              <br>
              > + else:
              <br>
              > + self.data[key] = [value]
              <br>
              > + def items(self):
              <br>
              > + return self.data.items()
              <br>
              > + def values(self):
              <br>
              > + return self.data.values()
              <br>
              > + def keys(self):
              <br>
              > + return self.data.keys()
              <br>
              > + def __len__(self):
              <br>
              > + return len(self.data)
              <br>
              > + def get(self, key, default=None):
              <br>
              > + return self.data.get(key, default)
              <br>
              > + def todict(self):
              <br>
              > + return self.data.copy()
              <br>
              > +
              <br>
              > +###
              <br>
              > +
              <br>
              > +class Preferences(object):
              <br>
              > + def __init__(self, path):
              <br>
              > + self.path = path
              <br>
              > + self.config_path = os.path.join(path, "config")
              <br>
              > + self.options = ConfigParser.RawConfigParser()
              <br>
              > +
              <br>
              > + # Load the config file, if present.
              <br>
              > + if os.path.exists(self.config_path):
              <br>
              > + self.options.read(self.config_path)
              <br>
              > +
              <br>
              > + def save(self):
              <br>
              > + file = open(self.config_path, "w")
              <br>
              > + try:
              <br>
              > + self.options.write(file)
              <br>
              > + finally:
              <br>
              > + file.close()
              <br>
              > +
              <br>
              > + def get(self, section, option, default = None):
              <br>
              > + if self.options.has_option(section, option):
              <br>
              > + return self.options.get(section, option)
              <br>
              > + else:
              <br>
              > + return default
              <br>
              > +
              <br>
              > + def getboolean(self, section, option, default =
              None):
              <br>
              > + if self.options.has_option(section, option):
              <br>
              > + return self.options.getboolean(section, option)
              <br>
              > + else:
              <br>
              > + return default
              <br>
              > +
              <br>
              > + def setboolean(self, section, option, value):
              <br>
              > + return self.options.set(section, option,
              str(value))
              <br>
              > +
              <br>
              > +_prefs = None
              <br>
              > +def get_prefs():
              <br>
              > + global _prefs
              <br>
              > + if _prefs is None:
              <br>
              > + _prefs =
              Preferences(os.path.expanduser("~/.llvmlab"))
              <br>
              > +
              <br>
              > + # Allow dynamic override of only_use_cache option.
              <br>
              > + if os.environ.get("LLVMLAB_ONLY_USE_CACHE"):
              <br>
              > + _prefs.setboolean("ci", "only_use_cache", True)
              <br>
              > +
              <br>
              > + return _prefs
              <br>
              > +
              <br>
              > +###
              <br>
              > +
              <br>
              > +import threading
              <br>
              > +import Queue
              <br>
              > +
              <br>
              > +def detect_num_cpus():
              <br>
              > + """
              <br>
              > + Detects the number of CPUs on a system. Cribbed
              from pp.
              <br>
              > + """
              <br>
              > + # Linux, Unix and MacOS:
              <br>
              > + if hasattr(os, "sysconf"):
              <br>
              > + if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
              <br>
              > + # Linux & Unix:
              <br>
              > + ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
              <br>
              > + if isinstance(ncpus, int) and ncpus > 0:
              <br>
              > + return ncpus
              <br>
              > + else: # OSX:
              <br>
              > + return int(os.popen2("sysctl -n
              hw.ncpu")[1].read())
              <br>
              > + # Windows:
              <br>
              > + if os.environ.has_key("NUMBER_OF_PROCESSORS"):
              <br>
              > + ncpus = int(os.environ["NUMBER_OF_PROCESSORS"])
              <br>
              > + if ncpus > 0:
              <br>
              > + return ncpus
              <br>
              > + return 1 # Default
              <br>
              > +
              <br>
              > +def execute_task_on_threads(fn, iterable,
              num_threads = None):
              <br>
              > + """execute_task_on_threads(fn, iterable) ->
              iterable
              <br>
              > +
              <br>
              > + Given a task function to run on an iterable list of
              work items, execute the
              <br>
              > + task on each item in the list using some number of
              threads, and yield the
              <br>
              > + results of the task function.
              <br>
              > +
              <br>
              > + If a task function throws an exception, the
              exception will be
              <br>
              > + printed but not returned to the caller. Clients
              which wish to
              <br>
              > + control exceptions should handle them inside the
              task function.
              <br>
              > + """
              <br>
              > + def push_work():
              <br>
              > + for item in iterable:
              <br>
              > + work_queue.put(item)
              <br>
              > +
              <br>
              > + # Push sentinels to cause workers to terminate.
              <br>
              > + for i in range(num_threads):
              <br>
              > + work_queue.put(_sentinel)
              <br>
              > + def do_work():
              <br>
              > + while True:
              <br>
              > + # Read a work item.
              <br>
              > + item = work_queue.get()
              <br>
              > +
              <br>
              > + # If we hit a sentinel, propogate it to the output
              queue and
              <br>
              > + # terminate.
              <br>
              > + if item is _sentinel:
              <br>
              > + output_queue.put(_sentinel)
              <br>
              > + break
              <br>
              > +
              <br>
              > + # Otherwise, execute the task and push to the
              output queue.
              <br>
              > + try:
              <br>
              > + output = (None, fn(item))
              <br>
              > + except Exception, e:
              <br>
              > + output = ('error', sys.exc_info())
              <br>
              > +
              <br>
              > + output_queue.put(output)
              <br>
              > +
              <br>
              > + # Compute the number of threads to use.
              <br>
              > + if num_threads is None:
              <br>
              > + num_threads = detect_num_cpus()
              <br>
              > +
              <br>
              > + # Create two queues, one for feeding items to the
              works and another for
              <br>
              > + # consuming the output.
              <br>
              > + work_queue = Queue.Queue()
              <br>
              > + output_queue = Queue.Queue()
              <br>
              > +
              <br>
              > + # Create our unique sentinel object.
              <br>
              > + _sentinel = []
              <br>
              > +
              <br>
              > + # Create and run thread to push items onto the work
              queue.
              <br>
              > + threading.Thread(target=push_work).start()
              <br>
              > +
              <br>
              > + # Create and run the worker threads.
              <br>
              > + for i in range(num_threads):
              <br>
              > + t = threading.Thread(target=do_work)
              <br>
              > + t.daemon = True
              <br>
              > + t.start()
              <br>
              > +
              <br>
              > + # Read items from the output queue until all
              threads are finished.
              <br>
              > + finished = 0
              <br>
              > + while finished != num_threads:
              <br>
              > + item = output_queue.get()
              <br>
              > +
              <br>
              > + # Check for termination marker.
              <br>
              > + if item is _sentinel:
              <br>
              > + finished += 1
              <br>
              > + continue
              <br>
              > +
              <br>
              > + # Check for exceptions.
              <br>
              > + if item[0] == 'error':
              <br>
              > + _,(t,v,tb) = item
              <br>
              > + traceback.print_exception(t, v, tb)
              <br>
              > + continue
              <br>
              > +
              <br>
              > + assert item[0] is None
              <br>
              > + yield item[1]
              <br>
              > +
              <br>
              > +def timestamp():
              <br>
              > + return
              datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
              <br>
              > +
              <br>
              > +###
              <br>
              > +
              <br>
              > +import collections
              <br>
              > +
              <br>
              > +class orderedset(object):
              <br>
              > + def __init__(self, items=None):
              <br>
              > + self.base = collections.OrderedDict()
              <br>
              > + if items is not None:
              <br>
              > + self.update(items)
              <br>
              > +
              <br>
              > + def update(self, items):
              <br>
              > + for item in items:
              <br>
              > + self.add(item)
              <br>
              > +
              <br>
              > + def add(self, item):
              <br>
              > + self.base[item] = None
              <br>
              > +
              <br>
              > + def remove(self, item):
              <br>
              > + del self.base[item]
              <br>
              > +
              <br>
              > + def __nonzero__(self):
              <br>
              > + return bool(self.base)
              <br>
              > +
              <br>
              > + def __len__(self):
              <br>
              > + return len(self.base)
              <br>
              > +
              <br>
              > + def __iter__(self):
              <br>
              > + return iter(self.base)
              <br>
              > +
              <br>
              > + def __contains__(self, item):
              <br>
              > + return item in self.base
              <br>
              >
              <br>
              > Added: zorg/trunk/llvmbisect/setup.py
              <br>
              > URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/setup.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/setup.py?rev=249757&view=auto</a><br>
              >
==============================================================================<br>
              > --- zorg/trunk/llvmbisect/setup.py (added)
              <br>
              > +++ zorg/trunk/llvmbisect/setup.py Thu Oct 8 16:52:50
              2015
              <br>
              > @@ -0,0 +1,41 @@
              <br>
              > +import os
              <br>
              > +
              <br>
              > +from setuptools import setup, find_packages
              <br>
              > +
              <br>
              > +# setuptools expects to be invoked from within the
              directory of setup.py, but it
              <br>
              > +# is nice to allow:
              <br>
              > +# python path/to/setup.py install
              <br>
              > +# to work (for scripts, etc.)
              <br>
              > +os.chdir(os.path.dirname(os.path.abspath(__file__)))
              <br>
              > +
              <br>
              > +setup(
              <br>
              > + name = "llvmbisect",
              <br>
              > + version = "1.0",
              <br>
              > +
              <br>
              > + author = "Daniel Dunbar and Chris Matthews",
              <br>
              > + author_email = <a class="moz-txt-link-rfc2396E" href="mailto:chris.matthews@apple.com">"chris.matthews@apple.com"</a>,
              <br>
              > + url = '<a class="moz-txt-link-freetext" href="http://lab.llvm.org">http://lab.llvm.org</a>',
              <br>
              > + license = 'BSD',
              <br>
              > +
              <br>
              > + description = "Compiler bisection service.",
              <br>
              > + keywords = 'testing compiler performance
              development llvm',
              <br>
              > +
              <br>
              > + classifiers=[
              <br>
              > + 'Development Status :: 4 - Beta',
              <br>
              > + 'Environment :: Console',
              <br>
              > + 'Intended Audience :: Developers',
              <br>
              > + ('License :: OSI Approved :: '
              <br>
              > + 'University of Illinois/NCSA Open Source License'),
              <br>
              > + 'Natural Language :: English',
              <br>
              > + 'Operating System :: OS Independent',
              <br>
              > + 'Progamming Language :: Python',
              <br>
              > + 'Topic :: Software Development :: Quality
              Assurance',
              <br>
              > + 'Topic :: Software Development :: Testing',
              <br>
              > + ],
              <br>
              > +
              <br>
              > + packages = find_packages(),
              <br>
              > +
              <br>
              > + scripts = ['bin/llvmlab'],
              <br>
              > +
              <br>
              > + install_requires=['requests'],
              <br>
              > +)
              <br>
              >
              <br>
              >
              <br>
              > _______________________________________________
              <br>
              > llvm-commits mailing list
              <br>
              > <a class="moz-txt-link-abbreviated" href="mailto:llvm-commits@lists.llvm.org">llvm-commits@lists.llvm.org</a>
              <br>
              >
              <a class="moz-txt-link-freetext" href="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits">http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a>
              <br>
              <br>
              <br>
            </div>
          </div>
        </span></blockquote>
    </blockquote>
    <p><br>
    </p>
  </body>
</html>