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.