brintos

brintos / llvm-project-archived public Read only

0
0
Text · 31.8 KiB · 1fa8aab Raw
974 lines · python
1#!/usr/bin/env python2 3import argparse4import os5import platform6import shutil7import signal8import subprocess9import sys10 11if sys.platform == "win32":12    import winreg13 14 15if __name__ != "__main__":16    raise RuntimeError("Do not import this script, run it instead")17 18 19parser = argparse.ArgumentParser(description="LLDB compilation wrapper")20parser.add_argument(21    "--arch",22    metavar="arch",23    dest="arch",24    required=True,25    default="host",26    choices=["32", "64", "host"],27    help="Specify the architecture to target.",28)29 30parser.add_argument(31    "--compiler",32    metavar="compiler",33    dest="compiler",34    required=True,35    help="Path to a compiler executable, or one of the values [any, msvc, clang-cl, gcc, clang]",36)37 38parser.add_argument(39    "--libs-dir",40    metavar="directory",41    dest="libs_dir",42    required=False,43    action="append",44    help="If specified, a path to linked libraries to be passed via -L",45)46 47parser.add_argument(48    "--tools-dir",49    metavar="directory",50    dest="tools_dir",51    required=False,52    action="append",53    help="If specified, a path to search in addition to PATH when --compiler is not an exact path",54)55 56parser.add_argument(57    "--objc-gnustep-dir",58    metavar="directory",59    dest="objc_gnustep_dir",60    required=False,61    help="If specified, a path to GNUstep libobjc2 runtime for use on Windows and Linux",62)63 64parser.add_argument(65    "--objc-gnustep",66    dest="objc_gnustep",67    action="store_true",68    default=False,69    help="Include and link GNUstep libobjc2 (Windows and Linux only)",70)71 72parser.add_argument(73    "--sysroot",74    metavar="directory",75    dest="sysroot",76    required=False,77    help="If specified, a sysroot to be passed via --sysroot",78)79 80if sys.platform == "darwin":81    parser.add_argument(82        "--apple-sdk",83        metavar="apple_sdk",84        dest="apple_sdk",85        default="macosx",86        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.",87    )88 89parser.add_argument(90    "--output",91    "-o",92    dest="output",93    metavar="file",94    required=False,95    default="",96    help="Path to output file",97)98 99parser.add_argument(100    "--outdir",101    "-d",102    dest="outdir",103    metavar="directory",104    required=False,105    help="Directory for output files",106)107 108parser.add_argument(109    "--nodefaultlib",110    dest="nodefaultlib",111    action="store_true",112    default=False,113    help="When specified, the resulting image should not link against system libraries or include system headers.  Useful when writing cross-targeting tests.",114)115 116parser.add_argument(117    "--opt",118    dest="opt",119    default="none",120    choices=["none", "basic", "lto"],121    help="Optimization level",122)123 124parser.add_argument(125    "--mode",126    dest="mode",127    default="compile-and-link",128    choices=["compile", "link", "compile-and-link"],129    help="Specifies whether to compile, link, or both",130)131 132parser.add_argument(133    "--noclean",134    dest="clean",135    action="store_false",136    default=True,137    help="Dont clean output file before building",138)139 140parser.add_argument(141    "--verbose",142    dest="verbose",143    action="store_true",144    default=False,145    help="Print verbose output",146)147 148parser.add_argument(149    "-n",150    "--dry-run",151    dest="dry",152    action="store_true",153    default=False,154    help="Print the commands that would run, but dont actually run them",155)156 157parser.add_argument(158    "inputs",159    metavar="file",160    nargs="+",161    help="Source file(s) to compile / object file(s) to link",162)163 164parser.add_argument(165    "--std",166    metavar="std",167    dest="std",168    required=False,169    help="Specify the C/C++ standard.",170)171 172 173args = parser.parse_args(args=sys.argv[1:])174 175 176def to_string(b):177    """Return the parameter as type 'str', possibly encoding it.178 179    In Python2, the 'str' type is the same as 'bytes'. In Python3, the180    'str' type is (essentially) Python2's 'unicode' type, and 'bytes' is181    distinct.182 183    This function is copied from llvm/utils/lit/lit/util.py184    """185    if isinstance(b, str):186        # In Python2, this branch is taken for types 'str' and 'bytes'.187        # In Python3, this branch is taken only for 'str'.188        return b189    if isinstance(b, bytes):190        # In Python2, this branch is never taken ('bytes' is handled as 'str').191        # In Python3, this is true only for 'bytes'.192        try:193            return b.decode("utf-8")194        except UnicodeDecodeError:195            # If the value is not valid Unicode, return the default196            # repr-line encoding.197            return str(b)198 199    # By this point, here's what we *don't* have:200    #201    #  - In Python2:202    #    - 'str' or 'bytes' (1st branch above)203    #  - In Python3:204    #    - 'str' (1st branch above)205    #    - 'bytes' (2nd branch above)206    #207    # The last type we might expect is the Python2 'unicode' type. There is no208    # 'unicode' type in Python3 (all the Python3 cases were already handled). In209    # order to get a 'str' object, we need to encode the 'unicode' object.210    try:211        return b.encode("utf-8")212    except AttributeError:213        raise TypeError("not sure how to convert %s to %s" % (type(b), str))214 215 216def format_text(lines, indent_0, indent_n):217    result = " " * indent_0 + lines[0]218    for next in lines[1:]:219        result = result + "\n{0}{1}".format(" " * indent_n, next)220    return result221 222 223def print_environment(env):224    if env is None:225        print("    Inherited")226        return227    for e in env:228        value = env[e]229        lines = value.split(os.pathsep)230        formatted_value = format_text(lines, 0, 7 + len(e))231        print("    {0} = {1}".format(e, formatted_value))232 233 234def find_executable(binary_name, search_paths):235    # shutil.which will ignore PATH if given a path argument, we want to include it.236    search_paths.append(os.environ.get("PATH", ""))237    search_path = os.pathsep.join(search_paths)238    binary_path = shutil.which(binary_name, path=search_path)239    if binary_path is not None:240        # So for example, we get '/bin/gcc' instead of '/usr/../bin/gcc'.241        binary_path = os.path.normpath(binary_path)242    return binary_path243 244 245def find_toolchain(compiler, tools_dir):246    if compiler == "msvc":247        return ("msvc", find_executable("cl", tools_dir))248    if compiler == "clang-cl":249        return ("clang-cl", find_executable("clang-cl", tools_dir))250    if compiler == "gcc":251        return ("gcc", find_executable("g++", tools_dir))252    if compiler == "clang":253        return ("clang", find_executable("clang++", tools_dir))254    if compiler == "any":255        priorities = []256        if sys.platform == "win32":257            priorities = ["clang-cl", "msvc", "clang", "gcc"]258        else:259            priorities = ["clang", "gcc", "clang-cl"]260        for toolchain in priorities:261            (type, dir) = find_toolchain(toolchain, tools_dir)262            if type and dir:263                return (type, dir)264        # Could not find any toolchain.265        return (None, None)266 267    # From here on, assume that |compiler| is a path to a file.268    file = os.path.basename(compiler)269    name, ext = os.path.splitext(file)270    if file.lower() == "cl.exe":271        return ("msvc", compiler)272    if name == "clang-cl":273        return ("clang-cl", compiler)274    if name.startswith("clang"):275        return ("clang", compiler)276    if name.startswith("gcc") or name.startswith("g++"):277        return ("gcc", compiler)278    if name == "cc" or name == "c++":279        return ("generic", compiler)280    return ("unknown", compiler)281 282 283class Builder(object):284    def __init__(self, toolchain_type, args, obj_ext):285        self.toolchain_type = toolchain_type286        self.inputs = args.inputs287        self.arch = args.arch288        self.opt = args.opt289        self.outdir = args.outdir290        self.compiler = args.compiler291        self.clean = args.clean292        self.output = args.output293        self.mode = args.mode294        self.nodefaultlib = args.nodefaultlib295        self.verbose = args.verbose296        self.obj_ext = obj_ext297        self.lib_paths = args.libs_dir298        self.std = args.std299        assert (300            not args.objc_gnustep or args.objc_gnustep_dir301        ), "--objc-gnustep specified without path to libobjc2"302        self.objc_gnustep_inc = (303            os.path.join(args.objc_gnustep_dir, "include")304            if args.objc_gnustep_dir305            else None306        )307        self.objc_gnustep_lib = (308            os.path.join(args.objc_gnustep_dir, "lib")309            if args.objc_gnustep_dir310            else None311        )312        self.sysroot = args.sysroot313 314    def _exe_file_name(self):315        assert self.mode != "compile"316        return self.output317 318    def _output_name(self, input, extension, with_executable=False):319        basename = os.path.splitext(os.path.basename(input))[0] + extension320        if with_executable:321            exe_basename = os.path.basename(self._exe_file_name())322            basename = exe_basename + "-" + basename323 324        output = os.path.join(self.outdir, basename)325        return os.path.normpath(output)326 327    def _obj_file_names(self):328        if self.mode == "link":329            return self.inputs330 331        if self.mode == "compile-and-link":332            # Object file names should factor in both the input file (source)333            # name and output file (executable) name, to ensure that two tests334            # which share a common source file don't race to write the same335            # object file.336            return [self._output_name(x, self.obj_ext, True) for x in self.inputs]337 338        if self.mode == "compile" and self.output:339            return [self.output]340 341        return [self._output_name(x, self.obj_ext) for x in self.inputs]342 343    def build_commands(self):344        commands = []345        if self.mode == "compile" or self.mode == "compile-and-link":346            for input, output in zip(self.inputs, self._obj_file_names()):347                commands.append(self._get_compilation_command(input, output))348        if self.mode == "link" or self.mode == "compile-and-link":349            commands.append(self._get_link_command())350        return commands351 352 353class MsvcBuilder(Builder):354    def __init__(self, toolchain_type, args):355        Builder.__init__(self, toolchain_type, args, ".obj")356 357        if platform.uname().machine.lower() == "arm64":358            self.msvc_arch_str = "arm" if self.arch == "32" else "arm64"359        else:360            self.msvc_arch_str = "x86" if self.arch == "32" else "x64"361 362        if toolchain_type == "msvc":363            # Make sure we're using the appropriate toolchain for the desired364            # target type.365            compiler_parent_dir = os.path.dirname(self.compiler)366            selected_target_version = os.path.basename(compiler_parent_dir)367            if selected_target_version != self.msvc_arch_str:368                host_dir = os.path.dirname(compiler_parent_dir)369                self.compiler = os.path.join(host_dir, self.msvc_arch_str, "cl.exe")370                if self.verbose:371                    print(372                        'Using alternate compiler "{0}" to match selected target.'.format(373                            self.compiler374                        )375                    )376 377        if self.mode == "link" or self.mode == "compile-and-link":378            self.linker = (379                self._find_linker("link")380                if toolchain_type == "msvc"381                else self._find_linker("lld-link", args.tools_dir)382            )383            if not self.linker:384                raise ValueError("Unable to find an appropriate linker.")385 386        self.compile_env, self.link_env = self._get_visual_studio_environment()387 388    def _find_linker(self, name, search_paths=[]):389        compiler_dir = os.path.dirname(self.compiler)390        linker_path = find_executable(name, [compiler_dir] + search_paths)391        if linker_path is None:392            raise ValueError("Could not find '{}'".format(name))393        return linker_path394 395    def _get_vc_install_dir(self):396        dir = os.getenv("VCINSTALLDIR", None)397        if dir:398            if self.verbose:399                print("Using %VCINSTALLDIR% {}".format(dir))400            return dir401 402        dir = os.getenv("VSINSTALLDIR", None)403        if dir:404            if self.verbose:405                print("Using %VSINSTALLDIR% {}".format(dir))406            return os.path.join(dir, "VC")407 408        dir = os.getenv("VS2019INSTALLDIR", None)409        if dir:410            if self.verbose:411                print("Using %VS2019INSTALLDIR% {}".format(dir))412            return os.path.join(dir, "VC")413 414        dir = os.getenv("VS2017INSTALLDIR", None)415        if dir:416            if self.verbose:417                print("Using %VS2017INSTALLDIR% {}".format(dir))418            return os.path.join(dir, "VC")419 420        dir = os.getenv("VS2015INSTALLDIR", None)421        if dir:422            if self.verbose:423                print("Using %VS2015INSTALLDIR% {}".format(dir))424            return os.path.join(dir, "VC")425        return None426 427    def _get_vctools_version(self):428        ver = os.getenv("VCToolsVersion", None)429        if ver:430            if self.verbose:431                print("Using %VCToolsVersion% {}".format(ver))432            return ver433 434        vcinstalldir = self._get_vc_install_dir()435        vcinstalldir = os.path.join(vcinstalldir, "Tools", "MSVC")436        subdirs = next(os.walk(vcinstalldir))[1]437        if not subdirs:438            return None439 440        from packaging import version441 442        subdirs.sort(key=lambda x: version.parse(x))443 444        if self.verbose:445            full_path = os.path.join(vcinstalldir, subdirs[-1])446            print(447                "Using VC tools version directory {0} found by directory walk.".format(448                    full_path449                )450            )451        return subdirs[-1]452 453    def _get_vctools_install_dir(self):454        dir = os.getenv("VCToolsInstallDir", None)455        if dir:456            if self.verbose:457                print("Using %VCToolsInstallDir% {}".format(dir))458            return dir459 460        vcinstalldir = self._get_vc_install_dir()461        if not vcinstalldir:462            return None463        vctoolsver = self._get_vctools_version()464        if not vctoolsver:465            return None466        result = os.path.join(vcinstalldir, "Tools", "MSVC", vctoolsver)467        if not os.path.exists(result):468            return None469        if self.verbose:470            print(471                "Using VC tools install dir {} found by directory walk".format(result)472            )473        return result474 475    def _find_windows_sdk_in_registry_view(self, view):476        products_key = None477        roots_key = None478        installed_options_keys = []479        try:480            sam = view | winreg.KEY_READ481            products_key = winreg.OpenKey(482                winreg.HKEY_LOCAL_MACHINE,483                r"Software\Microsoft\Windows Kits\Installed Products",484                0,485                sam,486            )487 488            # This is the GUID for the desktop component.  If this is present489            # then the components required for the Desktop SDK are installed.490            # If not it will throw an exception.491            winreg.QueryValueEx(products_key, "{5A3D81EC-D870-9ECF-D997-24BDA6644752}")492 493            roots_key = winreg.OpenKey(494                winreg.HKEY_LOCAL_MACHINE,495                r"Software\Microsoft\Windows Kits\Installed Roots",496                0,497                sam,498            )499            root_dir = winreg.QueryValueEx(roots_key, "KitsRoot10")500            root_dir = to_string(root_dir[0])501            sdk_versions = []502            index = 0503            while True:504                # Installed SDK versions are stored as sub-keys of the505                # 'Installed Roots' key.  Find all of their names, then sort506                # them by version507                try:508                    ver_key = winreg.EnumKey(roots_key, index)509                    sdk_versions.append(ver_key)510                    index = index + 1511                except WindowsError:512                    break513            if not sdk_versions:514                return (None, None)515 516            from packaging import version517 518            sdk_versions.sort(key=lambda x: version.parse(x), reverse=True)519            option_value_name = "OptionId.DesktopCPP" + self.msvc_arch_str520            for v in sdk_versions:521                try:522                    version_subkey = v + r"\Installed Options"523                    key = winreg.OpenKey(roots_key, version_subkey)524                    installed_options_keys.append(key)525                    (value, value_type) = winreg.QueryValueEx(key, option_value_name)526                    if value == 1:527                        # The proper architecture is installed.  Return the528                        # associated paths.529                        if self.verbose:530                            print(531                                "Found Installed Windows SDK v{0} at {1}".format(532                                    v, root_dir533                                )534                            )535                        return (root_dir, v)536                except:537                    continue538        except:539            return (None, None)540        finally:541            del products_key542            del roots_key543            for k in installed_options_keys:544                del k545        return (None, None)546 547    def _find_windows_sdk_in_registry(self):548        # This could be a clang-cl cross-compile.  If so, there's no registry549        # so just exit.550        if sys.platform != "win32":551            return (None, None)552        if self.verbose:553            print("Looking for Windows SDK in 64-bit registry.")554        dir, ver = self._find_windows_sdk_in_registry_view(winreg.KEY_WOW64_64KEY)555        if not dir or not ver:556            if self.verbose:557                print("Looking for Windows SDK in 32-bit registry.")558            dir, ver = self._find_windows_sdk_in_registry_view(winreg.KEY_WOW64_32KEY)559 560        return (dir, ver)561 562    def _get_winsdk_dir(self):563        # If a Windows SDK is specified in the environment, use that.  Otherwise564        # try to find one in the Windows registry.565        dir = os.getenv("WindowsSdkDir", None)566        if not dir or not os.path.exists(dir):567            return self._find_windows_sdk_in_registry()568        ver = os.getenv("WindowsSDKLibVersion", None)569        if not ver:570            return self._find_windows_sdk_in_registry()571 572        ver = ver.rstrip("\\")573        if self.verbose:574            print("Using %WindowsSdkDir% {}".format(dir))575            print("Using %WindowsSDKLibVersion% {}".format(ver))576        return (dir, ver)577 578    def _get_msvc_native_toolchain_dir(self):579        assert self.toolchain_type == "msvc"580        compiler_dir = os.path.dirname(self.compiler)581        target_dir = os.path.dirname(compiler_dir)582        host_name = os.path.basename(target_dir)583        host_name = host_name[4:].lower()584        return os.path.join(target_dir, host_name)585 586    def _get_visual_studio_environment(self):587        vctools = self._get_vctools_install_dir()588        winsdk, winsdkver = self._get_winsdk_dir()589 590        if not vctools and self.verbose:591            print("Unable to find VC tools installation directory.")592        if (not winsdk or not winsdkver) and self.verbose:593            print("Unable to find Windows SDK directory.")594 595        vcincludes = []596        vclibs = []597        sdkincludes = []598        sdklibs = []599        if vctools is not None:600            includes = [["ATLMFC", "include"], ["include"]]601            libs = [["ATLMFC", "lib"], ["lib"]]602            vcincludes = [os.path.join(vctools, *y) for y in includes]603            vclibs = [os.path.join(vctools, *y) for y in libs]604        if winsdk is not None:605            includes = [606                ["include", winsdkver, "ucrt"],607                ["include", winsdkver, "shared"],608                ["include", winsdkver, "um"],609                ["include", winsdkver, "winrt"],610                ["include", winsdkver, "cppwinrt"],611            ]612            libs = [["lib", winsdkver, "ucrt"], ["lib", winsdkver, "um"]]613            sdkincludes = [os.path.join(winsdk, *y) for y in includes]614            sdklibs = [os.path.join(winsdk, *y) for y in libs]615 616        includes = vcincludes + sdkincludes617        libs = vclibs + sdklibs618        libs = [os.path.join(x, self.msvc_arch_str) for x in libs]619        compileenv = None620        linkenv = None621        defaultenv = {}622        if sys.platform == "win32":623            defaultenv = {624                x: os.environ[x] for x in ["SystemDrive", "SystemRoot", "TMP", "TEMP"]625            }626            # The directory to mspdbcore.dll needs to be in PATH, but this is627            # always in the native toolchain path, not the cross-toolchain628            # path.  So, for example, if we're using HostX64\x86 then we need629            # to add HostX64\x64 to the path, and if we're using HostX86\x64630            # then we need to add HostX86\x86 to the path.631            if self.toolchain_type == "msvc":632                defaultenv["PATH"] = self._get_msvc_native_toolchain_dir()633 634        if includes:635            compileenv = {}636            compileenv["INCLUDE"] = os.pathsep.join(includes)637            compileenv.update(defaultenv)638        if libs:639            linkenv = {}640            linkenv["LIB"] = os.pathsep.join(libs)641            linkenv.update(defaultenv)642        return (compileenv, linkenv)643 644    def _ilk_file_names(self):645        if self.mode == "link":646            return []647 648        return [self._output_name(x, ".ilk") for x in self.inputs]649 650    def _pdb_file_name(self):651        if self.mode == "compile":652            return None653        return os.path.splitext(self.output)[0] + ".pdb"654 655    def _get_compilation_command(self, source, obj):656        args = []657 658        args.append(self.compiler)659        if self.toolchain_type == "clang-cl":660            args.append("-m" + self.arch)661 662        if self.opt == "none":663            args.append("/Od")664        elif self.opt == "basic":665            args.append("/O2")666        elif self.opt == "lto":667            if self.toolchain_type == "msvc":668                args.append("/GL")669                args.append("/Gw")670            else:671                args.append("-flto=thin")672        if self.nodefaultlib:673            args.append("/GS-")674            args.append("/GR-")675        args.append("/Z7")676        if self.toolchain_type == "clang-cl":677            args.append("-Xclang")678            args.append("-fkeep-static-consts")679            args.append("-fms-compatibility-version=19")680        args.append("/c")681 682        if self.std:683            args.append("/std:" + self.std)684 685        args.append("/Fo" + obj)686        if self.toolchain_type == "clang-cl":687            args.append("--")688        args.append(source)689 690        return ("compiling", [source], obj, self.compile_env, args)691 692    def _get_link_command(self):693        args = []694        args.append(self.linker)695        args.append("/DEBUG:FULL")696        args.append("/INCREMENTAL:NO")697        if self.nodefaultlib:698            args.append("/nodefaultlib")699            args.append("/entry:main")700        args.append("/PDB:" + self._pdb_file_name())701        args.append("/OUT:" + self._exe_file_name())702        args.extend(self._obj_file_names())703 704        return (705            "linking",706            self._obj_file_names(),707            self._exe_file_name(),708            self.link_env,709            args,710        )711 712    def build_commands(self):713        commands = []714        if self.mode == "compile" or self.mode == "compile-and-link":715            for input, output in zip(self.inputs, self._obj_file_names()):716                commands.append(self._get_compilation_command(input, output))717        if self.mode == "link" or self.mode == "compile-and-link":718            commands.append(self._get_link_command())719        return commands720 721    def output_files(self):722        outputs = []723        if self.mode == "compile" or self.mode == "compile-and-link":724            outputs.extend(self._ilk_file_names())725            outputs.extend(self._obj_file_names())726        if self.mode == "link" or self.mode == "compile-and-link":727            outputs.append(self._pdb_file_name())728            outputs.append(self._exe_file_name())729 730        return [x for x in outputs if x is not None]731 732 733class GccBuilder(Builder):734    def __init__(self, toolchain_type, args):735        Builder.__init__(self, toolchain_type, args, ".o")736        if sys.platform == "darwin":737            cmd = ["xcrun", "--sdk", args.apple_sdk, "--show-sdk-path"]738            self.apple_sdk = subprocess.check_output(cmd).strip().decode("utf-8")739 740    def _add_m_option_if_needed(self, args):741        # clang allows -m(32|64) for any target, gcc does not.742        uname = platform.uname().machine.lower()743        if self.toolchain_type != "gcc" or (744            not "arm" in uname and not "aarch64" in uname745        ):746            args.append("-m" + self.arch)747 748        return args749 750    def _get_compilation_command(self, source, obj):751        args = []752 753        args.append(self.compiler)754        args = self._add_m_option_if_needed(args)755 756        args.append("-g")757        if self.opt == "none":758            args.append("-O0")759        elif self.opt == "basic":760            args.append("-O2")761        elif self.opt == "lto":762            args.append("-flto=thin")763        if self.nodefaultlib:764            args.append("-nostdinc")765            args.append("-static")766        args.append("-c")767 768        if sys.platform == "darwin":769            args.extend(["-isysroot", self.apple_sdk])770        elif self.objc_gnustep_inc:771            if source.endswith(".m") or source.endswith(".mm"):772                args.extend(["-fobjc-runtime=gnustep-2.0", "-I", self.objc_gnustep_inc])773                if sys.platform == "win32":774                    args.extend(775                        ["-Xclang", "-gcodeview", "-Xclang", "--dependent-lib=msvcrtd"]776                    )777        elif self.sysroot:778            args.extend(["--sysroot", self.sysroot])779 780        if self.std:781            args.append("-std={0}".format(self.std))782 783        args.extend(["-o", obj])784        args.append(source)785 786        return ("compiling", [source], obj, None, args)787 788    def _get_link_command(self):789        args = []790        args.append(self.compiler)791        args = self._add_m_option_if_needed(args)792 793        if self.nodefaultlib:794            args.append("-nostdlib")795            args.append("-static")796            main_symbol = "main"797            if sys.platform == "darwin":798                main_symbol = "_main"799            args.append("-Wl,-e," + main_symbol)800        if sys.platform.startswith("netbsd"):801            for x in self.lib_paths:802                args += ["-L" + x, "-Wl,-rpath," + x]803        args.extend(["-o", self._exe_file_name()])804        args.extend(self._obj_file_names())805 806        if sys.platform == "darwin":807            # By default, macOS doesn't allow injecting the ASAN808            # runtime into system processes.809            system_clang = (810                subprocess.check_output(["xcrun", "-find", "clang"])811                .strip()812                .decode("utf-8")813            )814            system_liblto = os.path.join(815                os.path.dirname(os.path.dirname(system_clang)), "lib", "libLTO.dylib"816            )817            args.extend(["-isysroot", self.apple_sdk])818            args.extend(["-Wl,-lto_library", "-Wl," + system_liblto])819 820        elif self.objc_gnustep_lib:821            args.extend(["-L", self.objc_gnustep_lib, "-lobjc"])822            if sys.platform == "linux":823                args.extend(["-Wl,-rpath," + self.objc_gnustep_lib])824            elif sys.platform == "win32":825                args.extend(826                    ["-fuse-ld=lld-link", "-g", "-Xclang", "--dependent-lib=msvcrtd"]827                )828        elif self.sysroot:829            args.extend(["--sysroot", self.sysroot])830 831        return ("linking", self._obj_file_names(), self._exe_file_name(), None, args)832 833    def output_files(self):834        outputs = []835        if self.mode == "compile" or self.mode == "compile-and-link":836            outputs.extend(self._obj_file_names())837        if self.mode == "link" or self.mode == "compile-and-link":838            outputs.append(self._exe_file_name())839 840        return outputs841 842 843def indent(text, spaces):844    def prefixed_lines():845        prefix = " " * spaces846        for line in text.splitlines(True):847            yield prefix + line848 849    return "".join(prefixed_lines())850 851 852def build(commands):853    global args854    for status, inputs, output, env, child_args in commands:855        print("\n\n")856        inputs = [os.path.basename(x) for x in inputs]857        output = os.path.basename(output)858        print(status + " {0} -> {1}".format("+".join(inputs), output))859 860        if args.verbose:861            print("  Command Line: " + " ".join(child_args))862            print("  Env:")863            print_environment(env)864        if args.dry:865            continue866 867        popen = subprocess.Popen(868            child_args,869            stdout=subprocess.PIPE,870            stderr=subprocess.PIPE,871            env=env,872            universal_newlines=True,873        )874        stdout, stderr = popen.communicate()875        res = popen.wait()876        if res == -signal.SIGINT:877            raise KeyboardInterrupt878        print("  STDOUT:")879        print(indent(stdout, 4))880        if res != 0:881            print("  STDERR:")882            print(indent(stderr, 4))883            sys.exit(res)884 885 886def clean(files):887    global args888    if not files:889        return890    for o in files:891        file = o if args.verbose else os.path.basename(o)892        print("Cleaning {0}".format(file))893        try:894            if os.path.exists(o):895                if not args.dry:896                    os.remove(o)897                if args.verbose:898                    print("  The file was successfully cleaned.")899            elif args.verbose:900                print("  The file does not exist.")901        except:902            if args.verbose:903                print("  The file could not be removed.")904 905 906def fix_arguments(args):907    if not args.inputs:908        raise ValueError("No input files specified")909 910    if args.output and args.mode == "compile" and len(args.inputs) > 1:911        raise ValueError(912            "Cannot specify -o with mode=compile and multiple source files.  Use --outdir instead."913        )914 915    if not args.dry:916        args.inputs = [os.path.abspath(x) for x in args.inputs]917 918    # If user didn't specify the outdir, use the directory of the first input.919    if not args.outdir:920        if args.output:921            args.outdir = os.path.dirname(args.output)922        else:923            args.outdir = os.path.dirname(args.inputs[0])924            args.outdir = os.path.abspath(args.outdir)925        args.outdir = os.path.normpath(args.outdir)926 927    # If user specified a non-absolute path for the output file, append the928    # output directory to it.929    if args.output:930        if not os.path.isabs(args.output):931            args.output = os.path.join(args.outdir, args.output)932        args.output = os.path.normpath(args.output)933 934 935fix_arguments(args)936 937(toolchain_type, toolchain_path) = find_toolchain(args.compiler, args.tools_dir)938if not toolchain_path or not toolchain_type:939    print("Unable to find toolchain {0}".format(args.compiler))940    sys.exit(1)941 942if args.verbose:943    print("Script Arguments:")944    print("  Arch: " + args.arch)945    print("  Compiler: " + args.compiler)946    print("  Outdir: " + args.outdir)947    print("  Output: " + args.output)948    print("  Nodefaultlib: " + str(args.nodefaultlib))949    print("  Opt: " + args.opt)950    print("  Mode: " + args.mode)951    print("  Clean: " + str(args.clean))952    print("  Verbose: " + str(args.verbose))953    print("  Dryrun: " + str(args.dry))954    print("  Inputs: " + format_text(args.inputs, 0, 10))955    print("  C/C++ Standard: " + str(args.std))956    print("Script Environment:")957    print_environment(os.environ)958 959args.compiler = toolchain_path960if not os.path.exists(args.compiler) and not args.dry:961    raise ValueError("The toolchain {} does not exist.".format(args.compiler))962 963if toolchain_type == "msvc" or toolchain_type == "clang-cl":964    builder = MsvcBuilder(toolchain_type, args)965else:966    builder = GccBuilder(toolchain_type, args)967 968if args.clean:969    clean(builder.output_files())970 971cmds = builder.build_commands()972 973build(cmds)974