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 self.msvc_arch_str = 'x86' if self.arch == '32' else 'x64' 281 282 if toolchain_type == 'msvc': 283 # Make sure we're using the appropriate toolchain for the desired 284 # target type. 285 compiler_parent_dir = os.path.dirname(self.compiler) 286 selected_target_version = os.path.basename(compiler_parent_dir) 287 if selected_target_version != self.msvc_arch_str: 288 host_dir = os.path.dirname(compiler_parent_dir) 289 self.compiler = os.path.join(host_dir, self.msvc_arch_str, 'cl.exe') 290 if self.verbose: 291 print('Using alternate compiler "{0}" to match selected target.'.format(self.compiler)) 292 293 if self.mode == 'link' or self.mode == 'compile-and-link': 294 self.linker = self._find_linker('link') if toolchain_type == 'msvc' else self._find_linker('lld-link', args.tools_dir) 295 if not self.linker: 296 raise ValueError('Unable to find an appropriate linker.') 297 298 self.compile_env, self.link_env = self._get_visual_studio_environment() 299 300 def _find_linker(self, name, search_paths=[]): 301 compiler_dir = os.path.dirname(self.compiler) 302 linker_path = find_executable(name, [compiler_dir] + search_paths) 303 if linker_path is None: 304 raise ValueError('Could not find \'{}\''.format(name)) 305 return linker_path 306 307 def _get_vc_install_dir(self): 308 dir = os.getenv('VCINSTALLDIR', None) 309 if dir: 310 if self.verbose: 311 print('Using %VCINSTALLDIR% {}'.format(dir)) 312 return dir 313 314 dir = os.getenv('VSINSTALLDIR', None) 315 if dir: 316 if self.verbose: 317 print('Using %VSINSTALLDIR% {}'.format(dir)) 318 return os.path.join(dir, 'VC') 319 320 dir = os.getenv('VS2019INSTALLDIR', None) 321 if dir: 322 if self.verbose: 323 print('Using %VS2019INSTALLDIR% {}'.format(dir)) 324 return os.path.join(dir, 'VC') 325 326 dir = os.getenv('VS2017INSTALLDIR', None) 327 if dir: 328 if self.verbose: 329 print('Using %VS2017INSTALLDIR% {}'.format(dir)) 330 return os.path.join(dir, 'VC') 331 332 dir = os.getenv('VS2015INSTALLDIR', None) 333 if dir: 334 if self.verbose: 335 print('Using %VS2015INSTALLDIR% {}'.format(dir)) 336 return os.path.join(dir, 'VC') 337 return None 338 339 def _get_vctools_version(self): 340 ver = os.getenv('VCToolsVersion', None) 341 if ver: 342 if self.verbose: 343 print('Using %VCToolsVersion% {}'.format(ver)) 344 return ver 345 346 vcinstalldir = self._get_vc_install_dir() 347 vcinstalldir = os.path.join(vcinstalldir, 'Tools', 'MSVC') 348 subdirs = next(os.walk(vcinstalldir))[1] 349 if not subdirs: 350 return None 351 352 from distutils.version import StrictVersion 353 subdirs.sort(key=lambda x : StrictVersion(x)) 354 355 if self.verbose: 356 full_path = os.path.join(vcinstalldir, subdirs[-1]) 357 print('Using VC tools version directory {0} found by directory walk.'.format(full_path)) 358 return subdirs[-1] 359 360 def _get_vctools_install_dir(self): 361 dir = os.getenv('VCToolsInstallDir', None) 362 if dir: 363 if self.verbose: 364 print('Using %VCToolsInstallDir% {}'.format(dir)) 365 return dir 366 367 vcinstalldir = self._get_vc_install_dir() 368 if not vcinstalldir: 369 return None 370 vctoolsver = self._get_vctools_version() 371 if not vctoolsver: 372 return None 373 result = os.path.join(vcinstalldir, 'Tools', 'MSVC', vctoolsver) 374 if not os.path.exists(result): 375 return None 376 if self.verbose: 377 print('Using VC tools install dir {} found by directory walk'.format(result)) 378 return result 379 380 def _find_windows_sdk_in_registry_view(self, view): 381 products_key = None 382 roots_key = None 383 installed_options_keys = [] 384 try: 385 sam = view | winreg.KEY_READ 386 products_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 387 r'Software\Microsoft\Windows Kits\Installed Products', 388 0, 389 sam) 390 391 # This is the GUID for the desktop component. If this is present 392 # then the components required for the Desktop SDK are installed. 393 # If not it will throw an exception. 394 winreg.QueryValueEx(products_key, '{5A3D81EC-D870-9ECF-D997-24BDA6644752}') 395 396 roots_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 397 r'Software\Microsoft\Windows Kits\Installed Roots', 398 0, 399 sam) 400 root_dir = winreg.QueryValueEx(roots_key, 'KitsRoot10') 401 root_dir = to_string(root_dir[0]) 402 sdk_versions = [] 403 index = 0 404 while True: 405 # Installed SDK versions are stored as sub-keys of the 406 # 'Installed Roots' key. Find all of their names, then sort 407 # them by version 408 try: 409 ver_key = winreg.EnumKey(roots_key, index) 410 sdk_versions.append(ver_key) 411 index = index + 1 412 except WindowsError: 413 break 414 if not sdk_versions: 415 return (None, None) 416 417 # Windows SDK version numbers consist of 4 dotted components, so we 418 # have to use LooseVersion, as StrictVersion supports 3 or fewer. 419 from distutils.version import LooseVersion 420 sdk_versions.sort(key=lambda x : LooseVersion(x), reverse=True) 421 option_value_name = 'OptionId.DesktopCPP' + self.msvc_arch_str 422 for v in sdk_versions: 423 try: 424 version_subkey = v + r'\Installed Options' 425 key = winreg.OpenKey(roots_key, version_subkey) 426 installed_options_keys.append(key) 427 (value, value_type) = winreg.QueryValueEx(key, option_value_name) 428 if value == 1: 429 # The proper architecture is installed. Return the 430 # associated paths. 431 if self.verbose: 432 print('Found Installed Windows SDK v{0} at {1}'.format(v, root_dir)) 433 return (root_dir, v) 434 except: 435 continue 436 except: 437 return (None, None) 438 finally: 439 del products_key 440 del roots_key 441 for k in installed_options_keys: 442 del k 443 return (None, None) 444 445 def _find_windows_sdk_in_registry(self): 446 # This could be a clang-cl cross-compile. If so, there's no registry 447 # so just exit. 448 if sys.platform != 'win32': 449 return (None, None) 450 if self.verbose: 451 print('Looking for Windows SDK in 64-bit registry.') 452 dir, ver = self._find_windows_sdk_in_registry_view(winreg.KEY_WOW64_64KEY) 453 if not dir or not ver: 454 if self.verbose: 455 print('Looking for Windows SDK in 32-bit registry.') 456 dir, ver = self._find_windows_sdk_in_registry_view(winreg.KEY_WOW64_32KEY) 457 458 return (dir, ver) 459 460 def _get_winsdk_dir(self): 461 # If a Windows SDK is specified in the environment, use that. Otherwise 462 # try to find one in the Windows registry. 463 dir = os.getenv('WindowsSdkDir', None) 464 if not dir or not os.path.exists(dir): 465 return self._find_windows_sdk_in_registry() 466 ver = os.getenv('WindowsSDKLibVersion', None) 467 if not ver: 468 return self._find_windows_sdk_in_registry() 469 470 ver = ver.rstrip('\\') 471 if self.verbose: 472 print('Using %WindowsSdkDir% {}'.format(dir)) 473 print('Using %WindowsSDKLibVersion% {}'.format(ver)) 474 return (dir, ver) 475 476 def _get_msvc_native_toolchain_dir(self): 477 assert self.toolchain_type == 'msvc' 478 compiler_dir = os.path.dirname(self.compiler) 479 target_dir = os.path.dirname(compiler_dir) 480 host_name = os.path.basename(target_dir) 481 host_name = host_name[4:].lower() 482 return os.path.join(target_dir, host_name) 483 484 def _get_visual_studio_environment(self): 485 vctools = self._get_vctools_install_dir() 486 winsdk, winsdkver = self._get_winsdk_dir() 487 488 if not vctools and self.verbose: 489 print('Unable to find VC tools installation directory.') 490 if (not winsdk or not winsdkver) and self.verbose: 491 print('Unable to find Windows SDK directory.') 492 493 vcincludes = [] 494 vclibs = [] 495 sdkincludes = [] 496 sdklibs = [] 497 if vctools is not None: 498 includes = [['ATLMFC', 'include'], ['include']] 499 libs = [['ATLMFC', 'lib'], ['lib']] 500 vcincludes = [os.path.join(vctools, *y) for y in includes] 501 vclibs = [os.path.join(vctools, *y) for y in libs] 502 if winsdk is not None: 503 includes = [['include', winsdkver, 'ucrt'], 504 ['include', winsdkver, 'shared'], 505 ['include', winsdkver, 'um'], 506 ['include', winsdkver, 'winrt'], 507 ['include', winsdkver, 'cppwinrt']] 508 libs = [['lib', winsdkver, 'ucrt'], 509 ['lib', winsdkver, 'um']] 510 sdkincludes = [os.path.join(winsdk, *y) for y in includes] 511 sdklibs = [os.path.join(winsdk, *y) for y in libs] 512 513 includes = vcincludes + sdkincludes 514 libs = vclibs + sdklibs 515 libs = [os.path.join(x, self.msvc_arch_str) for x in libs] 516 compileenv = None 517 linkenv = None 518 defaultenv = {} 519 if sys.platform == 'win32': 520 defaultenv = { x : os.environ[x] for x in 521 ['SystemDrive', 'SystemRoot', 'TMP', 'TEMP'] } 522 # The directory to mspdbcore.dll needs to be in PATH, but this is 523 # always in the native toolchain path, not the cross-toolchain 524 # path. So, for example, if we're using HostX64\x86 then we need 525 # to add HostX64\x64 to the path, and if we're using HostX86\x64 526 # then we need to add HostX86\x86 to the path. 527 if self.toolchain_type == 'msvc': 528 defaultenv['PATH'] = self._get_msvc_native_toolchain_dir() 529 530 if includes: 531 compileenv = {} 532 compileenv['INCLUDE'] = os.pathsep.join(includes) 533 compileenv.update(defaultenv) 534 if libs: 535 linkenv = {} 536 linkenv['LIB'] = os.pathsep.join(libs) 537 linkenv.update(defaultenv) 538 return (compileenv, linkenv) 539 540 def _ilk_file_names(self): 541 if self.mode == 'link': 542 return [] 543 544 return [self._output_name(x, '.ilk') for x in self.inputs] 545 546 def _pdb_file_name(self): 547 if self.mode == 'compile': 548 return None 549 return os.path.splitext(self.output)[0] + '.pdb' 550 551 def _get_compilation_command(self, source, obj): 552 args = [] 553 554 args.append(self.compiler) 555 if self.toolchain_type == 'clang-cl': 556 args.append('-m' + self.arch) 557 558 if self.opt == 'none': 559 args.append('/Od') 560 elif self.opt == 'basic': 561 args.append('/O2') 562 elif self.opt == 'lto': 563 if self.toolchain_type == 'msvc': 564 args.append('/GL') 565 args.append('/Gw') 566 else: 567 args.append('-flto=thin') 568 if self.nodefaultlib: 569 args.append('/GS-') 570 args.append('/GR-') 571 args.append('/Z7') 572 if self.toolchain_type == 'clang-cl': 573 args.append('-Xclang') 574 args.append('-fkeep-static-consts') 575 args.append('-fms-compatibility-version=19') 576 args.append('/c') 577 578 args.append('/Fo' + obj) 579 if self.toolchain_type == 'clang-cl': 580 args.append('--') 581 args.append(source) 582 583 return ('compiling', [source], obj, 584 self.compile_env, 585 args) 586 587 def _get_link_command(self): 588 args = [] 589 args.append(self.linker) 590 args.append('/DEBUG:FULL') 591 args.append('/INCREMENTAL:NO') 592 if self.nodefaultlib: 593 args.append('/nodefaultlib') 594 args.append('/entry:main') 595 args.append('/PDB:' + self._pdb_file_name()) 596 args.append('/OUT:' + self._exe_file_name()) 597 args.extend(self._obj_file_names()) 598 599 return ('linking', self._obj_file_names(), self._exe_file_name(), 600 self.link_env, 601 args) 602 603 def build_commands(self): 604 commands = [] 605 if self.mode == 'compile' or self.mode == 'compile-and-link': 606 for input, output in zip(self.inputs, self._obj_file_names()): 607 commands.append(self._get_compilation_command(input, output)) 608 if self.mode == 'link' or self.mode == 'compile-and-link': 609 commands.append(self._get_link_command()) 610 return commands 611 612 def output_files(self): 613 outputs = [] 614 if self.mode == 'compile' or self.mode == 'compile-and-link': 615 outputs.extend(self._ilk_file_names()) 616 outputs.extend(self._obj_file_names()) 617 if self.mode == 'link' or self.mode == 'compile-and-link': 618 outputs.append(self._pdb_file_name()) 619 outputs.append(self._exe_file_name()) 620 621 return [x for x in outputs if x is not None] 622 623class GccBuilder(Builder): 624 def __init__(self, toolchain_type, args): 625 Builder.__init__(self, toolchain_type, args, '.o') 626 if sys.platform == 'darwin': 627 cmd = ['xcrun', '--sdk', args.apple_sdk, '--show-sdk-path'] 628 self.apple_sdk = subprocess.check_output(cmd).strip().decode('utf-8') 629 630 def _get_compilation_command(self, source, obj): 631 args = [] 632 633 args.append(self.compiler) 634 args.append('-m' + self.arch) 635 636 args.append('-g') 637 if self.opt == 'none': 638 args.append('-O0') 639 elif self.opt == 'basic': 640 args.append('-O2') 641 elif self.opt == 'lto': 642 args.append('-flto=thin') 643 if self.nodefaultlib: 644 args.append('-nostdinc') 645 args.append('-static') 646 args.append('-c') 647 648 args.extend(['-o', obj]) 649 args.append(source) 650 651 if sys.platform == 'darwin': 652 args.extend(['-isysroot', self.apple_sdk]) 653 654 return ('compiling', [source], obj, None, args) 655 656 def _get_link_command(self): 657 args = [] 658 args.append(self.compiler) 659 args.append('-m' + self.arch) 660 if self.nodefaultlib: 661 args.append('-nostdlib') 662 args.append('-static') 663 main_symbol = 'main' 664 if sys.platform == 'darwin': 665 main_symbol = '_main' 666 args.append('-Wl,-e,' + main_symbol) 667 if sys.platform.startswith('netbsd'): 668 for x in self.lib_paths: 669 args += ['-L' + x, '-Wl,-rpath,' + x] 670 args.extend(['-o', self._exe_file_name()]) 671 args.extend(self._obj_file_names()) 672 673 if sys.platform == 'darwin': 674 args.extend(['-isysroot', self.apple_sdk]) 675 676 return ('linking', self._obj_file_names(), self._exe_file_name(), None, args) 677 678 679 def output_files(self): 680 outputs = [] 681 if self.mode == 'compile' or self.mode == 'compile-and-link': 682 outputs.extend(self._obj_file_names()) 683 if self.mode == 'link' or self.mode == 'compile-and-link': 684 outputs.append(self._exe_file_name()) 685 686 return outputs 687 688def indent(text, spaces): 689 def prefixed_lines(): 690 prefix = ' ' * spaces 691 for line in text.splitlines(True): 692 yield prefix + line 693 return ''.join(prefixed_lines()) 694 695def build(commands): 696 global args 697 for (status, inputs, output, env, child_args) in commands: 698 print('\n\n') 699 inputs = [os.path.basename(x) for x in inputs] 700 output = os.path.basename(output) 701 print(status + ' {0} -> {1}'.format('+'.join(inputs), output)) 702 703 if args.verbose: 704 print(' Command Line: ' + ' '.join(child_args)) 705 print(' Env:') 706 print_environment(env) 707 if args.dry: 708 continue 709 710 popen = subprocess.Popen(child_args, 711 stdout=subprocess.PIPE, 712 stderr=subprocess.PIPE, 713 env=env, 714 universal_newlines=True) 715 stdout, stderr = popen.communicate() 716 res = popen.wait() 717 if res == -signal.SIGINT: 718 raise KeyboardInterrupt 719 print(' STDOUT:') 720 print(indent(stdout, 4)) 721 if res != 0: 722 print(' STDERR:') 723 print(indent(stderr, 4)) 724 sys.exit(res) 725 726def clean(files): 727 global args 728 if not files: 729 return 730 for o in files: 731 file = o if args.verbose else os.path.basename(o) 732 print('Cleaning {0}'.format(file)) 733 try: 734 if os.path.exists(o): 735 if not args.dry: 736 os.remove(o) 737 if args.verbose: 738 print(' The file was successfully cleaned.') 739 elif args.verbose: 740 print(' The file does not exist.') 741 except: 742 if args.verbose: 743 print(' The file could not be removed.') 744 745def fix_arguments(args): 746 if not args.inputs: 747 raise ValueError('No input files specified') 748 749 if args.output and args.mode == 'compile' and len(args.inputs) > 1: 750 raise ValueError('Cannot specify -o with mode=compile and multiple source files. Use --outdir instead.') 751 752 if not args.dry: 753 args.inputs = [os.path.abspath(x) for x in args.inputs] 754 755 # If user didn't specify the outdir, use the directory of the first input. 756 if not args.outdir: 757 if args.output: 758 args.outdir = os.path.dirname(args.output) 759 else: 760 args.outdir = os.path.dirname(args.inputs[0]) 761 args.outdir = os.path.abspath(args.outdir) 762 args.outdir = os.path.normpath(args.outdir) 763 764 # If user specified a non-absolute path for the output file, append the 765 # output directory to it. 766 if args.output: 767 if not os.path.isabs(args.output): 768 args.output = os.path.join(args.outdir, args.output) 769 args.output = os.path.normpath(args.output) 770 771fix_arguments(args) 772 773(toolchain_type, toolchain_path) = find_toolchain(args.compiler, args.tools_dir) 774if not toolchain_path or not toolchain_type: 775 print('Unable to find toolchain {0}'.format(args.compiler)) 776 sys.exit(1) 777 778if args.verbose: 779 print('Script Arguments:') 780 print(' Arch: ' + args.arch) 781 print(' Compiler: ' + args.compiler) 782 print(' Outdir: ' + args.outdir) 783 print(' Output: ' + args.output) 784 print(' Nodefaultlib: ' + str(args.nodefaultlib)) 785 print(' Opt: ' + args.opt) 786 print(' Mode: ' + args.mode) 787 print(' Clean: ' + str(args.clean)) 788 print(' Verbose: ' + str(args.verbose)) 789 print(' Dryrun: ' + str(args.dry)) 790 print(' Inputs: ' + format_text(args.inputs, 0, 10)) 791 print('Script Environment:') 792 print_environment(os.environ) 793 794args.compiler = toolchain_path 795if not os.path.exists(args.compiler) and not args.dry: 796 raise ValueError('The toolchain {} does not exist.'.format(args.compiler)) 797 798if toolchain_type == 'msvc' or toolchain_type=='clang-cl': 799 builder = MsvcBuilder(toolchain_type, args) 800else: 801 builder = GccBuilder(toolchain_type, args) 802 803if args.clean: 804 clean(builder.output_files()) 805 806cmds = builder.build_commands() 807 808build(cmds) 809