diff options
Diffstat (limited to 'lit/helper/build.py')
-rwxr-xr-x | lit/helper/build.py | 787 |
1 files changed, 0 insertions, 787 deletions
diff --git a/lit/helper/build.py b/lit/helper/build.py deleted file mode 100755 index 26f321d709f8..000000000000 --- a/lit/helper/build.py +++ /dev/null @@ -1,787 +0,0 @@ -#! /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') - 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): - if sys.platform == 'win32': - name = name + '.exe' - compiler_dir = os.path.dirname(self.compiler) - linker_path = os.path.join(compiler_dir, name) - if not os.path.exists(linker_path): - raise ValueError('Could not find \'{}\''.format(linker_path)) - 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('/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) |