1# 2# This file is part of Bakefile (http://www.bakefile.org) 3# 4# Copyright (C) 2006-2008 Vaclav Slavik, Kevin Powell, Steven Van Ingelgem, 5# Kevin Ollivier, Aleksander Jaromin 6# 7# Permission is hereby granted, free of charge, to any person obtaining a copy 8# of this software and associated documentation files (the "Software"), to 9# deal in the Software without restriction, including without limitation the 10# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11# sell copies of the Software, and to permit persons to whom the Software is 12# furnished to do so, subject to the following conditions: 13# 14# The above copyright notice and this permission notice shall be included in 15# all copies or substantial portions of the Software. 16# 17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23# IN THE SOFTWARE. 24# 25# $Id: msvs200xprj.py 1345 2011-02-01 14:02:34Z vaclavslavik $ 26# 27# MS Visual Studio 2003/2005 project files generator script 28# 29 30import os, os.path 31import errors, utils 32from xml.dom.minidom import * 33 34import mk 35import msvc_common 36from msvc_common import * 37 38# ------------------------------------------------------------------------ 39# misc .vcproj constants: 40# ------------------------------------------------------------------------ 41 42# compiler options 43 44rtMultiThreaded = '0' 45rtMultiThreadedDebug = '1' 46rtMultiThreadedDLL = '2' 47rtMultiThreadedDebugDLL = '3' 48rtSingleThreaded = '4' 49rtSingleThreadedDebug = '5' 50 51debugDisabled = '0' 52debugOldStyleInfo = '1' 53debugLineInfoOnly = '2' 54debugEnabled = '3' 55debugEditAndContinue = '4' 56 57runtimeBasicCheckNone = '0' 58runtimeCheckStackFrame = '1' 59runtimeCheckUninitVariables = '2' 60runtimeBasicCheckAll = '3' 61 62optimizeDisabled = '0' 63optimizeMinSpace = '1' 64optimizeMaxSpeed = '2' 65optimizeFull = '3' 66optimizeCustom = '4' 67 68pchNone = '0' 69pchCreateUsingSpecific = '1' 70 71# linker options 72 73linkIncrementalDefault = '0' 74linkIncrementalNo = '1' 75linkIncrementalYes = '2' 76 77optReferencesDefault = '0' 78optNoReferences = '1' 79optReferences = '2' 80 81machineX86 = '1' 82machineARM = '3' 83machineAMD64 = '17' 84 85 86# ------------------------------------------------------------------------ 87# extended minidom classes to create XML file with specified attributes 88# order (VC2003 projet file must have attribute "Name" as first) 89# ------------------------------------------------------------------------ 90 91class ElementSorted(Element): 92 def __init__(self, tagName, namespaceURI=EMPTY_NAMESPACE, 93 prefix=None, localName=None): 94 Element.__init__(self, tagName, namespaceURI, prefix, localName) 95 self.order = [] 96 97 def setAttribute(self, attname, value): 98 self.order.append(attname) 99 Element.setAttribute(self, attname, value) 100 101 def writexml(self, writer, indent="", addindent="", newl=""): 102 # This specialization does two things differently: 103 # 1) order of attributes is preserved 104 # 2) attributes are placed each on its own line and indented 105 # so that the output looks more like MSVC's native files 106 writer.write("%s<%s" % (indent, self.tagName)) 107 108 attrs = self._get_attributes() 109 110 for attr in self.order: 111 writer.write("\n%s%s%s=\"%s\"" % ( 112 indent, 113 addindent, 114 attr, 115 attrs[attr].value.replace('&', '&'). 116 replace('<', '<'). 117 replace('>', '>'). 118 replace('"', '"'))) 119 120 if self.childNodes: 121 if len(self.order) == 0: 122 writer.write(">%s" % newl) 123 else: 124 if _MSVS_VCPROJ_VERSION == "7.10": 125 writer.write(">%s" % newl) 126 else: 127 writer.write("%s%s%s>%s" % (newl, indent, addindent, newl)) 128 for node in self.childNodes: 129 node.writexml(writer, indent + addindent, addindent, newl) 130 writer.write("%s</%s>%s" % (indent, self.tagName, newl)) 131 else: 132 # <File> is serialized differently, see the discussion in 133 # http://www.bakefile.org/ticket/210 134 if self.tagName == "File": 135 if _MSVS_VCPROJ_VERSION == "7.10": 136 writer.write(">%s%s</%s>%s" % (newl, indent, self.tagName, newl)) 137 else: 138 writer.write("%s%s%s>%s%s</%s>%s" % (newl, indent, addindent, newl, indent, self.tagName, newl)) 139 else: 140 if _MSVS_VCPROJ_VERSION == "7.10": 141 writer.write("/>%s" % newl) 142 else: 143 writer.write("%s%s/>%s" % (newl, indent, newl)) 144 145 146class DocumentSorted(Document): 147 def createElement(self, tagName): 148 e = ElementSorted(tagName) 149 e.ownerDocument = self 150 return e 151 152 153class VerbatimFragment(Text): 154 def __init__(self, markup): 155 self.markup = markup 156 def writexml(self, writer, indent="", addindent="", newl=""): 157 writer.write(indent) 158 writer.write(self.markup) 159 writer.write(newl) 160 161 162# ------------------------------------------------------------------------ 163# helpers 164# ------------------------------------------------------------------------ 165 166# this GUID is used by all solutions in the Project() entry: 167GUID_SOLUTION = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}" 168 169# UUID for Bakefile namespace when generating GUIDs 170NAMESPACE_BAKEFILE_PROJ = "{b0c737d9-df87-4499-b156-418baa078a12}" 171NAMESPACE_BAKEFILE_FILTERS = "{0e8f53b3-f09d-40b1-b248-66f80b72e654}" 172 173def mk_uuid(namespace, seed): 174 # NB: we want to have the GUID strings be repeatable, so generate them 175 # from a repeatable seed 176 from uuid import uuid5, UUID 177 178 guid = uuid5(UUID(namespace), seed) 179 return '{%s}' % str(guid).upper() # MSVS uses upper-case strings for GUIDs 180 181def mk_proj_uuid(basename, proj_id): 182 return mk_uuid(NAMESPACE_BAKEFILE_PROJ, '%s/%s' % (basename, proj_id)) 183 184def mk_filter_uuid(name, wildcards): 185 return mk_uuid(NAMESPACE_BAKEFILE_FILTERS, '%s:%s' % (name, wildcards)) 186 187class MsvsFilesGroup(FilesGroup): 188 def __init__(self, name, files=None, extensions=None, uuid=None): 189 assert files != None or extensions != None 190 if files == None: 191 files = ' '.join(['*.%s' % x for x in extensions.split(';')]) 192 if uuid == None: 193 uuid = mk_filter_uuid(name, files) 194 195 FilesGroup.__init__(self, name, files) 196 self.uuid = uuid 197 self.extensions = extensions 198 199SOURCE_FILES_EXTENSIONS = [ 200 'cpp','c','cc','cxx','def','odl','idl','hpj','bat','asm','asmx'] 201 202DEFAULT_FILE_GROUPS = [ 203 MsvsFilesGroup('Source Files', 204 extensions=';'.join(SOURCE_FILES_EXTENSIONS), 205 uuid='{4FC737F1-C7A5-4376-A066-2A32D752A2FF}'), 206 MsvsFilesGroup('Header Files', 207 extensions='h;hpp;hxx;hm;inl;inc;xsd', 208 uuid='{93995380-89BD-4b04-88EB-625FBE52EBFB}'), 209 MsvsFilesGroup('Resource Files', 210 extensions='rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav', 211 uuid='{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}') 212] 213 214def mk_list(list): 215 # remove empty items from the list: 216 return ";".join([x for x in list.split(";") if len(x) > 0]) 217 218def mk_listsp(list): 219 # remove empty items from the list: 220 return ";".join([x for x in list.split(" ") if len(x) > 0]) 221 222def bool2vcstr(value): 223 """Returns "true"/"false" or "TRUE"/"FALSE" string depending on format 224 version.""" 225 if value: 226 s = 'true' 227 else: 228 s = 'false' 229 if _MSVS_VCPROJ_VERSION == "7.10": 230 return s.upper() 231 else: 232 return s 233 234 235def isSourceFile(filename): 236 """Is this file a source file (and not e.g. a header)?""" 237 ext = filename.split('.')[-1] 238 return ext in SOURCE_FILES_EXTENSIONS 239 240 241def findConflictingNames(sources): 242 """Finds source files that have conflicting basenames, i.e. that would be 243 compiled into the same file without special handling.""" 244 all_bases = {} 245 for s in sources: 246 base = s.split('\\')[-1] 247 all_bases.setdefault(base, []) 248 all_bases[base].append(s) 249 conflict_bases = [i for i in all_bases if len(all_bases[i]) > 1] 250 conflict_names = [] 251 for x in conflict_bases: 252 conflict_names += all_bases[x] 253 return conflict_names 254 255 256# ------------------------------------------------------------------------ 257# Generator class 258# ------------------------------------------------------------------------ 259 260class ProjectGeneratorMsvc9: 261 262 app_type_code = { 'console' : '1', 'gui' : '2' } 263 264 def __init__(self): 265 self.basename, ext = os.path.splitext(os.path.basename(FILE)) 266 self.dirname = os.path.dirname(FILE) 267 if ext.lower() == '.vcproj': 268 self.onlyProject = True 269 else: 270 if ext.lower() != '.sln' and ext != '': 271 print 'warning: ignoring extension "%s"' % ext[1:] # drop dot 272 self.onlyProject = False 273 274 self.fragments = {} 275 for f in mk.fragments: 276 prj, filename = f.location.split(':') 277 filename = filename.replace('/', '\\') 278 self.fragments[(prj, filename)] = f.content 279 280 281 # -------------------------------------------------------------------- 282 # basic configuration 283 # -------------------------------------------------------------------- 284 285 def getSolutionExtension(self): 286 return 'sln' 287 def getProjectExtension(self): 288 return 'vcproj' 289 290 # -------------------------------------------------------------------- 291 # helpers 292 # -------------------------------------------------------------------- 293 294 def splitConfigName(self, config): 295 idx = config.index('|') 296 platform = config[:idx] 297 name = config[idx+2:] 298 if name == '': 299 name = 'Default' 300 return (platform, name) 301 302 def mkConfigName(self, config): 303 # config name always contains the platform followed by "| " at the 304 # beginning of the string, so extract it and transform into 305 # "ConfigName|Platform": 306 platform, name = self.splitConfigName(config) 307 return '%s|%s' % (name, platform) 308 309 def sortConfigsForSLN(self, configs): 310 # .sln files have configs grouped, all platforms for one config 311 # are together, but our "natural" sort order is different: 312 sortedConfigs = [] 313 todo = [c for c in sortedConfigKeys(configs)] 314 while len(todo) > 0: 315 c = todo.pop(0) 316 if c in sortedConfigs: 317 continue 318 plat, nm = self.splitConfigName(c) 319 sortedConfigs.append(c) 320 for c2 in todo: 321 if c2 in sortedConfigs: 322 continue 323 plat2, nm2 = self.splitConfigName(c2) 324 if nm == nm2: 325 sortedConfigs.append(c2) 326 return sortedConfigs 327 328 def isEmbeddedConfig(self, config): 329 """Returns true if given config targets embedded device.""" 330 cfg = configs[config][0]['MSVS_PLATFORM'] 331 return cfg == 'pocketpc2003' 332 333 def isX64Config(self, config): 334 """Returns true if given config targets a 64bit system.""" 335 cfg = configs[config][0]['MSVS_PLATFORM'] 336 return cfg == 'win64' 337 338 339 def assignGUIDs(self, sln_targets): 340 """Returns the dictionary containing GUIDs indexed by target ids.""" 341 guid_dict = {} 342 for t in sln_targets: 343 if '_msvc_guid' in t.__dict__: 344 guid_dict[t.id] = '{%s}' % t._msvc_guid.upper() 345 else: 346 guid_dict[t.id] = mk_proj_uuid(self.basename, t.id) 347 return guid_dict 348 349 def createVCProjList(self, sln_targets, guid_dict, vcproj_list, deps_translation): 350 """Fills the project list and also returns the SLN projects sections as 351 a side effect.""" 352 sln = '' 353 single_target = (len(sln_targets) == 1) 354 for t in sln_targets: 355 deps = '' 356 if 'MSVS_PROJECT_FILE' in t.__dict__: 357 vcproj_name = t.MSVS_PROJECT_FILE 358 elif single_target: 359 vcproj_name = self.basename + '.' + self.getProjectExtension() 360 else: 361 vcproj_name = '%s_%s.%s' % (self.basename, t.id, self.getProjectExtension()) 362 deplist = t._deps.split() 363 364 # add external project dependencies: 365 for d in t._dsp_deps.split(): 366 deplist.append(d.split(':')[0]) 367 368 # create the dependencies section, if any: 369 deps_str = "" 370 if len(deplist) != 0: 371 deps_str = "\tProjectSection(ProjectDependencies) = postProject\n" 372 373 for d in deplist: 374 if d in deps_translation: 375 d2 = deps_translation[d] 376 else: 377 d2 = d 378 guid = guid_dict[d2] 379 deps_str += "\t\t%s = %s\n" % (guid, guid) 380 deps_str += "\tEndProjectSection\n" 381 382 guid = guid_dict[t.id] 383 384 # create the project section: 385 sln += 'Project("%s") = "%s", "%s", "%s"\n' % ( 386 GUID_SOLUTION, 387 t.id, 388 vcproj_name, 389 guid 390 ) 391 sln += deps_str 392 sln += 'EndProject\n' 393 394 vcproj_file = (t, 395 os.path.join(self.dirname, vcproj_name), 396 os.path.basename(os.path.splitext(vcproj_name)[0]), 397 guid) 398 399 if vcproj_file not in vcproj_list: 400 vcproj_list.append(vcproj_file) 401 402 return sln 403 404 405 # -------------------------------------------------------------------- 406 # Solution file (.sln) 407 # -------------------------------------------------------------------- 408 409 def makeSlnHeader(self): 410 if _MSVS_SLN_VERSION == "8.00": 411 return """\ 412Microsoft Visual Studio Solution File, Format Version 8.00 413""" 414 elif _MSVS_SLN_VERSION == "9.00": 415 return """\ 416Microsoft Visual Studio Solution File, Format Version 9.00 417# Visual Studio 2005 418""" 419 elif _MSVS_SLN_VERSION == "10.00": 420 return """\ 421Microsoft Visual Studio Solution File, Format Version 10.00 422# Visual Studio 2008 423""" 424 else: 425 import errors 426 raise errors.Error("unexpected MSVS format version") 427 428 def genSln(self, sln_targets, guid_dict, vcproj_list, deps_translation): 429 if len(sln_targets) == 0: 430 return 431 432 sln = self.makeSlnHeader() 433 434 sln += self.createVCProjList(sln_targets, guid_dict, vcproj_list, deps_translation) 435 436 sortedConfigs = self.sortConfigsForSLN(configs) 437 438 # generate configurations listing: 439 sln += "Global\n" 440 sln += "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n" 441 for c in sortedConfigs: 442 cfg = self.mkConfigName(c) 443 sln += "\t\t%s = %s\n" % (cfg,cfg) 444 sln += "\tEndGlobalSection\n" 445 446 # ...and configurations binding to vcproj configurations: 447 sln += "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n" 448 449 for t in sln_targets: 450 guid = guid_dict[t.id] 451 for c in sortedConfigs: 452 cfg = self.mkConfigName(c) 453 txt = "\t\t%(guid)s.%(cfg)s.%%s = %(cfg)s\n" % {'guid':guid, 'cfg':cfg} 454 sln += txt % 'ActiveCfg' 455 if c in t.configs: 456 sln += txt % 'Build.0' 457 if self.isEmbeddedConfig(c): 458 sln += txt % 'Deploy.0' 459 460 461 sln += "\tEndGlobalSection\n" 462 sln += "\tGlobalSection(SolutionProperties) = preSolution\n" 463 sln += "\t\tHideSolutionNode = FALSE\n" 464 sln += "\tEndGlobalSection\n" 465 sln += "EndGlobal\n" 466 467 writer.writeFile('%s.%s' % ( 468 os.path.join(self.dirname, self.basename), 469 self.getSolutionExtension() 470 ), sln) 471 472 473 def genWorkspaces(self): 474 vcproj_list = [] 475 476 # find all projects. Beware ugly hack here: MSVC6PRJ_MERGED_TARGETS is 477 # used to create fake targets as a merge of two (mutually exclusive) 478 # targets. This is sometimes useful, e.g. when you want to build both 479 # DLL and static lib of something. 480 deps_translation = {} 481 projects = [t for t in targets if t._kind == 'project'] 482 for mergeInfo in MSVC6PRJ_MERGED_TARGETS.split(): 483 split1 = mergeInfo.split('=') 484 split2 = split1[1].split('+') 485 tgR = split1[0] 486 tg1 = split2[0] 487 tg2 = split2[1] 488 489 # the targets may be disabled by some (weak) condition: 490 if tg1 not in targets and tg2 not in targets: 491 continue 492 493 t = targets[tg1] 494 for c in targets[tg2].configs: 495 assert c not in t.configs # otherwise not mutually exclusive 496 t.configs[c] = targets[tg2].configs[c] 497 t.id = tgR 498 projects.remove(targets[tg2]) 499 targets.append(tgR, t) 500 del targets[tg1] 501 del targets[tg2] 502 deps_translation[tg1] = tgR 503 deps_translation[tg2] = tgR 504 505 guid_dict = self.assignGUIDs(projects) 506 if self.onlyProject: 507 self.createVCProjList(projects, guid_dict, vcproj_list, deps_translation) 508 if len(vcproj_list) != 1: 509 raise errors.Error("Single project name specified for multiple targets.") 510 else: 511 self.genSln(projects, guid_dict, vcproj_list, deps_translation) 512 for t, filename, prjname, guid in vcproj_list: 513 self.genVCProj(t, filename, prjname, guid) 514 515 # warn about <action> targets that we can't handle (yet): 516 for t in [t for t in targets if t._kind == 'action']: 517 print "warning: ignoring action target '%s'" % t.id 518 519 520 # ------------------------------------------------------------------------ 521 # Project files (.vcproj) 522 # ------------------------------------------------------------------------ 523 524 # some helpers to build parts of the project file that change if you are 525 # doing PC vs WinCE 526 def buildConfElement(self, doc, cfg, c, t): 527 conf_name = self.mkConfigName(c) 528 conf_el = doc.createElement("Configuration") 529 conf_el.setAttribute("Name", conf_name) 530 conf_el.setAttribute("OutputDirectory", "%s" % cfg._targetdir[:-1]) 531 conf_el.setAttribute("IntermediateDirectory", "%s\\%s" % (cfg._builddir, t.id) ) 532 conf_el.setAttribute("ConfigurationType", "%s" % cfg._type_code) 533 conf_el.setAttribute("UseOfMFC", "0") 534 conf_el.setAttribute("ATLMinimizesCRunTimeLibraryUsage", bool2vcstr(False)) 535 536 # allow specifying CharacterSet value, it's not really clear what is 537 # the difference between specifying it as 0 (not specified), 2 (MBCS) 538 # or not using it at all, let the user decide 539 if globals().has_key('MSVS_CHARACTER_SET'): 540 conf_el.setAttribute("CharacterSet", MSVS_CHARACTER_SET) 541 # VC++ 2008 needs CharacterSet set, definining _UNICODE is not enough, 542 # see http://comments.gmane.org/gmane.comp.sysutils.bakefile.devel/1145 543 elif "_UNICODE" in cfg._defines.split(";"): 544 conf_el.setAttribute("CharacterSet", "1") 545 546 return conf_el 547 548 549 def buildBscMakeToolElement(self, tool, prjname, cfg, c, t): 550 tool.setAttribute("OutputFile", "%s%s.bsc" % (cfg._targetdir, prjname)) 551 tool.setAttribute("SuppressStartupBanner", bool2vcstr(True)) 552 553 554 def buildCompilerToolElement(self, tool, prjname, cfg, c, t): 555 # the order of attributes here is the same as used in the projects 556 # created by MSVS itself 557 558 if _MSVS_VCPROJ_VERSION not in ["7.10", "8.00"]: 559 extra_cppflags = "/MP" # parallel compilation 560 if cfg._cppflags: 561 extra_cppflags = "%s %s" % (extra_cppflags, cfg._cppflags) 562 else: 563 extra_cppflags = cfg._cppflags 564 if extra_cppflags: 565 tool.setAttribute("AdditionalOptions", extra_cppflags) 566 567 tool.setAttribute("Optimization", cfg._optimize) 568 tool.setAttribute("AdditionalIncludeDirectories", mk_list(cfg._include_paths)) 569 tool.setAttribute("PreprocessorDefinitions", mk_list(cfg._defines)) 570 571 if cfg._optimize == "0" and cfg._rtl_dbg == 'on': 572 # VC2008 projects use /MP switch, which is incompatible with 573 # MinimalRebuild; /MP is more useful, so omit MinimalRebuild: 574 if _MSVS_VCPROJ_VERSION in ["7.10", "8.00"]: 575 tool.setAttribute("MinimalRebuild", bool2vcstr(True)) 576 577 # this property type has changed from int to bool since VC7 578 eh = cfg._cxx_exceptions == 'on' 579 if _MSVS_VCPROJ_VERSION == "7.10": 580 eh = bool2vcstr(eh) # True/False -> 'TRUE'/'FALSE' 581 else: 582 eh = str(int(eh)) # True/False -> '1'/'0' 583 tool.setAttribute("ExceptionHandling", eh) 584 585 if cfg._rtl_dbg == 'on': 586 tool.setAttribute("BasicRuntimeChecks", runtimeBasicCheckAll) 587 588 # choose runtime library 589 if cfg._rtl_threading == 'multi': 590 if cfg._rtl_dbg == 'on': 591 if cfg._rtl_type == 'static': 592 rtl = rtMultiThreadedDebug 593 else: 594 rtl = rtMultiThreadedDebugDLL 595 else: # debug off 596 if cfg._rtl_type == 'static': 597 rtl = rtMultiThreaded 598 else: 599 rtl = rtMultiThreadedDLL 600 else: # single-threaded 601 if cfg._rtl_type == 'dynamic': 602 print "warning: single-threaded dynamic runtime doesn't exist, using static" 603 if cfg._rtl_dbg == 'on': 604 rtl = rtSingleThreadedDebug 605 else: 606 rtl = rtSingleThreaded 607 tool.setAttribute("RuntimeLibrary", rtl) 608 609 if cfg._optimize == "0": 610 tool.setAttribute("BufferSecurityCheck",bool2vcstr(True)) 611 612 if cfg._cxx_rtti == 'on': 613 tool.setAttribute("RuntimeTypeInfo", bool2vcstr(True)) 614 else: 615 tool.setAttribute("RuntimeTypeInfo", bool2vcstr(False)) 616 617 # the values of this enum changed in VC8 where pchGenerateAuto simply 618 # disappeared (and so the value of subsequent element shifted), so 619 # define separate constants for VC7 ... 620 if _MSVS_VCPROJ_VERSION == "7.10": 621 pchGenerateAuto = '2' 622 pchUseUsingSpecific = '3' 623 else: 624 # ... and for the later versions 625 pchUseUsingSpecific = '2' 626 627 if cfg._pch_use_pch == '1': 628 do_use_pch = True 629 if cfg._pch_generator: 630 tool.setAttribute("UsePrecompiledHeader", pchUseUsingSpecific) 631 else: 632 if _MSVS_VCPROJ_VERSION == "7.10": 633 tool.setAttribute("UsePrecompiledHeader", pchGenerateAuto) 634 else: 635 # automatic PCH support (/YX option) was removed in VC8, so 636 # disable the use of PCH completely when this option is 637 # specified (what else can we do?) 638 # 639 # FIXME: instead, we could pick a source file that includes 640 # _pch_header (if specified) at random and use that; 641 # or at least issue a warning 642 do_use_pch = False 643 644 if do_use_pch: 645 tool.setAttribute("PrecompiledHeaderThrough", cfg._pch_header) 646 if cfg._pch_file: 647 tool.setAttribute("PrecompiledHeaderFile", 648 "%s\\%s" % (cfg._builddir, cfg._pch_file)) 649 650 651 tool.setAttribute("ObjectFile", "%s\\%s\\" % (cfg._builddir, t.id) ) 652 tool.setAttribute("ProgramDataBaseFileName", cfg._pdbfile) 653 654 warnings_map = { 'no':'0', 'default':'3', 'max':'4'} 655 if warnings_map.has_key(cfg._warnings): 656 tool.setAttribute("WarningLevel", warnings_map[cfg._warnings]) 657 else: 658 tool.setAttribute("WarningLevel", warnings_map['default']) 659 660 tool.setAttribute("SuppressStartupBanner", bool2vcstr(True)) 661 662 # Detect64BitPortabilityProblems is deprecated in VS2008 and will 663 # be removed in the future: 664 if _MSVS_VCPROJ_VERSION in ["7.10", "8.00"]: 665 tool.setAttribute("Detect64BitPortabilityProblems", bool2vcstr(True)) 666 667 if cfg._debug == '1': 668 if cfg._debug_edit_and_continue == '1': 669 tool.setAttribute("DebugInformationFormat", debugEditAndContinue) 670 else: 671 tool.setAttribute("DebugInformationFormat", debugEnabled) 672 else: 673 tool.setAttribute("DebugInformationFormat", debugDisabled) 674 675 676 def buildLinkerToolElement(self, tool, prjname, cfg, c, t): 677 ldlibs = cfg._ldlibs 678 if cfg._cxx_rtti == 'on' and self.isEmbeddedConfig(c): 679 ldlibs += ' ccrtrtti.lib' 680 681 tool.setAttribute("AdditionalOptions", cfg._ldflags) 682 tool.setAttribute("AdditionalDependencies", ldlibs) 683 tool.setAttribute("OutputFile", "%s%s" % (cfg._targetdir, cfg._targetname)) 684 685 if cfg._rtl_dbg == 'on': 686 tool.setAttribute("LinkIncremental", linkIncrementalYes) 687 else: 688 tool.setAttribute("LinkIncremental", linkIncrementalNo) 689 690 if cfg._type_nick == 'dll': 691 if cfg._importlib != "": 692 implib = cfg._importlib 693 tool.setAttribute("ImportLibrary", 694 "%s%s" % (cfg._targetdir, implib)) 695 696 tool.setAttribute("SuppressStartupBanner", bool2vcstr(True)) 697 tool.setAttribute("AdditionalLibraryDirectories", mk_list(cfg._lib_paths)) 698 if _MSVS_VCPROJ_VERSION != "7.10": 699 tool.setAttribute("GenerateManifest", bool2vcstr(True)) 700 701 if cfg._debug == '1': 702 tool.setAttribute("GenerateDebugInformation", bool2vcstr(True)) 703 704 tool.setAttribute("ProgramDatabaseFile", cfg._pdbfile) 705 706 if cfg._type_nick != 'dll': 707 tool.setAttribute("SubSystem", 708 "%s" % self.app_type_code[cfg._type_nick]) 709 710 if self.isEmbeddedConfig(c): 711 tool.setAttribute("TargetMachine", machineARM) 712 tool.setAttribute("DelayLoadDLLs", "$(NOINHERIT)") 713 if cfg._rtl_dbg == 'off': 714 tool.setAttribute("OptimizeReferences", optReferences) 715 tool.setAttribute("EnableCOMDATFolding", "2") 716 else: 717 if self.isX64Config(c): 718 tool.setAttribute("TargetMachine", machineAMD64) 719 else: 720 tool.setAttribute("TargetMachine", machineX86) 721 # optimize linking unless we're using debug RTL (in which case we 722 # don't build production version anyhow) or we don't use /debug at 723 # all (in which case it's optimized by default) 724 if cfg._rtl_dbg == 'off' and cfg._debug == '1': 725 tool.setAttribute("OptimizeReferences", optReferences) 726 tool.setAttribute("EnableCOMDATFolding", "2") 727 728 729 def buildLibrarianToolElement(self, tool, prjname, cfg, c, t): 730 tool.setAttribute("OutputFile","%s%s" % (cfg._targetdir, cfg._targetname) ) 731 tool.setAttribute("SuppressStartupBanner", bool2vcstr(True)) 732 733 734 def buildResourceCompilerToolElement(self, tool, prjname, cfg, c, t): 735 tool.setAttribute("PreprocessorDefinitions", mk_list(cfg._res_defines)) 736 tool.setAttribute("Culture", "1033") 737 tool.setAttribute("AdditionalIncludeDirectories", mk_list(cfg._res_include_paths)) 738 739 740 def buildIdlToolElement(self, tool, prjname, cfg, c, t): 741 # MIDL compiler has a bug in it that prevents quoted values in 742 # PreprocessorDefinitions from being passed correctly to the compiler. 743 # We work around it by using AdditionalOptions. 744 # See http://www.vtk.org/Bug/view.php?id=8165 for the gory details. 745 all_defines = [x for x in cfg._defines.split(";") if len(x) > 0] 746 extras = [] 747 simple_defines = [] 748 for d in all_defines: 749 if '"' in d: 750 extras.append("/D %s" % d) 751 else: 752 simple_defines.append(d) 753 if extras: 754 tool.setAttribute("AdditionalOptions", " ".join(extras)) 755 tool.setAttribute("PreprocessorDefinitions", ";".join(simple_defines)) 756 tool.setAttribute("AdditionalIncludeDirectories", mk_list(cfg._include_paths)) 757 758 759 def buildPlatformsElement(self, doc): 760 #Platforms Node 761 plats_el = doc.createElement("Platforms") 762 for p in MSVS_PLATFORMS_DESC.split(','): 763 plat_el = doc.createElement("Platform") 764 plat_el.setAttribute("Name", p) 765 plats_el.appendChild(plat_el) 766 return plats_el 767 768 def buildToolFilesElement(self, doc): 769 #ToolFiles Node 770 tf_el = doc.createElement("ToolFiles") 771 tf_el.appendChild(doc.createTextNode("")) 772 return tf_el 773 774 def buildAllConfigurations(self, doc, prjname, t): 775 #Configurations Node 776 confs_el = doc.createElement("Configurations") 777 for c in sortedConfigKeys(t.configs): 778 cfg = t.configs[c] 779 confs_el.appendChild(self.buildSingleConfiguration(doc, prjname, cfg, c, t)) 780 return confs_el 781 782 def buildSingleConfiguration(self, doc, prjname, cfg, c, t): 783 conf_el = self.buildConfElement(doc, cfg, c, t) 784 785 # this dict is indexed by the names of the tools which need special 786 # treatment, i.e. should be non-empty in the generated file: the value 787 # of the corresponding dict element is our method which is called with 788 # tool itself as well as prjname, cfg, c and t as parameters and should 789 # fill in the tool with contents 790 # 791 # NB: order here doesn't matter so we sort tools alphabetically, the 792 # order in the file is determined by the tools list below 793 toolHandlers = { 794 'VCBscMakeTool' : self.buildBscMakeToolElement, 795 'VCCLCompilerTool' : self.buildCompilerToolElement, 796 'VCMIDLTool' : self.buildIdlToolElement, 797 'VCLibrarianTool' : self.buildLibrarianToolElement, 798 'VCLinkerTool' : self.buildLinkerToolElement, 799 'VCResourceCompilerTool' 800 : self.buildResourceCompilerToolElement, 801 } 802 803 if cfg._type_nick == 'lib': 804 linkTool = 'VCLibrarianTool' 805 else: 806 linkTool = 'VCLinkerTool' 807 808 # this list contains the tools in the order appropriate for the current 809 # project file version 810 if _MSVS_VCPROJ_VERSION == "7.10": 811 tools = [ 812 'VCCLCompilerTool', 813 'VCCustomBuildTool', 814 linkTool, 815 'VCMIDLTool', 816 'VCPostBuildEventTool', 817 'VCPreBuildEventTool', 818 'VCPreLinkEventTool', 819 'VCResourceCompilerTool', 820 'VCWebServiceProxyGeneratorTool', 821 'VCXMLDataGeneratorTool', 822 'VCWebDeploymentTool', 823 'VCManagedWrapperGeneratorTool', 824 'VCAuxiliaryManagedWrapperGeneratorTool', 825 ] 826 else: 827 tools = [ 828 'VCPreBuildEventTool', 829 'VCCustomBuildTool', 830 'VCXMLDataGeneratorTool', 831 'VCWebServiceProxyGeneratorTool', 832 'VCMIDLTool', 833 'VCCLCompilerTool', 834 'VCManagedResourceCompilerTool', 835 'VCResourceCompilerTool', 836 'VCPreLinkEventTool', 837 linkTool, 838 'VCALinkTool', 839 'VCManifestTool', 840 'VCXDCMakeTool', 841 'VCBscMakeTool', 842 'VCFxCopTool', 843 'VCAppVerifierTool', 844 'VCWebDeploymentTool', 845 'VCPostBuildEventTool', 846 ] 847 848 if cfg._type_nick not in ['gui', 'console']: 849 # these tools only make sense for the applications, not libraries 850 tools.remove('VCAppVerifierTool') 851 tools.remove('VCWebDeploymentTool') 852 else: 853 if _MSVS_VCPROJ_VERSION != "8.00": 854 # this tool was removed in VS 2008 855 tools.remove('VCWebDeploymentTool') 856 857 # add all the tools 858 for tool in tools: 859 node = doc.createElement("Tool") 860 node.setAttribute("Name", tool) 861 if tool in toolHandlers: 862 toolHandlers[tool](node, prjname, cfg, c, t) 863 conf_el.appendChild(node) 864 865 866 # additional tools 867 if self.isEmbeddedConfig(c): 868 self.buildEmbeddedTools(doc, conf_el) 869 870 return conf_el 871 872 def buildCustomBuildElement(self, doc, data): 873 # parse MSVC6-style custom build element code (FIXME: this is for 874 # compatibility, will be replaced with <action> handling in the 875 # future): 876 lines = data.split('\n') 877 description = lines[0] 878 assert lines[1].startswith('InputPath=') 879 assert lines[2] == '' 880 delim = lines[3].index(' : ') 881 output = lines[3][:delim].strip(' "') 882 deps = lines[3][delim+3:].strip() 883 cmdline = lines[4].strip() 884 885 # build the output: 886 el = doc.createElement("Tool") 887 el.setAttribute("Name", "VCCustomBuildTool") 888 el.setAttribute('Description', description) 889 el.setAttribute('CommandLine', cmdline) 890 el.setAttribute('Outputs', output) 891 if len(deps) > 0: 892 # FIXME: remove this hack once custom build steps are 893 # implemented properly 894 # these are VC6-specific things that VC7+ doesn't recognize 895 if deps.startswith('"$(SOURCE)" '): 896 deps = deps[len('"$(SOURCE)" '):] 897 elif deps.startswith('$(SOURCE) '): 898 deps = deps[len('$(SOURCE) '):] 899 900 if _MSVS_SLN_VERSION == "10.00": 901 el.setAttribute('AdditionalDependencies', mk_listsp(deps)) 902 else: 903 el.setAttribute('AdditionalDependencies', deps) 904 905 return el 906 907 908 def buildEmbeddedTools(self, doc, conf_el): 909 deployment_tool_el = doc.createElement("DeploymentTool") 910 deployment_tool_el.setAttribute("ForceDirty", "-1") 911 deployment_tool_el.setAttribute("RemoteDirectory", "") 912 deployment_tool_el.setAttribute("RegisterOutput", "0") 913 deployment_tool_el.setAttribute("AdditionalFiles", "") 914 conf_el.appendChild(deployment_tool_el) 915 916 debugger_tool_el = doc.createElement("DebuggerTool") 917 conf_el.appendChild(debugger_tool_el) 918 919 def genVCProj(self, t, filename, prjname, guid): 920 #start a new xml document 921 doc = DocumentSorted() 922 top_el = doc.createElement("VisualStudioProject") 923 doc.appendChild(top_el) 924 comment = doc.createComment(""" 925 926 This project was generated by 927 Bakefile %s (http://www.bakefile.org) 928 Do not modify, all changes will be overwritten! 929 930""" % BAKEFILE_VERSION) 931 doc.insertBefore(comment, top_el) 932 933 #fill in the attributes of the top element 934 top_el.setAttribute("ProjectType", "Visual C++") 935 top_el.setAttribute("Version", _MSVS_VCPROJ_VERSION) 936 top_el.setAttribute("Name", t.id) 937 top_el.setAttribute("ProjectGUID", "%s" % guid) 938 939 top_el.appendChild(self.buildPlatformsElement(doc)) 940 941 if _MSVS_VCPROJ_VERSION != "7.10": 942 top_el.appendChild(self.buildToolFilesElement(doc)) 943 944 top_el.appendChild(self.buildAllConfigurations(doc, prjname, t)) 945 946 # MSVC insists on having these tags even though they are empty and 947 # moreover, it wants to have "<References>\n</References>" and not 948 # just "<References/>" so we need to add a dummy text element in the 949 # middle to make it closer, although still not perfect because it 950 # results in an extra one line which native project files don't have 951 # (TODO: is there a way to avoid this extra blank line?) 952 refs_el = doc.createElement("References") 953 refs_el.appendChild(doc.createTextNode("")) 954 top_el.appendChild(refs_el) 955 956 files_el = doc.createElement("Files") 957 958 #munge the source files around so we can write them to the file 959 sources, groups, files, filesWithCustomBuild = \ 960 organizeFilesIntoGroups(t, DEFAULT_FILE_GROUPS, groupClass=MsvsFilesGroup) 961 962 conflictingNames = findConflictingNames(sources.keys()) 963 964 ##define a local helper function for building the files area 965 def makeFileConfig(t, cfg, c, src, group, sources, pchExcluded, conflictingNames): 966 conf_name = self.mkConfigName(c) 967 file_conf_el = doc.createElement("FileConfiguration") 968 file_conf_el.setAttribute("Name", conf_name) 969 970 if sources[src] != None and c not in sources[src]: 971 file_conf_el.setAttribute("ExcludedFromBuild", bool2vcstr(True)) 972 tool_el = None 973 elif (src in filesWithCustomBuild.keys() and 974 c in filesWithCustomBuild[src].keys()): 975 #custom build for this file for this config 976 data = filesWithCustomBuild[src][c] 977 if data != '': 978 tool_el = self.buildCustomBuildElement(doc, data) 979 else: 980 tool_el = None 981 elif (cfg._pch_use_pch and 982 (src == cfg._pch_generator or src in pchExcluded)): 983 tool_el = doc.createElement("Tool") 984 tool_el.setAttribute("Name", "VCCLCompilerTool") 985 if src == cfg._pch_generator: 986 tool_el.setAttribute("UsePrecompiledHeader", pchCreateUsingSpecific) 987 elif src in pchExcluded: 988 tool_el.setAttribute("UsePrecompiledHeader", pchNone) 989 elif src in conflictingNames and isSourceFile(src): 990 # allow source files with same name but in different directory 991 # (see bug #92): 992 obj = os.path.splitext(utils.makeUniqueName(src, conflictingNames))[0] + '.obj' 993 tool_el = doc.createElement("Tool") 994 tool_el.setAttribute("Name", "VCCLCompilerTool") 995 tool_el.setAttribute("ObjectFile", "%s\\%s\\%s" % (cfg._builddir, t.id, obj)) 996 else: 997 tool_el = None 998 file_conf_el = None 999 1000 if tool_el != None: 1001 file_conf_el.appendChild(tool_el) 1002 return file_conf_el 1003 1004 pchExcluded = t._pch_excluded.split() 1005 1006 for group in [g for g in groups if g.name in files]: 1007 lst = files[group.name] 1008 sortByBasename(lst) 1009 if len(lst) == 0: continue 1010 1011 filt_el = doc.createElement("Filter") 1012 filt_el.setAttribute("Name", group.name) 1013 if group.extensions != None: 1014 filt_el.setAttribute("Filter", group.extensions) 1015 filt_el.setAttribute("UniqueIdentifier", group.uuid) 1016 1017 for src in lst: 1018 file_el = doc.createElement("File") 1019 relpath = "%s\\%s" % (SRCDIR.replace('/','\\'), src) 1020 if relpath.startswith(".\\..\\"): 1021 relpath = relpath[2:] 1022 file_el.setAttribute("RelativePath", relpath) 1023 1024 for c in sortedConfigKeys(t.configs): 1025 cfg = t.configs[c] 1026 file_conf_el = makeFileConfig(t, cfg, c, src, group.name, 1027 sources, 1028 pchExcluded, 1029 conflictingNames 1030 ) 1031 if ( file_conf_el != None ): 1032 file_el.appendChild(file_conf_el) 1033 1034 if (t.id, src) in self.fragments: 1035 f = VerbatimFragment(self.fragments[(t.id, src)]) 1036 file_el.appendChild(f) 1037 1038 filt_el.appendChild(file_el) 1039 1040 files_el.appendChild(filt_el) 1041 1042 top_el.appendChild(files_el) 1043 1044 # see comment for refs_el above 1045 globals_el = doc.createElement("Globals") 1046 globals_el.appendChild(doc.createTextNode("")) 1047 top_el.appendChild(globals_el) 1048 1049 vcprojText = doc.toprettyxml(encoding="Windows-1252") 1050 1051 writer.writeFile(filename, vcprojText) 1052 1053def run(): 1054 msvc_common.__dict__.update(globals()) 1055 generator = ProjectGeneratorMsvc9() 1056 generator.genWorkspaces() 1057