• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

Data/Generics/H22-Jan-2018-213131

System/Console/H22-Jan-2018-4,5722,714

LICENSEH A D22-Jan-20181.5 KiB3125

Main.hsH A D22-Jan-20183.3 KiB10381

README.mdH A D22-Jan-201813.8 KiB365268

Setup.hsH A D22-Jan-201870 53

cmdargs.cabalH A D22-Jan-20184.1 KiB120109

README.md

1# CmdArgs: Easy Command Line Processing [![Hackage version](https://img.shields.io/hackage/v/cmdargs.svg?label=Hackage)](https://hackage.haskell.org/package/cmdargs) [![Build Status](https://img.shields.io/travis/ndmitchell/cmdargs.svg)](https://travis-ci.org/ndmitchell/cmdargs)
2
3CmdArgs is a Haskell library for defining command line parsers. The two features that make it a better choice than the standard [getopt library](http://haskell.org/ghc/docs/latest/html/libraries/base/System-Console-GetOpt.html) are:
4
5* It's very concise to use. The HLint command line handling is three times shorter with CmdArgs.
6* It supports programs with multiple modes, such as [darcs](http://darcs.net) or [Cabal](http://haskell.org/cabal/).
7
8A very simple example of a command line processor is:
9```haskell
10data Sample = Sample {hello :: String} deriving (Show, Data, Typeable)
11
12sample = Sample{hello = def &= help "World argument" &= opt "world"}
13         &= summary "Sample v1"
14
15main = print =<< cmdArgs sample
16```
17Despite being very concise, this processor is already fairly well featured:
18
19    $ runhaskell Sample.hs --hello=world
20    Sample {hello = "world"}
21
22    $ runhaskell Sample.hs --help
23    Sample v1, (C) Neil Mitchell 2009
24
25    sample [FLAG]
26
27      -? --help[=FORMAT]  Show usage information (optional format)
28      -V --version        Show version information
29      -v --verbose        Higher verbosity
30      -q --quiet          Lower verbosity
31      -h --hello=VALUE    World argument (default=world)
32
33## User Manual
34
35The rest of 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 `cmdargs` program can run. The three samples are:
36
37* `hlint` - the [HLint](https://github.com/ndmitchell/hlint#readme) program.
38* `diffy` - a program to compare the differences between directories.
39* `maker` - a make style program.
40
41For each example you are encouraged to look at it's source (in the [repo](https://github.com/ndmitchell/cmdargs/tree/master/System/Console/CmdArgs/Test/Implicit)) and run it (try `cmdargs hlint --help`). The HLint program is fairly standard in terms of it's argument processing, and previously used the [System.Console.GetOpt](http://haskell.org/ghc/docs/latest/html/libraries/base/System-Console-GetOpt.html) 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.
42
43**Acknowledgements** Thanks to Kevin Quick for substantial patches, and additional code contributions from Sebastian Fischer and Daniel Schoepe.
44
45
46## Hello World Example
47
48The following code defines a complete command line argument processor:
49```haskell
50{-# LANGUAGE DeriveDataTypeable #-}
51{-# OPTIONS_GHC -fno-cse #-}
52module Sample where
53import System.Console.CmdArgs
54
55data Sample = Sample {hello :: String}
56              deriving (Show, Data, Typeable)
57
58sample = Sample{hello = def}
59
60main = print =<< cmdArgs sample
61```
62To use the CmdArgs library there are three steps:
63
64* Define a record data type (`Sample`) that contains a field for each argument. This type needs to have instances for `Show`, `Data` and `Typeable`.
65* Give a value of that type (`sample`) with default values (`def` is a default value of any type, but I could also have written `""`). This value is turned into a command line by calling the `cmdArgs` function.
66* To ensure GHC evalutes attributes the right number of times we disable the CSE optimisation on this module.
67
68Now we have a reasonably functional command line argument processor. Some sample interactions are:
69
70    $ runhaskell Sample.hs --hello=world
71    Sample {hello = "world"}
72
73    $ runhaskell Sample.hs --version
74    The sample program
75
76    $ runhaskell Sample.hs --help
77    The sample program
78
79    sample [OPTIONS]
80
81      -? --help        Display help message
82      -V --version     Print version information
83      -h --hello=ITEM
84
85CmdArgs uses defaults to automatically infer a command line parser for a value, and provides annotations to override any of the the defaults. CmdArgs automatically supports `--help` and `--version` flags, and optionally supports verbosity flags.
86
87## Specifying Attributes
88
89In order to control the behaviour we can add attributes. For example to add an attribute specifying the help text for the `--hello` argument we can write:
90```haskell
91sample = Sample{hello = def &= help "Who to say hello to"}
92```
93We can add additional attributes, for example to specify the type of the value expected by hello:
94```haskell
95sample = Sample {hello = def &= help "Who to say hello to" &= typ "WORLD"}
96```
97Now when running `--help` the final line is:
98
99      -h --hello=WORLD  Who to say hello to
100
101There are many more attributes, detailed in the [Haddock documentation](http://hackage.haskell.org/packages/archive/cmdargs/latest/doc/html/System-Console-CmdArgs.html#2).
102
103
104## Multiple Modes
105
106To specify a program with multiple modes, similar to [darcs](http://darcs.net/), we can supply a data type with multiple constructors, for example:
107```haskell
108data Sample = Hello {whom :: String}
109            | Goodbye
110              deriving (Show, Data, Typeable)
111
112hello = Hello{whom = def}
113goodbye = Goodbye
114
115main = print =<< cmdArgs (modes [hello,goodbye])
116```
117Compared to the first example, we now have multiple constructors, and a sample value for each constructor is passed to `cmdArgs`. Some sample interactions with this command line are:
118
119    $ runhaskell Sample.hs hello --whom=world
120    Hello {whom = "world"}
121
122    $ runhaskell Sample.hs goodbye
123    Goodbye
124
125    $ runhaskell Sample.hs --help
126    The sample program
127
128    sample [OPTIONS]
129
130     Common flags
131      -? --help       Display help message
132      -V --version    Print version information
133
134    sample hello [OPTIONS]
135
136      -w --whom=ITEM
137
138    sample goodbye [OPTIONS]
139
140As before, the behaviour can be customised using attributes.
141
142
143## Larger Examples
144
145For each of the following examples we first explain the purpose of the program, then give the source code, and finally the output of `--help`. The programs are intended to show sample uses of CmdArgs, and are available to experiment with through `cmdargs progname`.
146
147### HLint
148
149The [HLint](https://github.com/ndmitchell/hlint#readme) 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:
150
151* The `--report` flag can be used to output a report in a standard location, but giving the flag a value changes where the file is output.
152* The `color` field is assigned two flag aliases, `--colour` and `-c`. Assigning the `-c` short flag explicitly stops either of the CPP fields using it.
153* The `show_` field would clash with `show` if given the expected name, but CmdArgs automatically strips the trailing underscore.
154* The `cpp_define` field has an underscore in it's name, which is transformed into a hyphen for the flag name.
155
156The code is:
157```haskell
158{-# LANGUAGE DeriveDataTypeable #-}
159module HLint where
160import System.Console.CmdArgs
161
162data HLint = HLint
163    {report :: [FilePath]
164    ,hint :: [FilePath]
165    ,color :: Bool
166    ,ignore_ :: [String]
167    ,show_ :: Bool
168    ,extension :: [String]
169    ,language :: [String]
170    ,utf8 :: Bool
171    ,encoding :: String
172    ,find :: [FilePath]
173    ,test_ :: Bool
174    ,datadir :: [FilePath]
175    ,cpp_define :: [String]
176    ,cpp_include :: [FilePath]
177    ,files :: [FilePath]
178    }
179    deriving (Data,Typeable,Show,Eq)
180
181hlint = HLint
182    {report = def &= opt "report.html" &= typFile &= help "Generate a report in HTML"
183    ,hint = def &= typFile &= help "Hint/ignore file to use"
184    ,color = def &= name "c" &= name "colour" &= help "Color the output (requires ANSI terminal)"
185    ,ignore_ = def &= typ "MESSAGE" &= help "Ignore a particular hint"
186    ,show_ = def &= help "Show all ignored ideas"
187    ,extension = def &= typ "EXT" &= help "File extensions to search (defaults to hs and lhs)"
188    ,language = def &= name "X" &= typ "LANG" &= help "Language extension (Arrows, NoCPP)"
189    ,utf8 = def &= help "Use UTF-8 text encoding"
190    ,encoding = def &= typ "ENC" &= help "Choose the text encoding"
191    ,find = def &= typFile &= help "Find hints in a Haskell file"
192    ,test_ = def &= help "Run in test mode"
193    ,datadir = def &= typDir &= help "Override the data directory"
194    ,cpp_define = def &= typ "NAME[=VALUE]" &= help "CPP #define"
195    ,cpp_include = def &= typDir &= help "CPP include path"
196    ,files = def &= args &= typ "FILES/DIRS"
197    } &=
198    verbosity &=
199    help "Suggest improvements to Haskell source code" &=
200    summary "HLint v0.0.0, (C) Neil Mitchell" &=
201    details ["Hlint gives hints on how to improve Haskell code",""
202            ,"To check all Haskell files in 'src' and generate a report type:","  hlint src --report"]
203
204mode = cmdArgsMode hlint
205```
206Produces the `--help` output:
207
208    HLint v0.0.0, (C) Neil Mitchell
209
210    hlint [OPTIONS] [FILES/DIRS]
211    Suggest improvements to Haskell source code
212
213    Common flags:
214      -r --report[=FILE]            Generate a report in HTML
215      -h --hint=FILE                Hint/ignore file to use
216      -c --colour --color            Color the output (requires ANSI terminal)
217      -i --ignore=MESSAGE            Ignore a particular hint
218      -s --show                     Show all ignored ideas
219         --extension=EXT            File extensions to search (defaults to hs and lhs)
220      -X --language=LANG            Language extension (Arrows, NoCPP)
221      -u --utf8                        Use UTF-8 text encoding
222         --encoding=ENC                Choose the text encoding
223      -f --find=FILE                Find hints in a Haskell file
224      -t --test                        Run in test mode
225      -d --datadir=DIR                Override the data directory
226         --cpp-define=NAME[=VALUE]  CPP #define
227         --cpp-include=DIR            CPP include path
228      -? --help                        Display help message
229      -V --version                    Print version information
230      -v --verbose                    Loud verbosity
231      -q --quiet                    Quiet verbosity
232
233    Hlint gives hints on how to improve Haskell code
234
235    To check all Haskell files in 'src' and generate a report type:
236      hlint src --report
237
238
239### Diffy
240
241The Diffy sample is a based on the idea of creating directory listings and comparing them. The tool can operate in two separate modes, `create` or `diff`. This sample is fictional, but the ideas are drawn from a real program. A few notable features:
242
243* There are multiple modes of execution, creating and diffing.
244* The diff mode takes exactly two arguments, the old file and the new file.
245* Default values are given for the `out` field, which are different in both modes.
246
247The code is:
248```haskell
249{-# LANGUAGE DeriveDataTypeable #-}
250module Diffy where
251import System.Console.CmdArgs
252
253data Diffy = Create {src :: Maybe FilePath, out :: FilePath}
254           | Diff {old :: FilePath, new :: FilePath, out :: FilePath}
255             deriving (Data,Typeable,Show,Eq)
256
257outFlags x = x &= help "Output file" &= typFile
258
259create = Create
260    {src = def &= help "Source directory" &= typDir
261    ,out = outFlags "ls.txt"
262    } &= help "Create a fingerprint"
263
264diff = Diff
265    {old = def &= typ "OLDFILE" &= argPos 0
266    ,new = def &= typ "NEWFILE" &= argPos 1
267    ,out = outFlags "diff.txt"
268    } &= help "Perform a diff"
269
270mode = cmdArgsMode $ modes [create,diff] &= help "Create and compare differences" &= program "diffy" &= summary "Diffy v1.0"
271```
272And `--help` produces:
273
274    Diffy v1.0
275
276    diffy [COMMAND] ... [OPTIONS]
277      Create and compare differences
278
279    Common flags:
280      -o --out=FILE     Output file
281      -? --help         Display help message
282      -V --version     Print version information
283
284    diffy create [OPTIONS]
285      Create a fingerprint
286
287      -s  --src=DIR  Source directory
288
289    diffy diff [OPTIONS] OLDFILE NEWFILE
290      Perform a diff
291
292### Maker
293
294The Maker sample is based around a build system, where we can either build a project, clean the temporary files, or run a test. Some interesting features are:
295
296* The build mode is the default, so `maker` on it's own will be interpreted as a build command.
297* The build method is an enumeration.
298* The `threads` field is in two of the constructors, but not all three. It is given the short flag `-j`, rather than the default `-t`.
299
300The code is:
301```haskell
302{-# LANGUAGE DeriveDataTypeable #-}
303module Maker where
304import System.Console.CmdArgs
305
306data Method = Debug | Release | Profile
307              deriving (Data,Typeable,Show,Eq)
308
309data Maker
310    = Wipe
311    | Test {threads :: Int, extra :: [String]}
312    | Build {threads :: Int, method :: Method, files :: [FilePath]}
313      deriving (Data,Typeable,Show,Eq)
314
315threadsMsg x = x &= help "Number of threads to use" &= name "j" &= typ "NUM"
316
317wipe = Wipe &= help "Clean all build objects"
318
319test_ = Test
320    {threads = threadsMsg def
321    ,extra = def &= typ "ANY" &= args
322    } &= help "Run the test suite"
323
324build = Build
325    {threads = threadsMsg def
326    ,method = enum
327        [Release &= help "Release build"
328        ,Debug &= help "Debug build"
329        ,Profile &= help "Profile build"]
330    ,files = def &= args
331    } &= help "Build the project" &= auto
332
333mode = cmdArgsMode $ modes [build,wipe,test_]
334     &= help "Build helper program"
335     &= program "maker"
336     &= summary "Maker v1.0\nMake it"
337```
338And `--help` produces:
339
340    Maker v1.0
341      Make it
342
343    maker [COMMAND] ... [OPTIONS]
344      Build helper program
345
346    Common flags:
347      -? --help     Display help message
348      -V --version  Print version information
349
350    maker [build] [OPTIONS] [ITEM]
351      Build the project
352
353      -j --threads=NUM  Number of threads to use
354      -r --release      Release build
355      -d --debug        Debug build
356      -p --profile      Profile build
357
358    maker wipe [OPTIONS]
359      Clean all build objects
360
361    maker test [OPTIONS] [ANY]
362      Run the test suite
363
364      -j --threads=NUM  Number of threads to use
365