{"action":"created","comment":{"url":"https://api.github.com/repos/dotnet/buildtools/pulls/comments/35369280","id":35369280,"diff_hunk":"@@ -0,0 +1,1820 @@\n+﻿// Copyright (c) Microsoft. All rights reserved.\n+// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n+\n+using System;\n+using System.Diagnostics;\n+using System.Text;\n+using System.Collections.Generic;\n+using System.IO;\n+\n+using System.Reflection;\n+\n+namespace Microsoft.Fx.CommandLine\n+{\n+    // See code:#Overview to get started.\n+    /// <summary>\n+    /// #Overview\n+    /// \n+    /// The code:CommandLineParser is a utility for parsing command lines. Command lines consist of three basic\n+    /// entities. A command can have any (or none) of the following (separated by whitespace of any size).\n+    /// \n+    ///    * PARAMETERS - this are non-space strings. They are positional (logicaly they are numbered). Strings\n+    ///        with space can be specified by enclosing in double quotes.\n+    ///        \n+    ///    * QUALIFIERS - Qualifiers are name-value pairs. The following syntax is supported.\n+    ///        * -QUALIFIER\n+    ///        * -QUALIFIER:VALUE\n+    ///        * -QUALIFIER=VALUE\n+    ///        * -QUALIFIER VALUE\n+    ///        \n+    ///      The end of a value is delimited by space. Again values with spaces can be encoded by enclosing them\n+    ///      the value (or the whole qualifer-value string), in double quotes. The first form (where a value is\n+    ///      not specified is only available for boolean qualifiers, and boolean values can not use the form where\n+    ///      the qualifer and value are separated by space. The '/' character can also be used instead of the '-'\n+    ///      to begin a qualifier.\n+    ///      \n+    ///      Unlike parameters, qualifiers are NOT ordered. They may occur in any order with respect to the\n+    ///      parameters or other qualifiers and THAT ORDER IS NOT COMMUNICATED THROUGH THE PARSER. Thus it is not\n+    ///      possible to have qualifiers that only apply to specific parameters.\n+    ///      \n+    ///    * PARAMETER SET SPECIFIER - A parameter set is optional argument that looks like a boolean qualifier\n+    ///        (however if NoDashOnParameterSets is set the dash is not need, so it is looks like a parameter),\n+    ///        that is special in that it decides what qualifiers and positional parameters are allowed. See\n+    ///        code:#ParameterSets for more\n+    ///        \n+    /// #ParameterSets\n+    /// \n+    /// Parameter sets are an OPTIONAL facility of code:CommandLineParser that allow more complex command lines\n+    /// to be specified accurately. It not uncommon for a EXE to have several 'commands' that are logically\n+    /// independent of one another. For example a for example For example a program might have 'checkin'\n+    /// 'checkout' 'list' commands, and each of these commands has a different set of parameters that are needed\n+    /// and qualifiers that are allowed. (for example checkout will take a list of file names, list needs nothing,\n+    /// and checkin needs a comment). Additionally some qualifiers (like say -dataBaseName can apply to any of hte\n+    /// commands). Thus You would like to say that the following command lines are legal\n+    /// \n+    ///     * EXE -checkout MyFile1 MyFile -dataBaseName:MyDatabase\n+    ///     * EXE -dataBaseName:MyDatabase -list\n+    ///     * EXE -comment \"Specifying the comment first\" -checkin\n+    ///     * EXE -checkin -comment \"Specifying the comment afterward\"\n+    /// \n+    /// But the following are not\n+    /// \n+    ///     * EXE -checkout\n+    ///     * EXE -checkout -comment \"hello\"\n+    ///     * EXE -list MyFile\n+    /// \n+    /// You do this by specifying 'checkout', 'list' and 'checkin' as parameters sets. On the command line they\n+    /// look like boolean qualifiers, however they have additional sematantics. They must come before any\n+    /// positional parameters (because they affect whether the parameters are allowed and what they are named),\n+    /// and they are mutually exclusive. Each parameter set gets its own set of parameter definitions, and\n+    /// qualifiers can either be associated with a particular parameter set (like -comment) or global to all\n+    /// parameter sets (like -dataBaseName) .\n+    /// \n+    /// By default parameter set specifiers look like a boolean specifier (begin with a '-' or '/'), however\n+    /// because it is common practice to NOT have a dash for commands, there there is a Property\n+    /// code:CommandLineParser.NoDashOnParameterSets that indicates that the dash is not used. If this was\n+    /// specified then the following command would be legal.\n+    /// \n+    ///     * EXE checkout MyFile1 MyFile -dataBaseName:MyDatabase\n+    /// \n+    /// #DefaultParameterSet\n+    /// \n+    /// One parameters set (which has the empty string name), is special in that it is used when no other\n+    /// parameter set is matched. This is the default parameter set. For example, if -checkout was defined to be\n+    /// the default parameter set, then the following would be legal.\n+    /// \n+    ///     * EXE Myfile1 Myfile\n+    /// \n+    /// And would implicitly mean 'checkout' Myfile1, Myfile2\n+    /// \n+    /// If no parameter sets are defined, then all qualifiers and parameters are in the default parameter set.\n+    /// \n+    /// -------------------------------------------------------------------------\n+    /// #Syntatic ambiguities\n+    /// \n+    /// Because whitespace can separate a qualifier from its value AND Qualifier from each other, and because\n+    /// parameter values might start with a dash (and thus look like qualifiers), the syntax is ambiguous. It is\n+    /// disambigutated with the following rules.\n+    ///     * The command line is parsed into 'arguments' that are spearated by whitespace. Any string enclosed\n+    ///         in \"\" will be a single argument even if it has embedded whitespace. Double quote characters can\n+    ///         be specified by \\\" (and a \\\" literal can be specified by \\\\\" etc).\n+    ///     * Arguments are parsed into qualifiers. This parsing stops if a '--' argument is found. Thus all\n+    ///         qualifiers must come before any '--' argument but parameters that begin with - can be specified by\n+    ///         placing them after the '--' argument,\n+    ///     * Qualifiers are parsed. Because spaces can be used to separate a qualifier from its value, the type of\n+    ///         the qualifier must be known to parse it. Boolean values never consume an additional parameter, and\n+    ///         non-boolean qualifiers ALWAYS consume the next argument (if there is no : or =). If the empty\n+    ///         string is to be specified, it must use the ':' or '=' form. Moreover it is illegal for the values\n+    ///         that begin with '-' to use space as a separator. They must instead use the ':' or '=' form. This\n+    ///         is because it is too confusing for humans to parse (values look like qualifiers).\n+    ///      * Parameters are parsed. Whatever arguments that were not used by qualifiers are parameters.\n+    /// \n+    /// --------------------------------------------------------------------------------------------\n+    /// #DefiningParametersAndQualifiers\n+    /// \n+    /// The following example shows the steps for defining the parameters and qualifiers for the example. Note\n+    /// that the order is important. Qualifiers that apply to all commands must be specified first, then each\n+    /// parameter set then finally the default parameter set. Most steps are optional.\n+#if EXAMPLE1\n+        class CommandLineParserExample1\n+        {\n+            enum Command { checkout, checkin, list };\n+            static void Main()\n+            {\n+                string dataBaseName = \"myDefaultDataBase\";\n+                string comment = String.Empty;\n+                Command command = checkout;\n+                string[] fileNames = null;\n+\n+                // Step 1 define the parser. \n+                CommandLineParser commandLineParser = new CommandLineParser();      // by default uses Environment.CommandLine\n+\n+                // Step 2 (optional) define qualifiers that apply to all parameter sets. \n+                commandLineParser.DefineOptionalParameter(\"dataBaseName\", ref dataBaseName, \"Help for database.\");\n+\n+                // Step 3A define the checkin command this includes all parameters and qualifiers specific to this command\n+                commandLineParser.DefineParameterSet(\"checkin\", ref command, Command.checkin, \"Help for checkin.\");\n+                commandLineParser.DefineOptionalQualifiers(\"comment\", ref comment, \"Help for -comment.\");\n+\n+                // Step 3B define the list command this includes all parameters and qualifiers specific to this command\n+                commandLineParser.DefineParameterSet(\"list\", ref command, Command.list, \"Help for list.\");\n+\n+                // Step 4 (optional) define the default parameter set (in this case checkout). \n+                commandLineParser.DefineDefaultParameterSet(\"checkout\", ref command, Command.checkout, \"Help for checkout.\");\n+                commandLineParser.DefineParamter(\"fileNames\", ref fileNames, \"Help for fileNames.\");\n+\n+                // Step 5, do final validation (look for undefined qualifiers, extra parameters ...\n+                commandLineParser.CompleteValidation();\n+\n+                // Step 6 use the parsed values\n+                Console.WriteLine(\"Got {0} {1} {2} {3} {4}\", dataBaseName, command, comment, string.Join(',', fileNames));\n+            }\n+        }\n+#endif\n+    /// #RequiredAndOptional\n+    /// \n+    /// Parameters and qualifiers can be specified as required (the default), or optional. Makeing the default\n+    /// required was choses to make any mistakes 'obvious' since the parser will fail if a required parameter is\n+    /// not present (if the default was optional, it would be easy to make what should have been a required\n+    /// qualifier optional, leading to business logic failiure).\n+    /// \n+    /// #ParsedValues\n+    /// \n+    /// The class was designed maximize programmer convinience. For each parameter, only one call is needed to\n+    /// both define the parameter, its help message, and retrive its (strong typed) value. For example\n+    /// \n+    ///      * int count = 5;\n+    ///      * parser.DefineOptionalQualifier(\"count\", ref count, \"help for optional debugs qualifier\");\n+    /// \n+    /// Defines a qualifier 'count' and will place its value in the local variable 'count' as a integer. Default\n+    /// values are supported by doing nothing, so in the example above the default value will be 5.\n+    /// \n+    /// Types supported: The parser will support any type that has a static method called 'Parse' taking one\n+    /// string argument and returning that type. This is true for all primtive types, DateTime, Enumerations, and\n+    /// any user defined type that follows this convention.\n+    /// \n+    /// Array types: The parser has special knowedge of arrays. If the type of a qualifier is an array, then the\n+    /// string value is assumed to be a ',' separated list of strings which will be parsed as the element type of\n+    /// the array. In addition to the ',' syntax, it is also legal to specify the qualifier more than once. For\n+    /// example given the defintion\n+    /// \n+    ///      * int[] counts;\n+    ///      * parser.DefineOptionalQualifier(\"counts\", ref counts, \"help for optional counts qualifier\");\n+    ///      \n+    /// The command line\n+    /// \n+    ///     * EXE -counts 5 SomeArg -counts 6 -counts:7\n+    /// \n+    /// Is the same as\n+    /// \n+    ///     * EXE -counts:5,6,7 SomeArg\n+    ///      \n+    /// If a qualifier or parameter is an array type and is required, then the array must have at least one\n+    /// element. If it is optional, then the array can be empty (but in all cases, the array is created, thus\n+    /// null is never returned by the command line parser).\n+    /// \n+    /// By default is it is illegal for a non-array qualifier to be specified more than once. It is however\n+    /// possible to override this behavior by setting the LastQualifierWins property before defining the qualifier.\n+    /// \n+    /// -------------------------------------------------------------------------\n+    /// #Misc\n+    /// \n+    /// Qualifier can have more than one string form (typically a long and a short form). These are specified\n+    /// with the code:CommandLineParser.DefineAliases method.\n+    /// \n+    /// After defining all the qualifiers and parameters, it is necessary to call the parser to check for the user\n+    /// specifying a qualifier (or parameter) that does not exist. This is the purpose of the\n+    /// code:CommandLineParser.CompleteValidation method.\n+    /// \n+    /// When an error is detected at runtime an instance of code:CommandLineParserException is thrown. The error\n+    /// message in this exception was designed to be suitable to print to the user directly.\n+    /// \n+    /// #CommandLineHelp\n+    /// \n+    /// The parser also can generate help that is correct for the qualifier and parameter definitions. This can be\n+    /// accessed from the code:CommandLineParser.GetHelp method. It is also possible to get the help for just a\n+    /// particular Parameter set with code:CommandLineParser.GetHelpForParameterSet. This help includes command\n+    /// line syntax, whether the qualifier or parameter is optional or a list, the types of the qualifiers and\n+    /// parameters, the help text, and default values.   The help text comes from the 'Define' Methods, and is\n+    /// properly word-wrapped.  Newlines in the help text indicate new paragraphs.   \n+    /// \n+    /// #AutomaticExceptionProcessingAndHelp\n+    /// \n+    /// In the CommandLineParserExample1, while the command line parser did alot of the work there is still work\n+    /// needed to make the application user friendly that pretty much all applications need. These include\n+    ///  \n+    ///     * Call the code:CommandLineParser constructor and code:CommandLineParser.CompleteValidation\n+    ///     * Catch any code:CommandLineParserException and print a friendly message\n+    ///     * Define a -? qualifier and wire it up to print the help.\n+    /// \n+    /// Since this is stuff that all applications will likely need the\n+    /// code:CommandLineParser.ParseForConsoleApplication was created to do all of this for you, thus making it\n+    /// super-easy to make a production quality parser (and concentrate on getting your application logic instead\n+    /// of command line parsing. Here is an example which defines a 'Ping' command. If you will notice there are\n+    /// very few lines of code that are not expressing something very specific to this applications. This is how\n+    /// it should be!\n+#if EXAMPLE2\n+        class CommandLineParserExample2\n+        {\n+            static void Main()\n+            {\n+\t            // Step 1: Initialize to the defaults\n+\t            string Host = null;       \n+\t            int Timeout = 1000;\n+\t            bool Forever = false;\n+\n+\t            // Step 2: Define the parameters, in this case there is only the default parameter set. \n+\t            CommandLineParser.ParseForConsoleApplication(args, delegate(CommandLineParser parser)\n+\t            {\n+\t                parser.DefineOptionalQualifier(\"Timeout\", ref Timeout, \"Timeout in milliseconds to wait for each reply.\");\n+\t                parser.DefineOptionalQualifier(\"Forever\", ref Forever, \"Ping forever.\");\n+\t                parser.DefineDefaultParameterSet(\"Ping sends a network request to a host to reply to the message (to check for liveness).\");\n+\t                parser.DefineParameter(\"Host\", ref Host, \"The Host to send a ping message to.\");\n+\t            });\n+\n+\t            // Step 3, use the parameters\n+\t            Console.WriteLine(\"Got {0} {1} {2} {3}\", Host, Timeout, Forever);\n+            }\n+    }\n+#endif\n+    /// Using local variables for the parsed arguments if fine when the program is not complex and the values\n+    /// don't need to be passed around to many routines.  In general, however it is often a better idea to\n+    /// create a class whose sole purpose is to act as a repository for the parsed arguments.   This also nicely\n+    /// separates all command line processing into a single class.   This is how the ping example would look  in\n+    /// that style. Notice that the main program no longer holds any command line processing logic.  and that\n+    /// 'commandLine' can be passed to other routines in bulk easily.  \n+#if EXAMPLE3\n+class CommandLineParserExample3\n+{\n+    static void Main()\n+    {\n+        CommandLine commandLine = new CommandLine();\n+        Console.WriteLine(\"Got {0} {1} {2} {3}\", commandLine.Host, commandLine.Timeout, commandLine.Forever);\n+    }\n+}\n+class CommandLine\n+{\n+    public CommandLine()\n+    {\n+        CommandLineParser.ParseForConsoleApplication(args, delegate(CommandLineParser parser)\n+        {\n+            parser.DefineOptionalQualifier(\"Timeout\", ref Timeout, \"Timeout in milliseconds to wait for each reply.\");\n+            parser.DefineOptionalQualifier(\"Forever\", ref Forever, \"Ping forever.\");\n+            parser.DefineDefaultParameterSet(\"Ping sends a network request to a host to reply to the message (to check for liveness).\");\n+            parser.DefineParameter(\"Host\", ref Host, \"The Host to send a ping message to.\");\n+        });\n+    }\n+    public string Host = null;\n+    public int Timeout = 1000;\n+    public bool Forever = false;\n+};\n+#endif\n+    /// <summary>\n+    /// see code:#Overview for more \n+    /// </summary>\n+    public class CommandLineParser\n+    {\n+        /// <summary>\n+        /// If you are building a console Application, there is a common structure to parsing arguments. You want\n+        /// the text formated and output for console windows, and you want /? to be wired up to do this. All\n+        /// errors should be caught and displayed in a nice way.  This routine does this 'boiler plate'.  \n+        /// </summary>\n+        /// <param name=\"parseBody\">parseBody is the body of the parsing that this outer shell does not provide.\n+        /// in this delegate, you should be defining all the command line parameters using calls to Define* methods.\n+        ///  </param>\n+        public static bool ParseForConsoleApplication(Action<CommandLineParser> parseBody, string commandLine)\n+        {\n+            return Parse(parseBody, parser =>\n+            {\n+                var parameterSetTofocusOn = parser.HelpRequested;\n+                if (parameterSetTofocusOn.Length == 0)\n+                {\n+                    parameterSetTofocusOn = null;\n+                }\n+\n+                string helpString = parser.GetHelp(GetConsoleWidth() - 1, parameterSetTofocusOn, true);\n+                DisplayStringToConsole(helpString);\n+            }, (parser, ex) =>\n+            {\n+                Console.WriteLine(\"Error: \" + ex.Message);\n+                Console.WriteLine(\"Use -? for help.\");\n+            },\n+            commandLine);\n+        }\n+\n+        public static bool Parse(Action<CommandLineParser> parseBody, Action<CommandLineParser> helpHandler, Action<CommandLineParser, Exception> errorHandler, string commandLine)\n+        {\n+            var help = false;\n+            CommandLineParser parser = new CommandLineParser(commandLine);\n+            parser.DefineOptionalQualifier(\"?\", ref help, \"Print this help guide.\");\n+\n+            try\n+            {\n+                // RawPrint the help. \n+                if (parser.HelpRequested != null)\n+                {\n+                    parser._skipParameterSets = true;\n+                    parser._skipDefinitions = true;\n+                }\n+                parseBody(parser);\n+                if (parser.HelpRequested != null)\n+                {\n+                    helpHandler(parser);\n+                    return false;\n+                }\n+                parser.CompleteValidation();\n+                return true;\n+            }\n+            catch (CommandLineParserException e)\n+            {\n+                errorHandler(parser, e);\n+                return false;\n+            }\n+        }\n+\n+        /// <summary>\n+        /// Qualifiers are command line parameters of the form -NAME:VALUE where NAME is an alphanumeric name and\n+        /// VALUE is a string. The parser also accepts -NAME: VALUE and -NAME VALUE but not -NAME : VALUE For\n+        /// boolan parameters, the VALUE can be dropped (which means true), and a empty string VALUE means false.\n+        /// Thus -NAME means the same as -NAME:true and -NAME: means the same as -NAME:false (and boolean\n+        /// qualifiers DONT allow -NAME true or -NAME false).\n+        /// \n+        /// The types that are supported are any type that has a static 'Parse' function that takes a string\n+        /// (this includes all primitive types as well as DateTime, and Enumerations, as well as arrays of\n+        /// parsable types (values are comma separated without space).\n+        /// \n+        /// Qualifiers that are defined BEFORE any parameter sets apply to ALL parameter sets.  qualifiers that\n+        /// are defined AFTER a parameter set will apply only the the parameter set that preceeds them.  \n+        /// \n+        /// See code:#DefiningParametersAndQualifiers\n+        /// See code:#Overview \n+        /// <param name=\"name\">The name of the qualifier.</param>\n+        /// <param name=\"retVal\">The place to put the parsed value</param>\n+        /// <param name=\"helpText\">Text to print for this qualifier.  It will be word-wrapped.  Newlines indicate\n+        /// new paragraphs.</param>\n+        /// </summary>\n+        public void DefineOptionalQualifier<T>(string name, ref T retVal, string helpText)\n+        {\n+            object obj = DefineQualifier(name, typeof(T), retVal, helpText, false);\n+            if (obj != null)\n+                retVal = (T)obj;\n+        }\n+        /// <summary>\n+        /// Like code:DeclareOptionalQualifier except it is an error if this parameter is not on the command line. \n+        /// <param name=\"name\">The name of the qualifer.</param>\n+        /// <param name=\"retVal\">The place to put the parsed value</param>\n+        /// <param name=\"helpText\">Text to print for this qualifer.  It will be word-wrapped.  Newlines indicate\n+        /// new paragraphs.</param> \n+        /// </summary>\n+        public void DefineQualifier<T>(string name, ref T retVal, string helpText)\n+        {\n+            object obj = DefineQualifier(name, typeof(T), retVal, helpText, true);\n+            if (obj != null)\n+                retVal = (T)obj;\n+        }\n+        /// <summary>\n+        /// Specify additional aliases for an qualifier.  This call must come BEFORE the call to\n+        /// Define*Qualifier, since the definition needs to know about its aliases to do its job.  \n+        /// </summary>\n+        public void DefineAliases(string officalName, params string[] alaises)\n+        {\n+            // TODO assert that aliases are defined before the Definition.  \n+            // TODO confirm no ambiguities (same alias used again).  \n+            if (_aliasDefinitions != null && _aliasDefinitions.ContainsKey(officalName))\n+                throw new CommandLineParserDesignerException(\"Named parameter \" + officalName + \" already has been given aliases.\");\n+\n+            if (_aliasDefinitions == null)\n+                _aliasDefinitions = new Dictionary<string, string[]>();\n+            _aliasDefinitions.Add(officalName, alaises);\n+        }\n+\n+        /// <summary>\n+        /// DefineParameter declares an unnamed mandatory parameter (basically any parameter that is not a\n+        /// qualifier). These are given ordinal numbers (starting at 0). You should declare the parameter in the\n+        /// desired order.\n+        /// \n+        /// \n+        /// See code:#DefiningParametersAndQualifiers\n+        /// See code:#Overview \n+        /// <param name=\"name\">The name of the parameter.</param>\n+        /// <param name=\"retVal\">The place to put the parsed value</param>\n+        /// <param name=\"helpText\">Text to print for this qualifer.  It will be word-wrapped.  Newlines indicate\n+        /// new paragraphs.</param> \n+        /// </summary>\n+        public void DefineParameter<T>(string name, ref T retVal, string helpText)\n+        {\n+            object obj = DefineParameter(name, typeof(T), retVal, helpText, true);\n+            if (obj != null)\n+                retVal = (T)obj;\n+        }\n+        /// <summary>\n+        /// Like code:DeclareParameter except the parameter is optional. \n+        /// These must come after non-optional (required) parameters. \n+        /// </summary>\n+        /// <param name=\"name\">The name of the parameter</param>\n+        /// <param name=\"retVal\">The location to place the parsed value.</param>\n+        /// <param name=\"helpText\">Text to print for this qualifer.  It will be word-wrapped.  Newlines indicate\n+        /// new paragraphs.</param> \n+        public void DefineOptionalParameter<T>(string name, ref T retVal, string helpText)\n+        {\n+            object obj = DefineParameter(name, typeof(T), retVal, helpText, false);\n+            if (obj != null)\n+                retVal = (T)obj;\n+        }\n+\n+        /// <summary>\n+        /// A parameter set defines on of a set of 'commands' that decides how to parse the rest of the command\n+        /// line.   If this 'command' is present on the command line then 'val' is assigned to 'retVal'. \n+        /// Typically 'retVal' is a variable of a enumerated type (one for each command), and 'val' is one\n+        /// specific value of that enumeration.  \n+        /// \n+        /// * See code:#ParameterSets\n+        /// * See code:#DefiningParametersAndQualifiers\n+        /// * See code:#Overview \n+        /// <param name=\"name\">The name of the parameter set.</param>\n+        /// <param name=\"retVal\">The place to put the parsed value</param> \n+        /// <param name=\"val\">The value to place into 'retVal' if this parameter set is indicated</param>\n+        /// <param name=\"helpText\">Text to print for this qualifer.  It will be word-wrapped.  Newlines indicate\n+        /// new paragraphs.</param> \n+        /// </summary>\n+        public void DefineParameterSet<T>(string name, ref T retVal, T val, string helpText)\n+        {\n+            if (DefineParameterSet(name, helpText))\n+                retVal = val;\n+        }\n+        /// <summary>\n+        /// There is one special parameter set called the default parameter set (whose names is empty) which is\n+        /// used when a command line does not have one of defined parameter sets. It is always present, even if\n+        /// this method is not called, so calling this method is optional, however, by calling this method you can\n+        /// add help text for this case.  If present this call must be AFTER all other parameter set\n+        /// definitions. \n+        /// \n+        /// * See code:#DefaultParameterSet \n+        /// * See code:#DefiningParametersAndQualifiers\n+        /// * See code:#Overview\n+        /// <param name=\"helpText\">Text to print for this qualifer.  It will be word-wrapped.  Newlines indicate\n+        /// new paragraphs.</param> \n+        /// </summary>\n+        public void DefineDefaultParameterSet(string helpText)\n+        {\n+            DefineParameterSet(String.Empty, helpText);\n+        }\n+        /// <summary>\n+        /// This variation of DefineDefaultParameterSet has a 'retVal' and 'val' parameters.  If the command\n+        /// line does not match any of the other parameter set defintions, then 'val' is asigned to 'retVal'. \n+        /// Typically 'retVal' is a variable of a enumerated type and 'val' is a value of that type.    \n+        /// \n+        /// * See code:DefineDefaultParameterSet for more.\n+        /// <param name=\"retVal\">The place to put the parsed value</param> \n+        /// <param name=\"val\">The value to place into 'retVal' if this parameter set is indicated</param>\n+        /// <param name=\"helpText\">Text to print for this qualifer.  It will be word-wrapped.  Newlines indicate\n+        /// new paragraphs.</param> \n+        /// </summary>\n+        public void DefineDefaultParameterSet<T>(ref T retVal, T val, string helpText)\n+        {\n+            if (DefineParameterSet(String.Empty, helpText))\n+                retVal = val;\n+        }\n+\n+        // You can influence details of command line parsing by setting the following properties.  \n+        // These should be set before the first call to Define* routines and should not change \n+        // thereafter.  \n+        /// <summary>\n+        /// By default parameter set specifiers must look like a qualifier (begin with a -), however setting\n+        /// code:NoDashOnParameterSets will define a parameter set marker not to have any special prefix (just\n+        /// the name itself.  \n+        /// </summary>\n+        public bool NoDashOnParameterSets\n+        {\n+            get { return _noDashOnParameterSets; }\n+            set\n+            {\n+                if (_noDashOnParameterSets != value)\n+                    ThrowIfNotFirst(\"NoDashOnParameterSets\");\n+                _noDashOnParameterSets = value;\n+            }\n+        }\n+        /// <summary>\n+        /// By default, the syntax (-Qualifier:Value) and (-Qualifer Value) are both allowed.   However\n+        /// this makes it impossible to use -Qualifier to specify that a qualifier is present but uses\n+        /// a default value (you have to use (-Qualifier: )) Specifying code:NoSpaceOnQualifierValues\n+        /// indicates that the syntax (-Qualifer ObjectEnumerator) is not allowed, which allows this.  \n+        /// </summary>\n+        /// TODO decide if we should keep this...\n+        public bool NoSpaceOnQualifierValues\n+        {\n+            get { return _noSpaceOnQualifierValues; }\n+            set\n+            {\n+                if (_noSpaceOnQualifierValues != value)\n+                    ThrowIfNotFirst(\"NoSpaceOnQualifierValues\");\n+                _noSpaceOnQualifierValues = value;\n+            }\n+        }\n+        /// <summary>\n+        /// If the positional parameters might look like named parameters (typically happens when the tail of the\n+        /// command line is literal text), it is useful to stop the search for named parameters at the first\n+        /// positional parameter. \n+        /// \n+        /// Because some parameters sets might want this behavior and some might not, you specify the list of\n+        /// parameter sets that you want to opt in.\n+        /// \n+        /// The expectation is you do something like\n+        ///     * commandLine.ParameterSetsWhereQualifiersMustBeFirst = new string[] { \"parameterSet1\" };\n+        ///     \n+        /// The empty string is a wildcard that indicats all parameter sets have the qualifiersMustBeFirst \n+        /// attribute.   This is the only way to get this attribute on the default parameter set.  \n+        /// </summary>\n+        public string[] ParameterSetsWhereQualifiersMustBeFirst\n+        {\n+            get { return _parameterSetsWhereQualifiersMustBeFirst; }\n+            set\n+            {\n+                ThrowIfNotFirst(\"ParameterSetsWhereQualifiersMustBeFirst\");\n+                NoSpaceOnQualifierValues = true;\n+                _parameterSetsWhereQualifiersMustBeFirst = value;\n+            }\n+        }\n+        /// <summary>\n+        /// By default qualifiers may being with a - or a / character.   Setting code:QualifiersUseOnlyDash will\n+        /// make / invalid qualifier marker (only - can be used)\n+        /// </summary>\n+        public bool QualifiersUseOnlyDash\n+        {\n+            get { return _qualifiersUseOnlyDash; }\n+            set\n+            {\n+                if (_qualifiersUseOnlyDash != value)\n+                    ThrowIfNotFirst(\"OnlyDashForQualifiers\");\n+                _qualifiersUseOnlyDash = value;\n+            }\n+        }\n+\n+\n+        // TODO remove?   Not clear it is useful.  Can be useful for CMD.EXE alias (which provide a default) but later user may override.  \n+        /// <summary>\n+        /// By default, a non-list qualifier can not be specified more than once (since one or the other will\n+        /// have to be ignored).  Normally an error is thrown.  Setting code:LastQualifierWins makes it legal, and\n+        /// the last qualifier is the one that is used.  \n+        /// </summary>\n+        public bool LastQualifierWins\n+        {\n+            get { return _lastQualifierWins; }\n+            set { _lastQualifierWins = value; }\n+        }\n+\n+        // These routines are typically are not needed because ParseArgsForConsoleApp does the work.  \n+#if !COREFX\n+        public CommandLineParser() : this(Environment.CommandLine) { }\n+#endif\n+        public CommandLineParser(string commandLine)\n+        {\n+            ParseWords(commandLine);\n+        }\n+        public CommandLineParser(string[] args)\n+        {\n+            _args = new List<string>(args);\n+        }\n+\n+        /// <summary>\n+        /// Check for any parameters that the user specified but that were not defined by a Define*Parameter call\n+        /// and throw an exception if any are found. \n+        /// \n+        /// Returns true if validation was completed.  It can return false (rather than throwing), If the user\n+        /// requested help (/?).   Thus if this routine returns false, the 'GetHelp' should be called.\n+        /// </summary>\n+        public bool CompleteValidation()\n+        {\n+            // Check if there are any undefined parameters or ones specified twice.  \n+            foreach (int encodedPos in _dashedParameterEncodedPositions.Values)\n+            {\n+                throw new CommandLineParserException(\"Unexpected qualifier: \" + _args[GetPosition(encodedPos)] + \".\");\n+            }\n+\n+            // Find any 'unused' parameters;\n+            while (_curPosition < _args.Count && _args[_curPosition] == null)\n+                _curPosition++;\n+\n+            if (_curPosition < _args.Count)\n+                throw new CommandLineParserException(\"Extra positional parameter: \" + _args[_curPosition] + \".\");\n+\n+            // TODO we should null out data structures we no longer need, to save space. \n+            // Not critical because in the common case, the parser as a whole becomes dead.\n+\n+            if (_helpRequestedFor != null)\n+                return false;\n+\n+            if (!_defaultParamSetEncountered)\n+            {\n+                if (_paramSetEncountered && _parameterSetName == null)\n+                {\n+                    if (_noDashOnParameterSets && _curPosition < _args.Count)\n+                        throw new CommandLineParserException(\"Unrecognised command: \" + _args[_curPosition]);\n+                    else\n+                        throw new CommandLineParserException(\"No command given.\");\n+                }\n+            }\n+            return true;\n+        }\n+\n+        /// <summary>\n+        /// Return the string representing the help for a single paramter set.  If displayGlobalQualifiers is\n+        /// true than qualifiers that apply to all parameter sets is also included, otherwise it is just the\n+        /// parameters and qualifiers that are specific to that parameters set. \n+        /// \n+        /// If the parameterSetName null, then the help for the entire program (all parameter\n+        /// sets) is returned.  If parameterSetName is not null (empty string is default parameter set),\n+        /// then it generates help for the parameter set specified on the command line.\n+        /// \n+        /// Since most of the time you don't need help, helpInformatin is NOT collected during the Define* calls\n+        /// unless HelpRequested is true.   If /? is seen on the command line first, then this works.  You can\n+        /// also force this by setting HelpRequested to True before calling all the Define* APIs. \n+        /// \n+        /// </summary>\n+        public string GetHelp(int maxLineWidth, string parameterSetName = null, bool displayGlobalQualifiers = true)\n+        {\n+            Debug.Assert(_mustParseHelpStrings);\n+            if (!_mustParseHelpStrings)          // Backup for retail code.  \n+                return null;\n+\n+            if (parameterSetName == null)\n+                return GetFullHelp(maxLineWidth);\n+\n+            // Find the beginning of the parameter set parameters, as well as the end of the global parameters\n+            // (Which come before any parameters set). \n+            int parameterSetBody = 0;         // Points at body of the parameter set (parameters after the parameter set)\n+            CommandLineParameter parameterSetParameter = null;\n+            for (int i = 0; i < _parameterDescriptions.Count; i++)\n+            {\n+                CommandLineParameter parameter = _parameterDescriptions[i];\n+                if (parameter.IsParameterSet)\n+                {\n+                    parameterSetParameter = parameter;\n+                    if (string.Compare(parameterSetParameter.Name, parameterSetName, StringComparison.OrdinalIgnoreCase) == 0)\n+                    {\n+                        parameterSetBody = i + 1;\n+                        break;\n+                    }\n+                }\n+            }\n+\n+            if (parameterSetBody == 0 && parameterSetName != String.Empty)\n+                return String.Empty;\n+\n+            // At this point parameterSetBody and globalParametersEnd are properly set. Start generating strings\n+            StringBuilder sb = new StringBuilder();\n+\n+            // Create the 'Usage' line;\n+            // TODO: Find a better way to obtain this information.\n+#if COREFX\n+            string appName = \"<AppName>\";\n+\n+#else\n+            string appName = Path.GetFileNameWithoutExtension(System.Reflection.Assembly.GetEntryAssembly().ManifestModule.Name);\n+#endif\n+            sb.Append(\"Usage: \").Append(appName);\n+            if (parameterSetName.Length > 0)\n+            {\n+                sb.Append(' ');\n+                sb.Append(parameterSetParameter.Syntax(false, false));\n+            }\n+\n+            bool hasQualifiers = false;\n+            bool hasParameters = false;\n+            for (int i = parameterSetBody; i < _parameterDescriptions.Count; i++)\n+            {\n+                CommandLineParameter parameter = _parameterDescriptions[i];\n+                if (parameter.IsParameterSet)\n+                    break;\n+\n+                if (parameter.IsPositional)\n+                {\n+                    hasParameters = true;\n+                    sb.Append(' ').Append(parameter.Syntax(false, false));\n+                }\n+                else\n+                    hasQualifiers = true;\n+            }\n+            sb.AppendLine();\n+\n+            // Print the help for the command itself.  \n+            if (parameterSetParameter != null && !string.IsNullOrEmpty(parameterSetParameter.HelpText))\n+            {\n+                sb.AppendLine();\n+                if (parameterSetParameter != null && parameterSetParameter.HelpText != null)\n+                    Wrap(sb.Append(\"  \"), parameterSetParameter.HelpText, 2, \"  \", maxLineWidth);\n+            }\n+\n+            if (hasParameters)\n+            {\n+                sb.AppendLine().Append(\"  Parameters:\").AppendLine();\n+                for (int i = parameterSetBody; i < _parameterDescriptions.Count; i++)\n+                {\n+                    CommandLineParameter parameter = _parameterDescriptions[i];\n+                    if (parameter.IsParameterSet)\n+                        break;\n+                    if (parameter.IsPositional)\n+                        ParameterHelp(parameter, sb, QualifierSyntaxWidth, maxLineWidth);\n+                }\n+            }\n+\n+            string globalQualifiers = null;\n+            if (displayGlobalQualifiers)\n+                globalQualifiers = GetHelpGlobalQualifiers(maxLineWidth);\n+\n+            if (hasQualifiers || !string.IsNullOrEmpty(globalQualifiers))\n+            {\n+                sb.AppendLine().Append(\"  Qualifiers:\").AppendLine();\n+                for (int i = parameterSetBody; i < _parameterDescriptions.Count; i++)\n+                {\n+                    CommandLineParameter parameter = _parameterDescriptions[i];\n+                    if (parameter.IsParameterSet)\n+                        break;\n+                    if (parameter.IsNamed)\n+                        ParameterHelp(parameter, sb, QualifierSyntaxWidth, maxLineWidth);\n+                }\n+                if (globalQualifiers != null)\n+                    sb.Append(globalQualifiers);\n+            }\n+\n+            return sb.ToString();\n+        }\n+        /// <summary>\n+        /// Returns non-null if the user specified /? on the command line.   returns the word after /? (which may be the empty string)\n+        /// </summary>\n+        public string HelpRequested\n+        {\n+            get { return _helpRequestedFor; }\n+            set { _helpRequestedFor = value; _mustParseHelpStrings = true; }\n+        }\n+\n+\n+        #region private\n+        /// <summary>\n+        /// CommandLineParameter contains the 'full' information for a parameter or qualifier used for generating help.\n+        /// Most of the time we don't actualy generate instances of this class.  (see mustParseHelpStrings)\n+        /// </summary>\n+        private class CommandLineParameter\n+        {\n+            public string Name { get { return _name; } }\n+            public Type Type { get { return _type; } }\n+            public object DefaultValue { get { return _defaultValue; } }\n+            public bool IsRequired { get { return _isRequired; } }\n+            public bool IsPositional { get { return _isPositional; } }\n+            public bool IsNamed { get { return !_isPositional; } }\n+            public bool IsParameterSet { get { return _isParameterSet; } }\n+            public string HelpText { get { return _helpText; } }\n+            public override string ToString()\n+            {\n+                return \"<CommandLineParameter \" +\n+                    \"Name=\\\"\" + _name + \"\\\" \" +\n+                    \"Type=\\\"\" + _type + \"\\\" \" +\n+                    \"IsRequired=\\\"\" + IsRequired + \"\\\" \" +\n+                    \"IsPositional=\\\"\" + IsPositional + \"\\\" \" +\n+                    \"HelpText=\\\"\" + HelpText + \"\\\"/>\";\n+            }\n+            public string Syntax(bool printType, bool printDefaultValue)\n+            {\n+                string ret = _name;\n+                if (IsNamed)\n+                    ret = \"-\" + ret;\n+                if (printType)\n+                {\n+                    // We print out arrays with the ... notiation, so we don't want the [] when we display the type\n+                    Type displayType = Type;\n+                    if (displayType.IsArray)\n+                        displayType = displayType.GetElementType();\n+                    if (displayType.GetTypeInfo().IsGenericType && displayType.GetGenericTypeDefinition() == typeof(Nullable<>))\n+                        displayType = displayType.GetGenericArguments()[0];\n+\n+                    bool shouldPrint = true;\n+                    // Bool type implied on named parameters\n+                    if (IsNamed && _type == typeof(bool))\n+                    {\n+                        shouldPrint = false;\n+                        if (_defaultValue != null && (bool)_defaultValue)\n+                            shouldPrint = false;\n+                    }\n+                    // string type is implied on positional parameters\n+                    if (IsPositional && displayType == typeof(string) && string.IsNullOrEmpty(_defaultValue as string))\n+                        shouldPrint = false;\n+\n+                    if (shouldPrint)\n+                    {\n+                        ret = ret + (IsNamed ? \":\" : \".\") + displayType.Name.ToUpper();\n+                        if (printDefaultValue && _defaultValue != null)\n+                        {\n+                            string defValue = _defaultValue.ToString();\n+                            if (defValue.Length < 40)   // TODO is this reasonable?\n+                                ret += \"(\" + defValue + \")\";\n+                        }\n+                    }\n+                }\n+\n+                if (Type.IsArray)\n+                    ret = ret + (IsNamed ? \",\" : \" \") + \"...\";\n+\n+                if (!IsRequired)\n+                    ret = \"[\" + ret + \"]\";\n+                return ret;\n+            }\n+\n+            #region private\n+            internal CommandLineParameter(string Name, object defaultValue, string helpText, Type type,\n+                bool isRequired, bool isPositional, bool isParameterSet)\n+            {\n+                _name = Name;\n+                _defaultValue = defaultValue;\n+                _type = type;\n+                _helpText = helpText;\n+                _isRequired = isRequired;\n+                _isPositional = isPositional;\n+                _isParameterSet = isParameterSet;\n+            }\n+\n+            private string _name;\n+            private object _defaultValue;\n+            private string _helpText;\n+            private Type _type;\n+            private bool _isRequired;\n+            private bool _isPositional;\n+            private bool _isParameterSet;\n+            #endregion\n+        }\n+\n+        private void ThrowIfNotFirst(string propertyName)\n+        {\n+            if (_qualiferEncountered || _positionalArgEncountered || _paramSetEncountered)\n+                throw new CommandLineParserDesignerException(\"The property \" + propertyName + \" can only be set before any calls to Define* Methods.\");\n+        }\n+        private static bool IsDash(char c)\n+        {\n+            // can be any of ASCII dash (0x002d), endash (0x2013), emdash (0x2014) or hori-zontal bar (0x2015).\n+            return c == '-' || ('\\x2013' <= c && c <= '\\x2015');\n+        }\n+\n+        /// <summary>\n+        /// Returns true if 'arg' is a qualifier begins with / or -\n+        /// </summary>\n+        private bool IsQualifier(string arg)\n+        {\n+            if (arg.Length < 1)\n+                return false;\n+            if (IsDash(arg[0]))\n+                return true;\n+            if (!_qualifiersUseOnlyDash && arg[0] == '/')\n+                return true;\n+            return false;\n+        }\n+\n+        /// <summary>\n+        /// Qualifiers have the syntax -/NAME=Value.  This returns the NAME\n+        /// </summary>\n+        private string ParseParameterName(string arg)\n+        {\n+            string ret = null;\n+            if (arg != null && IsQualifier(arg))\n+            {\n+                int endName = arg.IndexOfAny(s_separators);\n+                if (endName < 0)\n+                    endName = arg.Length;\n+                ret = arg.Substring(1, endName - 1);\n+            }\n+            return ret;\n+        }\n+\n+\n+        /// <summary>\n+        /// Parses 'commandLine' into words (space separated items).  Handles quoting (using double quotes)\n+        /// and handles escapes of double quotes and backslashes with the \\\" and \\\\ syntax.  \n+        /// In theory this mimics the behavior of the parsing done before Main to parse the command line into\n+        /// the string[].  \n+        /// </summary>\n+        /// <param name=\"commandLine\"></param>\n+        private void ParseWords(string commandLine)\n+        {\n+            // TODO review this carefully.\n+            _args = new List<string>();\n+            int wordStartIndex = -1;\n+            bool hasExcapedQuotes = false;\n+            bool isResponseFile = false;\n+            int numQuotes = 0;\n+\n+            // Loop to <=length to reuse the same logic for AddWord on spaces and the end of the string\n+            for (int i = 0; i <= commandLine.Length; i++)\n+            {\n+                char c = (i < commandLine.Length ? commandLine[i] : '\\0');\n+                if (c == '\"')\n+                {\n+                    numQuotes++;\n+                    if (wordStartIndex < 0)\n+                        wordStartIndex = i;\n+                    i++;\n+                    for (;;)\n+                    {\n+                        if (i >= commandLine.Length)\n+                            throw new CommandLineParserException(\"Unmatched quote at position \" + i + \".\");\n+                        c = commandLine[i];\n+                        if (c == '\"')\n+                        {\n+                            if (i > 0 && commandLine[i - 1] == '\\\\')\n+                                hasExcapedQuotes = true;\n+                            else\n+                                break;\n+                        }\n+                        i++;\n+                    }\n+                }\n+                else if (c == ' ' || c == '\\t' || c == '\\0')\n+                {\n+                    if (wordStartIndex >= 0)\n+                    {\n+                        AddWord(ref commandLine, wordStartIndex, i, numQuotes, hasExcapedQuotes, isResponseFile);\n+                        wordStartIndex = -1;\n+                        hasExcapedQuotes = false;\n+                        numQuotes = 0;\n+                        isResponseFile = false;\n+                    }\n+                }\n+                else if (c == '@')\n+                {\n+                    isResponseFile = true;\n+                }\n+                else\n+                {\n+                    if (wordStartIndex < 0)\n+                        wordStartIndex = i;\n+                }\n+            }\n+        }\n+\n+        private void AddWord(ref string commandLine, int wordStartIndex, int wordEndIndex, int numQuotes, bool hasExcapedQuotes, bool isResponseFile)\n+        {\n+            if (!_seenExeArg)\n+            {\n+                _seenExeArg = true;\n+                return;\n+            }\n+            string word;\n+            if (numQuotes > 0)\n+            {\n+                // Common case, the whole word is quoted, and no escaping happened.   \n+                if (!hasExcapedQuotes && numQuotes == 1 && commandLine[wordStartIndex] == '\"' && commandLine[wordEndIndex - 1] == '\"')\n+                    word = commandLine.Substring(wordStartIndex + 1, wordEndIndex - wordStartIndex - 2);\n+                else\n+                {\n+                    // Remove \"\" (except for quoted quotes!)\n+                    StringBuilder sb = new StringBuilder();\n+                    for (int i = wordStartIndex; i < wordEndIndex;)\n+                    {\n+                        char c = commandLine[i++];\n+                        if (c != '\"')\n+                        {\n+                            if (c == '\\\\' && i < wordEndIndex && commandLine[i] == '\"')\n+                            {\n+                                sb.Append('\"');\n+                                i++;\n+                            }\n+                            else\n+                                sb.Append(c);\n+                        }\n+                    }\n+                    word = sb.ToString();\n+                }\n+            }\n+            else\n+                word = commandLine.Substring(wordStartIndex, wordEndIndex - wordStartIndex);\n+\n+            if (isResponseFile)\n+            {\n+                string responseFile = word;\n+\n+                if (!File.Exists(responseFile))\n+                    throw new CommandLineParserException(\"Response file '\" + responseFile + \"' does not exist!\");\n+\n+                // Replace the commandline with the contents from the text file.\n+                StringBuilder sb = new StringBuilder();\n+                foreach (string line in File.ReadAllLines(responseFile))\n+                {\n+                    string cleanLine = line.Trim();\n+                    if (string.IsNullOrEmpty(cleanLine))\n+                        continue;\n+                    sb.Append(cleanLine);\n+                    sb.Append(' ');\n+                }\n+                if (sb.Length > 0)\n+                    commandLine += \" \" + sb.ToString().Trim();\n+            }\n+            else\n+            {\n+                _args.Add(word);\n+            }\n+        }\n+\n+        private int GetNextOccuranceQualifier(string name, string[] aliases)\n+        {\n+            Debug.Assert(_args != null);\n+            Debug.Assert(_dashedParameterEncodedPositions != null);\n+            int ret = -1;\n+            string match = null;\n+            int encodedPos;\n+            if (_dashedParameterEncodedPositions.TryGetValue(name, out encodedPos))\n+            {\n+                match = name;\n+                ret = GetPosition(encodedPos);\n+            }\n+\n+            if (aliases != null)\n+            {\n+                foreach (string alias in aliases)\n+                {\n+                    int aliasEncodedPos;\n+                    if (_dashedParameterEncodedPositions.TryGetValue(name, out aliasEncodedPos))\n+                    {\n+                        int aliasPos = GetPosition(aliasEncodedPos);\n+                        if (aliasPos < ret)\n+                        {\n+                            name = alias;\n+                            encodedPos = aliasEncodedPos;\n+                            ret = aliasPos;\n+                        }\n+                    }\n+                }\n+            }\n+\n+            if (match != null)\n+            {\n+                if (!IsMulitple(encodedPos))\n+                    _dashedParameterEncodedPositions.Remove(match);\n+                else\n+                {\n+                    int nextPos = -1;\n+                    for (int i = ret + 1; i < _args.Count; i++)\n+                    {\n+                        if (string.Compare(ParseParameterName(_args[i]), name, StringComparison.OrdinalIgnoreCase) == 0)\n+                        {\n+                            nextPos = i;\n+                            break;\n+                        }\n+                    }\n+                    if (nextPos >= 0)\n+                        _dashedParameterEncodedPositions[name] = SetMulitple(nextPos);\n+                    else\n+                        _dashedParameterEncodedPositions.Remove(name);\n+                }\n+            }\n+            return ret;\n+        }\n+\n+        // Phase 2 parsing works into things that look like qualifiers (but without  knowledge of which qualifiers the command supports)\n+        /// <summary>\n+        /// Find the locations of all arguments that look like named parameters. \n+        /// </summary>\n+        private void ParseWordsIntoQualifiers()\n+        {\n+            _dashedParameterEncodedPositions = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);\n+\n+            bool paramSetEncountered = false;\n+            for (int i = 0; i < _args.Count; i++)\n+            {\n+                string arg = _args[i];\n+                if (arg == null)\n+                    continue;\n+\n+                string name = ParseParameterName(arg);\n+                if (name != null)\n+                {\n+                    if (name.Length == 1 && IsDash(name[0]))\n+                    {\n+                        _args[i] = null;\n+                        break;\n+                    }\n+                    if (name == \"?\")        // Did the user request help?\n+                    {\n+                        _args[i] = null;\n+                        _helpRequestedFor = String.Empty;\n+                        if (i + 1 < _args.Count)\n+                        {\n+                            i++;\n+                            _helpRequestedFor = _args[i];\n+                            _args[i] = null;\n+                        }\n+                        _mustParseHelpStrings = true;\n+                        break;\n+                    }\n+                    int position = i;\n+                    if (_dashedParameterEncodedPositions.TryGetValue(name, out position))\n+                        position = SetMulitple(position);\n+                    else\n+                        position = i;\n+                    _dashedParameterEncodedPositions[name] = position;\n+\n+                    if (!paramSetEncountered && !_noDashOnParameterSets && IsParameterSetWithqualifiersMustBeFirst(name))\n+                        break;\n+                }\n+                else\n+                {\n+                    if (!paramSetEncountered)\n+                    {\n+                        if (_noDashOnParameterSets && IsParameterSetWithqualifiersMustBeFirst(arg))\n+                            break;\n+                        else if (IsParameterSetWithqualifiersMustBeFirst(String.Empty))        // Then we are the default parameter set\n+                            break;\n+                        paramSetEncountered = true;     // If we have hit a parameter, we must have hit a parameter set.  \n+                    }\n+                }\n+            }\n+        }\n+        private bool IsParameterSetWithqualifiersMustBeFirst(string name)\n+        {\n+            if (_parameterSetsWhereQualifiersMustBeFirst != null)\n+            {\n+                foreach (string parameterSetName in _parameterSetsWhereQualifiersMustBeFirst)\n+                {\n+                    if (string.Compare(name, parameterSetName, StringComparison.OrdinalIgnoreCase) == 0)\n+                        return true;\n+                }\n+            }\n+            return false;\n+        }\n+\n+        // Phase 3, processing user defintions of qualifiers parameter sets etc.  \n+        /// <summary>\n+        /// returns the index in the 'args' array of the next instance of the 'name' qualifier.   returns -1 if there is \n+        /// no next instance of the qualifer.  \n+        /// </summary>\n+        private object DefineQualifier(string name, Type type, object defaultValue, string helpText, bool isRequired)\n+        {\n+            Debug.Assert(_args != null);\n+            if (_dashedParameterEncodedPositions == null)\n+                ParseWordsIntoQualifiers();\n+\n+            _qualiferEncountered = true;\n+            if (_mustParseHelpStrings)\n+                AddHelp(new CommandLineParameter(name, defaultValue, helpText, type, isRequired, false, false));\n+            if (_skipDefinitions)\n+                return null;\n+            if (_positionalArgEncountered && !_noSpaceOnQualifierValues)\n+                throw new CommandLineParserDesignerException(\"Definitions of Named parameters must come before defintions of positional parameters\");\n+\n+            object ret = null;\n+            string[] alaises = null;\n+            if (_aliasDefinitions != null)\n+                _aliasDefinitions.TryGetValue(name, out alaises);\n+\n+            int occuranceCount = 0;\n+            List<Array> arrayValues = null;\n+            for (;;)\n+            {\n+                int position = GetNextOccuranceQualifier(name, alaises);\n+                if (position < 0)\n+                    break;\n+\n+                string parameterStr = _args[position];\n+                _args[position] = null;\n+\n+                string value = null;\n+                int colonIdx = parameterStr.IndexOfAny(s_separators);\n+                if (colonIdx >= 0)\n+                    value = parameterStr.Substring(colonIdx + 1);\n+\n+                if (type == typeof(bool) || type == typeof(bool?))\n+                {\n+                    if (value == null)\n+                        value = \"true\";\n+                    else if (value == String.Empty)\n+                        value = \"false\";\n+                }\n+                else if (value == null)\n+                {\n+                    int valuePos = position + 1;\n+                    if (_noSpaceOnQualifierValues || valuePos >= _args.Count || _args[valuePos] == null)\n+                    {\n+                        string message = \"Parameter \" + name + \" is missing a value.\";\n+                        if (_noSpaceOnQualifierValues)\n+                            message += \"  The syntax -\" + name + \":<value> must be used.\";\n+                        throw new CommandLineParserException(message);\n+                    }\n+                    value = _args[valuePos];\n+\n+                    // Note that I don't absolutely need to exclude values that look like qualifiers, but it does\n+                    // complicate the code, and also leads to confusing error messages when we parse the command\n+                    // in a very different way then the user is expecting. Since you can provide values that\n+                    // begin with a '-' by doing -qualifier:-value instead of -qualifier -value I force the issue\n+                    // by excluding it here.  TODO: this makes negative numbers harder... \n+                    if (value.Length > 0 && IsQualifier(value))\n+                        throw new CommandLineParserException(\"Use \" + name + \":\" + value + \" if \" + value +\n+                            \" is meant to be value rather than a named parameter\");\n+                    _args[valuePos] = null;\n+                }\n+                ret = ParseValue(value, type, name);\n+                if (type.IsArray)\n+                {\n+                    if (arrayValues == null)\n+                        arrayValues = new List<Array>();\n+                    arrayValues.Add((Array)ret);\n+                    ret = null;\n+                }\n+                else if (occuranceCount > 0 && !_lastQualifierWins)\n+                    throw new CommandLineParserException(\"Parameter \" + name + \" specified more than once.\");\n+                occuranceCount++;\n+            }\n+\n+            if (occuranceCount == 0 && isRequired)\n+                throw new CommandLineParserException(\"Required named parameter \" + name + \" not present.\");\n+\n+            if (arrayValues != null)\n+                ret = ConcatinateArrays(type, arrayValues);\n+            return ret;\n+        }\n+        private object DefineParameter(string name, Type type, object defaultValue, string helpText, bool isRequired)\n+        {\n+            Debug.Assert(_args != null);\n+            if (_dashedParameterEncodedPositions == null)\n+                ParseWordsIntoQualifiers();\n+\n+            if (!isRequired)\n+                _optionalPositionalArgEncountered = true;\n+            else if (_optionalPositionalArgEncountered)\n+                throw new CommandLineParserDesignerException(\"Optional positional parameters can't preceed required positional parameters\");\n+\n+            _positionalArgEncountered = true;\n+            if (_mustParseHelpStrings)\n+                AddHelp(new CommandLineParameter(name, defaultValue, helpText, type, isRequired, true, false));\n+            if (_skipDefinitions)\n+                return null;\n+\n+            // Skip any nulled out args (things that used to be named parameters)\n+            while (_curPosition < _args.Count && _args[_curPosition] == null)\n+                _curPosition++;\n+\n+            object ret = null;\n+            if (type.IsArray)\n+            {\n+                // Pass 1, Get the count\n+                int count = 0;\n+                int argPosition = _curPosition;\n+                while (argPosition < _args.Count)\n+                    if (_args[argPosition++] != null)\n+                        count++;\n+\n+                if (count == 0 && isRequired)\n+                    throw new CommandLineParserException(\"Required positional parameter \" + name + \" not present.\");\n+\n+                Type elementType = type.GetElementType();\n+                Array array = Array.CreateInstance(elementType, count);\n+                argPosition = _curPosition;\n+                int index = 0;\n+                while (argPosition < _args.Count)\n+                {\n+                    string arg = _args[argPosition++];\n+                    if (arg != null)\n+                        array.SetValue(ParseValue(arg, elementType, name), index++);\n+                }\n+                _curPosition = _args.Count;\n+                ret = array;\n+            }\n+            else if (_curPosition < _args.Count)     // A 'normal' positional parameter with a value\n+            {\n+                ret = ParseValue(_args[_curPosition++], type, name);\n+            }\n+            else // No value\n+            {\n+                if (isRequired)\n+                    throw new CommandLineParserException(\"Required positional parameter \" + name + \" not present.\");\n+            }\n+\n+            return ret;\n+        }\n+        private bool DefineParameterSet(string name, string helpText)\n+        {\n+            Debug.Assert(_args != null);\n+            if (_dashedParameterEncodedPositions == null)\n+                ParseWordsIntoQualifiers();\n+\n+            if (!_paramSetEncountered && _positionalArgEncountered)\n+                throw new CommandLineParserDesignerException(\"Positional parameters must not preceed parameter set definitions.\");\n+\n+            _paramSetEncountered = true;\n+            _positionalArgEncountered = false;               // each parameter set gets a new arg set   \n+            _optionalPositionalArgEncountered = false;\n+            if (_defaultParamSetEncountered)\n+                throw new CommandLineParserDesignerException(\"The default parameter set must be defined last.\");\n+\n+            bool isDefaultParameterSet = (name.Length == 0);\n+            if (isDefaultParameterSet)\n+                _defaultParamSetEncountered = true;\n+\n+            if (_mustParseHelpStrings)\n+                AddHelp(new CommandLineParameter(name, null, helpText, typeof(bool), true, _noDashOnParameterSets, true));\n+            if (_skipParameterSets)\n+                return false;\n+\n+            // Have we just finish with the parameter set that was actually on the command line?\n+            if (_parameterSetName != null)\n+            {\n+                _skipDefinition","path":"src/common/CommandLine.cs","position":1564,"original_position":1564,"commit_id":"db31fd2a6f60cc022bc78f40f9ef8bba6be4d6c9","original_commit_id":"db31fd2a6f60cc022bc78f40f9ef8bba6be4d6c9","user":{"login":"mellinoe","id":8918977,"avatar_url":"https://avatars.githubusercontent.com/u/8918977?v=3","gravatar_id":"","url":"https://api.github.com/users/mellinoe","html_url":"https://github.com/mellinoe","followers_url":"https://api.github.com/users/mellinoe/followers","following_url":"https://api.github.com/users/mellinoe/following{/other_user}","gists_url":"https://api.github.com/users/mellinoe/gists{/gist_id}","starred_url":"https://api.github.com/users/mellinoe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mellinoe/subscriptions","organizations_url":"https://api.github.com/users/mellinoe/orgs","repos_url":"https://api.github.com/users/mellinoe/repos","events_url":"https://api.github.com/users/mellinoe/events{/privacy}","received_events_url":"https://api.github.com/users/mellinoe/received_events","type":"User","site_admin":false},"body":"Ah yes it actually shows up correctly if I open the \"Full View\". And you're right, since this is compiled into the application now instead of being a separate library, that would actually work fine and return the correct name.","created_at":"2015-07-23T20:46:37Z","updated_at":"2015-07-23T20:46:37Z","html_url":"https://github.com/dotnet/buildtools/pull/207#discussion_r35369280","pull_request_url":"https://api.github.com/repos/dotnet/buildtools/pulls/207","_links":{"self":{"href":"https://api.github.com/repos/dotnet/buildtools/pulls/comments/35369280"},"html":{"href":"https://github.com/dotnet/buildtools/pull/207#discussion_r35369280"},"pull_request":{"href":"https://api.github.com/repos/dotnet/buildtools/pulls/207"}}},"pull_request":{"url":"https://api.github.com/repos/dotnet/buildtools/pulls/207","id":40515883,"html_url":"https://github.com/dotnet/buildtools/pull/207","diff_url":"https://github.com/dotnet/buildtools/pull/207.diff","patch_url":"https://github.com/dotnet/buildtools/pull/207.patch","issue_url":"https://api.github.com/repos/dotnet/buildtools/issues/207","number":207,"state":"open","locked":false,"title":"Add a build for GenFacades.exe and dependencies","user":{"login":"mellinoe","id":8918977,"avatar_url":"https://avatars.githubusercontent.com/u/8918977?v=3","gravatar_id":"","url":"https://api.github.com/users/mellinoe","html_url":"https://github.com/mellinoe","followers_url":"https://api.github.com/users/mellinoe/followers","following_url":"https://api.github.com/users/mellinoe/following{/other_user}","gists_url":"https://api.github.com/users/mellinoe/gists{/gist_id}","starred_url":"https://api.github.com/users/mellinoe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mellinoe/subscriptions","organizations_url":"https://api.github.com/users/mellinoe/orgs","repos_url":"https://api.github.com/users/mellinoe/repos","events_url":"https://api.github.com/users/mellinoe/events{/privacy}","received_events_url":"https://api.github.com/users/mellinoe/received_events","type":"User","site_admin":false},"body":"This adds the source code for GenFacades and its dependencies, with builds for both Desktop (.NETFramework 4.6) and CoreCLR (using CoreConsole.exe as a host). There is an extra targets file (publishexe.targets) which handles some of the extra configuration needed to create a CoreCLR-based Console application, including the runtime, host, framework libraries, etc. I've included nuspec files to create nupkg's for both versions of GenFacades (CoreCLR and Desktop) which contain fully-standalone tools. The Desktop verison currently requires .NET 4.6 since it compiles against the contracts and therefore needs some of the in-box facades.\r\n\r\nThe bulk of the commit is the Microsoft.Cci.Extensions library. We will probably eventually trim this dependency out, at least for our basic tools like GenFacades.\r\n\r\nWhen we start shipping our cross-plat NuGet packages for our framework libraries, and for the runtime/hosts, we should be able to add another build configuration and easily produce cross-plat versions of GenFacades.","created_at":"2015-07-21T23:21:55Z","updated_at":"2015-07-23T20:46:37Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f8076c532aa79ccaa4754e42a421b648cb0eb03b","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/dotnet/buildtools/pulls/207/commits","review_comments_url":"https://api.github.com/repos/dotnet/buildtools/pulls/207/comments","review_comment_url":"https://api.github.com/repos/dotnet/buildtools/pulls/comments{/number}","comments_url":"https://api.github.com/repos/dotnet/buildtools/issues/207/comments","statuses_url":"https://api.github.com/repos/dotnet/buildtools/statuses/db31fd2a6f60cc022bc78f40f9ef8bba6be4d6c9","head":{"label":"mellinoe:add-genfacades","ref":"add-genfacades","sha":"db31fd2a6f60cc022bc78f40f9ef8bba6be4d6c9","user":{"login":"mellinoe","id":8918977,"avatar_url":"https://avatars.githubusercontent.com/u/8918977?v=3","gravatar_id":"","url":"https://api.github.com/users/mellinoe","html_url":"https://github.com/mellinoe","followers_url":"https://api.github.com/users/mellinoe/followers","following_url":"https://api.github.com/users/mellinoe/following{/other_user}","gists_url":"https://api.github.com/users/mellinoe/gists{/gist_id}","starred_url":"https://api.github.com/users/mellinoe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mellinoe/subscriptions","organizations_url":"https://api.github.com/users/mellinoe/orgs","repos_url":"https://api.github.com/users/mellinoe/repos","events_url":"https://api.github.com/users/mellinoe/events{/privacy}","received_events_url":"https://api.github.com/users/mellinoe/received_events","type":"User","site_admin":false},"repo":{"id":33899532,"name":"buildtools","full_name":"mellinoe/buildtools","owner":{"login":"mellinoe","id":8918977,"avatar_url":"https://avatars.githubusercontent.com/u/8918977?v=3","gravatar_id":"","url":"https://api.github.com/users/mellinoe","html_url":"https://github.com/mellinoe","followers_url":"https://api.github.com/users/mellinoe/followers","following_url":"https://api.github.com/users/mellinoe/following{/other_user}","gists_url":"https://api.github.com/users/mellinoe/gists{/gist_id}","starred_url":"https://api.github.com/users/mellinoe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mellinoe/subscriptions","organizations_url":"https://api.github.com/users/mellinoe/orgs","repos_url":"https://api.github.com/users/mellinoe/repos","events_url":"https://api.github.com/users/mellinoe/events{/privacy}","received_events_url":"https://api.github.com/users/mellinoe/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mellinoe/buildtools","description":"Build tools that are necessary for building the .NET Core projects","fork":true,"url":"https://api.github.com/repos/mellinoe/buildtools","forks_url":"https://api.github.com/repos/mellinoe/buildtools/forks","keys_url":"https://api.github.com/repos/mellinoe/buildtools/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mellinoe/buildtools/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mellinoe/buildtools/teams","hooks_url":"https://api.github.com/repos/mellinoe/buildtools/hooks","issue_events_url":"https://api.github.com/repos/mellinoe/buildtools/issues/events{/number}","events_url":"https://api.github.com/repos/mellinoe/buildtools/events","assignees_url":"https://api.github.com/repos/mellinoe/buildtools/assignees{/user}","branches_url":"https://api.github.com/repos/mellinoe/buildtools/branches{/branch}","tags_url":"https://api.github.com/repos/mellinoe/buildtools/tags","blobs_url":"https://api.github.com/repos/mellinoe/buildtools/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mellinoe/buildtools/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mellinoe/buildtools/git/refs{/sha}","trees_url":"https://api.github.com/repos/mellinoe/buildtools/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mellinoe/buildtools/statuses/{sha}","languages_url":"https://api.github.com/repos/mellinoe/buildtools/languages","stargazers_url":"https://api.github.com/repos/mellinoe/buildtools/stargazers","contributors_url":"https://api.github.com/repos/mellinoe/buildtools/contributors","subscribers_url":"https://api.github.com/repos/mellinoe/buildtools/subscribers","subscription_url":"https://api.github.com/repos/mellinoe/buildtools/subscription","commits_url":"https://api.github.com/repos/mellinoe/buildtools/commits{/sha}","git_commits_url":"https://api.github.com/repos/mellinoe/buildtools/git/commits{/sha}","comments_url":"https://api.github.com/repos/mellinoe/buildtools/comments{/number}","issue_comment_url":"https://api.github.com/repos/mellinoe/buildtools/issues/comments{/number}","contents_url":"https://api.github.com/repos/mellinoe/buildtools/contents/{+path}","compare_url":"https://api.github.com/repos/mellinoe/buildtools/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mellinoe/buildtools/merges","archive_url":"https://api.github.com/repos/mellinoe/buildtools/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mellinoe/buildtools/downloads","issues_url":"https://api.github.com/repos/mellinoe/buildtools/issues{/number}","pulls_url":"https://api.github.com/repos/mellinoe/buildtools/pulls{/number}","milestones_url":"https://api.github.com/repos/mellinoe/buildtools/milestones{/number}","notifications_url":"https://api.github.com/repos/mellinoe/buildtools/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mellinoe/buildtools/labels{/name}","releases_url":"https://api.github.com/repos/mellinoe/buildtools/releases{/id}","created_at":"2015-04-13T23:26:59Z","updated_at":"2015-04-16T22:18:41Z","pushed_at":"2015-07-21T23:19:46Z","git_url":"git://github.com/mellinoe/buildtools.git","ssh_url":"git@github.com:mellinoe/buildtools.git","clone_url":"https://github.com/mellinoe/buildtools.git","svn_url":"https://github.com/mellinoe/buildtools","homepage":"","size":875,"stargazers_count":0,"watchers_count":0,"language":"C#","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"dotnet:master","ref":"master","sha":"8a811ffc73c817aaf7e9ca05713c1b8d13826435","user":{"login":"dotnet","id":9141961,"avatar_url":"https://avatars.githubusercontent.com/u/9141961?v=3","gravatar_id":"","url":"https://api.github.com/users/dotnet","html_url":"https://github.com/dotnet","followers_url":"https://api.github.com/users/dotnet/followers","following_url":"https://api.github.com/users/dotnet/following{/other_user}","gists_url":"https://api.github.com/users/dotnet/gists{/gist_id}","starred_url":"https://api.github.com/users/dotnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dotnet/subscriptions","organizations_url":"https://api.github.com/users/dotnet/orgs","repos_url":"https://api.github.com/users/dotnet/repos","events_url":"https://api.github.com/users/dotnet/events{/privacy}","received_events_url":"https://api.github.com/users/dotnet/received_events","type":"Organization","site_admin":false},"repo":{"id":26295349,"name":"buildtools","full_name":"dotnet/buildtools","owner":{"login":"dotnet","id":9141961,"avatar_url":"https://avatars.githubusercontent.com/u/9141961?v=3","gravatar_id":"","url":"https://api.github.com/users/dotnet","html_url":"https://github.com/dotnet","followers_url":"https://api.github.com/users/dotnet/followers","following_url":"https://api.github.com/users/dotnet/following{/other_user}","gists_url":"https://api.github.com/users/dotnet/gists{/gist_id}","starred_url":"https://api.github.com/users/dotnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dotnet/subscriptions","organizations_url":"https://api.github.com/users/dotnet/orgs","repos_url":"https://api.github.com/users/dotnet/repos","events_url":"https://api.github.com/users/dotnet/events{/privacy}","received_events_url":"https://api.github.com/users/dotnet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/dotnet/buildtools","description":"Build tools that are necessary for building the .NET Core projects","fork":false,"url":"https://api.github.com/repos/dotnet/buildtools","forks_url":"https://api.github.com/repos/dotnet/buildtools/forks","keys_url":"https://api.github.com/repos/dotnet/buildtools/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dotnet/buildtools/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dotnet/buildtools/teams","hooks_url":"https://api.github.com/repos/dotnet/buildtools/hooks","issue_events_url":"https://api.github.com/repos/dotnet/buildtools/issues/events{/number}","events_url":"https://api.github.com/repos/dotnet/buildtools/events","assignees_url":"https://api.github.com/repos/dotnet/buildtools/assignees{/user}","branches_url":"https://api.github.com/repos/dotnet/buildtools/branches{/branch}","tags_url":"https://api.github.com/repos/dotnet/buildtools/tags","blobs_url":"https://api.github.com/repos/dotnet/buildtools/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dotnet/buildtools/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dotnet/buildtools/git/refs{/sha}","trees_url":"https://api.github.com/repos/dotnet/buildtools/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dotnet/buildtools/statuses/{sha}","languages_url":"https://api.github.com/repos/dotnet/buildtools/languages","stargazers_url":"https://api.github.com/repos/dotnet/buildtools/stargazers","contributors_url":"https://api.github.com/repos/dotnet/buildtools/contributors","subscribers_url":"https://api.github.com/repos/dotnet/buildtools/subscribers","subscription_url":"https://api.github.com/repos/dotnet/buildtools/subscription","commits_url":"https://api.github.com/repos/dotnet/buildtools/commits{/sha}","git_commits_url":"https://api.github.com/repos/dotnet/buildtools/git/commits{/sha}","comments_url":"https://api.github.com/repos/dotnet/buildtools/comments{/number}","issue_comment_url":"https://api.github.com/repos/dotnet/buildtools/issues/comments{/number}","contents_url":"https://api.github.com/repos/dotnet/buildtools/contents/{+path}","compare_url":"https://api.github.com/repos/dotnet/buildtools/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dotnet/buildtools/merges","archive_url":"https://api.github.com/repos/dotnet/buildtools/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dotnet/buildtools/downloads","issues_url":"https://api.github.com/repos/dotnet/buildtools/issues{/number}","pulls_url":"https://api.github.com/repos/dotnet/buildtools/pulls{/number}","milestones_url":"https://api.github.com/repos/dotnet/buildtools/milestones{/number}","notifications_url":"https://api.github.com/repos/dotnet/buildtools/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dotnet/buildtools/labels{/name}","releases_url":"https://api.github.com/repos/dotnet/buildtools/releases{/id}","created_at":"2014-11-06T23:43:05Z","updated_at":"2015-07-22T17:03:17Z","pushed_at":"2015-07-22T22:41:50Z","git_url":"git://github.com/dotnet/buildtools.git","ssh_url":"git@github.com:dotnet/buildtools.git","clone_url":"https://github.com/dotnet/buildtools.git","svn_url":"https://github.com/dotnet/buildtools","homepage":"","size":2078,"stargazers_count":362,"watchers_count":362,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":153,"mirror_url":null,"open_issues_count":14,"forks":153,"open_issues":14,"watchers":362,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/dotnet/buildtools/pulls/207"},"html":{"href":"https://github.com/dotnet/buildtools/pull/207"},"issue":{"href":"https://api.github.com/repos/dotnet/buildtools/issues/207"},"comments":{"href":"https://api.github.com/repos/dotnet/buildtools/issues/207/comments"},"review_comments":{"href":"https://api.github.com/repos/dotnet/buildtools/pulls/207/comments"},"review_comment":{"href":"https://api.github.com/repos/dotnet/buildtools/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/dotnet/buildtools/pulls/207/commits"},"statuses":{"href":"https://api.github.com/repos/dotnet/buildtools/statuses/db31fd2a6f60cc022bc78f40f9ef8bba6be4d6c9"}}}}