Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / lldb / test / Shell / helper / build.py
blob2a04967c89bc305e8cf968a988a333772639699c
1 #!/usr/bin/env python
3 import argparse
4 import os
5 import platform
6 import shutil
7 import signal
8 import subprocess
9 import sys
11 if 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
19 if __name__ != "__main__":
20 raise RuntimeError("Do not import this script, run it instead")
23 parser = argparse.ArgumentParser(description="LLDB compilation wrapper")
24 parser.add_argument(
25 "--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.",
34 parser.add_argument(
35 "--compiler",
36 metavar="compiler",
37 dest="compiler",
38 required=True,
39 help="Path to a compiler executable, or one of the values [any, msvc, clang-cl, gcc, clang]",
42 parser.add_argument(
43 "--libs-dir",
44 metavar="directory",
45 dest="libs_dir",
46 required=False,
47 action="append",
48 help="If specified, a path to linked libraries to be passed via -L",
51 parser.add_argument(
52 "--tools-dir",
53 metavar="directory",
54 dest="tools_dir",
55 required=False,
56 action="append",
57 help="If specified, a path to search in addition to PATH when --compiler is not an exact path",
60 parser.add_argument(
61 "--objc-gnustep-dir",
62 metavar="directory",
63 dest="objc_gnustep_dir",
64 required=False,
65 help="If specified, a path to GNUstep libobjc2 runtime for use on Windows and Linux",
68 parser.add_argument(
69 "--objc-gnustep",
70 dest="objc_gnustep",
71 action="store_true",
72 default=False,
73 help="Include and link GNUstep libobjc2 (Windows and Linux only)",
76 parser.add_argument(
77 "--sysroot",
78 metavar="directory",
79 dest="sysroot",
80 required=False,
81 help="If specified, a sysroot to be passed via --sysroot",
84 if sys.platform == "darwin":
85 parser.add_argument(
86 "--apple-sdk",
87 metavar="apple_sdk",
88 dest="apple_sdk",
89 default="macosx",
90 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.",
93 parser.add_argument(
94 "--output",
95 "-o",
96 dest="output",
97 metavar="file",
98 required=False,
99 default="",
100 help="Path to output file",
103 parser.add_argument(
104 "--outdir",
105 "-d",
106 dest="outdir",
107 metavar="directory",
108 required=False,
109 help="Directory for output files",
112 parser.add_argument(
113 "--nodefaultlib",
114 dest="nodefaultlib",
115 action="store_true",
116 default=False,
117 help="When specified, the resulting image should not link against system libraries or include system headers. Useful when writing cross-targeting tests.",
120 parser.add_argument(
121 "--opt",
122 dest="opt",
123 default="none",
124 choices=["none", "basic", "lto"],
125 help="Optimization level",
128 parser.add_argument(
129 "--mode",
130 dest="mode",
131 default="compile-and-link",
132 choices=["compile", "link", "compile-and-link"],
133 help="Specifies whether to compile, link, or both",
136 parser.add_argument(
137 "--noclean",
138 dest="clean",
139 action="store_false",
140 default=True,
141 help="Dont clean output file before building",
144 parser.add_argument(
145 "--verbose",
146 dest="verbose",
147 action="store_true",
148 default=False,
149 help="Print verbose output",
152 parser.add_argument(
153 "-n",
154 "--dry-run",
155 dest="dry",
156 action="store_true",
157 default=False,
158 help="Print the commands that would run, but dont actually run them",
161 parser.add_argument(
162 "inputs",
163 metavar="file",
164 nargs="+",
165 help="Source file(s) to compile / object file(s) to link",
168 parser.add_argument(
169 "--std",
170 metavar="std",
171 dest="std",
172 required=False,
173 help="Specify the C/C++ standard.",
177 args = parser.parse_args(args=sys.argv[1:])
180 def to_string(b):
181 """Return the parameter as type 'str', possibly encoding it.
183 In Python2, the 'str' type is the same as 'bytes'. In Python3, the
184 'str' type is (essentially) Python2's 'unicode' type, and 'bytes' is
185 distinct.
187 This function is copied from llvm/utils/lit/lit/util.py
189 if isinstance(b, str):
190 # In Python2, this branch is taken for types 'str' and 'bytes'.
191 # In Python3, this branch is taken only for 'str'.
192 return b
193 if isinstance(b, bytes):
194 # In Python2, this branch is never taken ('bytes' is handled as 'str').
195 # In Python3, this is true only for 'bytes'.
196 try:
197 return b.decode("utf-8")
198 except UnicodeDecodeError:
199 # If the value is not valid Unicode, return the default
200 # repr-line encoding.
201 return str(b)
203 # By this point, here's what we *don't* have:
205 # - In Python2:
206 # - 'str' or 'bytes' (1st branch above)
207 # - In Python3:
208 # - 'str' (1st branch above)
209 # - 'bytes' (2nd branch above)
211 # The last type we might expect is the Python2 'unicode' type. There is no
212 # 'unicode' type in Python3 (all the Python3 cases were already handled). In
213 # order to get a 'str' object, we need to encode the 'unicode' object.
214 try:
215 return b.encode("utf-8")
216 except AttributeError:
217 raise TypeError("not sure how to convert %s to %s" % (type(b), str))
220 def format_text(lines, indent_0, indent_n):
221 result = " " * indent_0 + lines[0]
222 for next in lines[1:]:
223 result = result + "\n{0}{1}".format(" " * indent_n, next)
224 return result
227 def print_environment(env):
228 if env is None:
229 print(" Inherited")
230 return
231 for e in env:
232 value = env[e]
233 lines = value.split(os.pathsep)
234 formatted_value = format_text(lines, 0, 7 + len(e))
235 print(" {0} = {1}".format(e, formatted_value))
238 def find_executable(binary_name, search_paths):
239 # shutil.which will ignore PATH if given a path argument, we want to include it.
240 search_paths.append(os.environ.get("PATH", ""))
241 search_path = os.pathsep.join(search_paths)
242 binary_path = shutil.which(binary_name, path=search_path)
243 if binary_path is not None:
244 # So for example, we get '/bin/gcc' instead of '/usr/../bin/gcc'.
245 binary_path = os.path.normpath(binary_path)
246 return binary_path
249 def find_toolchain(compiler, tools_dir):
250 if compiler == "msvc":
251 return ("msvc", find_executable("cl", tools_dir))
252 if compiler == "clang-cl":
253 return ("clang-cl", find_executable("clang-cl", tools_dir))
254 if compiler == "gcc":
255 return ("gcc", find_executable("g++", tools_dir))
256 if compiler == "clang":
257 return ("clang", find_executable("clang++", tools_dir))
258 if compiler == "any":
259 priorities = []
260 if sys.platform == "win32":
261 priorities = ["clang-cl", "msvc", "clang", "gcc"]
262 else:
263 priorities = ["clang", "gcc", "clang-cl"]
264 for toolchain in priorities:
265 (type, dir) = find_toolchain(toolchain, tools_dir)
266 if type and dir:
267 return (type, dir)
268 # Could not find any toolchain.
269 return (None, None)
271 # From here on, assume that |compiler| is a path to a file.
272 file = os.path.basename(compiler)
273 name, ext = os.path.splitext(file)
274 if file.lower() == "cl.exe":
275 return ("msvc", compiler)
276 if name == "clang-cl":
277 return ("clang-cl", compiler)
278 if name.startswith("clang"):
279 return ("clang", compiler)
280 if name.startswith("gcc") or name.startswith("g++"):
281 return ("gcc", compiler)
282 if name == "cc" or name == "c++":
283 return ("generic", compiler)
284 return ("unknown", compiler)
287 class Builder(object):
288 def __init__(self, toolchain_type, args, obj_ext):
289 self.toolchain_type = toolchain_type
290 self.inputs = args.inputs
291 self.arch = args.arch
292 self.opt = args.opt
293 self.outdir = args.outdir
294 self.compiler = args.compiler
295 self.clean = args.clean
296 self.output = args.output
297 self.mode = args.mode
298 self.nodefaultlib = args.nodefaultlib
299 self.verbose = args.verbose
300 self.obj_ext = obj_ext
301 self.lib_paths = args.libs_dir
302 self.std = args.std
303 assert (
304 not args.objc_gnustep or args.objc_gnustep_dir
305 ), "--objc-gnustep specified without path to libobjc2"
306 self.objc_gnustep_inc = (
307 os.path.join(args.objc_gnustep_dir, "include")
308 if args.objc_gnustep_dir
309 else None
311 self.objc_gnustep_lib = (
312 os.path.join(args.objc_gnustep_dir, "lib")
313 if args.objc_gnustep_dir
314 else None
316 self.sysroot = args.sysroot
318 def _exe_file_name(self):
319 assert self.mode != "compile"
320 return self.output
322 def _output_name(self, input, extension, with_executable=False):
323 basename = os.path.splitext(os.path.basename(input))[0] + extension
324 if with_executable:
325 exe_basename = os.path.basename(self._exe_file_name())
326 basename = exe_basename + "-" + basename
328 output = os.path.join(self.outdir, basename)
329 return os.path.normpath(output)
331 def _obj_file_names(self):
332 if self.mode == "link":
333 return self.inputs
335 if self.mode == "compile-and-link":
336 # Object file names should factor in both the input file (source)
337 # name and output file (executable) name, to ensure that two tests
338 # which share a common source file don't race to write the same
339 # object file.
340 return [self._output_name(x, self.obj_ext, True) for x in self.inputs]
342 if self.mode == "compile" and self.output:
343 return [self.output]
345 return [self._output_name(x, self.obj_ext) for x in self.inputs]
347 def build_commands(self):
348 commands = []
349 if self.mode == "compile" or self.mode == "compile-and-link":
350 for input, output in zip(self.inputs, self._obj_file_names()):
351 commands.append(self._get_compilation_command(input, output))
352 if self.mode == "link" or self.mode == "compile-and-link":
353 commands.append(self._get_link_command())
354 return commands
357 class MsvcBuilder(Builder):
358 def __init__(self, toolchain_type, args):
359 Builder.__init__(self, toolchain_type, args, ".obj")
361 if platform.uname().machine.lower() == "arm64":
362 self.msvc_arch_str = "arm" if self.arch == "32" else "arm64"
363 else:
364 self.msvc_arch_str = "x86" if self.arch == "32" else "x64"
366 if toolchain_type == "msvc":
367 # Make sure we're using the appropriate toolchain for the desired
368 # target type.
369 compiler_parent_dir = os.path.dirname(self.compiler)
370 selected_target_version = os.path.basename(compiler_parent_dir)
371 if selected_target_version != self.msvc_arch_str:
372 host_dir = os.path.dirname(compiler_parent_dir)
373 self.compiler = os.path.join(host_dir, self.msvc_arch_str, "cl.exe")
374 if self.verbose:
375 print(
376 'Using alternate compiler "{0}" to match selected target.'.format(
377 self.compiler
381 if self.mode == "link" or self.mode == "compile-and-link":
382 self.linker = (
383 self._find_linker("link")
384 if toolchain_type == "msvc"
385 else self._find_linker("lld-link", args.tools_dir)
387 if not self.linker:
388 raise ValueError("Unable to find an appropriate linker.")
390 self.compile_env, self.link_env = self._get_visual_studio_environment()
392 def _find_linker(self, name, search_paths=[]):
393 compiler_dir = os.path.dirname(self.compiler)
394 linker_path = find_executable(name, [compiler_dir] + search_paths)
395 if linker_path is None:
396 raise ValueError("Could not find '{}'".format(name))
397 return linker_path
399 def _get_vc_install_dir(self):
400 dir = os.getenv("VCINSTALLDIR", None)
401 if dir:
402 if self.verbose:
403 print("Using %VCINSTALLDIR% {}".format(dir))
404 return dir
406 dir = os.getenv("VSINSTALLDIR", None)
407 if dir:
408 if self.verbose:
409 print("Using %VSINSTALLDIR% {}".format(dir))
410 return os.path.join(dir, "VC")
412 dir = os.getenv("VS2019INSTALLDIR", None)
413 if dir:
414 if self.verbose:
415 print("Using %VS2019INSTALLDIR% {}".format(dir))
416 return os.path.join(dir, "VC")
418 dir = os.getenv("VS2017INSTALLDIR", None)
419 if dir:
420 if self.verbose:
421 print("Using %VS2017INSTALLDIR% {}".format(dir))
422 return os.path.join(dir, "VC")
424 dir = os.getenv("VS2015INSTALLDIR", None)
425 if dir:
426 if self.verbose:
427 print("Using %VS2015INSTALLDIR% {}".format(dir))
428 return os.path.join(dir, "VC")
429 return None
431 def _get_vctools_version(self):
432 ver = os.getenv("VCToolsVersion", None)
433 if ver:
434 if self.verbose:
435 print("Using %VCToolsVersion% {}".format(ver))
436 return ver
438 vcinstalldir = self._get_vc_install_dir()
439 vcinstalldir = os.path.join(vcinstalldir, "Tools", "MSVC")
440 subdirs = next(os.walk(vcinstalldir))[1]
441 if not subdirs:
442 return None
444 from distutils.version import StrictVersion
446 subdirs.sort(key=lambda x: StrictVersion(x))
448 if self.verbose:
449 full_path = os.path.join(vcinstalldir, subdirs[-1])
450 print(
451 "Using VC tools version directory {0} found by directory walk.".format(
452 full_path
455 return subdirs[-1]
457 def _get_vctools_install_dir(self):
458 dir = os.getenv("VCToolsInstallDir", None)
459 if dir:
460 if self.verbose:
461 print("Using %VCToolsInstallDir% {}".format(dir))
462 return dir
464 vcinstalldir = self._get_vc_install_dir()
465 if not vcinstalldir:
466 return None
467 vctoolsver = self._get_vctools_version()
468 if not vctoolsver:
469 return None
470 result = os.path.join(vcinstalldir, "Tools", "MSVC", vctoolsver)
471 if not os.path.exists(result):
472 return None
473 if self.verbose:
474 print(
475 "Using VC tools install dir {} found by directory walk".format(result)
477 return result
479 def _find_windows_sdk_in_registry_view(self, view):
480 products_key = None
481 roots_key = None
482 installed_options_keys = []
483 try:
484 sam = view | winreg.KEY_READ
485 products_key = winreg.OpenKey(
486 winreg.HKEY_LOCAL_MACHINE,
487 r"Software\Microsoft\Windows Kits\Installed Products",
489 sam,
492 # This is the GUID for the desktop component. If this is present
493 # then the components required for the Desktop SDK are installed.
494 # If not it will throw an exception.
495 winreg.QueryValueEx(products_key, "{5A3D81EC-D870-9ECF-D997-24BDA6644752}")
497 roots_key = winreg.OpenKey(
498 winreg.HKEY_LOCAL_MACHINE,
499 r"Software\Microsoft\Windows Kits\Installed Roots",
501 sam,
503 root_dir = winreg.QueryValueEx(roots_key, "KitsRoot10")
504 root_dir = to_string(root_dir[0])
505 sdk_versions = []
506 index = 0
507 while True:
508 # Installed SDK versions are stored as sub-keys of the
509 # 'Installed Roots' key. Find all of their names, then sort
510 # them by version
511 try:
512 ver_key = winreg.EnumKey(roots_key, index)
513 sdk_versions.append(ver_key)
514 index = index + 1
515 except WindowsError:
516 break
517 if not sdk_versions:
518 return (None, None)
520 # Windows SDK version numbers consist of 4 dotted components, so we
521 # have to use LooseVersion, as StrictVersion supports 3 or fewer.
522 from distutils.version import LooseVersion
524 sdk_versions.sort(key=lambda x: LooseVersion(x), reverse=True)
525 option_value_name = "OptionId.DesktopCPP" + self.msvc_arch_str
526 for v in sdk_versions:
527 try:
528 version_subkey = v + r"\Installed Options"
529 key = winreg.OpenKey(roots_key, version_subkey)
530 installed_options_keys.append(key)
531 (value, value_type) = winreg.QueryValueEx(key, option_value_name)
532 if value == 1:
533 # The proper architecture is installed. Return the
534 # associated paths.
535 if self.verbose:
536 print(
537 "Found Installed Windows SDK v{0} at {1}".format(
538 v, root_dir
541 return (root_dir, v)
542 except:
543 continue
544 except:
545 return (None, None)
546 finally:
547 del products_key
548 del roots_key
549 for k in installed_options_keys:
550 del k
551 return (None, None)
553 def _find_windows_sdk_in_registry(self):
554 # This could be a clang-cl cross-compile. If so, there's no registry
555 # so just exit.
556 if sys.platform != "win32":
557 return (None, None)
558 if self.verbose:
559 print("Looking for Windows SDK in 64-bit registry.")
560 dir, ver = self._find_windows_sdk_in_registry_view(winreg.KEY_WOW64_64KEY)
561 if not dir or not ver:
562 if self.verbose:
563 print("Looking for Windows SDK in 32-bit registry.")
564 dir, ver = self._find_windows_sdk_in_registry_view(winreg.KEY_WOW64_32KEY)
566 return (dir, ver)
568 def _get_winsdk_dir(self):
569 # If a Windows SDK is specified in the environment, use that. Otherwise
570 # try to find one in the Windows registry.
571 dir = os.getenv("WindowsSdkDir", None)
572 if not dir or not os.path.exists(dir):
573 return self._find_windows_sdk_in_registry()
574 ver = os.getenv("WindowsSDKLibVersion", None)
575 if not ver:
576 return self._find_windows_sdk_in_registry()
578 ver = ver.rstrip("\\")
579 if self.verbose:
580 print("Using %WindowsSdkDir% {}".format(dir))
581 print("Using %WindowsSDKLibVersion% {}".format(ver))
582 return (dir, ver)
584 def _get_msvc_native_toolchain_dir(self):
585 assert self.toolchain_type == "msvc"
586 compiler_dir = os.path.dirname(self.compiler)
587 target_dir = os.path.dirname(compiler_dir)
588 host_name = os.path.basename(target_dir)
589 host_name = host_name[4:].lower()
590 return os.path.join(target_dir, host_name)
592 def _get_visual_studio_environment(self):
593 vctools = self._get_vctools_install_dir()
594 winsdk, winsdkver = self._get_winsdk_dir()
596 if not vctools and self.verbose:
597 print("Unable to find VC tools installation directory.")
598 if (not winsdk or not winsdkver) and self.verbose:
599 print("Unable to find Windows SDK directory.")
601 vcincludes = []
602 vclibs = []
603 sdkincludes = []
604 sdklibs = []
605 if vctools is not None:
606 includes = [["ATLMFC", "include"], ["include"]]
607 libs = [["ATLMFC", "lib"], ["lib"]]
608 vcincludes = [os.path.join(vctools, *y) for y in includes]
609 vclibs = [os.path.join(vctools, *y) for y in libs]
610 if winsdk is not None:
611 includes = [
612 ["include", winsdkver, "ucrt"],
613 ["include", winsdkver, "shared"],
614 ["include", winsdkver, "um"],
615 ["include", winsdkver, "winrt"],
616 ["include", winsdkver, "cppwinrt"],
618 libs = [["lib", winsdkver, "ucrt"], ["lib", winsdkver, "um"]]
619 sdkincludes = [os.path.join(winsdk, *y) for y in includes]
620 sdklibs = [os.path.join(winsdk, *y) for y in libs]
622 includes = vcincludes + sdkincludes
623 libs = vclibs + sdklibs
624 libs = [os.path.join(x, self.msvc_arch_str) for x in libs]
625 compileenv = None
626 linkenv = None
627 defaultenv = {}
628 if sys.platform == "win32":
629 defaultenv = {
630 x: os.environ[x] for x in ["SystemDrive", "SystemRoot", "TMP", "TEMP"]
632 # The directory to mspdbcore.dll needs to be in PATH, but this is
633 # always in the native toolchain path, not the cross-toolchain
634 # path. So, for example, if we're using HostX64\x86 then we need
635 # to add HostX64\x64 to the path, and if we're using HostX86\x64
636 # then we need to add HostX86\x86 to the path.
637 if self.toolchain_type == "msvc":
638 defaultenv["PATH"] = self._get_msvc_native_toolchain_dir()
640 if includes:
641 compileenv = {}
642 compileenv["INCLUDE"] = os.pathsep.join(includes)
643 compileenv.update(defaultenv)
644 if libs:
645 linkenv = {}
646 linkenv["LIB"] = os.pathsep.join(libs)
647 linkenv.update(defaultenv)
648 return (compileenv, linkenv)
650 def _ilk_file_names(self):
651 if self.mode == "link":
652 return []
654 return [self._output_name(x, ".ilk") for x in self.inputs]
656 def _pdb_file_name(self):
657 if self.mode == "compile":
658 return None
659 return os.path.splitext(self.output)[0] + ".pdb"
661 def _get_compilation_command(self, source, obj):
662 args = []
664 args.append(self.compiler)
665 if self.toolchain_type == "clang-cl":
666 args.append("-m" + self.arch)
668 if self.opt == "none":
669 args.append("/Od")
670 elif self.opt == "basic":
671 args.append("/O2")
672 elif self.opt == "lto":
673 if self.toolchain_type == "msvc":
674 args.append("/GL")
675 args.append("/Gw")
676 else:
677 args.append("-flto=thin")
678 if self.nodefaultlib:
679 args.append("/GS-")
680 args.append("/GR-")
681 args.append("/Z7")
682 if self.toolchain_type == "clang-cl":
683 args.append("-Xclang")
684 args.append("-fkeep-static-consts")
685 args.append("-fms-compatibility-version=19")
686 args.append("/c")
688 args.append("/Fo" + obj)
689 if self.toolchain_type == "clang-cl":
690 args.append("--")
691 args.append(source)
693 if self.std:
694 args.append("/std:" + self.std)
696 return ("compiling", [source], obj, self.compile_env, args)
698 def _get_link_command(self):
699 args = []
700 args.append(self.linker)
701 args.append("/DEBUG:FULL")
702 args.append("/INCREMENTAL:NO")
703 if self.nodefaultlib:
704 args.append("/nodefaultlib")
705 args.append("/entry:main")
706 args.append("/PDB:" + self._pdb_file_name())
707 args.append("/OUT:" + self._exe_file_name())
708 args.extend(self._obj_file_names())
710 return (
711 "linking",
712 self._obj_file_names(),
713 self._exe_file_name(),
714 self.link_env,
715 args,
718 def build_commands(self):
719 commands = []
720 if self.mode == "compile" or self.mode == "compile-and-link":
721 for input, output in zip(self.inputs, self._obj_file_names()):
722 commands.append(self._get_compilation_command(input, output))
723 if self.mode == "link" or self.mode == "compile-and-link":
724 commands.append(self._get_link_command())
725 return commands
727 def output_files(self):
728 outputs = []
729 if self.mode == "compile" or self.mode == "compile-and-link":
730 outputs.extend(self._ilk_file_names())
731 outputs.extend(self._obj_file_names())
732 if self.mode == "link" or self.mode == "compile-and-link":
733 outputs.append(self._pdb_file_name())
734 outputs.append(self._exe_file_name())
736 return [x for x in outputs if x is not None]
739 class GccBuilder(Builder):
740 def __init__(self, toolchain_type, args):
741 Builder.__init__(self, toolchain_type, args, ".o")
742 if sys.platform == "darwin":
743 cmd = ["xcrun", "--sdk", args.apple_sdk, "--show-sdk-path"]
744 self.apple_sdk = subprocess.check_output(cmd).strip().decode("utf-8")
746 def _get_compilation_command(self, source, obj):
747 args = []
749 args.append(self.compiler)
750 args.append("-m" + self.arch)
752 args.append("-g")
753 if self.opt == "none":
754 args.append("-O0")
755 elif self.opt == "basic":
756 args.append("-O2")
757 elif self.opt == "lto":
758 args.append("-flto=thin")
759 if self.nodefaultlib:
760 args.append("-nostdinc")
761 args.append("-static")
762 args.append("-c")
764 if sys.platform == "darwin":
765 args.extend(["-isysroot", self.apple_sdk])
766 elif self.objc_gnustep_inc:
767 if source.endswith(".m") or source.endswith(".mm"):
768 args.extend(["-fobjc-runtime=gnustep-2.0", "-I", self.objc_gnustep_inc])
769 if sys.platform == "win32":
770 args.extend(
771 ["-Xclang", "-gcodeview", "-Xclang", "--dependent-lib=msvcrtd"]
773 elif self.sysroot:
774 args.extend(["--sysroot", self.sysroot])
776 if self.std:
777 args.append("-std={0}".format(self.std))
779 args.extend(["-o", obj])
780 args.append(source)
782 return ("compiling", [source], obj, None, args)
784 def _get_link_command(self):
785 args = []
786 args.append(self.compiler)
787 args.append("-m" + self.arch)
788 if self.nodefaultlib:
789 args.append("-nostdlib")
790 args.append("-static")
791 main_symbol = "main"
792 if sys.platform == "darwin":
793 main_symbol = "_main"
794 args.append("-Wl,-e," + main_symbol)
795 if sys.platform.startswith("netbsd"):
796 for x in self.lib_paths:
797 args += ["-L" + x, "-Wl,-rpath," + x]
798 args.extend(["-o", self._exe_file_name()])
799 args.extend(self._obj_file_names())
801 if sys.platform == "darwin":
802 args.extend(["-isysroot", self.apple_sdk])
803 elif self.objc_gnustep_lib:
804 args.extend(["-L", self.objc_gnustep_lib, "-lobjc"])
805 if sys.platform == "linux":
806 args.extend(["-Wl,-rpath," + self.objc_gnustep_lib])
807 elif sys.platform == "win32":
808 args.extend(
809 ["-fuse-ld=lld-link", "-g", "-Xclang", "--dependent-lib=msvcrtd"]
811 elif self.sysroot:
812 args.extend(["--sysroot", self.sysroot])
814 return ("linking", self._obj_file_names(), self._exe_file_name(), None, args)
816 def output_files(self):
817 outputs = []
818 if self.mode == "compile" or self.mode == "compile-and-link":
819 outputs.extend(self._obj_file_names())
820 if self.mode == "link" or self.mode == "compile-and-link":
821 outputs.append(self._exe_file_name())
823 return outputs
826 def indent(text, spaces):
827 def prefixed_lines():
828 prefix = " " * spaces
829 for line in text.splitlines(True):
830 yield prefix + line
832 return "".join(prefixed_lines())
835 def build(commands):
836 global args
837 for status, inputs, output, env, child_args in commands:
838 print("\n\n")
839 inputs = [os.path.basename(x) for x in inputs]
840 output = os.path.basename(output)
841 print(status + " {0} -> {1}".format("+".join(inputs), output))
843 if args.verbose:
844 print(" Command Line: " + " ".join(child_args))
845 print(" Env:")
846 print_environment(env)
847 if args.dry:
848 continue
850 popen = subprocess.Popen(
851 child_args,
852 stdout=subprocess.PIPE,
853 stderr=subprocess.PIPE,
854 env=env,
855 universal_newlines=True,
857 stdout, stderr = popen.communicate()
858 res = popen.wait()
859 if res == -signal.SIGINT:
860 raise KeyboardInterrupt
861 print(" STDOUT:")
862 print(indent(stdout, 4))
863 if res != 0:
864 print(" STDERR:")
865 print(indent(stderr, 4))
866 sys.exit(res)
869 def clean(files):
870 global args
871 if not files:
872 return
873 for o in files:
874 file = o if args.verbose else os.path.basename(o)
875 print("Cleaning {0}".format(file))
876 try:
877 if os.path.exists(o):
878 if not args.dry:
879 os.remove(o)
880 if args.verbose:
881 print(" The file was successfully cleaned.")
882 elif args.verbose:
883 print(" The file does not exist.")
884 except:
885 if args.verbose:
886 print(" The file could not be removed.")
889 def fix_arguments(args):
890 if not args.inputs:
891 raise ValueError("No input files specified")
893 if args.output and args.mode == "compile" and len(args.inputs) > 1:
894 raise ValueError(
895 "Cannot specify -o with mode=compile and multiple source files. Use --outdir instead."
898 if not args.dry:
899 args.inputs = [os.path.abspath(x) for x in args.inputs]
901 # If user didn't specify the outdir, use the directory of the first input.
902 if not args.outdir:
903 if args.output:
904 args.outdir = os.path.dirname(args.output)
905 else:
906 args.outdir = os.path.dirname(args.inputs[0])
907 args.outdir = os.path.abspath(args.outdir)
908 args.outdir = os.path.normpath(args.outdir)
910 # If user specified a non-absolute path for the output file, append the
911 # output directory to it.
912 if args.output:
913 if not os.path.isabs(args.output):
914 args.output = os.path.join(args.outdir, args.output)
915 args.output = os.path.normpath(args.output)
918 fix_arguments(args)
920 (toolchain_type, toolchain_path) = find_toolchain(args.compiler, args.tools_dir)
921 if not toolchain_path or not toolchain_type:
922 print("Unable to find toolchain {0}".format(args.compiler))
923 sys.exit(1)
925 if args.verbose:
926 print("Script Arguments:")
927 print(" Arch: " + args.arch)
928 print(" Compiler: " + args.compiler)
929 print(" Outdir: " + args.outdir)
930 print(" Output: " + args.output)
931 print(" Nodefaultlib: " + str(args.nodefaultlib))
932 print(" Opt: " + args.opt)
933 print(" Mode: " + args.mode)
934 print(" Clean: " + str(args.clean))
935 print(" Verbose: " + str(args.verbose))
936 print(" Dryrun: " + str(args.dry))
937 print(" Inputs: " + format_text(args.inputs, 0, 10))
938 print(" C/C++ Standard: " + str(args.std))
939 print("Script Environment:")
940 print_environment(os.environ)
942 args.compiler = toolchain_path
943 if not os.path.exists(args.compiler) and not args.dry:
944 raise ValueError("The toolchain {} does not exist.".format(args.compiler))
946 if toolchain_type == "msvc" or toolchain_type == "clang-cl":
947 builder = MsvcBuilder(toolchain_type, args)
948 else:
949 builder = GccBuilder(toolchain_type, args)
951 if args.clean:
952 clean(builder.output_files())
954 cmds = builder.build_commands()
956 build(cmds)