summaryrefslogtreecommitdiffstats
path: root/site_scons
diff options
context:
space:
mode:
authorbradnelson@google.com <bradnelson@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-07 19:39:17 +0000
committerbradnelson@google.com <bradnelson@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-07 19:39:17 +0000
commit303e2d50925f9c95004d7a49175119f58ef4d78c (patch)
tree44908d1736bf59adba2fd073858dd26fdcaa10a5 /site_scons
parentfe1cf28eda7c4d2219e7bb396079a9941fd6edfd (diff)
downloadchromium_src-303e2d50925f9c95004d7a49175119f58ef4d78c.zip
chromium_src-303e2d50925f9c95004d7a49175119f58ef4d78c.tar.gz
chromium_src-303e2d50925f9c95004d7a49175119f58ef4d78c.tar.bz2
Bringing in sct changes.
Review URL: http://codereview.chromium.org/10210 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@5008 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'site_scons')
-rwxr-xr-xsite_scons/site_tools/component_targets_msvs.py663
-rw-r--r--site_scons/site_tools/gather_inputs.py7
-rw-r--r--site_scons/site_tools/visual_studio_solution.py6
3 files changed, 603 insertions, 73 deletions
diff --git a/site_scons/site_tools/component_targets_msvs.py b/site_scons/site_tools/component_targets_msvs.py
index e86de89..b50f1a5 100755
--- a/site_scons/site_tools/component_targets_msvs.py
+++ b/site_scons/site_tools/component_targets_msvs.py
@@ -30,7 +30,9 @@
"""Visual Studio solution output of component targets for SCons."""
+import copy
import md5
+import os
import sys
import xml.dom
import xml.dom.minidom
@@ -65,7 +67,7 @@ def MakeGuid(name, seed='component_targets_msvs'):
#------------------------------------------------------------------------------
-def GetGuidFromVSProject(project_path):
+def GetGuidAndNameFromVSProject(project_path):
"""Reads the GUID from a Visual Studio project file.
Args:
@@ -73,6 +75,7 @@ def GetGuidFromVSProject(project_path):
Returns:
The GUID string from the file.
+ The project name from the file.
"""
doc = xml.dom.minidom.parse(project_path)
try:
@@ -80,7 +83,10 @@ def GetGuidFromVSProject(project_path):
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)
+ return (
+ str(n_root.attributes['ProjectGUID'].nodeValue),
+ str(n_root.attributes['Name'].nodeValue),
+ )
finally:
# Clean up doc
doc.unlink()
@@ -88,6 +94,146 @@ def GetGuidFromVSProject(project_path):
#------------------------------------------------------------------------------
+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.
@@ -109,70 +255,34 @@ def ComponentVSProjectBuilder(target, source, env):
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)
- # Project header
- xml_impl = xml.dom.getDOMImplementation()
- doc = xml_impl.createDocument(None, 'VisualStudioProject', None)
-
- n_root = doc.documentElement
- n_root.setAttribute('ProjectType', 'Visual C++')
- n_root.setAttribute('Version', '8.00')
- n_root.setAttribute('Name', target_name)
- n_root.setAttribute('ProjectGUID', MakeGuid(target_name))
- n_root.setAttribute('RootNamespace', target_name)
- n_root.setAttribute('Keyword', 'MakeFileProj')
-
- n_platform = doc.createElement('Platforms')
- n_root.appendChild(n_platform)
- n = doc.createElement('Platform')
- n.setAttribute('Name', 'Win32')
- n_platform.appendChild(n)
-
- n_root.appendChild(doc.createElement('ToolFiles'))
-
- # One configuration per build mode supported by this target
- n_configs = doc.createElement('Configurations')
- n_root.appendChild(n_configs)
+ 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():
- n_config = doc.createElement('Configuration')
- n_config.setAttribute('Name', '%s|Win32' % mode)
- n_config.setAttribute('OutputDirectory',
- '$(ProjectDir)/%s/%s/out' % (mode, target_name))
- n_config.setAttribute('IntermediateDirectory',
- '$(ProjectDir)/%s/%s/tmp' % (mode, target_name))
- n_config.setAttribute('ConfigurationType', '0')
- n_configs.appendChild(n_config)
+ attrs = {}
+ attrs['OutputDirectory'] = '$(ProjectDir)/%s/%s/out' % (mode, target_name)
+ attrs['IntermediateDirectory'] = ('$(ProjectDir)/%s/%s/tmp' %
+ (mode, target_name))
- n_tool = 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', '')
+ tool_attrs = {}
if path:
- n_tool.setAttribute(
- 'Output', env.RelativePath(target[0].dir, env.Entry(path), sep='/'))
+ 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)
- n_tool.setAttribute('BuildCommandLine', build_cmd)
- n_tool.setAttribute('CleanCommandLine', clean_cmd)
- n_tool.setAttribute('ReBuildCommandLine', clean_cmd + ' && ' + build_cmd)
- n_config.appendChild(n_tool)
+ tool_attrs['BuildCommandLine'] = build_cmd
+ tool_attrs['CleanCommandLine'] = clean_cmd
+ tool_attrs['ReBuildCommandLine'] = clean_cmd + ' && ' + build_cmd
+
+ vsp.AddConfig(mode, attrs, tool_attrs)
- n_files = doc.createElement('Files')
- n_root.appendChild(n_files)
# TODO(rspangler): Fill in files - at least, the .scons file invoking the
# target.
- n_root.appendChild(doc.createElement('Globals'))
-
- f = open(project_file, 'wt')
- doc.writexml(f, encoding='Windows-1252', addindent=' ', newl='\n')
- f.close()
-
+ # Write project
+ vsp.Write()
return 0
@@ -225,6 +335,344 @@ def ComponentVSProject(self, target_name, **kwargs):
#------------------------------------------------------------------------------
+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.
@@ -242,8 +690,13 @@ def ComponentVSSolutionBuilder(target, source, env):
projects = env['SOLUTION_PROJECTS']
folders = env['SOLUTION_FOLDERS']
- f = open(solution_file, 'wt')
+ # 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')
@@ -259,17 +712,19 @@ def ComponentVSSolutionBuilder(target, source, env):
))
# Projects generated elsewhere
- for p in source:
+ 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
- p, # Project name
+ name, # Project name
env.RelativePath(target[0].dir, p), # Path to project file
- GetGuidFromVSProject(p.abspath), # Project GUID
+ 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
@@ -285,7 +740,19 @@ def ComponentVSSolutionBuilder(target, source, env):
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' % (
@@ -293,14 +760,43 @@ def ComponentVSSolutionBuilder(target, source, env):
mode, # Solution build configuration
mode, # Project build config for that solution config
))
+
t = GetTargets().get(p)
- if t and mode in t.mode_properties:
+
+ # 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')
@@ -361,10 +857,13 @@ def ComponentVSSolution(self, solution_name, target_names, projects=None,
target_names = env.SubstList2(target_names)
env['SOLUTION_TARGETS'] = target_names
- project_names = {}
- folders = []
+ # 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:
@@ -420,8 +919,10 @@ def generate(env):
# 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'):
@@ -431,38 +932,66 @@ def generate(env):
env.Tool('gather_inputs')
env.SetDefault(
- COMPONENT_VS_SOLUTION_DIR='$DESTINATION_ROOT/solution',
COMPONENT_VS_PROJECT_DIR='$COMPONENT_VS_SOLUTION_DIR/projects',
- COMPONENT_VS_SOLUTION_SUFFIX='.sln',
- COMPONENT_VS_PROJECT_SUFFIX='.vcproj',
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',
- 'COMPONENT_VS_PROJECT_SCRIPT_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=[
- 'SOLUTION_TARGETS',
+ '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,
})
diff --git a/site_scons/site_tools/gather_inputs.py b/site_scons/site_tools/gather_inputs.py
index f299fed..6964bee 100644
--- a/site_scons/site_tools/gather_inputs.py
+++ b/site_scons/site_tools/gather_inputs.py
@@ -91,13 +91,14 @@ def GatherInputs(env, target, groups=['.*'], exclude_pattern=None):
_FindSources(ptrns, lst, all)
return
+ # Get real file (backed by repositories).
+ rfile = tgt.rfile()
+ rfile_is_file = rfile.isfile()
# See who it matches
for pattern, lst in ptrns.items():
- # Get real file (backed by repositories).
- rfile = tgt.rfile()
# Add files to the list for the first pattern that matches (implicitly,
# don't add directories).
- if rfile.isfile() and pattern.match(rfile.path):
+ if rfile_is_file and pattern.match(rfile.path):
lst.append(rfile.abspath)
break
diff --git a/site_scons/site_tools/visual_studio_solution.py b/site_scons/site_tools/visual_studio_solution.py
index 1e46767..597b19c 100644
--- a/site_scons/site_tools/visual_studio_solution.py
+++ b/site_scons/site_tools/visual_studio_solution.py
@@ -111,9 +111,9 @@ def Solution(env, solution_name,
variant=variants[0])
# Build Visual Studio Solution file.
- solution =env.MSVSSolution(target=solution_name + env['MSVSSOLUTIONSUFFIX'],
- projects=project_list,
- variant=variants)
+ solution = env.MSVSSolution(target=solution_name + env['MSVSSOLUTIONSUFFIX'],
+ projects=project_list,
+ variant=variants)
# Explicitly add dependencies.
env.Depends(solution, project_list)