Squashed 'third_party/boostorg/predef/' content from commit 560ff52

Change-Id: Ib7804ff2c768e0d597993b019146292dfb41e9dc
git-subtree-dir: third_party/boostorg/predef
git-subtree-split: 560ff5298ead78276872604f1ee6523e63a4fa90
diff --git a/tools/ci/common.py b/tools/ci/common.py
new file mode 100644
index 0000000..50ca860
--- /dev/null
+++ b/tools/ci/common.py
@@ -0,0 +1,878 @@
+#!/usr/bin/env python
+
+# Copyright Rene Rivera 2016
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+import sys
+import inspect
+import optparse
+import os.path
+import string
+import time
+import subprocess
+import codecs
+import shutil
+import threading
+
+toolset_info = {
+    'clang-3.4' : {
+        'ppa' : ["ppa:h-rayflood/llvm"],
+        'package' : 'clang-3.4',
+        'command' : 'clang++-3.4',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'clang-3.5' : {
+        'ppa' : ["ppa:h-rayflood/llvm"],
+        'package' : 'clang-3.5',
+        'command' : 'clang++-3.5',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'clang-3.6' : {
+        'ppa' : ["ppa:h-rayflood/llvm"],
+        'package' : 'clang-3.6',
+        'command' : 'clang++-3.6',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'clang-3.7' : {
+        'deb' : ["http://apt.llvm.org/trusty/","llvm-toolchain-trusty-3.7","main"],
+        'apt-key' : ['http://apt.llvm.org/llvm-snapshot.gpg.key'],
+        'package' : 'clang-3.7',
+        'command' : 'clang++-3.7',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'clang-3.8' : {
+        'deb' : ["http://apt.llvm.org/trusty/","llvm-toolchain-trusty-3.8","main"],
+        'apt-key' : ['http://apt.llvm.org/llvm-snapshot.gpg.key'],
+        'package' : 'clang-3.8',
+        'command' : 'clang++-3.8',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'clang-3.9' : {
+        'deb' : ["http://apt.llvm.org/trusty/","llvm-toolchain-trusty-3.9","main"],
+        'apt-key' : ['http://apt.llvm.org/llvm-snapshot.gpg.key'],
+        'package' : 'clang-3.9',
+        'command' : 'clang++-3.9',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'clang-4.0' : {
+        'deb' : ["http://apt.llvm.org/trusty/","llvm-toolchain-trusty-4.0","main"],
+        'apt-key' : ['http://apt.llvm.org/llvm-snapshot.gpg.key'],
+        'package' : 'clang-4.0',
+        'command' : 'clang++-4.0',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'clang-5.0' : {
+        'deb' : ["http://apt.llvm.org/trusty/","llvm-toolchain-trusty-5.0","main"],
+        'apt-key' : ['http://apt.llvm.org/llvm-snapshot.gpg.key'],
+        'package' : 'clang-5.0',
+        'command' : 'clang++-5.0',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'clang-6.0' : {
+        'deb' : ["http://apt.llvm.org/trusty/","llvm-toolchain-trusty-6.0","main"],
+        'apt-key' : ['http://apt.llvm.org/llvm-snapshot.gpg.key'],
+        'package' : 'clang-6.0',
+        'command' : 'clang++-6.0',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'gcc-4.7' : {
+        'ppa' : ["ppa:ubuntu-toolchain-r/test"],
+        'package' : 'g++-4.7',
+        'command' : 'g++-4.7',
+        'toolset' : 'gcc',
+        'version' : ''
+        },
+    'gcc-4.8' : {
+        'bin' : 'gcc-4.8',
+        'ppa' : ["ppa:ubuntu-toolchain-r/test"],
+        'package' : 'g++-4.8',
+        'command' : 'g++-4.8',
+        'toolset' : 'gcc',
+        'version' : ''
+        },
+    'gcc-4.9' : {
+        'ppa' : ["ppa:ubuntu-toolchain-r/test"],
+        'package' : 'g++-4.9',
+        'command' : 'g++-4.9',
+        'toolset' : 'gcc',
+        'version' : ''
+        },
+    'gcc-5.1' : {
+        'ppa' : ["ppa:ubuntu-toolchain-r/test"],
+        'package' : 'g++-5',
+        'command' : 'g++-5',
+        'toolset' : 'gcc',
+        'version' : ''
+        },
+    'gcc-5' : {
+        'ppa' : ["ppa:ubuntu-toolchain-r/test"],
+        'package' : 'g++-5',
+        'command' : 'g++-5',
+        'toolset' : 'gcc',
+        'version' : ''
+        },
+    'gcc-6' : {
+        'ppa' : ["ppa:ubuntu-toolchain-r/test"],
+        'package' : 'g++-6',
+        'command' : 'g++-6',
+        'toolset' : 'gcc',
+        'version' : ''
+        },
+    'gcc-7' : {
+        'ppa' : ["ppa:ubuntu-toolchain-r/test"],
+        'package' : 'g++-7',
+        'command' : 'g++-7',
+        'toolset' : 'gcc',
+        'version' : ''
+        },
+    'gcc-8' : {
+        'ppa' : ["ppa:ubuntu-toolchain-r/test"],
+        'package' : 'g++-8',
+        'command' : 'g++-8',
+        'toolset' : 'gcc',
+        'version' : ''
+        },
+    'mingw-5' : {
+        'toolset' : 'gcc',
+        'command' : 'C:\\\\MinGW\\\\bin\\\\g++.exe',
+        'version' : ''
+        },
+    'mingw64-6' : {
+        'toolset' : 'gcc',
+        'command' : 'C:\\\\mingw-w64\\\\x86_64-6.3.0-posix-seh-rt_v5-rev1\\\\mingw64\\\\bin\\\\g++.exe',
+        'version' : ''
+        },
+    'vs-2008' : {
+        'toolset' : 'msvc',
+        'command' : '',
+        'version' : '9.0'
+        },
+    'vs-2010' : {
+        'toolset' : 'msvc',
+        'command' : '',
+        'version' : '10.0'
+        },
+    'vs-2012' : {
+        'toolset' : 'msvc',
+        'command' : '',
+        'version' : '11.0'
+        },
+    'vs-2013' : {
+        'toolset' : 'msvc',
+        'command' : '',
+        'version' : '12.0'
+        },
+    'vs-2015' : {
+        'toolset' : 'msvc',
+        'command' : '',
+        'version' : '14.0'
+        },
+    'vs-2017' : {
+        'toolset' : 'msvc',
+        'command' : '',
+        'version' : '14.1'
+        },
+    'xcode-6.1' : {
+        'command' : 'clang++',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'xcode-6.2' : {
+        'command' : 'clang++',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'xcode-6.3' : {
+        'command' : 'clang++',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'xcode-6.4' : {
+        'command' : 'clang++',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'xcode-7.0' : {
+        'command' : 'clang++',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'xcode-7.1' : {
+        'command' : 'clang++',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'xcode-7.2' : {
+        'command' : 'clang++',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'xcode-7.3' : {
+        'command' : 'clang++',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'xcode-8.0' : {
+        'command' : 'clang++',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'xcode-8.1' : {
+        'command' : 'clang++',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'xcode-8.2' : {
+        'command' : 'clang++',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'xcode-8.3' : {
+        'command' : 'clang++',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'xcode-9.0' : {
+        'command' : 'clang++',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    'xcode-9.1' : {
+        'command' : 'clang++',
+        'toolset' : 'clang',
+        'version' : ''
+        },
+    }
+
+class SystemCallError(Exception):
+    def __init__(self, command, result):
+        self.command = command
+        self.result = result
+    def __str__(self, *args, **kwargs):
+        return "'%s' ==> %s"%("' '".join(self.command), self.result)
+
+class utils:
+    
+    call_stats = []
+    
+    @staticmethod
+    def call(*command, **kargs):
+        utils.log( "%s> '%s'"%(os.getcwd(), "' '".join(command)) )
+        t = time.time()
+        result = subprocess.call(command, **kargs)
+        t = time.time()-t
+        if result != 0:
+            print "Failed: '%s' ERROR = %s"%("' '".join(command), result)
+        utils.call_stats.append((t,os.getcwd(),command,result))
+        utils.log( "%s> '%s' execution time %s seconds"%(os.getcwd(), "' '".join(command), t) )
+        return result
+    
+    @staticmethod
+    def print_call_stats():
+        utils.log("================================================================================")
+        for j in sorted(utils.call_stats, reverse=True):
+            utils.log("{:>12.4f}\t{}> {} ==> {}".format(*j))
+        utils.log("================================================================================")
+    
+    @staticmethod
+    def check_call(*command, **kargs):
+        cwd = os.getcwd()
+        result = utils.call(*command, **kargs)
+        if result != 0:
+            raise(SystemCallError([cwd].extend(command), result))
+    
+    @staticmethod
+    def makedirs( path ):
+        if not os.path.exists( path ):
+            os.makedirs( path )
+    
+    @staticmethod
+    def log_level():
+        frames = inspect.stack()
+        level = 0
+        for i in frames[ 3: ]:
+            if i[0].f_locals.has_key( '__log__' ):
+                level = level + i[0].f_locals[ '__log__' ]
+        return level
+    
+    @staticmethod
+    def log( message ):
+        sys.stdout.flush()
+        sys.stderr.flush()
+        sys.stderr.write( '# ' + '    ' * utils.log_level() +  message + '\n' )
+        sys.stderr.flush()
+
+    @staticmethod
+    def rmtree(path):
+        if os.path.exists( path ):
+            #~ shutil.rmtree( unicode( path ) )
+            if sys.platform == 'win32':
+                os.system( 'del /f /s /q "%s" >nul 2>&1' % path )
+                shutil.rmtree( unicode( path ) )
+            else:
+                os.system( 'rm -f -r "%s"' % path )
+
+    @staticmethod
+    def retry( f, max_attempts=5, sleep_secs=10 ):
+        for attempts in range( max_attempts, -1, -1 ):
+            try:
+                return f()
+            except Exception, msg:
+                utils.log( '%s failed with message "%s"' % ( f.__name__, msg ) )
+                if attempts == 0:
+                    utils.log( 'Giving up.' )
+                    raise
+
+                utils.log( 'Retrying (%d more attempts).' % attempts )
+                time.sleep( sleep_secs )
+
+    @staticmethod
+    def web_get( source_url, destination_file, proxy = None ):
+        import urllib
+
+        proxies = None
+        if proxy is not None:
+            proxies = {
+                'https' : proxy,
+                'http' : proxy
+                }
+
+        src = urllib.urlopen( source_url, proxies = proxies )
+
+        f = open( destination_file, 'wb' )
+        while True:
+            data = src.read( 16*1024 )
+            if len( data ) == 0: break
+            f.write( data )
+
+        f.close()
+        src.close()
+
+    @staticmethod
+    def unpack_archive( archive_path ):
+        utils.log( 'Unpacking archive ("%s")...' % archive_path )
+
+        archive_name = os.path.basename( archive_path )
+        extension = archive_name[ archive_name.find( '.' ) : ]
+
+        if extension in ( ".tar.gz", ".tar.bz2" ):
+            import tarfile
+            import stat
+
+            mode = os.path.splitext( extension )[1][1:]
+            tar = tarfile.open( archive_path, 'r:%s' % mode )
+            for tarinfo in tar:
+                tar.extract( tarinfo )
+                if sys.platform == 'win32' and not tarinfo.isdir():
+                    # workaround what appears to be a Win32-specific bug in 'tarfile'
+                    # (modification times for extracted files are not set properly)
+                    f = os.path.join( os.curdir, tarinfo.name )
+                    os.chmod( f, stat.S_IWRITE )
+                    os.utime( f, ( tarinfo.mtime, tarinfo.mtime ) )
+            tar.close()
+        elif extension in ( ".zip" ):
+            import zipfile
+
+            z = zipfile.ZipFile( archive_path, 'r', zipfile.ZIP_DEFLATED )
+            for f in z.infolist():
+                destination_file_path = os.path.join( os.curdir, f.filename )
+                if destination_file_path[-1] == "/": # directory
+                    if not os.path.exists( destination_file_path  ):
+                        os.makedirs( destination_file_path  )
+                else: # file
+                    result = open( destination_file_path, 'wb' )
+                    result.write( z.read( f.filename ) )
+                    result.close()
+            z.close()
+        else:
+            raise 'Do not know how to unpack archives with extension \"%s\"' % extension
+    
+    @staticmethod
+    def make_file(filename, *text):
+        text = string.join( text, '\n' )
+        with codecs.open( filename, 'w', 'utf-8' ) as f:
+            f.write( text )
+    
+    @staticmethod
+    def append_file(filename, *text):
+        with codecs.open( filename, 'a', 'utf-8' ) as f:
+            f.write( string.join( text, '\n' ) )
+    
+    @staticmethod
+    def mem_info():
+        if sys.platform == "darwin":
+            utils.call("top","-l","1","-s","0","-n","0")
+        elif sys.platform.startswith("linux"):
+            utils.call("free","-m","-l")
+    
+    @staticmethod
+    def query_boost_version(boost_root):
+        '''
+        Read in the Boost version from a given boost_root.
+        '''
+        boost_version = None
+        if os.path.exists(os.path.join(boost_root,'Jamroot')):
+            with codecs.open(os.path.join(boost_root,'Jamroot'), 'r', 'utf-8') as f:
+                for line in f.readlines():
+                    parts = line.split()
+                    if len(parts) >= 5 and parts[1] == 'BOOST_VERSION':
+                        boost_version = parts[3]
+                        break
+        if not boost_version:
+            boost_version = 'default'
+        return boost_version
+    
+    @staticmethod
+    def git_clone(sub_repo, branch, commit = None, cwd = None, no_submodules = False):
+        '''
+        This clone mimicks the way Travis-CI clones a project's repo. So far
+        Travis-CI is the most limiting in the sense of only fetching partial
+        history of the repo.
+        '''
+        if not cwd:
+            cwd = cwd = os.getcwd()
+        root_dir = os.path.join(cwd,'boostorg',sub_repo)
+        if not os.path.exists(os.path.join(root_dir,'.git')):
+            utils.check_call("git","clone",
+                "--depth=1",
+                "--branch=%s"%(branch),
+                "https://github.com/boostorg/%s.git"%(sub_repo),
+                root_dir)
+            os.chdir(root_dir)
+        else:
+            os.chdir(root_dir)
+            utils.check_call("git","pull",
+                # "--depth=1", # Can't do depth as we get merge errors.
+                "--quiet","--no-recurse-submodules")
+        if commit:
+            utils.check_call("git","checkout","-qf",commit)
+        if os.path.exists(os.path.join('.git','modules')):
+            if sys.platform == 'win32':
+                utils.check_call('dir',os.path.join('.git','modules'))
+            else:
+                utils.check_call('ls','-la',os.path.join('.git','modules'))
+        if not no_submodules:
+            utils.check_call("git","submodule","--quiet","update",
+                "--quiet","--init","--recursive",
+                )
+            utils.check_call("git","submodule","--quiet","foreach","git","fetch")
+        return root_dir
+
+class parallel_call(threading.Thread):
+    '''
+    Runs a synchronous command in a thread waiting for it to complete.
+    '''
+    
+    def __init__(self, *command, **kargs):
+        super(parallel_call,self).__init__()
+        self.command = command
+        self.command_kargs = kargs
+        self.start()
+    
+    def run(self):
+        self.result = utils.call(*self.command, **self.command_kargs)
+    
+    def join(self):
+        super(parallel_call,self).join()
+        if self.result != 0:
+            raise(SystemCallError(self.command, self.result))
+
+def set_arg(args, k, v = None):
+    if not args.get(k):
+        args[k] = v
+    return args[k]
+
+class script_common(object):
+    '''
+    Main script to run Boost C++ Libraries continuous integration.
+    '''
+
+    def __init__(self, ci_klass, **kargs):
+        self.ci = ci_klass(self)
+
+        opt = optparse.OptionParser(
+            usage="%prog [options] [commands]")
+
+        #~ Debug Options:
+        opt.add_option( '--debug-level',
+            help="debugging level; controls the amount of debugging output printed",
+            type='int' )
+        opt.add_option( '-j',
+            help="maximum number of parallel jobs to use for building with b2",
+            type='int', dest='jobs')
+        opt.add_option('--branch')
+        opt.add_option('--commit')
+        kargs = self.init(opt,kargs)
+        kargs = self.ci.init(opt, kargs)
+        set_arg(kargs,'debug_level',0)
+        set_arg(kargs,'jobs',2)
+        set_arg(kargs,'branch',None)
+        set_arg(kargs,'commit',None)
+        set_arg(kargs,'repo',None)
+        set_arg(kargs,'root_dir',None)
+        set_arg(kargs,'actions',None)
+        set_arg(kargs,'pull_request', None)
+
+        #~ Defaults
+        for (k,v) in kargs.iteritems():
+            setattr(self,k,v)
+        ( _opt_, self.actions ) = opt.parse_args(None,self)
+        if not self.actions or self.actions == []:
+            self.actions = kargs.get('actions',None)
+        if not self.actions or self.actions == []:
+            self.actions = [ 'info' ]
+        if not self.root_dir:
+            self.root_dir = os.getcwd()
+        self.build_dir = os.path.join(os.path.dirname(self.root_dir), "build")
+        
+        # API keys.
+        self.bintray_key = os.getenv('BINTRAY_KEY')
+
+        try:
+            self.start()
+            self.command_info()
+            self.main()
+            utils.print_call_stats()
+        except:
+            utils.print_call_stats()
+            raise
+    
+    def init(self, opt, kargs):
+        return kargs
+    
+    def start(self):
+        pass
+
+    def main(self):
+        for action in self.actions:
+            action_m = "command_"+action.replace('-','_')
+            ci_command = getattr(self.ci, action_m, None)
+            ci_script = getattr(self, action_m, None)
+            if ci_command or ci_script:
+                utils.log( "### %s.."%(action) )
+                if os.path.exists(self.root_dir):
+                    os.chdir(self.root_dir)
+                if ci_command:
+                    ci_command()
+                elif ci_script:
+                    ci_script()
+        
+    def b2( self, *args, **kargs ):
+        cmd = ['b2','--debug-configuration', '-j%s'%(self.jobs)]
+        cmd.extend(args)
+
+        if 'toolset' in kargs:
+            cmd.append('toolset=' + kargs['toolset'])
+
+        if 'parallel' in kargs:
+            return parallel_call(*cmd)
+        else:
+            return utils.check_call(*cmd)
+
+    # Common test commands in the order they should be executed..
+    
+    def command_info(self):
+        pass
+    
+    def command_install(self):
+        utils.makedirs(self.build_dir)
+        os.chdir(self.build_dir)
+    
+    def command_install_toolset(self, toolset):
+        if self.ci and hasattr(self.ci,'install_toolset'):
+            self.ci.install_toolset(toolset)
+    
+    def command_before_build(self):
+        pass
+
+    def command_build(self):
+        pass
+
+    def command_before_cache(self):
+        pass
+
+    def command_after_success(self):
+        pass
+
+class ci_cli(object):
+    '''
+    This version of the script provides a way to do manual building. It sets up
+    additional environment and adds fetching of the git repos that would
+    normally be done by the CI system.
+    
+    The common way to use this variant is to invoke something like:
+    
+        mkdir boost-ci
+        cd boost-ci
+        python path-to/ci_boost_<script>.py --branch=develop [--repo=mylib] ...
+    
+    Status: In working order.
+    '''
+    
+    def __init__(self,script):
+        if sys.platform == 'darwin':
+            # Requirements for running on OSX:
+            # https://www.stack.nl/~dimitri/doxygen/download.html#srcbin
+            # https://tug.org/mactex/morepackages.html
+            doxygen_path = "/Applications/Doxygen.app/Contents/Resources"
+            if os.path.isdir(doxygen_path):
+                os.environ["PATH"] = doxygen_path+':'+os.environ['PATH']
+        self.script = script
+        self.work_dir = os.getcwd()
+        self.exit_result = 0
+    
+    def init(self, opt, kargs):
+        kargs['actions'] = [
+            'clone',
+            'install',
+            'before_build',
+            'build',
+            'before_cache',
+            'finish'
+            ]
+        opt.add_option( '--repo',
+            help="Boost repo short name we are testing with, and hence the repo we clone.")
+        set_arg(kargs,'repo','boost')
+        return kargs
+    
+    def finish(self, result):
+        self.exit_result = result
+    
+    def command_clone(self):
+        self.script.root_dir = os.path.join(self.work_dir,'boostorg',self.script.repo)
+        self.script.build_dir = os.path.join(os.path.dirname(self.script.root_dir), "build")
+        utils.git_clone(self.script.repo, self.script.branch, self.script.commit, self.work_dir)
+    
+    def command_finish(self):
+        exit(self.exit_result)
+
+class ci_travis(object):
+    '''
+    This variant build releases in the context of the Travis-CI service.
+    '''
+    
+    def __init__(self,script):
+        self.script = script
+        self.work_dir = os.getenv("HOME")
+    
+    def init(self, opt, kargs):
+        set_arg(kargs,'root_dir', os.getenv("TRAVIS_BUILD_DIR"))
+        set_arg(kargs,'branch', os.getenv("TRAVIS_BRANCH"))
+        set_arg(kargs,'commit', os.getenv("TRAVIS_COMMIT"))
+        set_arg(kargs,'repo', os.getenv("TRAVIS_REPO_SLUG").split("/")[1])
+        set_arg(kargs,'pull_request',
+            os.getenv('TRAVIS_PULL_REQUEST') \
+                if os.getenv('TRAVIS_PULL_REQUEST') != 'false' else None)
+        return kargs
+    
+    def finish(self, result):
+        exit(result)
+    
+    def install_toolset(self, toolset):
+        '''
+        Installs specific toolset on CI system.
+        '''
+        info = toolset_info[toolset]
+        if sys.platform.startswith('linux'):
+            os.chdir(self.work_dir)
+            if 'ppa' in info:
+                for ppa in info['ppa']:
+                    utils.check_call(
+                        'sudo','add-apt-repository','--yes',ppa)
+            if 'deb' in info:
+                utils.make_file('sources.list',
+                    "deb %s"%(' '.join(info['deb'])),
+                    "deb-src %s"%(' '.join(info['deb'])))
+                utils.check_call('sudo','bash','-c','cat sources.list >> /etc/apt/sources.list')
+            if 'apt-key' in info:
+                for key in info['apt-key']:
+                    utils.check_call('wget',key,'-O','apt.key')
+                    utils.check_call('sudo','apt-key','add','apt.key')
+            utils.check_call(
+                'sudo','apt-get','update','-qq')
+            utils.check_call(
+                'sudo','apt-get','install','-qq',info['package'])
+            if 'debugpackage' in info and info['debugpackage']:
+                utils.check_call(
+                    'sudo','apt-get','install','-qq',info['debugpackage'])
+
+    # Travis-CI commands in the order they are executed. We need
+    # these to forward to our common commands, if they are different.
+    
+    def command_before_install(self):
+        pass
+    
+    def command_install(self):
+        self.script.command_install()
+
+    def command_before_script(self):
+        self.script.command_before_build()
+
+    def command_script(self):
+        self.script.command_build()
+
+    def command_before_cache(self):
+        self.script.command_before_cache()
+
+    def command_after_success(self):
+        self.script.command_after_success()
+
+    def command_after_failure(self):
+        pass
+
+    def command_before_deploy(self):
+        pass
+
+    def command_after_deploy(self):
+        pass
+
+    def command_after_script(self):
+        pass
+
+class ci_circleci(object):
+    '''
+    This variant build releases in the context of the CircleCI service.
+    '''
+    
+    def __init__(self,script):
+        self.script = script
+        self.work_dir = os.getenv("HOME")
+    
+    def init(self, opt, kargs):
+        set_arg(kargs,'root_dir', os.path.join(os.getenv("HOME"),os.getenv("CIRCLE_PROJECT_REPONAME")))
+        set_arg(kargs,'branch', os.getenv("CIRCLE_BRANCH"))
+        set_arg(kargs,'commit', os.getenv("CIRCLE_SHA1"))
+        set_arg(kargs,'repo', os.getenv("CIRCLE_PROJECT_REPONAME").split("/")[1])
+        set_arg(kargs,'pull_request', os.getenv('CIRCLE_PR_NUMBER'))
+        return kargs
+    
+    def finish(self, result):
+        exit(result)
+    
+    def command_machine_post(self):
+        # Apt update for the pckages installs we'll do later.
+        utils.check_call('sudo','apt-get','-qq','update')
+        # Need PyYAML to read Travis yaml in a later step.
+        utils.check_call("pip","install","--user","PyYAML")
+    
+    def command_checkout_post(self):
+        os.chdir(self.script.root_dir)
+        utils.check_call("git","submodule","update","--quiet","--init","--recursive")
+    
+    def command_dependencies_pre(self):
+        # Read in .travis.yml for list of packages to install
+        # as CircleCI doesn't have a convenient apt install method.
+        import yaml
+        utils.check_call('sudo','-E','apt-get','-yqq','update')
+        utils.check_call('sudo','apt-get','-yqq','purge','texlive*')
+        with open(os.path.join(self.script.root_dir,'.travis.yml')) as yml:
+            travis_yml = yaml.load(yml)
+            utils.check_call('sudo','apt-get','-yqq',
+                '--no-install-suggests','--no-install-recommends','--force-yes','install',
+                *travis_yml['addons']['apt']['packages'])
+    
+    def command_dependencies_override(self):
+        self.script.command_install()
+    
+    def command_dependencies_post(self):
+        pass
+    
+    def command_database_pre(self):
+        pass
+    
+    def command_database_override(self):
+        pass
+    
+    def command_database_post(self):
+        pass
+    
+    def command_test_pre(self):
+        self.script.command_install()
+        self.script.command_before_build()
+    
+    def command_test_override(self):
+        # CircleCI runs all the test subsets. So in order to avoid
+        # running the after_success we do it here as the build step
+        # will halt accordingly.
+        self.script.command_build()
+        self.script.command_before_cache()
+        self.script.command_after_success()
+    
+    def command_test_post(self):
+        pass
+
+class ci_appveyor(object):
+    
+    def __init__(self,script):
+        self.script = script
+        self.work_dir = os.path.dirname(os.getenv("APPVEYOR_BUILD_FOLDER"))
+    
+    def init(self, opt, kargs):
+        set_arg(kargs,'root_dir',os.getenv("APPVEYOR_BUILD_FOLDER"))
+        set_arg(kargs,'branch',os.getenv("APPVEYOR_REPO_BRANCH"))
+        set_arg(kargs,'commit',os.getenv("APPVEYOR_REPO_COMMIT"))
+        set_arg(kargs,'repo',os.getenv("APPVEYOR_REPO_NAME").split("/")[1])
+        set_arg(kargs,'address_model',os.getenv("PLATFORM",None))
+        set_arg(kargs,'variant',os.getenv("CONFIGURATION","debug"))
+        set_arg(kargs,'pull_request', os.getenv('APPVEYOR_PULL_REQUEST_NUMBER'))
+        return kargs
+    
+    def finish(self, result):
+        exit(result)
+    
+    # Appveyor commands in the order they are executed. We need
+    # these to forward to our common commands, if they are different.
+    
+    def command_install(self):
+        self.script.command_install()
+    
+    def command_before_build(self):
+        os.chdir(self.script.root_dir)
+        utils.check_call("git","submodule","update","--quiet","--init","--recursive")
+        self.script.command_before_build()
+    
+    def command_build_script(self):
+        self.script.command_build()
+    
+    def command_after_build(self):
+        self.script.command_before_cache()
+    
+    def command_before_test(self):
+        pass
+    
+    def command_test_script(self):
+        pass
+    
+    def command_after_test(self):
+        pass
+    
+    def command_on_success(self):
+        self.script.command_after_success()
+    
+    def command_on_failure(self):
+        pass
+    
+    def command_on_finish(self):
+        pass
+
+def main(script_klass):
+    if os.getenv('TRAVIS', False):
+        script_klass(ci_travis)
+    elif os.getenv('CIRCLECI', False):
+        script_klass(ci_circleci)
+    elif os.getenv('APPVEYOR', False):
+        script_klass(ci_appveyor)
+    else:
+        script_klass(ci_cli)