| #! /usr/bin/env python |
| |
| from __future__ import print_function |
| |
| import argparse |
| import os |
| import signal |
| import subprocess |
| import sys |
| |
| if sys.platform == 'win32': |
| # This module was renamed in Python 3. Make sure to import it using a |
| # consistent name regardless of python version. |
| try: |
| import winreg |
| except: |
| import _winreg as winreg |
| |
| if __name__ != "__main__": |
| raise RuntimeError("Do not import this script, run it instead") |
| |
| |
| parser = argparse.ArgumentParser(description='LLDB compilation wrapper') |
| parser.add_argument('--arch', |
| metavar='arch', |
| dest='arch', |
| required=True, |
| default='host', |
| choices=['32', '64', 'host'], |
| help='Specify the architecture to target.') |
| |
| parser.add_argument('--compiler', |
| metavar='compiler', |
| dest='compiler', |
| required=True, |
| help='Path to a compiler executable, or one of the values [any, msvc, clang-cl, gcc, clang]') |
| |
| parser.add_argument('--tools-dir', |
| metavar='directory', |
| dest='tools_dir', |
| required=False, |
| action='append', |
| help='If specified, a path to search in addition to PATH when --compiler is not an exact path') |
| |
| if sys.platform == 'darwin': |
| parser.add_argument('--apple-sdk', |
| metavar='apple_sdk', |
| dest='apple_sdk', |
| default="macosx", |
| help='Specify the name of the Apple SDK (macosx, macosx.internal, iphoneos, iphoneos.internal, or path to SDK) and use the appropriate tools from that SDK\'s toolchain.') |
| |
| parser.add_argument('--output', '-o', |
| dest='output', |
| metavar='file', |
| required=False, |
| default='', |
| help='Path to output file') |
| |
| parser.add_argument('--outdir', '-d', |
| dest='outdir', |
| metavar='directory', |
| required=False, |
| help='Directory for output files') |
| |
| parser.add_argument('--nodefaultlib', |
| dest='nodefaultlib', |
| action='store_true', |
| default=False, |
| help='When specified, the resulting image should not link against system libraries or include system headers. Useful when writing cross-targeting tests.') |
| |
| parser.add_argument('--opt', |
| dest='opt', |
| default='none', |
| choices=['none', 'basic', 'lto'], |
| help='Optimization level') |
| |
| parser.add_argument('--mode', |
| dest='mode', |
| default='compile-and-link', |
| choices=['compile', 'link', 'compile-and-link'], |
| help='Specifies whether to compile, link, or both') |
| |
| parser.add_argument('--noclean', |
| dest='clean', |
| action='store_false', |
| default=True, |
| help='Dont clean output file before building') |
| |
| parser.add_argument('--verbose', |
| dest='verbose', |
| action='store_true', |
| default=False, |
| help='Print verbose output') |
| |
| parser.add_argument('-n', '--dry-run', |
| dest='dry', |
| action='store_true', |
| default=False, |
| help='Print the commands that would run, but dont actually run them') |
| |
| parser.add_argument('inputs', |
| metavar='file', |
| nargs='+', |
| help='Source file(s) to compile / object file(s) to link') |
| |
| |
| args = parser.parse_args(args=sys.argv[1:]) |
| |
| |
| def to_string(b): |
| """Return the parameter as type 'str', possibly encoding it. |
| |
| In Python2, the 'str' type is the same as 'bytes'. In Python3, the |
| 'str' type is (essentially) Python2's 'unicode' type, and 'bytes' is |
| distinct. |
| |
| This function is copied from llvm/utils/lit/lit/util.py |
| """ |
| if isinstance(b, str): |
| # In Python2, this branch is taken for types 'str' and 'bytes'. |
| # In Python3, this branch is taken only for 'str'. |
| return b |
| if isinstance(b, bytes): |
| # In Python2, this branch is never taken ('bytes' is handled as 'str'). |
| # In Python3, this is true only for 'bytes'. |
| try: |
| return b.decode('utf-8') |
| except UnicodeDecodeError: |
| # If the value is not valid Unicode, return the default |
| # repr-line encoding. |
| return str(b) |
| |
| # By this point, here's what we *don't* have: |
| # |
| # - In Python2: |
| # - 'str' or 'bytes' (1st branch above) |
| # - In Python3: |
| # - 'str' (1st branch above) |
| # - 'bytes' (2nd branch above) |
| # |
| # The last type we might expect is the Python2 'unicode' type. There is no |
| # 'unicode' type in Python3 (all the Python3 cases were already handled). In |
| # order to get a 'str' object, we need to encode the 'unicode' object. |
| try: |
| return b.encode('utf-8') |
| except AttributeError: |
| raise TypeError('not sure how to convert %s to %s' % (type(b), str)) |
| |
| def format_text(lines, indent_0, indent_n): |
| result = ' ' * indent_0 + lines[0] |
| for next in lines[1:]: |
| result = result + '\n{0}{1}'.format(' ' * indent_n, next) |
| return result |
| |
| def print_environment(env): |
| if env is None: |
| print(' Inherited') |
| return |
| for e in env: |
| value = env[e] |
| lines = value.split(os.pathsep) |
| formatted_value = format_text(lines, 0, 7 + len(e)) |
| print(' {0} = {1}'.format(e, formatted_value)) |
| |
| def find_executable(binary_name, search_paths): |
| if sys.platform == 'win32': |
| binary_name = binary_name + '.exe' |
| |
| search_paths = os.pathsep.join(search_paths) |
| paths = search_paths + os.pathsep + os.environ.get('PATH', '') |
| for path in paths.split(os.pathsep): |
| p = os.path.join(path, binary_name) |
| if os.path.exists(p) and not os.path.isdir(p): |
| return os.path.normpath(p) |
| return None |
| |
| def find_toolchain(compiler, tools_dir): |
| if compiler == 'msvc': |
| return ('msvc', find_executable('cl', tools_dir)) |
| if compiler == 'clang-cl': |
| return ('clang-cl', find_executable('clang-cl', tools_dir)) |
| if compiler == 'gcc': |
| return ('gcc', find_executable('g++', tools_dir)) |
| if compiler == 'clang': |
| return ('clang', find_executable('clang++', tools_dir)) |
| if compiler == 'any': |
| priorities = [] |
| if sys.platform == 'win32': |
| priorities = ['clang-cl', 'msvc', 'clang', 'gcc'] |
| else: |
| priorities = ['clang', 'gcc', 'clang-cl'] |
| for toolchain in priorities: |
| (type, dir) = find_toolchain(toolchain, tools_dir) |
| if type and dir: |
| return (type, dir) |
| # Could not find any toolchain. |
| return (None, None) |
| |
| # From here on, assume that |compiler| is a path to a file. |
| file = os.path.basename(compiler) |
| name, ext = os.path.splitext(file) |
| if file.lower() == 'cl.exe': |
| return 'msvc' |
| if name == 'clang-cl': |
| return 'clang-cl' |
| if name.startswith('clang'): |
| return 'clang' |
| if name.startswith('gcc') or name.startswith('g++'): |
| return 'gcc' |
| if name == 'cc' or name == 'c++': |
| return 'generic' |
| return 'unknown' |
| |
| class Builder(object): |
| def __init__(self, toolchain_type, args, obj_ext): |
| self.toolchain_type = toolchain_type |
| self.inputs = args.inputs |
| self.arch = args.arch |
| self.opt = args.opt |
| self.outdir = args.outdir |
| self.compiler = args.compiler |
| self.clean = args.clean |
| self.output = args.output |
| self.mode = args.mode |
| self.nodefaultlib = args.nodefaultlib |
| self.verbose = args.verbose |
| self.obj_ext = obj_ext |
| |
| def _exe_file_name(self): |
| assert self.mode != 'compile' |
| return self.output |
| |
| def _output_name(self, input, extension, with_executable=False): |
| basename = os.path.splitext(os.path.basename(input))[0] + extension |
| if with_executable: |
| exe_basename = os.path.basename(self._exe_file_name()) |
| basename = exe_basename + '-' + basename |
| |
| output = os.path.join(self.outdir, basename) |
| return os.path.normpath(output) |
| |
| def _obj_file_names(self): |
| if self.mode == 'link': |
| return self.inputs |
| |
| if self.mode == 'compile-and-link': |
| # Object file names should factor in both the input file (source) |
| # name and output file (executable) name, to ensure that two tests |
| # which share a common source file don't race to write the same |
| # object file. |
| return [self._output_name(x, self.obj_ext, True) for x in self.inputs] |
| |
| if self.mode == 'compile' and self.output: |
| return [self.output] |
| |
| return [self._output_name(x, self.obj_ext) for x in self.inputs] |
| |
| def build_commands(self): |
| commands = [] |
| if self.mode == 'compile' or self.mode == 'compile-and-link': |
| for input, output in zip(self.inputs, self._obj_file_names()): |
| commands.append(self._get_compilation_command(input, output)) |
| if self.mode == 'link' or self.mode == 'compile-and-link': |
| commands.append(self._get_link_command()) |
| return commands |
| |
| |
| class MsvcBuilder(Builder): |
| def __init__(self, toolchain_type, args): |
| Builder.__init__(self, toolchain_type, args, '.obj') |
| |
| self.msvc_arch_str = 'x86' if self.arch == '32' else 'x64' |
| |
| if toolchain_type == 'msvc': |
| # Make sure we're using the appropriate toolchain for the desired |
| # target type. |
| compiler_parent_dir = os.path.dirname(self.compiler) |
| selected_target_version = os.path.basename(compiler_parent_dir) |
| if selected_target_version != self.msvc_arch_str: |
| host_dir = os.path.dirname(compiler_parent_dir) |
| self.compiler = os.path.join(host_dir, self.msvc_arch_str, 'cl.exe') |
| if self.verbose: |
| print('Using alternate compiler "{0}" to match selected target.'.format(self.compiler)) |
| |
| if self.mode == 'link' or self.mode == 'compile-and-link': |
| self.linker = self._find_linker('link') if toolchain_type == 'msvc' else self._find_linker('lld-link', args.tools_dir) |
| if not self.linker: |
| raise ValueError('Unable to find an appropriate linker.') |
| |
| self.compile_env, self.link_env = self._get_visual_studio_environment() |
| |
| def _find_linker(self, name, search_paths=[]): |
| compiler_dir = os.path.dirname(self.compiler) |
| linker_path = find_executable(name, [compiler_dir] + search_paths) |
| if linker_path is None: |
| raise ValueError('Could not find \'{}\''.format(name)) |
| return linker_path |
| |
| def _get_vc_install_dir(self): |
| dir = os.getenv('VCINSTALLDIR', None) |
| if dir: |
| if self.verbose: |
| print('Using %VCINSTALLDIR% {}'.format(dir)) |
| return dir |
| |
| dir = os.getenv('VSINSTALLDIR', None) |
| if dir: |
| if self.verbose: |
| print('Using %VSINSTALLDIR% {}'.format(dir)) |
| return os.path.join(dir, 'VC') |
| |
| dir = os.getenv('VS2019INSTALLDIR', None) |
| if dir: |
| if self.verbose: |
| print('Using %VS2019INSTALLDIR% {}'.format(dir)) |
| return os.path.join(dir, 'VC') |
| |
| dir = os.getenv('VS2017INSTALLDIR', None) |
| if dir: |
| if self.verbose: |
| print('Using %VS2017INSTALLDIR% {}'.format(dir)) |
| return os.path.join(dir, 'VC') |
| |
| dir = os.getenv('VS2015INSTALLDIR', None) |
| if dir: |
| if self.verbose: |
| print('Using %VS2015INSTALLDIR% {}'.format(dir)) |
| return os.path.join(dir, 'VC') |
| return None |
| |
| def _get_vctools_version(self): |
| ver = os.getenv('VCToolsVersion', None) |
| if ver: |
| if self.verbose: |
| print('Using %VCToolsVersion% {}'.format(ver)) |
| return ver |
| |
| vcinstalldir = self._get_vc_install_dir() |
| vcinstalldir = os.path.join(vcinstalldir, 'Tools', 'MSVC') |
| subdirs = next(os.walk(vcinstalldir))[1] |
| if not subdirs: |
| return None |
| |
| from distutils.version import StrictVersion |
| subdirs.sort(key=lambda x : StrictVersion(x)) |
| |
| if self.verbose: |
| full_path = os.path.join(vcinstalldir, subdirs[-1]) |
| print('Using VC tools version directory {0} found by directory walk.'.format(full_path)) |
| return subdirs[-1] |
| |
| def _get_vctools_install_dir(self): |
| dir = os.getenv('VCToolsInstallDir', None) |
| if dir: |
| if self.verbose: |
| print('Using %VCToolsInstallDir% {}'.format(dir)) |
| return dir |
| |
| vcinstalldir = self._get_vc_install_dir() |
| if not vcinstalldir: |
| return None |
| vctoolsver = self._get_vctools_version() |
| if not vctoolsver: |
| return None |
| result = os.path.join(vcinstalldir, 'Tools', 'MSVC', vctoolsver) |
| if not os.path.exists(result): |
| return None |
| if self.verbose: |
| print('Using VC tools install dir {} found by directory walk'.format(result)) |
| return result |
| |
| def _find_windows_sdk_in_registry_view(self, view): |
| products_key = None |
| roots_key = None |
| installed_options_keys = [] |
| try: |
| sam = view | winreg.KEY_READ |
| products_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, |
| r'Software\Microsoft\Windows Kits\Installed Products', |
| 0, |
| sam) |
| |
| # This is the GUID for the desktop component. If this is present |
| # then the components required for the Desktop SDK are installed. |
| # If not it will throw an exception. |
| winreg.QueryValueEx(products_key, '{5A3D81EC-D870-9ECF-D997-24BDA6644752}') |
| |
| roots_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, |
| r'Software\Microsoft\Windows Kits\Installed Roots', |
| 0, |
| sam) |
| root_dir = winreg.QueryValueEx(roots_key, 'KitsRoot10') |
| root_dir = to_string(root_dir[0]) |
| sdk_versions = [] |
| index = 0 |
| while True: |
| # Installed SDK versions are stored as sub-keys of the |
| # 'Installed Roots' key. Find all of their names, then sort |
| # them by version |
| try: |
| ver_key = winreg.EnumKey(roots_key, index) |
| sdk_versions.append(ver_key) |
| index = index + 1 |
| except WindowsError: |
| break |
| if not sdk_versions: |
| return (None, None) |
| |
| # Windows SDK version numbers consist of 4 dotted components, so we |
| # have to use LooseVersion, as StrictVersion supports 3 or fewer. |
| from distutils.version import LooseVersion |
| sdk_versions.sort(key=lambda x : LooseVersion(x), reverse=True) |
| option_value_name = 'OptionId.DesktopCPP' + self.msvc_arch_str |
| for v in sdk_versions: |
| try: |
| version_subkey = v + r'\Installed Options' |
| key = winreg.OpenKey(roots_key, version_subkey) |
| installed_options_keys.append(key) |
| (value, value_type) = winreg.QueryValueEx(key, option_value_name) |
| if value == 1: |
| # The proper architecture is installed. Return the |
| # associated paths. |
| if self.verbose: |
| print('Found Installed Windows SDK v{0} at {1}'.format(v, root_dir)) |
| return (root_dir, v) |
| except: |
| continue |
| except: |
| return (None, None) |
| finally: |
| del products_key |
| del roots_key |
| for k in installed_options_keys: |
| del k |
| return (None, None) |
| |
| def _find_windows_sdk_in_registry(self): |
| # This could be a clang-cl cross-compile. If so, there's no registry |
| # so just exit. |
| if sys.platform != 'win32': |
| return (None, None) |
| if self.verbose: |
| print('Looking for Windows SDK in 64-bit registry.') |
| dir, ver = self._find_windows_sdk_in_registry_view(winreg.KEY_WOW64_64KEY) |
| if not dir or not ver: |
| if self.verbose: |
| print('Looking for Windows SDK in 32-bit registry.') |
| dir, ver = self._find_windows_sdk_in_registry_view(winreg.KEY_WOW64_32KEY) |
| |
| return (dir, ver) |
| |
| def _get_winsdk_dir(self): |
| # If a Windows SDK is specified in the environment, use that. Otherwise |
| # try to find one in the Windows registry. |
| dir = os.getenv('WindowsSdkDir', None) |
| if not dir or not os.path.exists(dir): |
| return self._find_windows_sdk_in_registry() |
| ver = os.getenv('WindowsSDKLibVersion', None) |
| if not ver: |
| return self._find_windows_sdk_in_registry() |
| |
| ver = ver.rstrip('\\') |
| if self.verbose: |
| print('Using %WindowsSdkDir% {}'.format(dir)) |
| print('Using %WindowsSDKLibVersion% {}'.format(ver)) |
| return (dir, ver) |
| |
| def _get_msvc_native_toolchain_dir(self): |
| assert self.toolchain_type == 'msvc' |
| compiler_dir = os.path.dirname(self.compiler) |
| target_dir = os.path.dirname(compiler_dir) |
| host_name = os.path.basename(target_dir) |
| host_name = host_name[4:].lower() |
| return os.path.join(target_dir, host_name) |
| |
| def _get_visual_studio_environment(self): |
| vctools = self._get_vctools_install_dir() |
| winsdk, winsdkver = self._get_winsdk_dir() |
| |
| if not vctools and self.verbose: |
| print('Unable to find VC tools installation directory.') |
| if (not winsdk or not winsdkver) and self.verbose: |
| print('Unable to find Windows SDK directory.') |
| |
| vcincludes = [] |
| vclibs = [] |
| sdkincludes = [] |
| sdklibs = [] |
| if vctools is not None: |
| includes = [['ATLMFC', 'include'], ['include']] |
| libs = [['ATLMFC', 'lib'], ['lib']] |
| vcincludes = [os.path.join(vctools, *y) for y in includes] |
| vclibs = [os.path.join(vctools, *y) for y in libs] |
| if winsdk is not None: |
| includes = [['include', winsdkver, 'ucrt'], |
| ['include', winsdkver, 'shared'], |
| ['include', winsdkver, 'um'], |
| ['include', winsdkver, 'winrt'], |
| ['include', winsdkver, 'cppwinrt']] |
| libs = [['lib', winsdkver, 'ucrt'], |
| ['lib', winsdkver, 'um']] |
| sdkincludes = [os.path.join(winsdk, *y) for y in includes] |
| sdklibs = [os.path.join(winsdk, *y) for y in libs] |
| |
| includes = vcincludes + sdkincludes |
| libs = vclibs + sdklibs |
| libs = [os.path.join(x, self.msvc_arch_str) for x in libs] |
| compileenv = None |
| linkenv = None |
| defaultenv = {} |
| if sys.platform == 'win32': |
| defaultenv = { x : os.environ[x] for x in |
| ['SystemDrive', 'SystemRoot', 'TMP', 'TEMP'] } |
| # The directory to mspdbcore.dll needs to be in PATH, but this is |
| # always in the native toolchain path, not the cross-toolchain |
| # path. So, for example, if we're using HostX64\x86 then we need |
| # to add HostX64\x64 to the path, and if we're using HostX86\x64 |
| # then we need to add HostX86\x86 to the path. |
| if self.toolchain_type == 'msvc': |
| defaultenv['PATH'] = self._get_msvc_native_toolchain_dir() |
| |
| if includes: |
| compileenv = {} |
| compileenv['INCLUDE'] = os.pathsep.join(includes) |
| compileenv.update(defaultenv) |
| if libs: |
| linkenv = {} |
| linkenv['LIB'] = os.pathsep.join(libs) |
| linkenv.update(defaultenv) |
| return (compileenv, linkenv) |
| |
| def _ilk_file_names(self): |
| if self.mode == 'link': |
| return [] |
| |
| return [self._output_name(x, '.ilk') for x in self.inputs] |
| |
| def _pdb_file_name(self): |
| if self.mode == 'compile': |
| return None |
| return os.path.splitext(self.output)[0] + '.pdb' |
| |
| def _get_compilation_command(self, source, obj): |
| args = [] |
| |
| args.append(self.compiler) |
| if self.toolchain_type == 'clang-cl': |
| args.append('-m' + self.arch) |
| |
| if self.opt == 'none': |
| args.append('/Od') |
| elif self.opt == 'basic': |
| args.append('/O2') |
| elif self.opt == 'lto': |
| if self.toolchain_type == 'msvc': |
| args.append('/GL') |
| args.append('/Gw') |
| else: |
| args.append('-flto=thin') |
| if self.nodefaultlib: |
| args.append('/GS-') |
| args.append('/GR-') |
| args.append('/Z7') |
| if self.toolchain_type == 'clang-cl': |
| args.append('-Xclang') |
| args.append('-fkeep-static-consts') |
| args.append('-fms-compatibility-version=19') |
| args.append('/c') |
| |
| args.append('/Fo' + obj) |
| args.append(source) |
| |
| return ('compiling', [source], obj, |
| self.compile_env, |
| args) |
| |
| def _get_link_command(self): |
| args = [] |
| args.append(self.linker) |
| args.append('/DEBUG:FULL') |
| args.append('/INCREMENTAL:NO') |
| if self.nodefaultlib: |
| args.append('/nodefaultlib') |
| args.append('/entry:main') |
| args.append('/PDB:' + self._pdb_file_name()) |
| args.append('/OUT:' + self._exe_file_name()) |
| args.extend(self._obj_file_names()) |
| |
| return ('linking', self._obj_file_names(), self._exe_file_name(), |
| self.link_env, |
| args) |
| |
| def build_commands(self): |
| commands = [] |
| if self.mode == 'compile' or self.mode == 'compile-and-link': |
| for input, output in zip(self.inputs, self._obj_file_names()): |
| commands.append(self._get_compilation_command(input, output)) |
| if self.mode == 'link' or self.mode == 'compile-and-link': |
| commands.append(self._get_link_command()) |
| return commands |
| |
| def output_files(self): |
| outputs = [] |
| if self.mode == 'compile' or self.mode == 'compile-and-link': |
| outputs.extend(self._ilk_file_names()) |
| outputs.extend(self._obj_file_names()) |
| if self.mode == 'link' or self.mode == 'compile-and-link': |
| outputs.append(self._pdb_file_name()) |
| outputs.append(self._exe_file_name()) |
| |
| return [x for x in outputs if x is not None] |
| |
| class GccBuilder(Builder): |
| def __init__(self, toolchain_type, args): |
| Builder.__init__(self, toolchain_type, args, '.o') |
| |
| def _get_compilation_command(self, source, obj): |
| args = [] |
| |
| args.append(self.compiler) |
| args.append('-m' + self.arch) |
| |
| args.append('-g') |
| if self.opt == 'none': |
| args.append('-O0') |
| elif self.opt == 'basic': |
| args.append('-O2') |
| elif self.opt == 'lto': |
| args.append('-flto=thin') |
| if self.nodefaultlib: |
| args.append('-nostdinc') |
| args.append('-static') |
| args.append('-c') |
| |
| args.extend(['-o', obj]) |
| args.append(source) |
| |
| return ('compiling', [source], obj, None, args) |
| |
| def _get_link_command(self): |
| args = [] |
| args.append(self.compiler) |
| args.append('-m' + self.arch) |
| if self.nodefaultlib: |
| args.append('-nostdlib') |
| args.append('-static') |
| main_symbol = 'main' |
| if sys.platform == 'darwin': |
| main_symbol = '_main' |
| args.append('-Wl,-e,' + main_symbol) |
| args.extend(['-o', self._exe_file_name()]) |
| args.extend(self._obj_file_names()) |
| |
| return ('linking', self._obj_file_names(), self._exe_file_name(), None, args) |
| |
| |
| def output_files(self): |
| outputs = [] |
| if self.mode == 'compile' or self.mode == 'compile-and-link': |
| outputs.extend(self._obj_file_names()) |
| if self.mode == 'link' or self.mode == 'compile-and-link': |
| outputs.append(self._exe_file_name()) |
| |
| return outputs |
| |
| def indent(text, spaces): |
| def prefixed_lines(): |
| prefix = ' ' * spaces |
| for line in text.splitlines(True): |
| yield prefix + line |
| return ''.join(prefixed_lines()) |
| |
| def build(commands): |
| global args |
| for (status, inputs, output, env, child_args) in commands: |
| print('\n\n') |
| inputs = [os.path.basename(x) for x in inputs] |
| output = os.path.basename(output) |
| print(status + ' {0} -> {1}'.format('+'.join(inputs), output)) |
| |
| if args.verbose: |
| print(' Command Line: ' + ' '.join(child_args)) |
| print(' Env:') |
| print_environment(env) |
| if args.dry: |
| continue |
| |
| popen = subprocess.Popen(child_args, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE, |
| env=env, |
| universal_newlines=True) |
| stdout, stderr = popen.communicate() |
| res = popen.wait() |
| if res == -signal.SIGINT: |
| raise KeyboardInterrupt |
| print(' STDOUT:') |
| print(indent(stdout, 4)) |
| if res != 0: |
| print(' STDERR:') |
| print(indent(stderr, 4)) |
| sys.exit(res) |
| |
| def clean(files): |
| global args |
| if not files: |
| return |
| for o in files: |
| file = o if args.verbose else os.path.basename(o) |
| print('Cleaning {0}'.format(file)) |
| try: |
| if os.path.exists(o): |
| if not args.dry: |
| os.remove(o) |
| if args.verbose: |
| print(' The file was successfully cleaned.') |
| elif args.verbose: |
| print(' The file does not exist.') |
| except: |
| if args.verbose: |
| print(' The file could not be removed.') |
| |
| def fix_arguments(args): |
| if not args.inputs: |
| raise ValueError('No input files specified') |
| |
| if args.output and args.mode == 'compile' and len(args.inputs) > 1: |
| raise ValueError('Cannot specify -o with mode=compile and multiple source files. Use --outdir instead.') |
| |
| if not args.dry: |
| args.inputs = [os.path.abspath(x) for x in args.inputs] |
| |
| # If user didn't specify the outdir, use the directory of the first input. |
| if not args.outdir: |
| if args.output: |
| args.outdir = os.path.dirname(args.output) |
| else: |
| args.outdir = os.path.dirname(args.inputs[0]) |
| args.outdir = os.path.abspath(args.outdir) |
| args.outdir = os.path.normpath(args.outdir) |
| |
| # If user specified a non-absolute path for the output file, append the |
| # output directory to it. |
| if args.output: |
| if not os.path.isabs(args.output): |
| args.output = os.path.join(args.outdir, args.output) |
| args.output = os.path.normpath(args.output) |
| |
| fix_arguments(args) |
| |
| (toolchain_type, toolchain_path) = find_toolchain(args.compiler, args.tools_dir) |
| if not toolchain_path or not toolchain_type: |
| print('Unable to find toolchain {0}'.format(args.compiler)) |
| sys.exit(1) |
| |
| if args.verbose: |
| print('Script Arguments:') |
| print(' Arch: ' + args.arch) |
| print(' Compiler: ' + args.compiler) |
| print(' Outdir: ' + args.outdir) |
| print(' Output: ' + args.output) |
| print(' Nodefaultlib: ' + str(args.nodefaultlib)) |
| print(' Opt: ' + args.opt) |
| print(' Mode: ' + args.mode) |
| print(' Clean: ' + str(args.clean)) |
| print(' Verbose: ' + str(args.verbose)) |
| print(' Dryrun: ' + str(args.dry)) |
| print(' Inputs: ' + format_text(args.inputs, 0, 10)) |
| print('Script Environment:') |
| print_environment(os.environ) |
| |
| args.compiler = toolchain_path |
| if not os.path.exists(args.compiler) and not args.dry: |
| raise ValueError('The toolchain {} does not exist.'.format(args.compiler)) |
| |
| if toolchain_type == 'msvc' or toolchain_type=='clang-cl': |
| builder = MsvcBuilder(toolchain_type, args) |
| else: |
| builder = GccBuilder(toolchain_type, args) |
| |
| if args.clean: |
| clean(builder.output_files()) |
| |
| cmds = builder.build_commands() |
| |
| build(cmds) |