| 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 |
| 42 | 44 |
| 43 # Initialize random number generator | 45 # Initialize random number generator |
| 44 random.seed() | 46 random.seed() |
| 45 | 47 |
| 46 | 48 |
| 47 #------------------------------------------------------------------------------ | 49 #------------------------------------------------------------------------------ |
| 48 # Entry point for supplying a fixed map of GUIDs for testing. | 50 # Entry point for supplying a fixed map of GUIDs for testing. |
| 49 | 51 |
| 50 GUIDMap = {} | 52 GUIDMap = {} |
| 51 | 53 |
| (...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 257 def get_msvs_path(self, sln): | 259 def get_msvs_path(self, sln): |
| 258 return self.msvs_name | 260 return self.msvs_name |
| 259 | 261 |
| 260 def MSVSFolder(env, item, *args, **kw): | 262 def MSVSFolder(env, item, *args, **kw): |
| 261 return LookupCreate(_MSVSFolder, item, *args, **kw) | 263 return LookupCreate(_MSVSFolder, item, *args, **kw) |
| 262 | 264 |
| 263 #------------------------------------------------------------------------------ | 265 #------------------------------------------------------------------------------ |
| 264 | 266 |
| 265 class MSVSConfig(object): | 267 class MSVSConfig(object): |
| 266 """Visual Studio configuration.""" | 268 """Visual Studio configuration.""" |
| 267 def __init__(self, Name, config_type, tools=[], **attrs): | 269 def __init__(self, Name, config_type, tools=None, **attrs): |
| 268 """Initializes the configuration. | 270 """Initializes the configuration. |
| 269 | 271 |
| 270 Args: | 272 Args: |
| 271 **attrs: Configuration attributes. | 273 **attrs: Configuration attributes. |
| 272 """ | 274 """ |
| 273 # Special handling for attributes that we want to make more | 275 # Special handling for attributes that we want to make more |
| 274 # convenient for the user. | 276 # convenient for the user. |
| 275 ips = attrs.get('InheritedPropertySheets') | 277 ips = attrs.get('InheritedPropertySheets') |
| 276 if ips: | 278 if ips: |
| 277 if isinstance(ips, list): | 279 if isinstance(ips, list): |
| 278 ips = ';'.join(ips) | 280 ips = ';'.join(ips) |
| 279 attrs['InheritedPropertySheets'] = ips.replace('/', '\\') | 281 attrs['InheritedPropertySheets'] = ips.replace('/', '\\') |
| 280 | 282 |
| 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 | 283 self.Name = Name |
| 291 self.config_type = config_type | 284 self.config_type = config_type |
| 292 self.tools = tool_objects | 285 self.tools = tools |
| 293 self.attrs = attrs | 286 self.attrs = attrs |
| 294 | 287 |
| 295 def CreateElement(self, doc): | 288 def CreateElement(self, doc, project): |
| 296 """Creates an element for the configuration. | 289 """Creates an element for the configuration. |
| 297 | 290 |
| 298 Args: | 291 Args: |
| 299 doc: xml.dom.Document object to use for node creation. | 292 doc: xml.dom.Document object to use for node creation. |
| 300 | 293 |
| 301 Returns: | 294 Returns: |
| 302 A new xml.dom.Element for the configuration. | 295 A new xml.dom.Element for the configuration. |
| 303 """ | 296 """ |
| 304 node = doc.createElement(self.config_type) | 297 node = doc.createElement(self.config_type) |
| 305 node.setAttribute('Name', self.Name) | 298 node.setAttribute('Name', self.Name) |
| 306 for k, v in self.attrs.items(): | 299 for k, v in self.attrs.items(): |
| 307 node.setAttribute(k, v) | 300 node.setAttribute(k, v) |
| 308 for t in self.tools: | 301 |
| 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: |
| 309 node.appendChild(t.CreateElement(doc)) | 313 node.appendChild(t.CreateElement(doc)) |
| 314 |
| 310 return node | 315 return node |
| 311 | 316 |
| 312 | 317 |
| 313 class MSVSFileListBase(FileList): | 318 class MSVSFileListBase(FileList): |
| 314 """Base class for a file list in a Visual Studio project file.""" | 319 """Base class for a file list in a Visual Studio project file.""" |
| 315 | 320 |
| 316 def CreateElement(self, doc, node_func=lambda x: x): | 321 def CreateElement(self, doc, node_func=lambda x: x): |
| 317 """Creates an element for an MSVSFileListBase subclass. | 322 """Creates an element for an MSVSFileListBase subclass. |
| 318 | 323 |
| 319 Args: | 324 Args: |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 391 | 396 |
| 392 Returns: | 397 Returns: |
| 393 A new xml.dom.Element for the tool. | 398 A new xml.dom.Element for the tool. |
| 394 """ | 399 """ |
| 395 node = doc.createElement('Tool') | 400 node = doc.createElement('Tool') |
| 396 node.setAttribute('Name', self.Name) | 401 node.setAttribute('Name', self.Name) |
| 397 for k, v in self.attrs.items(): | 402 for k, v in self.attrs.items(): |
| 398 node.setAttribute(k, v) | 403 node.setAttribute(k, v) |
| 399 return node | 404 return node |
| 400 | 405 |
| 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 |
| 401 | 417 |
| 402 class MSVSToolFile(object): | 418 class MSVSToolFile(object): |
| 403 """Visual Studio tool file specification.""" | 419 """Visual Studio tool file specification.""" |
| 404 | 420 |
| 405 def __init__(self, node, **attrs): | 421 def __init__(self, node, **attrs): |
| 406 """Initializes the tool. | 422 """Initializes the tool. |
| 407 | 423 |
| 408 Args: | 424 Args: |
| 409 node: Node for the Tool File | 425 node: Node for the Tool File |
| 410 **attrs: Tool File attributes. | 426 **attrs: Tool File attributes. |
| (...skipping 11 matching lines...) Expand all Loading... |
| 422 def MSVSAction(target, source, env): | 438 def MSVSAction(target, source, env): |
| 423 target[0].Write(env) | 439 target[0].Write(env) |
| 424 | 440 |
| 425 MSVSProjectAction = SCons.Script.Action(MSVSAction, | 441 MSVSProjectAction = SCons.Script.Action(MSVSAction, |
| 426 "Generating Visual Studio project `$TARG
ET' ...") | 442 "Generating Visual Studio project `$TARG
ET' ...") |
| 427 | 443 |
| 428 class _MSVSProject(SCons.Node.FS.File): | 444 class _MSVSProject(SCons.Node.FS.File): |
| 429 """Visual Studio project.""" | 445 """Visual Studio project.""" |
| 430 | 446 |
| 431 entry_type_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}' | 447 entry_type_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}' |
| 448 initialized = False |
| 432 | 449 |
| 433 def initialize(self, env, path, name = None, | 450 def initialize(self, env, path, name = None, |
| 434 dependencies = None, | 451 dependencies = None, |
| 435 guid = None, | 452 guid = None, |
| 436 buildtargets = [], | 453 buildtargets = [], |
| 437 files = [], | 454 files = [], |
| 438 root_namespace = None, | 455 root_namespace = None, |
| 439 relative_path_prefix = '', | 456 relative_path_prefix = '', |
| 440 tools = None, | 457 tools = None, |
| 441 configurations = None): | 458 configurations = None, |
| 459 **attrs): |
| 442 """Initializes the project. | 460 """Initializes the project. |
| 443 | 461 |
| 444 Args: | 462 Args: |
| 445 path: Relative path to project file. | 463 path: Relative path to project file. |
| 446 name: Name of project. If None, the name will be the same as the base | 464 name: Name of project. If None, the name will be the same as the base |
| 447 name of the project file. | 465 name of the project file. |
| 448 dependencies: List of other Project objects this project is dependent | 466 dependencies: List of other Project objects this project is dependent |
| 449 upon, if not None. | 467 upon, if not None. |
| 450 guid: GUID to use for project, if not None. | 468 guid: GUID to use for project, if not None. |
| 451 buildtargets: List of target(s) being built by this project. | 469 buildtargets: List of target(s) being built by this project. |
| 452 files: List of source files for the project. This will be | 470 files: List of source files for the project. This will be |
| 453 supplemented by any source files of buildtargets. | 471 supplemented by any source files of buildtargets. |
| 454 root_namespace: The value of the RootNamespace attribute of the | 472 root_namespace: The value of the RootNamespace attribute of the |
| 455 project, if not None. The default is to use the same | 473 project, if not None. The default is to use the same |
| 456 string as the name. | 474 string as the name. |
| 457 relative_path_prefix: A prefix to be appended to the beginning of | 475 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 | 476 every file name in the list. The canonical use is to specify |
| 459 './' to make files explicitly relative to the local directory. | 477 './' to make files explicitly relative to the local directory. |
| 460 tools: A list of MSVSTool objects or strings representing | 478 tools: A list of MSVSTool objects or strings representing |
| 461 tools to be used to build this project. This will be used | 479 tools to be used to build this project. This will be used |
| 462 for any configurations that don't provide their own | 480 for any configurations that don't provide their own |
| 463 per-configuration tool list. | 481 per-configuration tool list. |
| 464 configurations: A list of MSVSConfig objects representing | 482 configurations: A list of MSVSConfig objects representing |
| 465 configurations built by this project. | 483 configurations built by this project. |
| 466 """ | 484 """ |
| 467 self.msvs_path = path | |
| 468 self.msvs_node = env.File(path) | |
| 469 if name is None: | 485 if name is None: |
| 470 if buildtargets: | 486 if buildtargets: |
| 471 name = os.path.splitext(buildtargets[0].name)[0] | 487 name = os.path.splitext(buildtargets[0].name)[0] |
| 472 else: | 488 else: |
| 473 name = os.path.splitext(os.path.basename(path))[0] | 489 name = os.path.splitext(os.path.basename(path))[0] |
| 490 if not root_namespace: |
| 491 root_namespace or name |
| 492 |
| 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 |
| 517 self.guid = guid |
| 474 self.msvs_name = name | 518 self.msvs_name = name |
| 519 self.msvs_path = path |
| 520 self.relative_path_prefix = relative_path_prefix |
| 475 self.root_namespace = root_namespace or self.msvs_name | 521 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 | 522 self.tools = tools |
| 479 | 523 |
| 480 self.env = env | 524 self.buildtargets.extend(buildtargets) |
| 481 self.guid = guid | 525 self.configurations.extend(configurations or []) |
| 526 self.dependencies.extend(list(dependencies or [])) |
| 527 self.AddFiles(files) |
| 482 | 528 |
| 483 self.dependencies = list(dependencies or []) | 529 env.Command(self, [], MSVSProjectAction) |
| 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 | 530 |
| 494 def args2nodes(self, entries): | 531 def args2nodes(self, entries): |
| 495 result = [] | 532 result = [] |
| 496 for entry in entries: | 533 for entry in entries: |
| 497 if SCons.Util.is_String(entry): | 534 if SCons.Util.is_String(entry): |
| 498 entry = self.env.File(entry) | 535 entry = self.env.File(entry) |
| 499 result.append(entry) | 536 result.append(entry) |
| 500 elif hasattr(entry, 'entries'): | 537 elif hasattr(entry, 'entries'): |
| 501 entry.entries = self.args2nodes(entry.entries) | 538 entry.entries = self.args2nodes(entry.entries) |
| 502 result.append(entry) | 539 result.append(entry) |
| 503 elif isinstance(entry, (list, UserList.UserList)): | 540 elif isinstance(entry, (list, UserList.UserList)): |
| 504 result.extend(self.args2nodes(entry)) | 541 result.extend(self.args2nodes(entry)) |
| 505 elif hasattr(entry, 'sources') and entry.sources: | 542 elif hasattr(entry, 'sources') and entry.sources: |
| 506 result.extend(entry.sources) | 543 result.extend(entry.sources) |
| 507 else: | 544 else: |
| 508 result.append(entry) | 545 result.append(entry.srcnode()) |
| 509 return result | 546 return result |
| 510 | 547 |
| 511 def FindFile(self, node): | 548 def FindFile(self, node): |
| 512 try: | 549 try: |
| 513 flat_file_dict = self.flat_file_dict | 550 flat_file_dict = self.flat_file_dict |
| 514 except AttributeError: | 551 except AttributeError: |
| 515 flat_file_dict = {} | 552 flat_file_dict = {} |
| 516 file_list = self.files[:] | 553 file_list = self.files[:] |
| 517 while file_list: | 554 while file_list: |
| 518 entry = file_list.pop(0) | 555 entry = file_list.pop(0) |
| 519 if not isinstance(entry, (list, UserList.UserList)): | 556 if not isinstance(entry, (list, UserList.UserList)): |
| 520 entry = [entry] | 557 entry = [entry] |
| 521 for f in entry: | 558 for f in entry: |
| 522 if hasattr(f, 'entries'): | 559 if hasattr(f, 'entries'): |
| 523 file_list.extend(f.entries) | 560 file_list.extend(f.entries) |
| 524 else: | 561 else: |
| 525 flat_file_dict[f] = True | 562 flat_file_dict[f] = True |
| 563 flat_file_dict[f.srcnode()] = True |
| 526 if hasattr(f, 'sources'): | 564 if hasattr(f, 'sources'): |
| 527 for s in f.sources: | 565 for s in f.sources: |
| 528 flat_file_dict[s] = True | 566 flat_file_dict[s] = True |
| 567 flat_file_dict[s.srcnode()] = True |
| 529 self.flat_file_dict = flat_file_dict | 568 self.flat_file_dict = flat_file_dict |
| 530 | 569 |
| 531 return flat_file_dict.get(node) | 570 return flat_file_dict.get(node) |
| 532 | 571 |
| 533 def get_guid(self): | 572 def get_guid(self): |
| 534 if self.guid is None: | 573 if self.guid is None: |
| 535 guid = GUIDMap.get(self.msvs_path) | 574 guid = GUIDMap.get(self.msvs_path) |
| 536 if not guid: | 575 if not guid: |
| 537 # Set GUID from path | 576 # Set GUID from path |
| 538 # TODO(rspangler): This is fragile. | 577 # TODO(rspangler): This is fragile. |
| 539 # 1. We can't just use the project filename sans path, since there | 578 # 1. We can't just use the project filename sans path, since there |
| 540 # could be multiple projects with the same base name (for example, | 579 # could be multiple projects with the same base name (for example, |
| 541 # foo/unittest.vcproj and bar/unittest.vcproj). | 580 # foo/unittest.vcproj and bar/unittest.vcproj). |
| 542 # 2. The path needs to be relative to $SOURCE_ROOT, so that the project | 581 # 2. The path needs to be relative to $SOURCE_ROOT, so that the project |
| 543 # GUID is the same whether it's included from base/base.sln or | 582 # GUID is the same whether it's included from base/base.sln or |
| 544 # foo/bar/baz/baz.sln. | 583 # foo/bar/baz/baz.sln. |
| 545 # 3. The GUID needs to be the same each time this builder is invoked, | 584 # 3. The GUID needs to be the same each time this builder is invoked, |
| 546 # so that we don't need to rebuild the solution when the | 585 # so that we don't need to rebuild the solution when the |
| 547 # project changes. | 586 # project changes. |
| 548 # 4. We should be able to handle pre-built project files by reading the | 587 # 4. We should be able to handle pre-built project files by reading the |
| 549 # GUID from the files. | 588 # GUID from the files. |
| 550 guid = MakeGuid(self.msvs_path) | 589 guid = MakeGuid(self.msvs_path) |
| 551 self.guid = guid | 590 self.guid = guid |
| 552 return self.guid | 591 return self.guid |
| 553 | 592 |
| 554 def get_msvs_path(self, sln): | 593 def get_msvs_path(self, sln): |
| 555 return sln.rel_path(self).replace('/', '\\') | 594 return sln.rel_path(self).replace('/', '\\') |
| 556 | 595 |
| 557 def get_rel_path(self, node): | 596 def get_rel_path(self, node): |
| 558 result = self.relative_path_prefix + self.msvs_node.rel_path(node) | 597 result = self.relative_path_prefix + self.rel_path(node) |
| 559 return result.replace('/', '\\') | 598 return result.replace('/', '\\') |
| 560 | 599 |
| 561 def AddConfig(self, Name, tools=None, **attrs): | 600 def AddConfig(self, Name, tools=None, **attrs): |
| 562 """Adds a configuration to the parent node. | 601 """Adds a configuration to the parent node. |
| 563 | 602 |
| 564 Args: | 603 Args: |
| 565 Name: The name of the configuration. | 604 Name: The name of the configuration. |
| 566 tools: List of tools (strings or Tool objects); may be None. | 605 tools: List of tools (strings or Tool objects); may be None. |
| 567 **attrs: Configuration attributes. | 606 **attrs: Configuration attributes. |
| 568 """ | 607 """ |
| 569 if tools is None: | 608 if tools is None: |
| 570 # No tool list specifically for this configuration, | 609 # No tool list specifically for this configuration, |
| 571 # use the Project's as a default. | 610 # use the Project's as a default. |
| 572 tools = self.tools | 611 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) |
| 573 c = MSVSConfig(Name, 'Configuration', tools=tools, **attrs) | 622 c = MSVSConfig(Name, 'Configuration', tools=tools, **attrs) |
| 574 self.configurations.append(c) | 623 self.configurations.append(c) |
| 575 | 624 |
| 576 def AddFiles(self, files): | 625 def AddFiles(self, files): |
| 577 """Adds files to the project. | 626 """Adds files to the project. |
| 578 | 627 |
| 579 Args: | 628 Args: |
| 580 files: A list of Filter objects and/or relative paths to files. | 629 files: A list of Filter objects and/or relative paths to files. |
| 581 | 630 |
| 582 This makes a copy of the file/filter tree at the time of this call. If you | 631 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 | 632 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. | 633 to AddFiles(), it will not be reflected in this project. |
| 585 """ | 634 """ |
| 586 # TODO(rspangler) This also doesn't handle adding files to an existing | 635 self.file_lists.append(self.args2nodes(files)) |
| 587 # filter. That is, it doesn't merge the trees. | 636 |
| 588 self.files.extend(self.args2nodes(files)) | 637 def _FilesToSourceFiles(self, 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) |
| 589 | 664 |
| 590 def AddFileConfig(self, path, Name, tools=None, **attrs): | 665 def AddFileConfig(self, path, Name, tools=None, **attrs): |
| 591 """Adds a configuration to a file. | 666 """Adds a configuration to a file. |
| 592 | 667 |
| 593 Args: | 668 Args: |
| 594 path: Relative path to the file. | 669 path: Relative path to the file. |
| 595 Name: Name of configuration to add. | 670 Name: Name of configuration to add. |
| 596 tools: List of tools (strings or MSVSTool objects); may be None. | 671 tools: List of tools (strings or MSVSTool objects); may be None. |
| 597 **attrs: Configuration attributes. | 672 **attrs: Configuration attributes. |
| 598 | 673 |
| 599 Raises: | 674 Raises: |
| 600 ValueError: Relative path does not match any file added via AddFiles(). | 675 ValueError: Relative path does not match any file added via AddFiles(). |
| 601 """ | 676 """ |
| 677 # Store as the VariantDir node, not as the source node. |
| 602 node = self.env.File(path) | 678 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) | 679 c = MSVSConfig(Name, 'FileConfiguration', tools=tools, **attrs) |
| 606 config_list = self.file_configurations.get(node) | 680 config_list = self.file_configurations.get(node) |
| 607 if config_list is None: | 681 if config_list is None: |
| 608 config_list = [] | 682 config_list = [] |
| 609 self.file_configurations[node] = config_list | 683 self.file_configurations[node] = config_list |
| 610 config_list.append(c) | 684 config_list.append(c) |
| 611 | 685 |
| 612 def AddToolFile(self, path): | 686 def AddToolFile(self, path): |
| 613 """Adds a tool file to the project. | 687 """Adds a tool file to the project. |
| 614 | 688 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 648 # Add tool files section | 722 # Add tool files section |
| 649 tool_files = self.doc.createElement('ToolFiles') | 723 tool_files = self.doc.createElement('ToolFiles') |
| 650 root.appendChild(tool_files) | 724 root.appendChild(tool_files) |
| 651 for tf in self.tool_files: | 725 for tf in self.tool_files: |
| 652 tool_files.appendChild(tf.CreateElement(self.doc, self)) | 726 tool_files.appendChild(tf.CreateElement(self.doc, self)) |
| 653 | 727 |
| 654 # Add configurations section | 728 # Add configurations section |
| 655 configs = self.doc.createElement('Configurations') | 729 configs = self.doc.createElement('Configurations') |
| 656 root.appendChild(configs) | 730 root.appendChild(configs) |
| 657 for c in self.configurations: | 731 for c in self.configurations: |
| 658 configs.appendChild(c.CreateElement(self.doc)) | 732 configs.appendChild(c.CreateElement(self.doc, self)) |
| 659 | 733 |
| 660 # Add empty References section | 734 # Add empty References section |
| 661 root.appendChild(self.doc.createElement('References')) | 735 root.appendChild(self.doc.createElement('References')) |
| 662 | 736 |
| 663 # Add files section | 737 # Add files section |
| 664 root.appendChild(self.files.CreateElement(self.doc, self.CreateFileElement)) | 738 root.appendChild(self.files.CreateElement(self.doc, self.CreateFileElement)) |
| 665 | 739 |
| 666 # Add empty Globals section | 740 # Add empty Globals section |
| 667 root.appendChild(self.doc.createElement('Globals')) | 741 root.appendChild(self.doc.createElement('Globals')) |
| 668 | 742 |
| 669 def CreateFileElement(self, file): | 743 def CreateFileElement(self, file): |
| 670 """Create a DOM node for the specified file. | 744 """Create a DOM node for the specified file. |
| 671 | 745 |
| 672 Args: | 746 Args: |
| 673 file: The file Node being considered. | 747 file: The file Node being considered. |
| 674 | 748 |
| 675 Returns: | 749 Returns: |
| 676 A DOM Node for the File, with a relative path to the current | 750 A DOM Node for the File, with a relative path to the current |
| 677 project object, and any file configurations attached to the | 751 project object, and any file configurations attached to the |
| 678 project. | 752 project. |
| 679 """ | 753 """ |
| 680 | 754 |
| 681 node = self.doc.createElement('File') | 755 node = self.doc.createElement('File') |
| 682 node.setAttribute('RelativePath', self.get_rel_path(file)) | 756 node.setAttribute('RelativePath', self.get_rel_path(file)) |
| 683 for c in self.file_configurations.get(file, []): | 757 for c in self.file_configurations.get(file, []): |
| 684 node.appendChild(c.CreateElement(self.doc)) | 758 node.appendChild(c.CreateElement(self.doc, self)) |
| 685 return node | 759 return node |
| 686 | 760 |
| 687 def _AddFileConfigurationDifferences(self, target, source, base_env, file_env)
: | 761 def VCCLCompilerTool(self, args): |
| 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): |
| 688 """Adds a per-file configuration. | 946 """Adds a per-file configuration. |
| 689 | 947 |
| 690 Args: | 948 Args: |
| 691 target: The target being built from the source. | 949 target: The target being built from the source. |
| 692 source: The source to which the file configuration is being added. | 950 source: The source to which the file configuration is being added. |
| 693 base_env: The base construction environment for the project. | 951 base_env: The base construction environment for the project. |
| 694 Differences from this will go into the FileConfiguration | 952 Differences from this will go into the FileConfiguration |
| 695 in the project file. | 953 in the project file. |
| 696 file_env: The construction environment for the target, containing | 954 file_env: The construction environment for the target, containing |
| 697 the per-target settings. | 955 the per-target settings. |
| 698 """ | 956 """ |
| 699 pass | 957 executor = target.get_executor() |
| 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]) |
| 700 | 969 |
| 701 def _AddFileConfigurations(self, env): | 970 def _AddFileConfigurations(self, env): |
| 702 """Adds per-file configurations for the buildtarget's sources. | 971 """Adds per-file configurations for the buildtarget's sources. |
| 703 | 972 |
| 704 Args: | 973 Args: |
| 705 env: The base construction environment for the project. | 974 env: The base construction environment for the project. |
| 706 """ | 975 """ |
| 707 if not self.buildtargets: | 976 if not self.buildtargets: |
| 708 return | 977 return |
| 709 | 978 |
| 710 bt = self.buildtargets[0] | 979 for bt in self.buildtargets: |
| 711 additional_files = [] | 980 executor = bt.get_executor() |
| 712 for t in bt.sources: | 981 build_env = bt.get_build_env() |
| 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: |
| 713 e = t.get_build_env() | 990 e = t.get_build_env() |
| 991 additional_files = SCons.Util.UniqueList() |
| 714 for s in t.sources: | 992 for s in t.sources: |
| 715 s = env.arg2nodes([s])[0] | 993 s = env.arg2nodes([s])[0].srcnode() |
| 716 if not self.FindFile(s): | 994 if not self.FindFile(s): |
| 717 additional_files.append(s) | 995 additional_files.append(s) |
| 718 if not env is e: | 996 if not build_env is e: |
| 719 self._AddFileConfigurationDifferences(t, s, env, e) | 997 # TODO(sgk): This test may be bogus, but it works for now. |
| 720 self.AddFiles(additional_files) | 998 # We're trying to figure out if the file configuration |
| 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) |
| 721 | 1010 |
| 722 def Write(self, env): | 1011 def Write(self, env): |
| 723 """Writes the project file.""" | 1012 """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 |
| 724 self._AddFileConfigurations(env) | 1021 self._AddFileConfigurations(env) |
| 725 | 1022 |
| 726 self.Create() | 1023 self.Create() |
| 727 | 1024 |
| 728 f = open(str(self.msvs_node), 'wt') | 1025 f = open(str(self), 'wt') |
| 729 f.write(self.formatMSVSProjectXML(self.doc)) | 1026 f.write(self.formatMSVSProjectXML(self.doc)) |
| 730 f.close() | 1027 f.close() |
| 731 | 1028 |
| 732 # Methods for formatting XML as nearly identically to Microsoft's | 1029 # Methods for formatting XML as nearly identically to Microsoft's |
| 733 # .vcproj output as we can practically make it. | 1030 # .vcproj output as we can practically make it. |
| 734 # | 1031 # |
| 735 # The general design here is copied from: | 1032 # The general design here is copied from: |
| 736 # | 1033 # |
| 737 # Bruce Eckels' MindView, Inc: 12-09-04 XML Oddyssey | 1034 # Bruce Eckels' MindView, Inc: 12-09-04 XML Oddyssey |
| 738 # http://www.mindview.net/WebLog/log-0068 | 1035 # http://www.mindview.net/WebLog/log-0068 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 782 'ConfigurationType', | 1079 'ConfigurationType', |
| 783 'InheritedPropertySheets', | 1080 'InheritedPropertySheets', |
| 784 ], | 1081 ], |
| 785 'FileConfiguration' : [ | 1082 'FileConfiguration' : [ |
| 786 'Name', | 1083 'Name', |
| 787 'ExcludedFromBuild', | 1084 'ExcludedFromBuild', |
| 788 ], | 1085 ], |
| 789 'Tool' : [ | 1086 'Tool' : [ |
| 790 'Name', | 1087 'Name', |
| 791 'DisableSpecificWarnings', | 1088 'DisableSpecificWarnings', |
| 1089 |
| 1090 'PreprocessorDefinitions', |
| 1091 'UsePrecompiledHeader', |
| 1092 'PrecompiledHeaderThrough', |
| 1093 'ForcedIncludeFiles', |
| 792 ], | 1094 ], |
| 793 'VisualStudioProject' : [ | 1095 'VisualStudioProject' : [ |
| 794 'ProjectType', | 1096 'ProjectType', |
| 795 'Version', | 1097 'Version', |
| 796 'Name', | 1098 'Name', |
| 797 'ProjectGUID', | 1099 'ProjectGUID', |
| 798 'RootNamespace', | 1100 'RootNamespace', |
| 799 'Keyword', | 1101 'Keyword', |
| 800 ], | 1102 ], |
| 801 } | 1103 } |
| (...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1055 if not SCons.Util.is_String(item): | 1357 if not SCons.Util.is_String(item): |
| 1056 return item | 1358 return item |
| 1057 item = env.subst(item) | 1359 item = env.subst(item) |
| 1058 result = env.fs._lookup(item, None, _MSVSSolution, create=1) | 1360 result = env.fs._lookup(item, None, _MSVSSolution, create=1) |
| 1059 result.initialize(env, item, *args, **kw) | 1361 result.initialize(env, item, *args, **kw) |
| 1060 LookupAdd(item, result) | 1362 LookupAdd(item, result) |
| 1061 return result | 1363 return result |
| 1062 | 1364 |
| 1063 import __builtin__ | 1365 import __builtin__ |
| 1064 | 1366 |
| 1367 __builtin__.MSVSConfig = MSVSConfig |
| 1065 __builtin__.MSVSFilter = MSVSFilter | 1368 __builtin__.MSVSFilter = MSVSFilter |
| 1066 __builtin__.MSVSProject = MSVSProject | 1369 __builtin__.MSVSProject = MSVSProject |
| 1067 __builtin__.MSVSSolution = MSVSSolution | 1370 __builtin__.MSVSSolution = MSVSSolution |
| 1068 __builtin__.MSVSTool = MSVSTool | 1371 __builtin__.MSVSTool = MSVSTool |
| OLD | NEW |