[Mono-dev] ANNOUNCE: NDesk.Options 0.2.0

Jonathan Pryor jonpryor at vt.edu
Thu Feb 14 16:58:15 EST 2008


I am pleased to announce the release of NDesk.Options 0.2.0.
NDesk.Options is a C# program option parser library, inspired by Perl's
Getopt::Long option parser.

To download, visit the NDesk.Options web page:

        http://www.ndesk.org/Options

Usage:
-----

See http://www.ndesk.org/Options and the OptionSet[0] documentation for
examples.

[0] http://www.ndesk.org/doc/ndesk-options/NDesk.Options/OptionSet.html

What's New?
----------

There have been numerous changes since the previous 0.1.0 release:

      * Mono 1.9 is now required to build. (An svn release was
        previously required anyway, so this isn't a surprising
        requirement.)
      * Simplify the API by removing all OptionSet.Add() methods which
        provided an OptionContext to the callback function; this
        includes: 
        
              * OptionSet.Add(string, Action<string, OptionContext>)
              * OptionSet.Add(string, string, Action<string,
                OptionContext>)
              * OptionSet.Add<T>(string, Action<T, OptionContext>)
              * OptionSet.Add<T>(string, string, Action<T,
                OptionContext>)
        
        If you really need access to an OptionContext, you can Add your
        own Option and override Option.OnParseComplete(OptionContext).
        
        
      * By Miguel's request, change the semantics for Options with
        optional values (arguments that have a type value of `:').
        Previously, Options accepting an optional value were virtually
        identical to Options accepting a required value; the only place
        they would differ is at the end of the command line, where if a
        value was missing for a Option with an optional value no error
        would occur, while an Option with a required value would
        generate an error.
        
        Now, we introduce the notion of greediness: required values are
        greedy, and will eat any number of following arguments in order
        to fulfill their requirements. Optional values are not greedy,
        and will only extract a value from the current argument.
        
        By way of example:
        
                string color = null;
                var p = new OptionSet () {
                    { "-color:", v => color = v },
                };
                p.Parse (new string[]{"--color=auto"});     // 1
                p.Parse (new string[]{"--color", "auto"});  // 2
        
        In NDesk.Options 0.1.0, (1) and (2) would be identical and color
        would be given the value auto. In 0.2.0, they are not identical:
        (1) would assign the value auto to color, while (2) would assign
        null to color. This permits consistency with GNU ls(1)'s ls
        --color behavior.
        
        If a required option were to be specified (by using = instead
        of :), then (1) and (2) would again have identical results.
        
        
      * NDesk.Options 0.1.0 restricted option bundling to boolean
        Options. This restriction has been relaxed so that (1) Options
        accepting both optional and required values may be bundled with
        boolean Options, and (2) the optional or required value may be
        bundled as well. As before, only single character Options may be
        bundled. 
        
        The logic is as follows: given an argument such as -cvfname:
        
             1. cvfname must not match a registered Option. If it does
                match a registered option, then that is the Option that
                will (eventually) be invoked.
             2. c must be a registered option. If it isn't, then
                -cvfname is returned from
                OptionSet.Parse(IEnumerable<string>).
             3. Each character is looked up; if it's a boolean Option,
                then the associated action is invoked with a non-null
                value.
             4. If instead the character is an Option accepting one or
                more optional or required values, then the rest of the
                argument (not including the Option character) is used as
                the value. This also follows the greediness of optional
                vs. required values: optional values will only use the
                current argument, while required values may use the
                following argument(s) if e.g. the Option's character is
                the last character in the sequence.
             5. If a non-Option character is encountered that is not (a)
                the first character in the sequence, or (b) used as the
                value for a previous Option, then an OptionException is
                thrown.
        
        This does The Right Thing for tar(1)-like option handling, with
        tar -cvfname ... creating (with verbose output) the file with
        the name name.
        
        
      * Options may now accept (or require) more than one value. The
        Option(string, string, int) constructor allows specifying how
        many values are accepted/required (depending on whether the
        Option has optional or required values). 
        
        The Option values are available through the
        OptionContext.OptionValues collection.
        
      * Direct support for Options accepting/required two values within
        OptionSet: 
              * OptionSet.Add(string, OptionAction<string, string>)
              * OptionSet.Add(string, string, OptionAction<string,
                string>)
              * OptionSet.Add<TKey, TValue> (string, OptionAction<TKey,
                TValue>)
              * OptionSet.Add<TKey, TValue> (string, string,
                OptionAction<TKey, TValue>)
        
        This now permits reasonable handling of cc(1)-style parameters:
        
                var macros = new Dictionary<string, string> ();
                var p = new OptionSet () {
                    { "D:", (k, v) => { if (k != null) macros.Add (k, v); } },
                };
                p.Parse (new string[]{"-DNAME1", "-DNAME2=VALUE2"});
                    // Adds the keys "NAME1" (with null value) 
                    // and "NAME2" (with value "VALUE2") to `macros'.
        
        Note that an optional value is used; if D= were specified, two
        values would be required, so -DNAME1 -DNAME2=VALUE2 would insert
        one macro -- NAME1 -- with the value -DNAME2=VALUE2. 
        
        
      * When an Option permits more than one value, it may provide a
        list of value separator strings, strings that may be used to
        separate the multiple values. If no separators are listed, =
        and : are used as the default (thus permitting the previous
        -DNAME2=VALUE2 example to work; -DNAME2:VALUE2 would have had
        the same result). 
        
        The value separator strings follow the : or = in the Option
        prototype. They consist of:
        
              * The string within { and }.
              * Any other individual character.
        
        Thus, the prototype M:+-*/ would use +, -, *, or / to split
        values, so -M5+2, -M5-2, -M5*2, and -M5/2 all provide two values
        (5 and 2) to the M option.
        
        The prototype N={-->}{=>} would parse both -NA-->B and -NA=>B so
        that A and B are provided as the two values to the N option.
        
        As a special construct, the separator {} requires that each
        value be a separate argument. (This makes no sense for Options
        with optional values.)
        
        
      * Naming consistency improvements: an argument is an unparsed
        string, an option is a parsed argument that corresponds to a
        registered Option, and a prototype is a description of an
        Option, describing all aliases, the value type, and value
        separators. This has resulted in method argument name changes.
      * Removal of .NET 3.5 support. Instead of using System.Action`2
        from System.Core.dll (or providing an internal equivalent), I've
        just defined a OptionAction<TKey, TValue> type. This simplifies
        assembly versioning.

As always, I'm interested in API and design feedback, and any way that
the library could be improved.

Thanks,
 - Jon



More information about the Mono-devel-list mailing list