| #!/usr/bin/env python3 |
| |
| # Copyright 2012-2017 The Meson development team |
| |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| import os |
| import sys |
| import time |
| import shutil |
| import subprocess |
| import tempfile |
| import platform |
| from mesonbuild import mesonlib |
| from mesonbuild import mesonmain |
| from mesonbuild import mlog |
| from mesonbuild.environment import detect_ninja |
| from io import StringIO |
| from enum import Enum |
| from glob import glob |
| |
| Backend = Enum('Backend', 'ninja vs xcode') |
| |
| if 'MESON_EXE' in os.environ: |
| import shlex |
| meson_exe = shlex.split(os.environ['MESON_EXE']) |
| else: |
| meson_exe = None |
| |
| if mesonlib.is_windows() or mesonlib.is_cygwin(): |
| exe_suffix = '.exe' |
| else: |
| exe_suffix = '' |
| |
| def get_backend_args_for_dir(backend, builddir): |
| ''' |
| Visual Studio backend needs to be given the solution to build |
| ''' |
| if backend is Backend.vs: |
| sln_name = glob(os.path.join(builddir, '*.sln'))[0] |
| return [os.path.split(sln_name)[-1]] |
| return [] |
| |
| def find_vcxproj_with_target(builddir, target): |
| import re, fnmatch |
| t, ext = os.path.splitext(target) |
| if ext: |
| p = '<TargetName>{}</TargetName>\s*<TargetExt>\{}</TargetExt>'.format(t, ext) |
| else: |
| p = '<TargetName>{}</TargetName>'.format(t) |
| for root, dirs, files in os.walk(builddir): |
| for f in fnmatch.filter(files, '*.vcxproj'): |
| f = os.path.join(builddir, f) |
| with open(f, 'r', encoding='utf-8') as o: |
| if re.search(p, o.read(), flags=re.MULTILINE): |
| return f |
| raise RuntimeError('No vcxproj matching {!r} in {!r}'.format(p, builddir)) |
| |
| def get_builddir_target_args(backend, builddir, target): |
| dir_args = [] |
| if not target: |
| dir_args = get_backend_args_for_dir(backend, builddir) |
| if target is None: |
| return dir_args |
| if backend is Backend.vs: |
| vcxproj = find_vcxproj_with_target(builddir, target) |
| target_args = [vcxproj] |
| elif backend is Backend.xcode: |
| target_args = ['-target', target] |
| elif backend is Backend.ninja: |
| target_args = [target] |
| else: |
| raise AssertionError('Unknown backend: {!r}'.format(backend)) |
| return target_args + dir_args |
| |
| def get_backend_commands(backend, debug=False): |
| install_cmd = [] |
| uninstall_cmd = [] |
| if backend is Backend.vs: |
| cmd = ['msbuild'] |
| clean_cmd = cmd + ['/target:Clean'] |
| test_cmd = cmd + ['RUN_TESTS.vcxproj'] |
| elif backend is Backend.xcode: |
| cmd = ['xcodebuild'] |
| clean_cmd = cmd + ['-alltargets', 'clean'] |
| test_cmd = cmd + ['-target', 'RUN_TESTS'] |
| elif backend is Backend.ninja: |
| # We need at least 1.6 because of -w dupbuild=err |
| cmd = [detect_ninja('1.6'), '-w', 'dupbuild=err', '-d', 'explain'] |
| if cmd[0] is None: |
| raise RuntimeError('Could not find Ninja v1.6 or newer') |
| if debug: |
| cmd += ['-v'] |
| clean_cmd = cmd + ['clean'] |
| test_cmd = cmd + ['test', 'benchmark'] |
| install_cmd = cmd + ['install'] |
| uninstall_cmd = cmd + ['uninstall'] |
| else: |
| raise AssertionError('Unknown backend: {!r}'.format(backend)) |
| return cmd, clean_cmd, test_cmd, install_cmd, uninstall_cmd |
| |
| def ensure_backend_detects_changes(backend): |
| # We're using a ninja with QuLogic's patch for sub-1s resolution timestamps |
| # and not running on HFS+ which only stores dates in seconds: |
| # https://developer.apple.com/legacy/library/technotes/tn/tn1150.html#HFSPlusDates |
| # FIXME: Upgrade Travis image to Apple FS when that becomes available |
| if 'MESON_FIXED_NINJA' in os.environ and not mesonlib.is_osx(): |
| return |
| # This is needed to increase the difference between build.ninja's |
| # timestamp and the timestamp of whatever you changed due to a Ninja |
| # bug: https://github.com/ninja-build/ninja/issues/371 |
| if backend is Backend.ninja: |
| time.sleep(1) |
| |
| def get_fake_options(prefix): |
| import argparse |
| opts = argparse.Namespace() |
| opts.cross_file = None |
| opts.wrap_mode = None |
| opts.prefix = prefix |
| return opts |
| |
| def should_run_linux_cross_tests(): |
| return shutil.which('arm-linux-gnueabihf-gcc-7') and not platform.machine().lower().startswith('arm') |
| |
| def run_configure_inprocess(meson_command, commandlist): |
| old_stdout = sys.stdout |
| sys.stdout = mystdout = StringIO() |
| old_stderr = sys.stderr |
| sys.stderr = mystderr = StringIO() |
| try: |
| returncode = mesonmain.run(commandlist, meson_command) |
| finally: |
| sys.stdout = old_stdout |
| sys.stderr = old_stderr |
| return returncode, mystdout.getvalue(), mystderr.getvalue() |
| |
| def run_configure_external(full_command): |
| pc, o, e = mesonlib.Popen_safe(full_command) |
| return pc.returncode, o, e |
| |
| def run_configure(meson_command, commandlist): |
| global meson_exe |
| if meson_exe: |
| return run_configure_external(meson_exe + commandlist) |
| return run_configure_inprocess(meson_command, commandlist) |
| |
| def print_system_info(): |
| print(mlog.bold('System information.').get_text(mlog.colorize_console)) |
| print('Architecture:', platform.architecture()) |
| print('Machine:', platform.machine()) |
| print('Platform:', platform.system()) |
| print('Processor:', platform.processor()) |
| print('System:', platform.system()) |
| print('') |
| |
| if __name__ == '__main__': |
| print_system_info() |
| # Enable coverage early... |
| enable_coverage = '--cov' in sys.argv |
| if enable_coverage: |
| os.makedirs('.coverage', exist_ok=True) |
| sys.argv.remove('--cov') |
| import coverage |
| coverage.process_startup() |
| returncode = 0 |
| # Iterate over list in reverse order to find the last --backend arg |
| backend = Backend.ninja |
| for arg in reversed(sys.argv[1:]): |
| if arg.startswith('--backend'): |
| if arg.startswith('--backend=vs'): |
| backend = Backend.vs |
| elif arg == '--backend=xcode': |
| backend = Backend.xcode |
| break |
| # Running on a developer machine? Be nice! |
| if not mesonlib.is_windows() and not mesonlib.is_haiku() and 'TRAVIS' not in os.environ: |
| os.nice(20) |
| # Appveyor sets the `platform` environment variable which completely messes |
| # up building with the vs2010 and vs2015 backends. |
| # |
| # Specifically, MSBuild reads the `platform` environment variable to set |
| # the configured value for the platform (Win32/x64/arm), which breaks x86 |
| # builds. |
| # |
| # Appveyor setting this also breaks our 'native build arch' detection for |
| # Windows in environment.py:detect_windows_arch() by overwriting the value |
| # of `platform` set by vcvarsall.bat. |
| # |
| # While building for x86, `platform` should be unset. |
| if 'APPVEYOR' in os.environ and os.environ['arch'] == 'x86': |
| os.environ.pop('platform') |
| # Run tests |
| print(mlog.bold('Running unittests.').get_text(mlog.colorize_console)) |
| print() |
| # Can't pass arguments to unit tests, so set the backend to use in the environment |
| env = os.environ.copy() |
| env['MESON_UNIT_TEST_BACKEND'] = backend.name |
| with tempfile.TemporaryDirectory() as td: |
| # Enable coverage on all subsequent processes. |
| if enable_coverage: |
| with open(os.path.join(td, 'usercustomize.py'), 'w') as f: |
| f.write('import coverage\n' |
| 'coverage.process_startup()\n') |
| env['COVERAGE_PROCESS_START'] = '.coveragerc' |
| env['PYTHONPATH'] = os.pathsep.join([td] + env.get('PYTHONPATH', [])) |
| returncode += subprocess.call(mesonlib.python_command + ['run_unittests.py', '-v'], env=env) |
| # Ubuntu packages do not have a binary without -6 suffix. |
| if should_run_linux_cross_tests(): |
| print(mlog.bold('Running cross compilation tests.').get_text(mlog.colorize_console)) |
| print() |
| returncode += subprocess.call(mesonlib.python_command + ['run_cross_test.py', 'cross/ubuntu-armhf.txt'], |
| env=env) |
| returncode += subprocess.call(mesonlib.python_command + ['run_project_tests.py'] + sys.argv[1:], env=env) |
| sys.exit(returncode) |