3
# Copyright (c) 2009 Google Inc. All rights reserved.
4
# Use of this source code is governed by a BSD-style license that can be
5
# found in the LICENSE file.
7
"""Visual Studio project reader/writer."""
11
import xml.dom.minidom
14
#------------------------------------------------------------------------------
18
"""Visual Studio tool."""
20
def __init__(self, name, attrs=None):
21
"""Initializes the tool.
25
attrs: Dict of tool attributes; may be None.
28
self.attrs = attrs or {}
30
def CreateElement(self, doc):
31
"""Creates an element for the tool.
34
doc: xml.dom.Document object to use for node creation.
37
A new xml.dom.Element for the tool.
39
node = doc.createElement('Tool')
40
node.setAttribute('Name', self.name)
41
for k, v in self.attrs.items():
42
node.setAttribute(k, v)
47
"""Visual Studio filter - that is, a virtual folder."""
49
def __init__(self, name, contents=None):
50
"""Initializes the folder.
53
name: Filter (folder) name.
54
contents: List of filenames and/or Filter objects contained.
57
self.contents = list(contents or [])
60
#------------------------------------------------------------------------------
64
"""Visual Studio XML project writer."""
66
def __init__(self, project_path, version):
67
"""Initializes the project.
70
project_path: Path to the project file.
71
version: Format version to emit.
73
self.project_path = project_path
75
self.version = version
77
def Create(self, name, guid=None, platforms=None):
78
"""Creates the project document.
81
name: Name of the project.
82
guid: GUID to use for project, if not None.
85
self.guid = guid or MSVSNew.MakeGuid(self.project_path)
87
# Default to Win32 for platforms.
92
xml_impl = xml.dom.getDOMImplementation()
93
self.doc = xml_impl.createDocument(None, 'VisualStudioProject', None)
95
# Add attributes to root element
96
self.n_root = self.doc.documentElement
97
self.n_root.setAttribute('ProjectType', 'Visual C++')
98
self.n_root.setAttribute('Version', self.version.ProjectVersion())
99
self.n_root.setAttribute('Name', self.name)
100
self.n_root.setAttribute('ProjectGUID', self.guid)
101
self.n_root.setAttribute('RootNamespace', self.name)
102
self.n_root.setAttribute('Keyword', 'Win32Proj')
105
n_platform = self.doc.createElement('Platforms')
106
self.n_root.appendChild(n_platform)
107
for platform in platforms:
108
n = self.doc.createElement('Platform')
109
n.setAttribute('Name', platform)
110
n_platform.appendChild(n)
112
# Add tool files section
113
self.n_tool_files = self.doc.createElement('ToolFiles')
114
self.n_root.appendChild(self.n_tool_files)
116
# Add configurations section
117
self.n_configs = self.doc.createElement('Configurations')
118
self.n_root.appendChild(self.n_configs)
120
# Add empty References section
121
self.n_root.appendChild(self.doc.createElement('References'))
124
self.n_files = self.doc.createElement('Files')
125
self.n_root.appendChild(self.n_files)
126
# Keep a dict keyed on filename to speed up access.
127
self.n_files_dict = dict()
129
# Add empty Globals section
130
self.n_root.appendChild(self.doc.createElement('Globals'))
132
def AddToolFile(self, path):
133
"""Adds a tool file to the project.
136
path: Relative path from project to tool file.
138
n_tool = self.doc.createElement('ToolFile')
139
n_tool.setAttribute('RelativePath', path)
140
self.n_tool_files.appendChild(n_tool)
142
def _AddConfigToNode(self, parent, config_type, config_name, attrs=None,
144
"""Adds a configuration to the parent node.
147
parent: Destination node.
148
config_type: Type of configuration node.
149
config_name: Configuration name.
150
attrs: Dict of configuration attributes; may be None.
151
tools: List of tools (strings or Tool objects); may be None.
159
# Add configuration node and its attributes
160
n_config = self.doc.createElement(config_type)
161
n_config.setAttribute('Name', config_name)
162
for k, v in attrs.items():
163
n_config.setAttribute(k, v)
164
parent.appendChild(n_config)
166
# Add tool nodes and their attributes
169
if isinstance(t, Tool):
170
n_config.appendChild(t.CreateElement(self.doc))
172
n_config.appendChild(Tool(t).CreateElement(self.doc))
174
def AddConfig(self, name, attrs=None, tools=None):
175
"""Adds a configuration to the project.
178
name: Configuration name.
179
attrs: Dict of configuration attributes; may be None.
180
tools: List of tools (strings or Tool objects); may be None.
182
self._AddConfigToNode(self.n_configs, 'Configuration', name, attrs, tools)
184
def _AddFilesToNode(self, parent, files):
185
"""Adds files and/or filters to the parent node.
188
parent: Destination node
189
files: A list of Filter objects and/or relative paths to files.
191
Will call itself recursively, if the files list contains Filter objects.
194
if isinstance(f, Filter):
195
node = self.doc.createElement('Filter')
196
node.setAttribute('Name', f.name)
197
self._AddFilesToNode(node, f.contents)
199
node = self.doc.createElement('File')
200
node.setAttribute('RelativePath', f)
201
self.n_files_dict[f] = node
202
parent.appendChild(node)
204
def AddFiles(self, files):
205
"""Adds files to the project.
208
files: A list of Filter objects and/or relative paths to files.
210
This makes a copy of the file/filter tree at the time of this call. If you
211
later add files to a Filter object which was passed into a previous call
212
to AddFiles(), it will not be reflected in this project.
214
self._AddFilesToNode(self.n_files, files)
215
# TODO(rspangler) This also doesn't handle adding files to an existing
216
# filter. That is, it doesn't merge the trees.
218
def AddFileConfig(self, path, config, attrs=None, tools=None):
219
"""Adds a configuration to a file.
222
path: Relative path to the file.
223
config: Name of configuration to add.
224
attrs: Dict of configuration attributes; may be None.
225
tools: List of tools (strings or Tool objects); may be None.
228
ValueError: Relative path does not match any file added via AddFiles().
230
# Find the file node with the right relative path
231
parent = self.n_files_dict.get(path)
233
raise ValueError('AddFileConfig: file "%s" not in project.' % path)
235
# Add the config to the file node
236
self._AddConfigToNode(parent, 'FileConfiguration', config, attrs, tools)
238
def Write(self, writer=common.WriteOnDiff):
239
"""Writes the project file."""
240
f = writer(self.project_path)
241
self.doc.writexml(f, encoding='Windows-1252', addindent=' ', newl='\r\n')
244
#------------------------------------------------------------------------------