• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"""Visual Studio user preferences file writer."""
6
7import os
8import re
9import socket  # for gethostname
10
11import gyp.easy_xml as easy_xml
12
13
14# ------------------------------------------------------------------------------
15
16
17def _FindCommandInPath(command):
18    """If there are no slashes in the command given, this function
19     searches the PATH env to find the given command, and converts it
20     to an absolute path.  We have to do this because MSVS is looking
21     for an actual file to launch a debugger on, not just a command
22     line.  Note that this happens at GYP time, so anything needing to
23     be built needs to have a full path."""
24    if "/" in command or "\\" in command:
25        # If the command already has path elements (either relative or
26        # absolute), then assume it is constructed properly.
27        return command
28    else:
29        # Search through the path list and find an existing file that
30        # we can access.
31        paths = os.environ.get("PATH", "").split(os.pathsep)
32        for path in paths:
33            item = os.path.join(path, command)
34            if os.path.isfile(item) and os.access(item, os.X_OK):
35                return item
36    return command
37
38
39def _QuoteWin32CommandLineArgs(args):
40    new_args = []
41    for arg in args:
42        # Replace all double-quotes with double-double-quotes to escape
43        # them for cmd shell, and then quote the whole thing if there
44        # are any.
45        if arg.find('"') != -1:
46            arg = '""'.join(arg.split('"'))
47            arg = '"%s"' % arg
48
49        # Otherwise, if there are any spaces, quote the whole arg.
50        elif re.search(r"[ \t\n]", arg):
51            arg = '"%s"' % arg
52        new_args.append(arg)
53    return new_args
54
55
56class Writer:
57    """Visual Studio XML user user file writer."""
58
59    def __init__(self, user_file_path, version, name):
60        """Initializes the user file.
61
62    Args:
63      user_file_path: Path to the user file.
64      version: Version info.
65      name: Name of the user file.
66    """
67        self.user_file_path = user_file_path
68        self.version = version
69        self.name = name
70        self.configurations = {}
71
72    def AddConfig(self, name):
73        """Adds a configuration to the project.
74
75    Args:
76      name: Configuration name.
77    """
78        self.configurations[name] = ["Configuration", {"Name": name}]
79
80    def AddDebugSettings(
81        self, config_name, command, environment={}, working_directory=""
82    ):
83        """Adds a DebugSettings node to the user file for a particular config.
84
85    Args:
86      command: command line to run.  First element in the list is the
87        executable.  All elements of the command will be quoted if
88        necessary.
89      working_directory: other files which may trigger the rule. (optional)
90    """
91        command = _QuoteWin32CommandLineArgs(command)
92
93        abs_command = _FindCommandInPath(command[0])
94
95        if environment and isinstance(environment, dict):
96            env_list = [f'{key}="{val}"' for (key, val) in environment.items()]
97            environment = " ".join(env_list)
98        else:
99            environment = ""
100
101        n_cmd = [
102            "DebugSettings",
103            {
104                "Command": abs_command,
105                "WorkingDirectory": working_directory,
106                "CommandArguments": " ".join(command[1:]),
107                "RemoteMachine": socket.gethostname(),
108                "Environment": environment,
109                "EnvironmentMerge": "true",
110                # Currently these are all "dummy" values that we're just setting
111                # in the default manner that MSVS does it.  We could use some of
112                # these to add additional capabilities, I suppose, but they might
113                # not have parity with other platforms then.
114                "Attach": "false",
115                "DebuggerType": "3",  # 'auto' debugger
116                "Remote": "1",
117                "RemoteCommand": "",
118                "HttpUrl": "",
119                "PDBPath": "",
120                "SQLDebugging": "",
121                "DebuggerFlavor": "0",
122                "MPIRunCommand": "",
123                "MPIRunArguments": "",
124                "MPIRunWorkingDirectory": "",
125                "ApplicationCommand": "",
126                "ApplicationArguments": "",
127                "ShimCommand": "",
128                "MPIAcceptMode": "",
129                "MPIAcceptFilter": "",
130            },
131        ]
132
133        # Find the config, and add it if it doesn't exist.
134        if config_name not in self.configurations:
135            self.AddConfig(config_name)
136
137        # Add the DebugSettings onto the appropriate config.
138        self.configurations[config_name].append(n_cmd)
139
140    def WriteIfChanged(self):
141        """Writes the user file."""
142        configs = ["Configurations"]
143        for config, spec in sorted(self.configurations.items()):
144            configs.append(spec)
145
146        content = [
147            "VisualStudioUserFile",
148            {"Version": self.version.ProjectVersion(), "Name": self.name},
149            configs,
150        ]
151        easy_xml.WriteXmlIfChanged(
152            content, self.user_file_path, encoding="Windows-1252"
153        )
154