1#!/usr/bin/env python 2 3from __future__ import print_function 4 5import argparse 6import os 7import shutil 8import signal 9import subprocess 10import sys 11 12if sys.platform == 'win32': 13 # This module was renamed in Python 3. Make sure to import it using a 14 # consistent name regardless of python version. 15 try: 16 import winreg 17 except: 18 import _winreg as winreg 19 20if __name__ != "__main__": 21 raise RuntimeError("Do not import this script, run it instead") 22 23 24parser = argparse.ArgumentParser(description='LLDB compilation wrapper') 25parser.add_argument('--arch', 26 metavar='arch', 27 dest='arch', 28 required=True, 29 default='host', 30 choices=['32', '64', 'host'], 31 help='Specify the architecture to target.') 32 33parser.add_argument('--compiler', 34 metavar='compiler', 35 dest='compiler', 36 required=True, 37 help='Path to a compiler executable, or one of the values [any, msvc, clang-cl, gcc, clang]') 38 39parser.add_argument('--libs-dir', 40 metavar='directory', 41 dest='libs_dir', 42 required=False, 43 action='append', 44 help='If specified, a path to linked libraries to be passed via -L') 45 46parser.add_argument('--tools-dir', 47 metavar='directory', 48 dest='tools_dir', 49 required=False, 50 action='append', 51 help='If specified, a path to search in addition to PATH when --compiler is not an exact path') 52 53if sys.platform == 'darwin': 54 parser.add_argument('--apple-sdk', 55 metavar='apple_sdk', 56 dest='apple_sdk', 57 default="macosx", 58 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.') 59 60parser.add_argument('--output', '-o', 61 dest='output', 62 metavar='file', 63 required=False, 64 default='', 65 help='Path to output file') 66 67parser.add_argument('--outdir', '-d', 68 dest='outdir', 69 metavar='directory', 70 required=False, 71 help='Directory for output files') 72 73parser.add_argument('--nodefaultlib', 74 dest='nodefaultlib', 75 action='store_true', 76 default=False, 77 help='When specified, the resulting image should not link against system libraries or include system headers. Useful when writing cross-targeting tests.') 78 79parser.add_argument('--opt', 80 dest='opt', 81 default='none', 82 choices=['none', 'basic', 'lto'], 83 help='Optimization level') 84 85parser.add_argument('--mode', 86 dest='mode', 87 default='compile-and-link', 88 choices=['compile', 'link', 'compile-and-link'], 89 help='Specifies whether to compile, link, or both') 90 91parser.add_argument('--noclean', 92 dest='clean', 93 action='store_false', 94 default=True, 95 help='Dont clean output file before building') 96 97parser.add_argument('--verbose', 98 dest='verbose', 99 action='store_true', 100 default=False, 101 help='Print verbose output') 102 103parser.add_argument('-n', '--dry-run', 104 dest='dry', 105 action='store_true', 106 default=False, 107 help='Print the commands that would run, but dont actually run them') 108 109parser.add_argument('inputs', 110 metavar='file', 111 nargs='+', 112 help='Source file(s) to compile / object file(s) to link') 113 114 115args = parser.parse_args(args=sys.argv[1:]) 116 117 118def to_string(b): 119 """Return the parameter as type 'str', possibly encoding it. 120 121 In Python2, the 'str' type is the same as 'bytes'. In Python3, the 122 'str' type is (essentially) Python2's 'unicode' type, and 'bytes' is 123 distinct. 124 125 This function is copied from llvm/utils/lit/lit/util.py 126 """ 127 if isinstance(b, str): 128 # In Python2, this branch is taken for types 'str' and 'bytes'. 129 # In Python3, this branch is taken only for 'str'. 130 return b 131 if isinstance(b, bytes): 132 # In Python2, this branch is never taken ('bytes' is handled as 'str'). 133 # In Python3, this is true only for 'bytes'. 134 try: 135 return b.decode('utf-8') 136 except UnicodeDecodeError: 137 # If the value is not valid Unicode, return the default 138 # repr-line encoding. 139 return str(b) 140 141 # By this point, here's what we *don't* have: 142 # 143 # - In Python2: 144 # - 'str' or 'bytes' (1st branch above) 145 # - In Python3: 146 # - 'str' (1st branch above) 147 # - 'bytes' (2nd branch above) 148 # 149 # The last type we might expect is the Python2 'unicode' type. There is no 150 # 'unicode' type in Python3 (all the Python3 cases were already handled). In 151 # order to get a 'str' object, we need to encode the 'unicode' object. 152 try: 153 return b.encode('utf-8') 154 except AttributeError: 155 raise TypeError('not sure how to convert %s to %s' % (type(b), str)) 156 157def format_text(lines, indent_0, indent_n): 158 result = ' ' * indent_0 + lines[0] 159 for next in lines[1:]: 160 result = result + '\n{0}{1}'.format(' ' * indent_n, next) 161 return result 162 163def print_environment(env): 164 if env is None: 165 print(' Inherited') 166 return 167 for e in env: 168 value = env[e] 169 lines = value.split(os.pathsep) 170 formatted_value = format_text(lines, 0, 7 + len(e)) 171 print(' {0} = {1}'.format(e, formatted_value)) 172 173def find_executable(binary_name, search_paths): 174 # shutil.which will ignore PATH if given a path argument, we want to include it. 175 search_paths.append(os.environ.get('PATH', '')) 176 search_path = os.pathsep.join(search_paths) 177 binary_path = shutil.which(binary_name, path=search_path) 178 if binary_path is not None: 179 # So for example, we get '/bin/gcc' instead of '/usr/../bin/gcc'. 180 binary_path = os.path.normpath(binary_path) 181 return binary_path 182 183def find_toolchain(compiler, tools_dir): 184 if compiler == 'msvc': 185 return ('msvc', find_executable('cl', tools_dir)) 186 if compiler == 'clang-cl': 187 return ('clang-cl', find_executable('clang-cl', tools_dir)) 188 if compiler == 'gcc': 189 return ('gcc', find_executable('g++', tools_dir)) 190 if compiler == 'clang': 191 return ('clang', find_executable('clang++', tools_dir)) 192 if compiler == 'any': 193 priorities = [] 194 if sys.platform == 'win32': 195 priorities = ['clang-cl', 'msvc', 'clang', 'gcc'] 196 else: 197 priorities = ['clang', 'gcc', 'clang-cl'] 198 for toolchain in priorities: 199 (type, dir) = find_toolchain(toolchain, tools_dir) 200 if type and dir: 201 return (type, dir) 202 # Could not find any toolchain. 203 return (None, None) 204 205 # From here on, assume that |compiler| is a path to a file. 206 file = os.path.basename(compiler) 207 name, ext = os.path.splitext(file) 208 if file.lower() == 'cl.exe': 209 return ('msvc', compiler) 210 if name == 'clang-cl': 211 return ('clang-cl', compiler) 212 if name.startswith('clang'): 213 return ('clang', compiler) 214 if name.startswith('gcc') or name.startswith('g++'): 215 return ('gcc', compiler) 216 if name == 'cc' or name == 'c++': 217 return ('generic', compiler) 218 return ('unknown', compiler) 219 220class Builder(object): 221 def __init__(self, toolchain_type, args, obj_ext): 222 self.toolchain_type = toolchain_type 223 self.inputs = args.inputs 224 self.arch = args.arch 225 self.opt = args.opt 226 self.outdir = args.outdir 227 self.compiler = args.compiler 228 self.clean = args.clean 229 self.output = args.output 230 self.mode = args.mode 231 self.nodefaultlib = args.nodefaultlib 232 self.verbose = args.verbose 233 self.obj_ext = obj_ext 234 self.lib_paths = args.libs_dir 235 236 def _exe_file_name(self): 237 assert self.mode != 'compile' 238 return self.output 239 240 def _output_name(self, input, extension, with_executable=False): 241 basename = os.path.splitext(os.path.basename(input))[0] + extension 242 if with_executable: 243 exe_basename = os.path.basename(self._exe_file_name()) 244 basename = exe_basename + '-' + basename 245 246 output = os.path.join(self.outdir, basename) 247 return os.path.normpath(output) 248 249 def _obj_file_names(self): 250 if self.mode == 'link': 251 return self.inputs 252 253 if self.mode == 'compile-and-link': 254 # Object file names should factor in both the input file (source) 255 # name and output file (executable) name, to ensure that two tests 256 # which share a common source file don't race to write the same 257 # object file. 258 return [self._output_name(x, self.obj_ext, True) for x in self.inputs] 259 260 if self.mode == 'compile' and self.output: 261 return [self.output] 262 263 return [self._output_name(x, self.obj_ext) for x in self.inputs] 264 265 def build_commands(self): 266 commands = [] 267 if self.mode == 'compile' or self.mode == 'compile-and-link': 268 for input, output in zip(self.inputs, self._obj_file_names()): 269 commands.append(self._get_compilation_command(input, output)) 270 if self.mode == 'link' or self.mode == 'compile-and-link': 271 commands.append(self._get_link_command()) 272 return commands 273 274 275class MsvcBuilder(Builder): 276 def __init__(self, toolchain_type, args): 277 Builder.__init__(self, toolchain_type, args, '.obj') 278 279 if os.getenv('PLATFORM') == 'arm64': 280 self.msvc_arch_str = 'arm' if self.arch == '32' else 'arm64' 281 else: 282 self.msvc_arch_str = 'x86' if self.arch == '32' else 'x64' 283 284 if toolchain_type == 'msvc': 285 # Make sure we're using the appropriate toolchain for the desired 286 # target type. 287 compiler_parent_dir = os.path.dirname(self.compiler) 288 selected_target_version = os.path.basename(compiler_parent_dir) 289 if selected_target_version != self.msvc_arch_str: 290 host_dir = os.path.dirname(compiler_parent_dir) 291 self.compiler = os.path.join(host_dir, self.msvc_arch_str, 'cl.exe') 292 if self.verbose: 293 print('Using alternate compiler "{0}" to match selected target.'.format(self.compiler)) 294 295 if self.mode == 'link' or self.mode == 'compile-and-link': 296 self.linker = self._find_linker('link') if toolchain_type == 'msvc' else self._find_linker('lld-link', args.tools_dir) 297 if not self.linker: 298 raise ValueError('Unable to find an appropriate linker.') 299 300 self.compile_env, self.link_env = self._get_visual_studio_environment() 301 302 def _find_linker(self, name, search_paths=[]): 303 compiler_dir = os.path.dirname(self.compiler) 304 linker_path = find_executable(name, [compiler_dir] + search_paths) 305 if linker_path is None: 306 raise ValueError('Could not find \'{}\''.format(name)) 307 return linker_path 308 309 def _get_vc_install_dir(self): 310 dir = os.getenv('VCINSTALLDIR', None) 311 if dir: 312 if self.verbose: 313 print('Using %VCINSTALLDIR% {}'.format(dir)) 314 return dir 315 316 dir = os.getenv('VSINSTALLDIR', None) 317 if dir: 318 if self.verbose: 319 print('Using %VSINSTALLDIR% {}'.format(dir)) 320 return os.path.join(dir, 'VC') 321 322 dir = os.getenv('VS2019INSTALLDIR', None) 323 if dir: 324 if self.verbose: 325 print('Using %VS2019INSTALLDIR% {}'.format(dir)) 326 return os.path.join(dir, 'VC') 327 328 dir = os.getenv('VS2017INSTALLDIR', None) 329 if dir: 330 if self.verbose: 331 print('Using %VS2017INSTALLDIR% {}'.format(dir)) 332 return os.path.join(dir, 'VC') 333 334 dir = os.getenv('VS2015INSTALLDIR', None) 335 if dir: 336 if self.verbose: 337 print('Using %VS2015INSTALLDIR% {}'.format(dir)) 338 return os.path.join(dir, 'VC') 339 return None 340 341 def _get_vctools_version(self): 342 ver = os.getenv('VCToolsVersion', None) 343 if ver: 344 if self.verbose: 345 print('Using %VCToolsVersion% {}'.format(ver)) 346 return ver 347 348 vcinstalldir = self._get_vc_install_dir() 349 vcinstalldir = os.path.join(vcinstalldir, 'Tools', 'MSVC') 350 subdirs = next(os.walk(vcinstalldir))[1] 351 if not subdirs: 352 return None 353 354 from distutils.version import StrictVersion 355 subdirs.sort(key=lambda x : StrictVersion(x)) 356 357 if self.verbose: 358 full_path = os.path.join(vcinstalldir, subdirs[-1]) 359 print('Using VC tools version directory {0} found by directory walk.'.format(full_path)) 360 return subdirs[-1] 361 362 def _get_vctools_install_dir(self): 363 dir = os.getenv('VCToolsInstallDir', None) 364 if dir: 365 if self.verbose: 366 print('Using %VCToolsInstallDir% {}'.format(dir)) 367 return dir 368 369 vcinstalldir = self._get_vc_install_dir() 370 if not vcinstalldir: 371 return None 372 vctoolsver = self._get_vctools_version() 373 if not vctoolsver: 374 return None 375 result = os.path.join(vcinstalldir, 'Tools', 'MSVC', vctoolsver) 376 if not os.path.exists(result): 377 return None 378 if self.verbose: 379 print('Using VC tools install dir {} found by directory walk'.format(result)) 380 return result 381 382 def _find_windows_sdk_in_registry_view(self, view): 383 products_key = None 384 roots_key = None 385 installed_options_keys = [] 386 try: 387 sam = view | winreg.KEY_READ 388 products_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 389 r'Software\Microsoft\Windows Kits\Installed Products', 390 0, 391 sam) 392 393 # This is the GUID for the desktop component. If this is present 394 # then the components required for the Desktop SDK are installed. 395 # If not it will throw an exception. 396 winreg.QueryValueEx(products_key, '{5A3D81EC-D870-9ECF-D997-24BDA6644752}') 397 398 roots_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 399 r'Software\Microsoft\Windows Kits\Installed Roots', 400 0, 401 sam) 402 root_dir = winreg.QueryValueEx(roots_key, 'KitsRoot10') 403 root_dir = to_string(root_dir[0]) 404 sdk_versions = [] 405 index = 0 406 while True: 407 # Installed SDK versions are stored as sub-keys of the 408 # 'Installed Roots' key. Find all of their names, then sort 409 # them by version 410 try: 411 ver_key = winreg.EnumKey(roots_key, index) 412 sdk_versions.append(ver_key) 413 index = index + 1 414 except WindowsError: 415 break 416 if not sdk_versions: 417 return (None, None) 418 419 # Windows SDK version numbers consist of 4 dotted components, so we 420 # have to use LooseVersion, as StrictVersion supports 3 or fewer. 421 from distutils.version import LooseVersion 422 sdk_versions.sort(key=lambda x : LooseVersion(x), reverse=True) 423 option_value_name = 'OptionId.DesktopCPP' + self.msvc_arch_str 424 for v in sdk_versions: 425 try: 426 version_subkey = v + r'\Installed Options' 427 key = winreg.OpenKey(roots_key, version_subkey) 428 installed_options_keys.append(key) 429 (value, value_type) = winreg.QueryValueEx(key, option_value_name) 430 if value == 1: 431 # The proper architecture is installed. Return the 432 # associated paths. 433 if self.verbose: 434 print('Found Installed Windows SDK v{0} at {1}'.format(v, root_dir)) 435 return (root_dir, v) 436 except: 437 continue 438 except: 439 return (None, None) 440 finally: 441 del products_key 442 del roots_key 443 for k in installed_options_keys: 444 del k 445 return (None, None) 446 447 def _find_windows_sdk_in_registry(self): 448 # This could be a clang-cl cross-compile. If so, there's no registry 449 # so just exit. 450 if sys.platform != 'win32': 451 return (None, None) 452 if self.verbose: 453 print('Looking for Windows SDK in 64-bit registry.') 454 dir, ver = self._find_windows_sdk_in_registry_view(winreg.KEY_WOW64_64KEY) 455 if not dir or not ver: 456 if self.verbose: 457 print('Looking for Windows SDK in 32-bit registry.') 458 dir, ver = self._find_windows_sdk_in_registry_view(winreg.KEY_WOW64_32KEY) 459 460 return (dir, ver) 461 462 def _get_winsdk_dir(self): 463 # If a Windows SDK is specified in the environment, use that. Otherwise 464 # try to find one in the Windows registry. 465 dir = os.getenv('WindowsSdkDir', None) 466 if not dir or not os.path.exists(dir): 467 return self._find_windows_sdk_in_registry() 468 ver = os.getenv('WindowsSDKLibVersion', None) 469 if not ver: 470 return self._find_windows_sdk_in_registry() 471 472 ver = ver.rstrip('\\') 473 if self.verbose: 474 print('Using %WindowsSdkDir% {}'.format(dir)) 475 print('Using %WindowsSDKLibVersion% {}'.format(ver)) 476 return (dir, ver) 477 478 def _get_msvc_native_toolchain_dir(self): 479 assert self.toolchain_type == 'msvc' 480 compiler_dir = os.path.dirname(self.compiler) 481 target_dir = os.path.dirname(compiler_dir) 482 host_name = os.path.basename(target_dir) 483 host_name = host_name[4:].lower() 484 return os.path.join(target_dir, host_name) 485 486 def _get_visual_studio_environment(self): 487 vctools = self._get_vctools_install_dir() 488 winsdk, winsdkver = self._get_winsdk_dir() 489 490 if not vctools and self.verbose: 491 print('Unable to find VC tools installation directory.') 492 if (not winsdk or not winsdkver) and self.verbose: 493 print('Unable to find Windows SDK directory.') 494 495 vcincludes = [] 496 vclibs = [] 497 sdkincludes = [] 498 sdklibs = [] 499 if vctools is not None: 500 includes = [['ATLMFC', 'include'], ['include']] 501 libs = [['ATLMFC', 'lib'], ['lib']] 502 vcincludes = [os.path.join(vctools, *y) for y in includes] 503 vclibs = [os.path.join(vctools, *y) for y in libs] 504 if winsdk is not None: 505 includes = [['include', winsdkver, 'ucrt'], 506 ['include', winsdkver, 'shared'], 507 ['include', winsdkver, 'um'], 508 ['include', winsdkver, 'winrt'], 509 ['include', winsdkver, 'cppwinrt']] 510 libs = [['lib', winsdkver, 'ucrt'], 511 ['lib', winsdkver, 'um']] 512 sdkincludes = [os.path.join(winsdk, *y) for y in includes] 513 sdklibs = [os.path.join(winsdk, *y) for y in libs] 514 515 includes = vcincludes + sdkincludes 516 libs = vclibs + sdklibs 517 libs = [os.path.join(x, self.msvc_arch_str) for x in libs] 518 compileenv = None 519 linkenv = None 520 defaultenv = {} 521 if sys.platform == 'win32': 522 defaultenv = { x : os.environ[x] for x in 523 ['SystemDrive', 'SystemRoot', 'TMP', 'TEMP'] } 524 # The directory to mspdbcore.dll needs to be in PATH, but this is 525 # always in the native toolchain path, not the cross-toolchain 526 # path. So, for example, if we're using HostX64\x86 then we need 527 # to add HostX64\x64 to the path, and if we're using HostX86\x64 528 # then we need to add HostX86\x86 to the path. 529 if self.toolchain_type == 'msvc': 530 defaultenv['PATH'] = self._get_msvc_native_toolchain_dir() 531 532 if includes: 533 compileenv = {} 534 compileenv['INCLUDE'] = os.pathsep.join(includes) 535 compileenv.update(defaultenv) 536 if libs: 537 linkenv = {} 538 linkenv['LIB'] = os.pathsep.join(libs) 539 linkenv.update(defaultenv) 540 return (compileenv, linkenv) 541 542 def _ilk_file_names(self): 543 if self.mode == 'link': 544 return [] 545 546 return [self._output_name(x, '.ilk') for x in self.inputs] 547 548 def _pdb_file_name(self): 549 if self.mode == 'compile': 550 return None 551 return os.path.splitext(self.output)[0] + '.pdb' 552 553 def _get_compilation_command(self, source, obj): 554 args = [] 555 556 args.append(self.compiler) 557 if self.toolchain_type == 'clang-cl': 558 args.append('-m' + self.arch) 559 560 if self.opt == 'none': 561 args.append('/Od') 562 elif self.opt == 'basic': 563 args.append('/O2') 564 elif self.opt == 'lto': 565 if self.toolchain_type == 'msvc': 566 args.append('/GL') 567 args.append('/Gw') 568 else: 569 args.append('-flto=thin') 570 if self.nodefaultlib: 571 args.append('/GS-') 572 args.append('/GR-') 573 args.append('/Z7') 574 if self.toolchain_type == 'clang-cl': 575 args.append('-Xclang') 576 args.append('-fkeep-static-consts') 577 args.append('-fms-compatibility-version=19') 578 args.append('/c') 579 580 args.append('/Fo' + obj) 581 if self.toolchain_type == 'clang-cl': 582 args.append('--') 583 args.append(source) 584 585 return ('compiling', [source], obj, 586 self.compile_env, 587 args) 588 589 def _get_link_command(self): 590 args = [] 591 args.append(self.linker) 592 args.append('/DEBUG:FULL') 593 args.append('/INCREMENTAL:NO') 594 if self.nodefaultlib: 595 args.append('/nodefaultlib') 596 args.append('/entry:main') 597 args.append('/PDB:' + self._pdb_file_name()) 598 args.append('/OUT:' + self._exe_file_name()) 599 args.extend(self._obj_file_names()) 600 601 return ('linking', self._obj_file_names(), self._exe_file_name(), 602 self.link_env, 603 args) 604 605 def build_commands(self): 606 commands = [] 607 if self.mode == 'compile' or self.mode == 'compile-and-link': 608 for input, output in zip(self.inputs, self._obj_file_names()): 609 commands.append(self._get_compilation_command(input, output)) 610 if self.mode == 'link' or self.mode == 'compile-and-link': 611 commands.append(self._get_link_command()) 612 return commands 613 614 def output_files(self): 615 outputs = [] 616 if self.mode == 'compile' or self.mode == 'compile-and-link': 617 outputs.extend(self._ilk_file_names()) 618 outputs.extend(self._obj_file_names()) 619 if self.mode == 'link' or self.mode == 'compile-and-link': 620 outputs.append(self._pdb_file_name()) 621 outputs.append(self._exe_file_name()) 622 623 return [x for x in outputs if x is not None] 624 625class GccBuilder(Builder): 626 def __init__(self, toolchain_type, args): 627 Builder.__init__(self, toolchain_type, args, '.o') 628 if sys.platform == 'darwin': 629 cmd = ['xcrun', '--sdk', args.apple_sdk, '--show-sdk-path'] 630 self.apple_sdk = subprocess.check_output(cmd).strip().decode('utf-8') 631 632 def _get_compilation_command(self, source, obj): 633 args = [] 634 635 args.append(self.compiler) 636 args.append('-m' + self.arch) 637 638 args.append('-g') 639 if self.opt == 'none': 640 args.append('-O0') 641 elif self.opt == 'basic': 642 args.append('-O2') 643 elif self.opt == 'lto': 644 args.append('-flto=thin') 645 if self.nodefaultlib: 646 args.append('-nostdinc') 647 args.append('-static') 648 args.append('-c') 649 650 args.extend(['-o', obj]) 651 args.append(source) 652 653 if sys.platform == 'darwin': 654 args.extend(['-isysroot', self.apple_sdk]) 655 656 return ('compiling', [source], obj, None, args) 657 658 def _get_link_command(self): 659 args = [] 660 args.append(self.compiler) 661 args.append('-m' + self.arch) 662 if self.nodefaultlib: 663 args.append('-nostdlib') 664 args.append('-static') 665 main_symbol = 'main' 666 if sys.platform == 'darwin': 667 main_symbol = '_main' 668 args.append('-Wl,-e,' + main_symbol) 669 if sys.platform.startswith('netbsd'): 670 for x in self.lib_paths: 671 args += ['-L' + x, '-Wl,-rpath,' + x] 672 args.extend(['-o', self._exe_file_name()]) 673 args.extend(self._obj_file_names()) 674 675 if sys.platform == 'darwin': 676 args.extend(['-isysroot', self.apple_sdk]) 677 678 return ('linking', self._obj_file_names(), self._exe_file_name(), None, args) 679 680 681 def output_files(self): 682 outputs = [] 683 if self.mode == 'compile' or self.mode == 'compile-and-link': 684 outputs.extend(self._obj_file_names()) 685 if self.mode == 'link' or self.mode == 'compile-and-link': 686 outputs.append(self._exe_file_name()) 687 688 return outputs 689 690def indent(text, spaces): 691 def prefixed_lines(): 692 prefix = ' ' * spaces 693 for line in text.splitlines(True): 694 yield prefix + line 695 return ''.join(prefixed_lines()) 696 697def build(commands): 698 global args 699 for (status, inputs, output, env, child_args) in commands: 700 print('\n\n') 701 inputs = [os.path.basename(x) for x in inputs] 702 output = os.path.basename(output) 703 print(status + ' {0} -> {1}'.format('+'.join(inputs), output)) 704 705 if args.verbose: 706 print(' Command Line: ' + ' '.join(child_args)) 707 print(' Env:') 708 print_environment(env) 709 if args.dry: 710 continue 711 712 popen = subprocess.Popen(child_args, 713 stdout=subprocess.PIPE, 714 stderr=subprocess.PIPE, 715 env=env, 716 universal_newlines=True) 717 stdout, stderr = popen.communicate() 718 res = popen.wait() 719 if res == -signal.SIGINT: 720 raise KeyboardInterrupt 721 print(' STDOUT:') 722 print(indent(stdout, 4)) 723 if res != 0: 724 print(' STDERR:') 725 print(indent(stderr, 4)) 726 sys.exit(res) 727 728def clean(files): 729 global args 730 if not files: 731 return 732 for o in files: 733 file = o if args.verbose else os.path.basename(o) 734 print('Cleaning {0}'.format(file)) 735 try: 736 if os.path.exists(o): 737 if not args.dry: 738 os.remove(o) 739 if args.verbose: 740 print(' The file was successfully cleaned.') 741 elif args.verbose: 742 print(' The file does not exist.') 743 except: 744 if args.verbose: 745 print(' The file could not be removed.') 746 747def fix_arguments(args): 748 if not args.inputs: 749 raise ValueError('No input files specified') 750 751 if args.output and args.mode == 'compile' and len(args.inputs) > 1: 752 raise ValueError('Cannot specify -o with mode=compile and multiple source files. Use --outdir instead.') 753 754 if not args.dry: 755 args.inputs = [os.path.abspath(x) for x in args.inputs] 756 757 # If user didn't specify the outdir, use the directory of the first input. 758 if not args.outdir: 759 if args.output: 760 args.outdir = os.path.dirname(args.output) 761 else: 762 args.outdir = os.path.dirname(args.inputs[0]) 763 args.outdir = os.path.abspath(args.outdir) 764 args.outdir = os.path.normpath(args.outdir) 765 766 # If user specified a non-absolute path for the output file, append the 767 # output directory to it. 768 if args.output: 769 if not os.path.isabs(args.output): 770 args.output = os.path.join(args.outdir, args.output) 771 args.output = os.path.normpath(args.output) 772 773fix_arguments(args) 774 775(toolchain_type, toolchain_path) = find_toolchain(args.compiler, args.tools_dir) 776if not toolchain_path or not toolchain_type: 777 print('Unable to find toolchain {0}'.format(args.compiler)) 778 sys.exit(1) 779 780if args.verbose: 781 print('Script Arguments:') 782 print(' Arch: ' + args.arch) 783 print(' Compiler: ' + args.compiler) 784 print(' Outdir: ' + args.outdir) 785 print(' Output: ' + args.output) 786 print(' Nodefaultlib: ' + str(args.nodefaultlib)) 787 print(' Opt: ' + args.opt) 788 print(' Mode: ' + args.mode) 789 print(' Clean: ' + str(args.clean)) 790 print(' Verbose: ' + str(args.verbose)) 791 print(' Dryrun: ' + str(args.dry)) 792 print(' Inputs: ' + format_text(args.inputs, 0, 10)) 793 print('Script Environment:') 794 print_environment(os.environ) 795 796args.compiler = toolchain_path 797if not os.path.exists(args.compiler) and not args.dry: 798 raise ValueError('The toolchain {} does not exist.'.format(args.compiler)) 799 800if toolchain_type == 'msvc' or toolchain_type=='clang-cl': 801 builder = MsvcBuilder(toolchain_type, args) 802else: 803 builder = GccBuilder(toolchain_type, args) 804 805if args.clean: 806 clean(builder.output_files()) 807 808cmds = builder.build_commands() 809 810build(cmds) 811