λ

DEFMAIN - intuitive command line options parser for Common Lisp

Table of Contents

[in package DEFMAIN with nicknames DEFMAIN/DEFMAIN]

1 DEFMAIN ASDF System Details

Tests:
Linter:

λ

2 Reasoning

Library net.didierverna.clon very powerful, but too complicated to use in simple cases. This library provides a wrapper which will suite your needs in 80% cases.

Compare this code, which uses DEFMAIN macro:

(defmain (main) ((debug "Show traceback instead of short message."
                        :flag t)
                 (log   "Filename to write log to.")
                 (token "GitHub personal access token."
                        :env-var "TOKEN")
                 &rest repositories)
  "Utility to analyze github forks."

   ;; Making real work
  (loop for reporitory in repositories
        do (analyze repository
                    :log log
                    :debug debug
                    :token token)))

With code providing the same functionality, but using raw net.didierverna.clon system:

(net.didierverna.clon:defsynopsis (:postfix "REPOSITORY")
  (text :contents "This utility builds a report about all non-merged commits for any github repository. Just give some repository name like \"antirez/redis\" as an argument and pipe stdout to some file.")
  (flag :short-name "h" :long-name "help"
        :description "Print this help and exit.")
  (flag :short-name "v" :long-name "version"
        :description "Print version number and exit.")
  (flag :long-name "debug"
        :description "Show traceback instead of short message.")
  (stropt :short-name "l" :long-name "log"
          :description "Filename to write log to.")
  (stropt :short-name "t" :long-name "token"
          :env-var "TOKEN"
          :description "GitHub personal access token."))

(defun main (&rest argv)
  (declare (ignorable argv))
  (net.didierverna.clon:make-context :cmdline (cons "12forks" argv))
  (when (net.didierverna.clon:getopt :long-name "help")
    (net.didierverna.clon:help)
    (net.didierverna.clon:exit))

  ;; Making real work
  (loop for reporitory in (remainder)
        do (analyze repository
                    :log (net.didierverna.clon:getopt :long-name "log")
                    :debug (net.didierverna.clon:getopt :long-name "debug")
                    :token (net.didierverna.clon:getopt :long-name "token"))))

λ

3 Installation

This system is available as part of the https://ultralisp.org distribution. Follow instruction on the site to setup the distribution, and then install DEFMAIN system using Quicklisp client:

(ql:quickload :defmain)

λ

4 Usage

The main entry point for defining the main function for your program is the DEFMAIN macro:

λ

4.1 Subcommands

Also, you might want to build a more complex command-line interface with subcommands.

In this case, you need to use DEFMAIN macro to define the main entry-point, and then to define additional subcommands using DEFCOMMAND macro:

λ

4.2 Helpers

When writing more complex logic, these helpers could be useful:

λ

5 Roadmap

Unhandled SIMPLE-ERROR in thread #<SB-THREAD:THREAD "main thread"
RUNNING {10005285B3}>:

Options #<LISPOBJ {1002705593}> and #<STROPT {1002705C03}>:
indentical short name "s".

Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING
{10005285B3}>
0: (`SB-DEBUG::DEBUGGER-DISABLED-HOOK` #<SIMPLE-ERROR "Options ~A and
~A: indentical short name ~S." {100277D8F3}> #<unused argument>
:QUIT T)
1: (`SB-DEBUG::RUN-HOOK` SB-EXT:*INVOKE-DEBUGGER-HOOK* #<SIMPLE-ERROR
"Options ~A and ~A: indentical short name ~S." {100277D8F3}>)
2: (INVOKE-DEBUGGER #<SIMPLE-ERROR "Options ~A and ~A: indentical short name ~S." {100277D8F3}>)
3: (ERROR "Options ~A and ~A: indentical short name ~S."
#<NET.DIDIERVERNA.CLON::LISPOBJ {1002705593}>
#<NET.DIDIERVERNA.CLON: :STROPT {1002705C03}> "s")
4: ((:METHOD NET.DIDIERVERNA.CLON::CHECK-NAME-CLASH
(NET.DIDIERVERNA.CLON::OPTION NET.DIDIERVERNA.CLON::OPTION))
#<NET.DIDIERVERNA.CLON::LISPOBJ {1002705593}>
#<NET.DIDIERVERNA.CLON::STROPT {1002705C03}>) [fast-method]
5: ((:METHOD INITIALIZE-INSTANCE :AFTER
(NET.DIDIERVERNA.CLON::CONTAINER)) #<NET.DIDIERVERNA.CLON::SYNOPSIS
{100270C013}>) [fast-method]