summaryrefslogtreecommitdiff
path: root/pylib/gyp/MSVSProject.py
blob: 490695581deee11bf3c06c0887ef514fc60e0cd6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#!/usr/bin/python2.4

# Copyright (c) 2009 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Visual Studio project reader/writer."""

import common
import xml.dom
import xml.dom.minidom
import MSVSNew

#------------------------------------------------------------------------------


class Tool(object):
  """Visual Studio tool."""

  def __init__(self, name, attrs=None):
    """Initializes the tool.

    Args:
      name: Tool name.
      attrs: Dict of tool attributes; may be None.
    """
    self.name = name
    self.attrs = attrs or {}

  def CreateElement(self, doc):
    """Creates an element for the tool.

    Args:
      doc: xml.dom.Document object to use for node creation.

    Returns:
      A new xml.dom.Element for the tool.
    """
    node = doc.createElement('Tool')
    node.setAttribute('Name', self.name)
    for k, v in self.attrs.items():
      node.setAttribute(k, v)
    return node


class Filter(object):
  """Visual Studio filter - that is, a virtual folder."""

  def __init__(self, name, contents=None):
    """Initializes the folder.

    Args:
      name: Filter (folder) name.
      contents: List of filenames and/or Filter objects contained.
    """
    self.name = name
    self.contents = list(contents or [])


#------------------------------------------------------------------------------


class Writer(object):
  """Visual Studio XML project writer."""

  def __init__(self, project_path, version):
    """Initializes the project.

    Args:
      project_path: Path to the project file.
      version: Format version to emit.
    """
    self.project_path = project_path
    self.doc = None
    self.version = version

  def Create(self, name, guid=None):
    """Creates the project document.

    Args:
      name: Name of the project.
      guid: GUID to use for project, if not None.
    """
    self.name = name
    self.guid = guid or MSVSNew.MakeGuid(self.project_path)

    # Create XML doc
    xml_impl = xml.dom.getDOMImplementation()
    self.doc = xml_impl.createDocument(None, 'VisualStudioProject', None)

    # Add attributes to root element
    self.n_root = self.doc.documentElement
    self.n_root.setAttribute('ProjectType', 'Visual C++')
    self.n_root.setAttribute('Version', self.version.ProjectVersion())
    self.n_root.setAttribute('Name', self.name)
    self.n_root.setAttribute('ProjectGUID', self.guid)
    self.n_root.setAttribute('RootNamespace', self.name)
    self.n_root.setAttribute('Keyword', 'Win32Proj')

    # Add platform list
    n_platform = self.doc.createElement('Platforms')
    self.n_root.appendChild(n_platform)
    n = self.doc.createElement('Platform')
    n.setAttribute('Name', 'Win32')
    n_platform.appendChild(n)

    # Add tool files section
    self.n_tool_files = self.doc.createElement('ToolFiles')
    self.n_root.appendChild(self.n_tool_files)

    # Add configurations section
    self.n_configs = self.doc.createElement('Configurations')
    self.n_root.appendChild(self.n_configs)

    # Add empty References section
    self.n_root.appendChild(self.doc.createElement('References'))

    # Add files section
    self.n_files = self.doc.createElement('Files')
    self.n_root.appendChild(self.n_files)
    # Keep a dict keyed on filename to speed up access.
    self.n_files_dict = dict()

    # Add empty Globals section
    self.n_root.appendChild(self.doc.createElement('Globals'))

  def AddToolFile(self, path):
    """Adds a tool file to the project.

    Args:
      path: Relative path from project to tool file.
    """
    n_tool = self.doc.createElement('ToolFile')
    n_tool.setAttribute('RelativePath', path)
    self.n_tool_files.appendChild(n_tool)

  def _AddConfigToNode(self, parent, config_type, config_name, attrs=None,
                       tools=None):
    """Adds a configuration to the parent node.

    Args:
      parent: Destination node.
      config_type: Type of configuration node.
      config_name: Configuration name.
      attrs: Dict of configuration attributes; may be None.
      tools: List of tools (strings or Tool objects); may be None.
    """
    # Handle defaults
    if not attrs:
      attrs = {}
    if not tools:
      tools = []

    # Add configuration node and its attributes
    n_config = self.doc.createElement(config_type)
    n_config.setAttribute('Name', config_name)
    for k, v in attrs.items():
      n_config.setAttribute(k, v)
    parent.appendChild(n_config)

    # Add tool nodes and their attributes
    if tools:
      for t in tools:
        if isinstance(t, Tool):
          n_config.appendChild(t.CreateElement(self.doc))
        else:
          n_config.appendChild(Tool(t).CreateElement(self.doc))

  def AddConfig(self, name, attrs=None, tools=None):
    """Adds a configuration to the project.

    Args:
      name: Configuration name.
      attrs: Dict of configuration attributes; may be None.
      tools: List of tools (strings or Tool objects); may be None.
    """
    self._AddConfigToNode(self.n_configs, 'Configuration', name, attrs, tools)

  def _AddFilesToNode(self, parent, files):
    """Adds files and/or filters to the parent node.

    Args:
      parent: Destination node
      files: A list of Filter objects and/or relative paths to files.

    Will call itself recursively, if the files list contains Filter objects.
    """
    for f in files:
      if isinstance(f, Filter):
        node = self.doc.createElement('Filter')
        node.setAttribute('Name', f.name)
        self._AddFilesToNode(node, f.contents)
      else:
        node = self.doc.createElement('File')
        node.setAttribute('RelativePath', f)
        self.n_files_dict[f] = node
      parent.appendChild(node)

  def AddFiles(self, files):
    """Adds files to the project.

    Args:
      files: A list of Filter objects and/or relative paths to files.

    This makes a copy of the file/filter tree at the time of this call.  If you
    later add files to a Filter object which was passed into a previous call
    to AddFiles(), it will not be reflected in this project.
    """
    self._AddFilesToNode(self.n_files, files)
    # TODO(rspangler) This also doesn't handle adding files to an existing
    # filter.  That is, it doesn't merge the trees.

  def AddFileConfig(self, path, config, attrs=None, tools=None):
    """Adds a configuration to a file.

    Args:
      path: Relative path to the file.
      config: Name of configuration to add.
      attrs: Dict of configuration attributes; may be None.
      tools: List of tools (strings or Tool objects); may be None.

    Raises:
      ValueError: Relative path does not match any file added via AddFiles().
    """
    # Find the file node with the right relative path
    parent = self.n_files_dict.get(path)
    if not parent:
      raise ValueError('AddFileConfig: file "%s" not in project.' % path)

    # Add the config to the file node
    self._AddConfigToNode(parent, 'FileConfiguration', config, attrs, tools)

  def Write(self, writer=common.WriteOnDiff):
    """Writes the project file."""
    f = writer(self.project_path)
    self.doc.writexml(f, encoding='Windows-1252', addindent='  ', newl='\r\n')
    f.close()

#------------------------------------------------------------------------------