888 lines
27 KiB
Python
888 lines
27 KiB
Python
#!/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' : ''
|
|
},
|
|
'xcode-9.2' : {
|
|
'command' : 'clang++',
|
|
'toolset' : 'clang',
|
|
'version' : ''
|
|
},
|
|
'xcode-9.3' : {
|
|
'command' : 'clang++',
|
|
'toolset' : 'clang',
|
|
'version' : ''
|
|
},
|
|
'xcode-9.4' : {
|
|
'command' : 'clang++',
|
|
'toolset' : 'clang',
|
|
'version' : ''
|
|
},
|
|
'xcode-10.0' : {
|
|
'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(owner, repo, branch, commit = None, repo_dir = None, submodules = False, url_format = "https://github.com/%(owner)s/%(repo)s.git"):
|
|
'''
|
|
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 repo_dir:
|
|
repo_dir = os.path.join(os.getcwd(), owner+','+repo)
|
|
utils.makedirs(os.path.dirname(repo_dir))
|
|
if not os.path.exists(os.path.join(repo_dir,'.git')):
|
|
utils.check_call("git","clone",
|
|
"--depth=1",
|
|
"--branch=%s"%(branch),
|
|
url_format%{'owner':owner,'repo':repo},
|
|
repo_dir)
|
|
os.chdir(repo_dir)
|
|
else:
|
|
os.chdir(repo_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 submodules:
|
|
utils.check_call("git","submodule","--quiet","update",
|
|
"--quiet","--init","--recursive",
|
|
)
|
|
utils.check_call("git","submodule","--quiet","foreach","git","fetch")
|
|
return repo_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 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,'repo_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.repo_dir:
|
|
self.repo_dir = os.getcwd()
|
|
self.build_dir = os.path.join(os.path.dirname(self.repo_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.repo_dir):
|
|
os.chdir(self.repo_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 ci
|
|
cd ci
|
|
python path-to/library_test.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.repo_dir = os.getcwd()
|
|
self.exit_result = 0
|
|
|
|
def init(self, opt, kargs):
|
|
kargs['actions'] = [
|
|
# 'clone',
|
|
'install',
|
|
'before_build',
|
|
'build',
|
|
'before_cache',
|
|
'finish'
|
|
]
|
|
return kargs
|
|
|
|
def finish(self, result):
|
|
self.exit_result = result
|
|
|
|
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
|
|
|
|
def init(self, opt, kargs):
|
|
set_arg(kargs,'repo_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.script.build_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
|
|
|
|
def init(self, opt, kargs):
|
|
set_arg(kargs,'repo_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.repo_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.repo_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
|
|
|
|
def init(self, opt, kargs):
|
|
set_arg(kargs,'repo_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.repo_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)
|