packages feed

cmdargs-0.1: cmdargs.htm

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
        <title>CmdArgs: Easy Command Line Processing</title>
        <style type="text/css">
pre, .cmdargs {
    border: 2px solid gray;
    padding: 1px;
    padding-left: 5px;
    margin-left: 10px;
    background-color: #eee;
}

pre.define {
    background-color: #ffb;
    border-color: #cc0;
}

body {
    font-family: sans-serif;
}

h1, h2, h3 {
    font-family: serif;
}

h1 {
    color: rgb(23,54,93);
    border-bottom: 1px solid rgb(79,129,189);
    padding-bottom: 2px;
    font-variant: small-caps;
    text-align: center;
}

a {
    color: rgb(54,95,145);
}

h2 {
    color: rgb(54,95,145);
}

h3 {
    color: rgb(79,129,189);
}

p.rule {
    background-color: #ffb;
    padding: 3px;
    margin-left: 50px;
    margin-right: 50px;
}


.cmdargs {
	display: block;
	font-family: monospace;
}
.cmdargs td {
	padding: 0px;
	margin: 0px;
	padding-right: 1em;
}
.cmdargs td.indent {
	padding-left: 1em;
}
        </style>
    </head>
    <body>

<h1>CmdArgs: Easy Command Line Processing</h1>

<p style="text-align:right;margin-bottom:25px;">
    by <a href="http://community.haskell.org/~ndm/">Neil Mitchell</a>
</p>

<p>
    <a href="http://community.haskell.org/~ndm/cmdargs/">CmdArgs</a> is a library for defining and parsing command lines. The focus of CmdArgs is allowing the concise definition of fully-featured command line argument processors, in a mainly declarative manner (i.e. little coding needed). CmdArgs also supports multiple mode programs, for example as used in <a href="http://darcs.net/">darcs</a>.
</p><p>
	This document explains how to write the "hello world" of command line processors, then how to extend it with features into a complex command line processor. Finally this document gives three samples, which the <tt>cmdargs</tt> program can run. The three samples are:
</p>
<ol>
    <li><tt>hlint</tt> - the <a href="http://community.haskell.org/~ndm/hlint/">HLint</a> program.</li>
    <li><tt>diffy</tt> - a program to compare the differences between directories.</li>
    <li><tt>maker</tt> - a make style program.</li>
</ol>
<p>
	For each example you are encouraged to look at it's source (see the <a href="http://community.haskell.org/~ndm/darcs/hlint">darcs repo</a>, or the bottom of this document) and run it (try <tt>cmdargs hlint --help</tt>). The HLint program is fairly standard in terms of it's argument processing, and previously used the <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/System-Console-GetOpt.html">System.Console.GetOpt</a> library. Using GetOpt required 90 lines and a reasonable amount of duplication. Using CmdArgs the code requires 30 lines, and the logic is much simpler.
</p>


<h2>Hello World Example</h2>
<p>
	The following code defines a complete command line argument processor:
</p>
<pre>
{-# LANGUAGE DeriveDataTypeable #-}
module Sample where
import System.Console.CmdArgs

data Sample = Sample {hello :: String}
              deriving (Show, Data, Typeable)

sample = mode $ Sample{hello = def}

main = print =<< cmdArgs "Sample v1, (C) Neil Mitchell 2009" [sample]
</pre>
<p>
	To use the CmdArgs library there are three steps:
</p>
<ol>
	<li>Define a record data type (<tt>Sample</tt>) that contains a field for each argument. This type needs to have instances for <tt>Show</tt>, <tt>Data</tt> and <tt>Typeable</tt>.</li>
	<li>Give a value of that type (<tt>sample</tt>) with default values (<tt>def</tt> is the default value of any type). This value must be turned into a command line mode by calling the function <tt>mode</tt>.</li>
	<li>Call <tt>cmdArgs</tt> passing the mode, along with some text about the program.</li>
</ol>
<p>
	Now we have a reasonably functional command line argument processor. Some sample interactions are:
</p>
<pre>
$ runhaskell Sample.hs --hello=world
Sample {hello = "world"}

$ runhaskell Sample.hs --help
Sample v1, (C) Neil Mitchell 2009

sample [FLAG]

  -? --help[=FORMAT]  Show usage information (optional format)
  -V --version        Show version information
  -v --verbose        Higher verbosity
  -q --quiet          Lower verbosity
  -h --hello=VALUE
</pre>
<p>
	The CmdArgs library automatically provides support for:
</p>
<ul>
	<li>Help - if the user specifies <tt>--help</tt> then a help message is displayed. If <tt>--help=HTML</tt> is specified, then the help message is formatted in HTML, suitable for pasting into a web page (see the bottom of this document).</li>
	<li>Version - if the user specifies <tt>--version</tt> then the first argument passed to <tt>cmdArgs</tt> is written to the screen.</li>
	<li>Verbosity - if the user specifies <tt>--verbose</tt> or <tt>--quiet</tt> then the verbosity is set appropriately. The current verbosity can be queried with the functions <tt>isQuiet</tt>, <tt>isNormal</tt> and <tt>isLoud</tt>.</li>
</ul>

<h2>Specifying Attributes</h2>
<p>
	In order to control the behaviour we can add attributes. For example to add an attribute specifying the help text for the <tt>--hello</tt> argument we can write:
</p>
<pre>
sample = mode $ Sample{hello = def &= text "Who to say hello to"}
</pre>
<p>
	We can add additional attributes, for example to specify the type of the value expected by hello:
</p>
<pre>
sample = mode $ Sample {hello = def &= text "Who to say hello to" & typ "WORLD"}
</pre>
<p>
	Now when running <tt>--help</tt> the final line is:
</p>
<pre>
  -h --hello=WORLD    Who to say hello to
</pre>
<p>
	There are many more attributes, detailed in the <a href="http://hackage.haskell.org/packages/archive/cmdargs/latest/doc/html/System-Console-CmdArgs.html#2">Haddock documentation</a>.
</p>


<h2>Multiple Modes</h2>
<p>
	To specify a program with multiple modes, similar to <a href="http://darcs.net/">darcs</a>, we can supply a data type with multiple constructors, for example:
</p>
<pre>
data Sample = Hello {whom :: String}
            | Goodbye
              deriving (Show, Data, Typeable)

hello = mode $ Hello{whom = def}
goodbye = mode $ Goodbye

main = print =<< cmdArgs "Sample v2, (C) Neil Mitchell 2009" [hello,goodbye]
</pre>
<p>
	Compared to the first example, we now have multiple constructors, and a sample value for each constructor is passed to <tt>cmdArgs</tt>. Some sample interactions with this command line are:
</p>
<pre>
$ runhaskell Sample.hs hello --whom=world
Hello {whom = "world"}

$ runhaskell Sample.hs goodbye
Goodbye

$ runhaskell Sample.hs --help
Sample v2, (C) Neil Mitchell 2009

sample hello [FLAG]

  -w --whom=VALUE

sample goodbye [FLAG]

Common flags:
  -? --help[=FORMAT]  Show usage information (optional format)
  -V --version        Show version information
  -v --verbose        Higher verbosity
  -q --quiet          Lower verbosity
</pre>
<p>
	As before, the behaviour can be customised using attributes.
</p>

<h2>Larger Examples</h2>

<p>
	For each of the following examples we first explain the purpose of the program, then give the source code, and finally the output of <tt>--help=HTML</tt>. The programs are intended to show sample uses of CmdArgs, and are available to experiment with through <tt>cmdargs <i>progname</i></tt>.
</p>

<h3>HLint</h3>

<p>
	The <a href="http://community.haskell.org/~ndm/hlint/">HLint</a> program analyses a list of files, using various options to control the analysis. The command line processing is simple, but a few interesting points are:
</p>
<ul>
	<li>The <tt>--report</tt> flag can be used to output a report in a standard location, but giving the flag a value changes where the file is output.</li>
	<li>The <tt>color</tt> field is assigned two flag aliases, <tt>--colour</tt> and <tt>-c</tt>. Assigning the <tt>-c</tt> short flag explicitly stops either of the CPP fields using it.</li>
	<li>The <tt>show_</tt> field would clash with <tt>show</tt> if given the expected name, but CmdArgs automatically strips the trailing underscore.</li>
	<li>The <tt>cpp_define</tt> field has an underscore in it's name, which is transformed into a hyphen for the flag name.</li>
</ul>

<!-- BEGIN code hlint -->
<pre>
{-# LANGUAGE DeriveDataTypeable #-}
module HLint where
import System.Console.CmdArgs

data HLint = HLint
    {report :: [FilePath]
    ,hint :: [FilePath]
    ,color :: Bool
    ,ignore :: [String]
    ,show_ :: Bool
    ,test :: Bool
    ,cpp_define :: [String]
    ,cpp_include :: [String]
    ,files :: [String]
    }
    deriving (Data,Typeable,Show,Eq)

hlint = mode $ HLint
    {report = def &= empty "report.html" & typFile & text "Generate a report in HTML"
    ,hint = def &= typFile & text "Hint/ignore file to use"
    ,color = def &= flag "c" & flag "colour" & text "Color the output (requires ANSI terminal)"
    ,ignore = def &= typ "MESSAGE" & text "Ignore a particular hint"
    ,show_ = def &= text "Show all ignored ideas"
    ,test = def &= text "Run in test mode"
    ,cpp_define = def &= typ "NAME[=VALUE]" & text "CPP #define"
    ,cpp_include = def &= typDir & text "CPP include path"
    ,files = def &= args & typ "FILE/DIR"
    } &=
    prog "hlint" &
    text "Suggest improvements to Haskell source code" &
    helpSuffix ["To check all Haskell files in 'src' and generate a report type:","  hlint src --report"]

modes = [hlint]

main = print =<< cmdArgs "HLint v1.6.5, (C) Neil Mitchell 2006-2009" modes
</pre>
<!-- END -->
<!-- BEGIN help hlint -->
<table class='cmdargs'>
<tr><td colspan='3'>HLint v1.6.5, (C) Neil Mitchell 2006-2009</td></tr>
<tr><td colspan='3'>&nbsp;</td></tr>
<tr><td colspan='3'>hlint [FLAG] [FILE/DIR]</td></tr>
<tr><td colspan='3' class='indent'>Suggest improvements to Haskell source code</td></tr>
<tr><td colspan='3'>&nbsp;</td></tr>
<tr><td class='indent'>-?</td><td>--help[=FORMAT]</td><td>Show usage information (optional format)</td></tr>
<tr><td class='indent'>-V</td><td>--version</td><td>Show version information</td></tr>
<tr><td class='indent'>-v</td><td>--verbose</td><td>Higher verbosity</td></tr>
<tr><td class='indent'>-q</td><td>--quiet</td><td>Lower verbosity</td></tr>
<tr><td class='indent'>-r</td><td>--report[=FILE]</td><td>Generate a report in HTML (default=report.html)</td></tr>
<tr><td class='indent'>-h</td><td>--hint=FILE</td><td>Hint/ignore file to use</td></tr>
<tr><td class='indent'>-c</td><td>--color --colour</td><td>Color the output (requires ANSI terminal)</td></tr>
<tr><td class='indent'>-i</td><td>--ignore=MESSAGE</td><td>Ignore a particular hint</td></tr>
<tr><td class='indent'>-s</td><td>--show</td><td>Show all ignored ideas</td></tr>
<tr><td class='indent'>-t</td><td>--test</td><td>Run in test mode</td></tr>
<tr><td class='indent'></td><td>--cpp-define=NAME[=VALUE]</td><td>CPP #define</td></tr>
<tr><td class='indent'></td><td>--cpp-include=DIR</td><td>CPP include path</td></tr>
<tr><td colspan='3'>&nbsp;</td></tr>
<tr><td colspan='3'>To check all Haskell files in 'src' and generate a report type:</td></tr>
<tr><td colspan='3' class='indent'>hlint src --report</td></tr>
</table>
<!-- END -->

<h3>Diffy</h3>

<p>
	The Diffy sample is a based on the idea of creating directory listings and comparing them. The tool can operate in two separate modes, <tt>create</tt> or <tt>diff</tt>. This sample is fictional, but the ideas are drawn from a real program. A few notable features:
</p>
<ul>
	<li>There are multiple modes of execution, creating and diffing.</li>
	<li>The diff mode takes exactly two arguments, the old file and the new file.</li>
	<li>Default values are given for the <tt>out</tt> field, which are different in both modes.</li>
</ul>

<!-- BEGIN code diffy -->
<pre>
{-# LANGUAGE DeriveDataTypeable #-}
module Diffy where
import System.Console.CmdArgs

data Diffy = Create {src :: FilePath, out :: FilePath}
           | Diff {old :: FilePath, new :: FilePath, out :: FilePath}
             deriving (Data,Typeable,Show,Eq)

outFlags = text "Output file" & typFile

create = mode $ Create
    {src = "." &= text "Source directory" & typDir
    ,out = "ls.txt" &= outFlags
    } &= prog "diffy" & text "Create a fingerprint"

diff = mode $ Diff
    {old = def &= typ "OLDFILE" & argPos 0
    ,new = def &= typ "NEWFILE" & argPos 1
    ,out = "diff.txt" &= outFlags
    } &= text "Perform a diff"

modes = [create,diff]

main = print =<< cmdArgs "Diffy v1.0" modes
</pre>
<!-- END -->
<!-- BEGIN help diffy -->
<table class='cmdargs'>
<tr><td colspan='3'>Diffy v1.0</td></tr>
<tr><td colspan='3'>&nbsp;</td></tr>
<tr><td colspan='3'>diffy create [FLAG]</td></tr>
<tr><td colspan='3' class='indent'>Create a fingerprint</td></tr>
<tr><td colspan='3'>&nbsp;</td></tr>
<tr><td class='indent'>-s</td><td>--src=DIR</td><td>Source directory (default=.)</td></tr>
<tr><td class='indent'>-o</td><td>--out=FILE</td><td>Output file (default=ls.txt)</td></tr>
<tr><td colspan='3'>&nbsp;</td></tr>
<tr><td colspan='3'>diffy diff [FLAG] OLDFILE NEWFILE</td></tr>
<tr><td colspan='3' class='indent'>Perform a diff</td></tr>
<tr><td colspan='3'>&nbsp;</td></tr>
<tr><td class='indent'>-o</td><td>--out=FILE</td><td>Output file (default=diff.txt)</td></tr>
<tr><td colspan='3'>&nbsp;</td></tr>
<tr><td colspan='3'>Common flags:</td></tr>
<tr><td class='indent'>-?</td><td>--help[=FORMAT]</td><td>Show usage information (optional format)</td></tr>
<tr><td class='indent'>-V</td><td>--version</td><td>Show version information</td></tr>
<tr><td class='indent'>-v</td><td>--verbose</td><td>Higher verbosity</td></tr>
<tr><td class='indent'>-q</td><td>--quiet</td><td>Lower verbosity</td></tr>
</table>
<!-- END -->

<h3>Maker</h3>

<p>
	The Maker sample is based around a build system, where we can either build a project, clean the temporary files, or run a test. The test mode is designed to run another program, passing any unknown arguments onwards. Some interesting features are:
</p>
<ul>
	<li>The build mode is the default, so <tt>maker</tt> on it's own will be interpretted as a build command.</li>
	<li>The build method is an enumeration.</li>
	<li>The test mode collects unknown flags in the field <tt>extra</tt>.</li>
	<li>The <tt>threads</tt> field is in two of the constructors, but not all three. It is given the short flag <tt>-j</tt>, rather than the default <tt>-t</tt>.</li>
</ul>


<!-- BEGIN code maker -->
<pre>
{-# LANGUAGE DeriveDataTypeable #-}
module Maker where
import System.Console.CmdArgs

data Method = Debug | Release | Profile
              deriving (Data,Typeable,Show,Eq)

data Maker
    = Wipe
    | Test {threads :: Int, extra :: [String]}
    | Build {threads :: Int, method :: Method, files :: [FilePath]}
      deriving (Data,Typeable,Show,Eq)

threadsMsg = text "Number of threads to use" & flag "j" & typ "NUM"

wipe = mode $ Wipe &= prog "maker" & text "Clean all build objects"

test = mode $ Test
    {threads = def &= threadsMsg
    ,extra = def &= typ "ANY" & args & unknownFlags
    } &= text "Run the test suite"

build = mode $ Build
    {threads = def &= threadsMsg
    ,method = enum Release
        [Debug &= text "Debug build"
        ,Release &= text "Release build"
        ,Profile &= text "Profile build"]
    ,files = def &= args
    } &= text "Build the project" & defMode

modes = [build,wipe,test]

main = print =<< cmdArgs "Maker v1.0" modes
</pre>
<!-- END -->
<!-- BEGIN help maker -->
<table class='cmdargs'>
<tr><td colspan='3'>Maker v1.0</td></tr>
<tr><td colspan='3'>&nbsp;</td></tr>
<tr><td colspan='3'>maker [build] [FLAG] [FILE]</td></tr>
<tr><td colspan='3' class='indent'>Build the project</td></tr>
<tr><td colspan='3'>&nbsp;</td></tr>
<tr><td class='indent'>-j</td><td>--threads=NUM</td><td>Number of threads to use</td></tr>
<tr><td class='indent'>-d</td><td>--debug</td><td>Debug build</td></tr>
<tr><td class='indent'>-r</td><td>--release</td><td>Release build</td></tr>
<tr><td class='indent'>-p</td><td>--profile</td><td>Profile build</td></tr>
<tr><td colspan='3'>&nbsp;</td></tr>
<tr><td colspan='3'>maker wipe [FLAG]</td></tr>
<tr><td colspan='3' class='indent'>Clean all build objects</td></tr>
<tr><td colspan='3'>&nbsp;</td></tr>
<tr><td colspan='3'>maker test [FLAG] [ANY]</td></tr>
<tr><td colspan='3' class='indent'>Run the test suite</td></tr>
<tr><td colspan='3'>&nbsp;</td></tr>
<tr><td class='indent'>-j</td><td>--threads=NUM</td><td>Number of threads to use</td></tr>
<tr><td colspan='3'>&nbsp;</td></tr>
<tr><td colspan='3'>Common flags:</td></tr>
<tr><td class='indent'>-?</td><td>--help[=FORMAT]</td><td>Show usage information (optional format)</td></tr>
<tr><td class='indent'>-V</td><td>--version</td><td>Show version information</td></tr>
<tr><td class='indent'>-v</td><td>--verbose</td><td>Higher verbosity</td></tr>
<tr><td class='indent'>-q</td><td>--quiet</td><td>Lower verbosity</td></tr>
</table>
<!-- END -->

    </body>
</html>