1# 2# This file is part of Bakefile (http://www.bakefile.org) 3# 4# Copyright (C) 2003-2007 Vaclav Slavik 5# 6# Permission is hereby granted, free of charge, to any person obtaining a copy 7# of this software and associated documentation files (the "Software"), to 8# deal in the Software without restriction, including without limitation the 9# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10# sell copies of the Software, and to permit persons to whom the Software is 11# furnished to do so, subject to the following conditions: 12# 13# The above copyright notice and this permission notice shall be included in 14# all copies or substantial portions of the Software. 15# 16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22# IN THE SOFTWARE. 23# 24# $Id: msvc6prj.py 1295 2009-03-16 08:55:50Z vaclavslavik $ 25# 26# MS Visual C++ projects generator script 27# 28 29import os, os.path 30import errors, utils 31 32import msvc_common 33from msvc_common import * 34 35 36DEFAULT_FILE_GROUPS = [ 37 FilesGroup('Source Files', 38 '*.cpp *.c *.cxx *.rc *.def *.r *.odl *.idl *.hpj *.bat'), 39 FilesGroup('Header Files', 40 '*.h *.hpp *.hxx *.hm *.inl'), 41 FilesGroup('Resource Files', 42 '*.ico *.cur *.bmp *.dlg *.rc2 *.rct *.bin *.rgs *.gif *.jpg *.jpeg *.jpe') 43] 44 45# ------------------------------------------------------------------------ 46# Generator class 47# ------------------------------------------------------------------------ 48 49class ProjectGeneratorMsvc6: 50 51 def __init__(self): 52 self.basename = os.path.splitext(os.path.basename(FILE))[0] 53 self.dirname = os.path.dirname(FILE) 54 55 # -------------------------------------------------------------------- 56 # basic configuration 57 # -------------------------------------------------------------------- 58 59 def getSolutionExtension(self): 60 return 'dsw' 61 def getProjectExtension(self): 62 return 'dsp' 63 def getMakefileExtension(self): 64 return 'mak' 65 66 # -------------------------------------------------------------------- 67 # helpers 68 # -------------------------------------------------------------------- 69 70 def mkConfigName(self, target, config): 71 return '%s - Win32 %s' % (target, config) 72 73 def makeDependency(self, prj_id): 74 return """\ 75Begin Project Dependency 76Project_Dep_Name %s 77End Project Dependency 78""" % prj_id 79 80 81 # -------------------------------------------------------------------- 82 # DSW file 83 # -------------------------------------------------------------------- 84 85 def makeDswHeader(self): 86 return """\ 87Microsoft Developer Studio Workspace File, Format Version 6.00 88# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! 89 90###############################################################################""" 91 92 def genDSW(self, dsw_targets, dsp_list, deps_translation): 93 dsw = self.makeDswHeader() 94 project = """ 95Project: "%s"=%s.""" + self.getProjectExtension() + """ - Package Owner=<4> 96 97Package=<5> 98{{{ 99}}} 100 101Package=<4> 102{{{ 103%s}}} 104 105############################################################################### 106""" 107 108 single_target = (len(dsw_targets) == 1) 109 for t in dsw_targets: 110 deps = '' 111 if single_target: 112 dsp_name = self.basename 113 else: 114 dsp_name = '%s_%s' % (self.basename, t.id) 115 deplist = t._deps.split() 116 117 # add external dsp dependencies: 118 for d in t._dsp_deps.split(): 119 deplist.append(d.split(':')[0]) 120 # write dependencies: 121 for d in deplist: 122 if d in deps_translation: 123 d2 = deps_translation[d] 124 else: 125 d2 = d 126 deps += self.makeDependency(d2) 127 128 dsw += project % (t.id, dsp_name, deps) 129 dspfile = (t, 130 os.path.join(self.dirname, 131 dsp_name + '.' + self.getProjectExtension()), 132 dsp_name) 133 if dspfile not in dsp_list: 134 dsp_list.append(dspfile) 135 136 # add external dsp deps (we put them after bakefile's own targets so 137 # that the active project when you first open the workspace is something 138 # meaningful and not a mere dependency): 139 extern_deps = [] 140 for t in dsw_targets: 141 for d in t._dsp_deps.split(): 142 if d not in extern_deps: 143 extern_deps.append(d) 144 for d in extern_deps: 145 deps = '' 146 d_components = d.split(':') 147 if len(d_components) == 3: 148 for d_dep in d_components[2].split(','): 149 deps += self.makeDependency(d_dep) 150 dsw += project % (d_components[0], 151 os.path.splitext(d_components[1])[0], deps) 152 153 writer.writeFile('%s.%s' % ( 154 os.path.join(self.dirname, self.basename), 155 self.getSolutionExtension() 156 ), dsw) 157 158 159 def genWorkspaces(self): 160 dsp_list = [] 161 162 # find all projects. Beware ugly hack here: MSVC6PRJ_MERGED_TARGETS is 163 # used to create fake targets as a merge of two (mutually exclusive) 164 # targets. This is sometimes useful, e.g. when you want to build both 165 # DLL and static lib of something. 166 deps_translation = {} 167 projects = [t for t in targets if t._kind == 'project'] 168 for mergeInfo in MSVC6PRJ_MERGED_TARGETS.split(): 169 split1 = mergeInfo.split('=') 170 split2 = split1[1].split('+') 171 tgR = split1[0] 172 tg1 = split2[0] 173 tg2 = split2[1] 174 175 # the targets may be disabled by some (weak) condition: 176 if tg1 not in targets and tg2 not in targets: 177 continue 178 179 t = targets[tg1] 180 for c in targets[tg2].configs: 181 assert c not in t.configs # otherwise not mutually exclusive 182 t.configs[c] = targets[tg2].configs[c] 183 t.id = tgR 184 projects.remove(targets[tg2]) 185 targets.append(tgR, t) 186 del targets[tg1] 187 del targets[tg2] 188 deps_translation[tg1] = tgR 189 deps_translation[tg2] = tgR 190 191 self.genDSW(projects, dsp_list, deps_translation) 192 for t, filename, prjname in dsp_list: 193 self.genDSP(t, filename, prjname) 194 195 # warn about <action> targets that we can't handle (yet): 196 for t in [t for t in targets if t._kind == 'action']: 197 print "warning: ignoring action target '%s'" % t.id 198 199 200 # ------------------------------------------------------------------------ 201 # DSP files 202 # ------------------------------------------------------------------------ 203 204 def mkFlags(self, keyword, lines): 205 result = [] 206 splitted = lines.splitlines(); 207 splitted2 = [fixFlagsQuoting(' '.join(x.split())) for x in splitted] 208 for l in splitted2: 209 result.append('# %s BASE %s' % (keyword, l)) 210 for l in splitted2: 211 result.append('# %s %s' % (keyword, l)) 212 return '\n'.join(result)+'\n' 213 214 215 def makeDspHeader(self, id): 216 return """\ 217# Microsoft Developer Studio Project File - Name="%s" - Package Owner=<4> 218# Microsoft Developer Studio Generated Build File, Format Version 6.00 219# ** DO NOT EDIT ** 220 221""" % id 222 223 def makeBeginProject(self, t): 224 txt = """\ 225# PROP AllowPerConfigDependencies 0 226# PROP Scc_ProjName "" 227# PROP Scc_LocalPath "" 228CPP=cl.exe 229""" 230 if t._type_nick in ['gui','dll']: 231 txt += 'MTL=midl.exe\n' 232 txt += 'RSC=rc.exe\n' 233 return txt 234 235 def makeCpuPlatformID(self, cfg): 236 return '' 237 238 def makeSettingsCPP(self, cfg): 239 return self.mkFlags('ADD', 240 'CPP /nologo %s %s /c' % (cfg._cppflags, cfg._defines)) 241 def makeSettingsMTL(self, cfg): 242 return self.mkFlags('ADD', 243 'MTL /nologo %s /mktyplib203 /win32' % cfg._defines) 244 245 def makeSettingsRSC(self, cfg): 246 return self.mkFlags('ADD', 'RSC %s' % cfg._win32rc_flags) 247 248 def makeSettingsBSC(self, cfg): 249 return """\ 250BSC32=bscmake.exe 251# ADD BASE BSC32 /nologo 252# ADD BSC32 /nologo 253""" 254 def makeSettingsLIB(self, cfg): 255 txt = 'LIB32=link.exe -lib\n' 256 txt += self.mkFlags('ADD','LIB32 /nologo %s' % cfg._outflag) 257 return txt 258 259 def makeSettingsLINK(self, cfg): 260 txt = 'LINK32=link.exe\n' 261 txt += self.mkFlags('ADD','LINK32 %s /nologo %s' % (cfg._ldlibs, cfg._ldflags)) 262 return txt 263 264 def makeSettingsCPP_MTL_RSC_BSC_LINK(self, cfg): 265 txt = self.makeSettingsCPP(cfg) 266 if cfg._type_nick in ['gui','dll']: 267 txt += self.makeSettingsMTL(cfg) 268 txt += self.makeSettingsRSC(cfg) 269 txt += self.makeSettingsBSC(cfg) 270 if cfg._type_nick != 'lib': 271 txt += self.makeSettingsLINK(cfg) 272 else: 273 txt += self.makeSettingsLIB(cfg) 274 return txt 275 276 def genDSP(self, t, filename, prjname): 277 # Create header and list of configurations: 278 279 default_cfg = sortedConfigKeys(t.configs)[-1] 280 dsp = self.makeDspHeader(prjname) 281 targ_types = [] 282 for c in t.configs: 283 targ = '%s %s' % (t.configs[c]._type, t.configs[c]._type_code) 284 if targ not in targ_types: 285 targ_types.append(targ) 286 for tt in targ_types: 287 dsp += '# TARGTYPE %s\n' % tt 288 289 dsp += '\nCFG=%s\n' % self.mkConfigName(t.id, default_cfg) 290 dsp += """\ 291!MESSAGE This is not a valid makefile. To build this project using NMAKE, 292!MESSAGE use the Export Makefile command and run 293!MESSAGE 294!MESSAGE NMAKE /f "%s.%s". 295!MESSAGE 296!MESSAGE You can specify a configuration when running NMAKE 297!MESSAGE by defining the macro CFG on the command line. For example: 298!MESSAGE 299!MESSAGE NMAKE /f "%s.%s" CFG="%s" 300!MESSAGE 301!MESSAGE Possible choices for configuration are: 302!MESSAGE 303""" % (prjname, self.getMakefileExtension(), 304 prjname, self.getMakefileExtension(), 305 self.mkConfigName(t.id, default_cfg)) 306 for c in sortedConfigKeys(t.configs): 307 dsp += '!MESSAGE "%s" (based on %s)\n' % (self.mkConfigName(t.id, c), t.configs[c]._type) 308 dsp += """\ 309!MESSAGE 310 311# Begin Project 312""" 313 dsp += self.makeBeginProject(t) 314 dsp += '\n' 315 316 # Output settings for all configurations: 317 flags = [] 318 for c in sortedConfigKeys(t.configs): 319 cfg = t.configs[c] 320 fl = ' "$(CFG)" == "%s"' % self.mkConfigName(t.id, c) + '\n\n' 321 fl += self.mkFlags('PROP',"""\ 322Use_MFC 0 323Use_Debug_Libraries """ + cfg._debug + """ 324Output_Dir "%s" 325Intermediate_Dir "%s\\%s" 326%sTarget_Dir "" 327""" % (cfg._targetdir[:-1], cfg._builddir, t.id, 328 self.makeCpuPlatformID(cfg))) 329 fl += self.makeSettingsCPP_MTL_RSC_BSC_LINK(cfg) 330 fl += '\n' 331 flags.append(fl) 332 dsp += '!IF' + '!ELSEIF'.join(flags) + '!ENDIF' 333 334 dsp += '\n\n# Begin Target\n\n' 335 336 # Output list of configs one more: 337 for c in sortedConfigKeys(t.configs): 338 dsp += '# Name "%s"\n' % self.mkConfigName(t.id, c) 339 340 # Write source files: 341 342 sources, groups, files, filesWithCustomBuild = \ 343 organizeFilesIntoGroups(t, DEFAULT_FILE_GROUPS) 344 345 # (some files-related settings:) 346 pchExcluded = t._pch_excluded.split() 347 348 # (write them) 349 for group in [g.name for g in groups if g.name in files]: 350 lst = files[group] 351 sortByBasename(lst) 352 if len(lst) == 0: continue 353 if group != None: 354 dsp += """\ 355# Begin Group "%s" 356 357# PROP Default_Filter "" 358""" % group 359 for src in lst: 360 dsp += """\ 361# Begin Source File 362 363SOURCE=%s\\%s 364""" % (SRCDIR.replace('/','\\'),src) 365 file_flags = '' 366 if src == t._pch_generator: 367 file_flags += '# ADD BASE CPP /Yc"%s"\n' % t._pch_header 368 file_flags += '# ADD CPP /Yc"%s"\n' % t._pch_header 369 if src in pchExcluded: 370 file_flags += '# SUBTRACT CPP /YX /Yc /Yu\n' 371 372 # the file is either disabled in some configurations or has 373 # custom build step associated with it: 374 if sources[src] != None or src in filesWithCustomBuild: 375 flags = [] 376 old_file_flags = file_flags 377 for c in sortedConfigKeys(t.configs): 378 if sources[src] != None and c not in sources[src]: 379 file_flags += '# PROP Exclude_From_Build 1\n' 380 if src in filesWithCustomBuild: 381 file_flags += \ 382 '# Begin Custom Build - %s\n\n# End Custom Build\n' %\ 383 filesWithCustomBuild[src][c] 384 flags.append(' "$(CFG)" == "%s"' % self.mkConfigName(t.id, c) + 385 '\n\n' + file_flags + '\n') 386 file_flags = old_file_flags 387 dsp += '\n!IF' + '!ELSEIF'.join(flags) + '!ENDIF\n\n' 388 else: 389 dsp += file_flags 390 dsp += '# End Source File\n' 391 if group != None: 392 dsp += '# End Group\n' 393 394 # Write footer: 395 dsp += """\ 396# End Target 397# End Project 398""" 399 400 writer.writeFile(filename, dsp) 401 402def run(): 403 msvc_common.__dict__.update(globals()) 404 generator = ProjectGeneratorMsvc6() 405 generator.genWorkspaces() 406