List:Commits« Previous MessageNext Message »
From:Reggie Burnett Date:February 11 2011 12:26am
Subject:bzr commit into wex-installer-1.0 branch (reggie.burnett:331)
View as plain text  
#At file:///C:/Users/Reggie/work/wex/installer-updated/ based on revid:reggie.burnett@strippedkjufy2z9v20gk4

  331 Reggie Burnett	2011-02-10
      checked in first version of manifestupdater

    added:
      ManifestUpdater/
      ManifestUpdater/ManifestUpdater.csproj
      ManifestUpdater/Options.cs
      ManifestUpdater/Package.cs
      ManifestUpdater/Product.cs
      ManifestUpdater/ProductCatalog.cs
      ManifestUpdater/ProductCategory.cs
      ManifestUpdater/ProductFeature.cs
      ManifestUpdater/ProductManifest.cs
      ManifestUpdater/Program.cs
      ManifestUpdater/Properties/
      ManifestUpdater/Properties/AssemblyInfo.cs
      ManifestUpdater/app.config
      ManifestUpdater/wix.dll
    modified:
      installer-vs2010.sln
=== added directory 'ManifestUpdater'
=== added file 'ManifestUpdater/ManifestUpdater.csproj'
=== added directory 'ManifestUpdater'
=== added file 'ManifestUpdater/ManifestUpdater.csproj'
--- a/ManifestUpdater/ManifestUpdater.csproj	1970-01-01 00:00:00 +0000
+++ b/ManifestUpdater/ManifestUpdater.csproj	2011-02-11 00:26:57 +0000
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{57913AF9-D535-4B13-A088-8DF74FD47A53}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>ManifestUpdater</RootNamespace>
+    <AssemblyName>ManifestUpdater</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <TargetFrameworkProfile />

+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <PlatformTarget>x86</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <PlatformTarget>x86</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="wix">
+      <HintPath>.\wix.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Options.cs" />
+    <Compile Include="Package.cs" />
+    <Compile Include="Product.cs" />
+    <Compile Include="ProductCatalog.cs" />
+    <Compile Include="ProductCategory.cs" />
+    <Compile Include="ProductFeature.cs" />
+    <Compile Include="ProductManifest.cs" />
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="app.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <PropertyGroup>
+    <PostBuildEvent>copy $(TargetPath) $(SolutionDir)\Setup\$(TargetFileName)</PostBuildEvent>
+  </PropertyGroup>
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file

=== added file 'ManifestUpdater/Options.cs'
--- a/ManifestUpdater/Options.cs	1970-01-01 00:00:00 +0000
+++ b/ManifestUpdater/Options.cs	2011-02-11 00:26:57 +0000
@@ -0,0 +1,1112 @@
+//
+// Options.cs
+//
+// Authors:
+//  Jonathan Pryor <jpryor@novell.com>
+//
+// Copyright (C) 2008 Novell (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+// Compile With:
+//   gmcs -debug+ -r:System.Core Options.cs -o:NDesk.Options.dll
+//   gmcs -debug+ -d:LINQ -r:System.Core Options.cs -o:NDesk.Options.dll
+//
+// The LINQ version just changes the implementation of
+// OptionSet.Parse(IEnumerable<string>), and confers no semantic changes.
+
+//
+// A Getopt::Long-inspired option parsing library for C#.
+//
+// NDesk.Options.OptionSet is built upon a key/value table, where the
+// key is a option format string and the value is a delegate that is 
+// invoked when the format string is matched.
+//
+// Option format strings:
+//  Regex-like BNF Grammar: 
+//    name: .+
+//    type: [=:]
+//    sep: ( [^{}]+ | '{' .+ '}' )?
+//    aliases: ( name type sep ) ( '|' name type sep )*
+// 
+// Each '|'-delimited name is an alias for the associated action.  If the
+// format string ends in a '=', it has a required value.  If the format
+// string ends in a ':', it has an optional value.  If neither '=' or ':'
+// is present, no value is supported.  `=' or `:' need only be defined on one
+// alias, but if they are provided on more than one they must be consistent.
+//
+// Each alias portion may also end with a "key/value separator", which is used
+// to split option values if the option accepts > 1 value.  If not specified,
+// it defaults to '=' and ':'.  If specified, it can be any character except
+// '{' and '}' OR the *string* between '{' and '}'.  If no separator should be
+// used (i.e. the separate values should be distinct arguments), then "{}"
+// should be used as the separator.
+//
+// Options are extracted either from the current option by looking for
+// the option name followed by an '=' or ':', or is taken from the
+// following option IFF:
+//  - The current option does not contain a '=' or a ':'
+//  - The current option requires a value (i.e. not a Option type of ':')
+//
+// The `name' used in the option format string does NOT include any leading
+// option indicator, such as '-', '--', or '/'.  All three of these are
+// permitted/required on any named option.
+//
+// Option bundling is permitted so long as:
+//   - '-' is used to start the option group
+//   - all of the bundled options are a single character
+//   - at most one of the bundled options accepts a value, and the value
+//     provided starts from the next character to the end of the string.
+//
+// This allows specifying '-a -b -c' as '-abc', and specifying '-D name=value'
+// as '-Dname=value'.
+//
+// Option processing is disabled by specifying "--".  All options after "--"
+// are returned by OptionSet.Parse() unchanged and unprocessed.
+//
+// Unprocessed options are returned from OptionSet.Parse().
+//
+// Examples:
+//  int verbose = 0;
+//  OptionSet p = new OptionSet ()
+//    .Add ("v", v => ++verbose)
+//    .Add ("name=|value=", v => Console.WriteLine (v));
+//  p.Parse (new string[]{"-v", "--v", "/v", "-name=A", "/name", "B", "extra"});
+//
+// The above would parse the argument string array, and would invoke the
+// lambda expression three times, setting `verbose' to 3 when complete.  
+// It would also print out "A" and "B" to standard output.
+// The returned array would contain the string "extra".
+//
+// C# 3.0 collection initializers are supported and encouraged:
+//  var p = new OptionSet () {
+//    { "h|?|help", v => ShowHelp () },
+//  };
+//
+// System.ComponentModel.TypeConverter is also supported, allowing the use of
+// custom data types in the callback type; TypeConverter.ConvertFromString()
+// is used to convert the value option to an instance of the specified
+// type:
+//
+//  var p = new OptionSet () {
+//    { "foo=", (Foo f) => Console.WriteLine (f.ToString ()) },
+//  };
+//
+// Random other tidbits:
+//  - Boolean options (those w/o '=' or ':' in the option format string)
+//    are explicitly enabled if they are followed with '+', and explicitly
+//    disabled if they are followed with '-':
+//      string a = null;
+//      var p = new OptionSet () {
+//        { "a", s => a = s },
+//      };
+//      p.Parse (new string[]{"-a"});   // sets v != null
+//      p.Parse (new string[]{"-a+"});  // sets v != null
+//      p.Parse (new string[]{"-a-"});  // sets v == null
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Globalization;
+using System.IO;
+using System.Runtime.Serialization;
+using System.Security.Permissions;
+using System.Text;
+using System.Text.RegularExpressions;
+
+#if LINQ
+using System.Linq;
+#endif
+
+#if TEST
+using NDesk.Options;
+#endif
+
+#if NDESK_OPTIONS
+namespace NDesk.Options
+#else
+namespace Mono.Options
+#endif
+{
+	public class OptionValueCollection : IList, IList<string> {
+
+		List<string> values = new List<string> ();
+		OptionContext c;
+
+		internal OptionValueCollection (OptionContext c)
+		{
+			this.c = c;
+		}
+
+		#region ICollection
+		void ICollection.CopyTo (Array array, int index)  {(values as ICollection).CopyTo (array, index);}
+		bool ICollection.IsSynchronized                   {get {return (values as ICollection).IsSynchronized;}}
+		object ICollection.SyncRoot                       {get {return (values as ICollection).SyncRoot;}}
+		#endregion
+
+		#region ICollection<T>
+		public void Add (string item)                       {values.Add (item);}
+		public void Clear ()                                {values.Clear ();}
+		public bool Contains (string item)                  {return values.Contains (item);}
+		public void CopyTo (string[] array, int arrayIndex) {values.CopyTo (array, arrayIndex);}
+		public bool Remove (string item)                    {return values.Remove (item);}
+		public int Count                                    {get {return values.Count;}}
+		public bool IsReadOnly                              {get {return false;}}
+		#endregion
+
+		#region IEnumerable
+		IEnumerator IEnumerable.GetEnumerator () {return values.GetEnumerator ();}
+		#endregion
+
+		#region IEnumerable<T>
+		public IEnumerator<string> GetEnumerator () {return values.GetEnumerator ();}
+		#endregion
+
+		#region IList
+		int IList.Add (object value)                {return (values as IList).Add (value);}
+		bool IList.Contains (object value)          {return (values as IList).Contains (value);}
+		int IList.IndexOf (object value)            {return (values as IList).IndexOf (value);}
+		void IList.Insert (int index, object value) {(values as IList).Insert (index, value);}
+		void IList.Remove (object value)            {(values as IList).Remove (value);}
+		void IList.RemoveAt (int index)             {(values as IList).RemoveAt (index);}
+		bool IList.IsFixedSize                      {get {return false;}}
+		object IList.this [int index]               {get {return this [index];} set {(values as IList)[index] = value;}}
+		#endregion
+
+		#region IList<T>
+		public int IndexOf (string item)            {return values.IndexOf (item);}
+		public void Insert (int index, string item) {values.Insert (index, item);}
+		public void RemoveAt (int index)            {values.RemoveAt (index);}
+
+		private void AssertValid (int index)
+		{
+			if (c.Option == null)
+				throw new InvalidOperationException ("OptionContext.Option is null.");
+			if (index >= c.Option.MaxValueCount)
+				throw new ArgumentOutOfRangeException ("index");
+			if (c.Option.OptionValueType == OptionValueType.Required &&
+					index >= values.Count)
+				throw new OptionException (string.Format (
+							c.OptionSet.MessageLocalizer ("Missing required value for option '{0}'."), c.OptionName), 
+						c.OptionName);
+		}
+
+		public string this [int index] {
+			get {
+				AssertValid (index);
+				return index >= values.Count ? null : values [index];
+			}
+			set {
+				values [index] = value;
+			}
+		}
+		#endregion
+
+		public List<string> ToList ()
+		{
+			return new List<string> (values);
+		}
+
+		public string[] ToArray ()
+		{
+			return values.ToArray ();
+		}
+
+		public override string ToString ()
+		{
+			return string.Join (", ", values.ToArray ());
+		}
+	}
+
+	public class OptionContext {
+		private Option                option;
+		private string                name;
+		private int                   index;
+		private OptionSet             set;
+		private OptionValueCollection c;
+
+		public OptionContext (OptionSet set)
+		{
+			this.set = set;
+			this.c   = new OptionValueCollection (this);
+		}
+
+		public Option Option {
+			get {return option;}
+			set {option = value;}
+		}
+
+		public string OptionName { 
+			get {return name;}
+			set {name = value;}
+		}
+
+		public int OptionIndex {
+			get {return index;}
+			set {index = value;}
+		}
+
+		public OptionSet OptionSet {
+			get {return set;}
+		}
+
+		public OptionValueCollection OptionValues {
+			get {return c;}
+		}
+	}
+
+	public enum OptionValueType {
+		None, 
+		Optional,
+		Required,
+	}
+
+	public abstract class Option {
+		string prototype, description;
+		string[] names;
+		OptionValueType type;
+		int count;
+		string[] separators;
+
+		protected Option (string prototype, string description)
+			: this (prototype, description, 1)
+		{
+		}
+
+		protected Option (string prototype, string description, int maxValueCount)
+		{
+			if (prototype == null)
+				throw new ArgumentNullException ("prototype");
+			if (prototype.Length == 0)
+				throw new ArgumentException ("Cannot be the empty string.", "prototype");
+			if (maxValueCount < 0)
+				throw new ArgumentOutOfRangeException ("maxValueCount");
+
+			this.prototype   = prototype;
+			this.names       = prototype.Split ('|');
+			this.description = description;
+			this.count       = maxValueCount;
+			this.type        = ParsePrototype ();
+
+			if (this.count == 0 && type != OptionValueType.None)
+				throw new ArgumentException (
+						"Cannot provide maxValueCount of 0 for OptionValueType.Required or " +
+							"OptionValueType.Optional.",
+						"maxValueCount");
+			if (this.type == OptionValueType.None && maxValueCount > 1)
+				throw new ArgumentException (
+						string.Format ("Cannot provide maxValueCount of {0} for OptionValueType.None.", maxValueCount),
+						"maxValueCount");
+			if (Array.IndexOf (names, "<>") >= 0 && 
+					((names.Length == 1 && this.type != OptionValueType.None) ||
+					 (names.Length > 1 && this.MaxValueCount > 1)))
+				throw new ArgumentException (
+						"The default option handler '<>' cannot require values.",
+						"prototype");
+		}
+
+		public string           Prototype       {get {return prototype;}}
+		public string           Description     {get {return description;}}
+		public OptionValueType  OptionValueType {get {return type;}}
+		public int              MaxValueCount   {get {return count;}}
+
+		public string[] GetNames ()
+		{
+			return (string[]) names.Clone ();
+		}
+
+		public string[] GetValueSeparators ()
+		{
+			if (separators == null)
+				return new string [0];
+			return (string[]) separators.Clone ();
+		}
+
+		protected static T Parse<T> (string value, OptionContext c)
+		{
+			Type tt = typeof (T);
+			bool nullable = tt.IsValueType && tt.IsGenericType && 
+				!tt.IsGenericTypeDefinition && 
+				tt.GetGenericTypeDefinition () == typeof (Nullable<>);
+			Type targetType = nullable ? tt.GetGenericArguments () [0] : typeof (T);
+			TypeConverter conv = TypeDescriptor.GetConverter (targetType);
+			T t = default (T);
+			try {
+				if (value != null)
+					t = (T) conv.ConvertFromString (value);
+			}
+			catch (Exception e) {
+				throw new OptionException (
+						string.Format (
+							c.OptionSet.MessageLocalizer ("Could not convert string `{0}' to type {1} for option `{2}'."),
+							value, targetType.Name, c.OptionName),
+						c.OptionName, e);
+			}
+			return t;
+		}
+
+		internal string[] Names           {get {return names;}}
+		internal string[] ValueSeparators {get {return separators;}}
+
+		static readonly char[] NameTerminator = new char[]{'=', ':'};
+
+		private OptionValueType ParsePrototype ()
+		{
+			char type = '\0';
+			List<string> seps = new List<string> ();
+			for (int i = 0; i < names.Length; ++i) {
+				string name = names [i];
+				if (name.Length == 0)
+					throw new ArgumentException ("Empty option names are not supported.", "prototype");
+
+				int end = name.IndexOfAny (NameTerminator);
+				if (end == -1)
+					continue;
+				names [i] = name.Substring (0, end);
+				if (type == '\0' || type == name [end])
+					type = name [end];
+				else 
+					throw new ArgumentException (
+							string.Format ("Conflicting option types: '{0}' vs. '{1}'.", type, name [end]),
+							"prototype");
+				AddSeparators (name, end, seps);
+			}
+
+			if (type == '\0')
+				return OptionValueType.None;
+
+			if (count <= 1 && seps.Count != 0)
+				throw new ArgumentException (
+						string.Format ("Cannot provide key/value separators for Options taking {0} value(s).", count),
+						"prototype");
+			if (count > 1) {
+				if (seps.Count == 0)
+					this.separators = new string[]{":", "="};
+				else if (seps.Count == 1 && seps [0].Length == 0)
+					this.separators = null;
+				else
+					this.separators = seps.ToArray ();
+			}
+
+			return type == '=' ? OptionValueType.Required : OptionValueType.Optional;
+		}
+
+		private static void AddSeparators (string name, int end, ICollection<string> seps)
+		{
+			int start = -1;
+			for (int i = end+1; i < name.Length; ++i) {
+				switch (name [i]) {
+					case '{':
+						if (start != -1)
+							throw new ArgumentException (
+									string.Format ("Ill-formed name/value separator found in \"{0}\".", name),
+									"prototype");
+						start = i+1;
+						break;
+					case '}':
+						if (start == -1)
+							throw new ArgumentException (
+									string.Format ("Ill-formed name/value separator found in \"{0}\".", name),
+									"prototype");
+						seps.Add (name.Substring (start, i-start));
+						start = -1;
+						break;
+					default:
+						if (start == -1)
+							seps.Add (name [i].ToString ());
+						break;
+				}
+			}
+			if (start != -1)
+				throw new ArgumentException (
+						string.Format ("Ill-formed name/value separator found in \"{0}\".", name),
+						"prototype");
+		}
+
+		public void Invoke (OptionContext c)
+		{
+			OnParseComplete (c);
+			c.OptionName  = null;
+			c.Option      = null;
+			c.OptionValues.Clear ();
+		}
+
+		protected abstract void OnParseComplete (OptionContext c);
+
+		public override string ToString ()
+		{
+			return Prototype;
+		}
+	}
+
+	[Serializable]
+	public class OptionException : Exception {
+		private string option;
+
+		public OptionException ()
+		{
+		}
+
+		public OptionException (string message, string optionName)
+			: base (message)
+		{
+			this.option = optionName;
+		}
+
+		public OptionException (string message, string optionName, Exception innerException)
+			: base (message, innerException)
+		{
+			this.option = optionName;
+		}
+
+		protected OptionException (SerializationInfo info, StreamingContext context)
+			: base (info, context)
+		{
+			this.option = info.GetString ("OptionName");
+		}
+
+		public string OptionName {
+			get {return this.option;}
+		}
+
+		[SecurityPermission (SecurityAction.LinkDemand, SerializationFormatter = true)]
+		public override void GetObjectData (SerializationInfo info, StreamingContext context)
+		{
+			base.GetObjectData (info, context);
+			info.AddValue ("OptionName", option);
+		}
+	}
+
+	public delegate void OptionAction<TKey, TValue> (TKey key, TValue value);
+
+	public class OptionSet : KeyedCollection<string, Option>
+	{
+		public OptionSet ()
+			: this (delegate (string f) {return f;})
+		{
+		}
+
+		public OptionSet (Converter<string, string> localizer)
+		{
+			this.localizer = localizer;
+		}
+
+		Converter<string, string> localizer;
+
+		public Converter<string, string> MessageLocalizer {
+			get {return localizer;}
+		}
+
+		protected override string GetKeyForItem (Option item)
+		{
+			if (item == null)
+				throw new ArgumentNullException ("option");
+			if (item.Names != null && item.Names.Length > 0)
+				return item.Names [0];
+			// This should never happen, as it's invalid for Option to be
+			// constructed w/o any names.
+			throw new InvalidOperationException ("Option has no names!");
+		}
+
+		[Obsolete ("Use KeyedCollection.this[string]")]
+		protected Option GetOptionForName (string option)
+		{
+			if (option == null)
+				throw new ArgumentNullException ("option");
+			try {
+				return base [option];
+			}
+			catch (KeyNotFoundException) {
+				return null;
+			}
+		}
+
+		protected override void InsertItem (int index, Option item)
+		{
+			base.InsertItem (index, item);
+			AddImpl (item);
+		}
+
+		protected override void RemoveItem (int index)
+		{
+			base.RemoveItem (index);
+			Option p = Items [index];
+			// KeyedCollection.RemoveItem() handles the 0th item
+			for (int i = 1; i < p.Names.Length; ++i) {
+				Dictionary.Remove (p.Names [i]);
+			}
+		}
+
+		protected override void SetItem (int index, Option item)
+		{
+			base.SetItem (index, item);
+			RemoveItem (index);
+			AddImpl (item);
+		}
+
+		private void AddImpl (Option option)
+		{
+			if (option == null)
+				throw new ArgumentNullException ("option");
+			List<string> added = new List<string> (option.Names.Length);
+			try {
+				// KeyedCollection.InsertItem/SetItem handle the 0th name.
+				for (int i = 1; i < option.Names.Length; ++i) {
+					Dictionary.Add (option.Names [i], option);
+					added.Add (option.Names [i]);
+				}
+			}
+			catch (Exception) {
+				foreach (string name in added)
+					Dictionary.Remove (name);
+				throw;
+			}
+		}
+
+		public new OptionSet Add (Option option)
+		{
+			base.Add (option);
+			return this;
+		}
+
+		sealed class ActionOption : Option {
+			Action<OptionValueCollection> action;
+
+			public ActionOption (string prototype, string description, int count, Action<OptionValueCollection> action)
+				: base (prototype, description, count)
+			{
+				if (action == null)
+					throw new ArgumentNullException ("action");
+				this.action = action;
+			}
+
+			protected override void OnParseComplete (OptionContext c)
+			{
+				action (c.OptionValues);
+			}
+		}
+
+		public OptionSet Add (string prototype, Action<string> action)
+		{
+			return Add (prototype, null, action);
+		}
+
+		public OptionSet Add (string prototype, string description, Action<string> action)
+		{
+			if (action == null)
+				throw new ArgumentNullException ("action");
+			Option p = new ActionOption (prototype, description, 1, 
+					delegate (OptionValueCollection v) { action (v [0]); });
+			base.Add (p);
+			return this;
+		}
+
+		public OptionSet Add (string prototype, OptionAction<string, string> action)
+		{
+			return Add (prototype, null, action);
+		}
+
+		public OptionSet Add (string prototype, string description, OptionAction<string, string> action)
+		{
+			if (action == null)
+				throw new ArgumentNullException ("action");
+			Option p = new ActionOption (prototype, description, 2, 
+					delegate (OptionValueCollection v) {action (v [0], v [1]);});
+			base.Add (p);
+			return this;
+		}
+
+		sealed class ActionOption<T> : Option {
+			Action<T> action;
+
+			public ActionOption (string prototype, string description, Action<T> action)
+				: base (prototype, description, 1)
+			{
+				if (action == null)
+					throw new ArgumentNullException ("action");
+				this.action = action;
+			}
+
+			protected override void OnParseComplete (OptionContext c)
+			{
+				action (Parse<T> (c.OptionValues [0], c));
+			}
+		}
+
+		sealed class ActionOption<TKey, TValue> : Option {
+			OptionAction<TKey, TValue> action;
+
+			public ActionOption (string prototype, string description, OptionAction<TKey, TValue> action)
+				: base (prototype, description, 2)
+			{
+				if (action == null)
+					throw new ArgumentNullException ("action");
+				this.action = action;
+			}
+
+			protected override void OnParseComplete (OptionContext c)
+			{
+				action (
+						Parse<TKey> (c.OptionValues [0], c),
+						Parse<TValue> (c.OptionValues [1], c));
+			}
+		}
+
+		public OptionSet Add<T> (string prototype, Action<T> action)
+		{
+			return Add (prototype, null, action);
+		}
+
+		public OptionSet Add<T> (string prototype, string description, Action<T> action)
+		{
+			return Add (new ActionOption<T> (prototype, description, action));
+		}
+
+		public OptionSet Add<TKey, TValue> (string prototype, OptionAction<TKey, TValue> action)
+		{
+			return Add (prototype, null, action);
+		}
+
+		public OptionSet Add<TKey, TValue> (string prototype, string description, OptionAction<TKey, TValue> action)
+		{
+			return Add (new ActionOption<TKey, TValue> (prototype, description, action));
+		}
+
+		protected virtual OptionContext CreateOptionContext ()
+		{
+			return new OptionContext (this);
+		}
+
+#if LINQ
+		public List<string> Parse (IEnumerable<string> arguments)
+		{
+			bool process = true;
+			OptionContext c = CreateOptionContext ();
+			c.OptionIndex = -1;
+			var def = GetOptionForName ("<>");
+			var unprocessed = 
+				from argument in arguments
+				where ++c.OptionIndex >= 0 && (process || def != null)
+					? process
+						? argument == "--" 
+							? (process = false)
+							: !Parse (argument, c)
+								? def != null 
+									? Unprocessed (null, def, c, argument) 
+									: true
+								: false
+						: def != null 
+							? Unprocessed (null, def, c, argument)
+							: true
+					: true
+				select argument;
+			List<string> r = unprocessed.ToList ();
+			if (c.Option != null)
+				c.Option.Invoke (c);
+			return r;
+		}
+#else
+		public List<string> Parse (IEnumerable<string> arguments)
+		{
+			OptionContext c = CreateOptionContext ();
+			c.OptionIndex = -1;
+			bool process = true;
+			List<string> unprocessed = new List<string> ();
+			Option def = Contains ("<>") ? this ["<>"] : null;
+			foreach (string argument in arguments) {
+				++c.OptionIndex;
+				if (argument == "--") {
+					process = false;
+					continue;
+				}
+				if (!process) {
+					Unprocessed (unprocessed, def, c, argument);
+					continue;
+				}
+				if (!Parse (argument, c))
+					Unprocessed (unprocessed, def, c, argument);
+			}
+			if (c.Option != null)
+				c.Option.Invoke (c);
+			return unprocessed;
+		}
+#endif
+
+		private static bool Unprocessed (ICollection<string> extra, Option def, OptionContext c, string argument)
+		{
+			if (def == null) {
+				extra.Add (argument);
+				return false;
+			}
+			c.OptionValues.Add (argument);
+			c.Option = def;
+			c.Option.Invoke (c);
+			return false;
+		}
+
+		private readonly Regex ValueOption = new Regex (
+			@"^(?<flag>--|-|/)(?<name>[^:=]+)((?<sep>[:=])(?<value>.*))?$");
+
+		protected bool GetOptionParts (string argument, out string flag, out string name, out string sep, out string value)
+		{
+			if (argument == null)
+				throw new ArgumentNullException ("argument");
+
+			flag = name = sep = value = null;
+			Match m = ValueOption.Match (argument);
+			if (!m.Success) {
+				return false;
+			}
+			flag  = m.Groups ["flag"].Value;
+			name  = m.Groups ["name"].Value;
+			if (m.Groups ["sep"].Success && m.Groups ["value"].Success) {
+				sep   = m.Groups ["sep"].Value;
+				value = m.Groups ["value"].Value;
+			}
+			return true;
+		}
+
+		protected virtual bool Parse (string argument, OptionContext c)
+		{
+			if (c.Option != null) {
+				ParseValue (argument, c);
+				return true;
+			}
+
+			string f, n, s, v;
+			if (!GetOptionParts (argument, out f, out n, out s, out v))
+				return false;
+
+			Option p;
+			if (Contains (n)) {
+				p = this [n];
+				c.OptionName = f + n;
+				c.Option     = p;
+				switch (p.OptionValueType) {
+					case OptionValueType.None:
+						c.OptionValues.Add (n);
+						c.Option.Invoke (c);
+						break;
+					case OptionValueType.Optional:
+					case OptionValueType.Required: 
+						ParseValue (v, c);
+						break;
+				}
+				return true;
+			}
+			// no match; is it a bool option?
+			if (ParseBool (argument, n, c))
+				return true;
+			// is it a bundled option?
+			if (ParseBundledValue (f, string.Concat (n + s + v), c))
+				return true;
+
+			return false;
+		}
+
+		private void ParseValue (string option, OptionContext c)
+		{
+			if (option != null)
+				foreach (string o in c.Option.ValueSeparators != null 
+						? option.Split (c.Option.ValueSeparators, StringSplitOptions.None)
+						: new string[]{option}) {
+					c.OptionValues.Add (o);
+				}
+			if (c.OptionValues.Count == c.Option.MaxValueCount || 
+					c.Option.OptionValueType == OptionValueType.Optional)
+				c.Option.Invoke (c);
+			else if (c.OptionValues.Count > c.Option.MaxValueCount) {
+				throw new OptionException (localizer (string.Format (
+								"Error: Found {0} option values when expecting {1}.", 
+								c.OptionValues.Count, c.Option.MaxValueCount)),
+						c.OptionName);
+			}
+		}
+
+		private bool ParseBool (string option, string n, OptionContext c)
+		{
+			Option p;
+			string rn;
+			if (n.Length >= 1 && (n [n.Length-1] == '+' || n [n.Length-1] == '-') &&
+					Contains ((rn = n.Substring (0, n.Length-1)))) {
+				p = this [rn];
+				string v = n [n.Length-1] == '+' ? option : null;
+				c.OptionName  = option;
+				c.Option      = p;
+				c.OptionValues.Add (v);
+				p.Invoke (c);
+				return true;
+			}
+			return false;
+		}
+
+		private bool ParseBundledValue (string f, string n, OptionContext c)
+		{
+			if (f != "-")
+				return false;
+			for (int i = 0; i < n.Length; ++i) {
+				Option p;
+				string opt = f + n [i].ToString ();
+				string rn = n [i].ToString ();
+				if (!Contains (rn)) {
+					if (i == 0)
+						return false;
+					throw new OptionException (string.Format (localizer (
+									"Cannot bundle unregistered option '{0}'."), opt), opt);
+				}
+				p = this [rn];
+				switch (p.OptionValueType) {
+					case OptionValueType.None:
+						Invoke (c, opt, n, p);
+						break;
+					case OptionValueType.Optional:
+					case OptionValueType.Required: {
+						string v     = n.Substring (i+1);
+						c.Option     = p;
+						c.OptionName = opt;
+						ParseValue (v.Length != 0 ? v : null, c);
+						return true;
+					}
+					default:
+						throw new InvalidOperationException ("Unknown OptionValueType: " + p.OptionValueType);
+				}
+			}
+			return true;
+		}
+
+		private static void Invoke (OptionContext c, string name, string value, Option option)
+		{
+			c.OptionName  = name;
+			c.Option      = option;
+			c.OptionValues.Add (value);
+			option.Invoke (c);
+		}
+
+		private const int OptionWidth = 29;
+
+		public void WriteOptionDescriptions (TextWriter o)
+		{
+			foreach (Option p in this) {
+				int written = 0;
+				if (!WriteOptionPrototype (o, p, ref written))
+					continue;
+
+				if (written < OptionWidth)
+					o.Write (new string (' ', OptionWidth - written));
+				else {
+					o.WriteLine ();
+					o.Write (new string (' ', OptionWidth));
+				}
+
+				List<string> lines = GetLines (localizer (GetDescription (p.Description)));
+				o.WriteLine (lines [0]);
+				string prefix = new string (' ', OptionWidth+2);
+				for (int i = 1; i < lines.Count; ++i) {
+					o.Write (prefix);
+					o.WriteLine (lines [i]);
+				}
+			}
+		}
+
+		bool WriteOptionPrototype (TextWriter o, Option p, ref int written)
+		{
+			string[] names = p.Names;
+
+			int i = GetNextOptionIndex (names, 0);
+			if (i == names.Length)
+				return false;
+
+			if (names [i].Length == 1) {
+				Write (o, ref written, "  -");
+				Write (o, ref written, names [0]);
+			}
+			else {
+				Write (o, ref written, "      --");
+				Write (o, ref written, names [0]);
+			}
+
+			for ( i = GetNextOptionIndex (names, i+1); 
+					i < names.Length; i = GetNextOptionIndex (names, i+1)) {
+				Write (o, ref written, ", ");
+				Write (o, ref written, names [i].Length == 1 ? "-" : "--");
+				Write (o, ref written, names [i]);
+			}
+
+			if (p.OptionValueType == OptionValueType.Optional ||
+					p.OptionValueType == OptionValueType.Required) {
+				if (p.OptionValueType == OptionValueType.Optional) {
+					Write (o, ref written, localizer ("["));
+				}
+				Write (o, ref written, localizer ("=" + GetArgumentName (0, p.MaxValueCount, p.Description)));
+				string sep = p.ValueSeparators != null && p.ValueSeparators.Length > 0 
+					? p.ValueSeparators [0]
+					: " ";
+				for (int c = 1; c < p.MaxValueCount; ++c) {
+					Write (o, ref written, localizer (sep + GetArgumentName (c, p.MaxValueCount, p.Description)));
+				}
+				if (p.OptionValueType == OptionValueType.Optional) {
+					Write (o, ref written, localizer ("]"));
+				}
+			}
+			return true;
+		}
+
+		static int GetNextOptionIndex (string[] names, int i)
+		{
+			while (i < names.Length && names [i] == "<>") {
+				++i;
+			}
+			return i;
+		}
+
+		static void Write (TextWriter o, ref int n, string s)
+		{
+			n += s.Length;
+			o.Write (s);
+		}
+
+		private static string GetArgumentName (int index, int maxIndex, string description)
+		{
+			if (description == null)
+				return maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1);
+			string[] nameStart;
+			if (maxIndex == 1)
+				nameStart = new string[]{"{0:", "{"};
+			else
+				nameStart = new string[]{"{" + index + ":"};
+			for (int i = 0; i < nameStart.Length; ++i) {
+				int start, j = 0;
+				do {
+					start = description.IndexOf (nameStart [i], j);
+				} while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false);
+				if (start == -1)
+					continue;
+				int end = description.IndexOf ("}", start);
+				if (end == -1)
+					continue;
+				return description.Substring (start + nameStart [i].Length, end - start - nameStart [i].Length);
+			}
+			return maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1);
+		}
+
+		private static string GetDescription (string description)
+		{
+			if (description == null)
+				return string.Empty;
+			StringBuilder sb = new StringBuilder (description.Length);
+			int start = -1;
+			for (int i = 0; i < description.Length; ++i) {
+				switch (description [i]) {
+					case '{':
+						if (i == start) {
+							sb.Append ('{');
+							start = -1;
+						}
+						else if (start < 0)
+							start = i + 1;
+						break;
+					case '}':
+						if (start < 0) {
+							if ((i+1) == description.Length || description [i+1] != '}')
+								throw new InvalidOperationException ("Invalid option description: " + description);
+							++i;
+							sb.Append ("}");
+						}
+						else {
+							sb.Append (description.Substring (start, i - start));
+							start = -1;
+						}
+						break;
+					case ':':
+						if (start < 0)
+							goto default;
+						start = i + 1;
+						break;
+					default:
+						if (start < 0)
+							sb.Append (description [i]);
+						break;
+				}
+			}
+			return sb.ToString ();
+		}
+
+		private static List<string> GetLines (string description)
+		{
+			List<string> lines = new List<string> ();
+			if (string.IsNullOrEmpty (description)) {
+				lines.Add (string.Empty);
+				return lines;
+			}
+			int length = 80 - OptionWidth - 2;
+			int start = 0, end;
+			do {
+				end = GetLineEnd (start, length, description);
+				bool cont = false;
+				if (end < description.Length) {
+					char c = description [end];
+					if (c == '-' || (char.IsWhiteSpace (c) && c != '\n'))
+						++end;
+					else if (c != '\n') {
+						cont = true;
+						--end;
+					}
+				}
+				lines.Add (description.Substring (start, end - start));
+				if (cont) {
+					lines [lines.Count-1] += "-";
+				}
+				start = end;
+				if (start < description.Length && description [start] == '\n')
+					++start;
+			} while (end < description.Length);
+			return lines;
+		}
+
+		private static int GetLineEnd (int start, int length, string description)
+		{
+			int end = System.Math.Min (start + length, description.Length);
+			int sep = -1;
+			for (int i = start; i < end; ++i) {
+				switch (description [i]) {
+					case ' ':
+					case '\t':
+					case '\v':
+					case '-':
+					case ',':
+					case '.':
+					case ';':
+						sep = i;
+						break;
+					case '\n':
+						return i;
+				}
+			}
+			if (sep == -1 || end == description.Length)
+				return end;
+			return sep;
+		}
+	}
+}
+

=== added file 'ManifestUpdater/Package.cs'
--- a/ManifestUpdater/Package.cs	1970-01-01 00:00:00 +0000
+++ b/ManifestUpdater/Package.cs	2011-02-11 00:26:57 +0000
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Xml.Serialization;
+using Microsoft.Win32;
+using System.Diagnostics;
+using System.ComponentModel;
+
+namespace WexInstaller.Core
+{
+    public class Package
+    {
+        [XmlAttribute("type")]
+        public PackageType Type { get; set; }
+
+        [XmlAttribute("arch")]
+        public PackageArchitecture Architecture { get; set; }
+
+        [XmlAttribute("filename")]
+        public string FileName { get; set; }
+
+        [XmlAttribute("id")]
+        public string Id { get; set; }
+
+        [XmlAttribute("thisVersion")]
+        public string ThisVersion { get; set; }
+
+        [Browsable(false)]
+        [XmlElement("Feature")]
+        public List<ProductFeature> Features { get; set; }
+    }
+
+    public enum PackageType
+    {
+        MSI
+    }
+
+    public enum PackageArchitecture
+    {
+        X86,
+        X64,
+        Any
+    }
+
+    public enum PackagePlatform
+    {
+        Windows,
+        Any
+    }
+
+    public enum PackageMaturity
+    {
+        CTP,
+        Alpha,
+        Beta,
+        RC,
+        GA
+    }
+
+}

=== added file 'ManifestUpdater/Product.cs'
--- a/ManifestUpdater/Product.cs	1970-01-01 00:00:00 +0000
+++ b/ManifestUpdater/Product.cs	2011-02-11 00:26:57 +0000
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Xml.Serialization;
+using System.Collections.ObjectModel;
+using WexInstaller.Core;
+using System.Reflection;
+using System.Diagnostics;
+using System.Net;
+using System.ComponentModel;
+using System.Threading;
+using System.IO;
+
+namespace WexInstaller.Core
+{
+    public class Product : ProductElement
+    {
+        public List<Package> Packages { get; set; }
+
+        [XmlAttribute("upgradeId")]
+        public string UpgradeId { get; set; }
+
+        [XmlAttribute("urlBaseDir")]
+        public string UrlBase { get; set; }
+
+        public override string ToString()
+        {
+            return Name;
+        }
+    }
+
+}

=== added file 'ManifestUpdater/ProductCatalog.cs'
--- a/ManifestUpdater/ProductCatalog.cs	1970-01-01 00:00:00 +0000
+++ b/ManifestUpdater/ProductCatalog.cs	2011-02-11 00:26:57 +0000
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml.Serialization;
+using System.Runtime.Serialization;
+using System.ComponentModel;
+
+namespace WexInstaller.Core
+{
+    public class ProductCatalog
+    {
+        public ProductCatalog()
+        {
+            SetupTypes = new List<SetupType>();
+            Products = new List<CatalogProduct>();
+        }
+
+        [XmlAttribute("id")]
+        public string Id { get; set; }
+
+        [XmlAttribute("name")]
+        public string Name { get; set; }
+
+        [XmlAttribute("description")]
+        public string Description { get; set; }
+
+        [XmlAttribute("commercial")]
+        public bool Commercial { get; set; }
+
+        [Browsable(false)]
+        [XmlArray("SetupTypes")]
+        public List<SetupType> SetupTypes { get; set; }
+
+        [Browsable(false)]
+        [XmlArray("CatalogProducts")]
+        public List<CatalogProduct> Products { get; set; }
+
+        public bool Includes(Product p)
+        {
+            foreach (CatalogProduct catalogProduct in Products)
+                if (catalogProduct.ReferencedProduct == p) return true;
+            return false;
+        }
+    }
+
+    public class CatalogProduct
+    {
+        private Product referencedProduct;
+
+        [XmlIgnore]
+        public Product ReferencedProduct
+        {
+            get { return referencedProduct; }
+            set { referencedProduct = value;  }
+        }
+
+        [Browsable(false)]
+        [XmlAttribute("productId")]
+        public string ProductId { get; set; }
+
+        [XmlAttribute("setupTypeFlags")]
+        public int SetupTypeFlag { get; set; }
+
+        [Browsable(false)]
+        [XmlIgnore]
+        public ProductCatalog Catalog { get; set; }
+
+        public override string ToString()
+        {
+            return ProductId;
+        }
+    }
+
+    public enum SetupTypeFlag : int
+    {
+        Default=1,
+        ServerOnly=2,
+        Full=64,
+        Custom=128
+    }
+
+    [Serializable]
+    public class SetupType
+    {
+        [XmlAttribute("flag")]
+        [Browsable(false)]
+        public int FlagAsInt { get; set; }
+
+        [XmlIgnore]
+        public SetupTypeFlag Flag
+        {
+            get { return (SetupTypeFlag)FlagAsInt; }
+            set { FlagAsInt = (int)value; }
+        }
+
+        [XmlAttribute("name")]
+        public string Name { get; set; }
+
+        [XmlAttribute("description")]
+        public string Description { get; set; }
+
+    }
+}

=== added file 'ManifestUpdater/ProductCategory.cs'
--- a/ManifestUpdater/ProductCategory.cs	1970-01-01 00:00:00 +0000
+++ b/ManifestUpdater/ProductCategory.cs	2011-02-11 00:26:57 +0000
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml.Serialization;
+using System.ComponentModel;
+
+namespace WexInstaller.Core
+{
+    public class ProductCategory : ProductElement
+    {
+        [Browsable(false)]
+        [XmlElement("Product")]
+        public List<Product> Products;
+
+        public override string ToString()
+        {
+            return Name;
+        }
+
+        //public long GetInstallationSizeEstimate()
+        //{
+        //    long sizeEst = 0;
+        //    foreach (Product p in Products)
+        //        sizeEst = p.GetInstallationSizeEstimate();
+        //    return sizeEst;
+        //}
+    }
+
+    public enum ProductCategoryType
+    {
+        Server = 0,
+        Application,
+        Connector,
+        AddIn,
+        Documentation,
+        Tutorials
+    }
+}

=== added file 'ManifestUpdater/ProductFeature.cs'
--- a/ManifestUpdater/ProductFeature.cs	1970-01-01 00:00:00 +0000
+++ b/ManifestUpdater/ProductFeature.cs	2011-02-11 00:26:57 +0000
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Collections.ObjectModel;
+using System.Xml.Serialization;
+using System.Resources;
+using System.ComponentModel;
+
+namespace WexInstaller.Core
+{
+    abstract public class ProductElement
+    {
+        [Browsable(false)]
+        [XmlIgnore]
+        public ProductElement Parent { get; set; }
+
+        [XmlAttribute("name")]
+        public string Name { get; set; }
+
+        [XmlAttribute("title")]
+        public string Title { get; set; }
+
+        [XmlAttribute("description")]
+        public string Description { get; set; }
+    }
+
+    public class ProductFeature : ProductElement
+    {
+        private bool proposedInstalled;
+
+        [XmlAttribute("id")]
+        public string Id { get; set; }
+
+        [XmlAttribute("default")]
+        public bool IncludeInDefault { get; set; }
+
+        [XmlAttribute("size")]
+        public long SizeEstimate { get; set; }
+
+        [XmlAttribute("display")]
+        public bool Display { get; set; }
+
+        [XmlAttribute("hasComponents")]
+        public bool HasComponents { get; set; }
+
+        [Browsable(false)]
+        [XmlElement("Feature")]
+        public List<ProductFeature> Features { get; set; }
+
+        public ProductFeature()
+        {
+            Features = new List<ProductFeature>();
+        }
+
+        public virtual long GetInstallationSizeEstimate()
+        {
+            long sizeEst = 0;
+            foreach (ProductFeature f in Features)
+                sizeEst += f.SizeEstimate;
+            sizeEst += SizeEstimate;
+            return sizeEst;
+        }
+
+    }
+}

=== added file 'ManifestUpdater/ProductManifest.cs'
--- a/ManifestUpdater/ProductManifest.cs	1970-01-01 00:00:00 +0000
+++ b/ManifestUpdater/ProductManifest.cs	2011-02-11 00:26:57 +0000
@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml.Serialization;
+using System.IO;
+using System.ComponentModel;
+using System.Security.Cryptography;
+
+namespace WexInstaller.Core
+{
+    public sealed class ProductManifest
+    {
+        public static ProductManifest Instance;
+        private string installerFileName;
+
+        [Category("Version")]
+        [XmlAttribute("version")]
+        public int Version { get; set; }
+
+        [Category("Version")]
+        [XmlAttribute("format")]
+        public int Format { get; set; }
+
+        [Category("Updates")]
+        [XmlElement("UpdateVersion")]
+        public string UpdateVersion { get; set; }
+
+        [Category("Updates")]
+        [XmlElement("UpdateURL")]
+        public string UpdateUrl { get; set; }
+
+        [Category("Updates")]
+        [RefreshProperties(RefreshProperties.All)]
+        [XmlElement("UpdateHash")]
+        public string UpdateHash { get; set; }
+
+        [Browsable(false)]
+        [XmlArray()]
+        public List<ProductCatalog> ProductCatalogs;
+
+        [Browsable(false)]
+        [XmlArray]
+        public List<ProductCategory> ProductCategories;
+
+        public static ProductManifest Load(string file)
+        {
+            try
+            {
+                XmlRootAttribute root = new XmlRootAttribute("ProductManifest");
+                XmlSerializer s = new XmlSerializer(typeof(ProductManifest));
+                TextReader w = new StreamReader(file);
+                ProductManifest manifest = (ProductManifest)s.Deserialize(w);
+                w.Close();
+                return manifest;
+            }
+            catch (Exception ex)
+            {
+                return null;
+            }
+        }
+
+        public void Save(string file)
+        {
+            try
+            {
+                XmlSerializer s = new XmlSerializer(typeof(ProductManifest));
+                TextWriter w = new StreamWriter(file);
+                s.Serialize(w, this);
+                w.Close();
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine(ex.Message);
+            }
+        }
+        public static string GetSHAHash(string pathName)
+        {
+            SHA1CryptoServiceProvider hasher = new SHA1CryptoServiceProvider();
+
+            try
+            {
+                Stream stream = new FileStream(pathName, FileMode.Open);
+                byte[] hash = hasher.ComputeHash(stream);
+                stream.Close();
+
+                string stringHash = BitConverter.ToString(hash);
+                stringHash = stringHash.Replace("-", "");
+                return stringHash;
+            }
+            catch (Exception)
+            {
+                return null;
+            }
+        }
+    }
+}

=== added file 'ManifestUpdater/Program.cs'
--- a/ManifestUpdater/Program.cs	1970-01-01 00:00:00 +0000
+++ b/ManifestUpdater/Program.cs	2011-02-11 00:26:57 +0000
@@ -0,0 +1,204 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Mono.Options;
+using WexInstaller.Core;
+using System.IO;
+using Microsoft.Tools.WindowsInstallerXml.Msi;
+
+namespace ManifestUpdater
+{
+    class Program
+    {
+        static string inFile = null;
+        static string outFile = null;
+        static string cacheDir = null;
+
+        static void Main(string[] args)
+        {
+
+            // Parse our command line args
+            OptionSet p = new OptionSet()
+              .Add("input=|in=", input => inFile=input)
+              .Add("output=|out=", output => outFile=output)
+              .Add("cachedir=|cache=", dir => cacheDir=dir);
+            p.Parse(args);
+
+            // load the manifest
+            ProductManifest manifest = ProductManifest.Load(inFile);
+
+            foreach (ProductCategory category in manifest.ProductCategories)
+            {
+                foreach (Product product in category.Products)
+                {
+                    if (product.Packages.Count != 1) continue;
+                    Package package = product.Packages[0];
+                    UpdateProductAndPackage(product, package);
+                }
+            }
+
+            // now save the manifest
+            manifest.Save(outFile);
+            Console.ReadLine();
+        }
+
+        static void UpdateProductAndPackage(Product product, Package package)
+        {
+            // first find the latest MSI in our cache
+            string msiFile = FindLatestVersion(package.FileName);
+            if (String.IsNullOrEmpty(msiFile)) return;
+
+            UpdateProductInfo(product, msiFile);
+            UpdatePackageInfo(package, msiFile);
+        }
+
+        static string FindLatestVersion(string fileName)
+        {
+            int index = fileName.IndexOf('.');
+            index = fileName.IndexOf('.', index + 1);
+            string baseName = fileName.Substring(0, index);
+
+            Version newestVersion = new Version(0, 0);
+            string newestFile = null;
+
+            string[] files = Directory.GetFiles(cacheDir, baseName + "*.msi");
+            foreach (string file in files)
+            {
+                Version v = new Version(GetProperty(file, "ProductVersion"));
+                if (v <= newestVersion) continue;
+                newestVersion = v;
+                newestFile = file;
+            }
+            return newestFile;
+        }
+
+        static void UpdateProductInfo(Product product, string msiFile)
+        {
+            product.UpgradeId = GetProperty(msiFile, "UpgradeCode");
+        }
+
+        static void UpdatePackageInfo(Package package, string msiFile)
+        {
+            package.FileName = Path.GetFileName(msiFile);
+            package.ThisVersion = GetProperty(msiFile, "ProductVersion");
+            package.Id = GetProperty(msiFile, "ProductCode");
+            package.Features.Clear();
+            SetFeatures(msiFile, package);
+        }
+
+        static void SetFeatures(string msiFile, Package package)
+        {
+            Database msiDb = new Database(msiFile, OpenDatabase.ReadOnly);
+            GetFeatures(msiDb, package);
+            msiDb.Close();
+        }
+
+        static void GetFeatures(Database msiDb, object parent)
+        {
+            List<ProductFeature> features = new List<ProductFeature>();
+
+            if (parent is Package)
+                (parent as Package).Features = features;
+            else
+                (parent as ProductFeature).Features = features;
+            string parentName = parent is Package ? null : (parent as ProductFeature).Name;
+
+            using (View featureView = msiDb.OpenExecuteView("SELECT * FROM Feature ORDER BY Display"))
+            {
+                Record record;
+                while (null != (record = featureView.Fetch()))
+                {
+                    int featureLevel = record.GetInteger(6);
+                    if (featureLevel == 0) continue;    // level 0 features will not be installed anyway
+
+                    string featureParent = record.GetString(2);
+                    if (featureParent != parentName) continue;
+
+                    ProductFeature feature = new ProductFeature();
+                    feature.Name = record.GetString(1);
+                    feature.Title = record.GetString(3);
+                    feature.Description = record.GetString(4);
+                    feature.Display = record.GetInteger(5) == 1 ? true : false;
+                    feature.HasComponents = FeatureHasComponents(msiDb, feature.Name);
+                    feature.SizeEstimate = GetSizeEstimate(msiDb, feature.Name);
+                    features.Add(feature);
+                }
+            }
+
+            // now recurse
+            if (features != null && features.Count > 0)
+                foreach (ProductFeature feature in features)
+                    GetFeatures(msiDb, feature);
+        }
+
+        static long GetSizeEstimate(Database msiDb, string featureName)
+        {
+            List<string> components = GetComponentsForFeature(msiDb, featureName);
+
+            long size = 0;
+            foreach (string component in components)
+                size += GetSizeOfComponent(msiDb, component);
+
+            return size;
+        }
+
+        static List<string> GetComponentsForFeature(Database msiDb, string feature)
+        {
+            List<string> components = new List<string>();
+
+            using (View view = msiDb.OpenExecuteView("SELECT * FROM FeatureComponents"))
+            {
+                Record record;
+                while (null != (record = view.Fetch()))
+                {
+                    if (String.Compare(feature, record.GetString(1), true) == 0)
+                        components.Add(record.GetString(2));
+                }
+            }
+            return components;
+        }
+
+        static long GetSizeOfComponent(Database msiDb, string component)
+        {
+            long size = 0;
+
+            using (View view = msiDb.OpenExecuteView("SELECT * FROM File"))
+            {
+                Record record;
+                while (null != (record = view.Fetch()))
+                {
+                    if (String.Compare(component, record.GetString(2), true) == 0)
+                        size += (long)record.GetInteger(4);
+                }
+            }
+            return size;
+        }
+
+        static bool FeatureHasComponents(Database msiDb, string featureName)
+        {
+            using (View view = msiDb.OpenExecuteView("SELECT * FROM FeatureComponents"))
+            {
+                Record record;
+                while (null != (record = view.Fetch()))
+                {
+                    if (String.Compare(featureName, record.GetString(1), true) == 0)
+                        return true;
+                }
+            }
+            return false;
+        }
+
+        static string GetProperty(string msiFile, string property)
+        {
+            using (Database d = new Database(msiFile, OpenDatabase.ReadOnly))
+            {
+                string sql = String.Format("SELECT * FROM `Property` WHERE `Property`.`Property` = '{0}'", property);
+                View view = d.OpenView(sql);
+                view.Execute();
+                Record record = view.Fetch();
+                if (record == null) return String.Empty;
+                return record.GetString(2);
+            }
+        }
+    }
+}

=== added directory 'ManifestUpdater/Properties'
=== added file 'ManifestUpdater/Properties/AssemblyInfo.cs'
--- a/ManifestUpdater/Properties/AssemblyInfo.cs	1970-01-01 00:00:00 +0000
+++ b/ManifestUpdater/Properties/AssemblyInfo.cs	2011-02-11 00:26:57 +0000
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ManifestUpdater")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("ManifestUpdater")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2011")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("9ebb9ced-95b6-46f5-96ed-de27a1c4cdd3")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

=== added file 'ManifestUpdater/app.config'
--- a/ManifestUpdater/app.config	1970-01-01 00:00:00 +0000
+++ b/ManifestUpdater/app.config	2011-02-11 00:26:57 +0000
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<configuration>
+<startup><supportedRuntime version="v2.0.50727"/></startup></configuration>

=== added file 'ManifestUpdater/wix.dll'
Binary files a/ManifestUpdater/wix.dll	1970-01-01 00:00:00 +0000 and b/ManifestUpdater/wix.dll	2011-02-11 00:26:57 +0000 differ
=== modified file 'installer-vs2010.sln'
--- a/installer-vs2010.sln	2011-02-10 19:36:56 +0000
+++ b/installer-vs2010.sln	2011-02-11 00:26:57 +0000
@@ -27,6 +27,8 @@
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MySQLCA", "MySQLCA\MySQLCA.csproj", "{9B6E4CA6-C2D6-4782-BF70-98E9BB4CAE8D}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManifestUpdater", "ManifestUpdater\ManifestUpdater.csproj", "{57913AF9-D535-4B13-A088-8DF74FD47A53}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -53,7 +55,6 @@
 		{07B31F5A-9F17-4ACF-B3B6-2AF9000B1414}.Release|x86.ActiveCfg = Release|Any CPU
 		{07B31F5A-9F17-4ACF-B3B6-2AF9000B1414}.Release|x86.Build.0 = Release|Any CPU
 		{27F00801-BEAE-417F-9CF5-5686923F7538}.Debug|Any CPU.ActiveCfg = Debug|x86
-		{27F00801-BEAE-417F-9CF5-5686923F7538}.Debug|Any CPU.Build.0 = Debug|x86
 		{27F00801-BEAE-417F-9CF5-5686923F7538}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
 		{27F00801-BEAE-417F-9CF5-5686923F7538}.Debug|Mixed Platforms.Build.0 = Debug|x86
 		{27F00801-BEAE-417F-9CF5-5686923F7538}.Debug|Win32.ActiveCfg = Debug|x86
@@ -176,6 +177,19 @@
 		{9B6E4CA6-C2D6-4782-BF70-98E9BB4CAE8D}.Release|Win32.ActiveCfg = Release|x86
 		{9B6E4CA6-C2D6-4782-BF70-98E9BB4CAE8D}.Release|x86.ActiveCfg = Release|x86
 		{9B6E4CA6-C2D6-4782-BF70-98E9BB4CAE8D}.Release|x86.Build.0 = Release|x86
+		{57913AF9-D535-4B13-A088-8DF74FD47A53}.Debug|Any CPU.ActiveCfg = Debug|x86
+		{57913AF9-D535-4B13-A088-8DF74FD47A53}.Debug|Any CPU.Build.0 = Debug|x86
+		{57913AF9-D535-4B13-A088-8DF74FD47A53}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+		{57913AF9-D535-4B13-A088-8DF74FD47A53}.Debug|Mixed Platforms.Build.0 = Debug|x86
+		{57913AF9-D535-4B13-A088-8DF74FD47A53}.Debug|Win32.ActiveCfg = Debug|x86
+		{57913AF9-D535-4B13-A088-8DF74FD47A53}.Debug|x86.ActiveCfg = Debug|x86
+		{57913AF9-D535-4B13-A088-8DF74FD47A53}.Debug|x86.Build.0 = Debug|x86
+		{57913AF9-D535-4B13-A088-8DF74FD47A53}.Release|Any CPU.ActiveCfg = Release|x86
+		{57913AF9-D535-4B13-A088-8DF74FD47A53}.Release|Mixed Platforms.ActiveCfg = Release|x86
+		{57913AF9-D535-4B13-A088-8DF74FD47A53}.Release|Mixed Platforms.Build.0 = Release|x86
+		{57913AF9-D535-4B13-A088-8DF74FD47A53}.Release|Win32.ActiveCfg = Release|x86
+		{57913AF9-D535-4B13-A088-8DF74FD47A53}.Release|x86.ActiveCfg = Release|x86
+		{57913AF9-D535-4B13-A088-8DF74FD47A53}.Release|x86.Build.0 = Release|x86
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

Attachment: [text/bzr-bundle] bzr/reggie.burnett@oracle.com-20110211002657-rmzohd0c4p2hvk72.bundle
Thread
bzr commit into wex-installer-1.0 branch (reggie.burnett:331) Reggie Burnett11 Feb