Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ndmitchell/cmdargs
Haskell library for command line argument processing
https://github.com/ndmitchell/cmdargs
Last synced: 6 days ago
JSON representation
Haskell library for command line argument processing
- Host: GitHub
- URL: https://github.com/ndmitchell/cmdargs
- Owner: ndmitchell
- License: other
- Created: 2013-10-11T10:20:36.000Z (about 11 years ago)
- Default Branch: master
- Last Pushed: 2024-04-10T18:13:20.000Z (8 months ago)
- Last Synced: 2024-10-29T21:59:45.537Z (about 2 months ago)
- Language: Haskell
- Size: 1.41 MB
- Stars: 91
- Watchers: 6
- Forks: 12
- Open Issues: 46
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGES.txt
- License: LICENSE
Awesome Lists containing this project
- awesome-cli-frameworks - CmdArgs
README
# CmdArgs: Easy Command Line Processing [![Hackage version](https://img.shields.io/hackage/v/cmdargs.svg?label=Hackage)](https://hackage.haskell.org/package/cmdargs) [![Stackage version](https://www.stackage.org/package/cmdargs/badge/nightly?label=Stackage)](https://www.stackage.org/package/cmdargs) [![Build status](https://img.shields.io/github/actions/workflow/status/ndmitchell/cmdargs/ci.yml?branch=master)](https://github.com/ndmitchell/cmdargs/actions)
CmdArgs 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:
* It's very concise to use. The HLint command line handling is three times shorter with CmdArgs.
* It supports programs with multiple modes, such as [darcs](http://darcs.net) or [Cabal](http://haskell.org/cabal/).A very simple example of a command line processor is:
```haskell
data Sample = Sample {hello :: String} deriving (Show, Data, Typeable)sample = Sample{hello = def &= help "World argument" &= opt "world"}
&= summary "Sample v1"main = print =<< cmdArgs sample
```
Despite being very concise, this processor is already fairly well featured:$ runghc Sample.hs --hello=world
Sample {hello = "world"}$ runghc Sample.hs --help
Sample v1, (C) Neil Mitchell 2009sample [FLAG]
-? --help[=FORMAT] Show usage information (optional format)
-V --version Show version information
-v --verbose Higher verbosity
-q --quiet Lower verbosity
-h --hello=VALUE World argument (default=world)## User Manual
The 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:
* `hlint` - the [HLint](https://github.com/ndmitchell/hlint#readme) program.
* `diffy` - a program to compare the differences between directories.
* `maker` - a make style program.For 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.
**Acknowledgements** Thanks to Kevin Quick for substantial patches, and additional code contributions from Sebastian Fischer and Daniel Schoepe.
## Hello World Example
The following code defines a complete command line argument processor:
```haskell
{-# LANGUAGE DeriveDataTypeable #-}
{-# OPTIONS_GHC -fno-cse #-}
module Sample where
import System.Console.CmdArgsdata Sample = Sample {hello :: String}
deriving (Show, Data, Typeable)sample = Sample{hello = def}
main = print =<< cmdArgs sample
```
To use the CmdArgs library there are three steps:* Define a record data type (`Sample`) that contains a field for each argument. This type needs to have instances for `Show`, `Data` and `Typeable`.
* 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.
* To ensure GHC evalutes attributes the right number of times we disable the CSE optimisation on this module.Now we have a reasonably functional command line argument processor. Some sample interactions are:
$ runghc Sample.hs --hello=world
Sample {hello = "world"}$ runghc Sample.hs --version
The sample program$ runghc Sample.hs --help
The sample programsample [OPTIONS]
-? --help Display help message
-V --version Print version information
-h --hello=ITEMCmdArgs 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.
## Specifying Attributes
In 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:
```haskell
sample = Sample{hello = def &= help "Who to say hello to"}
```
We can add additional attributes, for example to specify the type of the value expected by hello:
```haskell
sample = Sample {hello = def &= help "Who to say hello to" &= typ "WORLD"}
```
Now when running `--help` the final line is:-h --hello=WORLD Who to say hello to
There are many more attributes, detailed in the [Haddock documentation](http://hackage.haskell.org/packages/archive/cmdargs/latest/doc/html/System-Console-CmdArgs.html#2).
## Multiple Modes
To specify a program with multiple modes, similar to [darcs](http://darcs.net/), we can supply a data type with multiple constructors, for example:
```haskell
data Sample = Hello {whom :: String}
| Goodbye
deriving (Show, Data, Typeable)hello = Hello{whom = def}
goodbye = Goodbyemain = print =<< cmdArgs (modes [hello,goodbye])
```
Compared 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:$ runghc Sample.hs hello --whom=world
Hello {whom = "world"}$ runghc Sample.hs goodbye
Goodbye$ runghc Sample.hs --help
The sample programsample [OPTIONS]
Common flags
-? --help Display help message
-V --version Print version informationsample hello [OPTIONS]
-w --whom=ITEM
sample goodbye [OPTIONS]
As before, the behaviour can be customised using attributes.
## Larger Examples
For 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`.
### HLint
The [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:
* 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.
* 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.
* The `show_` field would clash with `show` if given the expected name, but CmdArgs automatically strips the trailing underscore.
* The `cpp_define` field has an underscore in it's name, which is transformed into a hyphen for the flag name.The code is:
```haskell
{-# LANGUAGE DeriveDataTypeable #-}
module HLint where
import System.Console.CmdArgsdata HLint = HLint
{report :: [FilePath]
,hint :: [FilePath]
,color :: Bool
,ignore_ :: [String]
,show_ :: Bool
,extension :: [String]
,language :: [String]
,utf8 :: Bool
,encoding :: String
,find :: [FilePath]
,test_ :: Bool
,datadir :: [FilePath]
,cpp_define :: [String]
,cpp_include :: [FilePath]
,files :: [FilePath]
}
deriving (Data,Typeable,Show,Eq)hlint = HLint
{report = def &= opt "report.html" &= typFile &= help "Generate a report in HTML"
,hint = def &= typFile &= help "Hint/ignore file to use"
,color = def &= name "c" &= name "colour" &= help "Color the output (requires ANSI terminal)"
,ignore_ = def &= typ "MESSAGE" &= help "Ignore a particular hint"
,show_ = def &= help "Show all ignored ideas"
,extension = def &= typ "EXT" &= help "File extensions to search (defaults to hs and lhs)"
,language = def &= name "X" &= typ "LANG" &= help "Language extension (Arrows, NoCPP)"
,utf8 = def &= help "Use UTF-8 text encoding"
,encoding = def &= typ "ENC" &= help "Choose the text encoding"
,find = def &= typFile &= help "Find hints in a Haskell file"
,test_ = def &= help "Run in test mode"
,datadir = def &= typDir &= help "Override the data directory"
,cpp_define = def &= typ "NAME[=VALUE]" &= help "CPP #define"
,cpp_include = def &= typDir &= help "CPP include path"
,files = def &= args &= typ "FILES/DIRS"
} &=
verbosity &=
help "Suggest improvements to Haskell source code" &=
summary "HLint v0.0.0, (C) Neil Mitchell" &=
details ["Hlint gives hints on how to improve Haskell code",""
,"To check all Haskell files in 'src' and generate a report type:"," hlint src --report"]mode = cmdArgsMode hlint
```
Produces the `--help` output:HLint v0.0.0, (C) Neil Mitchell
hlint [OPTIONS] [FILES/DIRS]
Suggest improvements to Haskell source codeCommon flags:
-r --report[=FILE] Generate a report in HTML
-h --hint=FILE Hint/ignore file to use
-c --colour --color Color the output (requires ANSI terminal)
-i --ignore=MESSAGE Ignore a particular hint
-s --show Show all ignored ideas
--extension=EXT File extensions to search (defaults to hs and lhs)
-X --language=LANG Language extension (Arrows, NoCPP)
-u --utf8 Use UTF-8 text encoding
--encoding=ENC Choose the text encoding
-f --find=FILE Find hints in a Haskell file
-t --test Run in test mode
-d --datadir=DIR Override the data directory
--cpp-define=NAME[=VALUE] CPP #define
--cpp-include=DIR CPP include path
-? --help Display help message
-V --version Print version information
-v --verbose Loud verbosity
-q --quiet Quiet verbosityHlint gives hints on how to improve Haskell code
To check all Haskell files in 'src' and generate a report type:
hlint src --report### Diffy
The 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:
* There are multiple modes of execution, creating and diffing.
* The diff mode takes exactly two arguments, the old file and the new file.
* Default values are given for the `out` field, which are different in both modes.The code is:
```haskell
{-# LANGUAGE DeriveDataTypeable #-}
module Diffy where
import System.Console.CmdArgsdata Diffy = Create {src :: Maybe FilePath, out :: FilePath}
| Diff {old :: FilePath, new :: FilePath, out :: FilePath}
deriving (Data,Typeable,Show,Eq)outFlags x = x &= help "Output file" &= typFile
create = Create
{src = def &= help "Source directory" &= typDir
,out = outFlags "ls.txt"
} &= help "Create a fingerprint"diff = Diff
{old = def &= typ "OLDFILE" &= argPos 0
,new = def &= typ "NEWFILE" &= argPos 1
,out = outFlags "diff.txt"
} &= help "Perform a diff"mode = cmdArgsMode $ modes [create,diff] &= help "Create and compare differences" &= program "diffy" &= summary "Diffy v1.0"
```
And `--help` produces:Diffy v1.0
diffy [COMMAND] ... [OPTIONS]
Create and compare differencesCommon flags:
-o --out=FILE Output file
-? --help Display help message
-V --version Print version informationdiffy create [OPTIONS]
Create a fingerprint-s --src=DIR Source directory
diffy diff [OPTIONS] OLDFILE NEWFILE
Perform a diff### Maker
The 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:
* The build mode is the default, so `maker` on it's own will be interpreted as a build command.
* The build method is an enumeration.
* 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`.The code is:
```haskell
{-# LANGUAGE DeriveDataTypeable #-}
module Maker where
import System.Console.CmdArgsdata 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 x = x &= help "Number of threads to use" &= name "j" &= typ "NUM"
wipe = Wipe &= help "Clean all build objects"
test_ = Test
{threads = threadsMsg def
,extra = def &= typ "ANY" &= args
} &= help "Run the test suite"build = Build
{threads = threadsMsg def
,method = enum
[Release &= help "Release build"
,Debug &= help "Debug build"
,Profile &= help "Profile build"]
,files = def &= args
} &= help "Build the project" &= automode = cmdArgsMode $ modes [build,wipe,test_]
&= help "Build helper program"
&= program "maker"
&= summary "Maker v1.0\nMake it"
```
And `--help` produces:Maker v1.0
Make itmaker [COMMAND] ... [OPTIONS]
Build helper programCommon flags:
-? --help Display help message
-V --version Print version informationmaker [build] [OPTIONS] [ITEM]
Build the project-j --threads=NUM Number of threads to use
-r --release Release build
-d --debug Debug build
-p --profile Profile buildmaker wipe [OPTIONS]
Clean all build objectsmaker test [OPTIONS] [ANY]
Run the test suite-j --threads=NUM Number of threads to use