OLD | NEW |
1 # | 1 # |
2 # __COPYRIGHT__ | 2 # __COPYRIGHT__ |
3 # | 3 # |
4 # Permission is hereby granted, free of charge, to any person obtaining | 4 # Permission is hereby granted, free of charge, to any person obtaining |
5 # a copy of this software and associated documentation files (the | 5 # a copy of this software and associated documentation files (the |
6 # "Software"), to deal in the Software without restriction, including | 6 # "Software"), to deal in the Software without restriction, including |
7 # without limitation the rights to use, copy, modify, merge, publish, | 7 # without limitation the rights to use, copy, modify, merge, publish, |
8 # distribute, sublicense, and/or sell copies of the Software, and to | 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 | 9 # permit persons to whom the Software is furnished to do so, subject to |
10 # the following conditions: | 10 # the following conditions: |
(...skipping 21 matching lines...) Expand all Loading... |
32 import os | 32 import os |
33 import random | 33 import random |
34 import re | 34 import re |
35 import UserList | 35 import UserList |
36 import xml.dom | 36 import xml.dom |
37 import xml.dom.minidom | 37 import xml.dom.minidom |
38 | 38 |
39 import SCons.Node.FS | 39 import SCons.Node.FS |
40 import SCons.Script | 40 import SCons.Script |
41 | 41 |
42 from SCons.Debug import Trace | |
43 TODO = 0 | |
44 | 42 |
45 # Initialize random number generator | 43 # Initialize random number generator |
46 random.seed() | 44 random.seed() |
47 | 45 |
48 | 46 |
49 #------------------------------------------------------------------------------ | 47 #------------------------------------------------------------------------------ |
50 # Entry point for supplying a fixed map of GUIDs for testing. | 48 # Entry point for supplying a fixed map of GUIDs for testing. |
51 | 49 |
52 GUIDMap = {} | 50 GUIDMap = {} |
53 | 51 |
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
259 def get_msvs_path(self, sln): | 257 def get_msvs_path(self, sln): |
260 return self.msvs_name | 258 return self.msvs_name |
261 | 259 |
262 def MSVSFolder(env, item, *args, **kw): | 260 def MSVSFolder(env, item, *args, **kw): |
263 return LookupCreate(_MSVSFolder, item, *args, **kw) | 261 return LookupCreate(_MSVSFolder, item, *args, **kw) |
264 | 262 |
265 #------------------------------------------------------------------------------ | 263 #------------------------------------------------------------------------------ |
266 | 264 |
267 class MSVSConfig(object): | 265 class MSVSConfig(object): |
268 """Visual Studio configuration.""" | 266 """Visual Studio configuration.""" |
269 def __init__(self, Name, config_type, tools=None, **attrs): | 267 def __init__(self, Name, config_type, tools=[], **attrs): |
270 """Initializes the configuration. | 268 """Initializes the configuration. |
271 | 269 |
272 Args: | 270 Args: |
273 **attrs: Configuration attributes. | 271 **attrs: Configuration attributes. |
274 """ | 272 """ |
275 # Special handling for attributes that we want to make more | 273 # Special handling for attributes that we want to make more |
276 # convenient for the user. | 274 # convenient for the user. |
277 ips = attrs.get('InheritedPropertySheets') | 275 ips = attrs.get('InheritedPropertySheets') |
278 if ips: | 276 if ips: |
279 if isinstance(ips, list): | 277 if isinstance(ips, list): |
280 ips = ';'.join(ips) | 278 ips = ';'.join(ips) |
281 attrs['InheritedPropertySheets'] = ips.replace('/', '\\') | 279 attrs['InheritedPropertySheets'] = ips.replace('/', '\\') |
282 | 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 |
283 self.Name = Name | 290 self.Name = Name |
284 self.config_type = config_type | 291 self.config_type = config_type |
285 self.tools = tools | 292 self.tools = tool_objects |
286 self.attrs = attrs | 293 self.attrs = attrs |
287 | 294 |
288 def CreateElement(self, doc, project): | 295 def CreateElement(self, doc): |
289 """Creates an element for the configuration. | 296 """Creates an element for the configuration. |
290 | 297 |
291 Args: | 298 Args: |
292 doc: xml.dom.Document object to use for node creation. | 299 doc: xml.dom.Document object to use for node creation. |
293 | 300 |
294 Returns: | 301 Returns: |
295 A new xml.dom.Element for the configuration. | 302 A new xml.dom.Element for the configuration. |
296 """ | 303 """ |
297 node = doc.createElement(self.config_type) | 304 node = doc.createElement(self.config_type) |
298 node.setAttribute('Name', self.Name) | 305 node.setAttribute('Name', self.Name) |
299 for k, v in self.attrs.items(): | 306 for k, v in self.attrs.items(): |
300 node.setAttribute(k, v) | 307 node.setAttribute(k, v) |
301 | 308 for t in self.tools: |
302 tools = self.tools | |
303 if tools is None: | |
304 tools = project.tools or [] | |
305 if not SCons.Util.is_List(tools): | |
306 tools = [tools] | |
307 tool_objects = [] | |
308 for t in tools: | |
309 if not isinstance(t, MSVSTool): | |
310 t = MSVSTool(t) | |
311 tool_objects.append(t) | |
312 for t in tool_objects: | |
313 node.appendChild(t.CreateElement(doc)) | 309 node.appendChild(t.CreateElement(doc)) |
314 | |
315 return node | 310 return node |
316 | 311 |
317 | 312 |
318 class MSVSFileListBase(FileList): | 313 class MSVSFileListBase(FileList): |
319 """Base class for a file list in a Visual Studio project file.""" | 314 """Base class for a file list in a Visual Studio project file.""" |
320 | 315 |
321 def CreateElement(self, doc, node_func=lambda x: x): | 316 def CreateElement(self, doc, node_func=lambda x: x): |
322 """Creates an element for an MSVSFileListBase subclass. | 317 """Creates an element for an MSVSFileListBase subclass. |
323 | 318 |
324 Args: | 319 Args: |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
396 | 391 |
397 Returns: | 392 Returns: |
398 A new xml.dom.Element for the tool. | 393 A new xml.dom.Element for the tool. |
399 """ | 394 """ |
400 node = doc.createElement('Tool') | 395 node = doc.createElement('Tool') |
401 node.setAttribute('Name', self.Name) | 396 node.setAttribute('Name', self.Name) |
402 for k, v in self.attrs.items(): | 397 for k, v in self.attrs.items(): |
403 node.setAttribute(k, v) | 398 node.setAttribute(k, v) |
404 return node | 399 return node |
405 | 400 |
406 def _format(self): | |
407 """Formats a tool specification for debug printing""" | |
408 xml_impl = xml.dom.getDOMImplementation() | |
409 doc = xml_impl.createDocument(None, 'VisualStudioProject', None) | |
410 return self.CreateElement(doc).toprettyxml() | |
411 | |
412 def diff(self, other): | |
413 for key, value in self.attrs.items(): | |
414 if other.attrs[key] == value: | |
415 del self.attrs[key] | |
416 | |
417 | 401 |
418 class MSVSToolFile(object): | 402 class MSVSToolFile(object): |
419 """Visual Studio tool file specification.""" | 403 """Visual Studio tool file specification.""" |
420 | 404 |
421 def __init__(self, node, **attrs): | 405 def __init__(self, node, **attrs): |
422 """Initializes the tool. | 406 """Initializes the tool. |
423 | 407 |
424 Args: | 408 Args: |
425 node: Node for the Tool File | 409 node: Node for the Tool File |
426 **attrs: Tool File attributes. | 410 **attrs: Tool File attributes. |
(...skipping 11 matching lines...) Expand all Loading... |
438 def MSVSAction(target, source, env): | 422 def MSVSAction(target, source, env): |
439 target[0].Write(env) | 423 target[0].Write(env) |
440 | 424 |
441 MSVSProjectAction = SCons.Script.Action(MSVSAction, | 425 MSVSProjectAction = SCons.Script.Action(MSVSAction, |
442 "Generating Visual Studio project `$TARG
ET' ...") | 426 "Generating Visual Studio project `$TARG
ET' ...") |
443 | 427 |
444 class _MSVSProject(SCons.Node.FS.File): | 428 class _MSVSProject(SCons.Node.FS.File): |
445 """Visual Studio project.""" | 429 """Visual Studio project.""" |
446 | 430 |
447 entry_type_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}' | 431 entry_type_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}' |
448 initialized = False | |
449 | 432 |
450 def initialize(self, env, path, name = None, | 433 def initialize(self, env, path, name = None, |
451 dependencies = None, | 434 dependencies = None, |
452 guid = None, | 435 guid = None, |
453 buildtargets = [], | 436 buildtargets = [], |
454 files = [], | 437 files = [], |
455 root_namespace = None, | 438 root_namespace = None, |
456 relative_path_prefix = '', | 439 relative_path_prefix = '', |
457 tools = None, | 440 tools = None, |
458 configurations = None, | 441 configurations = None): |
459 **attrs): | |
460 """Initializes the project. | 442 """Initializes the project. |
461 | 443 |
462 Args: | 444 Args: |
463 path: Relative path to project file. | 445 path: Relative path to project file. |
464 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 |
465 name of the project file. | 447 name of the project file. |
466 dependencies: List of other Project objects this project is dependent | 448 dependencies: List of other Project objects this project is dependent |
467 upon, if not None. | 449 upon, if not None. |
468 guid: GUID to use for project, if not None. | 450 guid: GUID to use for project, if not None. |
469 buildtargets: List of target(s) being built by this project. | 451 buildtargets: List of target(s) being built by this project. |
470 files: List of source files for the project. This will be | 452 files: List of source files for the project. This will be |
471 supplemented by any source files of buildtargets. | 453 supplemented by any source files of buildtargets. |
472 root_namespace: The value of the RootNamespace attribute of the | 454 root_namespace: The value of the RootNamespace attribute of the |
473 project, if not None. The default is to use the same | 455 project, if not None. The default is to use the same |
474 string as the name. | 456 string as the name. |
475 relative_path_prefix: A prefix to be appended to the beginning of | 457 relative_path_prefix: A prefix to be appended to the beginning of |
476 every file name in the list. The canonical use is to specify | 458 every file name in the list. The canonical use is to specify |
477 './' to make files explicitly relative to the local directory. | 459 './' to make files explicitly relative to the local directory. |
478 tools: A list of MSVSTool objects or strings representing | 460 tools: A list of MSVSTool objects or strings representing |
479 tools to be used to build this project. This will be used | 461 tools to be used to build this project. This will be used |
480 for any configurations that don't provide their own | 462 for any configurations that don't provide their own |
481 per-configuration tool list. | 463 per-configuration tool list. |
482 configurations: A list of MSVSConfig objects representing | 464 configurations: A list of MSVSConfig objects representing |
483 configurations built by this project. | 465 configurations built by this project. |
484 """ | 466 """ |
| 467 self.msvs_path = path |
| 468 self.msvs_node = env.File(path) |
485 if name is None: | 469 if name is None: |
486 if buildtargets: | 470 if buildtargets: |
487 name = os.path.splitext(buildtargets[0].name)[0] | 471 name = os.path.splitext(buildtargets[0].name)[0] |
488 else: | 472 else: |
489 name = os.path.splitext(os.path.basename(path))[0] | 473 name = os.path.splitext(os.path.basename(path))[0] |
490 if not root_namespace: | 474 self.msvs_name = name |
491 root_namespace or 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 |
492 | 479 |
493 if self.initialized: | |
494 # TODO(sgk): fill in | |
495 if self.msvs_name != name: | |
496 pass | |
497 if self.root_namespace != root_namespace: | |
498 pass | |
499 if self.relative_path_prefix != relative_path_prefix: | |
500 pass | |
501 if self.guid != guid: | |
502 pass | |
503 #if self.env != env: | |
504 # pass | |
505 else: | |
506 self.buildtargets = [] | |
507 self.configurations = [] | |
508 self.dependencies = [] | |
509 self.file_configurations = {} | |
510 self.files = MSVSFiles([]) | |
511 self.tool_files = [] | |
512 self.file_lists = [] | |
513 self.initialized = True | |
514 | |
515 self.attrs = attrs | |
516 self.env = env | 480 self.env = env |
517 self.guid = guid | 481 self.guid = guid |
518 self.msvs_name = name | |
519 self.msvs_path = path | |
520 self.relative_path_prefix = relative_path_prefix | |
521 self.root_namespace = root_namespace or self.msvs_name | |
522 self.tools = tools | |
523 | 482 |
524 self.buildtargets.extend(buildtargets) | 483 self.dependencies = list(dependencies or []) |
525 self.configurations.extend(configurations or []) | 484 self.configurations = list(configurations or []) |
526 self.dependencies.extend(list(dependencies or [])) | 485 self.file_configurations = {} |
527 self.AddFiles(files) | 486 self.tool_files = [] |
528 | 487 |
529 env.Command(self, [], MSVSProjectAction) | 488 if not isinstance(files, MSVSFiles): |
| 489 files = MSVSFiles(self.args2nodes(files)) |
| 490 self.files = files |
| 491 |
| 492 env.Command(self, [], MSVSSolutionAction) |
530 | 493 |
531 def args2nodes(self, entries): | 494 def args2nodes(self, entries): |
532 result = [] | 495 result = [] |
533 for entry in entries: | 496 for entry in entries: |
534 if SCons.Util.is_String(entry): | 497 if SCons.Util.is_String(entry): |
535 entry = self.env.File(entry) | 498 entry = self.env.File(entry) |
536 result.append(entry) | 499 result.append(entry) |
537 elif hasattr(entry, 'entries'): | 500 elif hasattr(entry, 'entries'): |
538 entry.entries = self.args2nodes(entry.entries) | 501 entry.entries = self.args2nodes(entry.entries) |
539 result.append(entry) | 502 result.append(entry) |
540 elif isinstance(entry, (list, UserList.UserList)): | 503 elif isinstance(entry, (list, UserList.UserList)): |
541 result.extend(self.args2nodes(entry)) | 504 result.extend(self.args2nodes(entry)) |
542 elif hasattr(entry, 'sources') and entry.sources: | 505 elif hasattr(entry, 'sources') and entry.sources: |
543 result.extend(entry.sources) | 506 result.extend(entry.sources) |
544 else: | 507 else: |
545 result.append(entry.srcnode()) | 508 result.append(entry) |
546 return result | 509 return result |
547 | 510 |
548 def FindFile(self, node): | 511 def FindFile(self, node): |
549 try: | 512 try: |
550 flat_file_dict = self.flat_file_dict | 513 flat_file_dict = self.flat_file_dict |
551 except AttributeError: | 514 except AttributeError: |
552 flat_file_dict = {} | 515 flat_file_dict = {} |
553 file_list = self.files[:] | 516 file_list = self.files[:] |
554 while file_list: | 517 while file_list: |
555 entry = file_list.pop(0) | 518 entry = file_list.pop(0) |
556 if not isinstance(entry, (list, UserList.UserList)): | 519 if not isinstance(entry, (list, UserList.UserList)): |
557 entry = [entry] | 520 entry = [entry] |
558 for f in entry: | 521 for f in entry: |
559 if hasattr(f, 'entries'): | 522 if hasattr(f, 'entries'): |
560 file_list.extend(f.entries) | 523 file_list.extend(f.entries) |
561 else: | 524 else: |
562 flat_file_dict[f] = True | 525 flat_file_dict[f] = True |
563 flat_file_dict[f.srcnode()] = True | |
564 if hasattr(f, 'sources'): | 526 if hasattr(f, 'sources'): |
565 for s in f.sources: | 527 for s in f.sources: |
566 flat_file_dict[s] = True | 528 flat_file_dict[s] = True |
567 flat_file_dict[s.srcnode()] = True | |
568 self.flat_file_dict = flat_file_dict | 529 self.flat_file_dict = flat_file_dict |
569 | 530 |
570 return flat_file_dict.get(node) | 531 return flat_file_dict.get(node) |
571 | 532 |
572 def get_guid(self): | 533 def get_guid(self): |
573 if self.guid is None: | 534 if self.guid is None: |
574 guid = GUIDMap.get(self.msvs_path) | 535 guid = GUIDMap.get(self.msvs_path) |
575 if not guid: | 536 if not guid: |
576 # Set GUID from path | 537 # Set GUID from path |
577 # TODO(rspangler): This is fragile. | 538 # TODO(rspangler): This is fragile. |
578 # 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 |
579 # could be multiple projects with the same base name (for example, | 540 # could be multiple projects with the same base name (for example, |
580 # foo/unittest.vcproj and bar/unittest.vcproj). | 541 # foo/unittest.vcproj and bar/unittest.vcproj). |
581 # 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 |
582 # 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 |
583 # foo/bar/baz/baz.sln. | 544 # foo/bar/baz/baz.sln. |
584 # 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, |
585 # 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 |
586 # project changes. | 547 # project changes. |
587 # 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 |
588 # GUID from the files. | 549 # GUID from the files. |
589 guid = MakeGuid(self.msvs_path) | 550 guid = MakeGuid(self.msvs_path) |
590 self.guid = guid | 551 self.guid = guid |
591 return self.guid | 552 return self.guid |
592 | 553 |
593 def get_msvs_path(self, sln): | 554 def get_msvs_path(self, sln): |
594 return sln.rel_path(self).replace('/', '\\') | 555 return sln.rel_path(self).replace('/', '\\') |
595 | 556 |
596 def get_rel_path(self, node): | 557 def get_rel_path(self, node): |
597 result = self.relative_path_prefix + self.rel_path(node) | 558 result = self.relative_path_prefix + self.msvs_node.rel_path(node) |
598 return result.replace('/', '\\') | 559 return result.replace('/', '\\') |
599 | 560 |
600 def AddConfig(self, Name, tools=None, **attrs): | 561 def AddConfig(self, Name, tools=None, **attrs): |
601 """Adds a configuration to the parent node. | 562 """Adds a configuration to the parent node. |
602 | 563 |
603 Args: | 564 Args: |
604 Name: The name of the configuration. | 565 Name: The name of the configuration. |
605 tools: List of tools (strings or Tool objects); may be None. | 566 tools: List of tools (strings or Tool objects); may be None. |
606 **attrs: Configuration attributes. | 567 **attrs: Configuration attributes. |
607 """ | 568 """ |
608 if tools is None: | 569 if tools is None: |
609 # No tool list specifically for this configuration, | 570 # No tool list specifically for this configuration, |
610 # use the Project's as a default. | 571 # use the Project's as a default. |
611 tools = self.tools | 572 tools = self.tools |
612 if not attrs.has_key('ConfigurationType'): | |
613 # No ConfigurationType specifically for this configuration, | |
614 # use the Project's as a default. | |
615 try: | |
616 attrs['ConfigurationType'] = self.attrs['ConfigurationType'] | |
617 except KeyError: | |
618 pass | |
619 if attrs.has_key('InheritedPropertySheets'): | |
620 ips = attrs['InheritedPropertySheets'] | |
621 attrs['InheritedPropertySheets'] = self.env.subst(ips) | |
622 c = MSVSConfig(Name, 'Configuration', tools=tools, **attrs) | 573 c = MSVSConfig(Name, 'Configuration', tools=tools, **attrs) |
623 self.configurations.append(c) | 574 self.configurations.append(c) |
624 | 575 |
625 def AddFiles(self, files): | 576 def AddFiles(self, files): |
626 """Adds files to the project. | 577 """Adds files to the project. |
627 | 578 |
628 Args: | 579 Args: |
629 files: A list of Filter objects and/or relative paths to files. | 580 files: A list of Filter objects and/or relative paths to files. |
630 | 581 |
631 This makes a copy of the file/filter tree at the time of this call. If you | 582 This makes a copy of the file/filter tree at the time of this call. If you |
632 later add files to a Filter object which was passed into a previous call | 583 later add files to a Filter object which was passed into a previous call |
633 to AddFiles(), it will not be reflected in this project. | 584 to AddFiles(), it will not be reflected in this project. |
634 """ | 585 """ |
635 self.file_lists.append(self.args2nodes(files)) | 586 # TODO(rspangler) This also doesn't handle adding files to an existing |
636 | 587 # filter. That is, it doesn't merge the trees. |
637 def _FilesToSourceFiles(self, files): | 588 self.files.extend(self.args2nodes(files)) |
638 file_list = files[:] | |
639 result = [] | |
640 while file_list: | |
641 entry = file_list.pop(0) | |
642 if not isinstance(entry, (list, UserList.UserList)): | |
643 entry = [entry] | |
644 for f in entry: | |
645 if hasattr(f, 'entries'): | |
646 self._FilesToSourceFiles(f.entries) | |
647 result.append(f) | |
648 else: | |
649 if f.sources: | |
650 flist = f.sources | |
651 else: | |
652 flist = [f] | |
653 for x in flist: | |
654 result.append(x.srcnode()) | |
655 files[:] = result | |
656 | |
657 def _MergeFiles(self, dest_list, src_list): | |
658 for f in src_list: | |
659 if f not in dest_list: | |
660 dest_list.append(f) | |
661 continue | |
662 #if hasattr(f, 'entries'): | |
663 # self._FilesToSourceFiles(f.entries) | |
664 | 589 |
665 def AddFileConfig(self, path, Name, tools=None, **attrs): | 590 def AddFileConfig(self, path, Name, tools=None, **attrs): |
666 """Adds a configuration to a file. | 591 """Adds a configuration to a file. |
667 | 592 |
668 Args: | 593 Args: |
669 path: Relative path to the file. | 594 path: Relative path to the file. |
670 Name: Name of configuration to add. | 595 Name: Name of configuration to add. |
671 tools: List of tools (strings or MSVSTool objects); may be None. | 596 tools: List of tools (strings or MSVSTool objects); may be None. |
672 **attrs: Configuration attributes. | 597 **attrs: Configuration attributes. |
673 | 598 |
674 Raises: | 599 Raises: |
675 ValueError: Relative path does not match any file added via AddFiles(). | 600 ValueError: Relative path does not match any file added via AddFiles(). |
676 """ | 601 """ |
677 # Store as the VariantDir node, not as the source node. | |
678 node = self.env.File(path) | 602 node = self.env.File(path) |
| 603 if not self.FindFile(node): |
| 604 raise ValueError('AddFileConfig: file "%s" not in project' % path) |
679 c = MSVSConfig(Name, 'FileConfiguration', tools=tools, **attrs) | 605 c = MSVSConfig(Name, 'FileConfiguration', tools=tools, **attrs) |
680 config_list = self.file_configurations.get(node) | 606 config_list = self.file_configurations.get(node) |
681 if config_list is None: | 607 if config_list is None: |
682 config_list = [] | 608 config_list = [] |
683 self.file_configurations[node] = config_list | 609 self.file_configurations[node] = config_list |
684 config_list.append(c) | 610 config_list.append(c) |
685 | 611 |
686 def AddToolFile(self, path): | 612 def AddToolFile(self, path): |
687 """Adds a tool file to the project. | 613 """Adds a tool file to the project. |
688 | 614 |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
722 # Add tool files section | 648 # Add tool files section |
723 tool_files = self.doc.createElement('ToolFiles') | 649 tool_files = self.doc.createElement('ToolFiles') |
724 root.appendChild(tool_files) | 650 root.appendChild(tool_files) |
725 for tf in self.tool_files: | 651 for tf in self.tool_files: |
726 tool_files.appendChild(tf.CreateElement(self.doc, self)) | 652 tool_files.appendChild(tf.CreateElement(self.doc, self)) |
727 | 653 |
728 # Add configurations section | 654 # Add configurations section |
729 configs = self.doc.createElement('Configurations') | 655 configs = self.doc.createElement('Configurations') |
730 root.appendChild(configs) | 656 root.appendChild(configs) |
731 for c in self.configurations: | 657 for c in self.configurations: |
732 configs.appendChild(c.CreateElement(self.doc, self)) | 658 configs.appendChild(c.CreateElement(self.doc)) |
733 | 659 |
734 # Add empty References section | 660 # Add empty References section |
735 root.appendChild(self.doc.createElement('References')) | 661 root.appendChild(self.doc.createElement('References')) |
736 | 662 |
737 # Add files section | 663 # Add files section |
738 root.appendChild(self.files.CreateElement(self.doc, self.CreateFileElement)) | 664 root.appendChild(self.files.CreateElement(self.doc, self.CreateFileElement)) |
739 | 665 |
740 # Add empty Globals section | 666 # Add empty Globals section |
741 root.appendChild(self.doc.createElement('Globals')) | 667 root.appendChild(self.doc.createElement('Globals')) |
742 | 668 |
743 def CreateFileElement(self, file): | 669 def CreateFileElement(self, file): |
744 """Create a DOM node for the specified file. | 670 """Create a DOM node for the specified file. |
745 | 671 |
746 Args: | 672 Args: |
747 file: The file Node being considered. | 673 file: The file Node being considered. |
748 | 674 |
749 Returns: | 675 Returns: |
750 A DOM Node for the File, with a relative path to the current | 676 A DOM Node for the File, with a relative path to the current |
751 project object, and any file configurations attached to the | 677 project object, and any file configurations attached to the |
752 project. | 678 project. |
753 """ | 679 """ |
754 | 680 |
755 node = self.doc.createElement('File') | 681 node = self.doc.createElement('File') |
756 node.setAttribute('RelativePath', self.get_rel_path(file)) | 682 node.setAttribute('RelativePath', self.get_rel_path(file)) |
757 for c in self.file_configurations.get(file, []): | 683 for c in self.file_configurations.get(file, []): |
758 node.appendChild(c.CreateElement(self.doc, self)) | 684 node.appendChild(c.CreateElement(self.doc)) |
759 return node | 685 return node |
760 | 686 |
761 def VCCLCompilerTool(self, args): | 687 def _AddFileConfigurationDifferences(self, target, source, base_env, file_env)
: |
762 default_attrs = { | |
763 'BufferSecurityCheck' : "false", | |
764 'CompileAs' : 0, # default | |
765 'DebugInformationFormat' : 0, # TODO(???) | |
766 'DisableSpecificWarnings' : [], | |
767 'EnableFiberSafeOptimizations' : "false", | |
768 'EnableFunctionLevelLinking' : "false", | |
769 'EnableIntrinsicFunctions' : "false", | |
770 'FavorSizeOrSpeed' : 0, # favorNone | |
771 'InlineFunctionExpansion' : 1, # expandDisable | |
772 'MinimalRebuild' : "false", | |
773 'OmitFramePointers' : "false", | |
774 'Optimization' : 1, # optimizeDisabled TODO(???) | |
775 'PreprocessorDefinitions' : [], | |
776 'RuntimeLibrary' : TODO, | |
777 'RuntimeTypeInfo' : "false", | |
778 'StringPooling' : "false", | |
779 'SuppressStartupBanner' : "false", | |
780 'WarningAsError' : "false", | |
781 'WarningLevel' : 1, # warningLevel_1 | |
782 'WholeProgramOptimization' : "false", | |
783 } | |
784 | |
785 tool = MSVSTool('VCCLCompilerTool', **default_attrs) | |
786 attrs = tool.attrs | |
787 | |
788 for arg in args: | |
789 if arg in ('/c',): | |
790 continue | |
791 if arg.startswith('/Fo'): | |
792 continue | |
793 if arg.startswith('/D'): | |
794 attrs['PreprocessorDefinitions'].append(arg[2:]) | |
795 elif arg == '/EH': | |
796 attrs['ExceptionHandling'] = 0 | |
797 elif arg == '/GF': | |
798 attrs['StringPooling'] = "true" | |
799 elif arg == '/GL': | |
800 attrs['WholeProgramOptimization'] = "true" | |
801 elif arg == '/GM': | |
802 attrs['MinimalRebuild'] = "true" | |
803 elif arg == '/GR-': | |
804 attrs['RuntimeTypeInfo'] = "true" | |
805 elif arg == '/Gs': | |
806 attrs['BufferSecurityCheck'] = "true" | |
807 elif arg == '/Gs-': | |
808 attrs['BufferSecurityCheck'] = "false" | |
809 elif arg == '/GT': | |
810 attrs['EnableFiberSafeOptimizations'] = "true" | |
811 elif arg == '/Gy': | |
812 attrs['EnableFunctionLevelLinking'] = "true" | |
813 elif arg == '/MD': | |
814 attrs['RuntimeLibrary'] = 1 # rtMultiThreadedDebug | |
815 elif arg == '/MDd': | |
816 attrs['RuntimeLibrary'] = 2 # rtMultiThreadedDebugDLL | |
817 elif arg == '/MT': | |
818 attrs['RuntimeLibrary'] = 0 # rtMultiThreaded | |
819 elif arg == '/MTd': | |
820 attrs['RuntimeLibrary'] = 3 # rtMultiThreadedDLL | |
821 elif arg == '/nologo': | |
822 attrs['SuppressStartupBanner'] = "true" | |
823 elif arg == '/O1': | |
824 attrs['InlineFunctionExpansion'] = 4 # optimizeMinSpace | |
825 elif arg == '/O2': | |
826 attrs['InlineFunctionExpansion'] = 3 # optimizeMaxSpeed | |
827 elif arg == '/Ob1': | |
828 attrs['InlineFunctionExpansion'] = 2 # expandOnlyInline | |
829 elif arg == '/Ob2': | |
830 attrs['InlineFunctionExpansion'] = 0 # expandAnySuitable | |
831 elif arg == '/Od': | |
832 attrs['Optimization'] = 0 | |
833 elif arg == '/Oi': | |
834 attrs['EnableIntrinsicFunctions'] = "true" | |
835 elif arg == '/Os': | |
836 attrs['FavorSizeOrSpeed'] = 1 # favorSize | |
837 elif arg == '/Ot': | |
838 attrs['FavorSizeOrSpeed'] = 2 # favorSpeed | |
839 elif arg == '/Ox': | |
840 attrs['Optimization'] = 2 # optimizeFull | |
841 elif arg == '/Oy': | |
842 attrs['OmitFramePointers'] = "true" | |
843 elif arg == '/Oy-': | |
844 attrs['TODO'] = "true" | |
845 elif arg in ('/Tc', '/TC'): | |
846 attrs['CompileAs'] = 1 # compileAsC | |
847 elif arg in ('/Tp', '/TP'): | |
848 attrs['CompileAs'] = 2 # compileAsCPlusPlus | |
849 elif arg == '/WX': | |
850 attrs['WarnAsError'] = "true" | |
851 elif arg.startswith('/W'): | |
852 attrs['WarningLevel'] = int(arg[2:]) # 0 through 4 | |
853 elif arg.startswith('/wd'): | |
854 attrs['DisableSpecificWarnings'].append(str(arg[3:])) | |
855 elif arg == '/Z7': | |
856 attrs['DebugInformationFormat'] = 3 # debugOldSytleInfo TODO(???) | |
857 elif arg == '/Zd': | |
858 attrs['DebugInformationFormat'] = 0 # debugDisabled | |
859 elif arg == '/Zi': | |
860 attrs['DebugInformationFormat'] = 2 # debugEnabled TODO(???) | |
861 elif arg == '/ZI': | |
862 attrs['DebugInformationFormat'] = 1 # debugEditAndContinue TODO(???) | |
863 | |
864 cppdefines = attrs['PreprocessorDefinitions'] | |
865 if cppdefines: | |
866 attrs['PreprocessorDefinitions'] = ';'.join(cppdefines) | |
867 warnings = attrs['DisableSpecificWarnings'] | |
868 if warnings: | |
869 warnings = SCons.Util.uniquer(warnings) | |
870 attrs['DisableSpecificWarnings'] = ';'.join(warnings) | |
871 | |
872 return tool | |
873 | |
874 def VCLibrarianTool(self, args): | |
875 default_attrs = { | |
876 'LinkTimeCodeGeneration' : "false", | |
877 'SuppressStartupBanner' : "false", | |
878 } | |
879 | |
880 tool = MSVSTool('VCLibrarianTool', **default_attrs) | |
881 attrs = tool.attrs | |
882 | |
883 for arg in args: | |
884 if arg.startswith('/OUT'): | |
885 continue | |
886 if arg == '/ltcg': | |
887 attrs['LinkTimeCodeGeneration'] = "true" | |
888 elif arg == '/nologo': | |
889 attrs['SuppressStartupBanner'] = "true" | |
890 | |
891 return tool | |
892 | |
893 def VCLinkerTool(self, args): | |
894 default_attrs = { | |
895 'LinkIncremental' : "false", | |
896 'LinkTimeCodeGeneration' : "false", | |
897 'EnableCOMDATFolding' : TODO, | |
898 'OptimizeForWindows98' : TODO, | |
899 'OptimizeReferences' : TODO, | |
900 'Profile' : "false", | |
901 'SuppressStartupBanner' : "false", | |
902 } | |
903 | |
904 tool = MSVSTool('VCLinkerTool', **default_attrs) | |
905 attrs = tool.attrs | |
906 | |
907 for arg in args: | |
908 if arg == '': | |
909 continue | |
910 if arg == '/INCREMENTAL': | |
911 attrs['LinkIncremental'] = "true" | |
912 elif arg == '/INCREMENTAL:NO': | |
913 attrs['LinkIncremental'] = "false" | |
914 elif arg == '/LTCG': | |
915 attrs['LinkTimeCodeGeneration'] = "true" | |
916 elif arg == '/nologo': | |
917 attrs['SuppressStartupBanner'] = "true" | |
918 elif arg == '/OPT:NOICF': | |
919 attrs['EnableCOMDATFolding'] = 2 # | |
920 elif arg == '/OPT:NOWIN98': | |
921 attrs['OptimizeForWindows98'] = 1 # | |
922 elif arg == '/OPT:REF': | |
923 attrs['OptimizeReferences'] = 2 # | |
924 elif arg == '/PROFILE': | |
925 attrs['Profile'] = "true" | |
926 | |
927 return tool | |
928 | |
929 command_to_tool_map = { | |
930 'cl' : 'VCCLCompilerTool', | |
931 'cl.exe' : 'VCCLCompilerTool', | |
932 'lib' : 'VCLibrarianTool', | |
933 'lib.exe' : 'VCLibrarianTool', | |
934 'link' : 'VCLinkerTool', | |
935 'link.exe' : 'VCLinkerTool', | |
936 } | |
937 | |
938 def cl_to_tool(self, args): | |
939 command = os.path.basename(args[0]) | |
940 method_name = self.command_to_tool_map.get(command) | |
941 if not method_name: | |
942 return None | |
943 return getattr(self, method_name)(args[1:]) | |
944 | |
945 def _AddFileConfigurationDifferences(self, target, source, base_env, file_env,
name): | |
946 """Adds a per-file configuration. | 688 """Adds a per-file configuration. |
947 | 689 |
948 Args: | 690 Args: |
949 target: The target being built from the source. | 691 target: The target being built from the source. |
950 source: The source to which the file configuration is being added. | 692 source: The source to which the file configuration is being added. |
951 base_env: The base construction environment for the project. | 693 base_env: The base construction environment for the project. |
952 Differences from this will go into the FileConfiguration | 694 Differences from this will go into the FileConfiguration |
953 in the project file. | 695 in the project file. |
954 file_env: The construction environment for the target, containing | 696 file_env: The construction environment for the target, containing |
955 the per-target settings. | 697 the per-target settings. |
956 """ | 698 """ |
957 executor = target.get_executor() | 699 pass |
958 base_cl = map(str, base_env.subst_list(executor)[0]) | |
959 file_cl = map(str, file_env.subst_list(executor)[0]) | |
960 if base_cl == file_cl: | |
961 return | |
962 | |
963 base_tool = self.cl_to_tool(base_cl) | |
964 file_tool = self.cl_to_tool(file_cl) | |
965 | |
966 file_tool.diff(base_tool) | |
967 | |
968 self.AddFileConfig(source, name, tools=[file_tool]) | |
969 | 700 |
970 def _AddFileConfigurations(self, env): | 701 def _AddFileConfigurations(self, env): |
971 """Adds per-file configurations for the buildtarget's sources. | 702 """Adds per-file configurations for the buildtarget's sources. |
972 | 703 |
973 Args: | 704 Args: |
974 env: The base construction environment for the project. | 705 env: The base construction environment for the project. |
975 """ | 706 """ |
976 if not self.buildtargets: | 707 if not self.buildtargets: |
977 return | 708 return |
978 | 709 |
979 for bt in self.buildtargets: | 710 bt = self.buildtargets[0] |
980 executor = bt.get_executor() | 711 additional_files = [] |
981 build_env = bt.get_build_env() | 712 for t in bt.sources: |
982 bt_cl = map(str, build_env.subst_list(executor)[0]) | |
983 tool = self.cl_to_tool(bt_cl) | |
984 default_tool = self.cl_to_tool([bt_cl[0]]) | |
985 if default_tool: | |
986 tool.diff(default_tool) | |
987 else: | |
988 print "no tool for %r" % bt_cl[0] | |
989 for t in bt.sources: | |
990 e = t.get_build_env() | 713 e = t.get_build_env() |
991 additional_files = SCons.Util.UniqueList() | |
992 for s in t.sources: | 714 for s in t.sources: |
993 s = env.arg2nodes([s])[0].srcnode() | 715 s = env.arg2nodes([s])[0] |
994 if not self.FindFile(s): | 716 if not self.FindFile(s): |
995 additional_files.append(s) | 717 additional_files.append(s) |
996 if not build_env is e: | 718 if not env is e: |
997 # TODO(sgk): This test may be bogus, but it works for now. | 719 self._AddFileConfigurationDifferences(t, s, env, e) |
998 # We're trying to figure out if the file configuration | 720 self.AddFiles(additional_files) |
999 # differences need to be added one per build target, or one | |
1000 # per configuration for the entire project. The assumption | |
1001 # is that if the number of buildtargets configured matches | |
1002 # the number of project configurations, that we use those | |
1003 # in preference to the project configurations. | |
1004 if len(self.buildtargets) == len(self.configurations): | |
1005 self._AddFileConfigurationDifferences(t, s, build_env, e, e.subst(
'$MSVSCONFIGURATIONNAME')) | |
1006 else: | |
1007 for config in self.configurations: | |
1008 self._AddFileConfigurationDifferences(t, s, build_env, e, config
.Name) | |
1009 self._MergeFiles(self.files, additional_files) | |
1010 | 721 |
1011 def Write(self, env): | 722 def Write(self, env): |
1012 """Writes the project file.""" | 723 """Writes the project file.""" |
1013 for flist in self.file_lists: | |
1014 self._FilesToSourceFiles(flist) | |
1015 self._MergeFiles(self.files, flist) | |
1016 for k, v in self.file_configurations.items(): | |
1017 self.file_configurations[str(k)] = v | |
1018 k = self.env.File(k).srcnode() | |
1019 self.file_configurations[k] = v | |
1020 self.file_configurations[str(k)] = v | |
1021 self._AddFileConfigurations(env) | 724 self._AddFileConfigurations(env) |
1022 | 725 |
1023 self.Create() | 726 self.Create() |
1024 | 727 |
1025 f = open(str(self), 'wt') | 728 f = open(str(self.msvs_node), 'wt') |
1026 f.write(self.formatMSVSProjectXML(self.doc)) | 729 f.write(self.formatMSVSProjectXML(self.doc)) |
1027 f.close() | 730 f.close() |
1028 | 731 |
1029 # Methods for formatting XML as nearly identically to Microsoft's | 732 # Methods for formatting XML as nearly identically to Microsoft's |
1030 # .vcproj output as we can practically make it. | 733 # .vcproj output as we can practically make it. |
1031 # | 734 # |
1032 # The general design here is copied from: | 735 # The general design here is copied from: |
1033 # | 736 # |
1034 # Bruce Eckels' MindView, Inc: 12-09-04 XML Oddyssey | 737 # Bruce Eckels' MindView, Inc: 12-09-04 XML Oddyssey |
1035 # http://www.mindview.net/WebLog/log-0068 | 738 # http://www.mindview.net/WebLog/log-0068 |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1079 'ConfigurationType', | 782 'ConfigurationType', |
1080 'InheritedPropertySheets', | 783 'InheritedPropertySheets', |
1081 ], | 784 ], |
1082 'FileConfiguration' : [ | 785 'FileConfiguration' : [ |
1083 'Name', | 786 'Name', |
1084 'ExcludedFromBuild', | 787 'ExcludedFromBuild', |
1085 ], | 788 ], |
1086 'Tool' : [ | 789 'Tool' : [ |
1087 'Name', | 790 'Name', |
1088 'DisableSpecificWarnings', | 791 'DisableSpecificWarnings', |
1089 | |
1090 'PreprocessorDefinitions', | |
1091 'UsePrecompiledHeader', | |
1092 'PrecompiledHeaderThrough', | |
1093 'ForcedIncludeFiles', | |
1094 ], | 792 ], |
1095 'VisualStudioProject' : [ | 793 'VisualStudioProject' : [ |
1096 'ProjectType', | 794 'ProjectType', |
1097 'Version', | 795 'Version', |
1098 'Name', | 796 'Name', |
1099 'ProjectGUID', | 797 'ProjectGUID', |
1100 'RootNamespace', | 798 'RootNamespace', |
1101 'Keyword', | 799 'Keyword', |
1102 ], | 800 ], |
1103 } | 801 } |
(...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1357 if not SCons.Util.is_String(item): | 1055 if not SCons.Util.is_String(item): |
1358 return item | 1056 return item |
1359 item = env.subst(item) | 1057 item = env.subst(item) |
1360 result = env.fs._lookup(item, None, _MSVSSolution, create=1) | 1058 result = env.fs._lookup(item, None, _MSVSSolution, create=1) |
1361 result.initialize(env, item, *args, **kw) | 1059 result.initialize(env, item, *args, **kw) |
1362 LookupAdd(item, result) | 1060 LookupAdd(item, result) |
1363 return result | 1061 return result |
1364 | 1062 |
1365 import __builtin__ | 1063 import __builtin__ |
1366 | 1064 |
1367 __builtin__.MSVSConfig = MSVSConfig | |
1368 __builtin__.MSVSFilter = MSVSFilter | 1065 __builtin__.MSVSFilter = MSVSFilter |
1369 __builtin__.MSVSProject = MSVSProject | 1066 __builtin__.MSVSProject = MSVSProject |
1370 __builtin__.MSVSSolution = MSVSSolution | 1067 __builtin__.MSVSSolution = MSVSSolution |
1371 __builtin__.MSVSTool = MSVSTool | 1068 __builtin__.MSVSTool = MSVSTool |
OLD | NEW |