| Index: site_scons/site_tools/_Node_MSVS.py
|
| ===================================================================
|
| --- site_scons/site_tools/_Node_MSVS.py (revision 12583)
|
| +++ site_scons/site_tools/_Node_MSVS.py (working copy)
|
| @@ -1,1463 +0,0 @@
|
| -#
|
| -# __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
|
| -
|
| -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
|
| -import SCons.Util
|
| -
|
| -from SCons.Debug import Trace
|
| -TODO = 0
|
| -
|
| -# Initialize random number generator
|
| -random.seed()
|
| -
|
| -
|
| -#------------------------------------------------------------------------------
|
| -# Entry point for supplying a fixed map of GUIDs for testing.
|
| -
|
| -GUIDMap = {}
|
| -
|
| -
|
| -#------------------------------------------------------------------------------
|
| -# Helper functions
|
| -
|
| -
|
| -def MakeGuid(name, seed='msvs_new'):
|
| - """Returns a GUID for the specified target name.
|
| -
|
| - Args:
|
| - name: Target name.
|
| - seed: Seed for MD5 hash.
|
| - Returns:
|
| - A GUID-line string calculated from the name and seed.
|
| -
|
| - This generates something which looks like a GUID, but depends only on the
|
| - name and seed. This means the same name/seed will always generate the same
|
| - GUID, so that projects and solutions which refer to each other can explicitly
|
| - determine the GUID to refer to explicitly. It also means that the GUID will
|
| - not change when the project for a target is rebuilt.
|
| - """
|
| - # Calculate a MD5 signature for the seed and name.
|
| - d = md5.new(str(seed) + str(name)).hexdigest().upper()
|
| - # Convert most of the signature to GUID form (discard the rest)
|
| - guid = ('{' + d[:8] + '-' + d[8:12] + '-' + d[12:16] + '-' + d[16:20]
|
| - + '-' + d[20:32] + '}')
|
| - return guid
|
| -
|
| -
|
| -#------------------------------------------------------------------------------
|
| -# Global look up of string names.
|
| -
|
| -class LookupError(Exception):
|
| - def __str__(self):
|
| - string, expanded = self.args
|
| - if string == expanded:
|
| - return string
|
| - else:
|
| - return '%s (%s)' % (string, expanded)
|
| -
|
| -_lookup_dict = {}
|
| -
|
| -def LookupAdd(item, result):
|
| - _lookup_dict[item] = result
|
| - _lookup_dict[result] = result
|
| -
|
| -def Lookup(item):
|
| - """Looks up an MSVS item in the global dictionary.
|
| -
|
| - Args:
|
| - item: A path (string) or instance for looking up.
|
| - Returns:
|
| - An instance from the global _lookup_dict.
|
| -
|
| - Raises an exception if the item does not exist in the _lookup_dict.
|
| - """
|
| - global _lookup_dict
|
| - try:
|
| - return _lookup_dict[item]
|
| - except KeyError:
|
| - return SCons.Node.FS.default_fs.Entry(item, create=False)
|
| -
|
| -def LookupCreate(klass, item, *args, **kw):
|
| - """Looks up an MSVS item, creating it if it doesn't already exist.
|
| -
|
| - Args:
|
| - klass: The class of item being looked up, or created if it
|
| - doesn't already exist in the global _lookup_dict.
|
| - item: The a string (or instance) being looked up.
|
| - *args: positional arguments passed to the klass.initialize() method.
|
| - **kw: keyword arguments passed to the klass.initialize() method.
|
| - Returns:
|
| - An instance from the global _lookup_dict, or None if the item does
|
| - not exist in the _lookup_dict.
|
| -
|
| - This raises a LookupError if the found instance doesn't match the
|
| - requested klass.
|
| -
|
| - When creating a new instance, this populates the _lookup_dict with
|
| - both the item and the instance itself as keys, so that looking up
|
| - the instance will return itself.
|
| - """
|
| - global _lookup_dict
|
| - result = _lookup_dict.get(item)
|
| - if result:
|
| - if not isinstance(result, klass):
|
| - raise LookupError, "tried to redefine %s as a %s" % (item, klass)
|
| - return result
|
| - result = klass()
|
| - result.initialize(item, *args, **kw)
|
| - LookupAdd(item, result)
|
| - return result
|
| -
|
| -
|
| -#------------------------------------------------------------------------------
|
| -
|
| -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):
|
| - """Initializes the folder.
|
| -
|
| - Args:
|
| - path: The unique name of the folder, by which other MSVS Nodes can
|
| - refer to it. This is not necessarily the name that gets printed
|
| - in the .sln file.
|
| - name: The name of this folder as actually written in a generated
|
| - .sln file. The default is
|
| - entries: List of folder entries to nest inside this folder. May contain
|
| - Folder or Project objects. May be None, if the folder is empty.
|
| - guid: GUID to use for folder, if not None.
|
| - 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
|
| -
|
| - self.guid = guid
|
| -
|
| - # Copy passed lists (or set to empty lists)
|
| - self.items = list(items or [])
|
| -
|
| - def get_guid(self):
|
| - if self.guid is None:
|
| - guid = GUIDMap.get(self.msvs_path)
|
| - if not guid:
|
| - # The GUID for the folder can be random, since it's used only inside
|
| - # solution files and doesn't need to be consistent across runs.
|
| - guid = MakeGuid(random.random())
|
| - self.guid = guid
|
| - return self.guid
|
| -
|
| - def get_msvs_path(self, sln):
|
| - return self.msvs_name
|
| -
|
| -def MSVSFolder(env, item, *args, **kw):
|
| - return LookupCreate(_MSVSFolder, item, *args, **kw)
|
| -
|
| -#------------------------------------------------------------------------------
|
| -
|
| -class MSVSConfig(object):
|
| - """Visual Studio configuration."""
|
| - def __init__(self, Name, config_type, tools=None, **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('/', '\\')
|
| -
|
| - self.Name = Name
|
| - self.config_type = config_type
|
| - self.tools = tools
|
| - self.attrs = attrs
|
| -
|
| - def CreateElement(self, doc, project):
|
| - """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)
|
| -
|
| - tools = self.tools
|
| - if tools is None:
|
| - tools = project.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)
|
| - for t in tool_objects:
|
| - 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.
|
| - """
|
| -
|
| - val = attrs.get('AdditionalDependencies')
|
| - if val:
|
| - if isinstance(val, list):
|
| - val = ' '.join(val)
|
| - attrs['AdditionalDependencies'] = val.replace('/', '\\')
|
| -
|
| - val = attrs.get('AdditionalIncludeDirectories')
|
| - if val:
|
| - if isinstance(val, list):
|
| - val = ';'.join(val)
|
| - attrs['AdditionalIncludeDirectories'] = val.replace('/', '\\')
|
| -
|
| - val = attrs.get('AdditionalManifestFiles')
|
| - if val:
|
| - if isinstance(val, list):
|
| - val = ';'.join(val)
|
| - attrs['AdditionalManifestFiles'] = val.replace('/', '\\')
|
| -
|
| - val = attrs.get('CommandLine')
|
| - if val:
|
| - if isinstance(val, list):
|
| - val = '\r\n'.join(val)
|
| - attrs['CommandLine'] = val.replace('/', '\\')
|
| -
|
| - val = attrs.get('PreprocessorDefinitions')
|
| - if val:
|
| - if isinstance(val, list):
|
| - val = ';'.join(val)
|
| - attrs['PreprocessorDefinitions'] = val
|
| -
|
| - for a in ('ImportLibrary',
|
| - 'ObjectFile',
|
| - 'OutputFile',
|
| - 'Outputs',
|
| - 'XMLDocumentationFileName'):
|
| - val = attrs.get(a)
|
| - if val:
|
| - val = val.replace('/', '\\')
|
| - attrs[a] = val
|
| -
|
| - 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
|
| -
|
| - def _format(self):
|
| - """Formats a tool specification for debug printing"""
|
| - xml_impl = xml.dom.getDOMImplementation()
|
| - doc = xml_impl.createDocument(None, 'VisualStudioProject', None)
|
| - return self.CreateElement(doc).toprettyxml()
|
| -
|
| - def diff(self, other):
|
| - for key, value in self.attrs.items():
|
| - if other.attrs[key] == value:
|
| - del self.attrs[key]
|
| -
|
| -
|
| -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}'
|
| - initialized = False
|
| -
|
| - def initialize(self, env, path, name = None,
|
| - dependencies = None,
|
| - guid = None,
|
| - buildtargets = [],
|
| - files = [],
|
| - root_namespace = None,
|
| - keyword = None,
|
| - relative_path_prefix = None,
|
| - local_directory_prefix = None,
|
| - relative_path_substitutions = [],
|
| - tools = None,
|
| - configurations = None,
|
| - **attrs):
|
| - """Initializes the project.
|
| -
|
| - Args:
|
| - path: Relative path to project file.
|
| - name: Name of project. If None, the name will be the same as the base
|
| - name of the project file.
|
| - 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.
|
| - """
|
| - if name is None:
|
| - if buildtargets:
|
| - name = os.path.splitext(buildtargets[0].name)[0]
|
| - else:
|
| - name = os.path.splitext(os.path.basename(path))[0]
|
| - if not root_namespace:
|
| - root_namespace or name
|
| -
|
| - if self.initialized:
|
| - # TODO(sgk): fill in
|
| - if self.msvs_name != name:
|
| - pass
|
| - if self.root_namespace != root_namespace:
|
| - pass
|
| - if self.relative_path_prefix != relative_path_prefix:
|
| - pass
|
| - if self.guid != guid:
|
| - pass
|
| - #if self.env != env:
|
| - # pass
|
| - else:
|
| - self.buildtargets = []
|
| - self.configurations = []
|
| - self.dependencies = []
|
| - self.file_configurations = {}
|
| - self.files = MSVSFiles([])
|
| - self.tool_files = []
|
| - self.file_lists = []
|
| - self.initialized = True
|
| - self.keyword = None
|
| - self.local_directory_prefix = ''
|
| - self.relative_path_prefix = ''
|
| - self.relative_path_substitutions = []
|
| - self.root_namespace = name
|
| -
|
| - self.attrs = attrs
|
| - self.env = env
|
| - self.guid = guid
|
| - self.msvs_name = name
|
| - self.msvs_path = path
|
| - if relative_path_prefix:
|
| - self.relative_path_prefix = relative_path_prefix
|
| - if local_directory_prefix:
|
| - self.local_directory_prefix = local_directory_prefix
|
| - for left, right in relative_path_substitutions:
|
| - t = (left.replace('/', '\\'), right.replace('/', '\\'))
|
| - self.relative_path_substitutions.append(t)
|
| - if root_namespace:
|
| - self.root_namespace = root_namespace
|
| - if keyword:
|
| - self.keyword = keyword
|
| - self.tools = tools
|
| -
|
| - self.buildtargets.extend(buildtargets)
|
| - self.configurations.extend(configurations or [])
|
| - self.dependencies.extend(list(dependencies or []))
|
| - self.AddFiles(files)
|
| -
|
| - env.Command(self, [], MSVSProjectAction)
|
| -
|
| - def args2nodes(self, entries):
|
| - result = []
|
| - for entry in entries:
|
| - if SCons.Util.is_String(entry):
|
| - entry = self.env.File(entry)
|
| - result.append(entry.srcnode())
|
| - 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.srcnode())
|
| - 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
|
| - flat_file_dict[f.srcnode()] = True
|
| - if hasattr(f, 'sources'):
|
| - for s in f.sources:
|
| - flat_file_dict[s] = True
|
| - flat_file_dict[s.srcnode()] = 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)
|
| - if not guid:
|
| - # Set GUID from path
|
| - # TODO(rspangler): This is fragile.
|
| - # 1. We can't just use the project filename sans path, since there
|
| - # could be multiple projects with the same base name (for example,
|
| - # foo/unittest.vcproj and bar/unittest.vcproj).
|
| - # 2. The path needs to be relative to $SOURCE_ROOT, so that the project
|
| - # GUID is the same whether it's included from base/base.sln or
|
| - # foo/bar/baz/baz.sln.
|
| - # 3. The GUID needs to be the same each time this builder is invoked,
|
| - # so that we don't need to rebuild the solution when the
|
| - # project changes.
|
| - # 4. We should be able to handle pre-built project files by reading the
|
| - # GUID from the files.
|
| - guid = MakeGuid(self.msvs_path)
|
| - self.guid = guid
|
| - return self.guid
|
| -
|
| - def get_msvs_path(self, sln):
|
| - return sln.rel_path(self).replace('/', '\\')
|
| -
|
| - def get_rel_path(self, node):
|
| - result = self.rel_path(node)
|
| - if self.relative_path_prefix:
|
| - if not result.startswith('..'):
|
| - result = self.relative_path_prefix + result
|
| - elif not os.path.split(result)[0]:
|
| - result = self.local_directory_prefix + result
|
| - result = result.replace('/', '\\')
|
| - for left, right in self.relative_path_substitutions:
|
| - result = result.replace(left, right)
|
| - return result
|
| -
|
| - 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
|
| - if not attrs.has_key('ConfigurationType'):
|
| - # No ConfigurationType specifically for this configuration,
|
| - # use the Project's as a default.
|
| - try:
|
| - attrs['ConfigurationType'] = self.attrs['ConfigurationType']
|
| - except KeyError:
|
| - pass
|
| - if attrs.has_key('InheritedPropertySheets'):
|
| - ips = attrs['InheritedPropertySheets']
|
| - attrs['InheritedPropertySheets'] = self.env.subst(ips)
|
| - 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.
|
| - """
|
| - self.file_lists.append(self.args2nodes(files))
|
| -
|
| - def _FilesToSourceFiles(self, files):
|
| - file_list = files[:]
|
| - result = []
|
| - 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'):
|
| - self._FilesToSourceFiles(f.entries)
|
| - result.append(f)
|
| - else:
|
| - if f.sources:
|
| - flist = f.sources
|
| - else:
|
| - flist = [f]
|
| - for x in flist:
|
| - result.append(x.srcnode())
|
| - files[:] = result
|
| -
|
| - def _MergeFiles(self, dest_list, src_list):
|
| - for f in src_list:
|
| - if f not in dest_list:
|
| - dest_list.append(f)
|
| - continue
|
| - #if hasattr(f, 'entries'):
|
| - # self._FilesToSourceFiles(f.entries)
|
| -
|
| - 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().
|
| - """
|
| - # Store as the VariantDir node, not as the source node.
|
| - node = self.env.File(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)
|
| - if self.keyword:
|
| - root.setAttribute('Keyword', self.keyword)
|
| -
|
| - # 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, self))
|
| -
|
| - # 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, self))
|
| - return node
|
| -
|
| - def VCCLCompilerTool(self, args):
|
| - default_attrs = {
|
| - 'BufferSecurityCheck' : "false",
|
| - 'CompileAs' : 0, # default
|
| - 'DebugInformationFormat' : 0, # TODO(???)
|
| - 'DisableSpecificWarnings' : [],
|
| - 'EnableFiberSafeOptimizations' : "false",
|
| - 'EnableFunctionLevelLinking' : "false",
|
| - 'EnableIntrinsicFunctions' : "false",
|
| - 'FavorSizeOrSpeed' : 0, # favorNone
|
| - 'InlineFunctionExpansion' : 1, # expandDisable
|
| - 'MinimalRebuild' : "false",
|
| - 'OmitFramePointers' : "false",
|
| - 'Optimization' : 1, # optimizeDisabled TODO(???)
|
| - 'PreprocessorDefinitions' : [],
|
| - 'RuntimeLibrary' : TODO,
|
| - 'RuntimeTypeInfo' : "false",
|
| - 'StringPooling' : "false",
|
| - 'SuppressStartupBanner' : "false",
|
| - 'WarningAsError' : "false",
|
| - 'WarningLevel' : 1, # warningLevel_1
|
| - 'WholeProgramOptimization' : "false",
|
| - }
|
| -
|
| - tool = MSVSTool('VCCLCompilerTool', **default_attrs)
|
| - attrs = tool.attrs
|
| -
|
| - for arg in args:
|
| - if arg in ('/c',):
|
| - continue
|
| - if arg.startswith('/Fo'):
|
| - continue
|
| - if arg.startswith('/D'):
|
| - attrs['PreprocessorDefinitions'].append(arg[2:])
|
| - elif arg == '/EH':
|
| - attrs['ExceptionHandling'] = 0
|
| - elif arg == '/GF':
|
| - attrs['StringPooling'] = "true"
|
| - elif arg == '/GL':
|
| - attrs['WholeProgramOptimization'] = "true"
|
| - elif arg == '/GM':
|
| - attrs['MinimalRebuild'] = "true"
|
| - elif arg == '/GR-':
|
| - attrs['RuntimeTypeInfo'] = "true"
|
| - elif arg == '/Gs':
|
| - attrs['BufferSecurityCheck'] = "true"
|
| - elif arg == '/Gs-':
|
| - attrs['BufferSecurityCheck'] = "false"
|
| - elif arg == '/GT':
|
| - attrs['EnableFiberSafeOptimizations'] = "true"
|
| - elif arg == '/Gy':
|
| - attrs['EnableFunctionLevelLinking'] = "true"
|
| - elif arg == '/MD':
|
| - attrs['RuntimeLibrary'] = 1 # rtMultiThreadedDebug
|
| - elif arg == '/MDd':
|
| - attrs['RuntimeLibrary'] = 2 # rtMultiThreadedDebugDLL
|
| - elif arg == '/MT':
|
| - attrs['RuntimeLibrary'] = 0 # rtMultiThreaded
|
| - elif arg == '/MTd':
|
| - attrs['RuntimeLibrary'] = 3 # rtMultiThreadedDLL
|
| - elif arg == '/nologo':
|
| - attrs['SuppressStartupBanner'] = "true"
|
| - elif arg == '/O1':
|
| - attrs['InlineFunctionExpansion'] = 4 # optimizeMinSpace
|
| - elif arg == '/O2':
|
| - attrs['InlineFunctionExpansion'] = 3 # optimizeMaxSpeed
|
| - elif arg == '/Ob1':
|
| - attrs['InlineFunctionExpansion'] = 2 # expandOnlyInline
|
| - elif arg == '/Ob2':
|
| - attrs['InlineFunctionExpansion'] = 0 # expandAnySuitable
|
| - elif arg == '/Od':
|
| - attrs['Optimization'] = 0
|
| - elif arg == '/Oi':
|
| - attrs['EnableIntrinsicFunctions'] = "true"
|
| - elif arg == '/Os':
|
| - attrs['FavorSizeOrSpeed'] = 1 # favorSize
|
| - elif arg == '/Ot':
|
| - attrs['FavorSizeOrSpeed'] = 2 # favorSpeed
|
| - elif arg == '/Ox':
|
| - attrs['Optimization'] = 2 # optimizeFull
|
| - elif arg == '/Oy':
|
| - attrs['OmitFramePointers'] = "true"
|
| - elif arg == '/Oy-':
|
| - attrs['TODO'] = "true"
|
| - elif arg in ('/Tc', '/TC'):
|
| - attrs['CompileAs'] = 1 # compileAsC
|
| - elif arg in ('/Tp', '/TP'):
|
| - attrs['CompileAs'] = 2 # compileAsCPlusPlus
|
| - elif arg == '/WX':
|
| - attrs['WarnAsError'] = "true"
|
| - elif arg.startswith('/W'):
|
| - attrs['WarningLevel'] = int(arg[2:]) # 0 through 4
|
| - elif arg.startswith('/wd'):
|
| - attrs['DisableSpecificWarnings'].append(str(arg[3:]))
|
| - elif arg == '/Z7':
|
| - attrs['DebugInformationFormat'] = 3 # debugOldSytleInfo TODO(???)
|
| - elif arg == '/Zd':
|
| - attrs['DebugInformationFormat'] = 0 # debugDisabled
|
| - elif arg == '/Zi':
|
| - attrs['DebugInformationFormat'] = 2 # debugEnabled TODO(???)
|
| - elif arg == '/ZI':
|
| - attrs['DebugInformationFormat'] = 1 # debugEditAndContinue TODO(???)
|
| -
|
| - cppdefines = attrs['PreprocessorDefinitions']
|
| - if cppdefines:
|
| - attrs['PreprocessorDefinitions'] = ';'.join(cppdefines)
|
| - warnings = attrs['DisableSpecificWarnings']
|
| - if warnings:
|
| - warnings = SCons.Util.uniquer(warnings)
|
| - attrs['DisableSpecificWarnings'] = ';'.join(warnings)
|
| -
|
| - return tool
|
| -
|
| - def VCLibrarianTool(self, args):
|
| - default_attrs = {
|
| - 'LinkTimeCodeGeneration' : "false",
|
| - 'SuppressStartupBanner' : "false",
|
| - }
|
| -
|
| - tool = MSVSTool('VCLibrarianTool', **default_attrs)
|
| - attrs = tool.attrs
|
| -
|
| - for arg in args:
|
| - if arg.startswith('/OUT'):
|
| - continue
|
| - if arg == '/ltcg':
|
| - attrs['LinkTimeCodeGeneration'] = "true"
|
| - elif arg == '/nologo':
|
| - attrs['SuppressStartupBanner'] = "true"
|
| -
|
| - return tool
|
| -
|
| - def VCLinkerTool(self, args):
|
| - default_attrs = {
|
| - 'LinkIncremental' : "false",
|
| - 'LinkTimeCodeGeneration' : "false",
|
| - 'EnableCOMDATFolding' : TODO,
|
| - 'OptimizeForWindows98' : TODO,
|
| - 'OptimizeReferences' : TODO,
|
| - 'Profile' : "false",
|
| - 'SuppressStartupBanner' : "false",
|
| - }
|
| -
|
| - tool = MSVSTool('VCLinkerTool', **default_attrs)
|
| - attrs = tool.attrs
|
| -
|
| - for arg in args:
|
| - if arg == '':
|
| - continue
|
| - if arg == '/INCREMENTAL':
|
| - attrs['LinkIncremental'] = "true"
|
| - elif arg == '/INCREMENTAL:NO':
|
| - attrs['LinkIncremental'] = "false"
|
| - elif arg == '/LTCG':
|
| - attrs['LinkTimeCodeGeneration'] = "true"
|
| - elif arg == '/nologo':
|
| - attrs['SuppressStartupBanner'] = "true"
|
| - elif arg == '/OPT:NOICF':
|
| - attrs['EnableCOMDATFolding'] = 2 #
|
| - elif arg == '/OPT:NOWIN98':
|
| - attrs['OptimizeForWindows98'] = 1 #
|
| - elif arg == '/OPT:REF':
|
| - attrs['OptimizeReferences'] = 2 #
|
| - elif arg == '/PROFILE':
|
| - attrs['Profile'] = "true"
|
| -
|
| - return tool
|
| -
|
| - command_to_tool_map = {
|
| - 'cl' : 'VCCLCompilerTool',
|
| - 'cl.exe' : 'VCCLCompilerTool',
|
| - 'lib' : 'VCLibrarianTool',
|
| - 'lib.exe' : 'VCLibrarianTool',
|
| - 'link' : 'VCLinkerTool',
|
| - 'link.exe' : 'VCLinkerTool',
|
| - }
|
| -
|
| - def cl_to_tool(self, args):
|
| - command = os.path.basename(args[0])
|
| - method_name = self.command_to_tool_map.get(command)
|
| - if not method_name:
|
| - return None
|
| - return getattr(self, method_name)(args[1:])
|
| -
|
| - def _AddFileConfigurationDifferences(self, target, source, base_env, file_env, name):
|
| - """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.
|
| - """
|
| - executor = target.get_executor()
|
| - base_cl = map(str, base_env.subst_list(executor)[0])
|
| - file_cl = map(str, file_env.subst_list(executor)[0])
|
| - if base_cl == file_cl:
|
| - return
|
| -
|
| - base_tool = self.cl_to_tool(base_cl)
|
| - file_tool = self.cl_to_tool(file_cl)
|
| -
|
| - if not base_tool or not_file_tool:
|
| - return
|
| -
|
| - file_tool.diff(base_tool)
|
| -
|
| - self.AddFileConfig(source, name, tools=[file_tool])
|
| -
|
| - 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
|
| -
|
| - for bt in self.buildtargets:
|
| - executor = bt.get_executor()
|
| - build_env = bt.get_build_env()
|
| - bt_cl = map(str, build_env.subst_list(executor)[0])
|
| - tool = self.cl_to_tool(bt_cl)
|
| - default_tool = self.cl_to_tool([bt_cl[0]])
|
| - if default_tool:
|
| - tool.diff(default_tool)
|
| - else:
|
| - # TODO(sgk): print a message unconditionally is too
|
| - # verbose for things like Python function actions,
|
| - # but doing nothing runs the risk of burying problems.
|
| - # Work out a better solution.
|
| - #print "no tool for %r" % bt_cl[0]
|
| - pass
|
| - for t in bt.sources:
|
| - e = t.get_build_env()
|
| - additional_files = SCons.Util.UniqueList()
|
| - for s in t.sources:
|
| - s = env.arg2nodes([s])[0].srcnode()
|
| - if not self.FindFile(s):
|
| - additional_files.append(s)
|
| - if not build_env is e:
|
| - # TODO(sgk): This test may be bogus, but it works for now.
|
| - # We're trying to figure out if the file configuration
|
| - # differences need to be added one per build target, or one
|
| - # per configuration for the entire project. The assumption
|
| - # is that if the number of buildtargets configured matches
|
| - # the number of project configurations, that we use those
|
| - # in preference to the project configurations.
|
| - if len(self.buildtargets) == len(self.configurations):
|
| - self._AddFileConfigurationDifferences(t, s, build_env, e, e.subst('$MSVSCONFIGURATIONNAME'))
|
| - else:
|
| - for config in self.configurations:
|
| - self._AddFileConfigurationDifferences(t, s, build_env, e, config.Name)
|
| - self._MergeFiles(self.files, additional_files)
|
| -
|
| - def Write(self, env):
|
| - """Writes the project file."""
|
| - for flist in self.file_lists:
|
| - self._FilesToSourceFiles(flist)
|
| - self._MergeFiles(self.files, flist)
|
| - for k, v in self.file_configurations.items():
|
| - self.file_configurations[str(k)] = v
|
| - k = self.env.File(k).srcnode()
|
| - self.file_configurations[k] = v
|
| - self.file_configurations[str(k)] = v
|
| - self._AddFileConfigurations(env)
|
| -
|
| - self.Create()
|
| -
|
| - f = open(str(self), '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',
|
| -
|
| - 'AdditionalIncludeDirectories',
|
| - 'Description',
|
| - 'CommandLine',
|
| - 'OutputFile',
|
| - 'ImportLibrary',
|
| - 'PreprocessorDefinitions',
|
| - 'UsePrecompiledHeader',
|
| - 'PrecompiledHeaderThrough',
|
| - 'ForcedIncludeFiles',
|
| - ],
|
| - '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(env, item, *args, **kw)
|
| - LookupAdd(item, result)
|
| - return result
|
| -
|
| -#------------------------------------------------------------------------------
|
| -
|
| -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):
|
| - """Initializes the solution.
|
| -
|
| - Args:
|
| - path: Path to solution file.
|
| - entries: List of entries in solution. May contain Folder or Project
|
| - objects. May be None, if the folder is empty.
|
| - variants: List of build variant strings. If none, a default list will
|
| - be used.
|
| - """
|
| - self.msvs_path = path
|
| - self.websiteProperties = websiteProperties
|
| -
|
| - # Copy passed lists (or set to empty lists)
|
| - self.entries = list(entries or [])
|
| -
|
| - if variants:
|
| - # Copy passed list
|
| - self.variants = variants[:]
|
| - else:
|
| - # Use default
|
| - self.variants = ['Debug|Win32', 'Release|Win32']
|
| - # TODO(rspangler): Need to be able to handle a mapping of solution config
|
| - # to project config. Should we be able to handle variants being a dict,
|
| - # or add a separate variant_map variable? If it's a dict, we can't
|
| - # guarantee the order of variants since dict keys aren't ordered.
|
| -
|
| - env.Command(self, [], MSVSSolutionAction)
|
| -
|
| - def Write(self, env):
|
| - """Writes the solution file to disk.
|
| -
|
| - Raises:
|
| - IndexError: An entry appears multiple times.
|
| - """
|
| - r = []
|
| - errors = []
|
| -
|
| - def lookup_subst(item, env=env, errors=errors):
|
| - if SCons.Util.is_String(item):
|
| - lookup_item = env.subst(item)
|
| - else:
|
| - lookup_item = item
|
| - try:
|
| - return Lookup(lookup_item)
|
| - except SCons.Errors.UserError:
|
| - raise LookupError(item, lookup_item)
|
| -
|
| - # Walk the entry tree and collect all the folders and projects.
|
| - all_entries = []
|
| - entries_to_check = self.entries[:]
|
| - while entries_to_check:
|
| - # Pop from the beginning of the list to preserve the user's order.
|
| - entry = entries_to_check.pop(0)
|
| - try:
|
| - entry = lookup_subst(entry)
|
| - except LookupError, e:
|
| - errors.append("Could not look up entry `%s'." % e)
|
| - continue
|
| -
|
| - # A project or folder can only appear once in the solution's folder tree.
|
| - # This also protects from cycles.
|
| - if entry in all_entries:
|
| - #raise IndexError('Entry "%s" appears more than once in solution' %
|
| - # e.name)
|
| - continue
|
| -
|
| - all_entries.append(entry)
|
| -
|
| - # If this is a folder, check its entries too.
|
| - if isinstance(entry, _MSVSFolder):
|
| - entries_to_check += entry.entries
|
| -
|
| - # Header
|
| - r.append('Microsoft Visual Studio Solution File, Format Version 9.00\n')
|
| - r.append('# Visual Studio 2005\n')
|
| -
|
| - # Project entries
|
| - for e in all_entries:
|
| - r.append('Project("%s") = "%s", "%s", "%s"\n' % (
|
| - e.entry_type_guid, # Entry type GUID
|
| - e.msvs_name, # Folder name
|
| - e.get_msvs_path(self), # Folder name (again)
|
| - e.get_guid(), # Entry GUID
|
| - ))
|
| -
|
| - # TODO(rspangler): Need a way to configure this stuff
|
| - if self.websiteProperties:
|
| - r.append('\tProjectSection(WebsiteProperties) = preProject\n'
|
| - '\t\tDebug.AspNetCompiler.Debug = "True"\n'
|
| - '\t\tRelease.AspNetCompiler.Debug = "False"\n'
|
| - '\tEndProjectSection\n')
|
| -
|
| - if isinstance(e, _MSVSFolder):
|
| - if e.items:
|
| - r.append('\tProjectSection(SolutionItems) = preProject\n')
|
| - for i in e.items:
|
| - i = i.replace('/', '\\')
|
| - r.append('\t\t%s = %s\n' % (i, i))
|
| - r.append('\tEndProjectSection\n')
|
| -
|
| - if isinstance(e, _MSVSProject):
|
| - if e.dependencies:
|
| - r.append('\tProjectSection(ProjectDependencies) = postProject\n')
|
| - for d in e.dependencies:
|
| - try:
|
| - d = lookup_subst(d)
|
| - except LookupError, e:
|
| - errors.append("Could not look up dependency `%s'." % e)
|
| - else:
|
| - r.append('\t\t%s = %s\n' % (d.get_guid(), d.get_guid()))
|
| - r.append('\tEndProjectSection\n')
|
| -
|
| - r.append('EndProject\n')
|
| -
|
| - # Global section
|
| - r.append('Global\n')
|
| -
|
| - # Configurations (variants)
|
| - r.append('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
|
| - for v in self.variants:
|
| - r.append('\t\t%s = %s\n' % (v, v))
|
| - r.append('\tEndGlobalSection\n')
|
| -
|
| - # Sort config guids for easier diffing of solution changes.
|
| - config_guids = []
|
| - for e in all_entries:
|
| - if isinstance(e, _MSVSProject):
|
| - config_guids.append(e.get_guid())
|
| - config_guids.sort()
|
| -
|
| - r.append('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
|
| - for g in config_guids:
|
| - for v in self.variants:
|
| - r.append('\t\t%s.%s.ActiveCfg = %s\n' % (
|
| - g, # Project GUID
|
| - v, # Solution build configuration
|
| - v, # Project build config for that solution config
|
| - ))
|
| -
|
| - # Enable project in this solution configuratation
|
| - r.append('\t\t%s.%s.Build.0 = %s\n' % (
|
| - g, # Project GUID
|
| - v, # Solution build configuration
|
| - v, # Project build config for that solution config
|
| - ))
|
| - r.append('\tEndGlobalSection\n')
|
| -
|
| - # TODO(rspangler): Should be able to configure this stuff too (though I've
|
| - # never seen this be any different)
|
| - r.append('\tGlobalSection(SolutionProperties) = preSolution\n')
|
| - r.append('\t\tHideSolutionNode = FALSE\n')
|
| - r.append('\tEndGlobalSection\n')
|
| -
|
| - # Folder mappings
|
| - # TODO(rspangler): Should omit this section if there are no folders
|
| - folder_mappings = []
|
| - for e in all_entries:
|
| - if not isinstance(e, _MSVSFolder):
|
| - continue # Does not apply to projects, only folders
|
| - for subentry in e.entries:
|
| - try:
|
| - subentry = lookup_subst(subentry)
|
| - except LookupError, e:
|
| - errors.append("Could not look up subentry `%s'." % subentry)
|
| - else:
|
| - folder_mappings.append((subentry.get_guid(), e.get_guid()))
|
| - folder_mappings.sort()
|
| - r.append('\tGlobalSection(NestedProjects) = preSolution\n')
|
| - for fm in folder_mappings:
|
| - r.append('\t\t%s = %s\n' % fm)
|
| - r.append('\tEndGlobalSection\n')
|
| -
|
| - r.append('EndGlobal\n')
|
| -
|
| - if errors:
|
| - errors = ['Errors while generating solution file:'] + errors
|
| - raise SCons.Errors.UserError, '\n\t'.join(errors)
|
| -
|
| - f = open(self.path, 'wt')
|
| - f.write(''.join(r))
|
| - f.close()
|
| -
|
| -def MSVSSolution(env, item, *args, **kw):
|
| - if not SCons.Util.is_String(item):
|
| - return item
|
| - item = env.subst(item)
|
| - result = env.fs._lookup(item, None, _MSVSSolution, create=1)
|
| - result.initialize(env, item, *args, **kw)
|
| - LookupAdd(item, result)
|
| - return result
|
| -
|
| -class Derived(SCons.Util.Proxy):
|
| - def srcnode(self, *args, **kw):
|
| - return self
|
| - def __getattr__(self, name):
|
| - if name == 'sources':
|
| - return []
|
| - return SCons.Util.Proxy.__getattr__(self, name)
|
| - def __hash__(self, *args, **kw):
|
| - return id(self)
|
| -
|
| -import __builtin__
|
| -
|
| -__builtin__.Derived = Derived
|
| -__builtin__.MSVSConfig = MSVSConfig
|
| -__builtin__.MSVSFilter = MSVSFilter
|
| -__builtin__.MSVSProject = MSVSProject
|
| -__builtin__.MSVSSolution = MSVSSolution
|
| -__builtin__.MSVSTool = MSVSTool
|
|
|