Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
msvs_emulation.py1256 linesDownload Raw Back to gyp
1# Copyright (c) 2012 Google Inc. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""
6This module helps emulate Visual Studio 2008 behavior on top of other
7build systems, primarily ninja.
8"""
9
10import os
11import re
12import subprocess
13import sys
14from collections import namedtuple
15
16import gyp.MSVSUtil
17import gyp.MSVSVersion
18from gyp.common import OrderedSet
19
20windows_quoter_regex = re.compile(r'(\\*)"')
21
22
23def QuoteForRspFile(arg, quote_cmd=True):
24    """Quote a command line argument so that it appears as one argument when
25    processed via cmd.exe and parsed by CommandLineToArgvW (as is typical for
26    Windows programs)."""
27    # See http://goo.gl/cuFbX and http://goo.gl/dhPnp including the comment
28    # threads. This is actually the quoting rules for CommandLineToArgvW, not
29    # for the shell, because the shell doesn't do anything in Windows. This
30    # works more or less because most programs (including the compiler, etc.)
31    # use that function to handle command line arguments.
32
33    # Use a heuristic to try to find args that are paths, and normalize them
34    if arg.find("/") > 0 or arg.count("/") > 1:
35        arg = os.path.normpath(arg)
36
37    # For a literal quote, CommandLineToArgvW requires 2n+1 backslashes
38    # preceding it, and results in n backslashes + the quote. So we substitute
39    # in 2* what we match, +1 more, plus the quote.
40    if quote_cmd:
41        arg = windows_quoter_regex.sub(lambda mo: 2 * mo.group(1) + '\\"', arg)
42
43    # %'s also need to be doubled otherwise they're interpreted as batch
44    # positional arguments. Also make sure to escape the % so that they're
45    # passed literally through escaping so they can be singled to just the
46    # original %. Otherwise, trying to pass the literal representation that
47    # looks like an environment variable to the shell (e.g. %PATH%) would fail.
48    arg = arg.replace("%", "%%")
49
50    # These commands are used in rsp files, so no escaping for the shell (via ^)
51    # is necessary.
52
53    # As a workaround for programs that don't use CommandLineToArgvW, gyp
54    # supports msvs_quote_cmd=0, which simply disables all quoting.
55    if quote_cmd:
56        # Finally, wrap the whole thing in quotes so that the above quote rule
57        # applies and whitespace isn't a word break.
58        return f'"{arg}"'
59
60    return arg
61
62
63def EncodeRspFileList(args, quote_cmd):
64    """Process a list of arguments using QuoteCmdExeArgument."""
65    # Note that the first argument is assumed to be the command. Don't add
66    # quotes around it because then built-ins like 'echo', etc. won't work.
67    # Take care to normpath only the path in the case of 'call ../x.bat' because
68    # otherwise the whole thing is incorrectly interpreted as a path and not
69    # normalized correctly.
70    if not args:
71        return ""
72    if args[0].startswith("call "):
73        call, program = args[0].split(" ", 1)
74        program = call + " " + os.path.normpath(program)
75    else:
76        program = os.path.normpath(args[0])
77    return program + " " + " ".join(QuoteForRspFile(arg, quote_cmd) for arg in args[1:])
78
79
80def _GenericRetrieve(root, default, path):
81    """Given a list of dictionary keys |path| and a tree of dicts |root|, find
82    value at path, or return |default| if any of the path doesn't exist."""
83    if not root:
84        return default
85    if not path:
86        return root
87    return _GenericRetrieve(root.get(path[0]), default, path[1:])
88
89
90def _AddPrefix(element, prefix):
91    """Add |prefix| to |element| or each subelement if element is iterable."""
92    if element is None:
93        return element
94    # Note, not Iterable because we don't want to handle strings like that.
95    if isinstance(element, (list, tuple)):
96        return [prefix + e for e in element]
97    else:
98        return prefix + element
99
100
101def _DoRemapping(element, map):
102    """If |element| then remap it through |map|. If |element| is iterable then
103    each item will be remapped. Any elements not found will be removed."""
104    if map is not None and element is not None:
105        if not callable(map):
106            map = map.get  # Assume it's a dict, otherwise a callable to do the remap.
107        if isinstance(element, (list, tuple)):
108            element = filter(None, [map(elem) for elem in element])
109        else:
110            element = map(element)
111    return element
112
113
114def _AppendOrReturn(append, element):
115    """If |append| is None, simply return |element|. If |append| is not None,
116    then add |element| to it, adding each item in |element| if it's a list or
117    tuple."""
118    if append is not None and element is not None:
119        if isinstance(element, (list, tuple)):
120            append.extend(element)
121        else:
122            append.append(element)
123    else:
124        return element
125
126
127def _FindDirectXInstallation():
128    """Try to find an installation location for the DirectX SDK. Check for the
129    standard environment variable, and if that doesn't exist, try to find
130    via the registry. May return None if not found in either location."""
131    # Return previously calculated value, if there is one
132    if hasattr(_FindDirectXInstallation, "dxsdk_dir"):
133        return _FindDirectXInstallation.dxsdk_dir
134
135    dxsdk_dir = os.environ.get("DXSDK_DIR")
136    if not dxsdk_dir:
137        # Setup params to pass to and attempt to launch reg.exe.
138        cmd = ["reg.exe", "query", r"HKLM\Software\Microsoft\DirectX", "/s"]
139        p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
140        stdout = p.communicate()[0].decode("utf-8")
141        for line in stdout.splitlines():
142            if "InstallPath" in line:
143                dxsdk_dir = line.split("    ")[3] + "\\"
144
145    # Cache return value
146    _FindDirectXInstallation.dxsdk_dir = dxsdk_dir
147    return dxsdk_dir
148
149
150def GetGlobalVSMacroEnv(vs_version):
151    """Get a dict of variables mapping internal VS macro names to their gyp
152    equivalents. Returns all variables that are independent of the target."""
153    env = {}
154    # '$(VSInstallDir)' and '$(VCInstallDir)' are available when and only when
155    # Visual Studio is actually installed.
156    if vs_version.Path():
157        env["$(VSInstallDir)"] = vs_version.Path()
158        env["$(VCInstallDir)"] = os.path.join(vs_version.Path(), "VC") + "\\"
159    # Chromium uses DXSDK_DIR in include/lib paths, but it may or may not be
160    # set. This happens when the SDK is sync'd via src-internal, rather than
161    # by typical end-user installation of the SDK. If it's not set, we don't
162    # want to leave the unexpanded variable in the path, so simply strip it.
163    dxsdk_dir = _FindDirectXInstallation()
164    env["$(DXSDK_DIR)"] = dxsdk_dir if dxsdk_dir else ""
165    # Try to find an installation location for the Windows DDK by checking
166    # the WDK_DIR environment variable, may be None.
167    env["$(WDK_DIR)"] = os.environ.get("WDK_DIR", "")
168    return env
169
170
171def ExtractSharedMSVSSystemIncludes(configs, generator_flags):
172    """Finds msvs_system_include_dirs that are common to all targets, removes
173    them from all targets, and returns an OrderedSet containing them."""
174    all_system_includes = OrderedSet(configs[0].get("msvs_system_include_dirs", []))
175    for config in configs[1:]:
176        system_includes = config.get("msvs_system_include_dirs", [])
177        all_system_includes = all_system_includes & OrderedSet(system_includes)
178    if not all_system_includes:
179        return None
180    # Expand macros in all_system_includes.
181    env = GetGlobalVSMacroEnv(GetVSVersion(generator_flags))
182    expanded_system_includes = OrderedSet(
183        [ExpandMacros(include, env) for include in all_system_includes]
184    )
185    if any("$" in include for include in expanded_system_includes):
186        # Some path relies on target-specific variables, bail.
187        return None
188
189    # Remove system includes shared by all targets from the targets.
190    for config in configs:
191        includes = config.get("msvs_system_include_dirs", [])
192        if includes:  # Don't insert a msvs_system_include_dirs key if not needed.
193            # This must check the unexpanded includes list:
194            new_includes = [i for i in includes if i not in all_system_includes]
195            config["msvs_system_include_dirs"] = new_includes
196    return expanded_system_includes
197
198
199class MsvsSettings:
200    """A class that understands the gyp 'msvs_...' values (especially the
201    msvs_settings field). They largely correpond to the VS2008 IDE DOM. This
202    class helps map those settings to command line options."""
203
204    def __init__(self, spec, generator_flags):
205        self.spec = spec
206        self.vs_version = GetVSVersion(generator_flags)
207
208        supported_fields = [
209            ("msvs_configuration_attributes", dict),
210            ("msvs_settings", dict),
211            ("msvs_system_include_dirs", list),
212            ("msvs_disabled_warnings", list),
213            ("msvs_precompiled_header", str),
214            ("msvs_precompiled_source", str),
215            ("msvs_configuration_platform", str),
216            ("msvs_target_platform", str),
217        ]
218        configs = spec["configurations"]
219        for field, default in supported_fields:
220            setattr(self, field, {})
221            for configname, config in configs.items():
222                getattr(self, field)[configname] = config.get(field, default())
223
224        self.msvs_cygwin_dirs = spec.get("msvs_cygwin_dirs", ["."])
225
226        unsupported_fields = [
227            "msvs_prebuild",
228            "msvs_postbuild",
229        ]
230        unsupported = []
231        for field in unsupported_fields:
232            for config in configs.values():
233                if field in config:
234                    unsupported += [
235                        "{} not supported (target {}).".format(
236                            field, spec["target_name"]
237                        )
238                    ]
239        if unsupported:
240            raise Exception("\n".join(unsupported))
241
242    def GetExtension(self):
243        """Returns the extension for the target, with no leading dot.
244
245        Uses 'product_extension' if specified, otherwise uses MSVS defaults based on
246        the target type.
247        """
248        ext = self.spec.get("product_extension", None)
249        return ext or gyp.MSVSUtil.TARGET_TYPE_EXT.get(self.spec["type"], "")
250
251    def GetVSMacroEnv(self, base_to_build=None, config=None):
252        """Get a dict of variables mapping internal VS macro names to their gyp
253        equivalents."""
254        target_arch = self.GetArch(config)
255        target_platform = "Win32" if target_arch == "x86" else target_arch
256        target_name = self.spec.get("product_prefix", "") + self.spec.get(
257            "product_name", self.spec["target_name"]
258        )
259        target_dir = base_to_build + "\\" if base_to_build else ""
260        target_ext = "." + self.GetExtension()
261        target_file_name = target_name + target_ext
262
263        replacements = {
264            "$(InputName)": "${root}",
265            "$(InputPath)": "${source}",
266            "$(IntDir)": "$!INTERMEDIATE_DIR",
267            "$(OutDir)\\": target_dir,
268            "$(PlatformName)": target_platform,
269            "$(ProjectDir)\\": "",
270            "$(ProjectName)": self.spec["target_name"],
271            "$(TargetDir)\\": target_dir,
272            "$(TargetExt)": target_ext,
273            "$(TargetFileName)": target_file_name,
274            "$(TargetName)": target_name,
275            "$(TargetPath)": os.path.join(target_dir, target_file_name),
276        }
277        replacements.update(GetGlobalVSMacroEnv(self.vs_version))
278        return replacements
279
280    def ConvertVSMacros(self, s, base_to_build=None, config=None):
281        """Convert from VS macro names to something equivalent."""
282        env = self.GetVSMacroEnv(base_to_build, config=config)
283        return ExpandMacros(s, env)
284
285    def AdjustLibraries(self, libraries):
286        """Strip -l from library if it's specified with that."""
287        libs = [lib[2:] if lib.startswith("-l") else lib for lib in libraries]
288        return [
289            lib + ".lib"
290            if not lib.lower().endswith(".lib") and not lib.lower().endswith(".obj")
291            else lib
292            for lib in libs
293        ]
294
295    def _GetAndMunge(self, field, path, default, prefix, append, map):
296        """Retrieve a value from |field| at |path| or return |default|. If
297        |append| is specified, and the item is found, it will be appended to that
298        object instead of returned. If |map| is specified, results will be
299        remapped through |map| before being returned or appended."""
300        result = _GenericRetrieve(field, default, path)
301        result = _DoRemapping(result, map)
302        result = _AddPrefix(result, prefix)
303        return _AppendOrReturn(append, result)
304
305    class _GetWrapper:
306        def __init__(self, parent, field, base_path, append=None):
307            self.parent = parent
308            self.field = field
309            self.base_path = [base_path]
310            self.append = append
311
312        def __call__(self, name, map=None, prefix="", default=None):
313            return self.parent._GetAndMunge(
314                self.field,
315                self.base_path + [name],
316                default=default,
317                prefix=prefix,
318                append=self.append,
319                map=map,
320            )
321
322    def GetArch(self, config):
323        """Get architecture based on msvs_configuration_platform and
324        msvs_target_platform. Returns either 'x86' or 'x64'."""
325        configuration_platform = self.msvs_configuration_platform.get(config, "")
326        platform = self.msvs_target_platform.get(config, "")
327        if not platform:  # If no specific override, use the configuration's.
328            platform = configuration_platform
329        # Map from platform to architecture.
330        return {"Win32": "x86", "x64": "x64", "ARM64": "arm64"}.get(platform, "x86")
331
332    def _TargetConfig(self, config):
333        """Returns the target-specific configuration."""
334        # There's two levels of architecture/platform specification in VS. The
335        # first level is globally for the configuration (this is what we consider
336        # "the" config at the gyp level, which will be something like 'Debug' or
337        # 'Release'), VS2015 and later only use this level
338        if int(self.vs_version.short_name) >= 2015:
339            return config
340        # and a second target-specific configuration, which is an
341        # override for the global one. |config| is remapped here to take into
342        # account the local target-specific overrides to the global configuration.
343        arch = self.GetArch(config)
344        if arch == "x64" and not config.endswith("_x64"):
345            config += "_x64"
346        if arch == "x86" and config.endswith("_x64"):
347            config = config.rsplit("_", 1)[0]
348        return config
349
350    def _Setting(self, path, config, default=None, prefix="", append=None, map=None):
351        """_GetAndMunge for msvs_settings."""
352        return self._GetAndMunge(
353            self.msvs_settings[config], path, default, prefix, append, map
354        )
355
356    def _ConfigAttrib(
357        self, path, config, default=None, prefix="", append=None, map=None
358    ):
359        """_GetAndMunge for msvs_configuration_attributes."""
360        return self._GetAndMunge(
361            self.msvs_configuration_attributes[config],
362            path,
363            default,
364            prefix,
365            append,
366            map,
367        )
368
369    def AdjustIncludeDirs(self, include_dirs, config):
370        """Updates include_dirs to expand VS specific paths, and adds the system
371        include dirs used for platform SDK and similar."""
372        config = self._TargetConfig(config)
373        includes = include_dirs + self.msvs_system_include_dirs[config]
374        includes.extend(
375            self._Setting(
376                ("VCCLCompilerTool", "AdditionalIncludeDirectories"), config, default=[]
377            )
378        )
379        return [self.ConvertVSMacros(p, config=config) for p in includes]
380
381    def AdjustMidlIncludeDirs(self, midl_include_dirs, config):
382        """Updates midl_include_dirs to expand VS specific paths, and adds the
383        system include dirs used for platform SDK and similar."""
384        config = self._TargetConfig(config)
385        includes = midl_include_dirs + self.msvs_system_include_dirs[config]
386        includes.extend(
387            self._Setting(
388                ("VCMIDLTool", "AdditionalIncludeDirectories"), config, default=[]
389            )
390        )
391        return [self.ConvertVSMacros(p, config=config) for p in includes]
392
393    def GetComputedDefines(self, config):
394        """Returns the set of defines that are injected to the defines list based
395        on other VS settings."""
396        config = self._TargetConfig(config)
397        defines = []
398        if self._ConfigAttrib(["CharacterSet"], config) == "1":
399            defines.extend(("_UNICODE", "UNICODE"))
400        if self._ConfigAttrib(["CharacterSet"], config) == "2":
401            defines.append("_MBCS")
402        defines.extend(
403            self._Setting(
404                ("VCCLCompilerTool", "PreprocessorDefinitions"), config, default=[]
405            )
406        )
407        return defines
408
409    def GetCompilerPdbName(self, config, expand_special):
410        """Get the pdb file name that should be used for compiler invocations, or
411        None if there's no explicit name specified."""
412        config = self._TargetConfig(config)
413        pdbname = self._Setting(("VCCLCompilerTool", "ProgramDataBaseFileName"), config)
414        if pdbname:
415            pdbname = expand_special(self.ConvertVSMacros(pdbname))
416        return pdbname
417
418    def GetMapFileName(self, config, expand_special):
419        """Gets the explicitly overridden map file name for a target or returns None
420        if it's not set."""
421        config = self._TargetConfig(config)
422        map_file = self._Setting(("VCLinkerTool", "MapFileName"), config)
423        if map_file:
424            map_file = expand_special(self.ConvertVSMacros(map_file, config=config))
425        return map_file
426
427    def GetOutputName(self, config, expand_special):
428        """Gets the explicitly overridden output name for a target or returns None
429        if it's not overridden."""
430        config = self._TargetConfig(config)
431        type = self.spec["type"]
432        root = "VCLibrarianTool" if type == "static_library" else "VCLinkerTool"
433        # TODO(scottmg): Handle OutputDirectory without OutputFile.
434        output_file = self._Setting((root, "OutputFile"), config)
435        if output_file:
436            output_file = expand_special(
437                self.ConvertVSMacros(output_file, config=config)
438            )
439        return output_file
440
441    def GetPDBName(self, config, expand_special, default):
442        """Gets the explicitly overridden pdb name for a target or returns
443        default if it's not overridden, or if no pdb will be generated."""
444        config = self._TargetConfig(config)
445        output_file = self._Setting(("VCLinkerTool", "ProgramDatabaseFile"), config)
446        generate_debug_info = self._Setting(
447            ("VCLinkerTool", "GenerateDebugInformation"), config
448        )
449        if generate_debug_info == "true":
450            if output_file:
451                return expand_special(self.ConvertVSMacros(output_file, config=config))
452            else:
453                return default
454        else:
455            return None
456
457    def GetNoImportLibrary(self, config):
458        """If NoImportLibrary: true, ninja will not expect the output to include
459        an import library."""
460        config = self._TargetConfig(config)
461        noimplib = self._Setting(("NoImportLibrary",), config)
462        return noimplib == "true"
463
464    def GetAsmflags(self, config):
465        """Returns the flags that need to be added to ml invocations."""
466        config = self._TargetConfig(config)
467        asmflags = []
468        safeseh = self._Setting(("MASM", "UseSafeExceptionHandlers"), config)
469        if safeseh == "true":
470            asmflags.append("/safeseh")
471        return asmflags
472
473    def GetCflags(self, config):
474        """Returns the flags that need to be added to .c and .cc compilations."""
475        config = self._TargetConfig(config)
476        cflags = []
477        cflags.extend(["/wd" + w for w in self.msvs_disabled_warnings[config]])
478        cl = self._GetWrapper(
479            self, self.msvs_settings[config], "VCCLCompilerTool", append=cflags
480        )
481        cl(
482            "Optimization",
483            map={"0": "d", "1": "1", "2": "2", "3": "x"},
484            prefix="/O",
485            default="2",
486        )
487        cl("InlineFunctionExpansion", prefix="/Ob")
488        cl("DisableSpecificWarnings", prefix="/wd")
489        cl("StringPooling", map={"true": "/GF"})
490        cl("EnableFiberSafeOptimizations", map={"true": "/GT"})
491        cl("OmitFramePointers", map={"false": "-", "true": ""}, prefix="/Oy")
492        cl("EnableIntrinsicFunctions", map={"false": "-", "true": ""}, prefix="/Oi")
493        cl("FavorSizeOrSpeed", map={"1": "t", "2": "s"}, prefix="/O")
494        cl(
495            "FloatingPointModel",
496            map={"0": "precise", "1": "strict", "2": "fast"},
497            prefix="/fp:",
498            default="0",
499        )
500        cl("CompileAsManaged", map={"false": "", "true": "/clr"})
501        cl("WholeProgramOptimization", map={"true": "/GL"})
502        cl("WarningLevel", prefix="/W")
503        cl("WarnAsError", map={"true": "/WX"})
504        cl(
505            "CallingConvention",
506            map={"0": "d", "1": "r", "2": "z", "3": "v"},
507            prefix="/G",
508        )
509        cl("DebugInformationFormat", map={"1": "7", "3": "i", "4": "I"}, prefix="/Z")
510        cl("RuntimeTypeInfo", map={"true": "/GR", "false": "/GR-"})
511        cl("EnableFunctionLevelLinking", map={"true": "/Gy", "false": "/Gy-"})
512        cl("MinimalRebuild", map={"true": "/Gm"})
513        cl("BufferSecurityCheck", map={"true": "/GS", "false": "/GS-"})
514        cl("BasicRuntimeChecks", map={"1": "s", "2": "u", "3": "1"}, prefix="/RTC")
515        cl(
516            "RuntimeLibrary",
517            map={"0": "T", "1": "Td", "2": "D", "3": "Dd"},
518            prefix="/M",
519        )
520        cl("ExceptionHandling", map={"1": "sc", "2": "a"}, prefix="/EH")
521        cl("DefaultCharIsUnsigned", map={"true": "/J"})
522        cl(
523            "TreatWChar_tAsBuiltInType",
524            map={"false": "-", "true": ""},
525            prefix="/Zc:wchar_t",
526        )
527        cl("EnablePREfast", map={"true": "/analyze"})
528        cl("AdditionalOptions", prefix="")
529        cl(
530            "EnableEnhancedInstructionSet",
531            map={"1": "SSE", "2": "SSE2", "3": "AVX", "4": "IA32", "5": "AVX2"},
532            prefix="/arch:",
533        )
534        cflags.extend(
535            [
536                "/FI" + f
537                for f in self._Setting(
538                    ("VCCLCompilerTool", "ForcedIncludeFiles"), config, default=[]
539                )
540            ]
541        )
542        if float(self.vs_version.project_version) >= 12.0:
543            # New flag introduced in VS2013 (project version 12.0) Forces writes to
544            # the program database (PDB) to be serialized through MSPDBSRV.EXE.
545            # https://msdn.microsoft.com/en-us/library/dn502518.aspx
546            cflags.append("/FS")
547        # ninja handles parallelism by itself, don't have the compiler do it too.
548        cflags = [x for x in cflags if not x.startswith("/MP")]
549        return cflags
550
551    def _GetPchFlags(self, config, extension):
552        """Get the flags to be added to the cflags for precompiled header support."""
553        config = self._TargetConfig(config)
554        # The PCH is only built once by a particular source file. Usage of PCH must
555        # only be for the same language (i.e. C vs. C++), so only include the pch
556        # flags when the language matches.
557        if self.msvs_precompiled_header[config]:
558            source_ext = os.path.splitext(self.msvs_precompiled_source[config])[1]
559            if _LanguageMatchesForPch(source_ext, extension):
560                pch = self.msvs_precompiled_header[config]
561                pchbase = os.path.split(pch)[1]
562                return ["/Yu" + pch, "/FI" + pch, "/Fp${pchprefix}." + pchbase + ".pch"]
563        return []
564
565    def GetCflagsC(self, config):
566        """Returns the flags that need to be added to .c compilations."""
567        config = self._TargetConfig(config)
568        return self._GetPchFlags(config, ".c")
569
570    def GetCflagsCC(self, config):
571        """Returns the flags that need to be added to .cc compilations."""
572        config = self._TargetConfig(config)
573        return ["/TP"] + self._GetPchFlags(config, ".cc")
574
575    def _GetAdditionalLibraryDirectories(self, root, config, gyp_to_build_path):
576        """Get and normalize the list of paths in AdditionalLibraryDirectories
577        setting."""
578        config = self._TargetConfig(config)
579        libpaths = self._Setting(
580            (root, "AdditionalLibraryDirectories"), config, default=[]
581        )
582        libpaths = [
583            os.path.normpath(gyp_to_build_path(self.ConvertVSMacros(p, config=config)))
584            for p in libpaths
585        ]
586        return ['/LIBPATH:"' + p + '"' for p in libpaths]
587
588    def GetLibFlags(self, config, gyp_to_build_path):
589        """Returns the flags that need to be added to lib commands."""
590        config = self._TargetConfig(config)
591        libflags = []
592        lib = self._GetWrapper(
593            self, self.msvs_settings[config], "VCLibrarianTool", append=libflags
594        )
595        libflags.extend(
596            self._GetAdditionalLibraryDirectories(
597                "VCLibrarianTool", config, gyp_to_build_path
598            )
599        )
600        lib("LinkTimeCodeGeneration", map={"true": "/LTCG"})
601        lib(
602            "TargetMachine",
603            map={"1": "X86", "17": "X64", "3": "ARM"},
604            prefix="/MACHINE:",
605        )
606        lib("AdditionalOptions")
607        return libflags
608
609    def GetDefFile(self, gyp_to_build_path):
610        """Returns the .def file from sources, if any.  Otherwise returns None."""
611        spec = self.spec
612        if spec["type"] in ("shared_library", "loadable_module", "executable"):
613            def_files = [
614                s for s in spec.get("sources", []) if s.lower().endswith(".def")
615            ]
616            if len(def_files) == 1:
617                return gyp_to_build_path(def_files[0])
618            elif len(def_files) > 1:
619                raise Exception("Multiple .def files")
620        return None
621
622    def _GetDefFileAsLdflags(self, ldflags, gyp_to_build_path):
623        """.def files get implicitly converted to a ModuleDefinitionFile for the
624        linker in the VS generator. Emulate that behaviour here."""
625        if def_file := self.GetDefFile(gyp_to_build_path):
626            ldflags.append('/DEF:"%s"' % def_file)
627
628    def GetPGDName(self, config, expand_special):
629        """Gets the explicitly overridden pgd name for a target or returns None
630        if it's not overridden."""
631        config = self._TargetConfig(config)
632        output_file = self._Setting(("VCLinkerTool", "ProfileGuidedDatabase"), config)
633        if output_file:
634            output_file = expand_special(
635                self.ConvertVSMacros(output_file, config=config)
636            )
637        return output_file
638
639    def GetLdflags(
640        self,
641        config,
642        gyp_to_build_path,
643        expand_special,
644        manifest_base_name,
645        output_name,
646        is_executable,
647        build_dir,
648    ):
649        """Returns the flags that need to be added to link commands, and the
650        manifest files."""
651        config = self._TargetConfig(config)
652        ldflags = []
653        ld = self._GetWrapper(
654            self, self.msvs_settings[config], "VCLinkerTool", append=ldflags
655        )
656        self._GetDefFileAsLdflags(ldflags, gyp_to_build_path)
657        ld("GenerateDebugInformation", map={"true": "/DEBUG"})
658        # TODO: These 'map' values come from machineTypeOption enum,
659        # and does not have an official value for ARM64 in VS2017 (yet).
660        # It needs to verify the ARM64 value when machineTypeOption is updated.
661        ld(
662            "TargetMachine",
663            map={"1": "X86", "17": "X64", "3": "ARM", "18": "ARM64"},
664            prefix="/MACHINE:",
665        )
666        ldflags.extend(
667            self._GetAdditionalLibraryDirectories(
668                "VCLinkerTool", config, gyp_to_build_path
669            )
670        )
671        ld("DelayLoadDLLs", prefix="/DELAYLOAD:")
672        ld("TreatLinkerWarningAsErrors", prefix="/WX", map={"true": "", "false": ":NO"})
673        if out := self.GetOutputName(config, expand_special):
674            ldflags.append("/OUT:" + out)
675        if pdb := self.GetPDBName(config, expand_special, output_name + ".pdb"):
676            ldflags.append("/PDB:" + pdb)
677        if pgd := self.GetPGDName(config, expand_special):
678            ldflags.append("/PGD:" + pgd)
679        map_file = self.GetMapFileName(config, expand_special)
680        ld("GenerateMapFile", map={"true": "/MAP:" + map_file if map_file else "/MAP"})
681        ld("MapExports", map={"true": "/MAPINFO:EXPORTS"})
682        ld("AdditionalOptions", prefix="")
683
684        minimum_required_version = self._Setting(
685            ("VCLinkerTool", "MinimumRequiredVersion"), config, default=""
686        )
687        if minimum_required_version:
688            minimum_required_version = "," + minimum_required_version
689        ld(
690            "SubSystem",
691            map={
692                "1": "CONSOLE%s" % minimum_required_version,
693                "2": "WINDOWS%s" % minimum_required_version,
694            },
695            prefix="/SUBSYSTEM:",
696        )
697
698        stack_reserve_size = self._Setting(
699            ("VCLinkerTool", "StackReserveSize"), config, default=""
700        )
701        if stack_reserve_size:
702            stack_commit_size = self._Setting(
703                ("VCLinkerTool", "StackCommitSize"), config, default=""
704            )
705            if stack_commit_size:
706                stack_commit_size = "," + stack_commit_size
707            ldflags.append(f"/STACK:{stack_reserve_size}{stack_commit_size}")
708
709        ld("TerminalServerAware", map={"1": ":NO", "2": ""}, prefix="/TSAWARE")
710        ld("LinkIncremental", map={"1": ":NO", "2": ""}, prefix="/INCREMENTAL")
711        ld("BaseAddress", prefix="/BASE:")
712        ld("FixedBaseAddress", map={"1": ":NO", "2": ""}, prefix="/FIXED")
713        ld("RandomizedBaseAddress", map={"1": ":NO", "2": ""}, prefix="/DYNAMICBASE")
714        ld("DataExecutionPrevention", map={"1": ":NO", "2": ""}, prefix="/NXCOMPAT")
715        ld("OptimizeReferences", map={"1": "NOREF", "2": "REF"}, prefix="/OPT:")
716        ld("ForceSymbolReferences", prefix="/INCLUDE:")
717        ld("EnableCOMDATFolding", map={"1": "NOICF", "2": "ICF"}, prefix="/OPT:")
718        ld(
719            "LinkTimeCodeGeneration",
720            map={"1": "", "2": ":PGINSTRUMENT", "3": ":PGOPTIMIZE", "4": ":PGUPDATE"},
721            prefix="/LTCG",
722        )
723        ld("IgnoreDefaultLibraryNames", prefix="/NODEFAULTLIB:")
724        ld("ResourceOnlyDLL", map={"true": "/NOENTRY"})
725        ld("EntryPointSymbol", prefix="/ENTRY:")
726        ld("Profile", map={"true": "/PROFILE"})
727        ld("LargeAddressAware", map={"1": ":NO", "2": ""}, prefix="/LARGEADDRESSAWARE")
728        # TODO(scottmg): This should sort of be somewhere else (not really a flag).
729        ld("AdditionalDependencies", prefix="")
730
731        safeseh_default = "true" if self.GetArch(config) == "x86" else None
732        ld(
733            "ImageHasSafeExceptionHandlers",
734            map={"false": ":NO", "true": ""},
735            prefix="/SAFESEH",
736            default=safeseh_default,
737        )
738
739        # If the base address is not specifically controlled, DYNAMICBASE should
740        # be on by default.
741        if not any("DYNAMICBASE" in flag or flag == "/FIXED" for flag in ldflags):
742            ldflags.append("/DYNAMICBASE")
743
744        # If the NXCOMPAT flag has not been specified, default to on. Despite the
745        # documentation that says this only defaults to on when the subsystem is
746        # Vista or greater (which applies to the linker), the IDE defaults it on
747        # unless it's explicitly off.
748        if not any("NXCOMPAT" in flag for flag in ldflags):
749            ldflags.append("/NXCOMPAT")
750
751        have_def_file = any(flag.startswith("/DEF:") for flag in ldflags)
752        (
753            manifest_flags,
754            intermediate_manifest,
755            manifest_files,
756        ) = self._GetLdManifestFlags(
757            config,
758            manifest_base_name,
759            gyp_to_build_path,
760            is_executable and not have_def_file,
761            build_dir,
762        )
763        ldflags.extend(manifest_flags)
764        return ldflags, intermediate_manifest, manifest_files
765
766    def _GetLdManifestFlags(
767        self, config, name, gyp_to_build_path, allow_isolation, build_dir
768    ):
769        """Returns a 3-tuple:
770        - the set of flags that need to be added to the link to generate
771          a default manifest
772        - the intermediate manifest that the linker will generate that should be
773          used to assert it doesn't add anything to the merged one.
774        - the list of all the manifest files to be merged by the manifest tool and
775          included into the link."""
776        generate_manifest = self._Setting(
777            ("VCLinkerTool", "GenerateManifest"), config, default="true"
778        )
779        if generate_manifest != "true":
780            # This means not only that the linker should not generate the intermediate
781            # manifest but also that the manifest tool should do nothing even when
782            # additional manifests are specified.
783            return ["/MANIFEST:NO"], [], []
784
785        output_name = name + ".intermediate.manifest"
786        flags = [
787            "/MANIFEST",
788            "/ManifestFile:" + output_name,
789        ]
790
791        # Instead of using the MANIFESTUAC flags, we generate a .manifest to
792        # include into the list of manifests. This allows us to avoid the need to
793        # do two passes during linking. The /MANIFEST flag and /ManifestFile are
794        # still used, and the intermediate manifest is used to assert that the
795        # final manifest we get from merging all the additional manifest files
796        # (plus the one we generate here) isn't modified by merging the
797        # intermediate into it.
798
799        # Always NO, because we generate a manifest file that has what we want.
800        flags.append("/MANIFESTUAC:NO")
801
802        config = self._TargetConfig(config)
803        enable_uac = self._Setting(
804            ("VCLinkerTool", "EnableUAC"), config, default="true"
805        )
806        manifest_files = []
807        generated_manifest_outer = (
808            "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>"
809            "<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>"
810            "%s</assembly>"
811        )
812        if enable_uac == "true":
813            execution_level = self._Setting(
814                ("VCLinkerTool", "UACExecutionLevel"), config, default="0"
815            )
816            execution_level_map = {
817                "0": "asInvoker",
818                "1": "highestAvailable",
819                "2": "requireAdministrator",
820            }
821
822            ui_access = self._Setting(
823                ("VCLinkerTool", "UACUIAccess"), config, default="false"
824            )
825
826            level = execution_level_map[execution_level]
827            inner = f"""
828<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
829  <security>
830    <requestedPrivileges>
831      <requestedExecutionLevel level='{level}' uiAccess='{ui_access}' />
832    </requestedPrivileges>
833  </security>
834</trustInfo>"""
835        else:
836            inner = ""
837
838        generated_manifest_contents = generated_manifest_outer % inner
839        generated_name = name + ".generated.manifest"
840        # Need to join with the build_dir here as we're writing it during
841        # generation time, but we return the un-joined version because the build
842        # will occur in that directory. We only write the file if the contents
843        # have changed so that simply regenerating the project files doesn't
844        # cause a relink.
845        build_dir_generated_name = os.path.join(build_dir, generated_name)
846        gyp.common.EnsureDirExists(build_dir_generated_name)
847        f = gyp.common.WriteOnDiff(build_dir_generated_name)
848        f.write(generated_manifest_contents)
849        f.close()
850        manifest_files = [generated_name]
851
852        if allow_isolation:
853            flags.append("/ALLOWISOLATION")
854
855        manifest_files += self._GetAdditionalManifestFiles(config, gyp_to_build_path)
856        return flags, output_name, manifest_files
857
858    def _GetAdditionalManifestFiles(self, config, gyp_to_build_path):
859        """Gets additional manifest files that are added to the default one
860        generated by the linker."""
861        files = self._Setting(
862            ("VCManifestTool", "AdditionalManifestFiles"), config, default=[]
863        )
864        if isinstance(files, str):
865            files = files.split(";")
866        return [
867            os.path.normpath(gyp_to_build_path(self.ConvertVSMacros(f, config=config)))
868            for f in files
869        ]
870
871    def IsUseLibraryDependencyInputs(self, config):
872        """Returns whether the target should be linked via Use Library Dependency
873        Inputs (using component .objs of a given .lib)."""
874        config = self._TargetConfig(config)
875        uldi = self._Setting(("VCLinkerTool", "UseLibraryDependencyInputs"), config)
876        return uldi == "true"
877
878    def IsEmbedManifest(self, config):
879        """Returns whether manifest should be linked into binary."""
880        config = self._TargetConfig(config)
881        embed = self._Setting(
882            ("VCManifestTool", "EmbedManifest"), config, default="true"
883        )
884        return embed == "true"
885
886    def IsLinkIncremental(self, config):
887        """Returns whether the target should be linked incrementally."""
888        config = self._TargetConfig(config)
889        link_inc = self._Setting(("VCLinkerTool", "LinkIncremental"), config)
890        return link_inc != "1"
891
892    def GetRcflags(self, config, gyp_to_ninja_path):
893        """Returns the flags that need to be added to invocations of the resource
894        compiler."""
895        config = self._TargetConfig(config)
896        rcflags = []
897        rc = self._GetWrapper(
898            self, self.msvs_settings[config], "VCResourceCompilerTool", append=rcflags
899        )
900        rc("AdditionalIncludeDirectories", map=gyp_to_ninja_path, prefix="/I")
901        rcflags.append("/I" + gyp_to_ninja_path("."))
902        rc("PreprocessorDefinitions", prefix="/d")
903        # /l arg must be in hex without leading '0x'
904        rc("Culture", prefix="/l", map=lambda x: hex(int(x))[2:])
905        return rcflags
906
907    def BuildCygwinBashCommandLine(self, args, path_to_base):
908        """Build a command line that runs args via cygwin bash. We assume that all
909        incoming paths are in Windows normpath'd form, so they need to be
910        converted to posix style for the part of the command line that's passed to
911        bash. We also have to do some Visual Studio macro emulation here because
912        various rules use magic VS names for things. Also note that rules that
913        contain ninja variables cannot be fixed here (for example ${source}), so
914        the outer generator needs to make sure that the paths that are written out
915        are in posix style, if the command line will be used here."""
916        cygwin_dir = os.path.normpath(
917            os.path.join(path_to_base, self.msvs_cygwin_dirs[0])
918        )
919        cd = ("cd %s" % path_to_base).replace("\\", "/")
920        args = [a.replace("\\", "/").replace('"', '\\"') for a in args]
921        args = ["'%s'" % a.replace("'", "'\\''") for a in args]
922        bash_cmd = " ".join(args)
923        cmd = (
924            'call "%s\\setup_env.bat" && set CYGWIN=nontsec && ' % cygwin_dir
925            + f'bash -c "{cd} ; {bash_cmd}"'
926        )
927        return cmd
928
929    RuleShellFlags = namedtuple("RuleShellFlags", ["cygwin", "quote"])  # noqa: PYI024
930
931    def GetRuleShellFlags(self, rule):
932        """Return RuleShellFlags about how the given rule should be run. This
933        includes whether it should run under cygwin (msvs_cygwin_shell), and
934        whether the commands should be quoted (msvs_quote_cmd)."""
935        # If the variable is unset, or set to 1 we use cygwin
936        cygwin = (
937            int(rule.get("msvs_cygwin_shell", self.spec.get("msvs_cygwin_shell", 1)))
938            != 0
939        )
940        # Default to quoting. There's only a few special instances where the
941        # target command uses non-standard command line parsing and handle quotes
942        # and quote escaping differently.
943        quote_cmd = int(rule.get("msvs_quote_cmd", 1))
944        assert quote_cmd != 0 or cygwin != 1, (
945            "msvs_quote_cmd=0 only applicable for msvs_cygwin_shell=0"
946        )
947        return MsvsSettings.RuleShellFlags(cygwin, quote_cmd)
948
949    def _HasExplicitRuleForExtension(self, spec, extension):
950        """Determine if there's an explicit rule for a particular extension."""
951        return any(rule["extension"] == extension for rule in spec.get("rules", []))
952
953    def _HasExplicitIdlActions(self, spec):
954        """Determine if an action should not run midl for .idl files."""
955        return any(
956            action.get("explicit_idl_action", 0) for action in spec.get("actions", [])
957        )
958
959    def HasExplicitIdlRulesOrActions(self, spec):
960        """Determine if there's an explicit rule or action for idl files. When
961        there isn't we need to generate implicit rules to build MIDL .idl files."""
962        return self._HasExplicitRuleForExtension(
963            spec, "idl"
964        ) or self._HasExplicitIdlActions(spec)
965
966    def HasExplicitAsmRules(self, spec):
967        """Determine if there's an explicit rule for asm files. When there isn't we
968        need to generate implicit rules to assemble .asm files."""
969        return self._HasExplicitRuleForExtension(spec, "asm")
970
971    def GetIdlBuildData(self, source, config):
972        """Determine the implicit outputs for an idl file. Returns output
973        directory, outputs, and variables and flags that are required."""
974        config = self._TargetConfig(config)
975        midl_get = self._GetWrapper(self, self.msvs_settings[config], "VCMIDLTool")
976
977        def midl(name, default=None):
978            return self.ConvertVSMacros(midl_get(name, default=default), config=config)
979
980        tlb = midl("TypeLibraryName", default="${root}.tlb")
981        header = midl("HeaderFileName", default="${root}.h")
982        dlldata = midl("DLLDataFileName", default="dlldata.c")
983        iid = midl("InterfaceIdentifierFileName", default="${root}_i.c")
984        proxy = midl("ProxyFileName", default="${root}_p.c")
985        # Note that .tlb is not included in the outputs as it is not always
986        # generated depending on the content of the input idl file.
987        outdir = midl("OutputDirectory", default="")
988        output = [header, dlldata, iid, proxy]
989        variables = [
990            ("tlb", tlb),
991            ("h", header),
992            ("dlldata", dlldata),
993            ("iid", iid),
994            ("proxy", proxy),
995        ]
996        # TODO(scottmg): Are there configuration settings to set these flags?
997        target_platform = self.GetArch(config)
998        if target_platform == "x86":
999            target_platform = "win32"
1000        flags = ["/char", "signed", "/env", target_platform, "/Oicf"]
1001        return outdir, output, variables, flags
1002
1003
1004def _LanguageMatchesForPch(source_ext, pch_source_ext):
1005    c_exts = (".c",)
1006    cc_exts = (".cc", ".cxx", ".cpp")
1007    return (source_ext in c_exts and pch_source_ext in c_exts) or (
1008        source_ext in cc_exts and pch_source_ext in cc_exts
1009    )
1010
1011
1012class PrecompiledHeader:
1013    """Helper to generate dependencies and build rules to handle generation of
1014    precompiled headers. Interface matches the GCH handler in xcode_emulation.py.
1015    """
1016
1017    def __init__(
1018        self, settings, config, gyp_to_build_path, gyp_to_unique_output, obj_ext
1019    ):
1020        self.settings = settings
1021        self.config = config
1022        pch_source = self.settings.msvs_precompiled_source[self.config]
1023        self.pch_source = gyp_to_build_path(pch_source)
1024        filename, _ = os.path.splitext(pch_source)
1025        self.output_obj = gyp_to_unique_output(filename + obj_ext).lower()
1026
1027    def _PchHeader(self):
1028        """Get the header that will appear in an #include line for all source
1029        files."""
1030        return self.settings.msvs_precompiled_header[self.config]
1031
1032    def GetObjDependencies(self, sources, objs, arch):
1033        """Given a list of sources files and the corresponding object files,
1034        returns a list of the pch files that should be depended upon. The
1035        additional wrapping in the return value is for interface compatibility
1036        with make.py on Mac, and xcode_emulation.py."""
1037        assert arch is None
1038        if not self._PchHeader():
1039            return []
1040        pch_ext = os.path.splitext(self.pch_source)[1]
1041        for source in sources:
1042            if _LanguageMatchesForPch(os.path.splitext(source)[1], pch_ext):
1043                return [(None, None, self.output_obj)]
1044        return []
1045
1046    def GetPchBuildCommands(self, arch):
1047        """Not used on Windows as there are no additional build steps required
1048        (instead, existing steps are modified in GetFlagsModifications below)."""
1049        return []
1050
1051    def GetFlagsModifications(
1052        self, input, output, implicit, command, cflags_c, cflags_cc, expand_special
1053    ):
1054        """Get the modified cflags and implicit dependencies that should be used
1055        for the pch compilation step."""
1056        if input == self.pch_source:
1057            pch_output = ["/Yc" + self._PchHeader()]
1058            if command == "cxx":
1059                return (
1060                    [("cflags_cc", map(expand_special, cflags_cc + pch_output))],
1061                    self.output_obj,
1062                    [],
1063                )
1064            elif command == "cc":
1065                return (
1066                    [("cflags_c", map(expand_special, cflags_c + pch_output))],
1067                    self.output_obj,
1068                    [],
1069                )
1070        return [], output, implicit
1071
1072
1073vs_version = None
1074
1075
1076def GetVSVersion(generator_flags):
1077    global vs_version
1078    if not vs_version:
1079        vs_version = gyp.MSVSVersion.SelectVisualStudioVersion(
1080            generator_flags.get("msvs_version", "auto"), allow_fallback=False
1081        )
1082    return vs_version
1083
1084
1085def _GetVsvarsSetupArgs(generator_flags, arch):
1086    vs = GetVSVersion(generator_flags)
1087    return vs.SetupScript()
1088
1089
1090def ExpandMacros(string, expansions):
1091    """Expand $(Variable) per expansions dict. See MsvsSettings.GetVSMacroEnv
1092    for the canonical way to retrieve a suitable dict."""
1093    if "$" in string:
1094        for old, new in expansions.items():
1095            assert "$(" not in new, new
1096            string = string.replace(old, new)
1097    return string
1098
1099
1100def _ExtractImportantEnvironment(output_of_set):
1101    """Extracts environment variables required for the toolchain to run from
1102    a textual dump output by the cmd.exe 'set' command."""
1103    envvars_to_save = (
1104        "goma_.*",  # TODO(scottmg): This is ugly, but needed for goma.
1105        "include",
1106        "lib",
1107        "libpath",
1108        "path",
1109        "pathext",
1110        "systemroot",
1111        "temp",
1112        "tmp",
1113    )
1114    env = {}
1115    # This occasionally happens and leads to misleading SYSTEMROOT error messages
1116    # if not caught here.
1117    if output_of_set.count("=") == 0:
1118        raise Exception("Invalid output_of_set. Value is:\n%s" % output_of_set)
1119    for line in output_of_set.splitlines():
1120        for envvar in envvars_to_save:
1121            if re.match(envvar + "=", line.lower()):
1122                var, setting = line.split("=", 1)
1123                if envvar == "path":
1124                    # Our own rules (for running gyp-win-tool) and other actions in
1125                    # Chromium rely on python being in the path. Add the path to this
1126                    # python here so that if it's not in the path when ninja is run
1127                    # later, python will still be found.
1128                    setting = os.path.dirname(sys.executable) + os.pathsep + setting
1129                env[var.upper()] = setting
1130                break
1131    for required in ("SYSTEMROOT", "TEMP", "TMP"):
1132        if required not in env:
1133            raise Exception(
1134                'Environment variable "%s" required to be set to valid path' % required
1135            )
1136    return env
1137
1138
1139def _FormatAsEnvironmentBlock(envvar_dict):
1140    """Format as an 'environment block' directly suitable for CreateProcess.
1141    Briefly this is a list of key=value\0, terminated by an additional \0. See
1142    CreateProcess documentation for more details."""
1143    block = ""
1144    nul = "\0"
1145    for key, value in envvar_dict.items():
1146        block += key + "=" + value + nul
1147    block += nul
1148    return block
1149
1150
1151def _ExtractCLPath(output_of_where):
1152    """Gets the path to cl.exe based on the output of calling the environment
1153    setup batch file, followed by the equivalent of `where`."""
1154    # Take the first line, as that's the first found in the PATH.
1155    for line in output_of_where.strip().splitlines():
1156        if line.startswith("LOC:"):
1157            return line[len("LOC:") :].strip()
1158
1159
1160def GenerateEnvironmentFiles(
1161    toplevel_build_dir, generator_flags, system_includes, open_out
1162):
1163    """It's not sufficient to have the absolute path to the compiler, linker,
1164    etc. on Windows, as those tools rely on .dlls being in the PATH. We also
1165    need to support both x86 and x64 compilers within the same build (to support
1166    msvs_target_platform hackery). Different architectures require a different
1167    compiler binary, and different supporting environment variables (INCLUDE,
1168    LIB, LIBPATH). So, we extract the environment here, wrap all invocations
1169    of compiler tools (cl, link, lib, rc, midl, etc.) via win_tool.py which
1170    sets up the environment, and then we do not prefix the compiler with
1171    an absolute path, instead preferring something like "cl.exe" in the rule
1172    which will then run whichever the environment setup has put in the path.
1173    When the following procedure to generate environment files does not
1174    meet your requirement (e.g. for custom toolchains), you can pass
1175    "-G ninja_use_custom_environment_files" to the gyp to suppress file
1176    generation and use custom environment files prepared by yourself."""
1177    archs = ("x86", "x64")
1178    if generator_flags.get("ninja_use_custom_environment_files", 0):
1179        cl_paths = {}
1180        for arch in archs:
1181            cl_paths[arch] = "cl.exe"
1182        return cl_paths
1183    vs = GetVSVersion(generator_flags)
1184    cl_paths = {}
1185    for arch in archs:
1186        # Extract environment variables for subprocesses.
1187        args = vs.SetupScript(arch)
1188        args.extend(("&&", "set"))
1189        popen = subprocess.Popen(
1190            args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
1191        )
1192        variables = popen.communicate()[0].decode("utf-8")
1193        if popen.returncode != 0:
1194            raise Exception('"%s" failed with error %d' % (args, popen.returncode))
1195        env = _ExtractImportantEnvironment(variables)
1196
1197        # Inject system includes from gyp files into INCLUDE.
1198        if system_includes:
1199            system_includes = system_includes | OrderedSet(
1200                env.get("INCLUDE", "").split(";")

Showing the first 1,200 of 1256 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai