208 lines
6.2 KiB
Python
208 lines
6.2 KiB
Python
# Copyright (c) 2012 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 gyp.common
|
|
import gyp.easy_xml as easy_xml
|
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
|
|
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._attrs = attrs or {}
|
|
self._attrs['Name'] = name
|
|
|
|
def _GetSpecification(self):
|
|
"""Creates an element for the tool.
|
|
|
|
Returns:
|
|
A new xml.dom.Element for the tool.
|
|
"""
|
|
return ['Tool', self._attrs]
|
|
|
|
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, name, guid=None, platforms=None):
|
|
"""Initializes the project.
|
|
|
|
Args:
|
|
project_path: Path to the project file.
|
|
version: Format version to emit.
|
|
name: Name of the project.
|
|
guid: GUID to use for project, if not None.
|
|
platforms: Array of string, the supported platforms. If null, ['Win32']
|
|
"""
|
|
self.project_path = project_path
|
|
self.version = version
|
|
self.name = name
|
|
self.guid = guid
|
|
|
|
# Default to Win32 for platforms.
|
|
if not platforms:
|
|
platforms = ['Win32']
|
|
|
|
# Initialize the specifications of the various sections.
|
|
self.platform_section = ['Platforms']
|
|
for platform in platforms:
|
|
self.platform_section.append(['Platform', {'Name': platform}])
|
|
self.tool_files_section = ['ToolFiles']
|
|
self.configurations_section = ['Configurations']
|
|
self.files_section = ['Files']
|
|
|
|
# Keep a dict keyed on filename to speed up access.
|
|
self.files_dict = dict()
|
|
|
|
def AddToolFile(self, path):
|
|
"""Adds a tool file to the project.
|
|
|
|
Args:
|
|
path: Relative path from project to tool file.
|
|
"""
|
|
self.tool_files_section.append(['ToolFile', {'RelativePath': path}])
|
|
|
|
def _GetSpecForConfiguration(self, config_type, config_name, attrs, tools):
|
|
"""Returns the specification for a configuration.
|
|
|
|
Args:
|
|
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.
|
|
Returns:
|
|
"""
|
|
# Handle defaults
|
|
if not attrs:
|
|
attrs = {}
|
|
if not tools:
|
|
tools = []
|
|
|
|
# Add configuration node and its attributes
|
|
node_attrs = attrs.copy()
|
|
node_attrs['Name'] = config_name
|
|
specification = [config_type, node_attrs]
|
|
|
|
# Add tool nodes and their attributes
|
|
if tools:
|
|
for t in tools:
|
|
if isinstance(t, Tool):
|
|
specification.append(t._GetSpecification())
|
|
else:
|
|
specification.append(Tool(t)._GetSpecification())
|
|
return specification
|
|
|
|
|
|
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.
|
|
"""
|
|
spec = self._GetSpecForConfiguration('Configuration', name, attrs, tools)
|
|
self.configurations_section.append(spec)
|
|
|
|
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 = ['Filter', {'Name': f.name}]
|
|
self._AddFilesToNode(node, f.contents)
|
|
else:
|
|
node = ['File', {'RelativePath': f}]
|
|
self.files_dict[f] = node
|
|
parent.append(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.files_section, 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.files_dict.get(path)
|
|
if not parent:
|
|
raise ValueError('AddFileConfig: file "%s" not in project.' % path)
|
|
|
|
# Add the config to the file node
|
|
spec = self._GetSpecForConfiguration('FileConfiguration', config, attrs,
|
|
tools)
|
|
parent.append(spec)
|
|
|
|
def WriteIfChanged(self):
|
|
"""Writes the project file."""
|
|
# First create XML content definition
|
|
content = [
|
|
'VisualStudioProject',
|
|
{'ProjectType': 'Visual C++',
|
|
'Version': self.version.ProjectVersion(),
|
|
'Name': self.name,
|
|
'ProjectGUID': self.guid,
|
|
'RootNamespace': self.name,
|
|
'Keyword': 'Win32Proj'
|
|
},
|
|
self.platform_section,
|
|
self.tool_files_section,
|
|
self.configurations_section,
|
|
['References'], # empty section
|
|
self.files_section,
|
|
['Globals'] # empty section
|
|
]
|
|
easy_xml.WriteXmlIfChanged(content, self.project_path,
|
|
encoding="Windows-1252")
|