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