using System;
using Bytepoint.System;
using BpConsole = Bytepoint.System.Console;
using Bytepoint.IO;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.XPath;
using System.Xml.Serialization;
using System.Reflection;
namespace Vrt
{
public enum MessageType
{
Normal,
Warning,
Error
};
internal enum VersionPart
{
Major,
Minor,
Point,
Build
};
/// <summary>
/// Summary description for Class1.
/// </summary>
class MainClass
{
private static ArrayList outputFiles = new ArrayList();
private static string templateDir;
private static string inputFile;
private static string versionString = "+0.0.0.1";
private static bool noLogo = false;
private static bool trace = false;
private static ushort lang = 1033;
private static ushort charset = 1200;
private static string fileVersionName = String.Empty;
private static XmlDocument document = new XmlDocument();
private static XPathNavigator navigator = null;
const int OptionOut = 1;
const int OptionNoLogo = 2;
const int OptionSet = 3;
const int OptionRecurse = 4;
const int OptionTemplates = 5;
const int OptionLang = 6;
const int OptionCharset = 7;
const int OptionTrace = 8;
const int OptionName = 9;
const int OptionHelp = 10;
private static Argument[] argDefs =
{
new Argument(OptionHelp, "HELP|?", ArgType.Bool, "Displays this message."),
new Argument(OptionOut, "OUT", ArgType.String, "File to be output, including extension."),
new Argument(OptionNoLogo, "NOLOGO", ArgType.String, "Supress banner."),
new Argument(OptionSet, "SET", ArgType.String, "Absolute or delta value to use to modify version numbers. Can be +, - or absolute. Default is +0.0.0.1."),
new Argument(OptionTemplates, "TEMPLATES", ArgType.String, "Template file directory. Default is program directory."),
new Argument(OptionLang, "LANG", ArgType.Numeric, "Language to generate version resource for. Default U.S. English 0x0409 (1033)."),
new Argument(OptionCharset, "CHARSET", ArgType.Numeric, "Character set to generate version resource for. Default is Unicode 0x04B0 (1200)."),
new Argument(OptionTrace, "TRACE", ArgType.Bool, "Show intermediate version data after dynamic nodes are generated."),
new Argument(OptionName, "NAME", ArgType.String, "Project/file for which to generate version data for (if template requires it).")
};
private static void ShowHelp()
{
//TODO: Copyright information
string s = ParseArgs.FormatUsage(argDefs);
BpConsole.Write(s);
BpConsole.WriteLine(
/*09876543210987654321098765432109876543210987654321098765432109876543210987654321*/
"\n" +
"You can set the MKVER_TEMPLATES environment variable to the directory that\n" +
"contains the XSL template files, e.g. VERSION.H.XSL, VERSION.HTML.XSL,\n" +
"VERSION.CS.XSL, etc..\n");
}
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
ParseArgs parse = new ParseArgs(args, argDefs);
int id;
while (parse.GetNext(out id))
{
switch (id)
{
case OptionOut:
outputFiles.Add(parse.SwitchString);
break;
case OptionHelp:
ShowBanner();
ShowHelp();
return;
case OptionNoLogo:
noLogo = true;
break;
case OptionTrace:
trace = true;
break;
case OptionLang:
lang = (ushort)parse.SwitchInt;
break;
case OptionCharset:
charset = (ushort)parse.SwitchInt;
break;
case OptionName:
fileVersionName = parse.SwitchString;
break;
case OptionTemplates:
templateDir = parse.SwitchString;
break;
case OptionSet:
versionString = parse.SwitchString;
break;
default:
if (id < 0 || inputFile != null)
{
inputFile = parse.SwitchString;
break;
}
else // Invalid switch
{
ShowHelp();
return;
}
}
}
ShowBanner();
if (inputFile == null)
{
WriteMessage(MessageType.Error, "No version file specified");
return;
}
if (!FindTemplateDirectory())
return;
try
{
inputFile = Path.GetFullPath((string)inputFile);
// load the version data
document.Load(inputFile);
// wrap it with an XmlNavigator
navigator = document.CreateNavigator();
if (!IsCorrectRevision(1))
return;
UpdateProductVersion();
// Add dynamically generated nodes
AddNodes();
if (trace)
{
XmlTextWriter xw = new XmlTextWriter(BpConsole.Out);
xw.Formatting = Formatting.Indented;
document.WriteContentTo(xw);
xw.Flush();
BpConsole.WriteLine();
}
TransformDocument();
}
catch (Exception e)
{
WriteMessage(MessageType.Error, e.Message);
}
}
private static void TransformDocument()
{
foreach (string filePath in outputFiles)
{
string outputFilePath = Path.GetFullPath((string)filePath);
string outputFileExt = Path.GetExtension(outputFilePath);
// create the XslTransform object
XslTransform transform = new XslTransform();
// load it with the stylesheet
transform.Load(templateDir + "version" + outputFileExt + ".xsl");
// create the XmlWriter object
TextWriter output = new StreamWriter(outputFilePath, false);
// call transform - output streamed to console
XsltArgumentList argList = new XsltArgumentList();
argList.AddParam("lang", "", lang.ToString());
argList.AddParam("charset", "", charset.ToString());
argList.AddParam("name", "", fileVersionName);
transform.Transform(navigator, argList, output);
output.Close();
}
}
private static void UpdateProductVersion()
{
XPathNodeIterator iter = navigator.Select("//ProductVersion");
iter.MoveNext();
ulong verProduct = GetVersion(iter.Current.Value);
int sign;
ulong verSet = GetVersion(versionString, out sign);
if (sign == 0)
verProduct = verSet; // Absolute
else
verProduct = (ulong)((long)verProduct + ((long)verSet * sign)); // Relative
versionString = GetVersion(verProduct);
((IHasXmlNode)iter.Current).GetNode().FirstChild.Value = versionString;
if (sign != 0 && verSet == 0)
{
BpConsole.WriteLine("Leaving product version at {0}.", versionString);
}
else
{
BpConsole.WriteLine("{0} product version to {1}.",
sign == 0 ? "Setting" : sign < 0 ? "Decrementing" : "Incrementing",
versionString);
// Save the document now, before we mess with it
document.Save(inputFile);
}
}
private static bool IsCorrectRevision(int revExpected)
{
XPathNodeIterator iter1 = navigator.Select("/VersionData");
iter1.MoveNext();
string s = iter1.Current.GetAttribute("revision", navigator.NamespaceURI);
int revActual = Int32.Parse(s);
// Check that the version data revision is OK
if (revActual != revExpected)
{
WriteMessage(MessageType.Error,
"Wrong data file revision. Expected {0} and got {1}.", revActual, revExpected);
return false;
}
return true;
}
public static void AppendElement(XPathNodeIterator iter, string name, string val)
{
XmlNode node = ((IHasXmlNode)iter.Current).GetNode();
XmlElement newElem = document.CreateElement(name);
XmlText newText = document.CreateTextNode(val);
newElem.AppendChild(newText);
node.AppendChild(newElem);
}
public static bool AddNodes()
{
try
{
XPathNodeIterator iter = navigator.Select("/VersionData");
iter.MoveNext();
// Add a DateTime element
AppendElement(iter, "Date", DateTime.Now.ToLongDateString());
AppendElement(iter, "Time", DateTime.Now.ToLongTimeString());
iter = iter.Current.Select("ProductVersionDatum");
iter.MoveNext();
XPathNodeIterator iter2 = iter.Current.Select("ProductVersion");
iter2.MoveNext();
ulong version = GetVersion(iter2.Current.Value);
string major = GetVersionPart(version, VersionPart.Major);
string minor = GetVersionPart(version, VersionPart.Minor);
string point = GetVersionPart(version, VersionPart.Point);
string build = GetVersionPart(version, VersionPart.Build);
AppendElement(iter, "ProductVerMajor", major);
AppendElement(iter, "ProductVerMinor", minor);
AppendElement(iter, "ProductVerPoint", point);
AppendElement(iter, "ProductVerBuild", build);
AppendElement(iter, "ProductVersionHex", "0x" + Convert.ToString((long)version, 16));
AppendElement(iter, "ProductVersionCSV", major + "," + minor + "," + point + "," + build);
iter = iter.Current.Select("//FileVersionDatum");
while (iter.MoveNext())
{
iter2 = iter.Current.Select("FileVersion");
iter2.MoveNext();
version = GetVersion(iter2.Current.Value);
major = GetVersionPart(version, VersionPart.Major);
minor = GetVersionPart(version, VersionPart.Minor);
point = GetVersionPart(version, VersionPart.Point);
build = GetVersionPart(version, VersionPart.Build);
AppendElement(iter, "FileVerMajor", major);
AppendElement(iter, "FileVerMinor", minor);
AppendElement(iter, "FileVerPoint", point);
AppendElement(iter, "FileVerBuild", build);
AppendElement(iter, "FileVersionHex", "0x" + Convert.ToString((long)version, 16));
AppendElement(iter, "FileVersionCSV", major + "," + minor + "," + point + "," + build);
}
}
catch (Exception e)
{
WriteMessage(MessageType.Error, "Exception '{0}'", e.Message);
return false;
}
return true;
}
private static bool FindTemplateDirectory()
{
try
{
if (templateDir != null)
{
templateDir = Path.GetFullPath(templateDir);
if (Directory.Exists(templateDir))
{
templateDir = PathEx.AddDirectorySeparator(templateDir);
return true;
}
else
{
WriteMessage(MessageType.Warning,
"Unable to find directory '{0}' specified by command line", templateDir);
templateDir = null;
}
}
templateDir = Environment.GetEnvironmentVariable("MKVER_TEMPLATES");
if (templateDir != null)
{
templateDir = Path.GetFullPath(templateDir);
if (Directory.Exists(templateDir))
{
templateDir = PathEx.AddDirectorySeparator(templateDir);
return true;
}
else
{
WriteMessage(MessageType.Warning,
"Unable to find directory '{0}' specified by MKVER_TEMPLATES", templateDir);
templateDir = null;
}
}
templateDir = Environment.CurrentDirectory + @"\Templates\";
if (Directory.Exists(templateDir))
return true;
templateDir = Path.GetPathRoot(Process.GetCurrentProcess().MainModule.FileName) + @"Templates\";
if (Directory.Exists(templateDir))
return true;
}
catch (Exception e)
{
WriteMessage(MessageType.Error, e.Message);
return false;
}
WriteMessage(MessageType.Error, "Unable to find a template directory.");
return false;
}
private static void ShowBanner()
{
if (noLogo)
return;
FileVersionInfo ver = FileVersionInfo.GetVersionInfo(Process.GetCurrentProcess().MainModule.FileName);
BpConsole.Write(ver.FileDescription);
BpConsole.Write(". ");
BpConsole.Write(ver.IsDebug ? "Debug" : "Release");
BpConsole.Write(" Version ");
BpConsole.WriteLine(ver.FileVersion);
BpConsole.WriteLine(ver.LegalCopyright);
BpConsole.WriteLine();
}
public static void WriteMessage(MessageType s, string format, params object [] args)
{
switch (s)
{
default:
case MessageType.Normal:
break;
case MessageType.Warning:
BpConsole.Write("WARNING: ");
break;
case MessageType.Error:
BpConsole.Write("ERROR: ");
break;
}
BpConsole.Error.WriteLine(format, args);
}
public static string GetVersionPart(ulong version, VersionPart part)
{
return GetVersionPart(version, part, 10);
}
public static string GetVersionPart(ulong version, VersionPart part, int toBase)
{
string s = null;
switch (part)
{
case VersionPart.Major:
s = Convert.ToString((long)(version >> 48), toBase);
break;
case VersionPart.Minor:
s = Convert.ToString((long)((version >> 32) & 0xFFFF), toBase);
break;
case VersionPart.Point:
s = Convert.ToString((long)((version >> 16) & 0xFFFF), toBase);
break;
case VersionPart.Build:
s = Convert.ToString((long)(version & 0xFFFF), toBase);
break;
}
return s;
}
public static string GetVersion(ulong version)
{
return
GetVersionPart(version, VersionPart.Major) + "." +
GetVersionPart(version, VersionPart.Minor) + "." +
GetVersionPart(version, VersionPart.Point) + "." +
GetVersionPart(version, VersionPart.Build);
}
public static ulong GetVersion(string version)
{
int sign;
return GetVersion(version, out sign);
}
public static ulong GetVersion(string version, out int sign)
{
int i = 0;
sign = 0;
if (version == null || version == String.Empty)
return 0;
if (version[i] == '+')
{
sign = 1;
i++;
}
else if (version[i] == '-')
{
sign = -1;
i++;
}
if (!Char.IsDigit(version[i]))
return 0;
ulong versionNum = 0;
ulong t = 0;
int j = 3;
while (true)
{
int length = 0;
while (i + length < version.Length && Char.IsDigit(version[i + length]))
length++;
t = (ulong)Convert.ToUInt64(version.Substring(i, length), 10);
versionNum |= (t << 16 * j);
j--;
i += length;
if (j < 0 || i >= version.Length || version[i] != '.')
break;
i++;
}
return versionNum;
}
} // MainClass
}