1 using System; 2 using System.Collections; 3 using System.IO; 4 using Premake.Tests.Framework; 5 6 namespace Premake.Tests.Vs6 7 { 8 public class Vs6Parser : Parser 9 { 10 #region Parser Methods 11 public override string TargetName 12 { 13 get { return "vs6"; } 14 } 15 #endregion 16 17 #region Workspace Parsing 18 Parse(Project project, string filename)19 public override void Parse(Project project, string filename) 20 { 21 /* File header */ 22 Begin(filename + ".dsw"); 23 24 Match("Microsoft Developer Studio Workspace File, Format Version 6.00"); 25 Match("# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!"); 26 Match(""); 27 Match("###############################################################################"); 28 Match(""); 29 30 Hashtable dependencies = new Hashtable(); 31 string[] matches; 32 while (!Match("Global:", true)) 33 { 34 Package package = new Package(); 35 project.Package.Add(package); 36 37 matches = Regex("Project: \"(.+)\"=(.+) - Package Owner=<4>"); 38 package.Name = matches[0]; 39 package.Path = Path.GetDirectoryName(matches[1]); 40 package.ScriptName = Path.GetFileName(matches[1]); 41 package.Language = "c++"; 42 Match(""); 43 44 Match("Package=<5>"); 45 Match("{{{"); 46 Match("}}}"); 47 Match(""); 48 Match("Package=<4>"); 49 Match("{{{"); 50 51 /* Get the package dependencies, cache until I have configs */ 52 ArrayList deps = new ArrayList(); 53 while (Match(" Begin Project Dependency", true)) 54 { 55 matches = Regex(" Project_Dep_Name (.+)"); 56 deps.Add(matches[0]); 57 Match(" End Project Dependency"); 58 } 59 dependencies[package] = (string[])deps.ToArray(typeof(string)); 60 61 Match("}}}"); 62 Match(""); 63 Match("###############################################################################"); 64 Match(""); 65 } 66 67 Match(""); 68 Match("Package=<5>"); 69 Match("{{{"); 70 Match("}}}"); 71 Match(""); 72 Match("Package=<3>"); 73 Match("{{{"); 74 Match("}}}"); 75 Match(""); 76 Match("###############################################################################"); 77 Match(""); 78 79 foreach (Package package in project.Package) 80 { 81 filename = Path.Combine(Path.Combine(project.Path, package.Path), package.ScriptName); 82 switch (package.Language) 83 { 84 case "c++": 85 ParseCpp(project, package, filename); 86 break; 87 default: 88 throw new NotImplementedException("Loading of " + package.Language + " packages not implemented"); 89 } 90 91 ArrayList temp = new ArrayList(); 92 foreach (Configuration config in package.Config) 93 { 94 config.Dependencies = (string[])dependencies[package]; 95 temp.Add(config); 96 } 97 98 /* VS6 stores configs in reverse order */ 99 temp.Reverse(); 100 for (int i = 0; i < package.Config.Count; ++i) 101 package.Config[i] = (Configuration)temp[i]; 102 } 103 } 104 105 #endregion 106 107 #region C++ Parsing 108 ParseCpp(Project project, Package package, string filename)109 private void ParseCpp(Project project, Package package, string filename) 110 { 111 Begin(filename); 112 Match("# Microsoft Developer Studio Project File - Name=\"" + package.Name + "\" - Package Owner=<4>"); 113 Match("# Microsoft Developer Studio Generated Build File, Format Version 6.00"); 114 Match("# ** DO NOT EDIT **"); 115 Match(""); 116 117 string[] matches = Regex("# TARGTYPE (.+)"); 118 switch (matches[0]) 119 { 120 case "\"Win32 (x86) Application\" 0x0101": 121 package.Kind = "winexe"; 122 break; 123 case "\"Win32 (x86) Console Application\" 0x0103": 124 package.Kind = "exe"; 125 break; 126 case "\"Win32 (x86) Dynamic-Link Library\" 0x0102": 127 package.Kind = "dll"; 128 break; 129 case "\"Win32 (x86) Static Library\" 0x0104": 130 package.Kind = "lib"; 131 break; 132 default: 133 throw new FormatException("Unrecognized target type: '" + matches[0] + "'"); 134 } 135 string baseKind = matches[0].Substring(0, matches[0].Length - 7); 136 Match(""); 137 138 matches = Regex("CFG=" + package.Name + " - (.+)"); 139 string defaultConfig = matches[0]; 140 141 Match("!MESSAGE This is not a valid makefile. To build this project using NMAKE,"); 142 Match("!MESSAGE use the Export Makefile command and run"); 143 Match("!MESSAGE "); 144 Match("!MESSAGE NMAKE /f \"" + package.Name + ".mak\"."); 145 Match("!MESSAGE "); 146 Match("!MESSAGE You can specify a configuration when running NMAKE"); 147 Match("!MESSAGE by defining the macro CFG on the command line. For example:"); 148 Match("!MESSAGE "); 149 Match("!MESSAGE NMAKE /f \"" + package.Name + ".mak\" CFG=\"" + package.Name + " - " + defaultConfig + "\""); 150 Match("!MESSAGE "); 151 Match("!MESSAGE Possible choices for configuration are:"); 152 Match("!MESSAGE "); 153 154 baseKind = baseKind.Replace("(", "\\("); 155 baseKind = baseKind.Replace(")", "\\)"); 156 while (!Match("!MESSAGE ", true)) 157 { 158 matches = Regex("!MESSAGE \"" + package.Name + " - ([^\"]+)\" \\(based on " + baseKind + "\\)"); 159 160 Configuration config = new Configuration(); 161 config.Name = matches[0]; 162 config.Dependencies = new string[0]; 163 package.Config.Add(config); 164 } 165 Match(""); 166 167 /* Verify configurations */ 168 if (project.Configuration.Count == 0) 169 { 170 foreach (Configuration config in package.Config) 171 project.Configuration.Add(config.Name); 172 } 173 else 174 { 175 foreach (Configuration config in package.Config) 176 if (!project.Configuration.Contains(config.Name)) 177 throw new FormatException("Config '" + config.Name + "' not found in project"); 178 } 179 180 Match("# Begin Project"); 181 Match("# PROP AllowPerConfigDependencies 0"); 182 Match("# PROP Scc_ProjName \"\""); 183 Match("# PROP Scc_LocalPath \"\""); 184 Match("CPP=cl.exe"); 185 if (package.Kind != "lib") 186 Match("MTL=midl.exe"); 187 Match("RSC=rc.exe"); 188 Match(""); 189 190 bool first = true; 191 foreach (Configuration config in package.Config) 192 { 193 ArrayList bldflags = new ArrayList(); 194 ArrayList lnkflags = new ArrayList(); 195 ArrayList defines = new ArrayList(); 196 ArrayList incpaths = new ArrayList(); 197 ArrayList links = new ArrayList(); 198 ArrayList libpaths = new ArrayList(); 199 200 string cond = (first) ? "!IF" : "!ELSEIF"; 201 first = false; 202 203 Match(cond + " \"$(CFG)\" == \"" + package.Name + " - " + config.Name + "\""); 204 Match(""); 205 206 Match("# PROP BASE Use_MFC 0"); 207 208 matches = Regex("# PROP BASE Use_Debug_Libraries ([0-1])"); 209 string useDebugLib = matches[0]; 210 211 matches = Regex("# PROP BASE Output_Dir \"(.+)\""); 212 config.OutDir = matches[0]; 213 config.BinDir = matches[0]; 214 config.LibDir = matches[0]; 215 216 matches = Regex("# PROP BASE Intermediate_Dir \"(.+)\""); 217 config.ObjDir = matches[0]; 218 219 Match("# PROP BASE Target_Dir \"\""); 220 Match("# PROP Use_MFC 0"); 221 Match("# PROP Use_Debug_Libraries " + useDebugLib); 222 Match("# PROP Output_Dir \"" + config.OutDir + "\""); 223 Match("# PROP Intermediate_Dir \"" + config.ObjDir + "\""); 224 225 if (Match("# PROP Ignore_Export_Lib 1", true)) 226 bldflags.Add("no-import-lib"); 227 228 Match("# PROP Target_Dir \"\""); 229 230 matches = Regex("# ADD BASE CPP /nologo (.+)"); 231 string flags = matches[0]; 232 matches = flags.Split(' '); 233 234 int i = 0; 235 if (matches[i] == "/MTd" || matches[i] == "/MT") 236 { 237 lnkflags.Add("static-runtime"); 238 } 239 else 240 { 241 Expect(matches[i], "/MDd", "/MD"); 242 } 243 ++i; 244 245 if (matches[i] == "/W4") 246 { 247 bldflags.Add("extra-warnings"); 248 } 249 else 250 { 251 Expect(matches[i], "/W3"); 252 } 253 ++i; 254 255 if (matches[i] == "/WX") 256 { 257 bldflags.Add("fatal-warnings"); 258 ++i; 259 } 260 261 if (matches[i] == "/Gm") 262 { 263 ++i; 264 } 265 266 if (matches[i] == "/GR") 267 { 268 ++i; 269 } 270 else 271 { 272 bldflags.Add("no-rtti"); 273 } 274 275 if (matches[i] == "/GX") 276 { 277 ++i; 278 } 279 else 280 { 281 bldflags.Add("no-exceptions"); 282 } 283 284 if (matches[i] == "/ZI") 285 { 286 ++i; 287 } 288 else 289 { 290 bldflags.Add("no-symbols"); 291 } 292 293 if (matches[i] == "/O1") 294 { 295 bldflags.Add("optimize-size"); 296 } 297 else if (matches[i] == "/O2") 298 { 299 bldflags.Add("optimize"); 300 } 301 else 302 { 303 Expect(matches[i], "/Od"); 304 } 305 ++i; 306 307 if (matches[i] == "/Oy") 308 { 309 bldflags.Add("no-frame-pointer"); 310 ++i; 311 } 312 313 while (matches[i] == "/I") 314 { 315 incpaths.Add(matches[++i].Trim('\"')); 316 ++i; 317 } 318 319 while (matches[i] == "/D") 320 { 321 defines.Add(matches[++i].Trim('\"')); 322 ++i; 323 } 324 325 Expect(matches[i], "/YX"); 326 Expect(matches[++i], "/FD"); 327 if (matches[++i] == "/GZ") 328 ++i; 329 Expect(matches[i++], "/c"); 330 331 config.BuildOptions = String.Empty; 332 while (i < matches.Length) 333 config.BuildOptions += " " + matches[i++]; 334 config.BuildOptions = config.BuildOptions.Trim(); 335 336 Match("# ADD CPP /nologo " + flags); 337 338 string debugSymbol = bldflags.Contains("no-symbols") ? "NDEBUG" : "_DEBUG"; 339 340 bool hasMtl = Match("# ADD BASE MTL /nologo /D \"" + debugSymbol + "\" /mktyplib203 /win32", true); 341 if (hasMtl) 342 Match("# ADD MTL /nologo /D \"" + debugSymbol + "\" /mktyplib203 /win32"); 343 344 ArrayList resDefines = new ArrayList(); 345 ArrayList resPaths = new ArrayList(); 346 matches = Regex("# ADD BASE RSC /l 0x409 (.+)"); 347 while (matches[0].Length > 0) 348 { 349 if (matches[0].StartsWith("/d") || matches[0].StartsWith("/i")) 350 { 351 int split = matches[0].IndexOf('"', 4); 352 string value = matches[0].Substring(4, split - 4); 353 if (matches[0].StartsWith("/d")) 354 resDefines.Add(value); 355 else 356 resPaths.Add(value); 357 matches[0] = matches[0].Substring(split + 1).TrimStart(); 358 } 359 else 360 { 361 config.ResOptions = matches[0]; 362 matches[0] = ""; 363 } 364 } 365 366 config.ResDefines = (string[])resDefines.ToArray(typeof(string)); 367 config.ResPaths = (string[])resPaths.ToArray(typeof(string)); 368 369 string rsc = "# ADD RSC /l 0x409"; 370 foreach (string symbol in resDefines) 371 rsc += " /d \"" + symbol + "\""; 372 foreach (string symbol in resPaths) 373 rsc += " /i \"" + symbol + "\""; 374 if (config.ResOptions != null) 375 rsc += " " + config.ResOptions; 376 Match(rsc); 377 378 Match("BSC32=bscmake.exe"); 379 Match("# ADD BASE BSC32 /nologo"); 380 Match("# ADD BSC32 /nologo"); 381 382 if (Match("LINK32=link.exe -lib", true)) 383 { 384 config.Kind = "lib"; 385 Match("# ADD BASE LIB32 /nologo"); 386 matches = Regex("# ADD LIB32 /nologo /out:\"(.+)\"", true); 387 if (matches != null) 388 { 389 config.Target = matches[0]; 390 } 391 else 392 { 393 Match("# ADD LIB32 /nologo"); 394 } 395 } 396 else 397 { 398 Match("LINK32=link.exe"); 399 matches = Regex("# ADD BASE LINK32 (.+)"); 400 flags = matches[0]; 401 matches = flags.Split(' '); 402 403 i = 0; 404 while (i < matches.Length && matches[i][0] != '/') 405 links.Add(matches[i++]); 406 407 Expect(matches[i++], "/nologo"); 408 409 bool noMain = true; 410 if (matches[i] == "/entry:\"mainCRTStartup\"") 411 { 412 noMain = false; 413 ++i; 414 } 415 416 if (matches[i] == "/subsystem:windows") 417 { 418 config.Kind = "winexe"; 419 } 420 else if (matches[i] == "/subsystem:console") 421 { 422 config.Kind = "exe"; 423 } 424 else if (matches[i] == "/dll") 425 { 426 config.Kind = "dll"; 427 } 428 else 429 { 430 throw new FormatException("Unexpected subsystem flag: " + matches[i]); 431 } 432 ++i; 433 434 if (hasMtl && config.Kind != "winexe" && config.Kind != "dll") 435 throw new FormatException("Found unexpected MTL block"); 436 437 if ((config.Kind == "winexe" || config.Kind == "exe") && noMain) 438 bldflags.Add("no-main"); 439 440 if (!bldflags.Contains("no-symbols")) 441 { 442 Expect(matches[i++], "/incremental:yes"); 443 Expect(matches[i++], "/debug"); 444 } 445 446 Expect(matches[i++], "/machine:I386"); 447 448 if (config.Kind == "dll") 449 { 450 config.ImportLib = matches[i].Substring(9, matches[i].Length - 10); 451 ++i; 452 } 453 454 config.Target = matches[i].Substring(6, matches[i].Length - 7); 455 if (config.Target.StartsWith(config.OutDir)) 456 config.Target = config.Target.Substring(config.OutDir.Length + 1); 457 ++i; 458 459 if (!bldflags.Contains("no-symbols")) 460 Expect(matches[i++], "/pdbtype:sept"); 461 462 config.LibDir = matches[i].Substring(10, matches[i].Length - 11); 463 ++i; 464 465 while (i < matches.Length && matches[i].StartsWith("/libpath:")) 466 { 467 libpaths.Add(matches[i].Substring(10, matches[i].Length - 11)); 468 ++i; 469 } 470 471 Match("# ADD LINK32 " + flags); 472 } 473 474 config.BuildFlags = (string[])bldflags.ToArray(typeof(string)); 475 config.LinkFlags = (string[])lnkflags.ToArray(typeof(string)); 476 config.Defines = (string[])defines.ToArray(typeof(string)); 477 config.IncludePaths = (string[])incpaths.ToArray(typeof(string)); 478 config.Links = (string[])links.ToArray(typeof(string)); 479 config.LibPaths = (string[])libpaths.ToArray(typeof(string)); 480 481 Match(""); 482 } 483 484 Match("!ENDIF"); 485 Match(""); 486 Match("# Begin Target"); 487 Match(""); 488 489 foreach (Configuration config in package.Config) 490 Match("# Name \"" + package.Name + " - " + config.Name + "\""); 491 492 string folder = ""; 493 while (!Match("# End Target", true)) 494 { 495 matches = Regex("# Begin Group \"(.+)\"", true); 496 if (matches != null) 497 { 498 folder = Path.Combine(folder, matches[0]); 499 Match(""); 500 Match("# PROP Default_Filter \"\""); 501 continue; 502 } 503 504 matches = Regex("# End Group", true); 505 if (matches != null) 506 { 507 folder = Path.GetDirectoryName(folder); 508 continue; 509 } 510 511 Match("# Begin Source File"); 512 Match(""); 513 matches = Regex("SOURCE=(.+)"); 514 515 filename = matches[0]; 516 package.File.Add(filename); 517 518 if (filename.StartsWith("./")) 519 filename = filename.Substring(2); 520 while (filename.StartsWith("../")) 521 filename = filename.Substring(3); 522 if (Path.GetDirectoryName(filename) != folder) 523 throw new FormatException("File '" + matches[0] + "' is in folder '" + folder + "'"); 524 525 Match("# End Source File"); 526 } 527 } 528 529 #endregion 530 } 531 } 532