using System;
using System.Collections;
using System.IO;
using Premake.Tests.Framework;
namespace Premake.Tests.Vs2002
{
public class Vs2002Parser : Parser
{
#region Parser Methods
public override string TargetName
{
get { return "vs2002"; }
}
#endregion
#region Solution Parsing
public override void Parse(Project project, string filename)
{
/* File header */
Begin(filename + ".sln");
Match("Microsoft Visual Studio Solution File, Format Version 7.00");
/* Package entries - VS "projects" */
string[] matches;
do
{
matches = Regex("Project\\(\"{([0-9A-F-]+)}\"\\) = \"(.+)\", \"(.+)\", \"{([0-9A-F-]+)}\"", true);
if (matches != null)
{
Package package = new Package();
project.Package.Add(package);
package.Name = matches[1];
package.ID = matches[3];
package.Path = Path.GetDirectoryName(matches[2]);
package.ScriptName = Path.GetFileName(matches[2]);
if (package.Path != String.Empty)
package.Path += '\\';
switch (matches[0])
{
case "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942":
package.Language = "c++";
break;
case "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC":
package.Language = "c#";
break;
}
Match("EndProject");
}
} while (matches != null);
Match("Global");
Match("\tGlobalSection(SolutionConfiguration) = preSolution");
/* Read the list of configurations */
int i = 0;
do
{
matches = Regex("\t\tConfigName[.]" + i + "[ ]=[ ](.+)", true);
if (matches != null)
project.Configuration.Add(matches[0]);
++i;
} while (matches != null);
Hashtable packageDependencies = new Hashtable();
foreach (Package package in project.Package)
{
package.Config.Add(project);
packageDependencies[package] = new ArrayList();
}
Match("\tEndGlobalSection");
/* Collect package dependencies, I'll sort them out after I finish
* parsing the scripts below */
Match("\tGlobalSection(ProjectDependencies) = postSolution");
while (!Match("\tEndGlobalSection", true))
{
matches = Regex("\t\t{([0-9A-F-]+)}[.]([0-9]+) = {([0-9A-F-]+)}");
foreach (Package p2 in project.Package)
{
if (p2.ID == matches[0])
{
ArrayList pkgdeps = (ArrayList)packageDependencies[p2];
if (int.Parse(matches[1]) != pkgdeps.Count)
throw new FormatException("Package dependency index should be " + pkgdeps.Count + ", is " + matches[1]);
pkgdeps.Add(matches[2]);
}
}
}
Match("\tGlobalSection(ProjectConfiguration) = postSolution");
/* Read the list of package configurations */
foreach (Package package in project.Package)
{
string arch = (package.Language == "c++") ? "Win32" : ".NET";
foreach (string config in project.Configuration)
{
string pattern = "\t\t{" + package.ID + "}." + config + ".ActiveCfg = " + config + "|" + arch;
Match(pattern);
pattern = "\t\t{" + package.ID + "}." + config + ".Build.0 = " + config + "|" + arch;
Match(pattern);
}
}
Match("\tEndGlobalSection");
Match("\tGlobalSection(ExtensibilityGlobals) = postSolution");
Match("\tEndGlobalSection");
Match("\tGlobalSection(ExtensibilityAddIns) = postSolution");
Match("\tEndGlobalSection");
Match("EndGlobal");
foreach (Package package in project.Package)
{
filename = Path.Combine(Path.Combine(project.Path, package.Path), package.ScriptName);
switch (package.Language)
{
case "c++":
ParseCpp(project, package, filename);
break;
case "c#":
ParseCs(project, package, filename);
break;
default:
throw new NotImplementedException("Loading of " + package.Language + " packages not implemented");
}
}
/* Now sort out the inter-package dependencies */
foreach (Package package in project.Package)
{
ArrayList deps = (ArrayList)packageDependencies[package];
string[] deplist = new string[deps.Count];
for (i = 0; i < deps.Count; ++i)
{
foreach (Package p2 in project.Package)
{
if (p2.ID == (string)deps[i])
deplist[i] = p2.Name;
}
}
foreach (Configuration config in package.Config)
config.Dependencies = deplist;
}
}
#endregion
#region C++ Parsing
private void ParseCpp(Project project, Package package, string filename)
{
Begin(filename);
Match("");
Match("");
Match("\t");
Match("\t\t");
Match("\t");
Match("\t");
foreach (Configuration config in package.Config)
{
ArrayList buildFlags = new ArrayList();
Match("\t\t");
if (matches[0] == "1")
buildFlags.Add("unicode");
Match("\t\t\t 0)
Match("\t\t\t\tStringPooling=\"TRUE\"", true);
if (optimization == 0)
Match("\t\t\t\tBasicRuntimeChecks=\"3\"", true);
if (optimization == 0)
matches = Regex("\t\t\t\tRuntimeLibrary=\"(1|3)\"");
else
matches = Regex("\t\t\t\tRuntimeLibrary=\"(0|2)\"");
if (matches[0] == "0" || matches[0] == "1")
config.LinkFlags = new string[] { "static-runtime" };
Match("\t\t\t\tEnableFunctionLevelLinking=\"TRUE\"");
if (!Match("\t\t\t\tRuntimeTypeInfo=\"TRUE\"", true))
buildFlags.Add("no-rtti");
matches = Regex("\t\t\t\tTreatWChar_tAsBuiltInType=\"(TRUE|FALSE)\"", true);
if (matches != null)
buildFlags.Add(matches[0] == "TRUE" ? "native-wchar" : "no-native-wchar");
config.Pch = "auto";
matches = Regex("\t\t\t\tUsePrecompiledHeader=\"([0-9])\"");
if (matches[0] == "3")
{
config.Pch = "on";
matches = Regex("\t\t\t\tPrecompiledHeaderThrough=\"(.+?)\"");
config.PchHeader = matches[0];
}
else if (matches[0] == "0")
{
config.Pch = "off";
}
else if (matches[0] != "2")
{
throw new FormatException("Expected UsePrecompiledHeader to be 2, got " + matches[0]);
}
matches = Regex("\t\t\t\tWarningLevel=\"([3-4])\"");
if (matches[0] == "4")
buildFlags.Add("extra-warnings");
if (Match("\t\t\t\tWarnAsError=\"TRUE\"", true))
buildFlags.Add("fatal-warnings");
matches = Regex("\t\t\t\tDetect64BitPortabilityProblems=\"(TRUE|FALSE)\"");
if (matches[0] == "FALSE")
buildFlags.Add("no-64bit-checks");
matches = Regex("\t\t\t\tDebugInformationFormat=\"([0-9])\"/>");
if (matches[0] == "0")
buildFlags.Add("no-symbols");
else if (matches[0] == "3")
buildFlags.Add("no-edit-and-continue");
Match("\t\t\t");
Match("\t\t\t");
config.Target = matches[0];
}
else
{
Match("\t\t\t\tName=\"VCLinkerTool\"");
matches = Regex("\t\t\t\tAdditionalDependencies=\"(.+)\"", true);
if (matches != null)
config.Links = matches[0].Split(' ');
if (Match("\t\t\t\tIgnoreImportLibrary=\"TRUE\"", true))
buildFlags.Add("no-import-lib");
matches = Regex("\t\t\t\tOutputFile=\"\\$\\(OutDir\\)/(.+)\"");
config.Target = matches[0];
Match("\t\t\t\tLinkIncremental=\"" + (optimization == 0 ? 2 : 1) + "\"");
matches = Regex("\t\t\t\tAdditionalLibraryDirectories=\"(.+)\"");
matches = matches[0].Split(';');
config.LibDir = matches[0];
if (config.LibDir != config.OutDir)
config.BinDir = config.OutDir;
config.LibPaths = new string[matches.Length - 1];
for (int i = 0; i < matches.Length - 1; ++i)
config.LibPaths[i] = matches[i + 1];
matches = Regex("\t\t\t\tModuleDefinitionFile=\"(.+)\"", true);
if (matches != null)
package.DefFile = matches[0];
if (Match("\t\t\t\tGenerateManifest=\"FALSE\"", true))
buildFlags.Add("no-manifest");
string expected = buildFlags.Contains("no-symbols") ? "FALSE" : "TRUE";
Match("\t\t\t\tGenerateDebugInformation=\"" + expected + "\"");
if (!buildFlags.Contains("no-symbols"))
Match("\t\t\t\tProgramDatabaseFile=\"$(OutDir)/" + Path.GetFileNameWithoutExtension(config.Target) + ".pdb\"");
matches = Regex("\t\t\t\tSubSystem=\"([0-9])\"");
if (config.Kind == "exe" && matches[0] == "2")
config.Kind = "winexe";
if (optimization > 0)
{
Match("\t\t\t\tOptimizeReferences=\"2\"");
Match("\t\t\t\tEnableCOMDATFolding=\"2\"");
}
if (config.Kind == "exe" || config.Kind == "winexe")
{
if (!Match("\t\t\t\tEntryPointSymbol=\"mainCRTStartup\"", true))
buildFlags.Add("no-main");
}
else
{
matches = Regex("\t\t\t\tImportLibrary=\"(.+)\"");
config.ImportLib = matches[0];
}
Match("\t\t\t\tTargetMachine=\"1\"/>");
}
Match("\t\t\t");
Match("\t\t\t");
Match("\t\t\t");
Match("\t\t\t");
Match("\t\t\t", true))
{
Match("\t\t\t\tName=\"VCResourceCompilerTool\"");
matches = Regex("\t\t\t\tAdditionalOptions=\"(.+)\"(.*)", true);
config.ResOptions = (matches != null) ? matches[0] : "";
matches = Regex("\t\t\t\tPreprocessorDefinitions=\"(.+)\"(.*)", true);
config.ResDefines = (matches != null) ? matches[0].Split(';') : new string[] { };
matches = Regex("\t\t\t\tAdditionalIncludeDirectories=\"(.+)\"(.*)", true);
config.ResPaths = (matches != null) ? matches[0].Split(';') : new string[] { };
}
Match("\t\t\t");
Match("\t\t\t");
Match("\t\t");
config.BuildFlags = (string[])buildFlags.ToArray(typeof(string));
}
Match("\t");
Match("\t");
string indent = "\t";
string folder = "";
while (!Match("\t", true))
{
if (Match(indent + "\t");
}
else if (Match(indent + "", true))
{
indent = indent.Substring(0, indent.Length - 1);
folder = Path.GetDirectoryName(folder);
}
else
{
Match(indent + "\t");
package.File.Add(matches[0]);
/* Make sure file appears in the correct folder */
filename = matches[0];
if (filename.StartsWith(".\\"))
filename = filename.Substring(2);
while (filename.StartsWith("..\\"))
filename = filename.Substring(3);
if (Path.GetDirectoryName(filename) != folder)
throw new FormatException("File '" + matches[0] + "' is in folder '" + folder + "'");
/* Check for file configuration section */
foreach (Configuration config in package.Config)
{
if (Match(indent + "\t\t");
Match(indent + "\t\t\t");
Match(indent + "\t\t");
}
}
Match(indent + "\t");
}
}
if (indent != "\t")
throw new FormatException("Unclosed entity in block");
Match("\t");
Match("\t");
Match("");
}
#endregion
#region C# Parsing
private void ParseCs(Project project, Package package, string filename)
{
string kind = null;
Begin(filename);
Match("");
Match("\t");
Match("\t\t");
Match("\t\t\t");
foreach (Configuration config in package.Config)
{
ArrayList buildFlags = new ArrayList();
config.Target = target;
Match("\t\t\t\t");
config.BuildFlags = (string[])buildFlags.ToArray(typeof(string));
}
Match("\t\t\t");
ArrayList links = new ArrayList();
ArrayList lddep = new ArrayList();
Match("\t\t\t");
while (!Match("\t\t\t", true))
{
Match("\t\t\t\t");
}
foreach (Configuration config in package.Config)
{
config.Kind = kind;
config.Links = (string[])links.ToArray(typeof(string));
config.LinkDeps = (string[])lddep.ToArray(typeof(string));
}
Match("\t\t");
Match("\t\t");
Match("\t\t\t");
while (!Match("\t\t\t", true))
{
Match("\t\t\t\t");
}
Match("\t\t");
Match("\t");
Match("");
if (kind != "aspnet")
ParseUserFile(project, package, filename);
}
private void ParseUserFile(Project project, Package package, string filename)
{
Begin(filename + ".user");
Match("");
Match("\t");
Match("\t\t");
string[] matches = Regex("\t\t\t");
matches = matches[0].Split(';');
string[] libpaths = new string[matches.Length - 1];
/* VS.NET stores reference directories as absolute paths, so I need
* to do some ugly trickery here */
string[] bindir = matches[matches.Length - 1].Split('\\');
for (int i = 0; i < matches.Length - 1; ++i)
{
string[] thisdir = matches[i].Split('\\');
int j = 0;
while (j < bindir.Length && j < thisdir.Length && bindir[j] == thisdir[j])
++j;
string path = "";
for (int k = j + 1; k < bindir.Length; ++k)
path += "..\\";
for (int k = j; k < thisdir.Length; ++k)
path += thisdir[k] + '\\';
libpaths[i] = path.Substring(0, path.Length - 1);
libpaths[i] = libpaths[i].Replace("/", "\\");
}
foreach (Configuration config in package.Config)
{
config.LibPaths = libpaths;
Match("\t\t\t\t");
}
Match("\t\t\t");
Match("\t\t");
Match("\t\t");
Match("\t");
Match("");
}
#endregion
}
}