changeset 38946:3e197739b3df

parser: isolate command-line parser; richer parse() method
author Dmitry Selyutin <ghostmansd@gmail.com>
date Tue, 12 Sep 2017 00:28:07 +0300
parents 1276fc5fe5ca
children 3c2f99db15b0
files pygnulib/config.py pygnulib/error.py pygnulib/parser.py
diffstat 3 files changed, 924 insertions(+), 947 deletions(-) [+]
line wrap: on
line diff
--- a/pygnulib/config.py	Mon Sep 11 20:23:56 2017 +0300
+++ b/pygnulib/config.py	Tue Sep 12 00:28:07 2017 +0300
@@ -3,7 +3,6 @@
 
 
 
-import argparse as _argparse_
 import codecs as _codecs_
 import collections as _collections_
 import os as _os_
@@ -12,7 +11,6 @@
 
 from .error import type_assert as _type_assert_
 from .error import AutoconfVersionError as _AutoconfVersionError_
-from .error import CommandLineParsingError as _CommandLineParsingError_
 
 
 
@@ -501,10 +499,11 @@
                 self[key] = match[-1]
 
     def __gnulib_cache(self):
-        gnulib_cache = _os_.path.join(self.root, self.m4_base, "gnulib-cache.m4")
-        if not _os_.path.exists(gnulib_cache):
-            raise FileNotFoundError(gnulib_cache)
-        with _codecs_.open(gnulib_cache, "rb", "UTF-8") as stream:
+        path = _os_.path.join(self.root, self.m4_base, "gnulib-cache.m4")
+        path = _os_.path.normpath(path)
+        if not _os_.path.exists(path):
+            raise FileNotFoundError(path)
+        with _codecs_.open(path, "rb", "UTF-8") as stream:
             data = stream.read()
         for key in Cache._GNULIB_CACHE_BOOL_:
             (_, macro) = Cache._GNULIB_CACHE_[key]
@@ -521,10 +520,11 @@
                 self[key] = [_.strip() for _ in match[macro].split("\n") if _.strip()]
 
     def __gnulib_comp(self):
-        gnulib_comp = _os_.path.join(self.root, self.m4_base, "gnulib-comp.m4")
-        if _os_.path.exists(gnulib_comp):
-            raise FileNotFoundError(gnulib_comp)
-        with _codecs_.open(gnulib_comp, "rb", "UTF-8") as stream:
+        path = _os_.path.join(self.root, self.m4_base, "gnulib-comp.m4")
+        path = _os_.path.normpath(path)
+        if not _os_.path.exists(path):
+            raise FileNotFoundError(path)
+        with _codecs_.open(path, "rb", "UTF-8") as stream:
             data = stream.read()
         regex = "AC_DEFUN\\(\\[%s_FILE_LIST\\], \\[(.*?)\\]\\)" % self["macro-prefix"]
         pattern = _re_.compile(regex, _re_.S | _re_.M)
@@ -539,939 +539,3 @@
         keys.update(Cache._GNULIB_CACHE_.keys())
         keys.update(["files"])
         return (_ for _ in keys)
-
-
-
-class CommandLine(Base):
-    """gnulib-tool command line configuration"""
-    _LIST_ = (1 << 0)
-    _FIND_ = (1 << 1)
-    _IMPORT_ = (1 << 2)
-    _ADD_IMPORT_ = (1 << 3)
-    _REMOVE_IMPORT_ = (1 << 4)
-    _UPDATE_ = (1 << 5)
-    _TEST_DIRECTORY_ = (1 << 6)
-    _MEGA_TEST_DIRECTORY_ = (1 << 7)
-    _TEST_ = (1 << 8)
-    _MEGA_TEST_ = (1 << 9)
-    _COPY_FILE_ = (1 << 10)
-    _EXTRACT_DESCRIPTION_ = (1 << 11)
-    _EXTRACT_COMMENT_ = (1 << 12)
-    _EXTRACT_STATUS_ = (1 << 13)
-    _EXTRACT_NOTICE_ = (1 << 14)
-    _EXTRACT_APPLICABILITY_ = (1 << 15)
-    _EXTRACT_FILELIST_ = (1 << 16)
-    _EXTRACT_DEPENDENCIES_ = (1 << 17)
-    _EXTRACT_AUTOCONF_SNIPPET_ = (1 << 18)
-    _EXTRACT_AUTOMAKE_SNIPPET_ = (1 << 19)
-    _EXTRACT_INCLUDE_DIRECTIVE_ = (1 << 20)
-    _EXTRACT_LINK_DIRECTIVE_ = (1 << 21)
-    _EXTRACT_LICENSE_ = (1 << 22)
-    _EXTRACT_MAINTAINER_ = (1 << 23)
-    _EXTRACT_TESTS_MODULE_ = (1 << 24)
-    _ANY_IMPORT_ = _IMPORT_ | _ADD_IMPORT_ | _REMOVE_IMPORT_ | _UPDATE_
-    _ANY_TEST_ = _TEST_ | _MEGA_TEST_ | _TEST_DIRECTORY_ | _MEGA_TEST_DIRECTORY_
-    _ALL_ = _LIST_ | _FIND_ | _ANY_IMPORT_ | _ANY_TEST_
-    _MODES_ = (
-        (_LIST_, "list", ""),
-        (_FIND_, "find", "filename"),
-        (_IMPORT_, "import", "[module1 ... moduleN]"),
-        (_ADD_IMPORT_, "add-import", "[module1 ... moduleN]"),
-        (_REMOVE_IMPORT_, "remove-import", "[module1 ... moduleN]"),
-        (_UPDATE_, "update", ""),
-        (_TEST_DIRECTORY_, "testdir", "--dir=directory [module1 ... moduleN]"),
-        (_MEGA_TEST_DIRECTORY_, "megatestdir", "--dir=directory [module1 ... moduleN]"),
-        (_TEST_, "test", "--dir=directory [module1 ... moduleN]"),
-        (_MEGA_TEST_, "megatest", "--dir=directory [module1 ... moduleN]"),
-        (_EXTRACT_DESCRIPTION_, "extract-description", "module"),
-        (_EXTRACT_COMMENT_, "extract-comment", "module"),
-        (_EXTRACT_STATUS_, "extract-status", "module"),
-        (_EXTRACT_NOTICE_, "extract-notice", "module"),
-        (_EXTRACT_APPLICABILITY_, "extract-applicability", "module"),
-        (_EXTRACT_FILELIST_, "extract-filelist", "module"),
-        (_EXTRACT_DEPENDENCIES_, "extract-dependencies", "module"),
-        (_EXTRACT_AUTOCONF_SNIPPET_, "extract-autoconf-snippet", "module"),
-        (_EXTRACT_AUTOMAKE_SNIPPET_, "extract-automake-snippet", "module"),
-        (_EXTRACT_INCLUDE_DIRECTIVE_, "extract-include-directive", "module"),
-        (_EXTRACT_LINK_DIRECTIVE_, "extract-link-directive", "module"),
-        (_EXTRACT_LICENSE_, "extract-license", "module"),
-        (_EXTRACT_MAINTAINER_, "extract-maintainer", "module"),
-        (_EXTRACT_TESTS_MODULE_, "extract-tests-module", "module"),
-        (_COPY_FILE_, "copy", "file [destination]"),
-    )
-    _LINK_SYMBOLIC_ = (1 << 0)
-    _LINK_HARD_ = (1 << 1)
-    _LINK_LOCAL_ = (1 << 2)
-    _LINK_NOTICE_ = (1 << 3)
-
-
-    class _ModeAction_(_argparse_.Action):
-        def __init__(self, *args, **kwargs):
-            mode = kwargs["const"]
-            kwargs["dest"] = "mode"
-            kwargs["nargs"] = 0
-            if mode & CommandLine._UPDATE_:
-                kwargs["nargs"] = 0
-            elif mode & CommandLine._ANY_IMPORT_ or mode & CommandLine._ANY_TEST_:
-                kwargs["nargs"] = "+"
-                kwargs["metavar"] = "module0 ... moduleN"
-            elif mode & CommandLine._FIND_:
-                kwargs.pop("nargs")
-                kwargs["metavar"] = "FILE"
-            elif mode & CommandLine._COPY_FILE_:
-                kwargs["nargs"] = "+"
-                kwargs["metavar"] = "SRC [DST]"
-            super().__init__(*args, **kwargs)
-
-
-        def __call__(self, parser, namespace, value, option=None):
-            old_option = ""
-            new_option = option
-            new_mode = None
-            old_mode = getattr(namespace, self.dest)
-            for (mode, name, _) in CommandLine._MODES_:
-                option = ("--" + name)
-                if mode == old_mode:
-                    old_option = option
-                if option == new_option:
-                    new_mode = mode
-                if old_option and new_mode:
-                    break
-            if old_mode != new_mode:
-                if not old_mode is None:
-                    fmt = "argument {0}: not allowed with {1}"
-                    parser.error(fmt.format(new_option, old_option))
-                setattr(namespace, "modules", list(value))
-                setattr(namespace, self.dest, new_mode)
-
-
-    class _AvoidAction_(_argparse_.Action):
-        def __call__(self, parser, namespace, value, option=None):
-            values = getattr(namespace, self.dest)
-            values += value
-
-
-    class _VerboseAction_(_argparse_.Action):
-        def __call__(self, parser, namespace, value, option=None):
-            value = getattr(namespace, self.dest)
-            verbose = option in ("-v", "--verbose")
-            value += +1 if verbose else -1
-            setattr(namespace, self.dest, value)
-
-
-    class _LinkAction_(_argparse_.Action):
-        def __call__(self, parser, namespace, value, option=None):
-            flags = getattr(namespace, self.dest)
-            symlink = ("-s", "--symlink", "--local-symlink", "-S", "--more-symlink")
-            hardlink = ("-h", "--hardlink", "--local-hardlink", "-H", "--more-hardlink")
-            local = ("--local-symlink", "--local-hardlink")
-            disable_notice = ("-S", "--more-symlink", "-H", "--more-hardlink")
-            if option in symlink:
-                if flags & CommandLine._LINK_HARD_:
-                    parser.error("conflicting --symlink and --hardlink options")
-                flags |= CommandLine._LINK_SYMBOLIC_
-            if option in hardlink:
-                if flags & CommandLine._LINK_SYMBOLIC_:
-                    parser.error("conflicting --symlink and --hardlink options")
-                flags |= CommandLine._LINK_HARD_
-            if option in local:
-                flags |= CommandLine._LINK_LOCAL_
-            if option in disable_notice:
-                flags &= ~CommandLine._LINK_NOTICE_
-            setattr(namespace, self.dest, flags)
-
-
-    # section0: (section0_modes, (([section0_option0, section0_optionN], **section0_kwargs)))
-    # sectionN: (sectionN_modes, (([sectionN_option0, sectionN_optionN], **sectionN_kwargs)))
-    #
-    # for (name, flags, arguments) in sections:
-    #     for argument in arguments:
-    #         (options, kwargs) = argument
-    _SECTIONS_ = (
-        (
-            "Operation modes",
-            None,
-            (
-                (["-l", "--list"], {
-                    "help": (
-                        "print the available module names",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _LIST_,
-                }),
-                (["-f", "--find"], {
-                    "help": (
-                        "find the modules which contain the specified file",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _FIND_,
-                }),
-                (["-i", "--import"], {
-                    "help": (
-                        "import the given modules into the current package",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _IMPORT_,
-                }),
-                (["-a", "--add-import"], {
-                    "help": (
-                        "augment the list of imports from gnulib into the",
-                        "current package, by adding the given modules;",
-                        "if no modules are specified, update the current",
-                        "package from the current gnulib",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _ADD_IMPORT_,
-                }),
-                (["-r", "--remove-import"], {
-                    "help": (
-                        "reduce the list of imports from gnulib into the",
-                        "current package, by removing the given modules",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _REMOVE_IMPORT_,
-                }),
-                (["-u", "--update"], {
-                    "help": (
-                        "update the current package, restore files omitted",
-                        "from version control",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _UPDATE_,
-                }),
-
-                (["--extract-description"], {
-                    "help": (
-                        "extract the description",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _EXTRACT_DESCRIPTION_,
-                }),
-                (["--extract-comment"], {
-                    "help": (
-                        "extract the comment",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _EXTRACT_COMMENT_,
-                }),
-                (["--extract-status"], {
-                    "help": (
-                        "extract the status (obsolete etc.)",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _EXTRACT_STATUS_,
-                }),
-                (["--extract-notice"], {
-                    "help": (
-                        "extract the notice or banner",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _EXTRACT_NOTICE_,
-                }),
-                (["--extract-applicability"], {
-                    "help": (
-                        "extract the applicability",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _EXTRACT_APPLICABILITY_,
-                }),
-                (["--extract-filelist"], {
-                    "help": (
-                        "extract the list of files",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _EXTRACT_FILELIST_,
-                }),
-                (["--extract-dependencies"], {
-                    "help": (
-                        "extract the dependencies",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _EXTRACT_DEPENDENCIES_,
-                }),
-                (["--extract-autoconf-snippet"], {
-                    "help": (
-                        "extract the snippet for configure.ac",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _EXTRACT_AUTOCONF_SNIPPET_,
-                }),
-                (["--extract-automake-snippet"], {
-                    "help": (
-                        "extract the snippet for library makefile",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _EXTRACT_AUTOMAKE_SNIPPET_,
-                }),
-                (["--extract-include-directive"], {
-                    "help": (
-                        "extract the #include directive",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _EXTRACT_INCLUDE_DIRECTIVE_,
-                }),
-                (["--extract-link-directive"], {
-                    "help": (
-                        "extract the linker directive",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _EXTRACT_LINK_DIRECTIVE_,
-                }),
-                (["--extract-license"], {
-                    "help": (
-                        "report the license terms of the source files",
-                        "under lib/",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _EXTRACT_LICENSE_,
-                }),
-                (["--extract-maintainer"], {
-                    "help": (
-                        "report the maintainer(s) inside gnulib",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _EXTRACT_MAINTAINER_,
-                }),
-                (["--extract-tests-module"], {
-                    "help": (
-                        "report the unit test module, if it exists",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _EXTRACT_TESTS_MODULE_,
-                }),
-
-                (["--copy-file"], {
-                    "help": (
-                        "copy a file that is not part of any module",
-                    ),
-                    "action": _ModeAction_,
-                    "const": _COPY_FILE_,
-                }),
-            ),
-        ),
-
-
-        (
-            "General options",
-            _ALL_,
-            (
-                (["--dir"], {
-                    "help": (
-                        "specify the target directory; on --import,",
-                        "this specifies where your configure.ac",
-                        "can be found; defaults to current directory",
-                    ),
-                    "dest": "root",
-                    "default": _argparse_.SUPPRESS,
-                    "metavar": "DIRECTORY",
-                }),
-                (["--local-dir"], {
-                    "help": (
-                        "pecify a local override directory where to look",
-                        "up files before looking in gnulib's directory",
-                    ),
-                    "dest": "local",
-                    "default": _argparse_.SUPPRESS,
-                    "nargs": 1,
-                    "metavar": "DIRECTORY",
-                }),
-                (["-v", "--verbose"], {
-                    "help": (
-                        "increase verbosity; may be repeated",
-                    ),
-                    "action": _VerboseAction_,
-                    "dest": "verbosity",
-                    "default": 0,
-                    "nargs": 0,
-                }),
-                (["-q", "--quiet"], {
-                    "help": (
-                        "decrease verbosity; may be repeated",
-                    ),
-                    "action": _VerboseAction_,
-                    "dest": "verbosity",
-                    "default": 0,
-                    "nargs": 0,
-                }),
-            ),
-        ),
-
-
-        (
-            "Options for --import, --add/remove-import, --update",
-            _ANY_IMPORT_,
-            (
-                (["--dry-run"], {
-                    "help": (
-                        "only print what would have been done",
-                    ),
-                    "dest": "dry_run",
-                    "action": "store_true",
-                    "default": False,
-                }),
-            ),
-        ),
-
-
-        (
-            "Options for --import, --add/remove-import",
-            (_IMPORT_ | _ADD_IMPORT_ | _REMOVE_IMPORT_),
-            (
-                (["--with-tests"], {
-                    "help": (
-                        "include unit tests for the included modules",
-                    ),
-                    "dest": "tests",
-                    "action": "store_true",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--single-configure"], {
-                    "help": (
-                        "generate a single configure file, not a separate",
-                        "configure file for the tests directory",
-                    ),
-                    "dest": "single_configure",
-                    "action": "store_true",
-                    "default": False,
-                }),
-            ),
-        ),
-
-
-        (
-            "Options for --create-[mega]testdir, --[mega]test",
-            _ANY_TEST_,
-            (
-                (["--without-tests"], {
-                    "help": (
-                        "don't include unit tests for the included modules",
-                    ),
-                    "dest": "tests",
-                    "action": "store_false",
-                    "default": _argparse_.SUPPRESS,
-                }),
-            ),
-        ),
-
-
-        (
-            "Options for --import, --add/remove-import,"
-            "\n"
-            "            --create-[mega]testdir, --[mega]test",
-            (_ANY_IMPORT_ & ~_UPDATE_) | _ANY_TEST_,
-            (
-                (["--with-obsolete"], {
-                    "help": (
-                        "include obsolete modules when they occur among the",
-                        "dependencies; by default, dependencies to obsolete",
-                        "modules are ignored",
-                    ),
-                    "dest": "obsolete",
-                    "action": "store_true",
-                    "default": _argparse_.SUPPRESS,
-                }),
-
-                (["--with-c++-tests"], {
-                    "help": (
-                        "include even unit tests for C++ interoperability",
-                    ),
-                    "dest": "cxx_tests",
-                    "action": "store_true",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--without-c++-tests"], {
-                    "help": (
-                        "exclude unit tests for C++ interoperability",
-                    ),
-                    "dest": "cxx_tests",
-                    "action": "store_false",
-                    "default": _argparse_.SUPPRESS,
-                }),
-
-                (["--with-longrunning-tests"], {
-                    "help": (
-                        "include even unit tests that are long-runners",
-                    ),
-                    "dest": "longrunning_tests",
-                    "action": "store_true",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--without-longrunning-tests"], {
-                    "help": (
-                        "exclude unit tests that are long-runners",
-                    ),
-                    "dest": "longrunning_tests",
-                    "action": "store_false",
-                    "default": _argparse_.SUPPRESS,
-                }),
-
-                (["--with-privileged-tests"], {
-                    "help": (
-                        "include even unit tests that require root privileges",
-                    ),
-                    "dest": "privileged_tests",
-                    "action": "store_true",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--without-privileged-tests"], {
-                    "help": (
-                        "exclude unit tests that require root privileges",
-                    ),
-                    "dest": "privileged_tests",
-                    "action": "store_false",
-                    "default": _argparse_.SUPPRESS,
-                }),
-
-                (["--with-unportable-tests"], {
-                    "help": (
-                        "include even unit tests that fail on some platforms",
-                    ),
-                    "dest": "unportable_tests",
-                    "action": "store_true",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--without-unportable-tests"], {
-                    "help": (
-                        "exclude unit tests that fail on some platforms",
-                    ),
-                    "dest": "unportable_tests",
-                    "action": "store_false",
-                    "default": _argparse_.SUPPRESS,
-                }),
-
-                (["--with-all-tests"], {
-                    "help": (
-                        "include all kinds of problematic unit tests",
-                    ),
-                    "dest": "all_tests",
-                    "action": "store_true",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--avoid"], {
-                    "help": (
-                        "avoid including the given MODULE; useful if you",
-                        "have code that provides equivalent functionality;",
-                        "this option can be repeated",
-                    ),
-                    "action": _AvoidAction_,
-                    "nargs": 1,
-                    "default": [],
-                    "metavar": "MODULE",
-                }),
-                (["--conditional-dependencies"], {
-                    "help": (
-                        "support conditional dependencies (may save configure",
-                        "time and object code)",
-                    ),
-                    "dest": "conddeps",
-                    "action": "store_true",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--no-conditional-dependencies"], {
-                    "help": (
-                        "don't use conditional dependencies",
-                    ),
-                    "dest": "conddeps",
-                    "action": "store_false",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--libtool"], {
-                    "help": (
-                        "use libtool rules",
-                    ),
-                    "dest": "libtool",
-                    "action": "store_true",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--no-libtool"], {
-                    "help": (
-                        "don't use libtool rules",
-                    ),
-                    "dest": "libtool",
-                    "action": "store_false",
-                    "default": _argparse_.SUPPRESS,
-                }),
-            ),
-        ),
-
-
-        (
-            "Options for --import, --add/remove-import",
-            (_ANY_IMPORT_ & ~_UPDATE_),
-            (
-                (["--lib"], {
-                    "help": (
-                        "specify the library name; defaults to 'libgnu'",
-                    ),
-                    "metavar": "LIBRARY",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--source-base"], {
-                    "help": (
-                        "directory relative to --dir where source code is",
-                        "placed (default \"lib\")",
-                    ),
-                    "metavar": "DIRECTORY",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--m4-base"], {
-                    "help": (
-                        "directory relative to --dir where *.m4 macros are",
-                        "placed (default \"m4\")",
-                    ),
-                    "metavar": "DIRECTORY",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--po-base"], {
-                    "help": (
-                        "directory relative to --dir where *.po files are",
-                        "placed (default \"po\")",
-                    ),
-                    "metavar": "DIRECTORY",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--doc-base"], {
-                    "help": (
-                        "directory relative to --dir where doc files are",
-                        "placed (default \"doc\")",
-                    ),
-                    "metavar": "DIRECTORY",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--tests-base"], {
-                    "help": (
-                        "directory relative to --dir where unit tests are",
-                        "placed (default \"tests\")",
-                    ),
-                    "metavar": "DIRECTORY",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--aux-dir"], {
-                    "help": (
-                        "directory relative to --dir where auxiliary build",
-                        "tools are placed (default comes from configure.ac);",
-                    ),
-                    "dest": "auxdir",
-                    "metavar": "DIRECTORY",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--lgpl"], {
-                    "help": (
-                        "abort if modules aren't available under the LGPL;",
-                        "also modify license template from GPL to LGPL;",
-                        "the version number of the LGPL can be specified;",
-                        "the default is currently LGPLv3.",
-                    ),
-                    "choices": (2, 3),
-                    "type": int,
-                    "metavar": "[=2|=3]",
-                }),
-                (["--makefile-name"], {
-                    "help": (
-                        "name of makefile in automake syntax in the",
-                        "source-base and tests-base directories",
-                        "(default \"Makefile.am\")",
-                    ),
-                    "metavar": "NAME",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--macro-prefix"], {
-                    "help": (
-                        "specify the prefix of the macros 'gl_EARLY' and",
-                        "'gl_INIT'; default is 'gl'",
-                    ),
-                    "metavar": "PREFIX",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--po-domain"], {
-                    "help": (
-                        "specify the prefix of the i18n domain; usually use",
-                        "the package name; a suffix '-gnulib' is appended",
-                    ),
-                    "metavar": "NAME",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--witness-c-macro"], {
-                    "help": (
-                        "specify the C macro that is defined when the",
-                        "sources in this directory are compiled or used",
-                    ),
-                    "metavar": "NAME",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--vc-files"], {
-                    "help": (
-                        "update version control related files",
-                        "(.gitignore and/or .cvsignore)",
-                    ),
-                    "dest": "vc_files",
-                    "action": "store_true",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--no-vc-files"], {
-                    "help": (
-                        "don't update version control related files",
-                        "(.gitignore and/or .cvsignore)",
-                    ),
-                    "dest": "libtool",
-                    "action": "store_false",
-                    "default": _argparse_.SUPPRESS,
-                }),
-                (["--no-changelog"], {
-                    "help": (
-                        "don't update or create ChangeLog files;",
-                        "this option is currently deprecated",
-                    ),
-                    "action": "store_const",
-                    "const": None,
-                    "default": _argparse_.SUPPRESS,
-                }),
-            ),
-        ),
-
-
-        (
-            "Options for --import, --add/remove-import, --update"
-            "\n"
-            "            --create-[mega]testdir, --[mega]test",
-            _ANY_IMPORT_ | _ANY_TEST_,
-            (
-                (["-s", "--symlink"], {
-                    "help": (
-                        "make symbolic links instead of copying files",
-                    ),
-                    "dest": "link",
-                    "action": _LinkAction_,
-                    "nargs": 0,
-                    "default": _LINK_NOTICE_,
-                }),
-                (["--local-symlink"], {
-                    "help": (
-                        "make symbolic links instead of copying files, only",
-                        "for files from the local override directory"
-                    ),
-                    "dest": "link",
-                    "action": _LinkAction_,
-                    "nargs": 0,
-                    "default": _LINK_NOTICE_,
-                }),
-                (["-h", "--hardlink"], {
-                    "help": (
-                        "make hard links instead of copying files",
-                    ),
-                    "dest": "link",
-                    "action": _LinkAction_,
-                    "nargs": 0,
-                    "default": _LINK_NOTICE_,
-                }),
-                (["--local-hardlink"], {
-                    "help": (
-                        "make hard links instead of copying files, only",
-                        "for files from the local override directory"
-                    ),
-                    "dest": "link",
-                    "action": _LinkAction_,
-                    "nargs": 0,
-                    "default": _LINK_NOTICE_,
-                }),
-            ),
-        ),
-
-
-        (
-            "Options for --import, --add/remove-import, --update",
-            _ANY_IMPORT_,
-            (
-                (["-S", "--more-symlink"], {
-                    "help": (
-                        "make symbolic links instead of copying files and",
-                        "don't replace copyright notices",
-                    ),
-                    "dest": "link",
-                    "action": _LinkAction_,
-                    "nargs": 0,
-                    "default": _LINK_NOTICE_,
-                }),
-                (["-H", "--more-hardlink"], {
-                    "help": (
-                        "make symbolic links instead of copying files and",
-                        "don't replace copyright notices",
-                    ),
-                    "dest": "link",
-                    "action": _LinkAction_,
-                    "nargs": 0,
-                    "default": _LINK_NOTICE_,
-                }),
-            ),
-        ),
-    )
-
-
-    def __error(self, message):
-        raise _CommandLineParsingError_(self.__program, message)
-
-
-    def __format_help(self):
-        lines = [""]
-        for (name, _, args) in CommandLine._SECTIONS_:
-            offset = -1
-            lines += ["", "%s:" % name, ""]
-            for arg in args:
-                (options, kwargs) = arg
-                options = ", ".join(options)
-                if "metavar" in kwargs:
-                    options += (" " + kwargs["metavar"])
-                length = len(options)
-                if length > offset:
-                    offset = length
-            fmt1 = "      %-{0}s    %s".format(offset)
-            fmt2 = "      " + " " * offset + "    %s"
-            for arg in args:
-                (options, kwargs) = arg
-                options = ", ".join(options)
-                if "metavar" in kwargs:
-                    sep = "" if "choices" in kwargs else "="
-                    options += ("%s%s" % (sep, kwargs["metavar"]))
-                description = iter(kwargs["help"])
-                line = next(description)
-                lines += [fmt1 % (options, line)]
-                for line in description:
-                    lines += [fmt2 % line]
-            lines += [""]
-        return self.__format_usage()[:-1] + "\n".join(lines)
-
-
-    def __format_usage(self):
-        iterable = iter(CommandLine._MODES_)
-        (_, cmd, args) = next(iterable)
-        fmt = (" --{cmd}" + (" {args}" if args else ""))
-        lines = ["usage: {program}" + fmt.format(cmd=cmd, args=args)]
-        for (_, cmd, args) in iterable:
-            fmt = (" --{cmd}" + (" {args}" if args else ""))
-            lines += ["       {program}" + fmt.format(cmd=cmd, args=args)]
-        lines += ["", ""]
-        return "\n".join(lines).format(program=self.__program)
-
-
-    def __init__(self, program, **kwargs):
-        _type_assert_("program", program, str)
-        super().__init__(**kwargs)
-        self.__parser = _argparse_.ArgumentParser(prog=program, add_help=False, allow_abbrev=False)
-        for (_, _, args) in CommandLine._SECTIONS_:
-            for arg in args:
-                (options, kwargs) = arg
-                self.__parser.add_argument(*options, **kwargs)
-        self.__program = _os_.path.basename(program)
-        self.__parser.error = self.__error
-        self.__parser.format_help = self.__format_help
-        self.__parser.format_usage = self.__format_usage
-        self.__mode = None
-        self.__dry_run = None
-        self.__link = None
-        self.__single_configure = None
-        self.__verbosity = None
-        self.__namespace = None
-
-
-    def parse(self, *arguments):
-        self.__namespace = self.__parser.parse_args(arguments)
-        self.__namespace = vars(self.__namespace)
-        self.__mode = self.__namespace.pop("mode")
-        if self.__mode is None:
-            self.__parser.error("no operating mode selected")
-        self.__dry_run = self.__namespace.pop("dry_run")
-        self.__link = self.__namespace.pop("link", None)
-        self.__single_configure = self.__namespace.pop("single_configure")
-        self.__verbosity = self.__namespace.pop("verbosity")
-        self.__namespace.pop("no_changelog", None)
-        for (key, value) in self.__namespace.items():
-            self[key] = value
-
-
-    @property
-    def mode(self):
-        """operating mode"""
-        if self.__namespace is None:
-            raise ValueError("command-line parser not ready")
-        for (flag, cmd, _) in CommandLine._MODES_:
-            if flag == self.__mode:
-                return cmd
-        return ""
-
-
-    @property
-    def dry_run(self):
-        """running in dry-run mode?"""
-        if self.__namespace is None:
-            raise ValueError("command-line parser not ready")
-        return self.__dry_run
-
-
-    @property
-    def verbosity(self):
-        """verbosity level"""
-        if self.__namespace is None:
-            raise ValueError("command-line parser not ready")
-        return self.__verbosity
-
-
-    @property
-    def single_configure(self):
-        """generate a single configure file?"""
-        if self.__namespace is None:
-            raise ValueError("command-line parser not ready")
-        return self.__single_configure
-
-
-    @property
-    def symlink(self):
-        """make symbolic links instead of copying files?"""
-        if self.__namespace is None:
-            raise ValueError("command-line parser not ready")
-        return bool(self.__link & CommandLine._LINK_SYMBOLIC_)
-
-
-    def hardlink(self):
-        """make hard links instead of copying files?"""
-        if self.__namespace is None:
-            raise ValueError("command-line parser not ready")
-        return bool(self.__link & CommandLine._LINK_HARD_)
-
-
-    @property
-    def only_local_links(self):
-        """make links only for files from the local override directory?"""
-        if self.__namespace is None:
-            raise ValueError("command-line parser not ready")
-        return bool(self.__link & CommandLine._LINK_LOCAL_)
-
-
-    @property
-    def allow_license_update(self):
-        """allow to update license notice?"""
-        if self.__namespace is None:
-            raise ValueError("command-line parser not ready")
-        return bool(self.__link & CommandLine._LINK_NOTICE_)
-
-
-    @property
-    def usage(self):
-        """usage message"""
-        return self.__format_usage()[:-1]
-
-
-    @property
-    def help(self):
-        """help message"""
-        return self.__format_help()
-
-
-    def update(self, config):
-        """update each key if and only if it was not set explicitly"""
-        if self.__namespace is None:
-            raise ValueError("command-line parser not ready")
-        _type_assert_("config", config, Base)
-        explicit = self.__namespace.keys()
-        for key in Base().keys():
-            if key not in explicit:
-                self[key] = config[key]
--- a/pygnulib/error.py	Mon Sep 11 20:23:56 2017 +0300
+++ b/pygnulib/error.py	Tue Sep 12 00:28:07 2017 +0300
@@ -29,7 +29,7 @@
 
 
 
-class CommandLineParsingError(Exception):
+class CommandLineError(Exception):
     def __init__(self, program, message):
         super().__init__("{0}: {1}".format(program, message))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pygnulib/parser.py	Tue Sep 12 00:28:07 2017 +0300
@@ -0,0 +1,913 @@
+#!/usr/bin/python
+# encoding: UTF-8
+
+
+
+import argparse as _argparse_
+import enum as _enum_
+import os as _os_
+
+from .config import Base as _BaseConfig_
+from .error import CommandLineError as _CommandLineError_
+
+
+
+class CommandLine:
+    """gnulib-tool command line configuration"""
+    _LIST_ = (1 << 0)
+    _FIND_ = (1 << 1)
+    _IMPORT_ = (1 << 2)
+    _ADD_IMPORT_ = (1 << 3)
+    _REMOVE_IMPORT_ = (1 << 4)
+    _UPDATE_ = (1 << 5)
+    _TEST_DIRECTORY_ = (1 << 6)
+    _MEGA_TEST_DIRECTORY_ = (1 << 7)
+    _TEST_ = (1 << 8)
+    _MEGA_TEST_ = (1 << 9)
+    _COPY_FILE_ = (1 << 10)
+    _EXTRACT_DESCRIPTION_ = (1 << 11)
+    _EXTRACT_COMMENT_ = (1 << 12)
+    _EXTRACT_STATUS_ = (1 << 13)
+    _EXTRACT_NOTICE_ = (1 << 14)
+    _EXTRACT_APPLICABILITY_ = (1 << 15)
+    _EXTRACT_FILELIST_ = (1 << 16)
+    _EXTRACT_DEPENDENCIES_ = (1 << 17)
+    _EXTRACT_AUTOCONF_SNIPPET_ = (1 << 18)
+    _EXTRACT_AUTOMAKE_SNIPPET_ = (1 << 19)
+    _EXTRACT_INCLUDE_DIRECTIVE_ = (1 << 20)
+    _EXTRACT_LINK_DIRECTIVE_ = (1 << 21)
+    _EXTRACT_LICENSE_ = (1 << 22)
+    _EXTRACT_MAINTAINER_ = (1 << 23)
+    _EXTRACT_TESTS_MODULE_ = (1 << 24)
+    _HELP_ = (1 << 25)
+    _ANY_IMPORT_ = _IMPORT_ | _ADD_IMPORT_ | _REMOVE_IMPORT_ | _UPDATE_
+    _ANY_TEST_ = _TEST_ | _MEGA_TEST_ | _TEST_DIRECTORY_ | _MEGA_TEST_DIRECTORY_
+    _ALL_ = _LIST_ | _FIND_ | _ANY_IMPORT_ | _ANY_TEST_
+    _MODES_ = (
+        (_LIST_, "list", ""),
+        (_FIND_, "find", "filename"),
+        (_IMPORT_, "import", "[module1 ... moduleN]"),
+        (_ADD_IMPORT_, "add-import", "[module1 ... moduleN]"),
+        (_REMOVE_IMPORT_, "remove-import", "[module1 ... moduleN]"),
+        (_UPDATE_, "update", ""),
+        (_TEST_DIRECTORY_, "testdir", "--dir=directory [module1 ... moduleN]"),
+        (_MEGA_TEST_DIRECTORY_, "megatestdir", "--dir=directory [module1 ... moduleN]"),
+        (_TEST_, "test", "--dir=directory [module1 ... moduleN]"),
+        (_MEGA_TEST_, "megatest", "--dir=directory [module1 ... moduleN]"),
+        (_EXTRACT_DESCRIPTION_, "extract-description", "module"),
+        (_EXTRACT_COMMENT_, "extract-comment", "module"),
+        (_EXTRACT_STATUS_, "extract-status", "module"),
+        (_EXTRACT_NOTICE_, "extract-notice", "module"),
+        (_EXTRACT_APPLICABILITY_, "extract-applicability", "module"),
+        (_EXTRACT_FILELIST_, "extract-filelist", "module"),
+        (_EXTRACT_DEPENDENCIES_, "extract-dependencies", "module"),
+        (_EXTRACT_AUTOCONF_SNIPPET_, "extract-autoconf-snippet", "module"),
+        (_EXTRACT_AUTOMAKE_SNIPPET_, "extract-automake-snippet", "module"),
+        (_EXTRACT_INCLUDE_DIRECTIVE_, "extract-include-directive", "module"),
+        (_EXTRACT_LINK_DIRECTIVE_, "extract-link-directive", "module"),
+        (_EXTRACT_LICENSE_, "extract-license", "module"),
+        (_EXTRACT_MAINTAINER_, "extract-maintainer", "module"),
+        (_EXTRACT_TESTS_MODULE_, "extract-tests-module", "module"),
+        (_COPY_FILE_, "copy", "file [destination]"),
+        (_HELP_, "help", ""),
+    )
+    _LINK_SYMBOLIC_ = (1 << 0)
+    _LINK_HARD_ = (1 << 1)
+    _LINK_LOCAL_ = (1 << 2)
+    _LINK_NOTICE_ = (1 << 3)
+
+
+    class _ModeAction_(_argparse_.Action):
+        def __init__(self, *args, **kwargs):
+            mode = kwargs["const"]
+            kwargs["dest"] = "mode"
+            kwargs["nargs"] = 0
+            if mode & CommandLine._UPDATE_:
+                kwargs["nargs"] = 0
+            elif mode & CommandLine._ANY_IMPORT_ or mode & CommandLine._ANY_TEST_:
+                kwargs["nargs"] = "+"
+                kwargs["metavar"] = "module0 ... moduleN"
+            elif mode & CommandLine._FIND_:
+                kwargs.pop("nargs")
+                kwargs["metavar"] = "FILE"
+            elif mode & CommandLine._COPY_FILE_:
+                kwargs["nargs"] = "+"
+                kwargs["metavar"] = "SRC [DST]"
+            super().__init__(*args, **kwargs)
+
+
+        def __call__(self, parser, namespace, value, option=None):
+            old_option = ""
+            new_option = option
+            new_mode = None
+            old_mode = getattr(namespace, self.dest)
+            for (mode, name, _) in CommandLine._MODES_:
+                option = ("--" + name)
+                if mode == old_mode:
+                    old_option = option
+                if option == new_option:
+                    new_mode = mode
+                if old_option and new_mode:
+                    break
+            if old_mode != new_mode:
+                if not old_mode is None:
+                    fmt = "argument {0}: not allowed with {1}"
+                    parser.error(fmt.format(new_option, old_option))
+                setattr(namespace, "modules", list(value))
+                setattr(namespace, self.dest, new_mode)
+
+
+    class _AvoidAction_(_argparse_.Action):
+        def __call__(self, parser, namespace, value, option=None):
+            values = getattr(namespace, self.dest)
+            values += value
+
+
+    class _VerboseAction_(_argparse_.Action):
+        def __call__(self, parser, namespace, value, option=None):
+            value = getattr(namespace, self.dest)
+            verbose = option in ("-v", "--verbose")
+            value += +1 if verbose else -1
+            setattr(namespace, self.dest, value)
+
+
+    class _LinkAction_(_argparse_.Action):
+        def __call__(self, parser, namespace, value, option=None):
+            flags = getattr(namespace, self.dest)
+            symlink = ("-s", "--symlink", "--local-symlink", "-S", "--more-symlink")
+            hardlink = ("-h", "--hardlink", "--local-hardlink", "-H", "--more-hardlink")
+            local = ("--local-symlink", "--local-hardlink")
+            disable_notice = ("-S", "--more-symlink", "-H", "--more-hardlink")
+            if option in symlink:
+                if flags & CommandLine._LINK_HARD_:
+                    parser.error("conflicting --symlink and --hardlink options")
+                flags |= CommandLine._LINK_SYMBOLIC_
+            if option in hardlink:
+                if flags & CommandLine._LINK_SYMBOLIC_:
+                    parser.error("conflicting --symlink and --hardlink options")
+                flags |= CommandLine._LINK_HARD_
+            if option in local:
+                flags |= CommandLine._LINK_LOCAL_
+            if option in disable_notice:
+                flags &= ~CommandLine._LINK_NOTICE_
+            setattr(namespace, self.dest, flags)
+
+
+    # section0: (section0_modes, (([section0_option0, section0_optionN], **section0_kwargs)))
+    # sectionN: (sectionN_modes, (([sectionN_option0, sectionN_optionN], **sectionN_kwargs)))
+    #
+    # for (name, flags, arguments) in sections:
+    #     for argument in arguments:
+    #         (options, kwargs) = argument
+    _SECTIONS_ = (
+        (
+            "Operation modes",
+            None,
+            (
+                (["-l", "--list"], {
+                    "help": (
+                        "print the available module names",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _LIST_,
+                }),
+                (["-f", "--find"], {
+                    "help": (
+                        "find the modules which contain the specified file",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _FIND_,
+                }),
+                (["-i", "--import"], {
+                    "help": (
+                        "import the given modules into the current package",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _IMPORT_,
+                }),
+                (["-a", "--add-import"], {
+                    "help": (
+                        "augment the list of imports from gnulib into the",
+                        "current package, by adding the given modules;",
+                        "if no modules are specified, update the current",
+                        "package from the current gnulib",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _ADD_IMPORT_,
+                }),
+                (["-r", "--remove-import"], {
+                    "help": (
+                        "reduce the list of imports from gnulib into the",
+                        "current package, by removing the given modules",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _REMOVE_IMPORT_,
+                }),
+                (["-u", "--update"], {
+                    "help": (
+                        "update the current package, restore files omitted",
+                        "from version control",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _UPDATE_,
+                }),
+
+                (["--extract-description"], {
+                    "help": (
+                        "extract the description",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _EXTRACT_DESCRIPTION_,
+                }),
+                (["--extract-comment"], {
+                    "help": (
+                        "extract the comment",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _EXTRACT_COMMENT_,
+                }),
+                (["--extract-status"], {
+                    "help": (
+                        "extract the status (obsolete etc.)",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _EXTRACT_STATUS_,
+                }),
+                (["--extract-notice"], {
+                    "help": (
+                        "extract the notice or banner",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _EXTRACT_NOTICE_,
+                }),
+                (["--extract-applicability"], {
+                    "help": (
+                        "extract the applicability",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _EXTRACT_APPLICABILITY_,
+                }),
+                (["--extract-filelist"], {
+                    "help": (
+                        "extract the list of files",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _EXTRACT_FILELIST_,
+                }),
+                (["--extract-dependencies"], {
+                    "help": (
+                        "extract the dependencies",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _EXTRACT_DEPENDENCIES_,
+                }),
+                (["--extract-autoconf-snippet"], {
+                    "help": (
+                        "extract the snippet for configure.ac",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _EXTRACT_AUTOCONF_SNIPPET_,
+                }),
+                (["--extract-automake-snippet"], {
+                    "help": (
+                        "extract the snippet for library makefile",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _EXTRACT_AUTOMAKE_SNIPPET_,
+                }),
+                (["--extract-include-directive"], {
+                    "help": (
+                        "extract the #include directive",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _EXTRACT_INCLUDE_DIRECTIVE_,
+                }),
+                (["--extract-link-directive"], {
+                    "help": (
+                        "extract the linker directive",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _EXTRACT_LINK_DIRECTIVE_,
+                }),
+                (["--extract-license"], {
+                    "help": (
+                        "report the license terms of the source files",
+                        "under lib/",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _EXTRACT_LICENSE_,
+                }),
+                (["--extract-maintainer"], {
+                    "help": (
+                        "report the maintainer(s) inside gnulib",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _EXTRACT_MAINTAINER_,
+                }),
+                (["--extract-tests-module"], {
+                    "help": (
+                        "report the unit test module, if it exists",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _EXTRACT_TESTS_MODULE_,
+                }),
+
+                (["--copy-file"], {
+                    "help": (
+                        "copy a file that is not part of any module",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _COPY_FILE_,
+                }),
+
+                (["--help"], {
+                    "help": (
+                        "show this help text",
+                    ),
+                    "action": _ModeAction_,
+                    "const": _HELP_,
+                }),
+            ),
+        ),
+
+
+        (
+            "General options",
+            _ALL_,
+            (
+                (["--dir"], {
+                    "help": (
+                        "specify the target directory; on --import,",
+                        "this specifies where your configure.ac",
+                        "can be found; defaults to current directory",
+                    ),
+                    "dest": "root",
+                    "default": _argparse_.SUPPRESS,
+                    "metavar": "DIRECTORY",
+                }),
+                (["--local-dir"], {
+                    "help": (
+                        "pecify a local override directory where to look",
+                        "up files before looking in gnulib's directory",
+                    ),
+                    "dest": "local",
+                    "default": _argparse_.SUPPRESS,
+                    "nargs": 1,
+                    "metavar": "DIRECTORY",
+                }),
+                (["-v", "--verbose"], {
+                    "help": (
+                        "increase verbosity; may be repeated",
+                    ),
+                    "action": _VerboseAction_,
+                    "dest": "verbosity",
+                    "default": 0,
+                    "nargs": 0,
+                }),
+                (["-q", "--quiet"], {
+                    "help": (
+                        "decrease verbosity; may be repeated",
+                    ),
+                    "action": _VerboseAction_,
+                    "dest": "verbosity",
+                    "default": 0,
+                    "nargs": 0,
+                }),
+            ),
+        ),
+
+
+        (
+            "Options for --import, --add/remove-import, --update",
+            _ANY_IMPORT_,
+            (
+                (["--dry-run"], {
+                    "help": (
+                        "only print what would have been done",
+                    ),
+                    "dest": "dry_run",
+                    "action": "store_true",
+                    "default": False,
+                }),
+            ),
+        ),
+
+
+        (
+            "Options for --import, --add/remove-import",
+            (_IMPORT_ | _ADD_IMPORT_ | _REMOVE_IMPORT_),
+            (
+                (["--with-tests"], {
+                    "help": (
+                        "include unit tests for the included modules",
+                    ),
+                    "dest": "tests",
+                    "action": "store_true",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--single-configure"], {
+                    "help": (
+                        "generate a single configure file, not a separate",
+                        "configure file for the tests directory",
+                    ),
+                    "dest": "single_configure",
+                    "action": "store_true",
+                    "default": False,
+                }),
+            ),
+        ),
+
+
+        (
+            "Options for --create-[mega]testdir, --[mega]test",
+            _ANY_TEST_,
+            (
+                (["--without-tests"], {
+                    "help": (
+                        "don't include unit tests for the included modules",
+                    ),
+                    "dest": "tests",
+                    "action": "store_false",
+                    "default": _argparse_.SUPPRESS,
+                }),
+            ),
+        ),
+
+
+        (
+            "Options for --import, --add/remove-import,"
+            "\n"
+            "            --create-[mega]testdir, --[mega]test",
+            (_ANY_IMPORT_ & ~_UPDATE_) | _ANY_TEST_,
+            (
+                (["--with-obsolete"], {
+                    "help": (
+                        "include obsolete modules when they occur among the",
+                        "dependencies; by default, dependencies to obsolete",
+                        "modules are ignored",
+                    ),
+                    "dest": "obsolete",
+                    "action": "store_true",
+                    "default": _argparse_.SUPPRESS,
+                }),
+
+                (["--with-c++-tests"], {
+                    "help": (
+                        "include even unit tests for C++ interoperability",
+                    ),
+                    "dest": "cxx_tests",
+                    "action": "store_true",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--without-c++-tests"], {
+                    "help": (
+                        "exclude unit tests for C++ interoperability",
+                    ),
+                    "dest": "cxx_tests",
+                    "action": "store_false",
+                    "default": _argparse_.SUPPRESS,
+                }),
+
+                (["--with-longrunning-tests"], {
+                    "help": (
+                        "include even unit tests that are long-runners",
+                    ),
+                    "dest": "longrunning_tests",
+                    "action": "store_true",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--without-longrunning-tests"], {
+                    "help": (
+                        "exclude unit tests that are long-runners",
+                    ),
+                    "dest": "longrunning_tests",
+                    "action": "store_false",
+                    "default": _argparse_.SUPPRESS,
+                }),
+
+                (["--with-privileged-tests"], {
+                    "help": (
+                        "include even unit tests that require root privileges",
+                    ),
+                    "dest": "privileged_tests",
+                    "action": "store_true",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--without-privileged-tests"], {
+                    "help": (
+                        "exclude unit tests that require root privileges",
+                    ),
+                    "dest": "privileged_tests",
+                    "action": "store_false",
+                    "default": _argparse_.SUPPRESS,
+                }),
+
+                (["--with-unportable-tests"], {
+                    "help": (
+                        "include even unit tests that fail on some platforms",
+                    ),
+                    "dest": "unportable_tests",
+                    "action": "store_true",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--without-unportable-tests"], {
+                    "help": (
+                        "exclude unit tests that fail on some platforms",
+                    ),
+                    "dest": "unportable_tests",
+                    "action": "store_false",
+                    "default": _argparse_.SUPPRESS,
+                }),
+
+                (["--with-all-tests"], {
+                    "help": (
+                        "include all kinds of problematic unit tests",
+                    ),
+                    "dest": "all_tests",
+                    "action": "store_true",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--avoid"], {
+                    "help": (
+                        "avoid including the given MODULE; useful if you",
+                        "have code that provides equivalent functionality;",
+                        "this option can be repeated",
+                    ),
+                    "action": _AvoidAction_,
+                    "nargs": 1,
+                    "default": [],
+                    "metavar": "MODULE",
+                }),
+                (["--conditional-dependencies"], {
+                    "help": (
+                        "support conditional dependencies (may save configure",
+                        "time and object code)",
+                    ),
+                    "dest": "conddeps",
+                    "action": "store_true",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--no-conditional-dependencies"], {
+                    "help": (
+                        "don't use conditional dependencies",
+                    ),
+                    "dest": "conddeps",
+                    "action": "store_false",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--libtool"], {
+                    "help": (
+                        "use libtool rules",
+                    ),
+                    "dest": "libtool",
+                    "action": "store_true",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--no-libtool"], {
+                    "help": (
+                        "don't use libtool rules",
+                    ),
+                    "dest": "libtool",
+                    "action": "store_false",
+                    "default": _argparse_.SUPPRESS,
+                }),
+            ),
+        ),
+
+
+        (
+            "Options for --import, --add/remove-import",
+            (_ANY_IMPORT_ & ~_UPDATE_),
+            (
+                (["--lib"], {
+                    "help": (
+                        "specify the library name; defaults to 'libgnu'",
+                    ),
+                    "metavar": "LIBRARY",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--source-base"], {
+                    "help": (
+                        "directory relative to --dir where source code is",
+                        "placed (default \"lib\")",
+                    ),
+                    "metavar": "DIRECTORY",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--m4-base"], {
+                    "help": (
+                        "directory relative to --dir where *.m4 macros are",
+                        "placed (default \"m4\")",
+                    ),
+                    "metavar": "DIRECTORY",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--po-base"], {
+                    "help": (
+                        "directory relative to --dir where *.po files are",
+                        "placed (default \"po\")",
+                    ),
+                    "metavar": "DIRECTORY",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--doc-base"], {
+                    "help": (
+                        "directory relative to --dir where doc files are",
+                        "placed (default \"doc\")",
+                    ),
+                    "metavar": "DIRECTORY",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--tests-base"], {
+                    "help": (
+                        "directory relative to --dir where unit tests are",
+                        "placed (default \"tests\")",
+                    ),
+                    "metavar": "DIRECTORY",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--aux-dir"], {
+                    "help": (
+                        "directory relative to --dir where auxiliary build",
+                        "tools are placed (default comes from configure.ac);",
+                    ),
+                    "dest": "auxdir",
+                    "metavar": "DIRECTORY",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--lgpl"], {
+                    "help": (
+                        "abort if modules aren't available under the LGPL;",
+                        "also modify license template from GPL to LGPL;",
+                        "the version number of the LGPL can be specified;",
+                        "the default is currently LGPLv3.",
+                    ),
+                    "choices": (2, 3),
+                    "type": int,
+                    "metavar": "[=2|=3]",
+                }),
+                (["--makefile-name"], {
+                    "help": (
+                        "name of makefile in automake syntax in the",
+                        "source-base and tests-base directories",
+                        "(default \"Makefile.am\")",
+                    ),
+                    "metavar": "NAME",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--macro-prefix"], {
+                    "help": (
+                        "specify the prefix of the macros 'gl_EARLY' and",
+                        "'gl_INIT'; default is 'gl'",
+                    ),
+                    "metavar": "PREFIX",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--po-domain"], {
+                    "help": (
+                        "specify the prefix of the i18n domain; usually use",
+                        "the package name; a suffix '-gnulib' is appended",
+                    ),
+                    "metavar": "NAME",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--witness-c-macro"], {
+                    "help": (
+                        "specify the C macro that is defined when the",
+                        "sources in this directory are compiled or used",
+                    ),
+                    "metavar": "NAME",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--vc-files"], {
+                    "help": (
+                        "update version control related files",
+                        "(.gitignore and/or .cvsignore)",
+                    ),
+                    "dest": "vc_files",
+                    "action": "store_true",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--no-vc-files"], {
+                    "help": (
+                        "don't update version control related files",
+                        "(.gitignore and/or .cvsignore)",
+                    ),
+                    "dest": "libtool",
+                    "action": "store_false",
+                    "default": _argparse_.SUPPRESS,
+                }),
+                (["--no-changelog"], {
+                    "help": (
+                        "don't update or create ChangeLog files;",
+                        "this option is currently deprecated",
+                    ),
+                    "action": "store_const",
+                    "const": None,
+                    "default": _argparse_.SUPPRESS,
+                }),
+            ),
+        ),
+
+
+        (
+            "Options for --import, --add/remove-import, --update"
+            "\n"
+            "            --create-[mega]testdir, --[mega]test",
+            _ANY_IMPORT_ | _ANY_TEST_,
+            (
+                (["-s", "--symlink"], {
+                    "help": (
+                        "make symbolic links instead of copying files",
+                    ),
+                    "dest": "link",
+                    "action": _LinkAction_,
+                    "nargs": 0,
+                    "default": _LINK_NOTICE_,
+                }),
+                (["--local-symlink"], {
+                    "help": (
+                        "make symbolic links instead of copying files, only",
+                        "for files from the local override directory"
+                    ),
+                    "dest": "link",
+                    "action": _LinkAction_,
+                    "nargs": 0,
+                    "default": _LINK_NOTICE_,
+                }),
+                (["-h", "--hardlink"], {
+                    "help": (
+                        "make hard links instead of copying files",
+                    ),
+                    "dest": "link",
+                    "action": _LinkAction_,
+                    "nargs": 0,
+                    "default": _LINK_NOTICE_,
+                }),
+                (["--local-hardlink"], {
+                    "help": (
+                        "make hard links instead of copying files, only",
+                        "for files from the local override directory"
+                    ),
+                    "dest": "link",
+                    "action": _LinkAction_,
+                    "nargs": 0,
+                    "default": _LINK_NOTICE_,
+                }),
+            ),
+        ),
+
+
+        (
+            "Options for --import, --add/remove-import, --update",
+            _ANY_IMPORT_,
+            (
+                (["-S", "--more-symlink"], {
+                    "help": (
+                        "make symbolic links instead of copying files and",
+                        "don't replace copyright notices",
+                    ),
+                    "dest": "link",
+                    "action": _LinkAction_,
+                    "nargs": 0,
+                    "default": _LINK_NOTICE_,
+                }),
+                (["-H", "--more-hardlink"], {
+                    "help": (
+                        "make symbolic links instead of copying files and",
+                        "don't replace copyright notices",
+                    ),
+                    "dest": "link",
+                    "action": _LinkAction_,
+                    "nargs": 0,
+                    "default": _LINK_NOTICE_,
+                }),
+            ),
+        ),
+    )
+
+
+    def __error(self, message):
+        raise _CommandLineError_(self.__program, message)
+
+
+    def __format_help(self):
+        lines = [""]
+        for (name, _, args) in CommandLine._SECTIONS_:
+            offset = -1
+            lines += ["", "%s:" % name, ""]
+            for arg in args:
+                (options, kwargs) = arg
+                options = ", ".join(options)
+                if "metavar" in kwargs:
+                    options += (" " + kwargs["metavar"])
+                length = len(options)
+                if length > offset:
+                    offset = length
+            fmt1 = "      %-{0}s    %s".format(offset)
+            fmt2 = "      " + " " * offset + "    %s"
+            for arg in args:
+                (options, kwargs) = arg
+                options = ", ".join(options)
+                if "metavar" in kwargs:
+                    sep = "" if "choices" in kwargs else "="
+                    options += ("%s%s" % (sep, kwargs["metavar"]))
+                description = iter(kwargs["help"])
+                line = next(description)
+                lines += [fmt1 % (options, line)]
+                for line in description:
+                    lines += [fmt2 % line]
+            lines += [""]
+        return self.__format_usage()[:-1] + "\n".join(lines)
+
+
+    def __format_usage(self):
+        iterable = iter(CommandLine._MODES_)
+        (_, cmd, args) = next(iterable)
+        fmt = (" --{cmd}" + (" {args}" if args else ""))
+        lines = ["usage: {program}" + fmt.format(cmd=cmd, args=args)]
+        for (_, cmd, args) in iterable:
+            fmt = (" --{cmd}" + (" {args}" if args else ""))
+            lines += ["       {program}" + fmt.format(cmd=cmd, args=args)]
+        lines += ["", ""]
+        return "\n".join(lines).format(program=self.__program)
+
+
+    class Option(_enum_.Flag):
+        DryRun = (1 << 0)
+        Symlink = (1 << 1)
+        Hardlink = (1 << 2)
+        OnlyLocalLinks = (1 << 3)
+        UpdateCopyrights = (1 << 4)
+        SingleConfigure = (1 << 5)
+
+
+    def __init__(self, program):
+        self.__parser = _argparse_.ArgumentParser(prog=program, add_help=False)
+        for (_, _, args) in CommandLine._SECTIONS_:
+            for arg in args:
+                (options, kwargs) = arg
+                self.__parser.add_argument(*options, **kwargs)
+        self.__program = _os_.path.basename(program)
+        self.__parser.error = self.__error
+        self.__parser.format_help = self.__format_help
+        self.__parser.format_usage = self.__format_usage
+
+
+    @property
+    def usage(self):
+        """usage message"""
+        return self.__format_usage()[:-1]
+
+
+    @property
+    def help(self):
+        """help message"""
+        return self.__format_help()
+
+
+    def parse(self, *arguments):
+        """
+        self.parse(*arguments) -> (config, explicit, mode, verbosity, options)
+
+        Parse the command-line arguments and return a new configuration.
+        The default configuration holds default parameter values.
+        The parameter is overriden if and only if it was set explicitly.
+
+        config: a new configuration instance (pygnulib.Config.Base)
+        explicit: an iterable representing explicit configuration keys
+        mode: a string representing the mode ("import", "add-import", etc.)
+        verbosity: an integer representing the desired verbosity level
+        options: a combination of CommandLine.Option flags
+        """
+        namespace = vars(self.__parser.parse_args(arguments))
+        options = ~(CommandLine.Option.DryRun |
+                    CommandLine.Option.Symlink |
+                    CommandLine.Option.Hardlink |
+                    CommandLine.Option.OnlyLocalLinks |
+                    CommandLine.Option.UpdateCopyrights |
+                    CommandLine.Option.SingleConfigure)
+        mode = namespace.pop("mode", None)
+        if mode is None:
+            self.__parser.error("no operating mode selected")
+        mode = dict((_[0], _[1]) for _ in CommandLine._MODES_)[mode]
+        verbosity = namespace.pop("verbosity", 0)
+        if namespace.pop("dry_run", False):
+            options |= CommandLine.Option.DryRun
+        link = namespace.pop("link", None)
+        if link is not None:
+            if link & CommandLine._LINK_SYMBOLIC_:
+                options |= CommandLine.Option.Symlink
+            if link & CommandLine._LINK_HARD_:
+                options |= CommandLine.Option.Hardlink
+            if link & CommandLine._LINK_LOCAL_:
+                options |= CommandLine.Option.OnlyLocalLinks
+            if link & CommandLine._LINK_NOTICE_:
+                options |= CommandLine.Option.UpdateCopyrights
+        if namespace.pop("single_configure", False):
+            options |= CommandLine.Option.SingleConfigure
+        namespace.pop("no_changelog", None)
+
+        config = _BaseConfig_(**namespace)
+        explicit = namespace.keys()
+        return (config, explicit, mode, verbosity, options)