diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..296b6c4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,181 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.sln.docstates
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+build/
+bld/
+[Bb]in/
+[Oo]bj/
+
+# Roslyn cache directories
+*.ide/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+#NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding addin-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment out the next line if you want to keep your passwords hidden
+*.pubxml
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# If using the old MSBuild-Integrated Package Restore, uncomment this:
+#!**/packages/repositories.config
+
+# Windows Azure Build Output
+csx/
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+sql/
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
diff --git a/AVSP.ConsoleSupport/ConsoleHelper.cs b/AVSP.ConsoleSupport/ConsoleHelper.cs
new file mode 100644
index 0000000..375a30d
--- /dev/null
+++ b/AVSP.ConsoleSupport/ConsoleHelper.cs
@@ -0,0 +1,142 @@
+#region License
+
+/*
+ * File: ConsoleHelper.cs
+ *
+ * The MIT License
+ *
+ * Copyright © 2017 AVSP Ltd
+ *
+ * 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.
+ */
+
+#endregion License
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+
+namespace AVSP.ConsoleSupport
+{
+ ///
+ /// Console support library for banner, copyright, version, command line arguments
+ ///
+ internal static class ConsoleHelper
+ {
+ public delegate int RunFunction(SimpleArguments arguments);
+
+ public static SimpleArguments Arguments { get; private set; }
+
+ ///
+ /// Runs a console app with parsed options, sensible exit codes, exception handler
+ ///
+ /// Example:
+ /// private static int Main(string[] args)
+ /// {
+ /// return ConsoleHelper.RunProgram(args, Run);
+ /// }
+ ///
+ /// arguments from Main()
+ /// delegate to run app
+ /// program exit code
+ public static int RunProgram(IList args, RunFunction run)
+ {
+ try
+ {
+ ConsoleHelper.Startup(args);
+ ConsoleHelper.WriteBanner();
+
+ int returnValue = run(ConsoleHelper.Arguments);
+
+ return (Environment.ExitCode == 0) ? returnValue : Environment.ExitCode;
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e.Message);
+ Trace.TraceError(e.ToString());
+
+ return Environment.ExitCode != 0
+ ? Environment.ExitCode : 100;
+ }
+ }
+
+ public static void Startup(IList args)
+ {
+ Arguments = new SimpleArguments(args);
+
+ // enable trace with v parameter
+ if (Arguments.GetFlag("v"))
+ {
+ Trace.Listeners.Add(new ConsoleTraceListener(true));
+ }
+ }
+
+ public static string GetProductVersion()
+ {
+ return Assembly.GetExecutingAssembly().GetName().Version.ToString();
+ }
+
+ public static string GetProductName()
+ {
+ return Assembly.GetExecutingAssembly().GetName().Name;
+ }
+
+ public static string GetDescription()
+ {
+ //Type of attribute that is desired
+ Type type = typeof(AssemblyDescriptionAttribute);
+
+ //Is there an attribute of this type already defined?
+ if (AssemblyDescriptionAttribute.IsDefined(Assembly.GetExecutingAssembly(), type))
+ {
+ //if there is, get attribute of desired type
+ AssemblyDescriptionAttribute assemblyDescriptionAttribute = (AssemblyDescriptionAttribute)AssemblyDescriptionAttribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), type);
+
+ return assemblyDescriptionAttribute.Description;
+ }
+
+ return null;
+ }
+
+ public static string GetCopyright()
+ {
+ //Type of attribute that is desired
+ Type type = typeof(AssemblyCopyrightAttribute);
+
+ //Is there an attribute of this type already defined?
+ if (AssemblyCopyrightAttribute.IsDefined(Assembly.GetExecutingAssembly(), type))
+ {
+ //if there is, get attribute of desired type
+ AssemblyCopyrightAttribute assemblyCopyrightAttribute = (AssemblyCopyrightAttribute)AssemblyCopyrightAttribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), type);
+
+ return assemblyCopyrightAttribute.Copyright;
+ }
+
+ return null;
+ }
+
+ public static void WriteBanner()
+ {
+ Console.WriteLine(GetProductName() + " v" + GetProductVersion());
+ Console.WriteLine(GetCopyright());
+ Console.WriteLine();
+ }
+ }
+}
\ No newline at end of file
diff --git a/AVSP.ConsoleSupport/SimpleArguments.cs b/AVSP.ConsoleSupport/SimpleArguments.cs
new file mode 100644
index 0000000..3a34b4d
--- /dev/null
+++ b/AVSP.ConsoleSupport/SimpleArguments.cs
@@ -0,0 +1,152 @@
+#region License
+
+/*
+ * File: SimpleArguments.cs
+ *
+ * The MIT License
+ *
+ * Copyright © 2017 AVSP Ltd
+ *
+ * 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.
+ */
+
+#endregion License
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+
+namespace AVSP.ConsoleSupport
+{
+ ///
+ /// Handles sets of arguments specified with a - and an optional parameter for each argument when no dash given
+ ///
+ public class SimpleArguments
+ {
+ protected Dictionary argumentValues = new Dictionary();
+ protected Dictionary flags = new Dictionary();
+
+ public ReadOnlyCollection Arguments { get; protected set; }
+
+ public SimpleArguments()
+ {
+ Arguments = new List().AsReadOnly();
+ }
+
+ ///
+ /// Constructor which parses command line arguments
+ ///
+ /// Args array from Program.Main()
+ public SimpleArguments(IList args)
+ {
+ Parse(args);
+ }
+
+ ///
+ /// Set default value for an argument, will not create a flag
+ ///
+ /// Name of argument to set value for
+ /// Value to set argument to
+ public void SetDefault(string argumentName, string value)
+ {
+ if (!argumentValues.ContainsKey(argumentName))
+ {
+ argumentValues[argumentName] = value;
+ }
+ }
+
+ ///
+ /// Parse a list of argument values, e.g. { "-argumentname", "argumentvalue", "-flagtoset" }
+ ///
+ /// List of arguments to parse
+ public void Parse(IList args)
+ {
+ // list of non-option arguments
+ List arguments = new List();
+
+ string lastArgumentName = null;
+
+ foreach (string arg in args)
+ {
+ if (arg.StartsWith("-"))
+ {
+ lastArgumentName = arg.Substring(1).ToLower();
+ flags[lastArgumentName] = true;
+ }
+ else
+ {
+ if (lastArgumentName != null && lastArgumentName.Length > 0)
+ {
+ argumentValues[lastArgumentName] = arg;
+ lastArgumentName = null;
+ }
+ else
+ {
+ arguments.Add(arg);
+ }
+ }
+ }
+
+ Arguments = arguments.AsReadOnly();
+ }
+
+ ///
+ /// Get the value of a passed argument, or the default
+ ///
+ /// Name of argument to set value for
+ /// string value of argument
+ public string GetValue(string argumentName)
+ {
+ string value;
+
+ argumentValues.TryGetValue(argumentName.ToLower(), out value);
+
+ return value;
+ }
+
+ ///
+ /// Get whether a flag was passed
+ ///
+ /// Name of argument to check for
+ /// true when argument was passed
+ public bool GetFlag(string argumentName)
+ {
+ return flags.ContainsKey(argumentName.ToLower());
+ }
+
+ ///
+ /// Get list of values in a comma-separated option
+ ///
+ /// argument to retrieve
+ /// list of string contained in argument
+ public IEnumerable GetList(string argumentName)
+ {
+ string value;
+ char[] charSplit = new char[] { ',' };
+
+ if (argumentValues.TryGetValue(argumentName.ToLower(), out value))
+ {
+ return value.Split(charSplit, StringSplitOptions.RemoveEmptyEntries);
+ }
+
+ return Enumerable.Empty();
+ }
+ }
+}
\ No newline at end of file
diff --git a/AVSP.ConsoleSupport/SimpleArgumentsReader.cs b/AVSP.ConsoleSupport/SimpleArgumentsReader.cs
new file mode 100644
index 0000000..5e45f2d
--- /dev/null
+++ b/AVSP.ConsoleSupport/SimpleArgumentsReader.cs
@@ -0,0 +1,149 @@
+#region License
+
+/*
+ * File: SimpleArgumentsReader.cs
+ *
+ * The MIT License
+ *
+ * Copyright © 2017 AVSP Ltd
+ *
+ * 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.
+ */
+
+#endregion License
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+
+namespace AVSP.ConsoleSupport
+{
+ ///
+ /// Reads arguments into POCOs, supports fields or properties of int, string, int[], string[], other type may work too
+ ///
+ public static class SimpleArgumentsReader
+ {
+ private static Type boolType = typeof(bool);
+
+ ///
+ /// Read a set of command line arguments into the properties or fields in a POCO
+ ///
+ /// source list of command line arguments, like the array from Main()
+ /// destination POCO to set values in
+ public static void ArgumentsToObject(IList args, object value)
+ {
+ SimpleArguments arguments = new SimpleArguments(args);
+ ArgumentsToObject(arguments, value);
+ }
+
+ ///
+ /// Read set of parsed SimpleArguments into the properties or fields in a POCO
+ ///
+ /// SimpleArguments objects with source values
+ ///
+ public static void ArgumentsToObject(SimpleArguments arguments, object value)
+ {
+ char[] commaSplit = new char[] { ',' };
+
+ Type t = value.GetType();
+
+ FieldInfo[] fields = t.GetFields(BindingFlags.Public | BindingFlags.Instance);
+
+ foreach (FieldInfo field in fields)
+ {
+ MemberInfo m = field;
+
+ try
+ {
+ if (field.FieldType == boolType)
+ {
+ field.SetValue(value, arguments.GetFlag(field.Name));
+ }
+ else
+ {
+ string valueString = arguments.GetValue(field.Name);
+
+ if (valueString != null)
+ {
+ field.SetValue(value, Convert.ChangeType(valueString, field.FieldType));
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new ArgumentException("ERROR: exception occurred whilst parsing argument " + field.Name, ex);
+ }
+ }
+
+ PropertyInfo[] properties = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
+
+ foreach (PropertyInfo property in properties)
+ {
+ try
+ {
+ if (property.PropertyType == boolType)
+ {
+ // handle a normal bool, if the flag was passed, set it to true, if not, false
+ property.SetValue(value, arguments.GetFlag(property.Name), null);
+ }
+ else if (property.PropertyType.BaseType.FullName == "System.Array")
+ {
+ // handle comma separated arrays
+ string valueString = arguments.GetValue(property.Name);
+
+ if (valueString != null)
+ {
+ string[] valueStringArray = valueString.Split(commaSplit);
+
+ int arrayLength = valueStringArray.Length;
+
+ Type elementType = property.PropertyType.GetElementType();
+
+ // build the array
+ Array y = Array.CreateInstance(elementType, arrayLength);
+
+ for (int i = 0; i < arrayLength; i++)
+ {
+ // set an element
+ y.SetValue(Convert.ChangeType(valueStringArray[i], elementType), i);
+ }
+
+ // put the array into the property
+ property.SetValue(value, y, null);
+ }
+ }
+ else
+ {
+ string valueString = arguments.GetValue(property.Name);
+
+ if (valueString != null)
+ {
+ property.SetValue(value, Convert.ChangeType(valueString, property.PropertyType), null);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new ArgumentException("ERROR: exception occurred whilst parsing argument " + property.Name, ex);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Program.cs b/Program.cs
new file mode 100644
index 0000000..4782a18
--- /dev/null
+++ b/Program.cs
@@ -0,0 +1,76 @@
+#region License
+
+/*
+ * File: Program.cs
+ *
+ * The MIT License
+ *
+ * Copyright © 2017 AVSP Ltd
+ *
+ * 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.
+ */
+
+#endregion License
+
+using System;
+using System.Diagnostics;
+using AVSP.ConsoleSupport;
+using VsIdeBuild.VsBuilderLibrary;
+
+namespace VsIdeBuild
+{
+ internal class Program
+ {
+ private static int Run(SimpleArguments arguments)
+ {
+ VsBuilderOptions options = new VsBuilderOptions();
+
+ // parse command line arguments into options
+ SimpleArgumentsReader.ArgumentsToObject(arguments, options);
+
+ if (options.Solution == null || options.Solution.Length == 0)
+ {
+ Console.Error.WriteLine(@"ERROR: solution must be specified with -solution c:\path\to\solution.sln");
+ return 1;
+ }
+
+ //@"C:\Dropbox\Code\CresNuget\BuildTest\BuildTest.sln"
+
+ VsBuilder builder = new VsBuilder();
+
+ builder.Run(options);
+
+ if (builder.Results.Failed)
+ {
+ Console.Error.WriteLine("ERROR: some builds failed, check output");
+ return 1;
+ }
+
+ Console.WriteLine("Success: all okay");
+ return 0;
+ }
+
+ // STAThread needed for COM
+ [STAThread]
+ private static int Main(string[] args)
+ {
+ return ConsoleHelper.RunProgram(args, Run);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..e71039e
--- /dev/null
+++ b/Properties/AssemblyInfo.cs
@@ -0,0 +1,37 @@
+using System.Reflection;
+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("VsIdeBuild")]
+[assembly: AssemblyDescription("Visual Studio 2008 Solution Builder")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("AVSP Ltd")]
+[assembly: AssemblyProduct("VsIdeBuild")]
+[assembly: AssemblyCopyright("Copyright © 2017 Chris Poole")]
+[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("bb967cbb-33f0-4095-9477-0899528d0c63")]
+
+// 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.*")]
+
+// when AssemblyFileVersion is ommitted, it will default to the AssemblyVersion
+//[assembly: AssemblyFileVersion("1.0.0.0")]
\ No newline at end of file
diff --git a/VsIdeBuild.csproj b/VsIdeBuild.csproj
new file mode 100644
index 0000000..c4b7474
--- /dev/null
+++ b/VsIdeBuild.csproj
@@ -0,0 +1,73 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {4791B3F0-B02E-4F24-A33E-3C5C31E5B472}
+ Exe
+ Properties
+ VsIdeBuild
+ VsIdeBuild
+ v3.5
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ False
+ ..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies\EnvDTE.dll
+
+
+ False
+ ..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies\EnvDTE80.dll
+
+
+ False
+ ..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies\EnvDTE90.dll
+
+
+
+ 3.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/VsIdeBuild.sln b/VsIdeBuild.sln
new file mode 100644
index 0000000..f4c8abf
--- /dev/null
+++ b/VsIdeBuild.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VsIdeBuild", "VsIdeBuild.csproj", "{4791B3F0-B02E-4F24-A33E-3C5C31E5B472}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {4791B3F0-B02E-4F24-A33E-3C5C31E5B472}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4791B3F0-B02E-4F24-A33E-3C5C31E5B472}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4791B3F0-B02E-4F24-A33E-3C5C31E5B472}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4791B3F0-B02E-4F24-A33E-3C5C31E5B472}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/VsIdeBuild/MessageFilter.cs b/VsIdeBuild/MessageFilter.cs
new file mode 100644
index 0000000..6ed5b80
--- /dev/null
+++ b/VsIdeBuild/MessageFilter.cs
@@ -0,0 +1,108 @@
+#region License
+
+/*
+ * File: MessageFilter.cs
+ *
+ * Derived from Microsoft sample code, see below for source
+ *
+ * Used under MICROSOFT LIMITED PUBLIC LICENSE
+ *
+ */
+
+#endregion License
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace VsIdeBuild.VsBuilderLibrary
+{
+ ///
+ /// See "How to: Fix 'Application is Busy' and 'Call was Rejected By Callee' Errors"
+ /// https://msdn.microsoft.com/en-us/library/ms228772.aspx
+ ///
+
+ internal class MessageFilter : IOleMessageFilter
+ {
+ //
+ // Class containing the IOleMessageFilter
+ // thread error-handling functions.
+
+ // Start the filter.
+ public static void Register()
+ {
+ IOleMessageFilter newFilter = new MessageFilter();
+ IOleMessageFilter oldFilter = null;
+ CoRegisterMessageFilter(newFilter, out oldFilter);
+ }
+
+ // Done with the filter, close it.
+ public static void Revoke()
+ {
+ IOleMessageFilter oldFilter = null;
+ CoRegisterMessageFilter(null, out oldFilter);
+ }
+
+ //
+ // IOleMessageFilter functions.
+ // Handle incoming thread requests.
+ int IOleMessageFilter.HandleInComingCall(int dwCallType,
+ System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr
+ lpInterfaceInfo)
+ {
+ //Return the flag SERVERCALL_ISHANDLED.
+ return 0;
+ }
+
+ // Thread call was rejected, so try again.
+ int IOleMessageFilter.RetryRejectedCall(System.IntPtr
+ hTaskCallee, int dwTickCount, int dwRejectType)
+ {
+ if (dwRejectType == 2)
+ // flag = SERVERCALL_RETRYLATER.
+ {
+ // Retry the thread call immediately if return >=0 &
+ // <100.
+ return 99;
+ }
+ // Too busy; cancel call.
+ return -1;
+ }
+
+ int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee,
+ int dwTickCount, int dwPendingType)
+ {
+ //Return the flag PENDINGMSG_WAITDEFPROCESS.
+ return 2;
+ }
+
+ // Implement the IOleMessageFilter interface.
+ [DllImport("Ole32.dll")]
+ private static extern int
+ CoRegisterMessageFilter(IOleMessageFilter newFilter, out
+ IOleMessageFilter oldFilter);
+ }
+
+ [ComImport(), Guid("00000016-0000-0000-C000-000000000046"),
+ InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
+ internal interface IOleMessageFilter
+ {
+ [PreserveSig]
+ int HandleInComingCall(
+ int dwCallType,
+ IntPtr hTaskCaller,
+ int dwTickCount,
+ IntPtr lpInterfaceInfo);
+
+ [PreserveSig]
+ int RetryRejectedCall(
+ IntPtr hTaskCallee,
+ int dwTickCount,
+ int dwRejectType);
+
+ [PreserveSig]
+ int MessagePending(
+ IntPtr hTaskCallee,
+ int dwTickCount,
+ int dwPendingType);
+ }
+}
\ No newline at end of file
diff --git a/VsIdeBuild/VsBuilder.cs b/VsIdeBuild/VsBuilder.cs
new file mode 100644
index 0000000..c6633c1
--- /dev/null
+++ b/VsIdeBuild/VsBuilder.cs
@@ -0,0 +1,425 @@
+#region License
+
+/*
+ * File: VsBuilder.cs
+ *
+ * The MIT License
+ *
+ * Copyright © 2017 AVSP Ltd
+ *
+ * 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.
+ */
+
+#endregion License
+
+using System;
+using System.IO;
+using EnvDTE;
+using EnvDTE80;
+
+namespace VsIdeBuild.VsBuilderLibrary
+{
+ public class VsBuilder
+ {
+ ///
+ /// Build log message we look for to indicate sandbox failure
+ ///
+ private const string crestronSandboxFailureMessage = "was not prepared";
+
+ private object visualStudio;
+ private DTE dte;
+ private DTE2 dte2;
+ private Solution sln;
+ private VsBuilderOptions options;
+
+ ///
+ /// After calling Run, Results will contain build counts and fail counts
+ ///
+ public VsBuilderResults Results { get; private set; }
+
+ ///
+ /// Build solution as specified in options
+ ///
+ /// Build configuration options
+ ///
+ public int Run(VsBuilderOptions options)
+ {
+ this.options = options;
+ this.Results = new VsBuilderResults();
+
+ Console.WriteLine("Opening Visual Studio 2008...");
+ OpenVS();
+
+ dte.SuppressUI = !options.Debug;
+ dte.UserControl = options.Debug;
+
+ if (!File.Exists(options.Solution))
+ {
+ Console.WriteLine("Solution file not found");
+ return 1;
+ }
+
+ Console.WriteLine("Opening Solution...");
+ if (!OpenSolution(options.Solution))
+ {
+ Console.WriteLine("Solution could not be opened");
+ return 2;
+ }
+
+ Console.WriteLine("Solution.Count = " + sln.Count);
+
+ Console.WriteLine("Projects Names...");
+ foreach (Project project in dte.Solution.Projects)
+ {
+ Console.WriteLine("Project: " + project.Name);
+ }
+
+ if (options.ShowProjectContexts)
+ {
+ Console.WriteLine("Showing project contexts...");
+ Console.WriteLine(GetProjectContexts());
+ }
+
+ ShowProjectOutputs();
+
+ if (options.BuildAll)
+ {
+ BuildAll();
+ }
+ else
+ {
+ if (options.BuildSolutionConfiguration != null)
+ {
+ BuildSolutionConfiguration(options.BuildSolutionConfiguration);
+ }
+ }
+
+ Console.WriteLine("Closing Solution...");
+ CloseSolution();
+
+ Console.WriteLine("Closing Visual Studio...");
+ CloseVS();
+
+ return 0;
+ }
+
+ private void PostBuildChecks()
+ {
+ if (sln.SolutionBuild.LastBuildInfo != 0)
+ {
+ Console.WriteLine("ERROR: some projects failed to build!");
+ Results.Failed = true;
+ return;
+ }
+
+ // Crestron SDK does not report sandbox failure as a failed build, because it is in a post-build step, so detect it separately
+ string simplSharpOutput = GetOutputWindowText("Build");
+
+ if (simplSharpOutput != null)
+ {
+ if (simplSharpOutput.IndexOf(crestronSandboxFailureMessage) != -1)
+ {
+ Console.WriteLine("ERROR: Crestron sandbox failues in build!");
+ Results.Failed = true;
+ }
+ }
+ }
+
+ private void BuildSolutionConfiguration(EnvDTE80.SolutionConfiguration2 solutionConfiguration2)
+ {
+ Console.WriteLine("Activating solution configuration '" + solutionConfiguration2.Name + "' platform '" + solutionConfiguration2.PlatformName + "'");
+ solutionConfiguration2.Activate();
+
+ if (options.Clean)
+ {
+ Console.WriteLine("Cleaning solution configuration '" + solutionConfiguration2.Name + "' platform '" + solutionConfiguration2.PlatformName + "'");
+ sln.SolutionBuild.Clean(true);
+ System.Threading.Thread.Sleep(1000);
+ }
+
+ Console.WriteLine("Building " + solutionConfiguration2.Name + ":" + solutionConfiguration2.PlatformName);
+ sln.SolutionBuild.Build(true);
+ System.Threading.Thread.Sleep(1000);
+ PostBuildChecks();
+ }
+
+ private void BuildProject(string solutionConfigurationName, string projectUniqueName)
+ {
+ sln.SolutionBuild.BuildProject(solutionConfigurationName, projectUniqueName, true);
+
+ System.Threading.Thread.Sleep(1000);
+
+ PostBuildChecks();
+ }
+
+ private void BuildSolutionConfiguration(string solutionConfigurationName)
+ {
+ EnvDTE.SolutionConfigurations solutionConfigurations;
+
+ solutionConfigurations = sln.SolutionBuild.SolutionConfigurations;
+
+ foreach (EnvDTE80.SolutionConfiguration2 solutionConfiguration2 in solutionConfigurations)
+ {
+ Console.WriteLine("BuildSolutionConfiguration considering solution configuration '" + solutionConfiguration2.Name + "' platform '" + solutionConfiguration2.PlatformName + "'");
+
+ if (solutionConfiguration2.Name == solutionConfigurationName)
+ {
+ Console.WriteLine("Matches, building...");
+ BuildSolutionConfiguration(solutionConfiguration2);
+ }
+ else
+ {
+ Console.WriteLine("Does not match, skipping");
+ }
+ }
+ }
+
+ private void BuildAll()
+ {
+ EnvDTE.SolutionConfigurations solutionConfigurations;
+
+ solutionConfigurations = sln.SolutionBuild.SolutionConfigurations;
+
+ foreach (EnvDTE80.SolutionConfiguration2 solutionConfiguration2 in solutionConfigurations)
+ {
+ Console.WriteLine("BuildAll starting solution configuration '" + solutionConfiguration2.Name + "' platform '" + solutionConfiguration2.PlatformName + "'");
+ BuildSolutionConfiguration(solutionConfiguration2);
+ }
+ }
+
+ private void SaveAllOutputWindowPanes(string basePath, string projectName)
+ {
+ OutputWindow outputWindow = dte2.ToolWindows.OutputWindow;
+ OutputWindowPanes panes = outputWindow.OutputWindowPanes;
+
+ foreach (OutputWindowPane pane in panes)
+ {
+ string filename = Path.Combine(basePath, "build." + projectName + "-" + pane.Name + ".log");
+
+ string text = GetOutputWindowPaneText(pane);
+ File.WriteAllText(filename, text);
+ }
+ }
+
+ private string GetOutputWindowPaneText(OutputWindowPane outputWindowPane)
+ {
+ TextDocument doc = outputWindowPane.TextDocument;
+ TextSelection sel = doc.Selection;
+
+ sel.SelectAll();
+ string txt = sel.Text;
+
+ return txt;
+ }
+
+ ///
+ /// Get the full text from an output pane
+ ///
+ ///
+ /// text from chosen output pane or null if pane does not exist or an error occurs
+ private string GetOutputWindowText(string fromPane)
+ {
+ try
+ {
+ OutputWindow outputWindow = dte2.ToolWindows.OutputWindow;
+ OutputWindowPane outputWindowPane = outputWindow.OutputWindowPanes.Item(fromPane);
+
+ return GetOutputWindowPaneText(outputWindowPane);
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+
+ private void ChangeProjectContexts(EnvDTE.Project project, string configurationName)
+ {
+ EnvDTE.SolutionConfigurations solutionConfigurations;
+
+ solutionConfigurations = sln.SolutionBuild.SolutionConfigurations;
+
+ foreach (EnvDTE80.SolutionConfiguration2 solutionConfiguration2 in solutionConfigurations)
+ {
+ foreach (EnvDTE.SolutionContext solutionContext in solutionConfiguration2.SolutionContexts)
+ {
+ if (solutionContext.ProjectName == project.UniqueName)
+ {
+ solutionContext.ConfigurationName = configurationName;
+ }
+ }
+ }
+ }
+
+ private void ShowProjectOutputs()
+ {
+ foreach (Project project in dte.Solution.Projects)
+ {
+ Console.WriteLine("Project: " + project.Name);
+
+ var dir = System.IO.Path.Combine(
+ project.FullName,
+ project.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value.ToString());
+
+ foreach (Property prop in project.ConfigurationManager.ActiveConfiguration.Properties)
+ {
+ Console.WriteLine(" - " + prop.Name + " = " + prop.Value);
+ }
+
+ string outputFileName = null;
+
+ try
+ {
+ // and combine it with the OutputFilename to get the assembly
+ // or skip this and grab all files in the output directory
+ outputFileName = System.IO.Path.Combine(
+ dir,
+ project.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputFilename").Value.ToString());
+ }
+ catch (ArgumentException)
+ {
+ // projects in VS2008 do not seem to define this property, oh well
+ outputFileName = System.IO.Path.Combine(dir, "???.???");
+ }
+
+ Console.WriteLine(outputFileName);
+ }
+ }
+
+ private string GetProjectContexts()
+ {
+ System.Text.StringBuilder sb = new System.Text.StringBuilder();
+ EnvDTE80.Solution2 solution2;
+ EnvDTE80.SolutionBuild2 solutionBuild2;
+ EnvDTE.SolutionContexts solutionContexts;
+
+ solution2 = (EnvDTE80.Solution2)sln;
+ solutionBuild2 = (EnvDTE80.SolutionBuild2)solution2.SolutionBuild;
+
+ // Solution configurations/platforms
+ sb.AppendLine();
+ sb.AppendLine("-----------------------------------------------");
+ sb.AppendLine("Project contexts for each solution configuration/platform:");
+
+ foreach (SolutionConfiguration2 solutionConfiguration2 in solutionBuild2.SolutionConfigurations)
+ {
+ sb.AppendLine();
+
+ sb.AppendLine(" - Solution configuration: " + solutionConfiguration2.Name);
+ sb.AppendLine(" - Solution platform: " + solutionConfiguration2.PlatformName);
+
+ solutionContexts = solutionConfiguration2.SolutionContexts;
+
+ foreach (EnvDTE.SolutionContext solutionContext in solutionContexts)
+ {
+ sb.AppendLine();
+ sb.AppendLine(" Project unique name: " + solutionContext.ProjectName);
+ sb.AppendLine(" Project configuration: " + solutionContext.ConfigurationName);
+ sb.AppendLine(" Project platform: " + solutionContext.PlatformName);
+ sb.AppendLine();
+ }
+ }
+
+ return sb.ToString();
+ }
+
+ private void ChangeActiveConfigurationAndPlatform(string configurationName, string platformName)
+ {
+ EnvDTE80.SolutionConfiguration2 solutionConfiguration2 = null;
+
+ solutionConfiguration2 = (EnvDTE80.SolutionConfiguration2)sln.SolutionBuild.ActiveConfiguration;
+
+ Console.WriteLine("The old configuration was: Configuration Name: " + solutionConfiguration2.Name + ", Platform Name: " + solutionConfiguration2.PlatformName);
+
+ foreach (EnvDTE80.SolutionConfiguration2 solConfiguration2 in sln.SolutionBuild.SolutionConfigurations)
+ {
+ if (solConfiguration2.Name == configurationName && solConfiguration2.PlatformName == platformName)
+ {
+ solConfiguration2.Activate();
+ break;
+ }
+ }
+
+ solutionConfiguration2 = (EnvDTE80.SolutionConfiguration2)sln.SolutionBuild.ActiveConfiguration;
+
+ Console.WriteLine("The new configuration is: Configuration Name: " + solutionConfiguration2.Name + ", Platform Name: " + solutionConfiguration2.PlatformName);
+ }
+
+ public void ShowSolutionConfigurations()
+ {
+ EnvDTE.SolutionConfigurations solutionConfigurations;
+
+ solutionConfigurations = sln.SolutionBuild.SolutionConfigurations;
+
+ foreach (EnvDTE80.SolutionConfiguration2 solutionConfiguration2 in solutionConfigurations)
+ {
+ Console.WriteLine(" SolutionConfigurationName: " + solutionConfiguration2.Name);
+ foreach (EnvDTE.SolutionContext solutionContext in solutionConfiguration2.SolutionContexts)
+ {
+ Console.WriteLine(" SolutionConfigurationContext");
+ Console.WriteLine(" ProjectName = " + solutionContext.ProjectName); // will match project.UniqueName
+ Console.WriteLine(" ConfigurationName = " + solutionContext.ConfigurationName); // you can write this too
+ }
+ }
+ }
+
+ public bool OpenSolution(string solutionFile)
+ {
+ sln = dte.Solution;
+ sln.Open(solutionFile);
+ Console.WriteLine("sln.IsOpen = " + sln.IsOpen);
+ return sln.IsOpen;
+ }
+
+ public void CloseSolution()
+ {
+ sln.Close(false);
+ }
+
+ ///
+ /// Open Visual Studio 2008
+ ///
+ public void OpenVS()
+ {
+ Console.WriteLine("Getting Type of Visual Studio...");
+ Type type = Type.GetTypeFromProgID("VisualStudio.DTE.9.0");
+
+ Console.WriteLine("Opening Visual Studio...");
+ visualStudio = Activator.CreateInstance(type, true);
+
+ // See http://msdn.microsoft.com/en-us/library/ms228772.aspx
+ MessageFilter.Register();
+
+ Console.WriteLine("Casting to DTE...");
+ dte = (DTE)visualStudio;
+
+ Console.WriteLine("Casting to DTE2...");
+ dte2 = (DTE2)visualStudio;
+ }
+
+ ///
+ /// Close Visual Studio 2008
+ ///
+ public void CloseVS()
+ {
+ dte.Quit();
+
+ MessageFilter.Revoke();
+ }
+ }
+}
\ No newline at end of file
diff --git a/VsIdeBuild/VsBuilderOptions.cs b/VsIdeBuild/VsBuilderOptions.cs
new file mode 100644
index 0000000..5054770
--- /dev/null
+++ b/VsIdeBuild/VsBuilderOptions.cs
@@ -0,0 +1,73 @@
+#region License
+
+/*
+ * File: VsBuilderOptions.cs
+ *
+ * The MIT License
+ *
+ * Copyright © 2017 AVSP Ltd
+ *
+ * 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.
+ */
+
+#endregion License
+
+namespace VsIdeBuild.VsBuilderLibrary
+{
+ ///
+ /// Configuration options POCO for the VsBuilder build process
+ ///
+ public class VsBuilderOptions
+ {
+ ///
+ /// Show the VS gui in the opened project, allow user control
+ ///
+ public bool Debug { get; set; }
+
+ ///
+ /// Clean is only available for the entire solution
+ ///
+ public bool Clean { get; set; }
+
+ ///
+ /// Build Solution in every available configuration
+ ///
+ public bool BuildAll { get; set; }
+
+ ///
+ /// Limit build to a single solution configuration, e.g. Debug, Release
+ ///
+ public string BuildSolutionConfiguration { get; set; }
+
+ ///
+ /// Limit build to a single project - BuildSolutionConfiguration MUST also be specified
+ ///
+ public string BuildProject { get; set; }
+
+ ///
+ /// Output ProjectContexts, useful to get necessary command lines for build
+ ///
+ public bool ShowProjectContexts { get; set; }
+
+ ///
+ /// Filepath of Visual Studio 2008 solution file
+ ///
+ public string Solution { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/VsIdeBuild/VsBuilderResults.cs b/VsIdeBuild/VsBuilderResults.cs
new file mode 100644
index 0000000..1e326f3
--- /dev/null
+++ b/VsIdeBuild/VsBuilderResults.cs
@@ -0,0 +1,38 @@
+#region License
+
+/*
+ * File: Program.cs
+ *
+ * The MIT License
+ *
+ * Copyright © 2017 AVSP Ltd
+ *
+ * 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.
+ */
+
+#endregion License
+
+namespace VsIdeBuild.VsBuilderLibrary
+{
+ public class VsBuilderResults
+ {
+ public int TotalBuilds;
+ public bool Failed;
+ }
+}
\ No newline at end of file
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..1a159e2
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,82 @@
+# VsIdeBuild
+
+## About
+
+VsIdeBuild is a command line tool to automate the Visual Studio 2008 IDE to build solutions.
+This is necessary when GUI IDE plugins are required for the build process, for example when using Crestron's SDK.
+
+## Contact
+
+E-mail: chris@avsp.co.uk
+
+## Copyright
+
+VsIdeBuild is Copyright © 2017 AVSP Ltd
+
+## Command line parameters:
+
+~~~
+ -Solution
+ Specify VS sln file to work on
+
+ -Clean
+ Cleans before build.
+ This does not work with BuildProject - VS2008 only supports simple cleaning of enitre solution configurations
+
+ -BuildAll
+ Builds all solution configurations, using their project build settings for each configuration
+
+ -BuildSolutionConfiguration
+ Build a single solution configuration, e.g. Debug or Release
+
+ -BuildProject
+ Build a single project, also needs -BuildSolutionConfiguration to be specified
+
+ -ShowProjectContexts
+ Outputs all available project names and configurations
+
+ -Debug
+ Will not hide the Visual Studio window that is opened, will allow user interaction with that window
+~~~
+
+## Examples
+
+Build just the Debug configuration of project ConsoleApp in Sample.sln
+~~~
+VsIdeBuild -Solution "C:\Test\Sample.sln" -BuildSolutionConfiguration "Debug" -BuildProject "ConsoleApp"
+~~~
+
+Clean then build all configurations of all projects in Sample.sln
+~~~
+VsIdeBuild -Solution "C:\Test\Sample.sln" -Clean -BuildAll
+~~~
+
+## Return value
+
+0 on success
+
+>=1 on failure
+
+## License
+
+The MIT License
+
+Copyright © 2017 AVSP Ltd
+
+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.