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