2faf1ed15dbcbb213c352dac4db1dcc32152fff58af76f392c78018cb174f789626f4bc3bd21a8f3bec5b720da54f6df28989c138396c7b38525ba02c089e8 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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. """Visual Studio project reader/writer."""
  5. import gyp.easy_xml as easy_xml
  6. # ------------------------------------------------------------------------------
  7. class Tool:
  8. """Visual Studio tool."""
  9. def __init__(self, name, attrs=None):
  10. """Initializes the tool.
  11. Args:
  12. name: Tool name.
  13. attrs: Dict of tool attributes; may be None.
  14. """
  15. self._attrs = attrs or {}
  16. self._attrs["Name"] = name
  17. def _GetSpecification(self):
  18. """Creates an element for the tool.
  19. Returns:
  20. A new xml.dom.Element for the tool.
  21. """
  22. return ["Tool", self._attrs]
  23. class Filter:
  24. """Visual Studio filter - that is, a virtual folder."""
  25. def __init__(self, name, contents=None):
  26. """Initializes the folder.
  27. Args:
  28. name: Filter (folder) name.
  29. contents: List of filenames and/or Filter objects contained.
  30. """
  31. self.name = name
  32. self.contents = list(contents or [])
  33. # ------------------------------------------------------------------------------
  34. class Writer:
  35. """Visual Studio XML project writer."""
  36. def __init__(self, project_path, version, name, guid=None, platforms=None):
  37. """Initializes the project.
  38. Args:
  39. project_path: Path to the project file.
  40. version: Format version to emit.
  41. name: Name of the project.
  42. guid: GUID to use for project, if not None.
  43. platforms: Array of string, the supported platforms. If null, ['Win32']
  44. """
  45. self.project_path = project_path
  46. self.version = version
  47. self.name = name
  48. self.guid = guid
  49. # Default to Win32 for platforms.
  50. if not platforms:
  51. platforms = ["Win32"]
  52. # Initialize the specifications of the various sections.
  53. self.platform_section = ["Platforms"]
  54. for platform in platforms:
  55. self.platform_section.append(["Platform", {"Name": platform}])
  56. self.tool_files_section = ["ToolFiles"]
  57. self.configurations_section = ["Configurations"]
  58. self.files_section = ["Files"]
  59. # Keep a dict keyed on filename to speed up access.
  60. self.files_dict = {}
  61. def AddToolFile(self, path):
  62. """Adds a tool file to the project.
  63. Args:
  64. path: Relative path from project to tool file.
  65. """
  66. self.tool_files_section.append(["ToolFile", {"RelativePath": path}])
  67. def _GetSpecForConfiguration(self, config_type, config_name, attrs, tools):
  68. """Returns the specification for a configuration.
  69. Args:
  70. config_type: Type of configuration node.
  71. config_name: Configuration name.
  72. attrs: Dict of configuration attributes; may be None.
  73. tools: List of tools (strings or Tool objects); may be None.
  74. Returns:
  75. """
  76. # Handle defaults
  77. if not attrs:
  78. attrs = {}
  79. if not tools:
  80. tools = []
  81. # Add configuration node and its attributes
  82. node_attrs = attrs.copy()
  83. node_attrs["Name"] = config_name
  84. specification = [config_type, node_attrs]
  85. # Add tool nodes and their attributes
  86. if tools:
  87. for t in tools:
  88. if isinstance(t, Tool):
  89. specification.append(t._GetSpecification())
  90. else:
  91. specification.append(Tool(t)._GetSpecification())
  92. return specification
  93. def AddConfig(self, name, attrs=None, tools=None):
  94. """Adds a configuration to the project.
  95. Args:
  96. name: Configuration name.
  97. attrs: Dict of configuration attributes; may be None.
  98. tools: List of tools (strings or Tool objects); may be None.
  99. """
  100. spec = self._GetSpecForConfiguration("Configuration", name, attrs, tools)
  101. self.configurations_section.append(spec)
  102. def _AddFilesToNode(self, parent, files):
  103. """Adds files and/or filters to the parent node.
  104. Args:
  105. parent: Destination node
  106. files: A list of Filter objects and/or relative paths to files.
  107. Will call itself recursively, if the files list contains Filter objects.
  108. """
  109. for f in files:
  110. if isinstance(f, Filter):
  111. node = ["Filter", {"Name": f.name}]
  112. self._AddFilesToNode(node, f.contents)
  113. else:
  114. node = ["File", {"RelativePath": f}]
  115. self.files_dict[f] = node
  116. parent.append(node)
  117. def AddFiles(self, files):
  118. """Adds files to the project.
  119. Args:
  120. files: A list of Filter objects and/or relative paths to files.
  121. This makes a copy of the file/filter tree at the time of this call. If you
  122. later add files to a Filter object which was passed into a previous call
  123. to AddFiles(), it will not be reflected in this project.
  124. """
  125. self._AddFilesToNode(self.files_section, files)
  126. # TODO(rspangler) This also doesn't handle adding files to an existing
  127. # filter. That is, it doesn't merge the trees.
  128. def AddFileConfig(self, path, config, attrs=None, tools=None):
  129. """Adds a configuration to a file.
  130. Args:
  131. path: Relative path to the file.
  132. config: Name of configuration to add.
  133. attrs: Dict of configuration attributes; may be None.
  134. tools: List of tools (strings or Tool objects); may be None.
  135. Raises:
  136. ValueError: Relative path does not match any file added via AddFiles().
  137. """
  138. # Find the file node with the right relative path
  139. parent = self.files_dict.get(path)
  140. if not parent:
  141. raise ValueError('AddFileConfig: file "%s" not in project.' % path)
  142. # Add the config to the file node
  143. spec = self._GetSpecForConfiguration("FileConfiguration", config, attrs, tools)
  144. parent.append(spec)
  145. def WriteIfChanged(self):
  146. """Writes the project file."""
  147. # First create XML content definition
  148. content = [
  149. "VisualStudioProject",
  150. {
  151. "ProjectType": "Visual C++",
  152. "Version": self.version.ProjectVersion(),
  153. "Name": self.name,
  154. "ProjectGUID": self.guid,
  155. "RootNamespace": self.name,
  156. "Keyword": "Win32Proj",
  157. },
  158. self.platform_section,
  159. self.tool_files_section,
  160. self.configurations_section,
  161. ["References"], # empty section
  162. self.files_section,
  163. ["Globals"], # empty section
  164. ]
  165. easy_xml.WriteXmlIfChanged(content, self.project_path, encoding="Windows-1252")