]> Savannah Git Hosting - gnulib.git/commitdiff
command-line configuration
authorDmitry Selyutin <ghostmansd@gmail.com>
Fri, 8 Sep 2017 10:19:54 +0000 (13:19 +0300)
committerDmitry Selyutin <ghostmansd@gmail.com>
Fri, 8 Sep 2017 10:19:54 +0000 (13:19 +0300)
pygnulib/config.py

index 7faac24bfba18dd5645f4f0bd707e3e2e90eb606..ae84b9820de465876204468a7f0f7d6efd5784ab 100644 (file)
@@ -2,11 +2,11 @@
 # encoding: UTF-8
 
 
+import argparse
 import codecs
 import os
 import re
 
-
 from .error import AutoconfVersionError
 
 
@@ -28,6 +28,7 @@ class Config:
         "witness_c_macro"   : "",
         "lgpl"              : 0,
         "tests"             : False,
+        "obsolete"          : False,
         "cxx_tests"         : False,
         "longrunning_tests" : False,
         "privileged_tests"  : False,
@@ -37,6 +38,9 @@ class Config:
         "conddeps"          : False,
         "vc_files"          : False,
         "autoconf"          : 2.59,
+        "modules"           : [],
+        "avoid"             : [],
+        "files"             : [],
     }
 
 
@@ -195,6 +199,16 @@ class Config:
         self["tests"] = value
 
 
+    @property
+    def obsolete(self):
+        """include obsolete modules when they occur among the modules"""
+        return self["obsolete"]
+
+    @obsolete.setter
+    def obsolete(self, value):
+        self["obsolete"] = value
+
+
     @property
     def cxx_tests(self):
         """include even unit tests for C++ interoperability"""
@@ -295,6 +309,36 @@ class Config:
         self["autoconf"] = value
 
 
+    @property
+    def modules(self):
+        """list of modules"""
+        return self["modules"]
+
+    @modules.setter
+    def modules(self, value):
+        self["modules"] = tuple(value)
+
+
+    @property
+    def avoid(self):
+        """list of modules to avoid"""
+        return self["avoid"]
+
+    @avoid.setter
+    def avoid(self, value):
+        self["avoid"] = tuple(value)
+
+
+    @property
+    def files(self):
+        """list of files to be processed"""
+        return self["files"]
+
+    @files.setter
+    def files(self, value):
+        self["files"] = list(value)
+
+
     @property
     def include_guard_prefix(self):
         """include guard prefix"""
@@ -348,33 +392,33 @@ class Cache(Config):
     """gnulib cached configuration"""
     _AUTOCONF_ = {
         "autoconf" : re.compile(".*AC_PREREQ\\(\\[(.*?)\\]\\)", re.S | re.M),
-        "aux-dir"  : re.compile("^AC_CONFIG_AUX_DIR\\(\\[(.*?)\\]\\)$", re.S | re.M),
+        "auxdir"   : re.compile("^AC_CONFIG_AUX_DIR\\(\\[(.*?)\\]\\)$", re.S | re.M),
         "libtool"  : re.compile("A[CM]_PROG_LIBTOOL", re.S | re.M)
     }
     _GNULIB_CACHE_ = {
         "libtool"           : (bool, "gl_LIBTOOL"),
         "conddeps"          : (bool, "gl_CONDITIONAL_DEPENDENCIES"),
-        "vc-files"          : (bool, "gl_VC_FILES"),
+        "vc_files"          : (bool, "gl_VC_FILES"),
         "tests"             : (bool, "gl_WITH_TESTS"),
         "obsolete"          : (bool, "gl_WITH_OBSOLETE"),
-        "cxx-tests"         : (bool, "gl_WITH_CXX_TESTS"),
-        "longrunning-tests" : (bool, "gl_WITH_LONGRUNNING_TESTS"),
-        "privileged-tests"  : (bool, "gl_WITH_PRIVILEGED_TESTS"),
-        "unportable-tests"  : (bool, "gl_WITH_UNPORTABLE_TESTS"),
-        "all-tests"         : (bool, "gl_WITH_ALL_TESTS"),
-        "local-dir"         : (str, "gl_LOCAL_DIR"),
-        "source-base"       : (str, "gl_SOURCE_BASE"),
-        "m4-base"           : (str, "gl_M4_BASE"),
-        "po-base"           : (str, "gl_PO_BASE"),
-        "doc-base"          : (str, "gl_DOC_BASE"),
-        "tests-base"        : (str, "gl_TESTS_BASE"),
-        "makefile-name"     : (str, "gl_MAKEFILE_NAME"),
-        "macro-prefix"      : (str, "gl_MACRO_PREFIX"),
-        "po-domain"         : (str, "gl_PO_DOMAIN"),
-        "witness-c-macro"   : (str, "gl_WITNESS_C_MACRO"),
+        "cxx_tests"         : (bool, "gl_WITH_CXX_TESTS"),
+        "longrunning_tests" : (bool, "gl_WITH_LONGRUNNING_TESTS"),
+        "privileged_tests"  : (bool, "gl_WITH_PRIVILEGED_TESTS"),
+        "unportable_tests"  : (bool, "gl_WITH_UNPORTABLE_TESTS"),
+        "all_tests"         : (bool, "gl_WITH_ALL_TESTS"),
+        "local_dir"         : (str, "gl_LOCAL_DIR"),
+        "source_base"       : (str, "gl_SOURCE_BASE"),
+        "m4_base"           : (str, "gl_M4_BASE"),
+        "po_base"           : (str, "gl_PO_BASE"),
+        "doc_base"          : (str, "gl_DOC_BASE"),
+        "tests_base"        : (str, "gl_TESTS_BASE"),
+        "makefile_name"     : (str, "gl_MAKEFILE_NAME"),
+        "macro_prefix"      : (str, "gl_MACRO_PREFIX"),
+        "po_domain"         : (str, "gl_PO_DOMAIN"),
+        "witness_c_macro"   : (str, "gl_WITNESS_C_MACRO"),
         "lib"               : (str, "gl_LIB"),
         "modules"           : (list, "gl_MODULES"),
-        "avoids"            : (list, "gl_AVOID"),
+        "avoid            : (list, "gl_AVOID"),
         "lgpl"              : (str, "gl_LGPL"),
     }
     _GNULIB_CACHE_BOOL_ = []
@@ -405,6 +449,7 @@ class Cache(Config):
                 autoconf = os.path.join(root, "configure.in")
         if not os.path.isabs(autoconf):
             autoconf = os.path.join(root, autoconf)
+        autoconf = os.path.normpath(autoconf)
         with codecs.open(autoconf, "rb", "UTF-8") as stream:
             data = stream.read()
         for key, pattern in Cache._AUTOCONF_.items():
@@ -446,4 +491,680 @@ class Cache(Config):
             pattern = re.compile(regex, re.S | re.M)
             match = pattern.findall(data)
             if match:
-                self["files"] = [_.strip() for _ in match[-1].split("\n") if _.strip()]
+                self.files = [_.strip() for _ in match[-1].split("\n") if _.strip()]
+
+
+
+class CommandLine(Config):
+    """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]"),
+    )
+
+
+    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)
+
+
+    # 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",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _LIST_,
+                }),
+                (["-f", "--find"], {
+                    "help": (
+                        "find the modules which contain the specified file",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _FIND_,
+                }),
+                (["-i", "--import"], {
+                    "help": (
+                        "import the given modules into the current package",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "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",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _ADD_IMPORT_,
+                }),
+                (["-r", "--remove-import"], {
+                    "help": (
+                        "reduce the list of imports from gnulib into the",
+                        "current package, by removing the given modules",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _REMOVE_IMPORT_,
+                }),
+                (["-u", "--update"], {
+                    "help": (
+                        "update the current package, restore files omitted",
+                        "from version control",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _UPDATE_,
+                }),
+
+                (["--extract-description"], {
+                    "help": (
+                        "extract the description",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _EXTRACT_DESCRIPTION_,
+                }),
+                (["--extract-comment"], {
+                    "help": (
+                        "extract the comment",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _EXTRACT_COMMENT_,
+                }),
+                (["--extract-status"], {
+                    "help": (
+                        "extract the status (obsolete etc.)",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _EXTRACT_STATUS_,
+                }),
+                (["--extract-notice"], {
+                    "help": (
+                        "extract the notice or banner",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _EXTRACT_NOTICE_,
+                }),
+                (["--extract-applicability"], {
+                    "help": (
+                        "extract the applicability",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _EXTRACT_APPLICABILITY_,
+                }),
+                (["--extract-filelist"], {
+                    "help": (
+                        "extract the list of files",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _EXTRACT_FILELIST_,
+                }),
+                (["--extract-dependencies"], {
+                    "help": (
+                        "extract the dependencies",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _EXTRACT_DEPENDENCIES_,
+                }),
+                (["--extract-autoconf-snippet"], {
+                    "help": (
+                        "extract the snippet for configure.ac",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _EXTRACT_AUTOCONF_SNIPPET_,
+                }),
+                (["--extract-automake-snippet"], {
+                    "help": (
+                        "extract the snippet for library makefile",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _EXTRACT_AUTOMAKE_SNIPPET_,
+                }),
+                (["--extract-include-directive"], {
+                    "help": (
+                        "extract the #include directive",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _EXTRACT_INCLUDE_DIRECTIVE_,
+                }),
+                (["--extract-link-directive"], {
+                    "help": (
+                        "extract the linker directive",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _EXTRACT_LINK_DIRECTIVE_,
+                }),
+                (["--extract-license"], {
+                    "help": (
+                        "report the license terms of the source files",
+                        "under lib/",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _EXTRACT_LICENSE_,
+                }),
+                (["--extract-maintainer"], {
+                    "help": (
+                        "report the maintainer(s) inside gnulib",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _EXTRACT_MAINTAINER_,
+                }),
+                (["--extract-tests-module"], {
+                    "help": (
+                        "report the unit test module, if it exists",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _EXTRACT_TESTS_MODULE_,
+                }),
+
+                (["--copy-file"], {
+                    "help": (
+                        "copy a file that is not part of any module",
+                    ),
+                    "dest": "mode",
+                    "default": None,
+                    "action": "store_const",
+                    "const": _COPY_FILE_,
+                }),
+            ),
+        ),
+
+
+        (
+            "General options",
+            _ALL_,
+            (
+                (["--dir"], {
+                    "help": (
+                        "specify the target directory; on --import,",
+                        "this specifies where your configure.ac",
+                        "can be found",
+                    ),
+                    "dest": "root",
+                    "default": ".",
+                    "metavar": "DIRECTORY",
+                }),
+                (["--local-dir"], {
+                    "help": (
+                        "pecify a local override directory where to look",
+                        "up files before looking in gnulib's directory",
+                    ),
+                    "dest": "local_dir",
+                    "default": ".",
+                    "nargs": 1,
+                    "metavar": "DIRECTORY",
+                }),
+                (["-v", "--verbose"], {
+                    "help": (
+                        "increase verbosity; may be repeated",
+                    ),
+                    "dest": "verbosity",
+                    "action": _VerboseAction_,
+                    "default": 0,
+                    "nargs": 0,
+                }),
+                (["-q", "--quiet"], {
+                    "help": (
+                        "decrease verbosity; may be repeated",
+                    ),
+                    "dest": "verbosity",
+                    "action": _VerboseAction_,
+                    "default": 0,
+                    "nargs": 0,
+                }),
+            ),
+        ),
+
+
+        (
+            "Options for --import, --add/remove-import,\n"
+            "            --create-[mega]testdir, --[mega]test",
+            (_ANY_IMPORT_ & ~_UPDATE_) | _ANY_TEST_,
+            (
+                (["--with-tests"], {
+                    "help": (
+                        "include unit tests for the included modules",
+                    ),
+                    "dest": "tests",
+                    "action": "store_true",
+                    "default": False,
+                }),
+                (["--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": False,
+                }),
+                (["--with-c++-tests"], {
+                    "help": (
+                        "include even unit tests for C++ interoperability",
+                    ),
+                    "dest": "cxx_tests",
+                    "action": "store_true",
+                    "default": False,
+                }),
+                (["--with-longrunning-tests"], {
+                    "help": (
+                        "include even unit tests that are long-runners",
+                    ),
+                    "dest": "longrunning_tests",
+                    "action": "store_true",
+                    "default": False,
+                }),
+                (["--with-privileged-tests"], {
+                    "help": (
+                        "include even unit tests that require root",
+                        "privileges",
+                    ),
+                    "dest": "privileged_tests",
+                    "action": "store_true",
+                    "default": False,
+                }),
+                (["--with-unportable-tests"], {
+                    "help": (
+                        "include even unit tests that fail on some platforms",
+                    ),
+                    "dest": "unportable_tests",
+                    "action": "store_true",
+                    "default": False,
+                }),
+                (["--with-all-tests"], {
+                    "help": (
+                        "include all kinds of problematic unit tests",
+                    ),
+                    "dest": "all_tests",
+                    "action": "store_true",
+                    "default": False,
+                }),
+                (["--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)",
+                    ),
+                    "action": "store_true",
+                    "default": False,
+                    "dest": "conddeps",
+                }),
+                (["--no-conditional-dependencies"], {
+                    "help": (
+                        "don't use conditional dependencies",
+                    ),
+                    "action": "store_false",
+                    "default": False,
+                    "dest": "conddeps",
+                }),
+                (["--libtool"], {
+                    "help": (
+                        "use libtool rules",
+                    ),
+                    "action": "store_true",
+                    "default": False,
+                    "dest": "libtool",
+                }),
+                (["--no-libtool"], {
+                    "help": (
+                        "don't use libtool rules",
+                    ),
+                    "action": "store_false",
+                    "default": False,
+                    "dest": "libtool",
+                }),
+            ),
+        ),
+
+
+        (
+            "Options for --import, --add/remove-import",
+            (_ANY_IMPORT_ & ~_UPDATE_),
+            (
+                (["--lib"], {
+                    "help": (
+                        "specify the library name; defaults to 'libgnu'",
+                    ),
+                    "default": "libgnu",
+                    "metavar": "LIBRARY",
+                }),
+                (["--source-base"], {
+                    "help": (
+                        "directory relative to --dir where source code is",
+                        "placed (default \"lib\")",
+                    ),
+                    "default": "lib",
+                    "metavar": "DIRECTORY",
+                }),
+                (["--m4-base"], {
+                    "help": (
+                        "directory relative to --dir where *.m4 macros are",
+                        "placed (default \"m4\")",
+                    ),
+                    "default": "m4",
+                    "metavar": "DIRECTORY",
+                }),
+                (["--po-base"], {
+                    "help": (
+                        "directory relative to --dir where *.po files are",
+                        "placed (default \"po\")",
+                    ),
+                    "default": "po",
+                    "metavar": "DIRECTORY",
+                }),
+                (["--doc-base"], {
+                    "help": (
+                        "directory relative to --dir where doc files are",
+                        "placed (default \"doc\")",
+                    ),
+                    "default": "doc",
+                    "metavar": "DIRECTORY",
+                }),
+                (["--tests-base"], {
+                    "help": (
+                        "directory relative to --dir where unit tests are",
+                        "placed (default \"tests\")",
+                    ),
+                    "default": "tests",
+                    "metavar": "DIRECTORY",
+                }),
+                (["--aux-dir"], {
+                    "help": (
+                        "directory relative to --dir where auxiliary build",
+                        "tools are placed (default comes from configure.ac);",
+                    ),
+                    "dest": "auxdir",
+                    "default": "",
+                    "metavar": "DIRECTORY",
+                }),
+                (["--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\")",
+                    ),
+                    "default": "Makefile.am",
+                    "metavar": "NAME",
+                }),
+                (["--macro-prefix"], {
+                    "help": (
+                        "specify the prefix of the macros 'gl_EARLY' and",
+                        "'gl_INIT'; default is 'gl'",
+                    ),
+                    "default": "gl",
+                    "metavar": "PREFIX",
+                }),
+                (["--po-domain"], {
+                    "help": (
+                        "specify the prefix of the i18n domain; usually use",
+                        "the package name; a suffix '-gnulib' is appended",
+                    ),
+                    "default": "",
+                    "metavar": "NAME",
+                }),
+                (["--witness-c-macro"], {
+                    "help": (
+                        "specify the C macro that is defined when the",
+                        "sources in this directory are compiled or used",
+                    ),
+                    "default": "",
+                    "metavar": "NAME",
+                }),
+                (["--vc-files"], {
+                    "help": (
+                        "update version control related files",
+                        "(.gitignore and/or .cvsignore)",
+                    ),
+                    "action": "store_true",
+                    "default": False,
+                    "dest": "vc_files",
+                }),
+                (["--no-vc-files"], {
+                    "help": (
+                        "don't update version control related files",
+                        "(.gitignore and/or .cvsignore)",
+                    ),
+                    "action": "store_false",
+                    "default": False,
+                    "dest": "libtool",
+                }),
+                (["--no-changelog"], {
+                    "help": (
+                        "don't update or create ChangeLog files;",
+                        "this option is currently deprecated",
+                    ),
+                    "default": None,
+                    "action": "store_const",
+                    "const": None,
+                }),
+            ),
+        ),
+    )
+
+
+    def _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 _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._usage_()[:-1] + "\n".join(lines)
+
+
+    def __init__(self, program, argv, **kwargs):
+        super().__init__(**kwargs)
+
+        exclude = {}
+        parser = argparse.ArgumentParser(prog=program, allow_abbrev=False)
+        modes = parser.add_mutually_exclusive_group()
+        for (options, kwargs) in CommandLine._SECTIONS_[0][2]:
+            for (flag, cmd, _) in CommandLine._MODES_:
+                if "--%s" % cmd not in options:
+                    continue
+                exclude[flag] = [options, []]
+                modes.add_argument(*options, **kwargs)
+                for (_, flags, xargs) in CommandLine._SECTIONS_[1:]:
+                    if not flags & flag:
+                        for xarg in xargs:
+                            (options, kwargs) = xarg
+                            exclude[flag][1] += options
+                break
+
+        for (_, _, args) in CommandLine._SECTIONS_[1:]:
+            for arg in args:
+                (options, kwargs) = arg
+                parser.add_argument(*options, **kwargs)
+        parser.add_argument("modules", nargs="+", metavar="module1 ... moduleN")
+
+        self._program_ = os.path.basename(program)
+        parser.format_usage = self._usage_
+        parser.format_help = self._help_
+
+        namespace = vars(parser.parse_args(argv))
+        self._mode_ = namespace.pop("mode")
+        if self._mode_ is None:
+            parser.error("no operating mode selected")
+        self._verbosity_ = namespace.pop("verbosity")
+
+        (mode, conflict) = exclude[self._mode_]
+        conflict = set(conflict).intersection(argv)
+        if conflict:
+            conflict = list(conflict)[0]
+            fmt = "argument {0}: not allowed with argument {1}"
+            parser.error(fmt.format(conflict, "/".join(mode)))
+        _ = namespace.pop("no_changelog", None)
+        for (key, value) in namespace.items():
+            self[key] = value
+
+
+    @property
+    def mode(self):
+        for (flag, cmd, _) in CommandLine._MODES_:
+            if flag == self._mode_:
+                return cmd
+        return ""