[OptTable] Fix typo VALUE => VALUES (NFCI) (#121523)
[llvm-project.git] / lldb / test / Shell / helper / build.py
blobb2b8146e88c75b46d6eccbf32ea520815da2ad62
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 packaging import version
446 subdirs.sort(key=lambda x: version.parse(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 from packaging import version
522 sdk_versions.sort(key=lambda x: version.parse(x), reverse=True)
523 option_value_name = "OptionId.DesktopCPP" + self.msvc_arch_str
524 for v in sdk_versions:
525 try:
526 version_subkey = v + r"\Installed Options"
527 key = winreg.OpenKey(roots_key, version_subkey)
528 installed_options_keys.append(key)
529 (value, value_type) = winreg.QueryValueEx(key, option_value_name)
530 if value == 1:
531 # The proper architecture is installed. Return the
532 # associated paths.
533 if self.verbose:
534 print(
535 "Found Installed Windows SDK v{0} at {1}".format(
536 v, root_dir
539 return (root_dir, v)
540 except:
541 continue
542 except:
543 return (None, None)
544 finally:
545 del products_key
546 del roots_key
547 for k in installed_options_keys:
548 del k
549 return (None, None)
551 def _find_windows_sdk_in_registry(self):
552 # This could be a clang-cl cross-compile. If so, there's no registry
553 # so just exit.
554 if sys.platform != "win32":
555 return (None, None)
556 if self.verbose:
557 print("Looking for Windows SDK in 64-bit registry.")
558 dir, ver = self._find_windows_sdk_in_registry_view(winreg.KEY_WOW64_64KEY)
559 if not dir or not ver:
560 if self.verbose:
561 print("Looking for Windows SDK in 32-bit registry.")
562 dir, ver = self._find_windows_sdk_in_registry_view(winreg.KEY_WOW64_32KEY)
564 return (dir, ver)
566 def _get_winsdk_dir(self):
567 # If a Windows SDK is specified in the environment, use that. Otherwise
568 # try to find one in the Windows registry.
569 dir = os.getenv("WindowsSdkDir", None)
570 if not dir or not os.path.exists(dir):
571 return self._find_windows_sdk_in_registry()
572 ver = os.getenv("WindowsSDKLibVersion", None)
573 if not ver:
574 return self._find_windows_sdk_in_registry()
576 ver = ver.rstrip("\\")
577 if self.verbose:
578 print("Using %WindowsSdkDir% {}".format(dir))
579 print("Using %WindowsSDKLibVersion% {}".format(ver))
580 return (dir, ver)
582 def _get_msvc_native_toolchain_dir(self):
583 assert self.toolchain_type == "msvc"
584 compiler_dir = os.path.dirname(self.compiler)
585 target_dir = os.path.dirname(compiler_dir)
586 host_name = os.path.basename(target_dir)
587 host_name = host_name[4:].lower()
588 return os.path.join(target_dir, host_name)
590 def _get_visual_studio_environment(self):
591 vctools = self._get_vctools_install_dir()
592 winsdk, winsdkver = self._get_winsdk_dir()
594 if not vctools and self.verbose:
595 print("Unable to find VC tools installation directory.")
596 if (not winsdk or not winsdkver) and self.verbose:
597 print("Unable to find Windows SDK directory.")
599 vcincludes = []
600 vclibs = []
601 sdkincludes = []
602 sdklibs = []
603 if vctools is not None:
604 includes = [["ATLMFC", "include"], ["include"]]
605 libs = [["ATLMFC", "lib"], ["lib"]]
606 vcincludes = [os.path.join(vctools, *y) for y in includes]
607 vclibs = [os.path.join(vctools, *y) for y in libs]
608 if winsdk is not None:
609 includes = [
610 ["include", winsdkver, "ucrt"],
611 ["include", winsdkver, "shared"],
612 ["include", winsdkver, "um"],
613 ["include", winsdkver, "winrt"],
614 ["include", winsdkver, "cppwinrt"],
616 libs = [["lib", winsdkver, "ucrt"], ["lib", winsdkver, "um"]]
617 sdkincludes = [os.path.join(winsdk, *y) for y in includes]
618 sdklibs = [os.path.join(winsdk, *y) for y in libs]
620 includes = vcincludes + sdkincludes
621 libs = vclibs + sdklibs
622 libs = [os.path.join(x, self.msvc_arch_str) for x in libs]
623 compileenv = None
624 linkenv = None
625 defaultenv = {}
626 if sys.platform == "win32":
627 defaultenv = {
628 x: os.environ[x] for x in ["SystemDrive", "SystemRoot", "TMP", "TEMP"]
630 # The directory to mspdbcore.dll needs to be in PATH, but this is
631 # always in the native toolchain path, not the cross-toolchain
632 # path. So, for example, if we're using HostX64\x86 then we need
633 # to add HostX64\x64 to the path, and if we're using HostX86\x64
634 # then we need to add HostX86\x86 to the path.
635 if self.toolchain_type == "msvc":
636 defaultenv["PATH"] = self._get_msvc_native_toolchain_dir()
638 if includes:
639 compileenv = {}
640 compileenv["INCLUDE"] = os.pathsep.join(includes)
641 compileenv.update(defaultenv)
642 if libs:
643 linkenv = {}
644 linkenv["LIB"] = os.pathsep.join(libs)
645 linkenv.update(defaultenv)
646 return (compileenv, linkenv)
648 def _ilk_file_names(self):
649 if self.mode == "link":
650 return []
652 return [self._output_name(x, ".ilk") for x in self.inputs]
654 def _pdb_file_name(self):
655 if self.mode == "compile":
656 return None
657 return os.path.splitext(self.output)[0] + ".pdb"
659 def _get_compilation_command(self, source, obj):
660 args = []
662 args.append(self.compiler)
663 if self.toolchain_type == "clang-cl":
664 args.append("-m" + self.arch)
666 if self.opt == "none":
667 args.append("/Od")
668 elif self.opt == "basic":
669 args.append("/O2")
670 elif self.opt == "lto":
671 if self.toolchain_type == "msvc":
672 args.append("/GL")
673 args.append("/Gw")
674 else:
675 args.append("-flto=thin")
676 if self.nodefaultlib:
677 args.append("/GS-")
678 args.append("/GR-")
679 args.append("/Z7")
680 if self.toolchain_type == "clang-cl":
681 args.append("-Xclang")
682 args.append("-fkeep-static-consts")
683 args.append("-fms-compatibility-version=19")
684 args.append("/c")
686 args.append("/Fo" + obj)
687 if self.toolchain_type == "clang-cl":
688 args.append("--")
689 args.append(source)
691 if self.std:
692 args.append("/std:" + self.std)
694 return ("compiling", [source], obj, self.compile_env, args)
696 def _get_link_command(self):
697 args = []
698 args.append(self.linker)
699 args.append("/DEBUG:FULL")
700 args.append("/INCREMENTAL:NO")
701 if self.nodefaultlib:
702 args.append("/nodefaultlib")
703 args.append("/entry:main")
704 args.append("/PDB:" + self._pdb_file_name())
705 args.append("/OUT:" + self._exe_file_name())
706 args.extend(self._obj_file_names())
708 return (
709 "linking",
710 self._obj_file_names(),
711 self._exe_file_name(),
712 self.link_env,
713 args,
716 def build_commands(self):
717 commands = []
718 if self.mode == "compile" or self.mode == "compile-and-link":
719 for input, output in zip(self.inputs, self._obj_file_names()):
720 commands.append(self._get_compilation_command(input, output))
721 if self.mode == "link" or self.mode == "compile-and-link":
722 commands.append(self._get_link_command())
723 return commands
725 def output_files(self):
726 outputs = []
727 if self.mode == "compile" or self.mode == "compile-and-link":
728 outputs.extend(self._ilk_file_names())
729 outputs.extend(self._obj_file_names())
730 if self.mode == "link" or self.mode == "compile-and-link":
731 outputs.append(self._pdb_file_name())
732 outputs.append(self._exe_file_name())
734 return [x for x in outputs if x is not None]
737 class GccBuilder(Builder):
738 def __init__(self, toolchain_type, args):
739 Builder.__init__(self, toolchain_type, args, ".o")
740 if sys.platform == "darwin":
741 cmd = ["xcrun", "--sdk", args.apple_sdk, "--show-sdk-path"]
742 self.apple_sdk = subprocess.check_output(cmd).strip().decode("utf-8")
744 def _add_m_option_if_needed(self, args):
745 # clang allows -m(32|64) for any target, gcc does not.
746 uname = platform.uname().machine.lower()
747 if self.toolchain_type != "gcc" or (
748 not "arm" in uname and not "aarch64" in uname
750 args.append("-m" + self.arch)
752 return args
754 def _get_compilation_command(self, source, obj):
755 args = []
757 args.append(self.compiler)
758 args = self._add_m_option_if_needed(args)
760 args.append("-g")
761 if self.opt == "none":
762 args.append("-O0")
763 elif self.opt == "basic":
764 args.append("-O2")
765 elif self.opt == "lto":
766 args.append("-flto=thin")
767 if self.nodefaultlib:
768 args.append("-nostdinc")
769 args.append("-static")
770 args.append("-c")
772 if sys.platform == "darwin":
773 args.extend(["-isysroot", self.apple_sdk])
774 elif self.objc_gnustep_inc:
775 if source.endswith(".m") or source.endswith(".mm"):
776 args.extend(["-fobjc-runtime=gnustep-2.0", "-I", self.objc_gnustep_inc])
777 if sys.platform == "win32":
778 args.extend(
779 ["-Xclang", "-gcodeview", "-Xclang", "--dependent-lib=msvcrtd"]
781 elif self.sysroot:
782 args.extend(["--sysroot", self.sysroot])
784 if self.std:
785 args.append("-std={0}".format(self.std))
787 args.extend(["-o", obj])
788 args.append(source)
790 return ("compiling", [source], obj, None, args)
792 def _get_link_command(self):
793 args = []
794 args.append(self.compiler)
795 args = self._add_m_option_if_needed(args)
797 if self.nodefaultlib:
798 args.append("-nostdlib")
799 args.append("-static")
800 main_symbol = "main"
801 if sys.platform == "darwin":
802 main_symbol = "_main"
803 args.append("-Wl,-e," + main_symbol)
804 if sys.platform.startswith("netbsd"):
805 for x in self.lib_paths:
806 args += ["-L" + x, "-Wl,-rpath," + x]
807 args.extend(["-o", self._exe_file_name()])
808 args.extend(self._obj_file_names())
810 if sys.platform == "darwin":
811 args.extend(["-isysroot", self.apple_sdk])
812 elif self.objc_gnustep_lib:
813 args.extend(["-L", self.objc_gnustep_lib, "-lobjc"])
814 if sys.platform == "linux":
815 args.extend(["-Wl,-rpath," + self.objc_gnustep_lib])
816 elif sys.platform == "win32":
817 args.extend(
818 ["-fuse-ld=lld-link", "-g", "-Xclang", "--dependent-lib=msvcrtd"]
820 elif self.sysroot:
821 args.extend(["--sysroot", self.sysroot])
823 return ("linking", self._obj_file_names(), self._exe_file_name(), None, args)
825 def output_files(self):
826 outputs = []
827 if self.mode == "compile" or self.mode == "compile-and-link":
828 outputs.extend(self._obj_file_names())
829 if self.mode == "link" or self.mode == "compile-and-link":
830 outputs.append(self._exe_file_name())
832 return outputs
835 def indent(text, spaces):
836 def prefixed_lines():
837 prefix = " " * spaces
838 for line in text.splitlines(True):
839 yield prefix + line
841 return "".join(prefixed_lines())
844 def build(commands):
845 global args
846 for status, inputs, output, env, child_args in commands:
847 print("\n\n")
848 inputs = [os.path.basename(x) for x in inputs]
849 output = os.path.basename(output)
850 print(status + " {0} -> {1}".format("+".join(inputs), output))
852 if args.verbose:
853 print(" Command Line: " + " ".join(child_args))
854 print(" Env:")
855 print_environment(env)
856 if args.dry:
857 continue
859 popen = subprocess.Popen(
860 child_args,
861 stdout=subprocess.PIPE,
862 stderr=subprocess.PIPE,
863 env=env,
864 universal_newlines=True,
866 stdout, stderr = popen.communicate()
867 res = popen.wait()
868 if res == -signal.SIGINT:
869 raise KeyboardInterrupt
870 print(" STDOUT:")
871 print(indent(stdout, 4))
872 if res != 0:
873 print(" STDERR:")
874 print(indent(stderr, 4))
875 sys.exit(res)
878 def clean(files):
879 global args
880 if not files:
881 return
882 for o in files:
883 file = o if args.verbose else os.path.basename(o)
884 print("Cleaning {0}".format(file))
885 try:
886 if os.path.exists(o):
887 if not args.dry:
888 os.remove(o)
889 if args.verbose:
890 print(" The file was successfully cleaned.")
891 elif args.verbose:
892 print(" The file does not exist.")
893 except:
894 if args.verbose:
895 print(" The file could not be removed.")
898 def fix_arguments(args):
899 if not args.inputs:
900 raise ValueError("No input files specified")
902 if args.output and args.mode == "compile" and len(args.inputs) > 1:
903 raise ValueError(
904 "Cannot specify -o with mode=compile and multiple source files. Use --outdir instead."
907 if not args.dry:
908 args.inputs = [os.path.abspath(x) for x in args.inputs]
910 # If user didn't specify the outdir, use the directory of the first input.
911 if not args.outdir:
912 if args.output:
913 args.outdir = os.path.dirname(args.output)
914 else:
915 args.outdir = os.path.dirname(args.inputs[0])
916 args.outdir = os.path.abspath(args.outdir)
917 args.outdir = os.path.normpath(args.outdir)
919 # If user specified a non-absolute path for the output file, append the
920 # output directory to it.
921 if args.output:
922 if not os.path.isabs(args.output):
923 args.output = os.path.join(args.outdir, args.output)
924 args.output = os.path.normpath(args.output)
927 fix_arguments(args)
929 (toolchain_type, toolchain_path) = find_toolchain(args.compiler, args.tools_dir)
930 if not toolchain_path or not toolchain_type:
931 print("Unable to find toolchain {0}".format(args.compiler))
932 sys.exit(1)
934 if args.verbose:
935 print("Script Arguments:")
936 print(" Arch: " + args.arch)
937 print(" Compiler: " + args.compiler)
938 print(" Outdir: " + args.outdir)
939 print(" Output: " + args.output)
940 print(" Nodefaultlib: " + str(args.nodefaultlib))
941 print(" Opt: " + args.opt)
942 print(" Mode: " + args.mode)
943 print(" Clean: " + str(args.clean))
944 print(" Verbose: " + str(args.verbose))
945 print(" Dryrun: " + str(args.dry))
946 print(" Inputs: " + format_text(args.inputs, 0, 10))
947 print(" C/C++ Standard: " + str(args.std))
948 print("Script Environment:")
949 print_environment(os.environ)
951 args.compiler = toolchain_path
952 if not os.path.exists(args.compiler) and not args.dry:
953 raise ValueError("The toolchain {} does not exist.".format(args.compiler))
955 if toolchain_type == "msvc" or toolchain_type == "clang-cl":
956 builder = MsvcBuilder(toolchain_type, args)
957 else:
958 builder = GccBuilder(toolchain_type, args)
960 if args.clean:
961 clean(builder.output_files())
963 cmds = builder.build_commands()
965 build(cmds)