Index: site_scons/site_tools/_Node_MSVS.py |
=================================================================== |
--- site_scons/site_tools/_Node_MSVS.py (revision 7407) |
+++ site_scons/site_tools/_Node_MSVS.py (working copy) |
@@ -1,23 +1,45 @@ |
-#!/usr/bin/python2.4 |
-# Copyright 2008, Google Inc. |
-# All rights reserved. |
+# |
+# __COPYRIGHT__ |
+# |
+# Permission is hereby granted, free of charge, to any person obtaining |
+# a copy of this software and associated documentation files (the |
+# "Software"), to deal in the Software without restriction, including |
+# without limitation the rights to use, copy, modify, merge, publish, |
+# distribute, sublicense, and/or sell copies of the Software, and to |
+# permit persons to whom the Software is furnished to do so, subject to |
+# the following conditions: |
+# |
+# The above copyright notice and this permission notice shall be included |
+# in all copies or substantial portions of the Software. |
+# |
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
+# |
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" |
+ |
__doc__ = """SCons.Node.MSVS |
-Microsoft Visual Studio nodes. |
+New implementation of Visual Studio project generation for SCons. |
""" |
-import SCons.Node.FS |
-import SCons.Script |
- |
- |
-"""New implementation of Visual Studio project generation for SCons.""" |
- |
import md5 |
import os |
import random |
+import re |
+import UserList |
+import xml.dom |
+import xml.dom.minidom |
+import SCons.Node.FS |
+import SCons.Script |
+ |
# Initialize random number generator |
random.seed() |
@@ -122,13 +144,81 @@ |
#------------------------------------------------------------------------------ |
-class _MSVSFolder(SCons.Node.Node): |
- """Folder in a Visual Studio project or solution.""" |
+class FileList(object): |
+ def __init__(self, entries=None): |
+ if isinstance(entries, FileList): |
+ entries = entries.entries |
+ self.entries = entries or [] |
+ def __getitem__(self, i): |
+ return self.entries[i] |
+ def __setitem__(self, i, item): |
+ self.entries[i] = item |
+ def __delitem__(self, i): |
+ del self.entries[i] |
+ def __add__(self, other): |
+ if isinstance(other, FileList): |
+ return self.__class__(self.entries + other.entries) |
+ elif isinstance(other, type(self.entries)): |
+ return self.__class__(self.entries + other) |
+ else: |
+ return self.__class__(self.entries + list(other)) |
+ def __radd__(self, other): |
+ if isinstance(other, FileList): |
+ return self.__class__(other.entries + self.entries) |
+ elif isinstance(other, type(self.entries)): |
+ return self.__class__(other + self.entries) |
+ else: |
+ return self.__class__(list(other) + self.entries) |
+ def __iadd__(self, other): |
+ if isinstance(other, FileList): |
+ self.entries += other.entries |
+ elif isinstance(other, type(self.entries)): |
+ self.entries += other |
+ else: |
+ self.entries += list(other) |
+ return self |
+ def append(self, item): |
+ return self.entries.append(item) |
+ def extend(self, item): |
+ return self.entries.extend(item) |
+ def index(self, item, *args): |
+ return self.entries.index(item, *args) |
+ def remove(self, item): |
+ return self.entries.remove(item) |
+def FileListWalk(top, topdown=True, onerror=None): |
+ """ |
+ """ |
+ try: |
+ entries = top.entries |
+ except AttributeError, err: |
+ if onerror is not None: |
+ onerror(err) |
+ return |
+ |
+ dirs, nondirs = [], [] |
+ for entry in entries: |
+ if hasattr(entry, 'entries'): |
+ dirs.append(entry) |
+ else: |
+ nondirs.append(entry) |
+ |
+ if topdown: |
+ yield top, dirs, nondirs |
+ for entry in dirs: |
+ for x in FileListWalk(entry, topdown, onerror): |
+ yield x |
+ if not topdown: |
+ yield top, dirs, nondirs |
+ |
+#------------------------------------------------------------------------------ |
+ |
+class _MSVSFolder(FileList): |
+ """Folder in a Visual Studio solution.""" |
+ |
entry_type_guid = '{2150E333-8FDC-42A3-9474-1A3956D46DE8}' |
- def initialize(self, path, name = None, entries = None, guid = None, |
- items = None): |
+ def initialize(self, path, name = None, entries = None, guid = None, items = None): |
"""Initializes the folder. |
Args: |
@@ -143,6 +233,8 @@ |
items: List of solution items to include in the folder project. May be |
None, if the folder does not directly contain items. |
""" |
+ super(_MSVSFolder, self).__init__(entries) |
+ |
# For folder entries, the path is the same as the name |
self.msvs_path = path |
self.msvs_name = name or path |
@@ -150,7 +242,6 @@ |
self.guid = guid |
# Copy passed lists (or set to empty lists) |
- self.entries = list(entries or []) |
self.items = list(items or []) |
def get_guid(self): |
@@ -171,13 +262,183 @@ |
#------------------------------------------------------------------------------ |
+class MSVSConfig(object): |
+ """Visual Studio configuration.""" |
+ def __init__(self, Name, config_type, tools=[], **attrs): |
+ """Initializes the configuration. |
+ Args: |
+ **attrs: Configuration attributes. |
+ """ |
+ # Special handling for attributes that we want to make more |
+ # convenient for the user. |
+ ips = attrs.get('InheritedPropertySheets') |
+ if ips: |
+ if isinstance(ips, list): |
+ ips = ';'.join(ips) |
+ attrs['InheritedPropertySheets'] = ips.replace('/', '\\') |
+ |
+ tools = tools or [] |
+ if not SCons.Util.is_List(tools): |
+ tools = [tools] |
+ tool_objects = [] |
+ for t in tools: |
+ if not isinstance(t, MSVSTool): |
+ t = MSVSTool(t) |
+ tool_objects.append(t) |
+ |
+ self.Name = Name |
+ self.config_type = config_type |
+ self.tools = tool_objects |
+ self.attrs = attrs |
+ |
+ def CreateElement(self, doc): |
+ """Creates an element for the configuration. |
+ |
+ Args: |
+ doc: xml.dom.Document object to use for node creation. |
+ |
+ Returns: |
+ A new xml.dom.Element for the configuration. |
+ """ |
+ node = doc.createElement(self.config_type) |
+ node.setAttribute('Name', self.Name) |
+ for k, v in self.attrs.items(): |
+ node.setAttribute(k, v) |
+ for t in self.tools: |
+ node.appendChild(t.CreateElement(doc)) |
+ return node |
+ |
+ |
+class MSVSFileListBase(FileList): |
+ """Base class for a file list in a Visual Studio project file.""" |
+ |
+ def CreateElement(self, doc, node_func=lambda x: x): |
+ """Creates an element for an MSVSFileListBase subclass. |
+ |
+ Args: |
+ doc: xml.dom.Document object to use for node creation. |
+ node_func: Function to use to return Nodes for objects that |
+ don't have a CreateElement() method of their own. |
+ |
+ Returns: |
+ A new xml.dom.Element for the MSVSFileListBase object. |
+ """ |
+ node = doc.createElement(self.element_name) |
+ for entry in self.entries: |
+ if hasattr(entry, 'CreateElement'): |
+ n = entry.CreateElement(doc, node_func) |
+ else: |
+ n = node_func(entry) |
+ node.appendChild(n) |
+ return node |
+ |
+ |
+class MSVSFiles(MSVSFileListBase): |
+ """Files list in a Visual Studio project file.""" |
+ element_name = 'Files' |
+ |
+ |
+class MSVSFilter(MSVSFileListBase): |
+ """Filter (that is, a virtual folder) in a Visual Studio project file.""" |
+ |
+ element_name = 'Filter' |
+ |
+ def __init__(self, Name, entries=None): |
+ """Initializes the folder. |
+ |
+ Args: |
+ Name: Filter (folder) name. |
+ entries: List of filenames and/or Filter objects contained. |
+ """ |
+ super(MSVSFilter, self).__init__(entries) |
+ self.Name = Name |
+ |
+ def CreateElement(self, doc, node_func=lambda x: x): |
+ """Creates an element for the Filter. |
+ |
+ Args: |
+ doc: xml.dom.Document object to use for node creation. |
+ node_func: Function to use to return Nodes for objects that |
+ don't have a CreateElement() method of their own. |
+ |
+ Returns: |
+ A new xml.dom.Element for the filter. |
+ """ |
+ node = super(MSVSFilter, self).CreateElement(doc, node_func) |
+ node.setAttribute('Name', self.Name) |
+ return node |
+ |
+ |
+class MSVSTool(object): |
+ """Visual Studio tool.""" |
+ |
+ def __init__(self, Name, **attrs): |
+ """Initializes the tool. |
+ |
+ Args: |
+ Name: Tool name. |
+ **attrs: Tool attributes. |
+ """ |
+ self.Name = Name |
+ self.attrs = attrs |
+ |
+ 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 MSVSToolFile(object): |
+ """Visual Studio tool file specification.""" |
+ |
+ def __init__(self, node, **attrs): |
+ """Initializes the tool. |
+ |
+ Args: |
+ node: Node for the Tool File |
+ **attrs: Tool File attributes. |
+ """ |
+ self.node = node |
+ |
+ def CreateElement(self, doc, project): |
+ result = doc.createElement('ToolFile') |
+ result.setAttribute('RelativePath', project.get_rel_path(self.node)) |
+ return result |
+ |
+ |
+#------------------------------------------------------------------------------ |
+ |
+def MSVSAction(target, source, env): |
+ target[0].Write(env) |
+ |
+MSVSProjectAction = SCons.Script.Action(MSVSAction, |
+ "Generating Visual Studio project `$TARGET' ...") |
+ |
class _MSVSProject(SCons.Node.FS.File): |
"""Visual Studio project.""" |
entry_type_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}' |
- def initialize(self, path, name = None, dependencies = None, guid = None): |
+ def initialize(self, env, path, name = None, |
+ dependencies = None, |
+ guid = None, |
+ buildtargets = [], |
+ files = [], |
+ root_namespace = None, |
+ relative_path_prefix = '', |
+ tools = None, |
+ configurations = None): |
"""Initializes the project. |
Args: |
@@ -187,15 +448,88 @@ |
dependencies: List of other Project objects this project is dependent |
upon, if not None. |
guid: GUID to use for project, if not None. |
+ buildtargets: List of target(s) being built by this project. |
+ files: List of source files for the project. This will be |
+ supplemented by any source files of buildtargets. |
+ root_namespace: The value of the RootNamespace attribute of the |
+ project, if not None. The default is to use the same |
+ string as the name. |
+ relative_path_prefix: A prefix to be appended to the beginning of |
+ every file name in the list. The canonical use is to specify |
+ './' to make files explicitly relative to the local directory. |
+ tools: A list of MSVSTool objects or strings representing |
+ tools to be used to build this project. This will be used |
+ for any configurations that don't provide their own |
+ per-configuration tool list. |
+ configurations: A list of MSVSConfig objects representing |
+ configurations built by this project. |
""" |
self.msvs_path = path |
- self.msvs_name = name or os.path.splitext(os.path.basename(self.name))[0] |
+ self.msvs_node = env.File(path) |
+ if name is None: |
+ if buildtargets: |
+ name = os.path.splitext(buildtargets[0].name)[0] |
+ else: |
+ name = os.path.splitext(os.path.basename(path))[0] |
+ self.msvs_name = name |
+ self.root_namespace = root_namespace or self.msvs_name |
+ self.buildtargets = buildtargets |
+ self.relative_path_prefix = relative_path_prefix |
+ self.tools = tools |
+ self.env = env |
self.guid = guid |
- # Copy passed lists (or set to empty lists) |
self.dependencies = list(dependencies or []) |
+ self.configurations = list(configurations or []) |
+ self.file_configurations = {} |
+ self.tool_files = [] |
+ if not isinstance(files, MSVSFiles): |
+ files = MSVSFiles(self.args2nodes(files)) |
+ self.files = files |
+ |
+ env.Command(self, [], MSVSSolutionAction) |
+ |
+ def args2nodes(self, entries): |
+ result = [] |
+ for entry in entries: |
+ if SCons.Util.is_String(entry): |
+ entry = self.env.File(entry) |
+ result.append(entry) |
+ elif hasattr(entry, 'entries'): |
+ entry.entries = self.args2nodes(entry.entries) |
+ result.append(entry) |
+ elif isinstance(entry, (list, UserList.UserList)): |
+ result.extend(self.args2nodes(entry)) |
+ elif hasattr(entry, 'sources') and entry.sources: |
+ result.extend(entry.sources) |
+ else: |
+ result.append(entry) |
+ return result |
+ |
+ def FindFile(self, node): |
+ try: |
+ flat_file_dict = self.flat_file_dict |
+ except AttributeError: |
+ flat_file_dict = {} |
+ file_list = self.files[:] |
+ while file_list: |
+ entry = file_list.pop(0) |
+ if not isinstance(entry, (list, UserList.UserList)): |
+ entry = [entry] |
+ for f in entry: |
+ if hasattr(f, 'entries'): |
+ file_list.extend(f.entries) |
+ else: |
+ flat_file_dict[f] = True |
+ if hasattr(f, 'sources'): |
+ for s in f.sources: |
+ flat_file_dict[s] = True |
+ self.flat_file_dict = flat_file_dict |
+ |
+ return flat_file_dict.get(node) |
+ |
def get_guid(self): |
if self.guid is None: |
guid = GUIDMap.get(self.msvs_path) |
@@ -220,28 +554,321 @@ |
def get_msvs_path(self, sln): |
return sln.rel_path(self).replace('/', '\\') |
+ def get_rel_path(self, node): |
+ result = self.relative_path_prefix + self.msvs_node.rel_path(node) |
+ return result.replace('/', '\\') |
+ |
+ def AddConfig(self, Name, tools=None, **attrs): |
+ """Adds a configuration to the parent node. |
+ |
+ Args: |
+ Name: The name of the configuration. |
+ tools: List of tools (strings or Tool objects); may be None. |
+ **attrs: Configuration attributes. |
+ """ |
+ if tools is None: |
+ # No tool list specifically for this configuration, |
+ # use the Project's as a default. |
+ tools = self.tools |
+ c = MSVSConfig(Name, 'Configuration', tools=tools, **attrs) |
+ self.configurations.append(c) |
+ |
+ 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. |
+ """ |
+ # TODO(rspangler) This also doesn't handle adding files to an existing |
+ # filter. That is, it doesn't merge the trees. |
+ self.files.extend(self.args2nodes(files)) |
+ |
+ def AddFileConfig(self, path, Name, tools=None, **attrs): |
+ """Adds a configuration to a file. |
+ |
+ Args: |
+ path: Relative path to the file. |
+ Name: Name of configuration to add. |
+ tools: List of tools (strings or MSVSTool objects); may be None. |
+ **attrs: Configuration attributes. |
+ |
+ Raises: |
+ ValueError: Relative path does not match any file added via AddFiles(). |
+ """ |
+ node = self.env.File(path) |
+ if not self.FindFile(node): |
+ raise ValueError('AddFileConfig: file "%s" not in project' % path) |
+ c = MSVSConfig(Name, 'FileConfiguration', tools=tools, **attrs) |
+ config_list = self.file_configurations.get(node) |
+ if config_list is None: |
+ config_list = [] |
+ self.file_configurations[node] = config_list |
+ config_list.append(c) |
+ |
+ def AddToolFile(self, path): |
+ """Adds a tool file to the project. |
+ |
+ Args: |
+ path: Relative path from project to tool file. |
+ """ |
+ tf = MSVSToolFile(self.env.File(path)) |
+ self.tool_files.append(tf) |
+ |
+ def Create(self): |
+ """Creates the project document. |
+ |
+ Args: |
+ name: Name of the project. |
+ guid: GUID to use for project, if not None. |
+ """ |
+ # Create XML doc |
+ xml_impl = xml.dom.getDOMImplementation() |
+ self.doc = xml_impl.createDocument(None, 'VisualStudioProject', None) |
+ |
+ # Add attributes to root element |
+ root = self.doc.documentElement |
+ root.setAttribute('ProjectType', 'Visual C++') |
+ root.setAttribute('Version', '8.00') |
+ root.setAttribute('Name', self.msvs_name) |
+ root.setAttribute('ProjectGUID', self.get_guid()) |
+ root.setAttribute('RootNamespace', self.root_namespace) |
+ root.setAttribute('Keyword', 'Win32Proj') |
+ |
+ # Add platform list |
+ platforms = self.doc.createElement('Platforms') |
+ root.appendChild(platforms) |
+ n = self.doc.createElement('Platform') |
+ n.setAttribute('Name', 'Win32') |
+ platforms.appendChild(n) |
+ |
+ # Add tool files section |
+ tool_files = self.doc.createElement('ToolFiles') |
+ root.appendChild(tool_files) |
+ for tf in self.tool_files: |
+ tool_files.appendChild(tf.CreateElement(self.doc, self)) |
+ |
+ # Add configurations section |
+ configs = self.doc.createElement('Configurations') |
+ root.appendChild(configs) |
+ for c in self.configurations: |
+ configs.appendChild(c.CreateElement(self.doc)) |
+ |
+ # Add empty References section |
+ root.appendChild(self.doc.createElement('References')) |
+ |
+ # Add files section |
+ root.appendChild(self.files.CreateElement(self.doc, self.CreateFileElement)) |
+ |
+ # Add empty Globals section |
+ root.appendChild(self.doc.createElement('Globals')) |
+ |
+ def CreateFileElement(self, file): |
+ """Create a DOM node for the specified file. |
+ |
+ Args: |
+ file: The file Node being considered. |
+ |
+ Returns: |
+ A DOM Node for the File, with a relative path to the current |
+ project object, and any file configurations attached to the |
+ project. |
+ """ |
+ |
+ node = self.doc.createElement('File') |
+ node.setAttribute('RelativePath', self.get_rel_path(file)) |
+ for c in self.file_configurations.get(file, []): |
+ node.appendChild(c.CreateElement(self.doc)) |
+ return node |
+ |
+ def _AddFileConfigurationDifferences(self, target, source, base_env, file_env): |
+ """Adds a per-file configuration. |
+ |
+ Args: |
+ target: The target being built from the source. |
+ source: The source to which the file configuration is being added. |
+ base_env: The base construction environment for the project. |
+ Differences from this will go into the FileConfiguration |
+ in the project file. |
+ file_env: The construction environment for the target, containing |
+ the per-target settings. |
+ """ |
+ pass |
+ |
+ def _AddFileConfigurations(self, env): |
+ """Adds per-file configurations for the buildtarget's sources. |
+ |
+ Args: |
+ env: The base construction environment for the project. |
+ """ |
+ if not self.buildtargets: |
+ return |
+ |
+ bt = self.buildtargets[0] |
+ additional_files = [] |
+ for t in bt.sources: |
+ e = t.get_build_env() |
+ for s in t.sources: |
+ s = env.arg2nodes([s])[0] |
+ if not self.FindFile(s): |
+ additional_files.append(s) |
+ if not env is e: |
+ self._AddFileConfigurationDifferences(t, s, env, e) |
+ self.AddFiles(additional_files) |
+ |
+ def Write(self, env): |
+ """Writes the project file.""" |
+ self._AddFileConfigurations(env) |
+ |
+ self.Create() |
+ |
+ f = open(str(self.msvs_node), 'wt') |
+ f.write(self.formatMSVSProjectXML(self.doc)) |
+ f.close() |
+ |
+ # Methods for formatting XML as nearly identically to Microsoft's |
+ # .vcproj output as we can practically make it. |
+ # |
+ # The general design here is copied from: |
+ # |
+ # Bruce Eckels' MindView, Inc: 12-09-04 XML Oddyssey |
+ # http://www.mindview.net/WebLog/log-0068 |
+ # |
+ # Eckels' implementation broke up long tag definitions for readability, |
+ # in much the same way that .vcproj does, but we've modified things |
+ # for .vcproj quirks (like some tags *always* terminating with </Tag>, |
+ # even when empty). |
+ |
+ encoding = 'Windows-1252' |
+ |
+ def formatMSVSProjectXML(self, xmldoc): |
+ xmldoc = xmldoc.toprettyxml("", "\n", encoding=self.encoding) |
+ # Remove trailing whitespace from each line: |
+ xmldoc = "\n".join( |
+ [line.rstrip() for line in xmldoc.split("\n")]) |
+ # Remove all empty lines before opening '<': |
+ while xmldoc.find("\n\n<") != -1: |
+ xmldoc = xmldoc.replace("\n\n<", "\n<") |
+ dom = xml.dom.minidom.parseString(xmldoc) |
+ xmldoc = dom.toprettyxml("\t", "", encoding=self.encoding) |
+ xmldoc = xmldoc.replace('?><', '?>\n<') |
+ xmldoc = self.reformatLines(xmldoc) |
+ return xmldoc |
+ |
+ def reformatLines(self, xmldoc): |
+ result = [] |
+ for line in [line.rstrip() for line in xmldoc.split("\n")]: |
+ if line.lstrip().startswith("<"): |
+ result.append(self.reformatLine(line) + "\n") |
+ else: |
+ result.append(line + "\n") |
+ return ''.join(result) |
+ |
+ # Keyword order for specific tags. |
+ # |
+ # Listed keywords will come first and in the specified order. |
+ # Any unlisted keywords come after, in whatever order they appear |
+ # in the input config. In theory this means we would only *have* to |
+ # list the keywords that we care about, but in practice we'll probably |
+ # want to nail down Visual Studio's order to make sure we match them |
+ # as nearly as possible. |
+ |
+ order = { |
+ 'Configuration' : [ |
+ 'Name', |
+ 'ConfigurationType', |
+ 'InheritedPropertySheets', |
+ ], |
+ 'FileConfiguration' : [ |
+ 'Name', |
+ 'ExcludedFromBuild', |
+ ], |
+ 'Tool' : [ |
+ 'Name', |
+ 'DisableSpecificWarnings', |
+ ], |
+ 'VisualStudioProject' : [ |
+ 'ProjectType', |
+ 'Version', |
+ 'Name', |
+ 'ProjectGUID', |
+ 'RootNamespace', |
+ 'Keyword', |
+ ], |
+ } |
+ |
+ force_closing_tag = [ |
+ 'File', |
+ 'Globals', |
+ 'References', |
+ 'ToolFiles' |
+ ] |
+ |
+ oneLiner = re.compile("(\s*)<(\w+)(.*)>") |
+ keyValuePair = re.compile('\w+="[^"]*?"') |
+ def reformatLine(self, line): |
+ """Reformat an xml tag to put each key-value |
+ element on a single indented line, for readability""" |
+ matchobj = self.oneLiner.match(line.rstrip()) |
+ if not matchobj: |
+ return line |
+ baseIndent, tag, rest = matchobj.groups() |
+ slash = '' |
+ if rest[-1:] == '/': |
+ slash = '/' |
+ rest = rest[:-1] |
+ result = [baseIndent + '<' + tag] |
+ indent = baseIndent + "\t" |
+ pairs = self.keyValuePair.findall(rest) |
+ for key in self.order.get(tag, []): |
+ for p in [ p for p in pairs if p.startswith(key+'=') ]: |
+ result.append("\n" + indent + p) |
+ pairs.remove(p) |
+ for pair in pairs: |
+ result.append("\n" + indent + pair) |
+ result = [''.join(result).rstrip()] |
+ |
+ if tag in self.force_closing_tag: |
+ # These force termination with </Tag>, so translate slash. |
+ if rest: |
+ result.append("\n") |
+ result.append(indent) |
+ result.append(">") |
+ if slash: |
+ result.append("\n") |
+ result.append(baseIndent + "</" + tag + ">") |
+ else: |
+ if rest: |
+ result.append("\n") |
+ if slash: |
+ result.append(baseIndent) |
+ else: |
+ result.append(indent) |
+ result.append(slash + ">") |
+ |
+ return ''.join(result) |
+ |
def MSVSProject(env, item, *args, **kw): |
if not SCons.Util.is_String(item): |
return item |
item = env.subst(item) |
result = env.fs._lookup(item, None, _MSVSProject, create=1) |
- result.initialize(item, *args, **kw) |
+ result.initialize(env, item, *args, **kw) |
LookupAdd(item, result) |
return result |
#------------------------------------------------------------------------------ |
-def MSVSAction(target, source, env): |
- target[0].Write(env) |
- |
MSVSSolutionAction = SCons.Script.Action(MSVSAction, |
"Generating Visual Studio solution `$TARGET' ...") |
class _MSVSSolution(SCons.Node.FS.File): |
"""Visual Studio solution.""" |
- def initialize(self, env, path, entries = None, variants = None, |
- websiteProperties = True): |
+ def initialize(self, env, path, entries = None, variants = None, websiteProperties = True): |
"""Initializes the solution. |
Args: |
@@ -432,3 +1059,10 @@ |
result.initialize(env, item, *args, **kw) |
LookupAdd(item, result) |
return result |
+ |
+import __builtin__ |
+ |
+__builtin__.MSVSFilter = MSVSFilter |
+__builtin__.MSVSProject = MSVSProject |
+__builtin__.MSVSSolution = MSVSSolution |
+__builtin__.MSVSTool = MSVSTool |