Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
msvs.py3971 linesDownload Raw Back to generator
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
6import ntpath
7import os
8import posixpath
9import re
10import subprocess
11import sys
12from collections import OrderedDict
13
14import gyp.common
15import gyp.generator.ninja as ninja_generator
16from gyp import (
17    MSVSNew,
18    MSVSProject,
19    MSVSSettings,
20    MSVSToolFile,
21    MSVSUserFile,
22    MSVSUtil,
23    MSVSVersion,
24    easy_xml,
25)
26from gyp.common import GypError, OrderedSet
27
28# Regular expression for validating Visual Studio GUIDs.  If the GUID
29# contains lowercase hex letters, MSVS will be fine. However,
30# IncrediBuild BuildConsole will parse the solution file, but then
31# silently skip building the target causing hard to track down errors.
32# Note that this only happens with the BuildConsole, and does not occur
33# if IncrediBuild is executed from inside Visual Studio.  This regex
34# validates that the string looks like a GUID with all uppercase hex
35# letters.
36VALID_MSVS_GUID_CHARS = re.compile(r"^[A-F0-9\-]+$")
37
38generator_supports_multiple_toolsets = gyp.common.CrossCompileRequested()
39
40generator_default_variables = {
41    "DRIVER_PREFIX": "",
42    "DRIVER_SUFFIX": ".sys",
43    "EXECUTABLE_PREFIX": "",
44    "EXECUTABLE_SUFFIX": ".exe",
45    "STATIC_LIB_PREFIX": "",
46    "SHARED_LIB_PREFIX": "",
47    "STATIC_LIB_SUFFIX": ".lib",
48    "SHARED_LIB_SUFFIX": ".dll",
49    "INTERMEDIATE_DIR": "$(IntDir)",
50    "SHARED_INTERMEDIATE_DIR": "$(OutDir)/obj/global_intermediate",
51    "OS": "win",
52    "PRODUCT_DIR": "$(OutDir)",
53    "LIB_DIR": "$(OutDir)lib",
54    "RULE_INPUT_ROOT": "$(InputName)",
55    "RULE_INPUT_DIRNAME": "$(InputDir)",
56    "RULE_INPUT_EXT": "$(InputExt)",
57    "RULE_INPUT_NAME": "$(InputFileName)",
58    "RULE_INPUT_PATH": "$(InputPath)",
59    "CONFIGURATION_NAME": "$(ConfigurationName)",
60}
61
62
63# The msvs specific sections that hold paths
64generator_additional_path_sections = [
65    "msvs_cygwin_dirs",
66    "msvs_props",
67]
68
69
70generator_additional_non_configuration_keys = [
71    "msvs_cygwin_dirs",
72    "msvs_cygwin_shell",
73    "msvs_large_pdb",
74    "msvs_shard",
75    "msvs_external_builder",
76    "msvs_external_builder_out_dir",
77    "msvs_external_builder_build_cmd",
78    "msvs_external_builder_clean_cmd",
79    "msvs_external_builder_clcompile_cmd",
80    "msvs_enable_winrt",
81    "msvs_requires_importlibrary",
82    "msvs_enable_winphone",
83    "msvs_application_type_revision",
84    "msvs_target_platform_version",
85    "msvs_target_platform_minversion",
86]
87
88generator_filelist_paths = None
89
90# List of precompiled header related keys.
91precomp_keys = [
92    "msvs_precompiled_header",
93    "msvs_precompiled_source",
94]
95
96
97cached_username = None
98
99
100cached_domain = None
101
102
103# TODO(gspencer): Switch the os.environ calls to be
104# win32api.GetDomainName() and win32api.GetUserName() once the
105# python version in depot_tools has been updated to work on Vista
106# 64-bit.
107def _GetDomainAndUserName():
108    if sys.platform not in ("win32", "cygwin"):
109        return ("DOMAIN", "USERNAME")
110    global cached_username
111    global cached_domain
112    if not cached_domain or not cached_username:
113        domain = os.environ.get("USERDOMAIN")
114        username = os.environ.get("USERNAME")
115        if not domain or not username:
116            call = subprocess.Popen(
117                ["net", "config", "Workstation"], stdout=subprocess.PIPE
118            )
119            config = call.communicate()[0].decode("utf-8")
120            username_re = re.compile(r"^User name\s+(\S+)", re.MULTILINE)
121            username_match = username_re.search(config)
122            if username_match:
123                username = username_match.group(1)
124            domain_re = re.compile(r"^Logon domain\s+(\S+)", re.MULTILINE)
125            domain_match = domain_re.search(config)
126            if domain_match:
127                domain = domain_match.group(1)
128        cached_domain = domain
129        cached_username = username
130    return (cached_domain, cached_username)
131
132
133fixpath_prefix = None
134
135
136def _NormalizedSource(source):
137    """Normalize the path.
138
139    But not if that gets rid of a variable, as this may expand to something
140    larger than one directory.
141
142    Arguments:
143        source: The path to be normalize.d
144
145    Returns:
146        The normalized path.
147    """
148    normalized = os.path.normpath(source)
149    if source.count("$") == normalized.count("$"):
150        source = normalized
151    return source
152
153
154def _FixPath(path, separator="\\"):
155    """Convert paths to a form that will make sense in a vcproj file.
156
157    Arguments:
158      path: The path to convert, may contain / etc.
159    Returns:
160      The path with all slashes made into backslashes.
161    """
162    if (
163        fixpath_prefix
164        and path
165        and not os.path.isabs(path)
166        and path[0] != "$"
167        and not _IsWindowsAbsPath(path)
168    ):
169        path = os.path.join(fixpath_prefix, path)
170    if separator == "\\":
171        path = path.replace("/", "\\")
172    path = _NormalizedSource(path)
173    if separator == "/":
174        path = path.replace("\\", "/")
175    if path and path[-1] == separator:
176        path = path[:-1]
177    return path
178
179
180def _IsWindowsAbsPath(path):
181    """
182    On Cygwin systems Python needs a little help determining if a path
183    is an absolute Windows path or not, so that
184    it does not treat those as relative, which results in bad paths like:
185    '..\\C:\\<some path>\\some_source_code_file.cc'
186    """
187    return path.startswith(("c:", "C:"))
188
189
190def _FixPaths(paths, separator="\\"):
191    """Fix each of the paths of the list."""
192    return [_FixPath(i, separator) for i in paths]
193
194
195def _ConvertSourcesToFilterHierarchy(
196    sources, prefix=None, excluded=None, list_excluded=True, msvs_version=None
197):
198    """Converts a list split source file paths into a vcproj folder hierarchy.
199
200    Arguments:
201      sources: A list of source file paths split.
202      prefix: A list of source file path layers meant to apply to each of sources.
203      excluded: A set of excluded files.
204      msvs_version: A MSVSVersion object.
205
206    Returns:
207      A hierarchy of filenames and MSVSProject.Filter objects that matches the
208      layout of the source tree.
209      For example:
210      _ConvertSourcesToFilterHierarchy([['a', 'bob1.c'], ['b', 'bob2.c']],
211                                       prefix=['joe'])
212      -->
213      [MSVSProject.Filter('a', contents=['joe\\a\\bob1.c']),
214       MSVSProject.Filter('b', contents=['joe\\b\\bob2.c'])]
215    """
216    if not prefix:
217        prefix = []
218    result = []
219    excluded_result = []
220    folders = OrderedDict()
221    # Gather files into the final result, excluded, or folders.
222    for s in sources:
223        if len(s) == 1:
224            filename = _NormalizedSource("\\".join(prefix + s))
225            if filename in excluded:
226                excluded_result.append(filename)
227            else:
228                result.append(filename)
229        elif msvs_version and not msvs_version.UsesVcxproj():
230            # For MSVS 2008 and earlier, we need to process all files before walking
231            # the sub folders.
232            if not folders.get(s[0]):
233                folders[s[0]] = []
234            folders[s[0]].append(s[1:])
235        else:
236            contents = _ConvertSourcesToFilterHierarchy(
237                [s[1:]],
238                prefix + [s[0]],
239                excluded=excluded,
240                list_excluded=list_excluded,
241                msvs_version=msvs_version,
242            )
243            contents = MSVSProject.Filter(s[0], contents=contents)
244            result.append(contents)
245    # Add a folder for excluded files.
246    if excluded_result and list_excluded:
247        excluded_folder = MSVSProject.Filter(
248            "_excluded_files", contents=excluded_result
249        )
250        result.append(excluded_folder)
251
252    if msvs_version and msvs_version.UsesVcxproj():
253        return result
254
255    # Populate all the folders.
256    for f in folders:
257        contents = _ConvertSourcesToFilterHierarchy(
258            folders[f],
259            prefix=prefix + [f],
260            excluded=excluded,
261            list_excluded=list_excluded,
262            msvs_version=msvs_version,
263        )
264        contents = MSVSProject.Filter(f, contents=contents)
265        result.append(contents)
266    return result
267
268
269def _ToolAppend(tools, tool_name, setting, value, only_if_unset=False):
270    if not value:
271        return
272    _ToolSetOrAppend(tools, tool_name, setting, value, only_if_unset)
273
274
275def _ToolSetOrAppend(tools, tool_name, setting, value, only_if_unset=False):
276    # TODO(bradnelson): ugly hack, fix this more generally!!!
277    if "Directories" in setting or "Dependencies" in setting:
278        if isinstance(value, str):
279            value = value.replace("/", "\\")
280        else:
281            value = [i.replace("/", "\\") for i in value]
282    if not tools.get(tool_name):
283        tools[tool_name] = {}
284    tool = tools[tool_name]
285    if setting == "CompileAsWinRT":
286        return
287    if tool.get(setting):
288        if only_if_unset:
289            return
290        if isinstance(tool[setting], list) and isinstance(value, list):
291            tool[setting] += value
292        else:
293            raise TypeError(
294                'Appending "%s" to a non-list setting "%s" for tool "%s" is '
295                "not allowed, previous value: %s"
296                % (value, setting, tool_name, str(tool[setting]))
297            )
298    else:
299        tool[setting] = value
300
301
302def _ConfigTargetVersion(config_data):
303    return config_data.get("msvs_target_version", "Windows7")
304
305
306def _ConfigPlatform(config_data):
307    return config_data.get("msvs_configuration_platform", "Win32")
308
309
310def _ConfigBaseName(config_name, platform_name):
311    if config_name.endswith("_" + platform_name):
312        return config_name[0 : -len(platform_name) - 1]
313    else:
314        return config_name
315
316
317def _ConfigFullName(config_name, config_data):
318    platform_name = _ConfigPlatform(config_data)
319    return f"{_ConfigBaseName(config_name, platform_name)}|{platform_name}"
320
321
322def _ConfigWindowsTargetPlatformVersion(config_data, version):
323    target_ver = config_data.get("msvs_windows_target_platform_version")
324    if target_ver and re.match(r"^\d+", target_ver):
325        return target_ver
326    config_ver = config_data.get("msvs_windows_sdk_version")
327    vers = [config_ver] if config_ver else version.compatible_sdks
328    for ver in vers:
329        for key in [
330            r"HKLM\Software\Microsoft\Microsoft SDKs\Windows\%s",
331            r"HKLM\Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows\%s",
332        ]:
333            sdk_dir = MSVSVersion._RegistryGetValue(key % ver, "InstallationFolder")
334            if not sdk_dir:
335                continue
336            version = MSVSVersion._RegistryGetValue(key % ver, "ProductVersion") or ""
337            # Find a matching entry in sdk_dir\include.
338            expected_sdk_dir = r"%s\include" % sdk_dir
339            names = sorted(
340                (
341                    x
342                    for x in (
343                        os.listdir(expected_sdk_dir)
344                        if os.path.isdir(expected_sdk_dir)
345                        else []
346                    )
347                    if x.startswith(version)
348                ),
349                reverse=True,
350            )
351            if names:
352                return names[0]
353            else:
354                print(
355                    "Warning: No include files found for detected "
356                    "Windows SDK version %s" % (version),
357                    file=sys.stdout,
358                )
359
360
361def _BuildCommandLineForRuleRaw(
362    spec, cmd, cygwin_shell, has_input_path, quote_cmd, do_setup_env
363):
364    if [x for x in cmd if "$(InputDir)" in x]:
365        input_dir_preamble = (
366            "set INPUTDIR=$(InputDir)\n"
367            "if NOT DEFINED INPUTDIR set INPUTDIR=.\\\n"
368            "set INPUTDIR=%INPUTDIR:~0,-1%\n"
369        )
370    else:
371        input_dir_preamble = ""
372
373    if cygwin_shell:
374        # Find path to cygwin.
375        cygwin_dir = _FixPath(spec.get("msvs_cygwin_dirs", ["."])[0])
376        # Prepare command.
377        direct_cmd = cmd
378        direct_cmd = [
379            i.replace("$(IntDir)", '`cygpath -m "${INTDIR}"`') for i in direct_cmd
380        ]
381        direct_cmd = [
382            i.replace("$(OutDir)", '`cygpath -m "${OUTDIR}"`') for i in direct_cmd
383        ]
384        direct_cmd = [
385            i.replace("$(InputDir)", '`cygpath -m "${INPUTDIR}"`') for i in direct_cmd
386        ]
387        if has_input_path:
388            direct_cmd = [
389                i.replace("$(InputPath)", '`cygpath -m "${INPUTPATH}"`')
390                for i in direct_cmd
391            ]
392        direct_cmd = ['\\"%s\\"' % i.replace('"', '\\\\\\"') for i in direct_cmd]
393        # direct_cmd = gyp.common.EncodePOSIXShellList(direct_cmd)
394        direct_cmd = " ".join(direct_cmd)
395        # TODO(quote):  regularize quoting path names throughout the module
396        cmd = ""
397        if do_setup_env:
398            cmd += 'call "$(ProjectDir)%(cygwin_dir)s\\setup_env.bat" && '
399        cmd += "set CYGWIN=nontsec&& "
400        if direct_cmd.find("NUMBER_OF_PROCESSORS") >= 0:
401            cmd += "set /a NUMBER_OF_PROCESSORS_PLUS_1=%%NUMBER_OF_PROCESSORS%%+1&& "
402        if direct_cmd.find("INTDIR") >= 0:
403            cmd += "set INTDIR=$(IntDir)&& "
404        if direct_cmd.find("OUTDIR") >= 0:
405            cmd += "set OUTDIR=$(OutDir)&& "
406        if has_input_path and direct_cmd.find("INPUTPATH") >= 0:
407            cmd += "set INPUTPATH=$(InputPath) && "
408        cmd += 'bash -c "%(cmd)s"'
409        cmd = cmd % {"cygwin_dir": cygwin_dir, "cmd": direct_cmd}
410        return input_dir_preamble + cmd
411    else:
412        # Convert cat --> type to mimic unix.
413        command = ["type"] if cmd[0] == "cat" else [cmd[0].replace("/", "\\")]
414        # Add call before command to ensure that commands can be tied together one
415        # after the other without aborting in Incredibuild, since IB makes a bat
416        # file out of the raw command string, and some commands (like python) are
417        # actually batch files themselves.
418        command.insert(0, "call")
419        # Fix the paths
420        # TODO(quote): This is a really ugly heuristic, and will miss path fixing
421        #              for arguments like "--arg=path", arg=path, or "/opt:path".
422        # If the argument starts with a slash or dash, or contains an equal sign,
423        # it's probably a command line switch.
424        # Return the path with forward slashes because the command using it might
425        # not support backslashes.
426        arguments = [
427            i if (i[:1] in "/-" or "=" in i) else _FixPath(i, "/") for i in cmd[1:]
428        ]
429        arguments = [i.replace("$(InputDir)", "%INPUTDIR%") for i in arguments]
430        arguments = [MSVSSettings.FixVCMacroSlashes(i) for i in arguments]
431        if quote_cmd:
432            # Support a mode for using cmd directly.
433            # Convert any paths to native form (first element is used directly).
434            # TODO(quote):  regularize quoting path names throughout the module
435            command[1] = '"%s"' % command[1]
436            arguments = ['"%s"' % i for i in arguments]
437        # Collapse into a single command.
438        return input_dir_preamble + " ".join(command + arguments)
439
440
441def _BuildCommandLineForRule(spec, rule, has_input_path, do_setup_env):
442    # Currently this weird argument munging is used to duplicate the way a
443    # python script would need to be run as part of the chrome tree.
444    # Eventually we should add some sort of rule_default option to set this
445    # per project. For now the behavior chrome needs is the default.
446    mcs = rule.get("msvs_cygwin_shell")
447    if mcs is None:
448        mcs = int(spec.get("msvs_cygwin_shell", 1))
449    elif isinstance(mcs, str):
450        mcs = int(mcs)
451    quote_cmd = int(rule.get("msvs_quote_cmd", 1))
452    return _BuildCommandLineForRuleRaw(
453        spec, rule["action"], mcs, has_input_path, quote_cmd, do_setup_env=do_setup_env
454    )
455
456
457def _AddActionStep(actions_dict, inputs, outputs, description, command):
458    """Merge action into an existing list of actions.
459
460    Care must be taken so that actions which have overlapping inputs either don't
461    get assigned to the same input, or get collapsed into one.
462
463    Arguments:
464      actions_dict: dictionary keyed on input name, which maps to a list of
465        dicts describing the actions attached to that input file.
466      inputs: list of inputs
467      outputs: list of outputs
468      description: description of the action
469      command: command line to execute
470    """
471    # Require there to be at least one input (call sites will ensure this).
472    assert inputs
473
474    action = {
475        "inputs": inputs,
476        "outputs": outputs,
477        "description": description,
478        "command": command,
479    }
480
481    # Pick where to stick this action.
482    # While less than optimal in terms of build time, attach them to the first
483    # input for now.
484    chosen_input = inputs[0]
485
486    # Add it there.
487    if chosen_input not in actions_dict:
488        actions_dict[chosen_input] = []
489    actions_dict[chosen_input].append(action)
490
491
492def _AddCustomBuildToolForMSVS(
493    p, spec, primary_input, inputs, outputs, description, cmd
494):
495    """Add a custom build tool to execute something.
496
497    Arguments:
498      p: the target project
499      spec: the target project dict
500      primary_input: input file to attach the build tool to
501      inputs: list of inputs
502      outputs: list of outputs
503      description: description of the action
504      cmd: command line to execute
505    """
506    inputs = _FixPaths(inputs)
507    outputs = _FixPaths(outputs)
508    tool = MSVSProject.Tool(
509        "VCCustomBuildTool",
510        {
511            "Description": description,
512            "AdditionalDependencies": ";".join(inputs),
513            "Outputs": ";".join(outputs),
514            "CommandLine": cmd,
515        },
516    )
517    # Add to the properties of primary input for each config.
518    for config_name, c_data in spec["configurations"].items():
519        p.AddFileConfig(
520            _FixPath(primary_input), _ConfigFullName(config_name, c_data), tools=[tool]
521        )
522
523
524def _AddAccumulatedActionsToMSVS(p, spec, actions_dict):
525    """Add actions accumulated into an actions_dict, merging as needed.
526
527    Arguments:
528      p: the target project
529      spec: the target project dict
530      actions_dict: dictionary keyed on input name, which maps to a list of
531          dicts describing the actions attached to that input file.
532    """
533    for primary_input in actions_dict:
534        inputs = OrderedSet()
535        outputs = OrderedSet()
536        descriptions = []
537        commands = []
538        for action in actions_dict[primary_input]:
539            inputs.update(OrderedSet(action["inputs"]))
540            outputs.update(OrderedSet(action["outputs"]))
541            descriptions.append(action["description"])
542            commands.append(action["command"])
543        # Add the custom build step for one input file.
544        description = ", and also ".join(descriptions)
545        command = "\r\n".join(commands)
546        _AddCustomBuildToolForMSVS(
547            p,
548            spec,
549            primary_input=primary_input,
550            inputs=inputs,
551            outputs=outputs,
552            description=description,
553            cmd=command,
554        )
555
556
557def _RuleExpandPath(path, input_file):
558    """Given the input file to which a rule applied, string substitute a path.
559
560    Arguments:
561      path: a path to string expand
562      input_file: the file to which the rule applied.
563    Returns:
564      The string substituted path.
565    """
566    path = path.replace(
567        "$(InputName)", os.path.splitext(os.path.split(input_file)[1])[0]
568    )
569    path = path.replace("$(InputDir)", os.path.dirname(input_file))
570    path = path.replace(
571        "$(InputExt)", os.path.splitext(os.path.split(input_file)[1])[1]
572    )
573    path = path.replace("$(InputFileName)", os.path.split(input_file)[1])
574    path = path.replace("$(InputPath)", input_file)
575    return path
576
577
578def _FindRuleTriggerFiles(rule, sources):
579    """Find the list of files which a particular rule applies to.
580
581    Arguments:
582      rule: the rule in question
583      sources: the set of all known source files for this project
584    Returns:
585      The list of sources that trigger a particular rule.
586    """
587    return rule.get("rule_sources", [])
588
589
590def _RuleInputsAndOutputs(rule, trigger_file):
591    """Find the inputs and outputs generated by a rule.
592
593    Arguments:
594      rule: the rule in question.
595      trigger_file: the main trigger for this rule.
596    Returns:
597      The pair of (inputs, outputs) involved in this rule.
598    """
599    raw_inputs = _FixPaths(rule.get("inputs", []))
600    raw_outputs = _FixPaths(rule.get("outputs", []))
601    inputs = OrderedSet()
602    outputs = OrderedSet()
603    inputs.add(trigger_file)
604    for i in raw_inputs:
605        inputs.add(_RuleExpandPath(i, trigger_file))
606    for o in raw_outputs:
607        outputs.add(_RuleExpandPath(o, trigger_file))
608    return (inputs, outputs)
609
610
611def _GenerateNativeRulesForMSVS(p, rules, output_dir, spec, options):
612    """Generate a native rules file.
613
614    Arguments:
615      p: the target project
616      rules: the set of rules to include
617      output_dir: the directory in which the project/gyp resides
618      spec: the project dict
619      options: global generator options
620    """
621    rules_filename = "{}{}.rules".format(spec["target_name"], options.suffix)
622    rules_file = MSVSToolFile.Writer(
623        os.path.join(output_dir, rules_filename), spec["target_name"]
624    )
625    # Add each rule.
626    for r in rules:
627        rule_name = r["rule_name"]
628        rule_ext = r["extension"]
629        inputs = _FixPaths(r.get("inputs", []))
630        outputs = _FixPaths(r.get("outputs", []))
631        # Skip a rule with no action and no inputs.
632        if "action" not in r and not r.get("rule_sources", []):
633            continue
634        cmd = _BuildCommandLineForRule(spec, r, has_input_path=True, do_setup_env=True)
635        rules_file.AddCustomBuildRule(
636            name=rule_name,
637            description=r.get("message", rule_name),
638            extensions=[rule_ext],
639            additional_dependencies=inputs,
640            outputs=outputs,
641            cmd=cmd,
642        )
643    # Write out rules file.
644    rules_file.WriteIfChanged()
645
646    # Add rules file to project.
647    p.AddToolFile(rules_filename)
648
649
650def _Cygwinify(path):
651    path = path.replace("$(OutDir)", "$(OutDirCygwin)")
652    path = path.replace("$(IntDir)", "$(IntDirCygwin)")
653    return path
654
655
656def _GenerateExternalRules(rules, output_dir, spec, sources, options, actions_to_add):
657    """Generate an external makefile to do a set of rules.
658
659    Arguments:
660      rules: the list of rules to include
661      output_dir: path containing project and gyp files
662      spec: project specification data
663      sources: set of sources known
664      options: global generator options
665      actions_to_add: The list of actions we will add to.
666    """
667    filename = "{}_rules{}.mk".format(spec["target_name"], options.suffix)
668    mk_file = gyp.common.WriteOnDiff(os.path.join(output_dir, filename))
669    # Find cygwin style versions of some paths.
670    mk_file.write('OutDirCygwin:=$(shell cygpath -u "$(OutDir)")\n')
671    mk_file.write('IntDirCygwin:=$(shell cygpath -u "$(IntDir)")\n')
672    # Gather stuff needed to emit all: target.
673    all_inputs = OrderedSet()
674    all_outputs = OrderedSet()
675    all_output_dirs = OrderedSet()
676    first_outputs = []
677    for rule in rules:
678        trigger_files = _FindRuleTriggerFiles(rule, sources)
679        for tf in trigger_files:
680            inputs, outputs = _RuleInputsAndOutputs(rule, tf)
681            all_inputs.update(OrderedSet(inputs))
682            all_outputs.update(OrderedSet(outputs))
683            # Only use one target from each rule as the dependency for
684            # 'all' so we don't try to build each rule multiple times.
685            first_outputs.append(next(iter(outputs)))
686            # Get the unique output directories for this rule.
687            output_dirs = [os.path.split(i)[0] for i in outputs]
688            for od in output_dirs:
689                all_output_dirs.add(od)
690    first_outputs_cyg = [_Cygwinify(i) for i in first_outputs]
691    # Write out all: target, including mkdir for each output directory.
692    mk_file.write("all: %s\n" % " ".join(first_outputs_cyg))
693    for od in all_output_dirs:
694        if od:
695            mk_file.write('\tmkdir -p `cygpath -u "%s"`\n' % od)
696    mk_file.write("\n")
697    # Define how each output is generated.
698    for rule in rules:
699        trigger_files = _FindRuleTriggerFiles(rule, sources)
700        for tf in trigger_files:
701            # Get all the inputs and outputs for this rule for this trigger file.
702            inputs, outputs = _RuleInputsAndOutputs(rule, tf)
703            inputs = [_Cygwinify(i) for i in inputs]
704            outputs = [_Cygwinify(i) for i in outputs]
705            # Prepare the command line for this rule.
706            cmd = [_RuleExpandPath(c, tf) for c in rule["action"]]
707            cmd = ['"%s"' % i for i in cmd]
708            cmd = " ".join(cmd)
709            # Add it to the makefile.
710            mk_file.write("{}: {}\n".format(" ".join(outputs), " ".join(inputs)))
711            mk_file.write("\t%s\n\n" % cmd)
712    # Close up the file.
713    mk_file.close()
714
715    # Add makefile to list of sources.
716    sources.add(filename)
717    # Add a build action to call makefile.
718    cmd = [
719        "make",
720        "OutDir=$(OutDir)",
721        "IntDir=$(IntDir)",
722        "-j",
723        "${NUMBER_OF_PROCESSORS_PLUS_1}",
724        "-f",
725        filename,
726    ]
727    cmd = _BuildCommandLineForRuleRaw(spec, cmd, True, False, True, True)
728    # Insert makefile as 0'th input, so it gets the action attached there,
729    # as this is easier to understand from in the IDE.
730    all_inputs = list(all_inputs)
731    all_inputs.insert(0, filename)
732    _AddActionStep(
733        actions_to_add,
734        inputs=_FixPaths(all_inputs),
735        outputs=_FixPaths(all_outputs),
736        description="Running external rules for %s" % spec["target_name"],
737        command=cmd,
738    )
739
740
741def _EscapeEnvironmentVariableExpansion(s):
742    """Escapes % characters.
743
744    Escapes any % characters so that Windows-style environment variable
745    expansions will leave them alone.
746    See http://connect.microsoft.com/VisualStudio/feedback/details/106127/cl-d-name-text-containing-percentage-characters-doesnt-compile
747    to understand why we have to do this.
748
749    Args:
750        s: The string to be escaped.
751
752    Returns:
753        The escaped string.
754    """
755    s = s.replace("%", "%%")
756    return s
757
758
759quote_replacer_regex = re.compile(r'(\\*)"')
760
761
762def _EscapeCommandLineArgumentForMSVS(s):
763    """Escapes a Windows command-line argument.
764
765    So that the Win32 CommandLineToArgv function will turn the escaped result back
766    into the original string.
767    See http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
768    ("Parsing C++ Command-Line Arguments") to understand why we have to do
769    this.
770
771    Args:
772        s: the string to be escaped.
773    Returns:
774        the escaped string.
775    """
776
777    def _Replace(match):
778        # For a literal quote, CommandLineToArgv requires an odd number of
779        # backslashes preceding it, and it produces half as many literal backslashes
780        # (rounded down). So we need to produce 2n+1 backslashes.
781        return 2 * match.group(1) + '\\"'
782
783    # Escape all quotes so that they are interpreted literally.
784    s = quote_replacer_regex.sub(_Replace, s)
785    # Now add unescaped quotes so that any whitespace is interpreted literally.
786    s = '"' + s + '"'
787    return s
788
789
790delimiters_replacer_regex = re.compile(r"(\\*)([,;]+)")
791
792
793def _EscapeVCProjCommandLineArgListItem(s):
794    """Escapes command line arguments for MSVS.
795
796    The VCProj format stores string lists in a single string using commas and
797    semi-colons as separators, which must be quoted if they are to be
798    interpreted literally. However, command-line arguments may already have
799    quotes, and the VCProj parser is ignorant of the backslash escaping
800    convention used by CommandLineToArgv, so the command-line quotes and the
801    VCProj quotes may not be the same quotes. So to store a general
802    command-line argument in a VCProj list, we need to parse the existing
803    quoting according to VCProj's convention and quote any delimiters that are
804    not already quoted by that convention. The quotes that we add will also be
805    seen by CommandLineToArgv, so if backslashes precede them then we also have
806    to escape those backslashes according to the CommandLineToArgv
807    convention.
808
809    Args:
810        s: the string to be escaped.
811    Returns:
812        the escaped string.
813    """
814
815    def _Replace(match):
816        # For a non-literal quote, CommandLineToArgv requires an even number of
817        # backslashes preceding it, and it produces half as many literal
818        # backslashes. So we need to produce 2n backslashes.
819        return 2 * match.group(1) + '"' + match.group(2) + '"'
820
821    segments = s.split('"')
822    # The unquoted segments are at the even-numbered indices.
823    for i in range(0, len(segments), 2):
824        segments[i] = delimiters_replacer_regex.sub(_Replace, segments[i])
825    # Concatenate back into a single string
826    s = '"'.join(segments)
827    if len(segments) % 2 == 0:
828        # String ends while still quoted according to VCProj's convention. This
829        # means the delimiter and the next list item that follow this one in the
830        # .vcproj file will be misinterpreted as part of this item. There is nothing
831        # we can do about this. Adding an extra quote would correct the problem in
832        # the VCProj but cause the same problem on the final command-line. Moving
833        # the item to the end of the list does works, but that's only possible if
834        # there's only one such item. Let's just warn the user.
835        print(
836            "Warning: MSVS may misinterpret the odd number of " + "quotes in " + s,
837            file=sys.stderr,
838        )
839    return s
840
841
842def _EscapeCppDefineForMSVS(s):
843    """Escapes a CPP define so that it will reach the compiler unaltered."""
844    s = _EscapeEnvironmentVariableExpansion(s)
845    s = _EscapeCommandLineArgumentForMSVS(s)
846    s = _EscapeVCProjCommandLineArgListItem(s)
847    # cl.exe replaces literal # characters with = in preprocessor definitions for
848    # some reason. Octal-encode to work around that.
849    s = s.replace("#", "\\%03o" % ord("#"))
850    return s
851
852
853quote_replacer_regex2 = re.compile(r'(\\+)"')
854
855
856def _EscapeCommandLineArgumentForMSBuild(s):
857    """Escapes a Windows command-line argument for use by MSBuild."""
858
859    def _Replace(match):
860        return (len(match.group(1)) / 2 * 4) * "\\" + '\\"'
861
862    # Escape all quotes so that they are interpreted literally.
863    s = quote_replacer_regex2.sub(_Replace, s)
864    return s
865
866
867def _EscapeMSBuildSpecialCharacters(s):
868    escape_dictionary = {
869        "%": "%25",
870        "$": "%24",
871        "@": "%40",
872        "'": "%27",
873        ";": "%3B",
874        "?": "%3F",
875        "*": "%2A",
876    }
877    result = "".join([escape_dictionary.get(c, c) for c in s])
878    return result
879
880
881def _EscapeCppDefineForMSBuild(s):
882    """Escapes a CPP define so that it will reach the compiler unaltered."""
883    s = _EscapeEnvironmentVariableExpansion(s)
884    s = _EscapeCommandLineArgumentForMSBuild(s)
885    s = _EscapeMSBuildSpecialCharacters(s)
886    # cl.exe replaces literal # characters with = in preprocessor definitions for
887    # some reason. Octal-encode to work around that.
888    s = s.replace("#", "\\%03o" % ord("#"))
889    return s
890
891
892def _GenerateRulesForMSVS(
893    p, output_dir, options, spec, sources, excluded_sources, actions_to_add
894):
895    """Generate all the rules for a particular project.
896
897    Arguments:
898      p: the project
899      output_dir: directory to emit rules to
900      options: global options passed to the generator
901      spec: the specification for this project
902      sources: the set of all known source files in this project
903      excluded_sources: the set of sources excluded from normal processing
904      actions_to_add: deferred list of actions to add in
905    """
906    rules = spec.get("rules", [])
907    rules_native = [r for r in rules if not int(r.get("msvs_external_rule", 0))]
908    rules_external = [r for r in rules if int(r.get("msvs_external_rule", 0))]
909
910    # Handle rules that use a native rules file.
911    if rules_native:
912        _GenerateNativeRulesForMSVS(p, rules_native, output_dir, spec, options)
913
914    # Handle external rules (non-native rules).
915    if rules_external:
916        _GenerateExternalRules(
917            rules_external, output_dir, spec, sources, options, actions_to_add
918        )
919    _AdjustSourcesForRules(rules, sources, excluded_sources, False)
920
921
922def _AdjustSourcesForRules(rules, sources, excluded_sources, is_msbuild):
923    # Add outputs generated by each rule (if applicable).
924    for rule in rules:
925        # Add in the outputs from this rule.
926        trigger_files = _FindRuleTriggerFiles(rule, sources)
927        for trigger_file in trigger_files:
928            # Remove trigger_file from excluded_sources to let the rule be triggered
929            # (e.g. rule trigger ax_enums.idl is added to excluded_sources
930            # because it's also in an action's inputs in the same project)
931            excluded_sources.discard(_FixPath(trigger_file))
932            # Done if not processing outputs as sources.
933            if int(rule.get("process_outputs_as_sources", False)):
934                inputs, outputs = _RuleInputsAndOutputs(rule, trigger_file)
935                inputs = OrderedSet(_FixPaths(inputs))
936                outputs = OrderedSet(_FixPaths(outputs))
937                inputs.remove(_FixPath(trigger_file))
938                sources.update(inputs)
939                if not is_msbuild:
940                    excluded_sources.update(inputs)
941                sources.update(outputs)
942
943
944def _FilterActionsFromExcluded(excluded_sources, actions_to_add):
945    """Take inputs with actions attached out of the list of exclusions.
946
947    Arguments:
948      excluded_sources: list of source files not to be built.
949      actions_to_add: dict of actions keyed on source file they're attached to.
950    Returns:
951      excluded_sources with files that have actions attached removed.
952    """
953    must_keep = OrderedSet(_FixPaths(actions_to_add.keys()))
954    return [s for s in excluded_sources if s not in must_keep]
955
956
957def _GetDefaultConfiguration(spec):
958    return spec["configurations"][spec["default_configuration"]]
959
960
961def _GetGuidOfProject(proj_path, spec):
962    """Get the guid for the project.
963
964    Arguments:
965      proj_path: Path of the vcproj or vcxproj file to generate.
966      spec: The target dictionary containing the properties of the target.
967    Returns:
968      the guid.
969    Raises:
970      ValueError: if the specified GUID is invalid.
971    """
972    # Pluck out the default configuration.
973    default_config = _GetDefaultConfiguration(spec)
974    # Decide the guid of the project.
975    guid = default_config.get("msvs_guid")
976    if guid:
977        if VALID_MSVS_GUID_CHARS.match(guid) is None:
978            raise ValueError(
979                'Invalid MSVS guid: "%s".  Must match regex: "%s".'
980                % (guid, VALID_MSVS_GUID_CHARS.pattern)
981            )
982        guid = "{%s}" % guid
983    guid = guid or MSVSNew.MakeGuid(proj_path)
984    return guid
985
986
987def _GetMsbuildToolsetOfProject(proj_path, spec, version):
988    """Get the platform toolset for the project.
989
990    Arguments:
991      proj_path: Path of the vcproj or vcxproj file to generate.
992      spec: The target dictionary containing the properties of the target.
993      version: The MSVSVersion object.
994    Returns:
995      the platform toolset string or None.
996    """
997    # Pluck out the default configuration.
998    default_config = _GetDefaultConfiguration(spec)
999    toolset = default_config.get("msbuild_toolset")
1000    if not toolset and version.DefaultToolset():
1001        toolset = version.DefaultToolset()
1002    if spec["type"] == "windows_driver":
1003        toolset = "WindowsKernelModeDriver10.0"
1004    return toolset
1005
1006
1007def _GenerateProject(project, options, version, generator_flags, spec):
1008    """Generates a vcproj file.
1009
1010    Arguments:
1011      project: the MSVSProject object.
1012      options: global generator options.
1013      version: the MSVSVersion object.
1014      generator_flags: dict of generator-specific flags.
1015    Returns:
1016      A list of source files that cannot be found on disk.
1017    """
1018    default_config = _GetDefaultConfiguration(project.spec)
1019
1020    # Skip emitting anything if told to with msvs_existing_vcproj option.
1021    if default_config.get("msvs_existing_vcproj"):
1022        return []
1023
1024    if version.UsesVcxproj():
1025        return _GenerateMSBuildProject(project, options, version, generator_flags, spec)
1026    else:
1027        return _GenerateMSVSProject(project, options, version, generator_flags)
1028
1029
1030def _GenerateMSVSProject(project, options, version, generator_flags):
1031    """Generates a .vcproj file.  It may create .rules and .user files too.
1032
1033    Arguments:
1034      project: The project object we will generate the file for.
1035      options: Global options passed to the generator.
1036      version: The VisualStudioVersion object.
1037      generator_flags: dict of generator-specific flags.
1038    """
1039    spec = project.spec
1040    gyp.common.EnsureDirExists(project.path)
1041
1042    platforms = _GetUniquePlatforms(spec)
1043    p = MSVSProject.Writer(
1044        project.path, version, spec["target_name"], project.guid, platforms
1045    )
1046
1047    # Get directory project file is in.
1048    project_dir = os.path.split(project.path)[0]
1049    gyp_path = _NormalizedSource(project.build_file)
1050    relative_path_of_gyp_file = gyp.common.RelativePath(gyp_path, project_dir)
1051
1052    config_type = _GetMSVSConfigurationType(spec, project.build_file)
1053    for config_name, config in spec["configurations"].items():
1054        _AddConfigurationToMSVSProject(p, spec, config_type, config_name, config)
1055
1056    # Prepare list of sources and excluded sources.
1057    gyp_file = os.path.split(project.build_file)[1]
1058    sources, excluded_sources = _PrepareListOfSources(spec, generator_flags, gyp_file)
1059
1060    # Add rules.
1061    actions_to_add = {}
1062    _GenerateRulesForMSVS(
1063        p, project_dir, options, spec, sources, excluded_sources, actions_to_add
1064    )
1065    list_excluded = generator_flags.get("msvs_list_excluded_files", True)
1066    sources, excluded_sources, excluded_idl = _AdjustSourcesAndConvertToFilterHierarchy(
1067        spec, options, project_dir, sources, excluded_sources, list_excluded, version
1068    )
1069
1070    # Add in files.
1071    missing_sources = _VerifySourcesExist(sources, project_dir)
1072    p.AddFiles(sources)
1073
1074    _AddToolFilesToMSVS(p, spec)
1075    _HandlePreCompiledHeaders(p, sources, spec)
1076    _AddActions(actions_to_add, spec, relative_path_of_gyp_file)
1077    _AddCopies(actions_to_add, spec)
1078    _WriteMSVSUserFile(project.path, version, spec)
1079
1080    # NOTE: this stanza must appear after all actions have been decided.
1081    # Don't excluded sources with actions attached, or they won't run.
1082    excluded_sources = _FilterActionsFromExcluded(excluded_sources, actions_to_add)
1083    _ExcludeFilesFromBeingBuilt(p, spec, excluded_sources, excluded_idl, list_excluded)
1084    _AddAccumulatedActionsToMSVS(p, spec, actions_to_add)
1085
1086    # Write it out.
1087    p.WriteIfChanged()
1088
1089    return missing_sources
1090
1091
1092def _GetUniquePlatforms(spec):
1093    """Returns the list of unique platforms for this spec, e.g ['win32', ...].
1094
1095    Arguments:
1096      spec: The target dictionary containing the properties of the target.
1097    Returns:
1098      The MSVSUserFile object created.
1099    """
1100    # Gather list of unique platforms.
1101    platforms = OrderedSet()
1102    for configuration in spec["configurations"]:
1103        platforms.add(_ConfigPlatform(spec["configurations"][configuration]))
1104    platforms = list(platforms)
1105    return platforms
1106
1107
1108def _CreateMSVSUserFile(proj_path, version, spec):
1109    """Generates a .user file for the user running this Gyp program.
1110
1111    Arguments:
1112      proj_path: The path of the project file being created.  The .user file
1113                 shares the same path (with an appropriate suffix).
1114      version: The VisualStudioVersion object.
1115      spec: The target dictionary containing the properties of the target.
1116    Returns:
1117      The MSVSUserFile object created.
1118    """
1119    (domain, username) = _GetDomainAndUserName()
1120    vcuser_filename = ".".join([proj_path, domain, username, "user"])
1121    user_file = MSVSUserFile.Writer(vcuser_filename, version, spec["target_name"])
1122    return user_file
1123
1124
1125def _GetMSVSConfigurationType(spec, build_file):
1126    """Returns the configuration type for this project.
1127
1128    It's a number defined by Microsoft.  May raise an exception.
1129
1130    Args:
1131        spec: The target dictionary containing the properties of the target.
1132        build_file: The path of the gyp file.
1133    Returns:
1134        An integer, the configuration type.
1135    """
1136    try:
1137        config_type = {
1138            "executable": "1",  # .exe
1139            "shared_library": "2",  # .dll
1140            "loadable_module": "2",  # .dll
1141            "static_library": "4",  # .lib
1142            "windows_driver": "5",  # .sys
1143            "none": "10",  # Utility type
1144        }[spec["type"]]
1145    except KeyError:
1146        if spec.get("type"):
1147            raise GypError(
1148                "Target type %s is not a valid target type for "
1149                "target %s in %s." % (spec["type"], spec["target_name"], build_file)
1150            )
1151        else:
1152            raise GypError(
1153                "Missing type field for target %s in %s."
1154                % (spec["target_name"], build_file)
1155            )
1156    return config_type
1157
1158
1159def _AddConfigurationToMSVSProject(p, spec, config_type, config_name, config):
1160    """Adds a configuration to the MSVS project.
1161
1162    Many settings in a vcproj file are specific to a configuration.  This
1163    function the main part of the vcproj file that's configuration specific.
1164
1165    Arguments:
1166      p: The target project being generated.
1167      spec: The target dictionary containing the properties of the target.
1168      config_type: The configuration type, a number as defined by Microsoft.
1169      config_name: The name of the configuration.
1170      config: The dictionary that defines the special processing to be done
1171              for this configuration.
1172    """
1173    # Get the information for this configuration
1174    include_dirs, midl_include_dirs, resource_include_dirs = _GetIncludeDirs(config)
1175    libraries = _GetLibraries(spec)
1176    library_dirs = _GetLibraryDirs(config)
1177    out_file, vc_tool, _ = _GetOutputFilePathAndTool(spec, msbuild=False)
1178    defines = _GetDefines(config)
1179    defines = [_EscapeCppDefineForMSVS(d) for d in defines]
1180    disabled_warnings = _GetDisabledWarnings(config)
1181    prebuild = config.get("msvs_prebuild")
1182    postbuild = config.get("msvs_postbuild")
1183    def_file = _GetModuleDefinition(spec)
1184    precompiled_header = config.get("msvs_precompiled_header")
1185
1186    # Prepare the list of tools as a dictionary.
1187    tools = {}
1188    # Add in user specified msvs_settings.
1189    msvs_settings = config.get("msvs_settings", {})
1190    MSVSSettings.ValidateMSVSSettings(msvs_settings)
1191
1192    # Prevent default library inheritance from the environment.
1193    _ToolAppend(tools, "VCLinkerTool", "AdditionalDependencies", ["$(NOINHERIT)"])
1194
1195    for tool in msvs_settings:
1196        settings = config["msvs_settings"][tool]
1197        for setting in settings:
1198            _ToolAppend(tools, tool, setting, settings[setting])
1199    # Add the information to the appropriate tool
1200    _ToolAppend(tools, "VCCLCompilerTool", "AdditionalIncludeDirectories", include_dirs)

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

codekingpro/portable-devtools · Team Ai