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