From 9976a64d11a31642ab570b11d1e6dce664d6ac93 Mon Sep 17 00:00:00 2001 From: Dmitry Selyutin Date: Wed, 17 Jan 2018 21:25:50 +0300 Subject: [PATCH] config: autoconf options; version match; cleanup --- pygnulib.py | 2 +- pygnulib/config.py | 428 ++++++++++++++++++++++++--------------------- pygnulib/parser.py | 7 - 3 files changed, 228 insertions(+), 209 deletions(-) diff --git a/pygnulib.py b/pygnulib.py index fa32be283f..f662907916 100755 --- a/pygnulib.py +++ b/pygnulib.py @@ -96,7 +96,7 @@ def extract_hook(program, gnulib, mode, namespace, *args, **kwargs): def import_hook(script, gnulib, namespace, explicit, verbosity, options, *args, **kwargs): (_, _) = (args, kwargs) config = BaseConfig(**namespace) - cache = CachedConfig(configure=None) + cache = CachedConfig(root=config.root) for key in {"ac_version", "files"}: if key not in namespace: config[key] = cache[key] diff --git a/pygnulib/config.py b/pygnulib/config.py index 7eef86bcf0..546c804ce7 100644 --- a/pygnulib/config.py +++ b/pygnulib/config.py @@ -7,7 +7,9 @@ import codecs as _codecs import os as _os import re as _re + from collections import OrderedDict as _OrderedDict +from distutils import version as _version from .error import type_assert as _type_assert @@ -56,42 +58,47 @@ class BaseConfig: "po_domain" : "", "witness_c_macro" : "", "licenses" : set(), - "libtool" : False, - "conditionals" : True, - "vc_files" : False, - "ac_version" : 2.59, + "ac_version" : "2.59", + "ac_file" : "configure.ac", "modules" : set(), "avoids" : set(), "files" : set(), "copymode" : None, "local_copymode" : None, + "tests" : False, + "obsolete" : False, + "cxx_tests" : False, + "longrunning_tests" : False, + "privileged_tests" : False, + "unportable_tests" : False, + "libtool" : False, + "conditionals" : True, + "copyrights" : False, "gnumake" : False, + "single_configure" : False, + "vc_files" : False, } - _OPTIONS = frozenset({ - "tests", - "obsolete", - "cxx_tests", - "longrunning_tests", - "privileged_tests", - "unportable_tests", - }) class _Option: """gnulib configuration options""" - Obsolete = (1 << 0) - Tests = (1 << 1) + Tests = (1 << 0) + Obsolete = (1 << 1) CXX = (1 << 2) Longrunning = (1 << 3) Privileged = (1 << 4) Unportable = (1 << 5) - Copyrights = (1 << 6) - GNUMake = (1 << 7) - AllTests = (Obsolete | Tests | CXX | Longrunning | Privileged | Unportable | GNUMake) + Libtool = (1 << 6) + Conditionals = (1 << 7) + Copyrights = (1 << 8) + GNUMake = (1 << 9) + SingeConfigure = (1 << 10) + AllTests = (Obsolete | Tests | CXX | Longrunning | Privileged | Unportable) def __init__(self, **kwargs): - self.__table = {"options": 0} + self.__options = 0 + self.__table = {} for (key, value) in BaseConfig._TABLE.items(): self[key] = kwargs.get(key, value) @@ -102,10 +109,6 @@ class BaseConfig: return "{}.{}{}".format(module, name, repr(self.__table)) - def __iter__(self): - return iter(self.__table) - - def __enter__(self): return self @@ -303,270 +306,297 @@ class BaseConfig: self.__table["licenses"] = frozenset(result) + @property + def ac_version(self): + """minimal supported autoconf version""" + return self.__table["ac_version"] + + @ac_version.setter + def ac_version(self, value): + _type_assert("ac_version", value, str) + if _version.LooseVersion(value) < _version.LooseVersion("2.59"): + raise _AutoconfVersionError("2.59") + self.__table["ac_version"] = value + + + @property + def ac_file(self): + """autoconf file (usually configure.ac or configure.in)""" + return self.__table["ac_file"] + + @ac_file.setter + def ac_file(self, value): + _type_assert("ac_file", value, str) + self.__table["ac_file"] = value + + + @property + def modules(self): + """list of modules""" + return self.__table["modules"] + + @modules.setter + def modules(self, value): + _type_assert("modules", value, _ITERABLES) + result = set() + for item in value: + _type_assert("module", item, str) + result.add(item) + self.__table["modules"] = frozenset(result) + + + @property + def avoids(self): + """list of modules to avoid""" + return self.__table["avoids"] + + @avoids.setter + def avoids(self, value): + _type_assert("avoids", value, _ITERABLES) + result = set() + for item in value: + _type_assert("avoid", item, str) + result.add(item) + self.__table["avoids"] = frozenset(result) + + + @property + def files(self): + """list of files to be processed""" + return self.__table["files"] + + @files.setter + def files(self, value): + _type_assert("files", value, _ITERABLES) + result = set() + for item in value: + _type_assert("file", item, str) + result.add(item) + self.__table["files"] = frozenset(result) + + + @property + def include_guard_prefix(self): + """include guard prefix""" + prefix = self.__table["macro_prefix"].upper() + default = BaseConfig._TABLE["macro_prefix"].upper() + return "GL" if prefix == default else "GL_{0}".format(prefix) + + + @property + def copymode(self): + """file copy mode ('symlink', 'hardlink' or None)""" + return self.__table["copymode"] + + @copymode.setter + def copymode(self, value): + if value not in frozenset({None, "symlink", "hardlink"}): + raise ValueError("copymode: None, 'symlink' or 'hardlink'") + self.__table["copymode"] = value + + + @property + def local_copymode(self): + """file copy mode for local directory ('symlink', 'hardlink' or None)""" + return self.__table["local_copymode"] + + @local_copymode.setter + def local_copymode(self, value): + if value not in frozenset({None, "symlink", "hardlink"}): + raise ValueError("local_copymode: None, 'symlink' or 'hardlink'") + self.__table["local_copymode"] = value + + @property def tests(self): - """include unit tests for the included modules""" - return bool(self.__table["options"] & BaseConfig._Option.Tests) + """include unit tests for the included modules?""" + return bool(self.__options & BaseConfig._Option.Tests) @tests.setter def tests(self, value): _type_assert("tests", value, bool) if value: - self.__table["options"] |= BaseConfig._Option.Tests + self.__options |= BaseConfig._Option.Tests else: - self.__table["options"] &= ~BaseConfig._Option.Tests + self.__options &= ~BaseConfig._Option.Tests @property def obsolete(self): - """include obsolete modules when they occur among the modules""" - return bool(self.__table["options"] & BaseConfig._Option.Tests) + """include obsolete modules when they occur among the modules?""" + return bool(self.__options & BaseConfig._Option.Tests) @obsolete.setter def obsolete(self, value): _type_assert("obsolete", value, bool) if value: - self.__table["options"] |= BaseConfig._Option.Obsolete + self.__options |= BaseConfig._Option.Obsolete else: - self.__table["options"] &= ~BaseConfig._Option.Obsolete + self.__options &= ~BaseConfig._Option.Obsolete @property def cxx_tests(self): - """include even unit tests for C++ interoperability""" - return bool(self.__table["options"] & BaseConfig._Option.CXX) + """include even unit tests for C++ interoperability?""" + return bool(self.__options & BaseConfig._Option.CXX) @cxx_tests.setter def cxx_tests(self, value): _type_assert("cxx_tests", value, bool) if value: - self.__table["options"] |= BaseConfig._Option.CXX + self.__options |= BaseConfig._Option.CXX else: - self.__table["options"] &= ~BaseConfig._Option.CXX + self.__options &= ~BaseConfig._Option.CXX @property def longrunning_tests(self): - """include even unit tests that are long-runners""" - return bool(self.__table["options"] & BaseConfig._Option.Longrunning) + """include even unit tests that are long-runners?""" + return bool(self.__options & BaseConfig._Option.Longrunning) @longrunning_tests.setter def longrunning_tests(self, value): _type_assert("longrunning_tests", value, bool) if value: - self.__table["options"] |= BaseConfig._Option.Longrunning + self.__options |= BaseConfig._Option.Longrunning else: - self.__table["options"] &= ~BaseConfig._Option.Longrunning + self.__options &= ~BaseConfig._Option.Longrunning @property def privileged_tests(self): - """include even unit tests that require root privileges""" - return bool(self.__table["options"] & BaseConfig._Option.Privileged) + """include even unit tests that require root privileges?""" + return bool(self.__options & BaseConfig._Option.Privileged) @privileged_tests.setter def privileged_tests(self, value): _type_assert("privileged_tests", value, bool) if value: - self.__table["options"] |= BaseConfig._Option.Privileged + self.__options |= BaseConfig._Option.Privileged else: - self.__table["options"] &= ~BaseConfig._Option.Privileged + self.__options &= ~BaseConfig._Option.Privileged @property def unportable_tests(self): - """include even unit tests that fail on some platforms""" - return bool(self.__table["options"] & BaseConfig._Option.Unportable) + """include even unit tests that fail on some platforms?""" + return bool(self.__options & BaseConfig._Option.Unportable) @unportable_tests.setter def unportable_tests(self, value): _type_assert("unportable_tests", value, bool) if value: - self.__table["options"] |= BaseConfig._Option.Unportable + self.__options |= BaseConfig._Option.Unportable else: - self.__table["options"] &= ~BaseConfig._Option.Unportable + self.__options &= ~BaseConfig._Option.Unportable @property def all_tests(self): - """include all kinds of problematic unit tests""" - return (self.__table["options"] & BaseConfig._Option.AllTests) == BaseConfig._Option.AllTests + """include all kinds of problematic unit tests?""" + return (self.__options & BaseConfig._Option.AllTests) == BaseConfig._Option.AllTests @all_tests.setter def all_tests(self, value): if value: - self.__table["options"] |= BaseConfig._Option.AllTests + self.__options |= BaseConfig._Option.AllTests else: - self.__table["options"] &= BaseConfig._Option.AllTests + self.__options &= BaseConfig._Option.AllTests @property def libtool(self): - """use libtool rules""" - return self.__table["libtool"] + """use libtool rules?""" + return bool(self.__options & BaseConfig._Option.Libtool) @libtool.setter def libtool(self, value): _type_assert("libtool", value, bool) - self.__table["libtool"] = value + if value: + self.__options |= BaseConfig._Option.Libtool + else: + self.__options &= ~BaseConfig._Option.Libtool @property def conditionals(self): - """support conditional dependencies (may save configure time and object code)""" - return self.__table["conditionals"] + """support conditional dependencies (may save configure time and object code)?""" + return bool(self.__options & BaseConfig._Option.Conditionals) @conditionals.setter def conditionals(self, value): _type_assert("conditionals", value, bool) - self.__table["conditionals"] = value - - - @property - def vc_files(self): - """update version control related files""" - return self.__table["vc_files"] - - @vc_files.setter - def vc_files(self, value): - _type_assert("vc_files", value, bool) - self.__table["vc_files"] = value - - - @property - def ac_version(self): - """minimal supported autoconf version""" - return self.__table["ac_version"] - - @ac_version.setter - def ac_version(self, value): - if isinstance(value, str): - value = float(value) - _type_assert("ac_version", value, float) - if value < 2.59: - raise _AutoconfVersionError(2.59) - self.__table["ac_version"] = value - - - @property - def modules(self): - """list of modules""" - return self.__table["modules"] - - @modules.setter - def modules(self, value): - _type_assert("modules", value, _ITERABLES) - result = set() - for item in value: - _type_assert("module", item, str) - result.add(item) - self.__table["modules"] = frozenset(result) - - - @property - def avoids(self): - """list of modules to avoid""" - return self.__table["avoids"] - - @avoids.setter - def avoids(self, value): - _type_assert("avoids", value, _ITERABLES) - result = set() - for item in value: - _type_assert("avoid", item, str) - result.add(item) - self.__table["avoids"] = frozenset(result) - - - @property - def files(self): - """list of files to be processed""" - return self.__table["files"] - - @files.setter - def files(self, value): - _type_assert("files", value, _ITERABLES) - result = set() - for item in value: - _type_assert("file", item, str) - result.add(item) - self.__table["files"] = frozenset(result) - - - @property - def include_guard_prefix(self): - """include guard prefix""" - prefix = self.__table["macro_prefix"].upper() - default = BaseConfig._TABLE["macro_prefix"].upper() - return "GL" if prefix == default else "GL_{0}".format(prefix) - - - @property - def copymode(self): - """file copy mode ('symlink', 'hardlink' or None)""" - return self.__table["copymode"] - - @copymode.setter - def copymode(self, value): - if value not in frozenset({None, "symlink", "hardlink"}): - raise ValueError("copymode: None, 'symlink' or 'hardlink'") - self.__table["copymode"] = value + if value: + self.__options |= BaseConfig._Option.Conditionals + else: + self.__options &= ~BaseConfig._Option.Conditionals @property - def local_copymode(self): - """file copy mode for local directory ('symlink', 'hardlink' or None)""" - return self.__table["local_copymode"] + def copyrights(self): + """update the license copyright text?""" + return bool(self.__options & BaseConfig._Option.Copyrights) - @local_copymode.setter - def local_copymode(self, value): - if value not in frozenset({None, "symlink", "hardlink"}): - raise ValueError("local_copymode: None, 'symlink' or 'hardlink'") - self.__table["local_copymode"] = value + @copyrights.setter + def copyrights(self, value): + _type_assert("copyrights", value, bool) + if value: + self.__options |= BaseConfig._Option.Copyrights + else: + self.__options &= ~BaseConfig._Option.Copyrights @property def gnumake(self): - """update the license copyright text""" - return bool(self.__table["options"] & BaseConfig._Option.GNUMake) + """update the license copyright text?""" + return bool(self.__options & BaseConfig._Option.GNUMake) @gnumake.setter def gnumake(self, value): _type_assert("gnumake", value, bool) if value: - self.__table["options"] |= BaseConfig._Option.GNUMake + self.__options |= BaseConfig._Option.GNUMake else: - self.__table["options"] &= ~BaseConfig._Option.GNUMake + self.__options &= ~BaseConfig._Option.GNUMake @property - def copyrights(self): - """update the license copyright text""" - return bool(self.__table["options"] & BaseConfig._Option.Copyrights) + def single_configure(self): + """generate a single configure file?""" + return bool(self.__options & BaseConfig._Option.SingeConfigure) - @copyrights.setter - def copyrights(self, value): - _type_assert("copyrights", value, bool) + @single_configure.setter + def single_configure(self, value): + _type_assert("single_configure", value, bool) if value: - self.__table["options"] |= BaseConfig._Option.Copyrights + self.__options |= BaseConfig._Option.SingeConfigure else: - self.__table["options"] &= ~BaseConfig._Option.Copyrights + self.__options &= ~BaseConfig._Option.SingeConfigure + + + @property + def vc_files(self): + """update version control related files?""" + return self.__table["vc_files"] + + @vc_files.setter + def vc_files(self, value): + _type_assert("vc_files", value, bool) + self.__table["vc_files"] = value def __getitem__(self, key): - table = (set(BaseConfig._TABLE.keys()) | BaseConfig._OPTIONS) - if key not in table: + if key not in BaseConfig._TABLE: key = key.replace("-", "_") - if key not in table: + if key not in BaseConfig._TABLE: raise KeyError("unsupported option: {0}".format(key)) return getattr(self, key) def __setitem__(self, key, value): - table = (set(BaseConfig._TABLE.keys()) | BaseConfig._OPTIONS) - if key not in table: + if key not in BaseConfig._TABLE: key = key.replace("_", "-") - if key not in table: + if key not in BaseConfig._TABLE: raise KeyError("unsupported option: {0}".format(key)) return setattr(self, key, value) @@ -592,8 +622,8 @@ class CachedConfig(BaseConfig): _COMMENTS = _compile(r"((?:(?:#)|(?:^dnl\s+)|(?:\s+dnl\s+)).*?)$") _AUTOCONF = { "ac_version" : _compile(r"AC_PREREQ\(\[(.*?)\]\)"), - "auxdir" : _compile(r"AC_CONFIG_AUX_DIR\(\[(.*?)\]\)$"), - "libtool" : _compile(r"A[CM]_PROG_LIBTOOL") + "auxdir" : _compile(r"AC_CONFIG_AUX_DIR\(\[(.*?)\]\)$"), + "libtool" : _compile(r"A[CM]_PROG_LIBTOOL") } _GNULIB_CACHE = { "overrides" : (list, _compile(r"gl_LOCAL_DIR\(\[(.*?)\]\)")), @@ -623,35 +653,32 @@ class CachedConfig(BaseConfig): } - def __init__(self, configure=None, **kwargs): - super().__init__(**kwargs) - if configure is None: - configure = _os.path.join(self.root, "configure.ac") - if not _os.path.exists(configure): - configure = _os.path.join(self.root, "configure.in") - if not _os.path.isabs(configure): - configure = _os.path.join(self.root, configure) - kwargs = {"configure": _os.path.normpath(configure), "explicit": tuple(kwargs.keys())} - calls = (self.__configure_ac, self.__gnulib_cache, self.__gnulib_comp) - for call in calls: - try: - call(**kwargs) - except FileNotFoundError: - pass # ignore non-existent files - - - def __configure_ac(self, configure, explicit, **kwargs): - with _codecs.open(configure, "rb", "UTF-8") as stream: + def __init__(self, root, ac_file=None, **kwargs): + if ac_file is None: + ac_file = "configure.ac" + _type_assert("ac_file", ac_file, str) + ac_path = _os.path.join(root, ac_file) + if not _os.path.exists(ac_path): + ac_file = "configure.in" + ac_path = _os.path.join(root, ac_file) + ac_path = _os.path.normpath(ac_path) + super().__init__(root=root, **kwargs) + self.__autoconf(ac_path, kwargs) + self.__gnulib_cache(kwargs) + self.__gnulib_comp(kwargs) + + + def __autoconf(self, ac_path, explicit): + with _codecs.open(ac_path, "rb", "UTF-8") as stream: data = CachedConfig._COMMENTS.sub("", stream.read()) for (key, pattern) in CachedConfig._AUTOCONF.items(): match = pattern.findall(data) if match and key not in explicit: self[key] = match[-1] - def __gnulib_cache(self, explicit, **kwargs): - m4_base = self.m4_base + def __gnulib_cache(self, explicit): + expected_m4_base = self.m4_base 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: @@ -673,13 +700,12 @@ class CachedConfig(BaseConfig): else: sep = None if key == "overrides" else "\n" self[key] = [_.strip() for _ in match[-1].split(sep) if _.strip()] - if m4_base != self.m4_base: - raise _M4BaseMismatchError(path, m4_base, self.m4_base) + if expected_m4_base != self.m4_base: + raise _M4BaseMismatchError(path, expected_m4_base, self.m4_base) - def __gnulib_comp(self, explicit, **kwargs): + def __gnulib_comp(self, explicit): macro_prefix = self.macro_prefix 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: diff --git a/pygnulib/parser.py b/pygnulib/parser.py index b46d061e32..505933dcfc 100644 --- a/pygnulib/parser.py +++ b/pygnulib/parser.py @@ -897,12 +897,6 @@ class CommandLine: return "\n".join(lines).format(program=self.__program) - class Option: - """option bitwise flags""" - DryRun = (1 << 0) - SingleConfigure = (1 << 1) - - def __init__(self, program): self.__parser = _argparse.ArgumentParser(prog=program, add_help=False) for (options, kwargs) in CommandLine._SECTIONS_[0][2]: @@ -958,6 +952,5 @@ class CommandLine: verbosity = namespace.pop("verbosity", 0) options = dict(namespace) options.setdefault("dry_run", False) - options.setdefault("single_configure", False) namespace["overrides"] = list(reversed(namespace.get("overrides", []))) return (namespace, mode, verbosity, options) -- 2.39.5