diff --git a/support/biicode-build.py b/support/biicode-build.py index e3939a83..20846456 100755 --- a/support/biicode-build.py +++ b/support/biicode-build.py @@ -1,12 +1,15 @@ #!/usr/bin/env python # Build the project with Biicode. -import glob, os, shutil +import bootstrap, glob, os, shutil from download import Downloader from subprocess import check_call os_name = os.environ['TRAVIS_OS_NAME'] if os_name == 'linux': + # Install newer version of CMake. + bootstrap.install_cmake( + 'cmake-3.1.1-Linux-i386.tar.gz', check_installed=False, download_dir=None, install_dir='.') with Downloader().download('http://www.biicode.com/downloads/latest/ubuntu64') as f: check_call(['sudo', 'dpkg', '-i', f]) elif os_name == 'osx': @@ -16,7 +19,8 @@ elif os_name == 'osx': project_dir = 'biicode_project' check_call(['bii', 'init', project_dir]) cppformat_dir = os.path.join(project_dir, 'blocks/vitaut/cppformat') -shutil.copytree('.', cppformat_dir, ignore=shutil.ignore_patterns('biicode_project')) +shutil.copytree('.', cppformat_dir, + ignore=shutil.ignore_patterns('biicode_project')) for f in glob.glob('support/biicode/*'): shutil.copy(f, cppformat_dir) check_call(['bii', 'cpp:build'], cwd=project_dir) diff --git a/support/bootstrap.py b/support/bootstrap.py new file mode 100644 index 00000000..3cab6e11 --- /dev/null +++ b/support/bootstrap.py @@ -0,0 +1,195 @@ +# Common bootstrap functionality. + +from __future__ import print_function +import glob, os, platform, re, shutil, sys +import tarfile, tempfile, uuid, zipfile +from contextlib import closing +from subprocess import check_call, call + +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +import timer +from download import Downloader + +windows = platform.system() == 'Windows' +opt_dir = r'\Program Files' if windows else '/opt' + +# Downloads to a temporary file in the current directory. +# When running in a Vagrant VM, the current directory is in a synced folder so +# this reduces VM growth. +def download(url, cookie=None): + return Downloader('.').download(url, cookie); + +# If we are in a VM managed by Vagrant, then do everything in the shared +# /vagrant directory to avoid growth of the VM drive. +vagrant_dir = '/vagrant' +def bootstrap_init(): + if os.path.exists(vagrant_dir): + os.chdir(vagrant_dir) + return True + return False + +# Returns executable location. +def which(name): + filename = name + '.exe' if windows else name + for path in os.environ['PATH'].split(os.pathsep): + path = os.path.join(path.strip('"'), filename) + if os.path.isfile(path) and os.access(path, os.X_OK): + return path + return None + +# Returns true if executable is installed on the path. +def installed(name): + path = which(name) + if path: + print(name, 'is installed in', path) + return path != None + +def create_symlink(filename, linkname): + if not os.path.exists(linkname): + print('Creating a symlink from', linkname, 'to', filename) + os.symlink(filename, linkname) + else: + print('File already exists:', linkname) + +# Adds path to search paths. +def add_to_path(path, linkname=None, isdir=False): + paths = os.environ['PATH'].split(os.pathsep) + dir = path if isdir else os.path.dirname(path) + print('Adding', dir, 'to PATH') + if isdir: + paths.insert(0, dir) + else: + paths.append(dir) + os.environ['PATH'] = os.pathsep.join(paths) + if windows: + check_call(['setx', '/m', 'PATH', os.environ['PATH']]) + if windows or isdir: + return + dir = '/usr/local/bin' + # Create /usr/local/bin directory if it doesn't exist which can happen + # on OS X. + if not os.path.exists(dir): + os.makedirs(dir) + linkname = os.path.join(dir, linkname or os.path.basename(path)) + create_symlink(path, linkname) + +# Downloads and installs CMake. +# package: The name of a CMake package, +# e.g. 'cmake-2.8.12.2-Linux-i386.tar.gz'. +def install_cmake(package, **kwargs): + if kwargs.get('check_installed', True) and installed('cmake'): + return + dir, version, minor = re.match( + r'(cmake-(\d+\.\d+)\.(\d+).*-[^\.]+)\..*', package).groups() + # extractall overwrites existing files, so no need to prepare the + # destination. + url = 'http://www.cmake.org/files/v{0}/{1}'.format(version, package) + install_dir = kwargs.get('install_dir', opt_dir) + with Downloader(kwargs.get('download_dir', '.')).download(url) as f: + iszip = package.endswith('zip') + with zipfile.ZipFile(f) if iszip \ + else closing(tarfile.open(f, 'r:gz')) as archive: + archive.extractall(install_dir) + dir = os.path.join(install_dir, dir) + if platform.system() == 'Darwin': + dir = glob.glob(os.path.join(dir, 'CMake*.app', 'Contents'))[0] + cmake_path = os.path.join(dir, 'bin', 'cmake') + if install_dir != '.': + add_to_path(cmake_path) + return cmake_path + +# Install f90cache. +def install_f90cache(): + if not installed('f90cache'): + f90cache = 'f90cache-0.95' + tempdir = tempfile.mkdtemp(suffix='f90cache', dir='') + with download( + 'http://people.irisa.fr/Edouard.Canot/f90cache/' + + f90cache + '.tar.bz2', False) as f: + with closing(tarfile.open(f, "r:bz2")) as archive: + archive.extractall(tempdir) + f90cache_dir = os.path.join(tempdir, f90cache) + check_call(['sh', 'configure'], cwd=f90cache_dir) + check_call(['make', 'all', 'install'], cwd=f90cache_dir) + shutil.rmtree(tempdir) + +# Returns true iff module exists. +def module_exists(module): + try: + __import__(module) + return True + except ImportError: + return False + +# Install package using pip if it hasn't been installed already. +def pip_install(package, test_module=None): + if not test_module: + test_module = package + if module_exists(test_module): + return + # If pip doesn't exist install it first. + if not module_exists('pip'): + with download('https://bootstrap.pypa.io/get-pip.py') as f: + check_call(['python', f]) + # Install the package. + print('Installing', package) + check_call(['pip', 'install', package]) + +# Installs buildbot slave. +def install_buildbot_slave(name, path=None, script_dir='', shell=False, **args): + username = 'vagrant' + if platform.system() == 'Linux': + # Create buildbot user if it doesn't exist. + username = 'buildbot' + import pwd + try: + pwd.getpwnam(username) + except KeyError: + check_call(['sudo', 'useradd', '--system', '--home', '/var/lib/buildbot', + '--create-home', '--shell', '/bin/false', 'buildbot']) + path = path or os.path.expanduser('~{0}/slave'.format(username)) + if os.path.exists(path): + return + pip_install('buildbot-slave', 'buildbot') + # The password is insecure but it doesn't matter as the buildslaves are + # not publicly accessible. + command = [os.path.join(script_dir, 'buildslave'), + 'create-slave', path, args.get('ip', '10.0.2.2'), name, 'pass'] + if not windows: + command = ['sudo', '-u', username] + command + check_call(command, shell=shell) + if windows: + return + if args.get('nocron', False): + return + pip_install('python-crontab', 'crontab') + from crontab import CronTab + cron = CronTab(username) + cron.new('PATH={0}:/usr/local/bin buildslave start {1}'.format( + os.environ['PATH'], path)).every_reboot() + cron.write() + # Ignore errors from buildslave as the buildbot may not be accessible. + call(['sudo', '-H', '-u', username, 'buildslave', 'start', path]) + +# Copies optional dependencies from opt/ to /opt. +def copy_optional_dependencies(platform): + if os.path.exists('opt'): + for src in glob.glob('opt/' + platform + '/*'): + dest = '/opt/' + os.path.basename(src) + if not os.path.exists(dest): + shutil.copytree(src, dest, symlinks=True) + +# Installs an OS X package. +def install_pkg(filename): + print('Installing', filename) + check_call(['sudo', 'installer', '-pkg', filename, '-target', '/']) + +# Installs a package from a .dmg file. +def install_dmg(filename): + dir = tempfile.mkdtemp() + check_call(['hdiutil', 'attach', filename, '-mountpoint', dir]) + install_pkg(glob.glob(dir + '/*pkg')[0]) + check_call(['hdiutil', 'detach', dir]) + os.rmdir(dir) + +LOCALSOLVER_VERSION = '4_5_20140718'