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."""
13 #------------------------------------------------------------------------------
17 """Visual Studio tool."""
19 def __init__(self, name, attrs=None):
20 """Initializes the tool.
24 attrs: Dict of tool attributes; may be None.
27 self.attrs = attrs or {}
29 def CreateElement(self, doc):
30 """Creates an element for the tool.
33 doc: xml.dom.Document object to use for node creation.
36 A new xml.dom.Element for the tool.
38 node = doc.createElement('Tool')
39 node.setAttribute('Name', self.name)
40 for k, v in self.attrs.items():
41 node.setAttribute(k, v)
46 """Visual Studio filter - that is, a virtual folder."""
48 def __init__(self, name, contents=None):
49 """Initializes the folder.
52 name: Filter (folder) name.
53 contents: List of filenames and/or Filter objects contained.
56 self.contents = list(contents or [])
59 #------------------------------------------------------------------------------
63 """Visual Studio XML project writer."""
65 def __init__(self, project_path, version):
66 """Initializes the project.
69 project_path: Path to the project file.
70 version: Format version to emit.
72 self.project_path = project_path
74 self.version = version
76 def Create(self, name, guid=None, platforms=None):
77 """Creates the project document.
80 name: Name of the project.
81 guid: GUID to use for project, if not None.
86 # Default to Win32 for platforms.
91 xml_impl = xml.dom.getDOMImplementation()
92 self.doc = xml_impl.createDocument(None, 'VisualStudioProject', None)
94 # Add attributes to root element
95 self.n_root = self.doc.documentElement
96 self.n_root.setAttribute('ProjectType', 'Visual C++')
97 self.n_root.setAttribute('Version', self.version.ProjectVersion())
98 self.n_root.setAttribute('Name', self.name)
99 self.n_root.setAttribute('ProjectGUID', self.guid)
100 self.n_root.setAttribute('RootNamespace', self.name)
101 self.n_root.setAttribute('Keyword', 'Win32Proj')
104 n_platform = self.doc.createElement('Platforms')
105 self.n_root.appendChild(n_platform)
106 for platform in platforms:
107 n = self.doc.createElement('Platform')
108 n.setAttribute('Name', platform)
109 n_platform.appendChild(n)
111 # Add tool files section
112 self.n_tool_files = self.doc.createElement('ToolFiles')
113 self.n_root.appendChild(self.n_tool_files)
115 # Add configurations section
116 self.n_configs = self.doc.createElement('Configurations')
117 self.n_root.appendChild(self.n_configs)
119 # Add empty References section
120 self.n_root.appendChild(self.doc.createElement('References'))
123 self.n_files = self.doc.createElement('Files')
124 self.n_root.appendChild(self.n_files)
125 # Keep a dict keyed on filename to speed up access.
126 self.n_files_dict = dict()
128 # Add empty Globals section
129 self.n_root.appendChild(self.doc.createElement('Globals'))
131 def AddToolFile(self, path):
132 """Adds a tool file to the project.
135 path: Relative path from project to tool file.
137 n_tool = self.doc.createElement('ToolFile')
138 n_tool.setAttribute('RelativePath', path)
139 self.n_tool_files.appendChild(n_tool)
141 def _AddConfigToNode(self, parent, config_type, config_name, attrs=None,
143 """Adds a configuration to the parent node.
146 parent: Destination node.
147 config_type: Type of configuration node.
148 config_name: Configuration name.
149 attrs: Dict of configuration attributes; may be None.
150 tools: List of tools (strings or Tool objects); may be None.
158 # Add configuration node and its attributes
159 n_config = self.doc.createElement(config_type)
160 n_config.setAttribute('Name', config_name)
161 for k, v in attrs.items():
162 n_config.setAttribute(k, v)
163 parent.appendChild(n_config)
165 # Add tool nodes and their attributes
168 if isinstance(t, Tool):
169 n_config.appendChild(t.CreateElement(self.doc))
171 n_config.appendChild(Tool(t).CreateElement(self.doc))
173 def AddConfig(self, name, attrs=None, tools=None):
174 """Adds a configuration to the project.
177 name: Configuration name.
178 attrs: Dict of configuration attributes; may be None.
179 tools: List of tools (strings or Tool objects); may be None.
181 self._AddConfigToNode(self.n_configs, 'Configuration', name, attrs, tools)
183 def _AddFilesToNode(self, parent, files):
184 """Adds files and/or filters to the parent node.
187 parent: Destination node
188 files: A list of Filter objects and/or relative paths to files.
190 Will call itself recursively, if the files list contains Filter objects.
193 if isinstance(f, Filter):
194 node = self.doc.createElement('Filter')
195 node.setAttribute('Name', f.name)
196 self._AddFilesToNode(node, f.contents)
198 node = self.doc.createElement('File')
199 node.setAttribute('RelativePath', f)
200 self.n_files_dict[f] = node
201 parent.appendChild(node)
203 def AddFiles(self, files):
204 """Adds files to the project.
207 files: A list of Filter objects and/or relative paths to files.
209 This makes a copy of the file/filter tree at the time of this call. If you
210 later add files to a Filter object which was passed into a previous call
211 to AddFiles(), it will not be reflected in this project.
213 self._AddFilesToNode(self.n_files, files)
214 # TODO(rspangler) This also doesn't handle adding files to an existing
215 # filter. That is, it doesn't merge the trees.
217 def AddFileConfig(self, path, config, attrs=None, tools=None):
218 """Adds a configuration to a file.
221 path: Relative path to the file.
222 config: Name of configuration to add.
223 attrs: Dict of configuration attributes; may be None.
224 tools: List of tools (strings or Tool objects); may be None.
227 ValueError: Relative path does not match any file added via AddFiles().
229 # Find the file node with the right relative path
230 parent = self.n_files_dict.get(path)
232 raise ValueError('AddFileConfig: file "%s" not in project.' % path)
234 # Add the config to the file node
235 self._AddConfigToNode(parent, 'FileConfiguration', config, attrs, tools)
237 def Write(self, writer=common.WriteOnDiff):
238 """Writes the project file."""
239 f = writer(self.project_path)
240 fix = xml_fix.XmlFix()
241 self.doc.writexml(f, encoding='Windows-1252', addindent=' ', newl='\r\n')
245 #------------------------------------------------------------------------------