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

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

Issue 16447: Initial subset of .vcproj file generation, covering generation of:... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 12 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 | « sandbox/sandbox_sln.scons ('k') | site_scons/site_tools/chromium_builders.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 #
2 # Copyright 2008, Google Inc. 2 # __COPYRIGHT__
3 # All rights reserved. 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__"
4 25
5 __doc__ = """SCons.Node.MSVS 26 __doc__ = """SCons.Node.MSVS
6 27
7 Microsoft Visual Studio nodes. 28 New implementation of Visual Studio project generation for SCons.
8 """ 29 """
9 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
10 import SCons.Node.FS 39 import SCons.Node.FS
11 import SCons.Script 40 import SCons.Script
12 41
13 42
14 """New implementation of Visual Studio project generation for SCons."""
15
16 import md5
17 import os
18 import random
19
20
21 # Initialize random number generator 43 # Initialize random number generator
22 random.seed() 44 random.seed()
23 45
24 46
25 #------------------------------------------------------------------------------ 47 #------------------------------------------------------------------------------
26 # Entry point for supplying a fixed map of GUIDs for testing. 48 # Entry point for supplying a fixed map of GUIDs for testing.
27 49
28 GUIDMap = {} 50 GUIDMap = {}
29 51
30 52
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
115 raise LookupError, "tried to redefine %s as a %s" % (item, klass) 137 raise LookupError, "tried to redefine %s as a %s" % (item, klass)
116 return result 138 return result
117 result = klass() 139 result = klass()
118 result.initialize(item, *args, **kw) 140 result.initialize(item, *args, **kw)
119 LookupAdd(item, result) 141 LookupAdd(item, result)
120 return result 142 return result
121 143
122 144
123 #------------------------------------------------------------------------------ 145 #------------------------------------------------------------------------------
124 146
125 class _MSVSFolder(SCons.Node.Node): 147 class FileList(object):
126 """Folder in a Visual Studio project or solution.""" 148 def __init__(self, entries=None):
149 if isinstance(entries, FileList):
150 entries = entries.entries
151 self.entries = entries or []
152 def __getitem__(self, i):
153 return self.entries[i]
154 def __setitem__(self, i, item):
155 self.entries[i] = item
156 def __delitem__(self, i):
157 del self.entries[i]
158 def __add__(self, other):
159 if isinstance(other, FileList):
160 return self.__class__(self.entries + other.entries)
161 elif isinstance(other, type(self.entries)):
162 return self.__class__(self.entries + other)
163 else:
164 return self.__class__(self.entries + list(other))
165 def __radd__(self, other):
166 if isinstance(other, FileList):
167 return self.__class__(other.entries + self.entries)
168 elif isinstance(other, type(self.entries)):
169 return self.__class__(other + self.entries)
170 else:
171 return self.__class__(list(other) + self.entries)
172 def __iadd__(self, other):
173 if isinstance(other, FileList):
174 self.entries += other.entries
175 elif isinstance(other, type(self.entries)):
176 self.entries += other
177 else:
178 self.entries += list(other)
179 return self
180 def append(self, item):
181 return self.entries.append(item)
182 def extend(self, item):
183 return self.entries.extend(item)
184 def index(self, item, *args):
185 return self.entries.index(item, *args)
186 def remove(self, item):
187 return self.entries.remove(item)
188
189 def FileListWalk(top, topdown=True, onerror=None):
190 """
191 """
192 try:
193 entries = top.entries
194 except AttributeError, err:
195 if onerror is not None:
196 onerror(err)
197 return
198
199 dirs, nondirs = [], []
200 for entry in entries:
201 if hasattr(entry, 'entries'):
202 dirs.append(entry)
203 else:
204 nondirs.append(entry)
205
206 if topdown:
207 yield top, dirs, nondirs
208 for entry in dirs:
209 for x in FileListWalk(entry, topdown, onerror):
210 yield x
211 if not topdown:
212 yield top, dirs, nondirs
213
214 #------------------------------------------------------------------------------
215
216 class _MSVSFolder(FileList):
217 """Folder in a Visual Studio solution."""
127 218
128 entry_type_guid = '{2150E333-8FDC-42A3-9474-1A3956D46DE8}' 219 entry_type_guid = '{2150E333-8FDC-42A3-9474-1A3956D46DE8}'
129 220
130 def initialize(self, path, name = None, entries = None, guid = None, 221 def initialize(self, path, name = None, entries = None, guid = None, items = N one):
131 items = None):
132 """Initializes the folder. 222 """Initializes the folder.
133 223
134 Args: 224 Args:
135 path: The unique name of the folder, by which other MSVS Nodes can 225 path: The unique name of the folder, by which other MSVS Nodes can
136 refer to it. This is not necessarily the name that gets printed 226 refer to it. This is not necessarily the name that gets printed
137 in the .sln file. 227 in the .sln file.
138 name: The name of this folder as actually written in a generated 228 name: The name of this folder as actually written in a generated
139 .sln file. The default is 229 .sln file. The default is
140 entries: List of folder entries to nest inside this folder. May contain 230 entries: List of folder entries to nest inside this folder. May contain
141 Folder or Project objects. May be None, if the folder is empty. 231 Folder or Project objects. May be None, if the folder is empty.
142 guid: GUID to use for folder, if not None. 232 guid: GUID to use for folder, if not None.
143 items: List of solution items to include in the folder project. May be 233 items: List of solution items to include in the folder project. May be
144 None, if the folder does not directly contain items. 234 None, if the folder does not directly contain items.
145 """ 235 """
236 super(_MSVSFolder, self).__init__(entries)
237
146 # For folder entries, the path is the same as the name 238 # For folder entries, the path is the same as the name
147 self.msvs_path = path 239 self.msvs_path = path
148 self.msvs_name = name or path 240 self.msvs_name = name or path
149 241
150 self.guid = guid 242 self.guid = guid
151 243
152 # Copy passed lists (or set to empty lists) 244 # Copy passed lists (or set to empty lists)
153 self.entries = list(entries or [])
154 self.items = list(items or []) 245 self.items = list(items or [])
155 246
156 def get_guid(self): 247 def get_guid(self):
157 if self.guid is None: 248 if self.guid is None:
158 guid = GUIDMap.get(self.msvs_path) 249 guid = GUIDMap.get(self.msvs_path)
159 if not guid: 250 if not guid:
160 # The GUID for the folder can be random, since it's used only inside 251 # The GUID for the folder can be random, since it's used only inside
161 # solution files and doesn't need to be consistent across runs. 252 # solution files and doesn't need to be consistent across runs.
162 guid = MakeGuid(random.random()) 253 guid = MakeGuid(random.random())
163 self.guid = guid 254 self.guid = guid
164 return self.guid 255 return self.guid
165 256
166 def get_msvs_path(self, sln): 257 def get_msvs_path(self, sln):
167 return self.msvs_name 258 return self.msvs_name
168 259
169 def MSVSFolder(env, item, *args, **kw): 260 def MSVSFolder(env, item, *args, **kw):
170 return LookupCreate(_MSVSFolder, item, *args, **kw) 261 return LookupCreate(_MSVSFolder, item, *args, **kw)
171 262
172 #------------------------------------------------------------------------------ 263 #------------------------------------------------------------------------------
173 264
265 class MSVSConfig(object):
266 """Visual Studio configuration."""
267 def __init__(self, Name, config_type, tools=[], **attrs):
268 """Initializes the configuration.
269
270 Args:
271 **attrs: Configuration attributes.
272 """
273 # Special handling for attributes that we want to make more
274 # convenient for the user.
275 ips = attrs.get('InheritedPropertySheets')
276 if ips:
277 if isinstance(ips, list):
278 ips = ';'.join(ips)
279 attrs['InheritedPropertySheets'] = ips.replace('/', '\\')
280
281 tools = tools or []
282 if not SCons.Util.is_List(tools):
283 tools = [tools]
284 tool_objects = []
285 for t in tools:
286 if not isinstance(t, MSVSTool):
287 t = MSVSTool(t)
288 tool_objects.append(t)
289
290 self.Name = Name
291 self.config_type = config_type
292 self.tools = tool_objects
293 self.attrs = attrs
294
295 def CreateElement(self, doc):
296 """Creates an element for the configuration.
297
298 Args:
299 doc: xml.dom.Document object to use for node creation.
300
301 Returns:
302 A new xml.dom.Element for the configuration.
303 """
304 node = doc.createElement(self.config_type)
305 node.setAttribute('Name', self.Name)
306 for k, v in self.attrs.items():
307 node.setAttribute(k, v)
308 for t in self.tools:
309 node.appendChild(t.CreateElement(doc))
310 return node
311
312
313 class MSVSFileListBase(FileList):
314 """Base class for a file list in a Visual Studio project file."""
315
316 def CreateElement(self, doc, node_func=lambda x: x):
317 """Creates an element for an MSVSFileListBase subclass.
318
319 Args:
320 doc: xml.dom.Document object to use for node creation.
321 node_func: Function to use to return Nodes for objects that
322 don't have a CreateElement() method of their own.
323
324 Returns:
325 A new xml.dom.Element for the MSVSFileListBase object.
326 """
327 node = doc.createElement(self.element_name)
328 for entry in self.entries:
329 if hasattr(entry, 'CreateElement'):
330 n = entry.CreateElement(doc, node_func)
331 else:
332 n = node_func(entry)
333 node.appendChild(n)
334 return node
335
336
337 class MSVSFiles(MSVSFileListBase):
338 """Files list in a Visual Studio project file."""
339 element_name = 'Files'
340
341
342 class MSVSFilter(MSVSFileListBase):
343 """Filter (that is, a virtual folder) in a Visual Studio project file."""
344
345 element_name = 'Filter'
346
347 def __init__(self, Name, entries=None):
348 """Initializes the folder.
349
350 Args:
351 Name: Filter (folder) name.
352 entries: List of filenames and/or Filter objects contained.
353 """
354 super(MSVSFilter, self).__init__(entries)
355 self.Name = Name
356
357 def CreateElement(self, doc, node_func=lambda x: x):
358 """Creates an element for the Filter.
359
360 Args:
361 doc: xml.dom.Document object to use for node creation.
362 node_func: Function to use to return Nodes for objects that
363 don't have a CreateElement() method of their own.
364
365 Returns:
366 A new xml.dom.Element for the filter.
367 """
368 node = super(MSVSFilter, self).CreateElement(doc, node_func)
369 node.setAttribute('Name', self.Name)
370 return node
371
372
373 class MSVSTool(object):
374 """Visual Studio tool."""
375
376 def __init__(self, Name, **attrs):
377 """Initializes the tool.
378
379 Args:
380 Name: Tool name.
381 **attrs: Tool attributes.
382 """
383 self.Name = Name
384 self.attrs = attrs
385
386 def CreateElement(self, doc):
387 """Creates an element for the tool.
388
389 Args:
390 doc: xml.dom.Document object to use for node creation.
391
392 Returns:
393 A new xml.dom.Element for the tool.
394 """
395 node = doc.createElement('Tool')
396 node.setAttribute('Name', self.Name)
397 for k, v in self.attrs.items():
398 node.setAttribute(k, v)
399 return node
400
401
402 class MSVSToolFile(object):
403 """Visual Studio tool file specification."""
404
405 def __init__(self, node, **attrs):
406 """Initializes the tool.
407
408 Args:
409 node: Node for the Tool File
410 **attrs: Tool File attributes.
411 """
412 self.node = node
413
414 def CreateElement(self, doc, project):
415 result = doc.createElement('ToolFile')
416 result.setAttribute('RelativePath', project.get_rel_path(self.node))
417 return result
418
419
420 #------------------------------------------------------------------------------
421
422 def MSVSAction(target, source, env):
423 target[0].Write(env)
424
425 MSVSProjectAction = SCons.Script.Action(MSVSAction,
426 "Generating Visual Studio project `$TARG ET' ...")
174 427
175 class _MSVSProject(SCons.Node.FS.File): 428 class _MSVSProject(SCons.Node.FS.File):
176 """Visual Studio project.""" 429 """Visual Studio project."""
177 430
178 entry_type_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}' 431 entry_type_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}'
179 432
180 def initialize(self, path, name = None, dependencies = None, guid = None): 433 def initialize(self, env, path, name = None,
434 dependencies = None,
435 guid = None,
436 buildtargets = [],
437 files = [],
438 root_namespace = None,
439 relative_path_prefix = '',
440 tools = None,
441 configurations = None):
181 """Initializes the project. 442 """Initializes the project.
182 443
183 Args: 444 Args:
184 path: Relative path to project file. 445 path: Relative path to project file.
185 name: Name of project. If None, the name will be the same as the base 446 name: Name of project. If None, the name will be the same as the base
186 name of the project file. 447 name of the project file.
187 dependencies: List of other Project objects this project is dependent 448 dependencies: List of other Project objects this project is dependent
188 upon, if not None. 449 upon, if not None.
189 guid: GUID to use for project, if not None. 450 guid: GUID to use for project, if not None.
451 buildtargets: List of target(s) being built by this project.
452 files: List of source files for the project. This will be
453 supplemented by any source files of buildtargets.
454 root_namespace: The value of the RootNamespace attribute of the
455 project, if not None. The default is to use the same
456 string as the name.
457 relative_path_prefix: A prefix to be appended to the beginning of
458 every file name in the list. The canonical use is to specify
459 './' to make files explicitly relative to the local directory.
460 tools: A list of MSVSTool objects or strings representing
461 tools to be used to build this project. This will be used
462 for any configurations that don't provide their own
463 per-configuration tool list.
464 configurations: A list of MSVSConfig objects representing
465 configurations built by this project.
190 """ 466 """
191 self.msvs_path = path 467 self.msvs_path = path
192 self.msvs_name = name or os.path.splitext(os.path.basename(self.name))[0] 468 self.msvs_node = env.File(path)
469 if name is None:
470 if buildtargets:
471 name = os.path.splitext(buildtargets[0].name)[0]
472 else:
473 name = os.path.splitext(os.path.basename(path))[0]
474 self.msvs_name = name
475 self.root_namespace = root_namespace or self.msvs_name
476 self.buildtargets = buildtargets
477 self.relative_path_prefix = relative_path_prefix
478 self.tools = tools
193 479
480 self.env = env
194 self.guid = guid 481 self.guid = guid
195 482
196 # Copy passed lists (or set to empty lists)
197 self.dependencies = list(dependencies or []) 483 self.dependencies = list(dependencies or [])
484 self.configurations = list(configurations or [])
485 self.file_configurations = {}
486 self.tool_files = []
487
488 if not isinstance(files, MSVSFiles):
489 files = MSVSFiles(self.args2nodes(files))
490 self.files = files
491
492 env.Command(self, [], MSVSSolutionAction)
493
494 def args2nodes(self, entries):
495 result = []
496 for entry in entries:
497 if SCons.Util.is_String(entry):
498 entry = self.env.File(entry)
499 result.append(entry)
500 elif hasattr(entry, 'entries'):
501 entry.entries = self.args2nodes(entry.entries)
502 result.append(entry)
503 elif isinstance(entry, (list, UserList.UserList)):
504 result.extend(self.args2nodes(entry))
505 elif hasattr(entry, 'sources') and entry.sources:
506 result.extend(entry.sources)
507 else:
508 result.append(entry)
509 return result
510
511 def FindFile(self, node):
512 try:
513 flat_file_dict = self.flat_file_dict
514 except AttributeError:
515 flat_file_dict = {}
516 file_list = self.files[:]
517 while file_list:
518 entry = file_list.pop(0)
519 if not isinstance(entry, (list, UserList.UserList)):
520 entry = [entry]
521 for f in entry:
522 if hasattr(f, 'entries'):
523 file_list.extend(f.entries)
524 else:
525 flat_file_dict[f] = True
526 if hasattr(f, 'sources'):
527 for s in f.sources:
528 flat_file_dict[s] = True
529 self.flat_file_dict = flat_file_dict
530
531 return flat_file_dict.get(node)
198 532
199 def get_guid(self): 533 def get_guid(self):
200 if self.guid is None: 534 if self.guid is None:
201 guid = GUIDMap.get(self.msvs_path) 535 guid = GUIDMap.get(self.msvs_path)
202 if not guid: 536 if not guid:
203 # Set GUID from path 537 # Set GUID from path
204 # TODO(rspangler): This is fragile. 538 # TODO(rspangler): This is fragile.
205 # 1. We can't just use the project filename sans path, since there 539 # 1. We can't just use the project filename sans path, since there
206 # could be multiple projects with the same base name (for example, 540 # could be multiple projects with the same base name (for example,
207 # foo/unittest.vcproj and bar/unittest.vcproj). 541 # foo/unittest.vcproj and bar/unittest.vcproj).
208 # 2. The path needs to be relative to $SOURCE_ROOT, so that the project 542 # 2. The path needs to be relative to $SOURCE_ROOT, so that the project
209 # GUID is the same whether it's included from base/base.sln or 543 # GUID is the same whether it's included from base/base.sln or
210 # foo/bar/baz/baz.sln. 544 # foo/bar/baz/baz.sln.
211 # 3. The GUID needs to be the same each time this builder is invoked, 545 # 3. The GUID needs to be the same each time this builder is invoked,
212 # so that we don't need to rebuild the solution when the 546 # so that we don't need to rebuild the solution when the
213 # project changes. 547 # project changes.
214 # 4. We should be able to handle pre-built project files by reading the 548 # 4. We should be able to handle pre-built project files by reading the
215 # GUID from the files. 549 # GUID from the files.
216 guid = MakeGuid(self.msvs_path) 550 guid = MakeGuid(self.msvs_path)
217 self.guid = guid 551 self.guid = guid
218 return self.guid 552 return self.guid
219 553
220 def get_msvs_path(self, sln): 554 def get_msvs_path(self, sln):
221 return sln.rel_path(self).replace('/', '\\') 555 return sln.rel_path(self).replace('/', '\\')
222 556
557 def get_rel_path(self, node):
558 result = self.relative_path_prefix + self.msvs_node.rel_path(node)
559 return result.replace('/', '\\')
560
561 def AddConfig(self, Name, tools=None, **attrs):
562 """Adds a configuration to the parent node.
563
564 Args:
565 Name: The name of the configuration.
566 tools: List of tools (strings or Tool objects); may be None.
567 **attrs: Configuration attributes.
568 """
569 if tools is None:
570 # No tool list specifically for this configuration,
571 # use the Project's as a default.
572 tools = self.tools
573 c = MSVSConfig(Name, 'Configuration', tools=tools, **attrs)
574 self.configurations.append(c)
575
576 def AddFiles(self, files):
577 """Adds files to the project.
578
579 Args:
580 files: A list of Filter objects and/or relative paths to files.
581
582 This makes a copy of the file/filter tree at the time of this call. If you
583 later add files to a Filter object which was passed into a previous call
584 to AddFiles(), it will not be reflected in this project.
585 """
586 # TODO(rspangler) This also doesn't handle adding files to an existing
587 # filter. That is, it doesn't merge the trees.
588 self.files.extend(self.args2nodes(files))
589
590 def AddFileConfig(self, path, Name, tools=None, **attrs):
591 """Adds a configuration to a file.
592
593 Args:
594 path: Relative path to the file.
595 Name: Name of configuration to add.
596 tools: List of tools (strings or MSVSTool objects); may be None.
597 **attrs: Configuration attributes.
598
599 Raises:
600 ValueError: Relative path does not match any file added via AddFiles().
601 """
602 node = self.env.File(path)
603 if not self.FindFile(node):
604 raise ValueError('AddFileConfig: file "%s" not in project' % path)
605 c = MSVSConfig(Name, 'FileConfiguration', tools=tools, **attrs)
606 config_list = self.file_configurations.get(node)
607 if config_list is None:
608 config_list = []
609 self.file_configurations[node] = config_list
610 config_list.append(c)
611
612 def AddToolFile(self, path):
613 """Adds a tool file to the project.
614
615 Args:
616 path: Relative path from project to tool file.
617 """
618 tf = MSVSToolFile(self.env.File(path))
619 self.tool_files.append(tf)
620
621 def Create(self):
622 """Creates the project document.
623
624 Args:
625 name: Name of the project.
626 guid: GUID to use for project, if not None.
627 """
628 # Create XML doc
629 xml_impl = xml.dom.getDOMImplementation()
630 self.doc = xml_impl.createDocument(None, 'VisualStudioProject', None)
631
632 # Add attributes to root element
633 root = self.doc.documentElement
634 root.setAttribute('ProjectType', 'Visual C++')
635 root.setAttribute('Version', '8.00')
636 root.setAttribute('Name', self.msvs_name)
637 root.setAttribute('ProjectGUID', self.get_guid())
638 root.setAttribute('RootNamespace', self.root_namespace)
639 root.setAttribute('Keyword', 'Win32Proj')
640
641 # Add platform list
642 platforms = self.doc.createElement('Platforms')
643 root.appendChild(platforms)
644 n = self.doc.createElement('Platform')
645 n.setAttribute('Name', 'Win32')
646 platforms.appendChild(n)
647
648 # Add tool files section
649 tool_files = self.doc.createElement('ToolFiles')
650 root.appendChild(tool_files)
651 for tf in self.tool_files:
652 tool_files.appendChild(tf.CreateElement(self.doc, self))
653
654 # Add configurations section
655 configs = self.doc.createElement('Configurations')
656 root.appendChild(configs)
657 for c in self.configurations:
658 configs.appendChild(c.CreateElement(self.doc))
659
660 # Add empty References section
661 root.appendChild(self.doc.createElement('References'))
662
663 # Add files section
664 root.appendChild(self.files.CreateElement(self.doc, self.CreateFileElement))
665
666 # Add empty Globals section
667 root.appendChild(self.doc.createElement('Globals'))
668
669 def CreateFileElement(self, file):
670 """Create a DOM node for the specified file.
671
672 Args:
673 file: The file Node being considered.
674
675 Returns:
676 A DOM Node for the File, with a relative path to the current
677 project object, and any file configurations attached to the
678 project.
679 """
680
681 node = self.doc.createElement('File')
682 node.setAttribute('RelativePath', self.get_rel_path(file))
683 for c in self.file_configurations.get(file, []):
684 node.appendChild(c.CreateElement(self.doc))
685 return node
686
687 def _AddFileConfigurationDifferences(self, target, source, base_env, file_env) :
688 """Adds a per-file configuration.
689
690 Args:
691 target: The target being built from the source.
692 source: The source to which the file configuration is being added.
693 base_env: The base construction environment for the project.
694 Differences from this will go into the FileConfiguration
695 in the project file.
696 file_env: The construction environment for the target, containing
697 the per-target settings.
698 """
699 pass
700
701 def _AddFileConfigurations(self, env):
702 """Adds per-file configurations for the buildtarget's sources.
703
704 Args:
705 env: The base construction environment for the project.
706 """
707 if not self.buildtargets:
708 return
709
710 bt = self.buildtargets[0]
711 additional_files = []
712 for t in bt.sources:
713 e = t.get_build_env()
714 for s in t.sources:
715 s = env.arg2nodes([s])[0]
716 if not self.FindFile(s):
717 additional_files.append(s)
718 if not env is e:
719 self._AddFileConfigurationDifferences(t, s, env, e)
720 self.AddFiles(additional_files)
721
722 def Write(self, env):
723 """Writes the project file."""
724 self._AddFileConfigurations(env)
725
726 self.Create()
727
728 f = open(str(self.msvs_node), 'wt')
729 f.write(self.formatMSVSProjectXML(self.doc))
730 f.close()
731
732 # Methods for formatting XML as nearly identically to Microsoft's
733 # .vcproj output as we can practically make it.
734 #
735 # The general design here is copied from:
736 #
737 # Bruce Eckels' MindView, Inc: 12-09-04 XML Oddyssey
738 # http://www.mindview.net/WebLog/log-0068
739 #
740 # Eckels' implementation broke up long tag definitions for readability,
741 # in much the same way that .vcproj does, but we've modified things
742 # for .vcproj quirks (like some tags *always* terminating with </Tag>,
743 # even when empty).
744
745 encoding = 'Windows-1252'
746
747 def formatMSVSProjectXML(self, xmldoc):
748 xmldoc = xmldoc.toprettyxml("", "\n", encoding=self.encoding)
749 # Remove trailing whitespace from each line:
750 xmldoc = "\n".join(
751 [line.rstrip() for line in xmldoc.split("\n")])
752 # Remove all empty lines before opening '<':
753 while xmldoc.find("\n\n<") != -1:
754 xmldoc = xmldoc.replace("\n\n<", "\n<")
755 dom = xml.dom.minidom.parseString(xmldoc)
756 xmldoc = dom.toprettyxml("\t", "", encoding=self.encoding)
757 xmldoc = xmldoc.replace('?><', '?>\n<')
758 xmldoc = self.reformatLines(xmldoc)
759 return xmldoc
760
761 def reformatLines(self, xmldoc):
762 result = []
763 for line in [line.rstrip() for line in xmldoc.split("\n")]:
764 if line.lstrip().startswith("<"):
765 result.append(self.reformatLine(line) + "\n")
766 else:
767 result.append(line + "\n")
768 return ''.join(result)
769
770 # Keyword order for specific tags.
771 #
772 # Listed keywords will come first and in the specified order.
773 # Any unlisted keywords come after, in whatever order they appear
774 # in the input config. In theory this means we would only *have* to
775 # list the keywords that we care about, but in practice we'll probably
776 # want to nail down Visual Studio's order to make sure we match them
777 # as nearly as possible.
778
779 order = {
780 'Configuration' : [
781 'Name',
782 'ConfigurationType',
783 'InheritedPropertySheets',
784 ],
785 'FileConfiguration' : [
786 'Name',
787 'ExcludedFromBuild',
788 ],
789 'Tool' : [
790 'Name',
791 'DisableSpecificWarnings',
792 ],
793 'VisualStudioProject' : [
794 'ProjectType',
795 'Version',
796 'Name',
797 'ProjectGUID',
798 'RootNamespace',
799 'Keyword',
800 ],
801 }
802
803 force_closing_tag = [
804 'File',
805 'Globals',
806 'References',
807 'ToolFiles'
808 ]
809
810 oneLiner = re.compile("(\s*)<(\w+)(.*)>")
811 keyValuePair = re.compile('\w+="[^"]*?"')
812 def reformatLine(self, line):
813 """Reformat an xml tag to put each key-value
814 element on a single indented line, for readability"""
815 matchobj = self.oneLiner.match(line.rstrip())
816 if not matchobj:
817 return line
818 baseIndent, tag, rest = matchobj.groups()
819 slash = ''
820 if rest[-1:] == '/':
821 slash = '/'
822 rest = rest[:-1]
823 result = [baseIndent + '<' + tag]
824 indent = baseIndent + "\t"
825 pairs = self.keyValuePair.findall(rest)
826 for key in self.order.get(tag, []):
827 for p in [ p for p in pairs if p.startswith(key+'=') ]:
828 result.append("\n" + indent + p)
829 pairs.remove(p)
830 for pair in pairs:
831 result.append("\n" + indent + pair)
832 result = [''.join(result).rstrip()]
833
834 if tag in self.force_closing_tag:
835 # These force termination with </Tag>, so translate slash.
836 if rest:
837 result.append("\n")
838 result.append(indent)
839 result.append(">")
840 if slash:
841 result.append("\n")
842 result.append(baseIndent + "</" + tag + ">")
843 else:
844 if rest:
845 result.append("\n")
846 if slash:
847 result.append(baseIndent)
848 else:
849 result.append(indent)
850 result.append(slash + ">")
851
852 return ''.join(result)
853
223 def MSVSProject(env, item, *args, **kw): 854 def MSVSProject(env, item, *args, **kw):
224 if not SCons.Util.is_String(item): 855 if not SCons.Util.is_String(item):
225 return item 856 return item
226 item = env.subst(item) 857 item = env.subst(item)
227 result = env.fs._lookup(item, None, _MSVSProject, create=1) 858 result = env.fs._lookup(item, None, _MSVSProject, create=1)
228 result.initialize(item, *args, **kw) 859 result.initialize(env, item, *args, **kw)
229 LookupAdd(item, result) 860 LookupAdd(item, result)
230 return result 861 return result
231 862
232 #------------------------------------------------------------------------------ 863 #------------------------------------------------------------------------------
233 864
234 def MSVSAction(target, source, env):
235 target[0].Write(env)
236
237 MSVSSolutionAction = SCons.Script.Action(MSVSAction, 865 MSVSSolutionAction = SCons.Script.Action(MSVSAction,
238 "Generating Visual Studio solution `$TA RGET' ...") 866 "Generating Visual Studio solution `$TA RGET' ...")
239 867
240 class _MSVSSolution(SCons.Node.FS.File): 868 class _MSVSSolution(SCons.Node.FS.File):
241 """Visual Studio solution.""" 869 """Visual Studio solution."""
242 870
243 def initialize(self, env, path, entries = None, variants = None, 871 def initialize(self, env, path, entries = None, variants = None, websiteProper ties = True):
244 websiteProperties = True):
245 """Initializes the solution. 872 """Initializes the solution.
246 873
247 Args: 874 Args:
248 path: Path to solution file. 875 path: Path to solution file.
249 entries: List of entries in solution. May contain Folder or Project 876 entries: List of entries in solution. May contain Folder or Project
250 objects. May be None, if the folder is empty. 877 objects. May be None, if the folder is empty.
251 variants: List of build variant strings. If none, a default list will 878 variants: List of build variant strings. If none, a default list will
252 be used. 879 be used.
253 """ 880 """
254 self.msvs_path = path 881 self.msvs_path = path
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after
425 f.close() 1052 f.close()
426 1053
427 def MSVSSolution(env, item, *args, **kw): 1054 def MSVSSolution(env, item, *args, **kw):
428 if not SCons.Util.is_String(item): 1055 if not SCons.Util.is_String(item):
429 return item 1056 return item
430 item = env.subst(item) 1057 item = env.subst(item)
431 result = env.fs._lookup(item, None, _MSVSSolution, create=1) 1058 result = env.fs._lookup(item, None, _MSVSSolution, create=1)
432 result.initialize(env, item, *args, **kw) 1059 result.initialize(env, item, *args, **kw)
433 LookupAdd(item, result) 1060 LookupAdd(item, result)
434 return result 1061 return result
1062
1063 import __builtin__
1064
1065 __builtin__.MSVSFilter = MSVSFilter
1066 __builtin__.MSVSProject = MSVSProject
1067 __builtin__.MSVSSolution = MSVSSolution
1068 __builtin__.MSVSTool = MSVSTool
OLDNEW
« no previous file with comments | « sandbox/sandbox_sln.scons ('k') | site_scons/site_tools/chromium_builders.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698