Files

1001 lines
30 KiB
Python
Raw Permalink Normal View History

2019-04-21 22:15:17 -05:00
# Copyright Rene Rivera 2016-2019
#
# 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 = {
2019-04-21 22:15:17 -05:00
'clang-3.4': {
'ppa': ["ppa:ubuntu-toolchain-r/test"],
'package': 'clang-3.4',
'command': 'clang++-3.4',
'toolset': 'clang',
'version': ''
},
'clang-3.5': {
'ppa': ["ppa:ubuntu-toolchain-r/test"],
'package': 'clang-3.5',
'command': 'clang++-3.5',
'toolset': 'clang',
'version': ''
},
'clang-3.6': {
'ppa': ["ppa:ubuntu-toolchain-r/test"],
'package': 'clang-3.6',
'command': 'clang++-3.6',
'toolset': 'clang',
'version': ''
},
'clang-3.7': {
'ppa': ["ppa:ubuntu-toolchain-r/test"],
'package': 'clang-3.7',
'command': 'clang++-3.7',
'toolset': 'clang',
'version': ''
},
'clang-3.8': {
'ppa': ["ppa:ubuntu-toolchain-r/test"],
'package': 'clang-3.8',
'command': 'clang++-3.8',
'toolset': 'clang',
'version': ''
},
'clang-3.9': {
'ppa': ["ppa:ubuntu-toolchain-r/test"],
'package': 'clang-3.9',
'command': 'clang++-3.9',
'toolset': 'clang',
'version': ''
},
'clang-4.0': {
'deb': ["http://apt.llvm.org/xenial/", "llvm-toolchain-xenial-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/xenial/", "llvm-toolchain-xenial-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/xenial/", "llvm-toolchain-xenial-6.0", "main"],
'apt-key': ['http://apt.llvm.org/llvm-snapshot.gpg.key'],
'package': 'clang-6.0',
'command': 'clang++-6.0',
'toolset': 'clang',
'version': ''
},
'clang-7': {
'deb': ["http://apt.llvm.org/xenial/", "llvm-toolchain-xenial-7", "main"],
'apt-key': ['http://apt.llvm.org/llvm-snapshot.gpg.key'],
'package': 'clang-6.0',
'command': 'clang++-6.0',
'toolset': 'clang',
'version': ''
},
'clang-8': {
'deb': ["http://apt.llvm.org/xenial/", "llvm-toolchain-xenial-8", "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-8': {
'toolset': 'gcc',
'command': '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'
},
'vs-2019': {
'toolset': 'msvc',
'command': '',
'version': '14.2'
},
'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.0.1': {
'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.3.1': {
'command': 'clang++',
'toolset': 'clang',
'version': ''
},
'xcode-9.4': {
'command': 'clang++',
'toolset': 'clang',
'version': ''
},
'xcode-9.4.1': {
'command': 'clang++',
'toolset': 'clang',
'version': ''
},
'xcode-10.0': {
'command': 'clang++',
'toolset': 'clang',
'version': ''
},
'xcode-10.1': {
'command': 'clang++',
'toolset': 'clang',
'version': ''
},
}
class SystemCallError(Exception):
def __init__(self, command, result):
self.command = command
self.result = result
2019-04-21 22:15:17 -05:00
def __str__(self, *args, **kwargs):
2019-04-21 22:15:17 -05:00
return "'%s' ==> %s" % ("' '".join(self.command), self.result)
class utils:
2019-04-21 22:15:17 -05:00
call_stats = []
2019-04-21 22:15:17 -05:00
@staticmethod
def call(*command, **kargs):
2019-04-21 22:15:17 -05:00
utils.log("%s> '%s'" % (os.getcwd(), "' '".join(command)))
t = time.time()
result = subprocess.call(command, **kargs)
t = time.time()-t
if result != 0:
2019-04-21 22:15:17 -05:00
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
2019-04-21 22:15:17 -05:00
@staticmethod
def print_call_stats():
2019-04-21 22:15:17 -05:00
utils.log(
"================================================================================")
for j in sorted(utils.call_stats, reverse=True):
utils.log("{:>12.4f}\t{}> {} ==> {}".format(*j))
2019-04-21 22:15:17 -05:00
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))
2019-04-21 22:15:17 -05:00
@staticmethod
2019-04-21 22:15:17 -05:00
def makedirs(path):
if not os.path.exists(path):
os.makedirs(path)
@staticmethod
def log_level():
frames = inspect.stack()
level = 0
2019-04-21 22:15:17 -05:00
for i in frames[3:]:
if '__log__' in i[0].f_locals:
level = level + i[0].f_locals['__log__']
return level
2019-04-21 22:15:17 -05:00
@staticmethod
2019-04-21 22:15:17 -05:00
def log(message):
sys.stdout.flush()
sys.stdout.write('# ' + ' ' * utils.log_level() + message + '\n')
sys.stdout.flush()
@staticmethod
def rmtree(path):
2019-04-21 22:15:17 -05:00
if os.path.exists(path):
#~ shutil.rmtree( unicode( path ) )
if sys.platform == 'win32':
2019-04-21 22:15:17 -05:00
os.system('del /f /s /q "%s" >nul 2>&1' % path)
shutil.rmtree(unicode(path))
else:
2019-04-21 22:15:17 -05:00
os.system('rm -f -r "%s"' % path)
@staticmethod
2019-04-21 22:15:17 -05:00
def retry(f, max_attempts=5, sleep_secs=10):
for attempts in range(max_attempts, -1, -1):
try:
return f()
2019-04-21 22:15:17 -05:00
except Exception as msg:
utils.log('%s failed with message "%s"' % (f.__name__, msg))
if attempts == 0:
2019-04-21 22:15:17 -05:00
utils.log('Giving up.')
raise
2019-04-21 22:15:17 -05:00
utils.log('Retrying (%d more attempts).' % attempts)
time.sleep(sleep_secs)
@staticmethod
2019-04-21 22:15:17 -05:00
def web_get(source_url, destination_file, proxy=None):
import urllib
proxies = None
if proxy is not None:
proxies = {
2019-04-21 22:15:17 -05:00
'https': proxy,
'http': proxy
}
2019-04-21 22:15:17 -05:00
src = urllib.urlopen(source_url, proxies=proxies)
2019-04-21 22:15:17 -05:00
f = open(destination_file, 'wb')
while True:
2019-04-21 22:15:17 -05:00
data = src.read(16*1024)
if len(data) == 0:
break
f.write(data)
f.close()
src.close()
@staticmethod
2019-04-21 22:15:17 -05:00
def unpack_archive(archive_path):
utils.log('Unpacking archive ("%s")...' % archive_path)
2019-04-21 22:15:17 -05:00
archive_name = os.path.basename(archive_path)
extension = archive_name[archive_name.find('.'):]
2019-04-21 22:15:17 -05:00
if extension in (".tar.gz", ".tar.bz2"):
import tarfile
import stat
2019-04-21 22:15:17 -05:00
mode = os.path.splitext(extension)[1][1:]
tar = tarfile.open(archive_path, 'r:%s' % mode)
for tarinfo in tar:
2019-04-21 22:15:17 -05:00
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)
2019-04-21 22:15:17 -05:00
f = os.path.join(os.curdir, tarinfo.name)
os.chmod(f, stat.S_IWRITE)
os.utime(f, (tarinfo.mtime, tarinfo.mtime))
tar.close()
2019-04-21 22:15:17 -05:00
elif extension in (".zip"):
import zipfile
2019-04-21 22:15:17 -05:00
z = zipfile.ZipFile(archive_path, 'r', zipfile.ZIP_DEFLATED)
for f in z.infolist():
2019-04-21 22:15:17 -05:00
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
2019-04-21 22:15:17 -05:00
@staticmethod
def make_file(filename, *text):
2019-04-21 22:15:17 -05:00
text = '\n'.join(text)
with codecs.open(filename, 'w', 'utf-8') as f:
f.write(text)
@staticmethod
def append_file(filename, *text):
2019-04-21 22:15:17 -05:00
with codecs.open(filename, 'a', 'utf-8') as f:
f.write('\n'.join(text))
@staticmethod
def mem_info():
if sys.platform == "darwin":
2019-04-21 22:15:17 -05:00
utils.call("top", "-l", "1", "-s", "0", "-n", "0")
elif sys.platform.startswith("linux"):
2019-04-21 22:15:17 -05:00
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
2019-04-21 22:15:17 -05:00
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
2019-04-21 22:15:17 -05:00
@staticmethod
2019-04-21 22:15:17 -05:00
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.
'''
2018-08-25 23:36:20 -05:00
if not repo_dir:
repo_dir = os.path.join(os.getcwd(), owner+','+repo)
utils.makedirs(os.path.dirname(repo_dir))
2019-04-21 22:15:17 -05:00
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)
2018-08-25 23:36:20 -05:00
os.chdir(repo_dir)
else:
2018-08-25 23:36:20 -05:00
os.chdir(repo_dir)
2019-04-21 22:15:17 -05:00
utils.check_call("git", "pull",
# "--depth=1", # Can't do depth as we get merge errors.
"--quiet", "--no-recurse-submodules")
if commit:
2019-04-21 22:15:17 -05:00
utils.check_call("git", "checkout", "-qf", commit)
if os.path.exists(os.path.join('.git', 'modules')):
if sys.platform == 'win32':
2019-04-21 22:15:17 -05:00
utils.check_call('dir', os.path.join('.git', 'modules'))
else:
2019-04-21 22:15:17 -05:00
utils.check_call('ls', '-la', os.path.join('.git', 'modules'))
2018-08-25 23:36:20 -05:00
if submodules:
2019-04-21 22:15:17 -05:00
utils.check_call("git", "submodule", "--quiet", "update",
"--quiet", "--init", "--recursive",
)
utils.check_call("git", "submodule", "--quiet",
"foreach", "git", "fetch")
2018-08-25 23:36:20 -05:00
return repo_dir
2019-04-21 22:15:17 -05:00
class parallel_call(threading.Thread):
'''
Runs a synchronous command in a thread waiting for it to complete.
'''
2019-04-21 22:15:17 -05:00
def __init__(self, *command, **kargs):
2019-04-21 22:15:17 -05:00
super(parallel_call, self).__init__()
self.command = command
self.command_kargs = kargs
self.start()
2019-04-21 22:15:17 -05:00
def run(self):
self.result = utils.call(*self.command, **self.command_kargs)
2019-04-21 22:15:17 -05:00
def join(self):
2019-04-21 22:15:17 -05:00
super(parallel_call, self).join()
if self.result != 0:
raise(SystemCallError(self.command, self.result))
2019-04-21 22:15:17 -05:00
def set_arg(args, k, v=None):
if not args.get(k):
args[k] = v
return args[k]
2019-04-21 22:15:17 -05:00
class script_common(object):
'''
2018-08-25 23:36:20 -05:00
Main script to run continuous integration.
'''
def __init__(self, ci_klass, **kargs):
self.ci = ci_klass(self)
opt = optparse.OptionParser(
usage="%prog [options] [commands]")
2019-04-21 22:15:17 -05:00
# ~ 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')
2019-04-21 22:15:17 -05:00
kargs = self.init(opt, kargs)
kargs = self.ci.init(opt, kargs)
2019-04-21 22:15:17 -05:00
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
2019-04-21 22:15:17 -05:00
for (k, v) in kargs.items():
setattr(self, k, v)
(_opt_, self.actions) = opt.parse_args(None, self)
if not self.actions or self.actions == []:
2019-04-21 22:15:17 -05:00
self.actions = kargs.get('actions', None)
if not self.actions or self.actions == []:
2019-04-21 22:15:17 -05:00
self.actions = ['info']
2018-08-25 23:36:20 -05:00
if not self.repo_dir:
self.repo_dir = os.getcwd()
self.build_dir = os.path.join(os.path.dirname(self.repo_dir), "build")
2019-04-21 22:15:17 -05:00
# API keys.
self.bintray_key = os.getenv('BINTRAY_KEY')
try:
self.start()
self.command_info()
self.main()
utils.print_call_stats()
2019-04-21 22:15:17 -05:00
except Exception as e:
utils.print_call_stats()
2019-04-21 22:15:17 -05:00
# raise
utils.log(str(e))
exit(1)
def init(self, opt, kargs):
return kargs
2019-04-21 22:15:17 -05:00
def start(self):
pass
def main(self):
for action in self.actions:
2019-04-21 22:15:17 -05:00
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:
2019-04-21 22:15:17 -05:00
utils.log("### %s.." % (action))
2018-08-25 23:36:20 -05:00
if os.path.exists(self.repo_dir):
os.chdir(self.repo_dir)
if ci_command:
ci_command()
elif ci_script:
ci_script()
2019-04-21 22:15:17 -05:00
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..
2019-04-21 22:15:17 -05:00
def command_info(self):
pass
2019-04-21 22:15:17 -05:00
def command_install(self):
utils.makedirs(self.build_dir)
os.chdir(self.build_dir)
2019-04-21 22:15:17 -05:00
def command_install_toolset(self, toolset):
2019-04-21 22:15:17 -05:00
if self.ci and hasattr(self.ci, 'install_toolset'):
self.ci.install_toolset(toolset)
2019-04-21 22:15:17 -05:00
def command_before_build(self):
pass
def command_build(self):
pass
def command_before_cache(self):
pass
def command_after_success(self):
pass
2019-04-21 22:15:17 -05:00
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.
2019-04-21 22:15:17 -05:00
The common way to use this variant is to invoke something like:
2019-04-21 22:15:17 -05:00
2018-08-25 23:36:20 -05:00
mkdir ci
cd ci
python path-to/library_test.py --branch=develop [--repo=mylib] ...
2019-04-21 22:15:17 -05:00
Status: In working order.
'''
2019-04-21 22:15:17 -05:00
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
2018-08-25 23:36:20 -05:00
self.repo_dir = os.getcwd()
self.exit_result = 0
2019-04-21 22:15:17 -05:00
def init(self, opt, kargs):
kargs['actions'] = [
2018-08-25 23:36:20 -05:00
# 'clone',
'install',
'before_build',
'build',
'before_cache',
'finish'
2019-04-21 22:15:17 -05:00
]
return kargs
2019-04-21 22:15:17 -05:00
def finish(self, result):
self.exit_result = result
2019-04-21 22:15:17 -05:00
def command_finish(self):
exit(self.exit_result)
2019-04-21 22:15:17 -05:00
class ci_travis(object):
'''
This variant build releases in the context of the Travis-CI service.
'''
2019-04-21 22:15:17 -05:00
def __init__(self, script):
self.script = script
2019-04-21 22:15:17 -05:00
def init(self, opt, kargs):
2019-04-21 22:15:17 -05:00
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
2019-04-21 22:15:17 -05:00
def finish(self, result):
exit(result)
2019-04-21 22:15:17 -05:00
def install_toolset(self, toolset):
'''
Installs specific toolset on CI system.
'''
info = toolset_info[toolset]
if sys.platform.startswith('linux'):
2018-08-25 23:36:20 -05:00
os.chdir(self.script.build_dir)
if 'ppa' in info:
for ppa in info['ppa']:
utils.check_call(
2019-04-21 22:15:17 -05:00
'sudo', 'add-apt-repository', '--yes', ppa)
if 'deb' in info:
utils.make_file('sources.list',
2019-04-21 22:15:17 -05:00
"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']:
2019-04-21 22:15:17 -05:00
utils.check_call('wget', key, '-O', 'apt.key')
utils.check_call('sudo', 'apt-key', 'add', 'apt.key')
utils.check_call(
2019-04-21 22:15:17 -05:00
'sudo', 'apt-get', 'update', '-qq')
utils.check_call(
2019-04-21 22:15:17 -05:00
'sudo', 'apt-get', 'install', '-qq', info['package'])
if 'debugpackage' in info and info['debugpackage']:
utils.check_call(
2019-04-21 22:15:17 -05:00
'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.
2019-04-21 22:15:17 -05:00
def command_before_install(self):
pass
2019-04-21 22:15:17 -05:00
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
2019-04-21 22:15:17 -05:00
class ci_circleci(object):
'''
This variant build releases in the context of the CircleCI service.
'''
2019-04-21 22:15:17 -05:00
def __init__(self, script):
self.script = script
2019-04-21 22:15:17 -05:00
def init(self, opt, kargs):
2019-04-21 22:15:17 -05:00
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
2019-04-21 22:15:17 -05:00
def finish(self, result):
exit(result)
2019-04-21 22:15:17 -05:00
def command_machine_post(self):
# Apt update for the pckages installs we'll do later.
2019-04-21 22:15:17 -05:00
utils.check_call('sudo', 'apt-get', '-qq', 'update')
# Need PyYAML to read Travis yaml in a later step.
2019-04-21 22:15:17 -05:00
utils.check_call("pip", "install", "--user", "PyYAML")
def command_checkout_post(self):
2018-08-25 23:36:20 -05:00
os.chdir(self.script.repo_dir)
2019-04-21 22:15:17 -05:00
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
2019-04-21 22:15:17 -05:00
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)
2019-04-21 22:15:17 -05:00
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()
2019-04-21 22:15:17 -05:00
def command_dependencies_post(self):
pass
2019-04-21 22:15:17 -05:00
def command_database_pre(self):
pass
2019-04-21 22:15:17 -05:00
def command_database_override(self):
pass
2019-04-21 22:15:17 -05:00
def command_database_post(self):
pass
2019-04-21 22:15:17 -05:00
def command_test_pre(self):
self.script.command_install()
self.script.command_before_build()
2019-04-21 22:15:17 -05:00
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()
2019-04-21 22:15:17 -05:00
def command_test_post(self):
pass
2019-04-21 22:15:17 -05:00
class ci_appveyor(object):
2019-04-21 22:15:17 -05:00
def __init__(self, script):
self.script = script
2019-04-21 22:15:17 -05:00
def init(self, opt, kargs):
2019-04-21 22:15:17 -05:00
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
2019-04-21 22:15:17 -05:00
def finish(self, result):
exit(result)
2019-04-21 22:15:17 -05:00
# Appveyor commands in the order they are executed. We need
# these to forward to our common commands, if they are different.
2019-04-21 22:15:17 -05:00
def command_install(self):
self.script.command_install()
2019-04-21 22:15:17 -05:00
def command_before_build(self):
2018-08-25 23:36:20 -05:00
os.chdir(self.script.repo_dir)
2019-04-21 22:15:17 -05:00
utils.check_call("git", "submodule", "update",
"--quiet", "--init", "--recursive")
self.script.command_before_build()
2019-04-21 22:15:17 -05:00
def command_build_script(self):
self.script.command_build()
2019-04-21 22:15:17 -05:00
def command_after_build(self):
self.script.command_before_cache()
2019-04-21 22:15:17 -05:00
def command_before_test(self):
pass
2019-04-21 22:15:17 -05:00
def command_test_script(self):
pass
2019-04-21 22:15:17 -05:00
def command_after_test(self):
pass
2019-04-21 22:15:17 -05:00
def command_on_success(self):
self.script.command_after_success()
2019-04-21 22:15:17 -05:00
def command_on_failure(self):
pass
2019-04-21 22:15:17 -05:00
def command_on_finish(self):
pass
2019-04-21 22:15:17 -05:00
class ci_azp(object):
'''
This variant build releases in the context of the Azure Pipelines service.
'''
def __init__(self, script):
self.script = script
def init(self, opt, kargs):
set_arg(kargs, 'repo_dir', os.getenv("AZP_REPO_DIR"))
set_arg(kargs, 'branch', os.getenv("AZP_BRANCH"))
set_arg(kargs, 'commit', os.getenv("AZP_COMMIT"))
set_arg(kargs, 'repo', os.getenv("AZP_REPO"))
set_arg(kargs, 'pull_request', os.getenv('AZP_PULL_REQUEST'))
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.check_call(
'sudo', '-E', 'apt-add-repository',
'deb %s' % (' '.join(info['deb'])))
if 'apt-key' in info:
for key in info['apt-key']:
utils.check_call('wget', key, '-O', 'apt.key')
utils.check_call('sudo', '-E', 'apt-key', 'add', 'apt.key')
utils.check_call(
'sudo', '-E', 'apt-get', 'update')
utils.check_call(
'sudo', '-E', 'apt-get', '-yq', '--no-install-suggests',
'--no-install-recommends', 'install', info['package'])
if 'debugpackage' in info and info['debugpackage']:
utils.check_call(
'sudo', 'apt-get', '-yq', '--no-install-suggests',
'--no-install-recommends', 'install', info['debugpackage'])
def command_install(self):
self.script.command_install()
self.script.command_before_build()
def command_script(self):
self.script.command_build()
self.script.command_before_cache()
self.script.command_after_success()
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)
2019-04-21 22:15:17 -05:00
elif os.getenv('AZP', False):
script_klass(ci_azp)
else:
script_klass(ci_cli)