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

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

Issue 42667: Remove Hammer files.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 11 years, 8 months 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 | « site_scons/site_tools/MSVSNew.py ('k') | site_scons/site_tools/atlmfc_vc80.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #
2 # __COPYRIGHT__
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #
23
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
25
26 __doc__ = """SCons.Node.MSVS
27
28 New implementation of Visual Studio project generation for SCons.
29 """
30
31 import md5
32 import os
33 import random
34 import re
35 import UserList
36 import xml.dom
37 import xml.dom.minidom
38
39 import SCons.Node.FS
40 import SCons.Script
41 import SCons.Util
42
43 from SCons.Debug import Trace
44 TODO = 0
45
46 # Initialize random number generator
47 random.seed()
48
49
50 #------------------------------------------------------------------------------
51 # Entry point for supplying a fixed map of GUIDs for testing.
52
53 GUIDMap = {}
54
55
56 #------------------------------------------------------------------------------
57 # Helper functions
58
59
60 def MakeGuid(name, seed='msvs_new'):
61 """Returns a GUID for the specified target name.
62
63 Args:
64 name: Target name.
65 seed: Seed for MD5 hash.
66 Returns:
67 A GUID-line string calculated from the name and seed.
68
69 This generates something which looks like a GUID, but depends only on the
70 name and seed. This means the same name/seed will always generate the same
71 GUID, so that projects and solutions which refer to each other can explicitly
72 determine the GUID to refer to explicitly. It also means that the GUID will
73 not change when the project for a target is rebuilt.
74 """
75 # Calculate a MD5 signature for the seed and name.
76 d = md5.new(str(seed) + str(name)).hexdigest().upper()
77 # Convert most of the signature to GUID form (discard the rest)
78 guid = ('{' + d[:8] + '-' + d[8:12] + '-' + d[12:16] + '-' + d[16:20]
79 + '-' + d[20:32] + '}')
80 return guid
81
82
83 #------------------------------------------------------------------------------
84 # Global look up of string names.
85
86 class LookupError(Exception):
87 def __str__(self):
88 string, expanded = self.args
89 if string == expanded:
90 return string
91 else:
92 return '%s (%s)' % (string, expanded)
93
94 _lookup_dict = {}
95
96 def LookupAdd(item, result):
97 _lookup_dict[item] = result
98 _lookup_dict[result] = result
99
100 def Lookup(item):
101 """Looks up an MSVS item in the global dictionary.
102
103 Args:
104 item: A path (string) or instance for looking up.
105 Returns:
106 An instance from the global _lookup_dict.
107
108 Raises an exception if the item does not exist in the _lookup_dict.
109 """
110 global _lookup_dict
111 try:
112 return _lookup_dict[item]
113 except KeyError:
114 return SCons.Node.FS.default_fs.Entry(item, create=False)
115
116 def LookupCreate(klass, item, *args, **kw):
117 """Looks up an MSVS item, creating it if it doesn't already exist.
118
119 Args:
120 klass: The class of item being looked up, or created if it
121 doesn't already exist in the global _lookup_dict.
122 item: The a string (or instance) being looked up.
123 *args: positional arguments passed to the klass.initialize() method.
124 **kw: keyword arguments passed to the klass.initialize() method.
125 Returns:
126 An instance from the global _lookup_dict, or None if the item does
127 not exist in the _lookup_dict.
128
129 This raises a LookupError if the found instance doesn't match the
130 requested klass.
131
132 When creating a new instance, this populates the _lookup_dict with
133 both the item and the instance itself as keys, so that looking up
134 the instance will return itself.
135 """
136 global _lookup_dict
137 result = _lookup_dict.get(item)
138 if result:
139 if not isinstance(result, klass):
140 raise LookupError, "tried to redefine %s as a %s" % (item, klass)
141 return result
142 result = klass()
143 result.initialize(item, *args, **kw)
144 LookupAdd(item, result)
145 return result
146
147
148 #------------------------------------------------------------------------------
149
150 class FileList(object):
151 def __init__(self, entries=None):
152 if isinstance(entries, FileList):
153 entries = entries.entries
154 self.entries = entries or []
155 def __getitem__(self, i):
156 return self.entries[i]
157 def __setitem__(self, i, item):
158 self.entries[i] = item
159 def __delitem__(self, i):
160 del self.entries[i]
161 def __add__(self, other):
162 if isinstance(other, FileList):
163 return self.__class__(self.entries + other.entries)
164 elif isinstance(other, type(self.entries)):
165 return self.__class__(self.entries + other)
166 else:
167 return self.__class__(self.entries + list(other))
168 def __radd__(self, other):
169 if isinstance(other, FileList):
170 return self.__class__(other.entries + self.entries)
171 elif isinstance(other, type(self.entries)):
172 return self.__class__(other + self.entries)
173 else:
174 return self.__class__(list(other) + self.entries)
175 def __iadd__(self, other):
176 if isinstance(other, FileList):
177 self.entries += other.entries
178 elif isinstance(other, type(self.entries)):
179 self.entries += other
180 else:
181 self.entries += list(other)
182 return self
183 def append(self, item):
184 return self.entries.append(item)
185 def extend(self, item):
186 return self.entries.extend(item)
187 def index(self, item, *args):
188 return self.entries.index(item, *args)
189 def remove(self, item):
190 return self.entries.remove(item)
191
192 def FileListWalk(top, topdown=True, onerror=None):
193 """
194 """
195 try:
196 entries = top.entries
197 except AttributeError, err:
198 if onerror is not None:
199 onerror(err)
200 return
201
202 dirs, nondirs = [], []
203 for entry in entries:
204 if hasattr(entry, 'entries'):
205 dirs.append(entry)
206 else:
207 nondirs.append(entry)
208
209 if topdown:
210 yield top, dirs, nondirs
211 for entry in dirs:
212 for x in FileListWalk(entry, topdown, onerror):
213 yield x
214 if not topdown:
215 yield top, dirs, nondirs
216
217 #------------------------------------------------------------------------------
218
219 class _MSVSFolder(FileList):
220 """Folder in a Visual Studio solution."""
221
222 entry_type_guid = '{2150E333-8FDC-42A3-9474-1A3956D46DE8}'
223
224 def initialize(self, path, name=None, entries=None, guid=None, items=None):
225 """Initializes the folder.
226
227 Args:
228 path: The unique name of the folder, by which other MSVS Nodes can
229 refer to it. This is not necessarily the name that gets printed
230 in the .sln file.
231 name: The name of this folder as actually written in a generated
232 .sln file. The default is
233 entries: List of folder entries to nest inside this folder. May contain
234 Folder or Project objects. May be None, if the folder is empty.
235 guid: GUID to use for folder, if not None.
236 items: List of solution items to include in the folder project. May be
237 None, if the folder does not directly contain items.
238 """
239 super(_MSVSFolder, self).__init__(entries)
240
241 # For folder entries, the path is the same as the name
242 self.msvs_path = path
243 self.msvs_name = name or path
244
245 self.guid = guid
246
247 # Copy passed lists (or set to empty lists)
248 self.items = list(items or [])
249
250 def get_guid(self):
251 if self.guid is None:
252 guid = GUIDMap.get(self.msvs_path)
253 if not guid:
254 # The GUID for the folder can be random, since it's used only inside
255 # solution files and doesn't need to be consistent across runs.
256 guid = MakeGuid(random.random())
257 self.guid = guid
258 return self.guid
259
260 def get_msvs_path(self, sln):
261 return self.msvs_name
262
263 def MSVSFolder(env, item, *args, **kw):
264 return LookupCreate(_MSVSFolder, item, *args, **kw)
265
266 #------------------------------------------------------------------------------
267
268 class MSVSConfig(object):
269 """Visual Studio configuration."""
270 def __init__(self, Name, config_type, tools=None, **attrs):
271 """Initializes the configuration.
272
273 Args:
274 **attrs: Configuration attributes.
275 """
276 # Special handling for attributes that we want to make more
277 # convenient for the user.
278 ips = attrs.get('InheritedPropertySheets')
279 if ips:
280 if isinstance(ips, list):
281 ips = ';'.join(ips)
282 attrs['InheritedPropertySheets'] = ips.replace('/', '\\')
283
284 self.Name = Name
285 self.config_type = config_type
286 self.tools = tools
287 self.attrs = attrs
288
289 def CreateElement(self, doc, project):
290 """Creates an element for the configuration.
291
292 Args:
293 doc: xml.dom.Document object to use for node creation.
294
295 Returns:
296 A new xml.dom.Element for the configuration.
297 """
298 node = doc.createElement(self.config_type)
299 node.setAttribute('Name', self.Name)
300 for k, v in self.attrs.items():
301 node.setAttribute(k, v)
302
303 tools = self.tools
304 if tools is None:
305 tools = project.tools or []
306 if not SCons.Util.is_List(tools):
307 tools = [tools]
308 tool_objects = []
309 for t in tools:
310 if not isinstance(t, MSVSTool):
311 t = MSVSTool(t)
312 tool_objects.append(t)
313 for t in tool_objects:
314 node.appendChild(t.CreateElement(doc))
315
316 return node
317
318
319 class MSVSFileListBase(FileList):
320 """Base class for a file list in a Visual Studio project file."""
321
322 def CreateElement(self, doc, node_func=lambda x: x):
323 """Creates an element for an MSVSFileListBase subclass.
324
325 Args:
326 doc: xml.dom.Document object to use for node creation.
327 node_func: Function to use to return Nodes for objects that
328 don't have a CreateElement() method of their own.
329
330 Returns:
331 A new xml.dom.Element for the MSVSFileListBase object.
332 """
333 node = doc.createElement(self.element_name)
334 for entry in self.entries:
335 if hasattr(entry, 'CreateElement'):
336 n = entry.CreateElement(doc, node_func)
337 else:
338 n = node_func(entry)
339 node.appendChild(n)
340 return node
341
342
343 class MSVSFiles(MSVSFileListBase):
344 """Files list in a Visual Studio project file."""
345 element_name = 'Files'
346
347
348 class MSVSFilter(MSVSFileListBase):
349 """Filter (that is, a virtual folder) in a Visual Studio project file."""
350
351 element_name = 'Filter'
352
353 def __init__(self, Name, entries=None):
354 """Initializes the folder.
355
356 Args:
357 Name: Filter (folder) name.
358 entries: List of filenames and/or Filter objects contained.
359 """
360 super(MSVSFilter, self).__init__(entries)
361 self.Name = Name
362
363 def CreateElement(self, doc, node_func=lambda x: x):
364 """Creates an element for the Filter.
365
366 Args:
367 doc: xml.dom.Document object to use for node creation.
368 node_func: Function to use to return Nodes for objects that
369 don't have a CreateElement() method of their own.
370
371 Returns:
372 A new xml.dom.Element for the filter.
373 """
374 node = super(MSVSFilter, self).CreateElement(doc, node_func)
375 node.setAttribute('Name', self.Name)
376 return node
377
378
379 class MSVSTool(object):
380 """Visual Studio tool."""
381
382 def __init__(self, Name, **attrs):
383 """Initializes the tool.
384
385 Args:
386 Name: Tool name.
387 **attrs: Tool attributes.
388 """
389
390 val = attrs.get('AdditionalDependencies')
391 if val:
392 if isinstance(val, list):
393 val = ' '.join(val)
394 attrs['AdditionalDependencies'] = val.replace('/', '\\')
395
396 val = attrs.get('AdditionalIncludeDirectories')
397 if val:
398 if isinstance(val, list):
399 val = ';'.join(val)
400 attrs['AdditionalIncludeDirectories'] = val.replace('/', '\\')
401
402 val = attrs.get('AdditionalManifestFiles')
403 if val:
404 if isinstance(val, list):
405 val = ';'.join(val)
406 attrs['AdditionalManifestFiles'] = val.replace('/', '\\')
407
408 val = attrs.get('CommandLine')
409 if val:
410 if isinstance(val, list):
411 val = '\r\n'.join(val)
412 attrs['CommandLine'] = val.replace('/', '\\')
413
414 val = attrs.get('PreprocessorDefinitions')
415 if val:
416 if isinstance(val, list):
417 val = ';'.join(val)
418 attrs['PreprocessorDefinitions'] = val
419
420 for a in ('ImportLibrary',
421 'ObjectFile',
422 'OutputFile',
423 'Outputs',
424 'XMLDocumentationFileName'):
425 val = attrs.get(a)
426 if val:
427 val = val.replace('/', '\\')
428 attrs[a] = val
429
430 self.Name = Name
431 self.attrs = attrs
432
433 def CreateElement(self, doc):
434 """Creates an element for the tool.
435
436 Args:
437 doc: xml.dom.Document object to use for node creation.
438
439 Returns:
440 A new xml.dom.Element for the tool.
441 """
442 node = doc.createElement('Tool')
443 node.setAttribute('Name', self.Name)
444 for k, v in self.attrs.items():
445 node.setAttribute(k, v)
446 return node
447
448 def _format(self):
449 """Formats a tool specification for debug printing"""
450 xml_impl = xml.dom.getDOMImplementation()
451 doc = xml_impl.createDocument(None, 'VisualStudioProject', None)
452 return self.CreateElement(doc).toprettyxml()
453
454 def diff(self, other):
455 for key, value in self.attrs.items():
456 if other.attrs[key] == value:
457 del self.attrs[key]
458
459
460 class MSVSToolFile(object):
461 """Visual Studio tool file specification."""
462
463 def __init__(self, node, **attrs):
464 """Initializes the tool.
465
466 Args:
467 node: Node for the Tool File
468 **attrs: Tool File attributes.
469 """
470 self.node = node
471
472 def CreateElement(self, doc, project):
473 result = doc.createElement('ToolFile')
474 result.setAttribute('RelativePath', project.get_rel_path(self.node))
475 return result
476
477
478 #------------------------------------------------------------------------------
479
480 def MSVSAction(target, source, env):
481 target[0].Write(env)
482
483 MSVSProjectAction = SCons.Script.Action(MSVSAction,
484 "Generating Visual Studio project `$TARG ET' ...")
485
486 class _MSVSProject(SCons.Node.FS.File):
487 """Visual Studio project."""
488
489 entry_type_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}'
490 initialized = False
491
492 def initialize(self, env, path, name = None,
493 dependencies = None,
494 guid = None,
495 buildtargets = [],
496 files = [],
497 root_namespace = None,
498 keyword = None,
499 relative_path_prefix = None,
500 local_directory_prefix = None,
501 relative_path_substitutions = [],
502 tools = None,
503 configurations = None,
504 **attrs):
505 """Initializes the project.
506
507 Args:
508 path: Relative path to project file.
509 name: Name of project. If None, the name will be the same as the base
510 name of the project file.
511 dependencies: List of other Project objects this project is dependent
512 upon, if not None.
513 guid: GUID to use for project, if not None.
514 buildtargets: List of target(s) being built by this project.
515 files: List of source files for the project. This will be
516 supplemented by any source files of buildtargets.
517 root_namespace: The value of the RootNamespace attribute of the
518 project, if not None. The default is to use the same
519 string as the name.
520 relative_path_prefix: A prefix to be appended to the beginning of
521 every file name in the list. The canonical use is to specify
522 './' to make files explicitly relative to the local directory.
523 tools: A list of MSVSTool objects or strings representing
524 tools to be used to build this project. This will be used
525 for any configurations that don't provide their own
526 per-configuration tool list.
527 configurations: A list of MSVSConfig objects representing
528 configurations built by this project.
529 """
530 if name is None:
531 if buildtargets:
532 name = os.path.splitext(buildtargets[0].name)[0]
533 else:
534 name = os.path.splitext(os.path.basename(path))[0]
535 if not root_namespace:
536 root_namespace or name
537
538 if self.initialized:
539 # TODO(sgk): fill in
540 if self.msvs_name != name:
541 pass
542 if self.root_namespace != root_namespace:
543 pass
544 if self.relative_path_prefix != relative_path_prefix:
545 pass
546 if self.guid != guid:
547 pass
548 #if self.env != env:
549 # pass
550 else:
551 self.buildtargets = []
552 self.configurations = []
553 self.dependencies = []
554 self.file_configurations = {}
555 self.files = MSVSFiles([])
556 self.tool_files = []
557 self.file_lists = []
558 self.initialized = True
559 self.keyword = None
560 self.local_directory_prefix = ''
561 self.relative_path_prefix = ''
562 self.relative_path_substitutions = []
563 self.root_namespace = name
564
565 self.attrs = attrs
566 self.env = env
567 self.guid = guid
568 self.msvs_name = name
569 self.msvs_path = path
570 if relative_path_prefix:
571 self.relative_path_prefix = relative_path_prefix
572 if local_directory_prefix:
573 self.local_directory_prefix = local_directory_prefix
574 for left, right in relative_path_substitutions:
575 t = (left.replace('/', '\\'), right.replace('/', '\\'))
576 self.relative_path_substitutions.append(t)
577 if root_namespace:
578 self.root_namespace = root_namespace
579 if keyword:
580 self.keyword = keyword
581 self.tools = tools
582
583 self.buildtargets.extend(buildtargets)
584 self.configurations.extend(configurations or [])
585 self.dependencies.extend(list(dependencies or []))
586 self.AddFiles(files)
587
588 env.Command(self, [], MSVSProjectAction)
589
590 def args2nodes(self, entries):
591 result = []
592 for entry in entries:
593 if SCons.Util.is_String(entry):
594 entry = self.env.File(entry)
595 result.append(entry.srcnode())
596 elif hasattr(entry, 'entries'):
597 entry.entries = self.args2nodes(entry.entries)
598 result.append(entry)
599 elif isinstance(entry, (list, UserList.UserList)):
600 result.extend(self.args2nodes(entry))
601 elif hasattr(entry, 'sources') and entry.sources:
602 result.extend(entry.sources)
603 else:
604 result.append(entry.srcnode())
605 return result
606
607 def FindFile(self, node):
608 try:
609 flat_file_dict = self.flat_file_dict
610 except AttributeError:
611 flat_file_dict = {}
612 file_list = self.files[:]
613 while file_list:
614 entry = file_list.pop(0)
615 if not isinstance(entry, (list, UserList.UserList)):
616 entry = [entry]
617 for f in entry:
618 if hasattr(f, 'entries'):
619 file_list.extend(f.entries)
620 else:
621 flat_file_dict[f] = True
622 flat_file_dict[f.srcnode()] = True
623 if hasattr(f, 'sources'):
624 for s in f.sources:
625 flat_file_dict[s] = True
626 flat_file_dict[s.srcnode()] = True
627 self.flat_file_dict = flat_file_dict
628
629 return flat_file_dict.get(node)
630
631 def get_guid(self):
632 if self.guid is None:
633 guid = GUIDMap.get(self.msvs_path)
634 if not guid:
635 # Set GUID from path
636 # TODO(rspangler): This is fragile.
637 # 1. We can't just use the project filename sans path, since there
638 # could be multiple projects with the same base name (for example,
639 # foo/unittest.vcproj and bar/unittest.vcproj).
640 # 2. The path needs to be relative to $SOURCE_ROOT, so that the project
641 # GUID is the same whether it's included from base/base.sln or
642 # foo/bar/baz/baz.sln.
643 # 3. The GUID needs to be the same each time this builder is invoked,
644 # so that we don't need to rebuild the solution when the
645 # project changes.
646 # 4. We should be able to handle pre-built project files by reading the
647 # GUID from the files.
648 guid = MakeGuid(self.msvs_path)
649 self.guid = guid
650 return self.guid
651
652 def get_msvs_path(self, sln):
653 return sln.rel_path(self).replace('/', '\\')
654
655 def get_rel_path(self, node):
656 result = self.rel_path(node)
657 if self.relative_path_prefix:
658 if not result.startswith('..'):
659 result = self.relative_path_prefix + result
660 elif not os.path.split(result)[0]:
661 result = self.local_directory_prefix + result
662 result = result.replace('/', '\\')
663 for left, right in self.relative_path_substitutions:
664 result = result.replace(left, right)
665 return result
666
667 def AddConfig(self, Name, tools=None, **attrs):
668 """Adds a configuration to the parent node.
669
670 Args:
671 Name: The name of the configuration.
672 tools: List of tools (strings or Tool objects); may be None.
673 **attrs: Configuration attributes.
674 """
675 if tools is None:
676 # No tool list specifically for this configuration,
677 # use the Project's as a default.
678 tools = self.tools
679 if not attrs.has_key('ConfigurationType'):
680 # No ConfigurationType specifically for this configuration,
681 # use the Project's as a default.
682 try:
683 attrs['ConfigurationType'] = self.attrs['ConfigurationType']
684 except KeyError:
685 pass
686 if attrs.has_key('InheritedPropertySheets'):
687 ips = attrs['InheritedPropertySheets']
688 attrs['InheritedPropertySheets'] = self.env.subst(ips)
689 c = MSVSConfig(Name, 'Configuration', tools=tools, **attrs)
690 self.configurations.append(c)
691
692 def AddFiles(self, files):
693 """Adds files to the project.
694
695 Args:
696 files: A list of Filter objects and/or relative paths to files.
697
698 This makes a copy of the file/filter tree at the time of this call. If you
699 later add files to a Filter object which was passed into a previous call
700 to AddFiles(), it will not be reflected in this project.
701 """
702 self.file_lists.append(self.args2nodes(files))
703
704 def _FilesToSourceFiles(self, files):
705 file_list = files[:]
706 result = []
707 while file_list:
708 entry = file_list.pop(0)
709 if not isinstance(entry, (list, UserList.UserList)):
710 entry = [entry]
711 for f in entry:
712 if hasattr(f, 'entries'):
713 self._FilesToSourceFiles(f.entries)
714 result.append(f)
715 else:
716 if f.sources:
717 flist = f.sources
718 else:
719 flist = [f]
720 for x in flist:
721 result.append(x.srcnode())
722 files[:] = result
723
724 def _MergeFiles(self, dest_list, src_list):
725 for f in src_list:
726 if f not in dest_list:
727 dest_list.append(f)
728 continue
729 #if hasattr(f, 'entries'):
730 # self._FilesToSourceFiles(f.entries)
731
732 def AddFileConfig(self, path, Name, tools=None, **attrs):
733 """Adds a configuration to a file.
734
735 Args:
736 path: Relative path to the file.
737 Name: Name of configuration to add.
738 tools: List of tools (strings or MSVSTool objects); may be None.
739 **attrs: Configuration attributes.
740
741 Raises:
742 ValueError: Relative path does not match any file added via AddFiles().
743 """
744 # Store as the VariantDir node, not as the source node.
745 node = self.env.File(path)
746 c = MSVSConfig(Name, 'FileConfiguration', tools=tools, **attrs)
747 config_list = self.file_configurations.get(node)
748 if config_list is None:
749 config_list = []
750 self.file_configurations[node] = config_list
751 config_list.append(c)
752
753 def AddToolFile(self, path):
754 """Adds a tool file to the project.
755
756 Args:
757 path: Relative path from project to tool file.
758 """
759 tf = MSVSToolFile(self.env.File(path))
760 self.tool_files.append(tf)
761
762 def Create(self):
763 """Creates the project document.
764
765 Args:
766 name: Name of the project.
767 guid: GUID to use for project, if not None.
768 """
769 # Create XML doc
770 xml_impl = xml.dom.getDOMImplementation()
771 self.doc = xml_impl.createDocument(None, 'VisualStudioProject', None)
772
773 # Add attributes to root element
774 root = self.doc.documentElement
775 root.setAttribute('ProjectType', 'Visual C++')
776 root.setAttribute('Version', '8.00')
777 root.setAttribute('Name', self.msvs_name)
778 root.setAttribute('ProjectGUID', self.get_guid())
779 root.setAttribute('RootNamespace', self.root_namespace)
780 if self.keyword:
781 root.setAttribute('Keyword', self.keyword)
782
783 # Add platform list
784 platforms = self.doc.createElement('Platforms')
785 root.appendChild(platforms)
786 n = self.doc.createElement('Platform')
787 n.setAttribute('Name', 'Win32')
788 platforms.appendChild(n)
789
790 # Add tool files section
791 tool_files = self.doc.createElement('ToolFiles')
792 root.appendChild(tool_files)
793 for tf in self.tool_files:
794 tool_files.appendChild(tf.CreateElement(self.doc, self))
795
796 # Add configurations section
797 configs = self.doc.createElement('Configurations')
798 root.appendChild(configs)
799 for c in self.configurations:
800 configs.appendChild(c.CreateElement(self.doc, self))
801
802 # Add empty References section
803 root.appendChild(self.doc.createElement('References'))
804
805 # Add files section
806 root.appendChild(self.files.CreateElement(self.doc, self.CreateFileElement))
807
808 # Add empty Globals section
809 root.appendChild(self.doc.createElement('Globals'))
810
811 def CreateFileElement(self, file):
812 """Create a DOM node for the specified file.
813
814 Args:
815 file: The file Node being considered.
816
817 Returns:
818 A DOM Node for the File, with a relative path to the current
819 project object, and any file configurations attached to the
820 project.
821 """
822
823 node = self.doc.createElement('File')
824 node.setAttribute('RelativePath', self.get_rel_path(file))
825 for c in self.file_configurations.get(file, []):
826 node.appendChild(c.CreateElement(self.doc, self))
827 return node
828
829 def VCCLCompilerTool(self, args):
830 default_attrs = {
831 'BufferSecurityCheck' : "false",
832 'CompileAs' : 0, # default
833 'DebugInformationFormat' : 0, # TODO(???)
834 'DisableSpecificWarnings' : [],
835 'EnableFiberSafeOptimizations' : "false",
836 'EnableFunctionLevelLinking' : "false",
837 'EnableIntrinsicFunctions' : "false",
838 'FavorSizeOrSpeed' : 0, # favorNone
839 'InlineFunctionExpansion' : 1, # expandDisable
840 'MinimalRebuild' : "false",
841 'OmitFramePointers' : "false",
842 'Optimization' : 1, # optimizeDisabled TODO(???)
843 'PreprocessorDefinitions' : [],
844 'RuntimeLibrary' : TODO,
845 'RuntimeTypeInfo' : "false",
846 'StringPooling' : "false",
847 'SuppressStartupBanner' : "false",
848 'WarningAsError' : "false",
849 'WarningLevel' : 1, # warningLevel_1
850 'WholeProgramOptimization' : "false",
851 }
852
853 tool = MSVSTool('VCCLCompilerTool', **default_attrs)
854 attrs = tool.attrs
855
856 for arg in args:
857 if arg in ('/c',):
858 continue
859 if arg.startswith('/Fo'):
860 continue
861 if arg.startswith('/D'):
862 attrs['PreprocessorDefinitions'].append(arg[2:])
863 elif arg == '/EH':
864 attrs['ExceptionHandling'] = 0
865 elif arg == '/GF':
866 attrs['StringPooling'] = "true"
867 elif arg == '/GL':
868 attrs['WholeProgramOptimization'] = "true"
869 elif arg == '/GM':
870 attrs['MinimalRebuild'] = "true"
871 elif arg == '/GR-':
872 attrs['RuntimeTypeInfo'] = "true"
873 elif arg == '/Gs':
874 attrs['BufferSecurityCheck'] = "true"
875 elif arg == '/Gs-':
876 attrs['BufferSecurityCheck'] = "false"
877 elif arg == '/GT':
878 attrs['EnableFiberSafeOptimizations'] = "true"
879 elif arg == '/Gy':
880 attrs['EnableFunctionLevelLinking'] = "true"
881 elif arg == '/MD':
882 attrs['RuntimeLibrary'] = 1 # rtMultiThreadedDebug
883 elif arg == '/MDd':
884 attrs['RuntimeLibrary'] = 2 # rtMultiThreadedDebugDLL
885 elif arg == '/MT':
886 attrs['RuntimeLibrary'] = 0 # rtMultiThreaded
887 elif arg == '/MTd':
888 attrs['RuntimeLibrary'] = 3 # rtMultiThreadedDLL
889 elif arg == '/nologo':
890 attrs['SuppressStartupBanner'] = "true"
891 elif arg == '/O1':
892 attrs['InlineFunctionExpansion'] = 4 # optimizeMinSpace
893 elif arg == '/O2':
894 attrs['InlineFunctionExpansion'] = 3 # optimizeMaxSpeed
895 elif arg == '/Ob1':
896 attrs['InlineFunctionExpansion'] = 2 # expandOnlyInline
897 elif arg == '/Ob2':
898 attrs['InlineFunctionExpansion'] = 0 # expandAnySuitable
899 elif arg == '/Od':
900 attrs['Optimization'] = 0
901 elif arg == '/Oi':
902 attrs['EnableIntrinsicFunctions'] = "true"
903 elif arg == '/Os':
904 attrs['FavorSizeOrSpeed'] = 1 # favorSize
905 elif arg == '/Ot':
906 attrs['FavorSizeOrSpeed'] = 2 # favorSpeed
907 elif arg == '/Ox':
908 attrs['Optimization'] = 2 # optimizeFull
909 elif arg == '/Oy':
910 attrs['OmitFramePointers'] = "true"
911 elif arg == '/Oy-':
912 attrs['TODO'] = "true"
913 elif arg in ('/Tc', '/TC'):
914 attrs['CompileAs'] = 1 # compileAsC
915 elif arg in ('/Tp', '/TP'):
916 attrs['CompileAs'] = 2 # compileAsCPlusPlus
917 elif arg == '/WX':
918 attrs['WarnAsError'] = "true"
919 elif arg.startswith('/W'):
920 attrs['WarningLevel'] = int(arg[2:]) # 0 through 4
921 elif arg.startswith('/wd'):
922 attrs['DisableSpecificWarnings'].append(str(arg[3:]))
923 elif arg == '/Z7':
924 attrs['DebugInformationFormat'] = 3 # debugOldSytleInfo TODO(???)
925 elif arg == '/Zd':
926 attrs['DebugInformationFormat'] = 0 # debugDisabled
927 elif arg == '/Zi':
928 attrs['DebugInformationFormat'] = 2 # debugEnabled TODO(???)
929 elif arg == '/ZI':
930 attrs['DebugInformationFormat'] = 1 # debugEditAndContinue TODO(???)
931
932 cppdefines = attrs['PreprocessorDefinitions']
933 if cppdefines:
934 attrs['PreprocessorDefinitions'] = ';'.join(cppdefines)
935 warnings = attrs['DisableSpecificWarnings']
936 if warnings:
937 warnings = SCons.Util.uniquer(warnings)
938 attrs['DisableSpecificWarnings'] = ';'.join(warnings)
939
940 return tool
941
942 def VCLibrarianTool(self, args):
943 default_attrs = {
944 'LinkTimeCodeGeneration' : "false",
945 'SuppressStartupBanner' : "false",
946 }
947
948 tool = MSVSTool('VCLibrarianTool', **default_attrs)
949 attrs = tool.attrs
950
951 for arg in args:
952 if arg.startswith('/OUT'):
953 continue
954 if arg == '/ltcg':
955 attrs['LinkTimeCodeGeneration'] = "true"
956 elif arg == '/nologo':
957 attrs['SuppressStartupBanner'] = "true"
958
959 return tool
960
961 def VCLinkerTool(self, args):
962 default_attrs = {
963 'LinkIncremental' : "false",
964 'LinkTimeCodeGeneration' : "false",
965 'EnableCOMDATFolding' : TODO,
966 'OptimizeForWindows98' : TODO,
967 'OptimizeReferences' : TODO,
968 'Profile' : "false",
969 'SuppressStartupBanner' : "false",
970 }
971
972 tool = MSVSTool('VCLinkerTool', **default_attrs)
973 attrs = tool.attrs
974
975 for arg in args:
976 if arg == '':
977 continue
978 if arg == '/INCREMENTAL':
979 attrs['LinkIncremental'] = "true"
980 elif arg == '/INCREMENTAL:NO':
981 attrs['LinkIncremental'] = "false"
982 elif arg == '/LTCG':
983 attrs['LinkTimeCodeGeneration'] = "true"
984 elif arg == '/nologo':
985 attrs['SuppressStartupBanner'] = "true"
986 elif arg == '/OPT:NOICF':
987 attrs['EnableCOMDATFolding'] = 2 #
988 elif arg == '/OPT:NOWIN98':
989 attrs['OptimizeForWindows98'] = 1 #
990 elif arg == '/OPT:REF':
991 attrs['OptimizeReferences'] = 2 #
992 elif arg == '/PROFILE':
993 attrs['Profile'] = "true"
994
995 return tool
996
997 command_to_tool_map = {
998 'cl' : 'VCCLCompilerTool',
999 'cl.exe' : 'VCCLCompilerTool',
1000 'lib' : 'VCLibrarianTool',
1001 'lib.exe' : 'VCLibrarianTool',
1002 'link' : 'VCLinkerTool',
1003 'link.exe' : 'VCLinkerTool',
1004 }
1005
1006 def cl_to_tool(self, args):
1007 command = os.path.basename(args[0])
1008 method_name = self.command_to_tool_map.get(command)
1009 if not method_name:
1010 return None
1011 return getattr(self, method_name)(args[1:])
1012
1013 def _AddFileConfigurationDifferences(self, target, source, base_env, file_env, name):
1014 """Adds a per-file configuration.
1015
1016 Args:
1017 target: The target being built from the source.
1018 source: The source to which the file configuration is being added.
1019 base_env: The base construction environment for the project.
1020 Differences from this will go into the FileConfiguration
1021 in the project file.
1022 file_env: The construction environment for the target, containing
1023 the per-target settings.
1024 """
1025 executor = target.get_executor()
1026 base_cl = map(str, base_env.subst_list(executor)[0])
1027 file_cl = map(str, file_env.subst_list(executor)[0])
1028 if base_cl == file_cl:
1029 return
1030
1031 base_tool = self.cl_to_tool(base_cl)
1032 file_tool = self.cl_to_tool(file_cl)
1033
1034 if not base_tool or not_file_tool:
1035 return
1036
1037 file_tool.diff(base_tool)
1038
1039 self.AddFileConfig(source, name, tools=[file_tool])
1040
1041 def _AddFileConfigurations(self, env):
1042 """Adds per-file configurations for the buildtarget's sources.
1043
1044 Args:
1045 env: The base construction environment for the project.
1046 """
1047 if not self.buildtargets:
1048 return
1049
1050 for bt in self.buildtargets:
1051 executor = bt.get_executor()
1052 build_env = bt.get_build_env()
1053 bt_cl = map(str, build_env.subst_list(executor)[0])
1054 tool = self.cl_to_tool(bt_cl)
1055 default_tool = self.cl_to_tool([bt_cl[0]])
1056 if default_tool:
1057 tool.diff(default_tool)
1058 else:
1059 # TODO(sgk): print a message unconditionally is too
1060 # verbose for things like Python function actions,
1061 # but doing nothing runs the risk of burying problems.
1062 # Work out a better solution.
1063 #print "no tool for %r" % bt_cl[0]
1064 pass
1065 for t in bt.sources:
1066 e = t.get_build_env()
1067 additional_files = SCons.Util.UniqueList()
1068 for s in t.sources:
1069 s = env.arg2nodes([s])[0].srcnode()
1070 if not self.FindFile(s):
1071 additional_files.append(s)
1072 if not build_env is e:
1073 # TODO(sgk): This test may be bogus, but it works for now.
1074 # We're trying to figure out if the file configuration
1075 # differences need to be added one per build target, or one
1076 # per configuration for the entire project. The assumption
1077 # is that if the number of buildtargets configured matches
1078 # the number of project configurations, that we use those
1079 # in preference to the project configurations.
1080 if len(self.buildtargets) == len(self.configurations):
1081 self._AddFileConfigurationDifferences(t, s, build_env, e, e.subst( '$MSVSCONFIGURATIONNAME'))
1082 else:
1083 for config in self.configurations:
1084 self._AddFileConfigurationDifferences(t, s, build_env, e, config .Name)
1085 self._MergeFiles(self.files, additional_files)
1086
1087 def Write(self, env):
1088 """Writes the project file."""
1089 for flist in self.file_lists:
1090 self._FilesToSourceFiles(flist)
1091 self._MergeFiles(self.files, flist)
1092 for k, v in self.file_configurations.items():
1093 self.file_configurations[str(k)] = v
1094 k = self.env.File(k).srcnode()
1095 self.file_configurations[k] = v
1096 self.file_configurations[str(k)] = v
1097 self._AddFileConfigurations(env)
1098
1099 self.Create()
1100
1101 f = open(str(self), 'wt')
1102 f.write(self.formatMSVSProjectXML(self.doc))
1103 f.close()
1104
1105 # Methods for formatting XML as nearly identically to Microsoft's
1106 # .vcproj output as we can practically make it.
1107 #
1108 # The general design here is copied from:
1109 #
1110 # Bruce Eckels' MindView, Inc: 12-09-04 XML Oddyssey
1111 # http://www.mindview.net/WebLog/log-0068
1112 #
1113 # Eckels' implementation broke up long tag definitions for readability,
1114 # in much the same way that .vcproj does, but we've modified things
1115 # for .vcproj quirks (like some tags *always* terminating with </Tag>,
1116 # even when empty).
1117
1118 encoding = 'Windows-1252'
1119
1120 def formatMSVSProjectXML(self, xmldoc):
1121 xmldoc = xmldoc.toprettyxml("", "\n", encoding=self.encoding)
1122 # Remove trailing whitespace from each line:
1123 xmldoc = "\n".join(
1124 [line.rstrip() for line in xmldoc.split("\n")])
1125 # Remove all empty lines before opening '<':
1126 while xmldoc.find("\n\n<") != -1:
1127 xmldoc = xmldoc.replace("\n\n<", "\n<")
1128 dom = xml.dom.minidom.parseString(xmldoc)
1129 xmldoc = dom.toprettyxml("\t", "", encoding=self.encoding)
1130 xmldoc = xmldoc.replace('?><', '?>\n<')
1131 xmldoc = self.reformatLines(xmldoc)
1132 return xmldoc
1133
1134 def reformatLines(self, xmldoc):
1135 result = []
1136 for line in [line.rstrip() for line in xmldoc.split("\n")]:
1137 if line.lstrip().startswith("<"):
1138 result.append(self.reformatLine(line) + "\n")
1139 else:
1140 result.append(line + "\n")
1141 return ''.join(result)
1142
1143 # Keyword order for specific tags.
1144 #
1145 # Listed keywords will come first and in the specified order.
1146 # Any unlisted keywords come after, in whatever order they appear
1147 # in the input config. In theory this means we would only *have* to
1148 # list the keywords that we care about, but in practice we'll probably
1149 # want to nail down Visual Studio's order to make sure we match them
1150 # as nearly as possible.
1151
1152 order = {
1153 'Configuration' : [
1154 'Name',
1155 'ConfigurationType',
1156 'InheritedPropertySheets',
1157 ],
1158 'FileConfiguration' : [
1159 'Name',
1160 'ExcludedFromBuild',
1161 ],
1162 'Tool' : [
1163 'Name',
1164 'DisableSpecificWarnings',
1165
1166 'AdditionalIncludeDirectories',
1167 'Description',
1168 'CommandLine',
1169 'OutputFile',
1170 'ImportLibrary',
1171 'PreprocessorDefinitions',
1172 'UsePrecompiledHeader',
1173 'PrecompiledHeaderThrough',
1174 'ForcedIncludeFiles',
1175 ],
1176 'VisualStudioProject' : [
1177 'ProjectType',
1178 'Version',
1179 'Name',
1180 'ProjectGUID',
1181 'RootNamespace',
1182 'Keyword',
1183 ],
1184 }
1185
1186 force_closing_tag = [
1187 'File',
1188 'Globals',
1189 'References',
1190 'ToolFiles'
1191 ]
1192
1193 oneLiner = re.compile("(\s*)<(\w+)(.*)>")
1194 keyValuePair = re.compile('\w+="[^"]*?"')
1195 def reformatLine(self, line):
1196 """Reformat an xml tag to put each key-value
1197 element on a single indented line, for readability"""
1198 matchobj = self.oneLiner.match(line.rstrip())
1199 if not matchobj:
1200 return line
1201 baseIndent, tag, rest = matchobj.groups()
1202 slash = ''
1203 if rest[-1:] == '/':
1204 slash = '/'
1205 rest = rest[:-1]
1206 result = [baseIndent + '<' + tag]
1207 indent = baseIndent + "\t"
1208 pairs = self.keyValuePair.findall(rest)
1209 for key in self.order.get(tag, []):
1210 for p in [ p for p in pairs if p.startswith(key+'=') ]:
1211 result.append("\n" + indent + p)
1212 pairs.remove(p)
1213 for pair in pairs:
1214 result.append("\n" + indent + pair)
1215 result = [''.join(result).rstrip()]
1216
1217 if tag in self.force_closing_tag:
1218 # These force termination with </Tag>, so translate slash.
1219 if rest:
1220 result.append("\n")
1221 result.append(indent)
1222 result.append(">")
1223 if slash:
1224 result.append("\n")
1225 result.append(baseIndent + "</" + tag + ">")
1226 else:
1227 if rest:
1228 result.append("\n")
1229 if slash:
1230 result.append(baseIndent)
1231 else:
1232 result.append(indent)
1233 result.append(slash + ">")
1234
1235 return ''.join(result)
1236
1237 def MSVSProject(env, item, *args, **kw):
1238 if not SCons.Util.is_String(item):
1239 return item
1240 item = env.subst(item)
1241 result = env.fs._lookup(item, None, _MSVSProject, create=1)
1242 result.initialize(env, item, *args, **kw)
1243 LookupAdd(item, result)
1244 return result
1245
1246 #------------------------------------------------------------------------------
1247
1248 MSVSSolutionAction = SCons.Script.Action(MSVSAction,
1249 "Generating Visual Studio solution `$TA RGET' ...")
1250
1251 class _MSVSSolution(SCons.Node.FS.File):
1252 """Visual Studio solution."""
1253
1254 def initialize(self, env, path, entries=None, variants=None, websiteProperties =True):
1255 """Initializes the solution.
1256
1257 Args:
1258 path: Path to solution file.
1259 entries: List of entries in solution. May contain Folder or Project
1260 objects. May be None, if the folder is empty.
1261 variants: List of build variant strings. If none, a default list will
1262 be used.
1263 """
1264 self.msvs_path = path
1265 self.websiteProperties = websiteProperties
1266
1267 # Copy passed lists (or set to empty lists)
1268 self.entries = list(entries or [])
1269
1270 if variants:
1271 # Copy passed list
1272 self.variants = variants[:]
1273 else:
1274 # Use default
1275 self.variants = ['Debug|Win32', 'Release|Win32']
1276 # TODO(rspangler): Need to be able to handle a mapping of solution config
1277 # to project config. Should we be able to handle variants being a dict,
1278 # or add a separate variant_map variable? If it's a dict, we can't
1279 # guarantee the order of variants since dict keys aren't ordered.
1280
1281 env.Command(self, [], MSVSSolutionAction)
1282
1283 def Write(self, env):
1284 """Writes the solution file to disk.
1285
1286 Raises:
1287 IndexError: An entry appears multiple times.
1288 """
1289 r = []
1290 errors = []
1291
1292 def lookup_subst(item, env=env, errors=errors):
1293 if SCons.Util.is_String(item):
1294 lookup_item = env.subst(item)
1295 else:
1296 lookup_item = item
1297 try:
1298 return Lookup(lookup_item)
1299 except SCons.Errors.UserError:
1300 raise LookupError(item, lookup_item)
1301
1302 # Walk the entry tree and collect all the folders and projects.
1303 all_entries = []
1304 entries_to_check = self.entries[:]
1305 while entries_to_check:
1306 # Pop from the beginning of the list to preserve the user's order.
1307 entry = entries_to_check.pop(0)
1308 try:
1309 entry = lookup_subst(entry)
1310 except LookupError, e:
1311 errors.append("Could not look up entry `%s'." % e)
1312 continue
1313
1314 # A project or folder can only appear once in the solution's folder tree.
1315 # This also protects from cycles.
1316 if entry in all_entries:
1317 #raise IndexError('Entry "%s" appears more than once in solution' %
1318 # e.name)
1319 continue
1320
1321 all_entries.append(entry)
1322
1323 # If this is a folder, check its entries too.
1324 if isinstance(entry, _MSVSFolder):
1325 entries_to_check += entry.entries
1326
1327 # Header
1328 r.append('Microsoft Visual Studio Solution File, Format Version 9.00\n')
1329 r.append('# Visual Studio 2005\n')
1330
1331 # Project entries
1332 for e in all_entries:
1333 r.append('Project("%s") = "%s", "%s", "%s"\n' % (
1334 e.entry_type_guid, # Entry type GUID
1335 e.msvs_name, # Folder name
1336 e.get_msvs_path(self), # Folder name (again)
1337 e.get_guid(), # Entry GUID
1338 ))
1339
1340 # TODO(rspangler): Need a way to configure this stuff
1341 if self.websiteProperties:
1342 r.append('\tProjectSection(WebsiteProperties) = preProject\n'
1343 '\t\tDebug.AspNetCompiler.Debug = "True"\n'
1344 '\t\tRelease.AspNetCompiler.Debug = "False"\n'
1345 '\tEndProjectSection\n')
1346
1347 if isinstance(e, _MSVSFolder):
1348 if e.items:
1349 r.append('\tProjectSection(SolutionItems) = preProject\n')
1350 for i in e.items:
1351 i = i.replace('/', '\\')
1352 r.append('\t\t%s = %s\n' % (i, i))
1353 r.append('\tEndProjectSection\n')
1354
1355 if isinstance(e, _MSVSProject):
1356 if e.dependencies:
1357 r.append('\tProjectSection(ProjectDependencies) = postProject\n')
1358 for d in e.dependencies:
1359 try:
1360 d = lookup_subst(d)
1361 except LookupError, e:
1362 errors.append("Could not look up dependency `%s'." % e)
1363 else:
1364 r.append('\t\t%s = %s\n' % (d.get_guid(), d.get_guid()))
1365 r.append('\tEndProjectSection\n')
1366
1367 r.append('EndProject\n')
1368
1369 # Global section
1370 r.append('Global\n')
1371
1372 # Configurations (variants)
1373 r.append('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
1374 for v in self.variants:
1375 r.append('\t\t%s = %s\n' % (v, v))
1376 r.append('\tEndGlobalSection\n')
1377
1378 # Sort config guids for easier diffing of solution changes.
1379 config_guids = []
1380 for e in all_entries:
1381 if isinstance(e, _MSVSProject):
1382 config_guids.append(e.get_guid())
1383 config_guids.sort()
1384
1385 r.append('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
1386 for g in config_guids:
1387 for v in self.variants:
1388 r.append('\t\t%s.%s.ActiveCfg = %s\n' % (
1389 g, # Project GUID
1390 v, # Solution build configuration
1391 v, # Project build config for that solution config
1392 ))
1393
1394 # Enable project in this solution configuratation
1395 r.append('\t\t%s.%s.Build.0 = %s\n' % (
1396 g, # Project GUID
1397 v, # Solution build configuration
1398 v, # Project build config for that solution config
1399 ))
1400 r.append('\tEndGlobalSection\n')
1401
1402 # TODO(rspangler): Should be able to configure this stuff too (though I've
1403 # never seen this be any different)
1404 r.append('\tGlobalSection(SolutionProperties) = preSolution\n')
1405 r.append('\t\tHideSolutionNode = FALSE\n')
1406 r.append('\tEndGlobalSection\n')
1407
1408 # Folder mappings
1409 # TODO(rspangler): Should omit this section if there are no folders
1410 folder_mappings = []
1411 for e in all_entries:
1412 if not isinstance(e, _MSVSFolder):
1413 continue # Does not apply to projects, only folders
1414 for subentry in e.entries:
1415 try:
1416 subentry = lookup_subst(subentry)
1417 except LookupError, e:
1418 errors.append("Could not look up subentry `%s'." % subentry)
1419 else:
1420 folder_mappings.append((subentry.get_guid(), e.get_guid()))
1421 folder_mappings.sort()
1422 r.append('\tGlobalSection(NestedProjects) = preSolution\n')
1423 for fm in folder_mappings:
1424 r.append('\t\t%s = %s\n' % fm)
1425 r.append('\tEndGlobalSection\n')
1426
1427 r.append('EndGlobal\n')
1428
1429 if errors:
1430 errors = ['Errors while generating solution file:'] + errors
1431 raise SCons.Errors.UserError, '\n\t'.join(errors)
1432
1433 f = open(self.path, 'wt')
1434 f.write(''.join(r))
1435 f.close()
1436
1437 def MSVSSolution(env, item, *args, **kw):
1438 if not SCons.Util.is_String(item):
1439 return item
1440 item = env.subst(item)
1441 result = env.fs._lookup(item, None, _MSVSSolution, create=1)
1442 result.initialize(env, item, *args, **kw)
1443 LookupAdd(item, result)
1444 return result
1445
1446 class Derived(SCons.Util.Proxy):
1447 def srcnode(self, *args, **kw):
1448 return self
1449 def __getattr__(self, name):
1450 if name == 'sources':
1451 return []
1452 return SCons.Util.Proxy.__getattr__(self, name)
1453 def __hash__(self, *args, **kw):
1454 return id(self)
1455
1456 import __builtin__
1457
1458 __builtin__.Derived = Derived
1459 __builtin__.MSVSConfig = MSVSConfig
1460 __builtin__.MSVSFilter = MSVSFilter
1461 __builtin__.MSVSProject = MSVSProject
1462 __builtin__.MSVSSolution = MSVSSolution
1463 __builtin__.MSVSTool = MSVSTool
OLDNEW
« no previous file with comments | « site_scons/site_tools/MSVSNew.py ('k') | site_scons/site_tools/atlmfc_vc80.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698