Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(516)

Side by Side Diff: site_scons/site_tools/component_targets_msvs.py

Issue 10210: Bringing in sct changes. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 12 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | site_scons/site_tools/gather_inputs.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python2.4 1 #!/usr/bin/python2.4
2 # Copyright 2008, Google Inc. 2 # Copyright 2008, Google Inc.
3 # All rights reserved. 3 # All rights reserved.
4 # 4 #
5 # Redistribution and use in source and binary forms, with or without 5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are 6 # modification, are permitted provided that the following conditions are
7 # met: 7 # met:
8 # 8 #
9 # * Redistributions of source code must retain the above copyright 9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer. 10 # notice, this list of conditions and the following disclaimer.
(...skipping 12 matching lines...) Expand all
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 30
31 """Visual Studio solution output of component targets for SCons.""" 31 """Visual Studio solution output of component targets for SCons."""
32 32
33 import copy
33 import md5 34 import md5
35 import os
34 import sys 36 import sys
35 import xml.dom 37 import xml.dom
36 import xml.dom.minidom 38 import xml.dom.minidom
37 import SCons 39 import SCons
38 40
39 41
40 #------------------------------------------------------------------------------ 42 #------------------------------------------------------------------------------
41 43
42 44
43 def MakeGuid(name, seed='component_targets_msvs'): 45 def MakeGuid(name, seed='component_targets_msvs'):
(...skipping 14 matching lines...) Expand all
58 # Calculate a MD5 signature for the seed and name. 60 # Calculate a MD5 signature for the seed and name.
59 d = md5.new(str(seed) + str(name)).hexdigest().upper() 61 d = md5.new(str(seed) + str(name)).hexdigest().upper()
60 # Convert most of the signature to GUID form (discard the rest) 62 # Convert most of the signature to GUID form (discard the rest)
61 guid = ('{' + d[:8] + '-' + d[8:12] + '-' + d[12:16] + '-' + d[16:20] 63 guid = ('{' + d[:8] + '-' + d[8:12] + '-' + d[12:16] + '-' + d[16:20]
62 + '-' + d[20:32] + '}') 64 + '-' + d[20:32] + '}')
63 return guid 65 return guid
64 66
65 #------------------------------------------------------------------------------ 67 #------------------------------------------------------------------------------
66 68
67 69
68 def GetGuidFromVSProject(project_path): 70 def GetGuidAndNameFromVSProject(project_path):
69 """Reads the GUID from a Visual Studio project file. 71 """Reads the GUID from a Visual Studio project file.
70 72
71 Args: 73 Args:
72 project_path: Path to the Visual Studio project file. 74 project_path: Path to the Visual Studio project file.
73 75
74 Returns: 76 Returns:
75 The GUID string from the file. 77 The GUID string from the file.
78 The project name from the file.
76 """ 79 """
77 doc = xml.dom.minidom.parse(project_path) 80 doc = xml.dom.minidom.parse(project_path)
78 try: 81 try:
79 n_root = doc.documentElement 82 n_root = doc.documentElement
80 if n_root.nodeName != 'VisualStudioProject': 83 if n_root.nodeName != 'VisualStudioProject':
81 raise SCons.Errors.UserError('%s is not a Visual Studio project.' % 84 raise SCons.Errors.UserError('%s is not a Visual Studio project.' %
82 project_path) 85 project_path)
83 return str(n_root.attributes['ProjectGUID'].nodeValue) 86 return (
87 str(n_root.attributes['ProjectGUID'].nodeValue),
88 str(n_root.attributes['Name'].nodeValue),
89 )
84 finally: 90 finally:
85 # Clean up doc 91 # Clean up doc
86 doc.unlink() 92 doc.unlink()
87 93
88 #------------------------------------------------------------------------------ 94 #------------------------------------------------------------------------------
89 95
90 96
97 class VSProjectWriter(object):
98 """Visual Studio XML project writer."""
99
100 def __init__(self, project_path):
101 """Initializes the project.
102
103 Args:
104 project_path: Path to the project file.
105 """
106 self.project_path = project_path
107 self.doc = None
108
109 def Create(self, name):
110 """Creates the project document.
111
112 Args:
113 name: Name of the project.
114 """
115 self.name = name
116 self.guid = MakeGuid(name)
117
118 # Create XML doc
119 xml_impl = xml.dom.getDOMImplementation()
120 self.doc = xml_impl.createDocument(None, 'VisualStudioProject', None)
121
122 # Add attributes to root element
123 self.n_root = self.doc.documentElement
124 self.n_root.setAttribute('ProjectType', 'Visual C++')
125 self.n_root.setAttribute('Version', '8.00')
126 self.n_root.setAttribute('Name', self.name)
127 self.n_root.setAttribute('ProjectGUID', self.guid)
128 self.n_root.setAttribute('RootNamespace', self.name)
129 self.n_root.setAttribute('Keyword', 'MakeFileProj')
130
131 # Add platform list
132 n_platform = self.doc.createElement('Platforms')
133 self.n_root.appendChild(n_platform)
134 n = self.doc.createElement('Platform')
135 n.setAttribute('Name', 'Win32')
136 n_platform.appendChild(n)
137
138 # Add empty ToolFiles section
139 self.n_root.appendChild(self.doc.createElement('ToolFiles'))
140
141 # Add configurations section
142 self.n_configs = self.doc.createElement('Configurations')
143 self.n_root.appendChild(self.n_configs)
144
145 # Add files section
146 self.n_files = self.doc.createElement('Files')
147 self.n_root.appendChild(self.n_files)
148
149 # Add empty Globals section
150 self.n_root.appendChild(self.doc.createElement('Globals'))
151
152 def AddConfig(self, name, attrs, tool_attrs):
153 """Adds a configuration to the project.
154
155 Args:
156 name: Configuration name.
157 attrs: Dict of configuration attributes.
158 tool_attrs: Dict of tool attributes.
159 """
160 # Add configuration node
161 n_config = self.doc.createElement('Configuration')
162 n_config.setAttribute('Name', '%s|Win32' % name)
163 n_config.setAttribute('ConfigurationType', '0')
164 for k, v in attrs.items():
165 n_config.setAttribute(k, v)
166 self.n_configs.appendChild(n_config)
167
168 # Add tool node
169 n_tool = self.doc.createElement('Tool')
170 n_tool.setAttribute('Name', 'VCNMakeTool')
171 n_tool.setAttribute('IncludeSearchPath', '')
172 n_tool.setAttribute('ForcedIncludes', '')
173 n_tool.setAttribute('AssemblySearchPath', '')
174 n_tool.setAttribute('ForcedUsingAssemblies', '')
175 n_tool.setAttribute('CompileAsManaged', '')
176 n_tool.setAttribute('PreprocessorDefinitions', '')
177 for k, v in tool_attrs.items():
178 n_tool.setAttribute(k, v)
179 n_config.appendChild(n_tool)
180
181 def _WalkFolders(self, folder_dict, parent):
182 """Recursively walks the folder tree.
183
184 Args:
185 folder_dict: Dict of folder entries. Entry is
186 either subdir_name:subdir_dict or relative_path_to_file:None.
187 parent: Parent node (folder node for that dict)
188 """
189 entries = folder_dict.keys()
190 entries.sort()
191 for e in entries:
192 if folder_dict[e]:
193 # Folder
194 n_subfolder = self.doc.createElement('Filter')
195 n_subfolder.setAttribute('Name', e)
196 parent.appendChild(n_subfolder)
197 self._WalkFolders(folder_dict[e], n_subfolder)
198 else:
199 # File
200 n_file = self.doc.createElement('File')
201 n_file.setAttribute('RelativePath', e)
202 parent.appendChild(n_file)
203
204 def AddFiles(self, name, files_dict):
205 """Adds files to the project.
206
207 Args:
208 name: Name of the folder. If None, files/folders will be added directly
209 to the files list.
210 files_dict: A dict of files / folders.
211
212 Within the files_dict:
213 * A file entry is relative_path:None
214 * A folder entry is folder_name:files_dict, where files_dict is another
215 dict of this form.
216 """
217 # Create subfolder if necessary
218 if name:
219 n_folder = self.doc.createElement('Filter')
220 n_folder.setAttribute('Name', name)
221 self.n_files.appendChild(n_folder)
222 else:
223 n_folder = self.n_files
224
225 # Recursively add files to the folder
226 self._WalkFolders(files_dict, n_folder)
227
228 def Write(self):
229 """Writes the project file."""
230 f = open(self.project_path, 'wt')
231 self.doc.writexml(f, encoding='Windows-1252', addindent=' ', newl='\n')
232 f.close()
233
234 #------------------------------------------------------------------------------
235
236
91 def ComponentVSProjectBuilder(target, source, env): 237 def ComponentVSProjectBuilder(target, source, env):
92 """Visual Studio project builder. 238 """Visual Studio project builder.
93 239
94 Args: 240 Args:
95 target: Destination file. 241 target: Destination file.
96 source: List of sources to be added to the target. 242 source: List of sources to be added to the target.
97 env: Environment context. 243 env: Environment context.
98 244
99 Returns: 245 Returns:
100 Zero if successful. 246 Zero if successful.
101 """ 247 """
102 source = source # Silence gpylint 248 source = source # Silence gpylint
103 249
104 target_name = env['TARGET_NAME'] 250 target_name = env['TARGET_NAME']
105 project_file = target[0].path 251 project_file = target[0].path
106 project_to_main = env.RelativePath(target[0].dir, env.Dir('$MAIN_DIR'), 252 project_to_main = env.RelativePath(target[0].dir, env.Dir('$MAIN_DIR'),
107 sep='/') 253 sep='/')
108 254
109 env_hammer_bat = env.Clone(VS_PROJECT_TO_MAIN_DIR=project_to_main) 255 env_hammer_bat = env.Clone(VS_PROJECT_TO_MAIN_DIR=project_to_main)
110 hammer_bat = env_hammer_bat.subst('$COMPONENT_VS_PROJECT_SCRIPT_PATH', raw=1) 256 hammer_bat = env_hammer_bat.subst('$COMPONENT_VS_PROJECT_SCRIPT_PATH', raw=1)
111 257
112 # Project header 258 vsp = VSProjectWriter(project_file)
113 xml_impl = xml.dom.getDOMImplementation() 259 vsp.Create(target_name)
114 doc = xml_impl.createDocument(None, 'VisualStudioProject', None)
115 260
116 n_root = doc.documentElement 261 # Add configuration per build mode supported by this target
117 n_root.setAttribute('ProjectType', 'Visual C++')
118 n_root.setAttribute('Version', '8.00')
119 n_root.setAttribute('Name', target_name)
120 n_root.setAttribute('ProjectGUID', MakeGuid(target_name))
121 n_root.setAttribute('RootNamespace', target_name)
122 n_root.setAttribute('Keyword', 'MakeFileProj')
123
124 n_platform = doc.createElement('Platforms')
125 n_root.appendChild(n_platform)
126 n = doc.createElement('Platform')
127 n.setAttribute('Name', 'Win32')
128 n_platform.appendChild(n)
129
130 n_root.appendChild(doc.createElement('ToolFiles'))
131
132 # One configuration per build mode supported by this target
133 n_configs = doc.createElement('Configurations')
134 n_root.appendChild(n_configs)
135
136 target_path = env['TARGET_PATH'] 262 target_path = env['TARGET_PATH']
137 for mode, path in target_path.items(): 263 for mode, path in target_path.items():
138 n_config = doc.createElement('Configuration') 264 attrs = {}
139 n_config.setAttribute('Name', '%s|Win32' % mode) 265 attrs['OutputDirectory'] = '$(ProjectDir)/%s/%s/out' % (mode, target_name)
140 n_config.setAttribute('OutputDirectory', 266 attrs['IntermediateDirectory'] = ('$(ProjectDir)/%s/%s/tmp' %
141 '$(ProjectDir)/%s/%s/out' % (mode, target_name)) 267 (mode, target_name))
142 n_config.setAttribute('IntermediateDirectory',
143 '$(ProjectDir)/%s/%s/tmp' % (mode, target_name))
144 n_config.setAttribute('ConfigurationType', '0')
145 n_configs.appendChild(n_config)
146 268
147 n_tool = doc.createElement('Tool') 269 tool_attrs = {}
148 n_tool.setAttribute('Name', 'VCNMakeTool')
149 n_tool.setAttribute('IncludeSearchPath', '')
150 n_tool.setAttribute('ForcedIncludes', '')
151 n_tool.setAttribute('AssemblySearchPath', '')
152 n_tool.setAttribute('ForcedUsingAssemblies', '')
153 n_tool.setAttribute('CompileAsManaged', '')
154 n_tool.setAttribute('PreprocessorDefinitions', '')
155 if path: 270 if path:
156 n_tool.setAttribute( 271 tool_attrs['Output'] = env.RelativePath(target[0].dir,
157 'Output', env.RelativePath(target[0].dir, env.Entry(path), sep='/')) 272 env.Entry(path), sep='/')
158 build_cmd = '%s --mode=%s %s' % (hammer_bat, mode, target_name) 273 build_cmd = '%s --mode=%s %s' % (hammer_bat, mode, target_name)
159 clean_cmd = '%s --mode=%s -c %s' % (hammer_bat, mode, target_name) 274 clean_cmd = '%s --mode=%s -c %s' % (hammer_bat, mode, target_name)
160 n_tool.setAttribute('BuildCommandLine', build_cmd) 275 tool_attrs['BuildCommandLine'] = build_cmd
161 n_tool.setAttribute('CleanCommandLine', clean_cmd) 276 tool_attrs['CleanCommandLine'] = clean_cmd
162 n_tool.setAttribute('ReBuildCommandLine', clean_cmd + ' && ' + build_cmd) 277 tool_attrs['ReBuildCommandLine'] = clean_cmd + ' && ' + build_cmd
163 n_config.appendChild(n_tool)
164 278
165 n_files = doc.createElement('Files') 279 vsp.AddConfig(mode, attrs, tool_attrs)
166 n_root.appendChild(n_files) 280
167 # TODO(rspangler): Fill in files - at least, the .scons file invoking the 281 # TODO(rspangler): Fill in files - at least, the .scons file invoking the
168 # target. 282 # target.
169 283
170 n_root.appendChild(doc.createElement('Globals')) 284 # Write project
171 285 vsp.Write()
172 f = open(project_file, 'wt')
173 doc.writexml(f, encoding='Windows-1252', addindent=' ', newl='\n')
174 f.close()
175
176 return 0 286 return 0
177 287
178 288
179 def ComponentVSProject(self, target_name, **kwargs): 289 def ComponentVSProject(self, target_name, **kwargs):
180 """Visual Studio project pseudo-builder for the specified target. 290 """Visual Studio project pseudo-builder for the specified target.
181 291
182 Args: 292 Args:
183 self: Environment context. 293 self: Environment context.
184 target_name: Name of the target. 294 target_name: Name of the target.
185 kwargs: Optional keyword arguments override environment variables in the 295 kwargs: Optional keyword arguments override environment variables in the
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
218 for mode in GetTargetModes(): 328 for mode in GetTargetModes():
219 env.Append(TARGET_PATH={mode: None}) 329 env.Append(TARGET_PATH={mode: None})
220 330
221 # Call the real builder 331 # Call the real builder
222 return env.ComponentVSProjectBuilder( 332 return env.ComponentVSProjectBuilder(
223 '$COMPONENT_VS_PROJECT_DIR/${TARGET_NAME}', []) 333 '$COMPONENT_VS_PROJECT_DIR/${TARGET_NAME}', [])
224 334
225 #------------------------------------------------------------------------------ 335 #------------------------------------------------------------------------------
226 336
227 337
338 class SourceWalker(object):
339 """Iterator for walking a node tree and collecting sources.
340
341 This is depth-first, children are visited before the parent. The object
342 can be initialized with any node, and returns the next node on the descent
343 with each Next() call.
344
345 This class does not get caught in node cycles caused, for example, by C
346 header file include loops.
347
348 Based on SCons.Node.Walker.
349 """
350
351 def __init__(self, node, seen, print_interval = 1000):
352 """Initializes the source walker.
353
354 Args:
355 node: Node to start walk from.
356 seen: Set to add seen nodes to, if not None.
357 print_interval: Interval in nodes examined at which to print a progress
358 indicator.
359 """
360 self.interval = print_interval
361 # Put node on stack
362 self.stack = [node]
363 # Scan for node's children, then copy them to node.wkids
364 node.wkids = copy.copy(node.children(scan=1))
365 # Keep track of nodes we've seen (returned)
366 if seen is None:
367 seen = set()
368 self.seen = seen
369 # Add node to history for cycle detection
370 self.history = set()
371 self.history.add(node)
372 # We've seen one node now
373 self.nodes_examined = 1
374 self.unique_nodes = 1
375
376
377 def Next(self):
378 """Get the next node for this walk of the tree.
379
380 Returns:
381 The next node, or None if no more nodes.
382
383 This function is intentionally iterative, not recursive, to sidestep any
384 issues of stack size limitations.
385 """
386 while self.stack:
387 if self.stack[-1].wkids:
388 # Node has children we haven't examined, so iterate into the first
389 # child
390 node = self.stack[-1].wkids.pop(0)
391 if not self.stack[-1].wkids:
392 # No more children of this node
393 self.stack[-1].wkids = None
394 self.nodes_examined += 1
395 if self.interval and not self.nodes_examined % self.interval:
396 self.PrintProgress()
397 if (node not in self.history) and (node not in self.seen):
398 # Haven't hit a cycle or a node we've already seen
399 node.wkids = copy.copy(node.children(scan=1))
400 self.stack.append(node)
401 self.history.add(node)
402 else:
403 # Coming back from iterating, so return the next node on the stack.
404 node = self.stack.pop()
405 self.history.remove(node)
406 self.seen.add(node)
407 self.unique_nodes += 1
408 return node
409 return None
410
411 def PrintProgress(self):
412 """Prints a progress indicator."""
413 print ' Examined %d nodes, found %d unique...' % (
414 self.nodes_examined, self.unique_nodes)
415
416 def WalkAll(self):
417 """Walks all nodes in the the tree."""
418 while self.Next():
419 pass
420 if self.interval:
421 self.PrintProgress()
422
423
424 def ComponentVSSourceProjectBuilder(target, source, env):
425 """Visual Studio source project builder.
426
427 Args:
428 target: Destination file.
429 source: List of sources to be added to the target.
430 env: Environment context.
431
432 Returns:
433 Zero if successful.
434 """
435 source = source # Silence gpylint
436
437 target_name = env['PROJECT_NAME']
438 project_file = target[0].path
439 project_dir = target[0].dir
440
441 # Get list of suffixes to include
442 suffixes = env.SubstList2('$COMPONENT_VS_SOURCE_SUFFIXES')
443
444 # Convert source folders to absolute paths
445 folders = []
446 for f in env['COMPONENT_VS_SOURCE_FOLDERS']:
447 # (folder name, folder abspath, dict of contents)
448 folders.append((f[0], env.Dir(f[1]).abspath, {}))
449
450 # TODO(rspangler): Additional enhancements:
451 # * Should be able to specify paths in folder name (i.e., foo/bar) and
452 # create the nested folder nodes ('foo' and 'bar')
453 # * Should be tolerant of a folder being specified more than once with
454 # the same name (probably necessary to support nested folder nodes anyway)
455 # Can probably accomplish both of those by creating a parent fodler dict and
456 # calling WalkFolders() only once.
457 # Create a temporary solution alias to point to all the targets, so we can
458 # make a single call to SourceWalker()
459 tmp_alias = env.Alias('vs_source_project_' + target_name,
460 map(env.Alias, env['COMPONENT_VS_SOURCE_TARGETS']))
461
462 # Scan all targets and add unique nodes to set of sources
463 print ' Scanning dependency tree ...'
464 all_srcs = set()
465 walker = SourceWalker(tmp_alias[0], all_srcs)
466 walker.WalkAll()
467
468 # Walk all sources and build directory trees
469 print ' Building source tree...'
470 for n in all_srcs:
471 if not hasattr(n, 'isfile') or not n.isfile():
472 continue # Not a file
473 if n.has_builder():
474 continue # Not a leaf node
475 if n.suffix not in suffixes:
476 continue # Not a file type we include
477
478 path = n.rfile().abspath
479 for f in folders:
480 if path.startswith(f[1]):
481 if f[0] is None:
482 # Folder name of None is a filter
483 break
484 relpath = path[len(f[1]) + 1:].split(os.sep)
485 folder_dict = f[2]
486 # Recursively add subdirs
487 for pathseg in relpath[:-1]:
488 if pathseg not in folder_dict:
489 folder_dict[pathseg] = {}
490 folder_dict = folder_dict[pathseg]
491 # Add file to last subdir. No dict, since this isn't a subdir
492 folder_dict[env.RelativePath(project_dir, path)] = None
493 break
494
495 print ' Writing project file...'
496
497 vsp = VSProjectWriter(project_file)
498 vsp.Create(target_name)
499
500 # One configuration for all build modes
501 vsp.AddConfig('all', {}, {})
502
503 # Add files
504 for f in folders:
505 if f[0] is None:
506 continue # Skip filters
507 vsp.AddFiles(f[0], f[2])
508
509 vsp.Write()
510 return 0
511
512
513 def ComponentVSSourceProject(self, project_name, target_names, **kwargs):
514 """Visual Studio source project pseudo-builder.
515
516 Args:
517 self: Environment context.
518 project_name: Name of the project.
519 target_names: List of target names to include source for.
520 kwargs: Optional keyword arguments override environment variables in the
521 derived environment used to create the project.
522
523 Returns:
524 A list of output nodes.
525 """
526 # Builder only works on Windows
527 if sys.platform not in ('win32', 'cygwin'):
528 return []
529
530 # Clone environment and add keyword args
531 env = self.Clone()
532 for k, v in kwargs.items():
533 env[k] = v
534
535 # Save the project name and targets
536 env['PROJECT_NAME'] = project_name
537 env['COMPONENT_VS_SOURCE_TARGETS'] = target_names
538
539 # Call the real builder
540 return env.ComponentVSSourceProjectBuilder(
541 '$COMPONENT_VS_PROJECT_DIR/${PROJECT_NAME}', [])
542
543 #------------------------------------------------------------------------------
544
545
546 def FindSources(env, dest, source, suffixes=None):
547 """Recursively finds sources and adds them to the destination set.
548
549 Args:
550 env: Environment context.
551 dest: Set to add source nodes to.
552 source: Source file(s) to find. May be a string, Node, or a list of
553 mixed strings or Nodes. Strings will be passed through env.Glob() to
554 evaluate wildcards. If a source evaluates to a directory, the entire
555 directory will be recursively added.
556 suffixes: List of suffixes to include. If not None, only files with these
557 suffixes will be added to dest.
558 """
559 for source_entry in env.Flatten(source):
560 if type(source_entry) == str:
561 # Search for matches for each source entry
562 source_nodes = env.Glob(source_entry)
563 else:
564 # Source entry is already a file or directory node; no need to glob it
565 source_nodes = [source_entry]
566 for s in source_nodes:
567 if str(s.__class__) == 'SCons.Node.FS.Dir':
568 # Recursively search subdir. Since glob('*') doesn't match dot files,
569 # also glob('.*').
570 FindSources(env, dest, [s.abspath + '/*', s.abspath + '/.*'], suffixes)
571 elif suffixes and s.suffix in suffixes:
572 dest.add(s)
573
574
575 def ComponentVSDirProjectBuilder(target, source, env):
576 """Visual Studio directory project builder.
577
578 Args:
579 target: Destination file.
580 source: List of sources to be added to the target.
581 env: Environment context.
582
583 Returns:
584 Zero if successful.
585 """
586 source = source # Silence gpylint
587
588 target_name = env['PROJECT_NAME']
589 project_file = target[0].path
590 project_dir = target[0].dir
591
592 # Convert source folders to absolute paths
593 folders = []
594 for f in env['COMPONENT_VS_SOURCE_FOLDERS']:
595 # (folder name, folder abspath, dict of contents)
596 folders.append((f[0], env.Dir(f[1]).abspath, {}))
597
598 # Recursively scan source directories
599 print ' Scanning directories for source...'
600 all_srcs = set()
601 FindSources(env, all_srcs, env['PROJECT_SOURCES'],
602 suffixes=env.SubstList2('$COMPONENT_VS_SOURCE_SUFFIXES'))
603
604 # Walk all sources and build directory trees
605 print ' Building source tree...'
606 for n in all_srcs:
607 # Map addRepository'd source to its real location.
608 path = n.rfile().abspath
609 for f in folders:
610 if path.startswith(f[1]):
611 if f[0] is None:
612 # Folder name of None is a filter
613 break
614 relpath = path[len(f[1]) + 1:].split(os.sep)
615 folder_dict = f[2]
616 # Recursively add subdirs
617 for pathseg in relpath[:-1]:
618 if pathseg not in folder_dict:
619 folder_dict[pathseg] = {}
620 folder_dict = folder_dict[pathseg]
621 # Add file to last subdir. No dict, since this isn't a subdir
622 folder_dict[env.RelativePath(project_dir, path)] = None
623 break
624
625 print ' Writing project file...'
626
627 vsp = VSProjectWriter(project_file)
628 vsp.Create(target_name)
629
630 # One configuration for all build modes
631 vsp.AddConfig('all', {}, {})
632
633 # Add files
634 for f in folders:
635 if f[0] is None:
636 continue # Skip filters
637 vsp.AddFiles(f[0], f[2])
638
639 vsp.Write()
640 return 0
641
642
643 def ComponentVSDirProject(self, project_name, source, **kwargs):
644 """Visual Studio directory project pseudo-builder.
645
646 Args:
647 self: Environment context.
648 project_name: Name of the project.
649 source: List of source files and/or directories.
650 kwargs: Optional keyword arguments override environment variables in the
651 derived environment used to create the project.
652
653 Returns:
654 A list of output nodes.
655 """
656 # Builder only works on Windows
657 if sys.platform not in ('win32', 'cygwin'):
658 return []
659
660 # Clone environment and add keyword args
661 env = self.Clone()
662 for k, v in kwargs.items():
663 env[k] = v
664
665 # Save the project name and sources
666 env['PROJECT_NAME'] = project_name
667 env['PROJECT_SOURCES'] = source
668
669 # Call the real builder
670 return env.ComponentVSDirProjectBuilder(
671 '$COMPONENT_VS_PROJECT_DIR/${PROJECT_NAME}', [])
672
673 #------------------------------------------------------------------------------
674
675
228 def ComponentVSSolutionBuilder(target, source, env): 676 def ComponentVSSolutionBuilder(target, source, env):
229 """Visual Studio solution builder. 677 """Visual Studio solution builder.
230 678
231 Args: 679 Args:
232 target: Destination file. 680 target: Destination file.
233 source: List of sources to be added to the target. 681 source: List of sources to be added to the target.
234 env: Environment context. 682 env: Environment context.
235 683
236 Returns: 684 Returns:
237 Zero if successful. 685 Zero if successful.
238 """ 686 """
239 source = source # Silence gpylint 687 source = source # Silence gpylint
240 688
241 solution_file = target[0].path 689 solution_file = target[0].path
242 projects = env['SOLUTION_PROJECTS'] 690 projects = env['SOLUTION_PROJECTS']
243 folders = env['SOLUTION_FOLDERS'] 691 folders = env['SOLUTION_FOLDERS']
244 692
693 # Scan externally-generated projects
694 external_projects = []
695 for p in source:
696 guid, name = GetGuidAndNameFromVSProject(p.abspath)
697 external_projects.append((p, name, guid))
698
245 f = open(solution_file, 'wt') 699 f = open(solution_file, 'wt')
246
247 f.write('Microsoft Visual Studio Solution File, Format Version 9.00\n') 700 f.write('Microsoft Visual Studio Solution File, Format Version 9.00\n')
248 f.write('# Visual Studio 2005\n') 701 f.write('# Visual Studio 2005\n')
249 702
250 # Projects generated by ComponentVSSolution() 703 # Projects generated by ComponentVSSolution()
251 for p in projects: 704 for p in projects:
252 project_file = env.File( 705 project_file = env.File(
253 '$COMPONENT_VS_PROJECT_DIR/%s$COMPONENT_VS_PROJECT_SUFFIX' % p) 706 '$COMPONENT_VS_PROJECT_DIR/%s$COMPONENT_VS_PROJECT_SUFFIX' % p)
254 f.write('Project("%s") = "%s", "%s", "%s"\nEndProject\n' % ( 707 f.write('Project("%s") = "%s", "%s", "%s"\nEndProject\n' % (
255 '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}', # External makefile GUID 708 '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}', # External makefile GUID
256 p, # Project name 709 p, # Project name
257 env.RelativePath(target[0].dir, project_file), # Path to project file 710 env.RelativePath(target[0].dir, project_file), # Path to project file
258 MakeGuid(p), # Project GUID 711 MakeGuid(p), # Project GUID
259 )) 712 ))
260 713
261 # Projects generated elsewhere 714 # Projects generated elsewhere
262 for p in source: 715 for p, name, guid in external_projects:
263 f.write('Project("%s") = "%s", "%s", "%s"\nEndProject\n' % ( 716 f.write('Project("%s") = "%s", "%s", "%s"\nEndProject\n' % (
264 # TODO(rspangler): What if this project isn't type external makefile? 717 # TODO(rspangler): What if this project isn't type external makefile?
265 # How to tell what type it is? 718 # How to tell what type it is?
266 '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}', # External makefile GUID 719 '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}', # External makefile GUID
267 p, # Project name 720 name, # Project name
268 env.RelativePath(target[0].dir, p), # Path to project file 721 env.RelativePath(target[0].dir, p), # Path to project file
269 GetGuidFromVSProject(p.abspath), # Project GUID 722 guid, # Project GUID
270 )) 723 ))
271 724
272 # Folders from build groups 725 # Folders from build groups
726 # TODO(rspangler): Currently no way to add external project (specified in
727 # sources) to a solution folder.
273 for folder in folders: 728 for folder in folders:
274 f.write('Project("%s") = "%s", "%s", "%s"\nEndProject\n' % ( 729 f.write('Project("%s") = "%s", "%s", "%s"\nEndProject\n' % (
275 '{2150E333-8FDC-42A3-9474-1A3956D46DE8}', # Solution folder GUID 730 '{2150E333-8FDC-42A3-9474-1A3956D46DE8}', # Solution folder GUID
276 folder, # Folder name 731 folder, # Folder name
277 folder, # Folder name (again) 732 folder, # Folder name (again)
278 # Use a different seed so the folder won't get the same GUID as a 733 # Use a different seed so the folder won't get the same GUID as a
279 # project. 734 # project.
280 MakeGuid(folder, seed='folder'), # Project GUID 735 MakeGuid(folder, seed='folder'), # Project GUID
281 )) 736 ))
282 737
283 f.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n') 738 f.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
284 for mode in GetTargetModes(): 739 for mode in GetTargetModes():
285 f.write('\t\t%s|Win32 = %s|Win32\n' % (mode, mode)) 740 f.write('\t\t%s|Win32 = %s|Win32\n' % (mode, mode))
286 f.write('\tEndGlobalSection\n') 741 f.write('\tEndGlobalSection\n')
287 742
743 # Determine which projects should be enabled
744 # TODO(rspangler): This is somewhat clunky. DEFAULT_TARGETS is global, and
745 # what we really need is something mode-specific. In theory we could make
746 # this a mode-specific dict rather than a list, but that'd also be a pain to
747 # populate.
748 # These variable names are also getting REALLY long. Perhaps we should
749 # define shorter ones (with the default value redirecting to the longer
750 # ones for legacy compatibility).
751 enable_projects = env.SubstList2('$COMPONENT_VS_ENABLED_PROJECTS')
752
288 f.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n') 753 f.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
754
755 # Projects generated by ComponentVSSolution()
289 for p in projects: 756 for p in projects:
290 for mode in GetTargetModes(): 757 for mode in GetTargetModes():
291 f.write('\t\t%s.%s|Win32.ActiveCfg = %s|Win32\n' % ( 758 f.write('\t\t%s.%s|Win32.ActiveCfg = %s|Win32\n' % (
292 MakeGuid(p), # Project GUID 759 MakeGuid(p), # Project GUID
293 mode, # Solution build configuration 760 mode, # Solution build configuration
294 mode, # Project build config for that solution config 761 mode, # Project build config for that solution config
295 )) 762 ))
763
296 t = GetTargets().get(p) 764 t = GetTargets().get(p)
297 if t and mode in t.mode_properties: 765
766 # Determine if project should be enabled in this mode
767 enabled = t and mode in t.mode_properties
768 if enable_projects and p not in enable_projects:
769 # Enable list specified, but target isn't in it
770 # TODO(rspangler): Since we env.Default(scons-out) elsewhere, this
771 # likely causes all projects to be disabled by default. But that's
772 # realistically better than enabling them all...
773 enabled = False
774
775 if enabled:
298 # Target can be built in this mode 776 # Target can be built in this mode
299 f.write('\t\t%s.%s|Win32.Build.0 = %s|Win32\n' % ( 777 f.write('\t\t%s.%s|Win32.Build.0 = %s|Win32\n' % (
300 MakeGuid(p), # Project GUID 778 MakeGuid(p), # Project GUID
301 mode, # Solution build configuration 779 mode, # Solution build configuration
302 mode, # Project build config for that solution config 780 mode, # Project build config for that solution config
303 )) 781 ))
782
783 # Projects generated elsewhere
784 for p, name, guid in external_projects:
785 for mode in GetTargetModes():
786 f.write('\t\t%s.%s|Win32.ActiveCfg = %s|Win32\n' % (
787 guid, # Project GUID
788 mode, # Solution build configuration
789 mode, # Project build config for that solution config
790 ))
791
792 if name in enable_projects or not enable_projects:
793 # Build target in this mode
794 f.write('\t\t%s.%s|Win32.Build.0 = %s|Win32\n' % (
795 guid, # Project GUID
796 mode, # Solution build configuration
797 mode, # Project build config for that solution config
798 ))
799
304 f.write('\tEndGlobalSection\n') 800 f.write('\tEndGlobalSection\n')
305 801
306 f.write('\tGlobalSection(SolutionProperties) = preSolution\n') 802 f.write('\tGlobalSection(SolutionProperties) = preSolution\n')
307 f.write('\t\tHideSolutionNode = FALSE\n') 803 f.write('\t\tHideSolutionNode = FALSE\n')
308 f.write('\tEndGlobalSection\n') 804 f.write('\tEndGlobalSection\n')
309 805
310 if folders: 806 if folders:
311 f.write('\tGlobalSection(NestedProjects) = preSolution\n') 807 f.write('\tGlobalSection(NestedProjects) = preSolution\n')
312 for p, folder in projects.items(): 808 for p, folder in projects.items():
313 f.write('\t\t%s = %s\n' % (MakeGuid(p), MakeGuid(folder, seed='folder'))) 809 f.write('\t\t%s = %s\n' % (MakeGuid(p), MakeGuid(folder, seed='folder')))
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
354 env[k] = v 850 env[k] = v
355 851
356 # Save the target name 852 # Save the target name
357 env['SOLUTION_NAME'] = solution_name 853 env['SOLUTION_NAME'] = solution_name
358 854
359 # Get list of targets to make projects for. At this point we haven't 855 # Get list of targets to make projects for. At this point we haven't
360 # determined whether they're groups or targets. 856 # determined whether they're groups or targets.
361 target_names = env.SubstList2(target_names) 857 target_names = env.SubstList2(target_names)
362 env['SOLUTION_TARGETS'] = target_names 858 env['SOLUTION_TARGETS'] = target_names
363 859
860 # Save the default targets list as an environment variable
861 env['COMPONENT_VS_SCONS_DEFAULT_TARGETS'] = SCons.Script.DEFAULT_TARGETS
862
863 # Expand target_names into project names, and create project-to-folder
864 # mappings
364 project_names = {} 865 project_names = {}
365 folders = [] 866 folders = []
366 # Expand target_names into project names, and create project-to-folder
367 # mappings
368 if target_names: 867 if target_names:
369 # Expand target_names into project names 868 # Expand target_names into project names
370 for target in target_names: 869 for target in target_names:
371 if target in GetTargetGroups(): 870 if target in GetTargetGroups():
372 # Add target to folders 871 # Add target to folders
373 folders.append(target) 872 folders.append(target)
374 # Add all project_names in the group 873 # Add all project_names in the group
375 for t in GetTargetGroups()[target].GetTargetNames(): 874 for t in GetTargetGroups()[target].GetTargetNames():
376 project_names[t] = target 875 project_names[t] = target
377 elif target in GetTargets(): 876 elif target in GetTargets():
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
413 #------------------------------------------------------------------------------ 912 #------------------------------------------------------------------------------
414 913
415 914
416 def generate(env): 915 def generate(env):
417 # NOTE: SCons requires the use of this name, which fails gpylint. 916 # NOTE: SCons requires the use of this name, which fails gpylint.
418 """SCons entry point for this tool.""" 917 """SCons entry point for this tool."""
419 918
420 # Add pseudo-builders to set up the project and solution builders. These 919 # Add pseudo-builders to set up the project and solution builders. These
421 # need to be available on all platforms so that SConscripts which reference 920 # need to be available on all platforms so that SConscripts which reference
422 # them will work. 921 # them will work.
922 env.AddMethod(ComponentVSDirProject)
423 env.AddMethod(ComponentVSProject) 923 env.AddMethod(ComponentVSProject)
424 env.AddMethod(ComponentVSSolution) 924 env.AddMethod(ComponentVSSolution)
925 env.AddMethod(ComponentVSSourceProject)
425 926
426 # If not on Windows, don't do anything else 927 # If not on Windows, don't do anything else
427 if sys.platform not in ('win32', 'cygwin'): 928 if sys.platform not in ('win32', 'cygwin'):
428 return 929 return
429 930
430 # Include tools we need 931 # Include tools we need
431 env.Tool('gather_inputs') 932 env.Tool('gather_inputs')
432 933
433 env.SetDefault( 934 env.SetDefault(
434 COMPONENT_VS_SOLUTION_DIR='$DESTINATION_ROOT/solution',
435 COMPONENT_VS_PROJECT_DIR='$COMPONENT_VS_SOLUTION_DIR/projects', 935 COMPONENT_VS_PROJECT_DIR='$COMPONENT_VS_SOLUTION_DIR/projects',
436 COMPONENT_VS_SOLUTION_SUFFIX='.sln',
437 COMPONENT_VS_PROJECT_SUFFIX='.vcproj',
438 COMPONENT_VS_PROJECT_SCRIPT_NAME = 'hammer.bat', 936 COMPONENT_VS_PROJECT_SCRIPT_NAME = 'hammer.bat',
439 COMPONENT_VS_PROJECT_SCRIPT_PATH = ( 937 COMPONENT_VS_PROJECT_SCRIPT_PATH = (
440 '$$(ProjectDir)/$VS_PROJECT_TO_MAIN_DIR/' 938 '$$(ProjectDir)/$VS_PROJECT_TO_MAIN_DIR/'
441 '$COMPONENT_VS_PROJECT_SCRIPT_NAME'), 939 '$COMPONENT_VS_PROJECT_SCRIPT_NAME'),
940 COMPONENT_VS_PROJECT_SUFFIX='.vcproj',
941
942 COMPONENT_VS_SOLUTION_DIR='$DESTINATION_ROOT/solution',
943 COMPONENT_VS_SOLUTION_SUFFIX='.sln',
944 COMPONENT_VS_ENABLED_PROJECTS=['$COMPONENT_VS_SCONS_DEFAULT_TARGETS'],
945
946 COMPONENT_VS_SOURCE_SUFFIXES=['$CPPSUFFIXES', '.rc', '.scons'],
947 COMPONENT_VS_SOURCE_FOLDERS=[('source', '$MAIN_DIR')],
442 ) 948 )
443 949
444 AddTargetGroup('all_solutions', 'solutions can be built') 950 AddTargetGroup('all_solutions', 'solutions can be built')
445 951
446 # Add builders 952 # Add builders
447 vcprojaction = SCons.Script.Action(ComponentVSProjectBuilder, varlist=[ 953 vcprojaction = SCons.Script.Action(ComponentVSProjectBuilder, varlist=[
954 'COMPONENT_VS_PROJECT_SCRIPT_PATH',
448 'TARGET_NAME', 955 'TARGET_NAME',
449 'TARGET_PATH', 956 'TARGET_PATH',
450 'COMPONENT_VS_PROJECT_SCRIPT_PATH',
451 ]) 957 ])
452 vcprojbuilder = SCons.Script.Builder( 958 vcprojbuilder = SCons.Script.Builder(
453 action=vcprojaction, 959 action=vcprojaction,
454 suffix='$COMPONENT_VS_PROJECT_SUFFIX') 960 suffix='$COMPONENT_VS_PROJECT_SUFFIX')
455 961
962 source_vcproj_action = SCons.Script.Action(
963 ComponentVSSourceProjectBuilder, varlist=[
964 'COMPONENT_VS_SOURCE_FOLDERS',
965 'COMPONENT_VS_SOURCE_SUFFIXES',
966 'COMPONENT_VS_SOURCE_TARGETS',
967 ])
968 source_vcproj_builder = SCons.Script.Builder(
969 action=source_vcproj_action,
970 suffix='$COMPONENT_VS_PROJECT_SUFFIX')
971
972 dir_vcproj_action = SCons.Script.Action(
973 ComponentVSDirProjectBuilder, varlist=[
974 'COMPONENT_VS_SOURCE_FOLDERS',
975 'COMPONENT_VS_SOURCE_SUFFIXES',
976 'PROJECT_SOURCES',
977 ])
978 dir_vcproj_builder = SCons.Script.Builder(
979 action=dir_vcproj_action,
980 suffix='$COMPONENT_VS_PROJECT_SUFFIX')
981
456 slnaction = SCons.Script.Action(ComponentVSSolutionBuilder, varlist=[ 982 slnaction = SCons.Script.Action(ComponentVSSolutionBuilder, varlist=[
457 'SOLUTION_TARGETS', 983 'COMPONENT_VS_ENABLED_PROJECTS',
458 'SOLUTION_FOLDERS', 984 'SOLUTION_FOLDERS',
459 'SOLUTION_PROJECTS', 985 'SOLUTION_PROJECTS',
986 'SOLUTION_TARGETS',
460 ]) 987 ])
461 slnbuilder = SCons.Script.Builder( 988 slnbuilder = SCons.Script.Builder(
462 action=slnaction, 989 action=slnaction,
463 suffix='$COMPONENT_VS_SOLUTION_SUFFIX') 990 suffix='$COMPONENT_VS_SOLUTION_SUFFIX')
464 991
465 env.Append(BUILDERS={ 992 env.Append(BUILDERS={
993 'ComponentVSDirProjectBuilder': dir_vcproj_builder,
466 'ComponentVSProjectBuilder': vcprojbuilder, 994 'ComponentVSProjectBuilder': vcprojbuilder,
467 'ComponentVSSolutionBuilder': slnbuilder, 995 'ComponentVSSolutionBuilder': slnbuilder,
996 'ComponentVSSourceProjectBuilder': source_vcproj_builder,
468 }) 997 })
OLDNEW
« no previous file with comments | « no previous file | site_scons/site_tools/gather_inputs.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698