Index: site_scons/site_tools/component_targets_msvs.py |
=================================================================== |
--- site_scons/site_tools/component_targets_msvs.py (revision 12583) |
+++ site_scons/site_tools/component_targets_msvs.py (working copy) |
@@ -1,997 +0,0 @@ |
-#!/usr/bin/python2.4 |
-# Copyright 2008, Google Inc. |
-# All rights reserved. |
-# |
-# Redistribution and use in source and binary forms, with or without |
-# modification, are permitted provided that the following conditions are |
-# met: |
-# |
-# * Redistributions of source code must retain the above copyright |
-# notice, this list of conditions and the following disclaimer. |
-# * Redistributions in binary form must reproduce the above |
-# copyright notice, this list of conditions and the following disclaimer |
-# in the documentation and/or other materials provided with the |
-# distribution. |
-# * Neither the name of Google Inc. nor the names of its |
-# contributors may be used to endorse or promote products derived from |
-# this software without specific prior written permission. |
-# |
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
- |
-"""Visual Studio solution output of component targets for SCons.""" |
- |
-import copy |
-import md5 |
-import os |
-import sys |
-import xml.dom |
-import xml.dom.minidom |
-import SCons |
- |
- |
-#------------------------------------------------------------------------------ |
- |
- |
-def MakeGuid(name, seed='component_targets_msvs'): |
- """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 |
- |
-#------------------------------------------------------------------------------ |
- |
- |
-def GetGuidAndNameFromVSProject(project_path): |
- """Reads the GUID from a Visual Studio project file. |
- |
- Args: |
- project_path: Path to the Visual Studio project file. |
- |
- Returns: |
- The GUID string from the file. |
- The project name from the file. |
- """ |
- doc = xml.dom.minidom.parse(project_path) |
- try: |
- n_root = doc.documentElement |
- if n_root.nodeName != 'VisualStudioProject': |
- raise SCons.Errors.UserError('%s is not a Visual Studio project.' % |
- project_path) |
- return ( |
- str(n_root.attributes['ProjectGUID'].nodeValue), |
- str(n_root.attributes['Name'].nodeValue), |
- ) |
- finally: |
- # Clean up doc |
- doc.unlink() |
- |
-#------------------------------------------------------------------------------ |
- |
- |
-class VSProjectWriter(object): |
- """Visual Studio XML project writer.""" |
- |
- def __init__(self, project_path): |
- """Initializes the project. |
- |
- Args: |
- project_path: Path to the project file. |
- """ |
- self.project_path = project_path |
- self.doc = None |
- |
- def Create(self, name): |
- """Creates the project document. |
- |
- Args: |
- name: Name of the project. |
- """ |
- self.name = name |
- self.guid = MakeGuid(name) |
- |
- # Create XML doc |
- xml_impl = xml.dom.getDOMImplementation() |
- self.doc = xml_impl.createDocument(None, 'VisualStudioProject', None) |
- |
- # Add attributes to root element |
- self.n_root = self.doc.documentElement |
- self.n_root.setAttribute('ProjectType', 'Visual C++') |
- self.n_root.setAttribute('Version', '8.00') |
- self.n_root.setAttribute('Name', self.name) |
- self.n_root.setAttribute('ProjectGUID', self.guid) |
- self.n_root.setAttribute('RootNamespace', self.name) |
- self.n_root.setAttribute('Keyword', 'MakeFileProj') |
- |
- # Add platform list |
- n_platform = self.doc.createElement('Platforms') |
- self.n_root.appendChild(n_platform) |
- n = self.doc.createElement('Platform') |
- n.setAttribute('Name', 'Win32') |
- n_platform.appendChild(n) |
- |
- # Add empty ToolFiles section |
- self.n_root.appendChild(self.doc.createElement('ToolFiles')) |
- |
- # Add configurations section |
- self.n_configs = self.doc.createElement('Configurations') |
- self.n_root.appendChild(self.n_configs) |
- |
- # Add files section |
- self.n_files = self.doc.createElement('Files') |
- self.n_root.appendChild(self.n_files) |
- |
- # Add empty Globals section |
- self.n_root.appendChild(self.doc.createElement('Globals')) |
- |
- def AddConfig(self, name, attrs, tool_attrs): |
- """Adds a configuration to the project. |
- |
- Args: |
- name: Configuration name. |
- attrs: Dict of configuration attributes. |
- tool_attrs: Dict of tool attributes. |
- """ |
- # Add configuration node |
- n_config = self.doc.createElement('Configuration') |
- n_config.setAttribute('Name', '%s|Win32' % name) |
- n_config.setAttribute('ConfigurationType', '0') |
- for k, v in attrs.items(): |
- n_config.setAttribute(k, v) |
- self.n_configs.appendChild(n_config) |
- |
- # Add tool node |
- n_tool = self.doc.createElement('Tool') |
- n_tool.setAttribute('Name', 'VCNMakeTool') |
- n_tool.setAttribute('IncludeSearchPath', '') |
- n_tool.setAttribute('ForcedIncludes', '') |
- n_tool.setAttribute('AssemblySearchPath', '') |
- n_tool.setAttribute('ForcedUsingAssemblies', '') |
- n_tool.setAttribute('CompileAsManaged', '') |
- n_tool.setAttribute('PreprocessorDefinitions', '') |
- for k, v in tool_attrs.items(): |
- n_tool.setAttribute(k, v) |
- n_config.appendChild(n_tool) |
- |
- def _WalkFolders(self, folder_dict, parent): |
- """Recursively walks the folder tree. |
- |
- Args: |
- folder_dict: Dict of folder entries. Entry is |
- either subdir_name:subdir_dict or relative_path_to_file:None. |
- parent: Parent node (folder node for that dict) |
- """ |
- entries = folder_dict.keys() |
- entries.sort() |
- for e in entries: |
- if folder_dict[e]: |
- # Folder |
- n_subfolder = self.doc.createElement('Filter') |
- n_subfolder.setAttribute('Name', e) |
- parent.appendChild(n_subfolder) |
- self._WalkFolders(folder_dict[e], n_subfolder) |
- else: |
- # File |
- n_file = self.doc.createElement('File') |
- n_file.setAttribute('RelativePath', e) |
- parent.appendChild(n_file) |
- |
- def AddFiles(self, name, files_dict): |
- """Adds files to the project. |
- |
- Args: |
- name: Name of the folder. If None, files/folders will be added directly |
- to the files list. |
- files_dict: A dict of files / folders. |
- |
- Within the files_dict: |
- * A file entry is relative_path:None |
- * A folder entry is folder_name:files_dict, where files_dict is another |
- dict of this form. |
- """ |
- # Create subfolder if necessary |
- if name: |
- n_folder = self.doc.createElement('Filter') |
- n_folder.setAttribute('Name', name) |
- self.n_files.appendChild(n_folder) |
- else: |
- n_folder = self.n_files |
- |
- # Recursively add files to the folder |
- self._WalkFolders(files_dict, n_folder) |
- |
- def Write(self): |
- """Writes the project file.""" |
- f = open(self.project_path, 'wt') |
- self.doc.writexml(f, encoding='Windows-1252', addindent=' ', newl='\n') |
- f.close() |
- |
-#------------------------------------------------------------------------------ |
- |
- |
-def ComponentVSProjectBuilder(target, source, env): |
- """Visual Studio project builder. |
- |
- Args: |
- target: Destination file. |
- source: List of sources to be added to the target. |
- env: Environment context. |
- |
- Returns: |
- Zero if successful. |
- """ |
- source = source # Silence gpylint |
- |
- target_name = env['TARGET_NAME'] |
- project_file = target[0].path |
- project_to_main = env.RelativePath(target[0].dir, env.Dir('$MAIN_DIR'), |
- sep='/') |
- |
- env_hammer_bat = env.Clone(VS_PROJECT_TO_MAIN_DIR=project_to_main) |
- hammer_bat = env_hammer_bat.subst('$COMPONENT_VS_PROJECT_SCRIPT_PATH', raw=1) |
- |
- vsp = VSProjectWriter(project_file) |
- vsp.Create(target_name) |
- |
- # Add configuration per build mode supported by this target |
- target_path = env['TARGET_PATH'] |
- for mode, path in target_path.items(): |
- attrs = {} |
- attrs['OutputDirectory'] = '$(ProjectDir)/%s/%s/out' % (mode, target_name) |
- attrs['IntermediateDirectory'] = ('$(ProjectDir)/%s/%s/tmp' % |
- (mode, target_name)) |
- |
- tool_attrs = {} |
- if path: |
- tool_attrs['Output'] = env.RelativePath(target[0].dir, |
- env.Entry(path), sep='/') |
- build_cmd = '%s --mode=%s %s' % (hammer_bat, mode, target_name) |
- clean_cmd = '%s --mode=%s -c %s' % (hammer_bat, mode, target_name) |
- tool_attrs['BuildCommandLine'] = build_cmd |
- tool_attrs['CleanCommandLine'] = clean_cmd |
- tool_attrs['ReBuildCommandLine'] = clean_cmd + ' && ' + build_cmd |
- |
- vsp.AddConfig(mode, attrs, tool_attrs) |
- |
- # TODO(rspangler): Fill in files - at least, the .scons file invoking the |
- # target. |
- |
- # Write project |
- vsp.Write() |
- return 0 |
- |
- |
-def ComponentVSProject(self, target_name, **kwargs): |
- """Visual Studio project pseudo-builder for the specified target. |
- |
- Args: |
- self: Environment context. |
- target_name: Name of the target. |
- kwargs: Optional keyword arguments override environment variables in the |
- derived environment used to create the project. |
- |
- Returns: |
- A list of output nodes. |
- """ |
- # Builder only works on Windows |
- if sys.platform not in ('win32', 'cygwin'): |
- return [] |
- |
- # Clone environment and add keyword args |
- env = self.Clone() |
- for k, v in kwargs.items(): |
- env[k] = v |
- |
- # Save the target name |
- env['TARGET_NAME'] = target_name |
- |
- # Extract the target properties and save in the environment for use by the |
- # real builder. |
- t = GetTargets().get(target_name) |
- env['TARGET_PATH'] = {} |
- if t: |
- for mode, mode_properties in t.mode_properties.items(): |
- # Since the target path is what Visual Studio will run, use the EXE |
- # property in preference to TARGET_PATH. |
- target_path = mode_properties.get('EXE', |
- mode_properties.get('TARGET_PATH')) |
- env.Append(TARGET_PATH={mode: target_path}) |
- else: |
- # No modes declared for this target. Could be a custom alias created by |
- # a SConscript, rather than a component builder. Assume it can be built in |
- # all modes, but produces no output. |
- for mode in GetTargetModes(): |
- env.Append(TARGET_PATH={mode: None}) |
- |
- # Call the real builder |
- return env.ComponentVSProjectBuilder( |
- '$COMPONENT_VS_PROJECT_DIR/${TARGET_NAME}', []) |
- |
-#------------------------------------------------------------------------------ |
- |
- |
-class SourceWalker(object): |
- """Iterator for walking a node tree and collecting sources. |
- |
- This is depth-first, children are visited before the parent. The object |
- can be initialized with any node, and returns the next node on the descent |
- with each Next() call. |
- |
- This class does not get caught in node cycles caused, for example, by C |
- header file include loops. |
- |
- Based on SCons.Node.Walker. |
- """ |
- |
- def __init__(self, node, seen, print_interval = 1000): |
- """Initializes the source walker. |
- |
- Args: |
- node: Node to start walk from. |
- seen: Set to add seen nodes to, if not None. |
- print_interval: Interval in nodes examined at which to print a progress |
- indicator. |
- """ |
- self.interval = print_interval |
- # Put node on stack |
- self.stack = [node] |
- # Scan for node's children, then copy them to node.wkids |
- node.wkids = copy.copy(node.children(scan=1)) |
- # Keep track of nodes we've seen (returned) |
- if seen is None: |
- seen = set() |
- self.seen = seen |
- # Add node to history for cycle detection |
- self.history = set() |
- self.history.add(node) |
- # We've seen one node now |
- self.nodes_examined = 1 |
- self.unique_nodes = 1 |
- |
- |
- def Next(self): |
- """Get the next node for this walk of the tree. |
- |
- Returns: |
- The next node, or None if no more nodes. |
- |
- This function is intentionally iterative, not recursive, to sidestep any |
- issues of stack size limitations. |
- """ |
- while self.stack: |
- if self.stack[-1].wkids: |
- # Node has children we haven't examined, so iterate into the first |
- # child |
- node = self.stack[-1].wkids.pop(0) |
- if not self.stack[-1].wkids: |
- # No more children of this node |
- self.stack[-1].wkids = None |
- self.nodes_examined += 1 |
- if self.interval and not self.nodes_examined % self.interval: |
- self.PrintProgress() |
- if (node not in self.history) and (node not in self.seen): |
- # Haven't hit a cycle or a node we've already seen |
- node.wkids = copy.copy(node.children(scan=1)) |
- self.stack.append(node) |
- self.history.add(node) |
- else: |
- # Coming back from iterating, so return the next node on the stack. |
- node = self.stack.pop() |
- self.history.remove(node) |
- self.seen.add(node) |
- self.unique_nodes += 1 |
- return node |
- return None |
- |
- def PrintProgress(self): |
- """Prints a progress indicator.""" |
- print ' Examined %d nodes, found %d unique...' % ( |
- self.nodes_examined, self.unique_nodes) |
- |
- def WalkAll(self): |
- """Walks all nodes in the the tree.""" |
- while self.Next(): |
- pass |
- if self.interval: |
- self.PrintProgress() |
- |
- |
-def ComponentVSSourceProjectBuilder(target, source, env): |
- """Visual Studio source project builder. |
- |
- Args: |
- target: Destination file. |
- source: List of sources to be added to the target. |
- env: Environment context. |
- |
- Returns: |
- Zero if successful. |
- """ |
- source = source # Silence gpylint |
- |
- target_name = env['PROJECT_NAME'] |
- project_file = target[0].path |
- project_dir = target[0].dir |
- |
- # Get list of suffixes to include |
- suffixes = env.SubstList2('$COMPONENT_VS_SOURCE_SUFFIXES') |
- |
- # Convert source folders to absolute paths |
- folders = [] |
- for f in env['COMPONENT_VS_SOURCE_FOLDERS']: |
- # (folder name, folder abspath, dict of contents) |
- folders.append((f[0], env.Dir(f[1]).abspath, {})) |
- |
- # TODO(rspangler): Additional enhancements: |
- # * Should be able to specify paths in folder name (i.e., foo/bar) and |
- # create the nested folder nodes ('foo' and 'bar') |
- # * Should be tolerant of a folder being specified more than once with |
- # the same name (probably necessary to support nested folder nodes anyway) |
- # Can probably accomplish both of those by creating a parent fodler dict and |
- # calling WalkFolders() only once. |
- # Create a temporary solution alias to point to all the targets, so we can |
- # make a single call to SourceWalker() |
- tmp_alias = env.Alias('vs_source_project_' + target_name, |
- map(env.Alias, env['COMPONENT_VS_SOURCE_TARGETS'])) |
- |
- # Scan all targets and add unique nodes to set of sources |
- print ' Scanning dependency tree ...' |
- all_srcs = set() |
- walker = SourceWalker(tmp_alias[0], all_srcs) |
- walker.WalkAll() |
- |
- # Walk all sources and build directory trees |
- print ' Building source tree...' |
- for n in all_srcs: |
- if not hasattr(n, 'isfile') or not n.isfile(): |
- continue # Not a file |
- if n.has_builder(): |
- continue # Not a leaf node |
- if n.suffix not in suffixes: |
- continue # Not a file type we include |
- |
- path = n.rfile().abspath |
- for f in folders: |
- if path.startswith(f[1]): |
- if f[0] is None: |
- # Folder name of None is a filter |
- break |
- relpath = path[len(f[1]) + 1:].split(os.sep) |
- folder_dict = f[2] |
- # Recursively add subdirs |
- for pathseg in relpath[:-1]: |
- if pathseg not in folder_dict: |
- folder_dict[pathseg] = {} |
- folder_dict = folder_dict[pathseg] |
- # Add file to last subdir. No dict, since this isn't a subdir |
- folder_dict[env.RelativePath(project_dir, path)] = None |
- break |
- |
- print ' Writing project file...' |
- |
- vsp = VSProjectWriter(project_file) |
- vsp.Create(target_name) |
- |
- # One configuration for all build modes |
- vsp.AddConfig('all', {}, {}) |
- |
- # Add files |
- for f in folders: |
- if f[0] is None: |
- continue # Skip filters |
- vsp.AddFiles(f[0], f[2]) |
- |
- vsp.Write() |
- return 0 |
- |
- |
-def ComponentVSSourceProject(self, project_name, target_names, **kwargs): |
- """Visual Studio source project pseudo-builder. |
- |
- Args: |
- self: Environment context. |
- project_name: Name of the project. |
- target_names: List of target names to include source for. |
- kwargs: Optional keyword arguments override environment variables in the |
- derived environment used to create the project. |
- |
- Returns: |
- A list of output nodes. |
- """ |
- # Builder only works on Windows |
- if sys.platform not in ('win32', 'cygwin'): |
- return [] |
- |
- # Clone environment and add keyword args |
- env = self.Clone() |
- for k, v in kwargs.items(): |
- env[k] = v |
- |
- # Save the project name and targets |
- env['PROJECT_NAME'] = project_name |
- env['COMPONENT_VS_SOURCE_TARGETS'] = target_names |
- |
- # Call the real builder |
- return env.ComponentVSSourceProjectBuilder( |
- '$COMPONENT_VS_PROJECT_DIR/${PROJECT_NAME}', []) |
- |
-#------------------------------------------------------------------------------ |
- |
- |
-def FindSources(env, dest, source, suffixes=None): |
- """Recursively finds sources and adds them to the destination set. |
- |
- Args: |
- env: Environment context. |
- dest: Set to add source nodes to. |
- source: Source file(s) to find. May be a string, Node, or a list of |
- mixed strings or Nodes. Strings will be passed through env.Glob() to |
- evaluate wildcards. If a source evaluates to a directory, the entire |
- directory will be recursively added. |
- suffixes: List of suffixes to include. If not None, only files with these |
- suffixes will be added to dest. |
- """ |
- for source_entry in env.Flatten(source): |
- if type(source_entry) == str: |
- # Search for matches for each source entry |
- source_nodes = env.Glob(source_entry) |
- else: |
- # Source entry is already a file or directory node; no need to glob it |
- source_nodes = [source_entry] |
- for s in source_nodes: |
- if str(s.__class__) == 'SCons.Node.FS.Dir': |
- # Recursively search subdir. Since glob('*') doesn't match dot files, |
- # also glob('.*'). |
- FindSources(env, dest, [s.abspath + '/*', s.abspath + '/.*'], suffixes) |
- elif suffixes and s.suffix in suffixes: |
- dest.add(s) |
- |
- |
-def ComponentVSDirProjectBuilder(target, source, env): |
- """Visual Studio directory project builder. |
- |
- Args: |
- target: Destination file. |
- source: List of sources to be added to the target. |
- env: Environment context. |
- |
- Returns: |
- Zero if successful. |
- """ |
- source = source # Silence gpylint |
- |
- target_name = env['PROJECT_NAME'] |
- project_file = target[0].path |
- project_dir = target[0].dir |
- |
- # Convert source folders to absolute paths |
- folders = [] |
- for f in env['COMPONENT_VS_SOURCE_FOLDERS']: |
- # (folder name, folder abspath, dict of contents) |
- folders.append((f[0], env.Dir(f[1]).abspath, {})) |
- |
- # Recursively scan source directories |
- print ' Scanning directories for source...' |
- all_srcs = set() |
- FindSources(env, all_srcs, env['PROJECT_SOURCES'], |
- suffixes=env.SubstList2('$COMPONENT_VS_SOURCE_SUFFIXES')) |
- |
- # Walk all sources and build directory trees |
- print ' Building source tree...' |
- for n in all_srcs: |
- # Map addRepository'd source to its real location. |
- path = n.rfile().abspath |
- for f in folders: |
- if path.startswith(f[1]): |
- if f[0] is None: |
- # Folder name of None is a filter |
- break |
- relpath = path[len(f[1]) + 1:].split(os.sep) |
- folder_dict = f[2] |
- # Recursively add subdirs |
- for pathseg in relpath[:-1]: |
- if pathseg not in folder_dict: |
- folder_dict[pathseg] = {} |
- folder_dict = folder_dict[pathseg] |
- # Add file to last subdir. No dict, since this isn't a subdir |
- folder_dict[env.RelativePath(project_dir, path)] = None |
- break |
- |
- print ' Writing project file...' |
- |
- vsp = VSProjectWriter(project_file) |
- vsp.Create(target_name) |
- |
- # One configuration for all build modes |
- vsp.AddConfig('all', {}, {}) |
- |
- # Add files |
- for f in folders: |
- if f[0] is None: |
- continue # Skip filters |
- vsp.AddFiles(f[0], f[2]) |
- |
- vsp.Write() |
- return 0 |
- |
- |
-def ComponentVSDirProject(self, project_name, source, **kwargs): |
- """Visual Studio directory project pseudo-builder. |
- |
- Args: |
- self: Environment context. |
- project_name: Name of the project. |
- source: List of source files and/or directories. |
- kwargs: Optional keyword arguments override environment variables in the |
- derived environment used to create the project. |
- |
- Returns: |
- A list of output nodes. |
- """ |
- # Builder only works on Windows |
- if sys.platform not in ('win32', 'cygwin'): |
- return [] |
- |
- # Clone environment and add keyword args |
- env = self.Clone() |
- for k, v in kwargs.items(): |
- env[k] = v |
- |
- # Save the project name and sources |
- env['PROJECT_NAME'] = project_name |
- env['PROJECT_SOURCES'] = source |
- |
- # Call the real builder |
- return env.ComponentVSDirProjectBuilder( |
- '$COMPONENT_VS_PROJECT_DIR/${PROJECT_NAME}', []) |
- |
-#------------------------------------------------------------------------------ |
- |
- |
-def ComponentVSSolutionBuilder(target, source, env): |
- """Visual Studio solution builder. |
- |
- Args: |
- target: Destination file. |
- source: List of sources to be added to the target. |
- env: Environment context. |
- |
- Returns: |
- Zero if successful. |
- """ |
- source = source # Silence gpylint |
- |
- solution_file = target[0].path |
- projects = env['SOLUTION_PROJECTS'] |
- folders = env['SOLUTION_FOLDERS'] |
- |
- # Scan externally-generated projects |
- external_projects = [] |
- for p in source: |
- guid, name = GetGuidAndNameFromVSProject(p.abspath) |
- external_projects.append((p, name, guid)) |
- |
- f = open(solution_file, 'wt') |
- f.write('Microsoft Visual Studio Solution File, Format Version 9.00\n') |
- f.write('# Visual Studio 2005\n') |
- |
- # Projects generated by ComponentVSSolution() |
- for p in projects: |
- project_file = env.File( |
- '$COMPONENT_VS_PROJECT_DIR/%s$COMPONENT_VS_PROJECT_SUFFIX' % p) |
- f.write('Project("%s") = "%s", "%s", "%s"\nEndProject\n' % ( |
- '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}', # External makefile GUID |
- p, # Project name |
- env.RelativePath(target[0].dir, project_file), # Path to project file |
- MakeGuid(p), # Project GUID |
- )) |
- |
- # Projects generated elsewhere |
- for p, name, guid in external_projects: |
- f.write('Project("%s") = "%s", "%s", "%s"\nEndProject\n' % ( |
- # TODO(rspangler): What if this project isn't type external makefile? |
- # How to tell what type it is? |
- '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}', # External makefile GUID |
- name, # Project name |
- env.RelativePath(target[0].dir, p), # Path to project file |
- guid, # Project GUID |
- )) |
- |
- # Folders from build groups |
- # TODO(rspangler): Currently no way to add external project (specified in |
- # sources) to a solution folder. |
- for folder in folders: |
- f.write('Project("%s") = "%s", "%s", "%s"\nEndProject\n' % ( |
- '{2150E333-8FDC-42A3-9474-1A3956D46DE8}', # Solution folder GUID |
- folder, # Folder name |
- folder, # Folder name (again) |
- # Use a different seed so the folder won't get the same GUID as a |
- # project. |
- MakeGuid(folder, seed='folder'), # Project GUID |
- )) |
- |
- f.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n') |
- for mode in GetTargetModes(): |
- f.write('\t\t%s|Win32 = %s|Win32\n' % (mode, mode)) |
- f.write('\tEndGlobalSection\n') |
- |
- # Determine which projects should be enabled |
- # TODO(rspangler): This is somewhat clunky. DEFAULT_TARGETS is global, and |
- # what we really need is something mode-specific. In theory we could make |
- # this a mode-specific dict rather than a list, but that'd also be a pain to |
- # populate. |
- # These variable names are also getting REALLY long. Perhaps we should |
- # define shorter ones (with the default value redirecting to the longer |
- # ones for legacy compatibility). |
- enable_projects = env.SubstList2('$COMPONENT_VS_ENABLED_PROJECTS') |
- |
- f.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n') |
- |
- # Projects generated by ComponentVSSolution() |
- for p in projects: |
- for mode in GetTargetModes(): |
- f.write('\t\t%s.%s|Win32.ActiveCfg = %s|Win32\n' % ( |
- MakeGuid(p), # Project GUID |
- mode, # Solution build configuration |
- mode, # Project build config for that solution config |
- )) |
- |
- t = GetTargets().get(p) |
- |
- # Determine if project should be enabled in this mode |
- enabled = t and mode in t.mode_properties |
- if enable_projects and p not in enable_projects: |
- # Enable list specified, but target isn't in it |
- # TODO(rspangler): Since we env.Default(scons-out) elsewhere, this |
- # likely causes all projects to be disabled by default. But that's |
- # realistically better than enabling them all... |
- enabled = False |
- |
- if enabled: |
- # Target can be built in this mode |
- f.write('\t\t%s.%s|Win32.Build.0 = %s|Win32\n' % ( |
- MakeGuid(p), # Project GUID |
- mode, # Solution build configuration |
- mode, # Project build config for that solution config |
- )) |
- |
- # Projects generated elsewhere |
- for p, name, guid in external_projects: |
- for mode in GetTargetModes(): |
- f.write('\t\t%s.%s|Win32.ActiveCfg = %s|Win32\n' % ( |
- guid, # Project GUID |
- mode, # Solution build configuration |
- mode, # Project build config for that solution config |
- )) |
- |
- if name in enable_projects or not enable_projects: |
- # Build target in this mode |
- f.write('\t\t%s.%s|Win32.Build.0 = %s|Win32\n' % ( |
- guid, # Project GUID |
- mode, # Solution build configuration |
- mode, # Project build config for that solution config |
- )) |
- |
- f.write('\tEndGlobalSection\n') |
- |
- f.write('\tGlobalSection(SolutionProperties) = preSolution\n') |
- f.write('\t\tHideSolutionNode = FALSE\n') |
- f.write('\tEndGlobalSection\n') |
- |
- if folders: |
- f.write('\tGlobalSection(NestedProjects) = preSolution\n') |
- for p, folder in projects.items(): |
- f.write('\t\t%s = %s\n' % (MakeGuid(p), MakeGuid(folder, seed='folder'))) |
- f.write('\tEndGlobalSection\n') |
- |
- f.write('EndGlobal\n') |
- f.close() |
- |
- return 0 |
- |
- |
-def ComponentVSSolution(self, solution_name, target_names, projects=None, |
- **kwargs): |
- """Visual Studio solution pseudo-builder. |
- |
- Args: |
- self: Environment context. |
- solution_name: Name of the solution. |
- target_names: Names of targets or target groups to include in the solution. |
- This will automatically build projects for them. |
- projects: List of aditional projects not generated by this solution to |
- include in the solution. |
- kwargs: Optional keyword arguments override environment variables in the |
- derived environment used to create the solution. |
- |
- Returns: |
- The list of output nodes. |
- """ |
- # TODO(rspangler): Should have option to build source project also. Perhaps |
- # select using a --source_project option, since it needs to use gather_inputs |
- # to scan the DAG and will blow up the null build time. |
- # TODO(rspangler): Should also have autobuild_projects option. If false, |
- # don't build them. |
- # TODO(rspangler): Should also be able to specify a target group directly |
- # (i.e. 'run_all_tests') |
- |
- # Builder only works on Windows |
- if sys.platform not in ('win32', 'cygwin'): |
- return [] |
- |
- # Clone environment and add keyword args |
- env = self.Clone() |
- for k, v in kwargs.items(): |
- env[k] = v |
- |
- # Save the target name |
- env['SOLUTION_NAME'] = solution_name |
- |
- # Get list of targets to make projects for. At this point we haven't |
- # determined whether they're groups or targets. |
- target_names = env.SubstList2(target_names) |
- env['SOLUTION_TARGETS'] = target_names |
- |
- # Save the default targets list as an environment variable |
- env['COMPONENT_VS_SCONS_DEFAULT_TARGETS'] = SCons.Script.DEFAULT_TARGETS |
- |
- # Expand target_names into project names, and create project-to-folder |
- # mappings |
- project_names = {} |
- folders = [] |
- if target_names: |
- # Expand target_names into project names |
- for target in target_names: |
- if target in GetTargetGroups(): |
- # Add target to folders |
- folders.append(target) |
- # Add all project_names in the group |
- for t in GetTargetGroups()[target].GetTargetNames(): |
- project_names[t] = target |
- elif target in GetTargets(): |
- # Just a target name |
- project_names[target] = None |
- else: |
- print 'Warning: ignoring unknown target "%s"' % target |
- else: |
- # No target names specified, so use all projects |
- for t in GetTargets(): |
- project_names[t] = None |
- |
- env['SOLUTION_FOLDERS'] = folders |
- env['SOLUTION_PROJECTS'] = project_names |
- |
- # Call the real builder |
- out_nodes = env.ComponentVSSolutionBuilder( |
- '$COMPONENT_VS_SOLUTION_DIR/${SOLUTION_NAME}', projects or []) |
- |
- # Call the real builder for the projects we generate |
- for p in project_names: |
- out_nodes += env.ComponentVSProject(p) |
- |
- # Add the solution target |
- # TODO(rspangler): Should really defer the rest of the work above, since |
- # otherwise we can't build a solution which has a target to rebuild itself. |
- env.Alias('all_solutions', env.Alias(solution_name, out_nodes)) |
- |
- # TODO(rspangler): To rebuild the solution properly, need to override its |
- # project configuration so it only has '--mode=all' (or some other way of |
- # setting the subset of modes which it should use to rebuild itself). |
- # Rebuilding with the property below will strip it down to just the current |
- # build mode, which isn't what we want. |
- # Let component_targets know this target is available in the current mode |
- #self.SetTargetProperty(solution_name, TARGET_PATH=out_nodes[0]) |
- |
- return out_nodes |
- |
-#------------------------------------------------------------------------------ |
- |
- |
-def generate(env): |
- # NOTE: SCons requires the use of this name, which fails gpylint. |
- """SCons entry point for this tool.""" |
- |
- # Add pseudo-builders to set up the project and solution builders. These |
- # need to be available on all platforms so that SConscripts which reference |
- # them will work. |
- env.AddMethod(ComponentVSDirProject) |
- env.AddMethod(ComponentVSProject) |
- env.AddMethod(ComponentVSSolution) |
- env.AddMethod(ComponentVSSourceProject) |
- |
- # If not on Windows, don't do anything else |
- if sys.platform not in ('win32', 'cygwin'): |
- return |
- |
- # Include tools we need |
- env.Tool('gather_inputs') |
- |
- env.SetDefault( |
- COMPONENT_VS_PROJECT_DIR='$COMPONENT_VS_SOLUTION_DIR/projects', |
- COMPONENT_VS_PROJECT_SCRIPT_NAME = 'hammer.bat', |
- COMPONENT_VS_PROJECT_SCRIPT_PATH = ( |
- '$$(ProjectDir)/$VS_PROJECT_TO_MAIN_DIR/' |
- '$COMPONENT_VS_PROJECT_SCRIPT_NAME'), |
- COMPONENT_VS_PROJECT_SUFFIX='.vcproj', |
- |
- COMPONENT_VS_SOLUTION_DIR='$DESTINATION_ROOT/solution', |
- COMPONENT_VS_SOLUTION_SUFFIX='.sln', |
- COMPONENT_VS_ENABLED_PROJECTS=['$COMPONENT_VS_SCONS_DEFAULT_TARGETS'], |
- |
- COMPONENT_VS_SOURCE_SUFFIXES=['$CPPSUFFIXES', '.rc', '.scons'], |
- COMPONENT_VS_SOURCE_FOLDERS=[('source', '$MAIN_DIR')], |
- ) |
- |
- AddTargetGroup('all_solutions', 'solutions can be built') |
- |
- # Add builders |
- vcprojaction = SCons.Script.Action(ComponentVSProjectBuilder, varlist=[ |
- 'COMPONENT_VS_PROJECT_SCRIPT_PATH', |
- 'TARGET_NAME', |
- 'TARGET_PATH', |
- ]) |
- vcprojbuilder = SCons.Script.Builder( |
- action=vcprojaction, |
- suffix='$COMPONENT_VS_PROJECT_SUFFIX') |
- |
- source_vcproj_action = SCons.Script.Action( |
- ComponentVSSourceProjectBuilder, varlist=[ |
- 'COMPONENT_VS_SOURCE_FOLDERS', |
- 'COMPONENT_VS_SOURCE_SUFFIXES', |
- 'COMPONENT_VS_SOURCE_TARGETS', |
- ]) |
- source_vcproj_builder = SCons.Script.Builder( |
- action=source_vcproj_action, |
- suffix='$COMPONENT_VS_PROJECT_SUFFIX') |
- |
- dir_vcproj_action = SCons.Script.Action( |
- ComponentVSDirProjectBuilder, varlist=[ |
- 'COMPONENT_VS_SOURCE_FOLDERS', |
- 'COMPONENT_VS_SOURCE_SUFFIXES', |
- 'PROJECT_SOURCES', |
- ]) |
- dir_vcproj_builder = SCons.Script.Builder( |
- action=dir_vcproj_action, |
- suffix='$COMPONENT_VS_PROJECT_SUFFIX') |
- |
- slnaction = SCons.Script.Action(ComponentVSSolutionBuilder, varlist=[ |
- 'COMPONENT_VS_ENABLED_PROJECTS', |
- 'SOLUTION_FOLDERS', |
- 'SOLUTION_PROJECTS', |
- 'SOLUTION_TARGETS', |
- ]) |
- slnbuilder = SCons.Script.Builder( |
- action=slnaction, |
- suffix='$COMPONENT_VS_SOLUTION_SUFFIX') |
- |
- env.Append(BUILDERS={ |
- 'ComponentVSDirProjectBuilder': dir_vcproj_builder, |
- 'ComponentVSProjectBuilder': vcprojbuilder, |
- 'ComponentVSSolutionBuilder': slnbuilder, |
- 'ComponentVSSourceProjectBuilder': source_vcproj_builder, |
- }) |