| Index: site_scons/site_tools/component_targets_msvs.py
|
| ===================================================================
|
| --- site_scons/site_tools/component_targets_msvs.py (revision 5002)
|
| +++ site_scons/site_tools/component_targets_msvs.py (working copy)
|
| @@ -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 GetGuidFromVSProject(project_path):
|
| +def GetGuidAndNameFromVSProject(project_path):
|
| """Reads the GUID from a Visual Studio project file.
|
|
|
| Args:
|
| @@ -73,6 +75,7 @@
|
|
|
| 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 @@
|
| 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 @@
|
| #------------------------------------------------------------------------------
|
|
|
|
|
| +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 @@
|
| 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)
|
| + vsp = VSProjectWriter(project_file)
|
| + vsp.Create(target_name)
|
|
|
| - 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)
|
| -
|
| + # 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
|
|
|
| - n_files = doc.createElement('Files')
|
| - n_root.appendChild(n_files)
|
| + vsp.AddConfig(mode, attrs, tool_attrs)
|
| +
|
| # 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 @@
|
| #------------------------------------------------------------------------------
|
|
|
|
|
| +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 @@
|
| 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')
|
|
|
| @@ -259,17 +712,19 @@
|
| ))
|
|
|
| # 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 @@
|
| 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 @@
|
| 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 @@
|
| 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 = []
|
| - # Expand target_names into project names, and create project-to-folder
|
| - # mappings
|
| if target_names:
|
| # Expand target_names into project names
|
| for target in target_names:
|
| @@ -420,8 +919,10 @@
|
| # 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 @@
|
| 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,
|
| })
|
|
|