def import_hook(script, gnulib, namespace, explicit, verbosity, options, *args, **kwargs):
(_, _) = (args, kwargs)
config = BaseConfig(**namespace)
- try:
- cache = CachedConfig(root=config.root)
- except FileNotFoundError:
- cache = BaseConfig(**config)
- cache.files = set()
- cache.ac_version = "2.59"
- for key in {"ac_version", "files"}:
- if key not in namespace:
- config[key] = cache[key]
+ cache = CachedConfig(root=config.root, gnulib_comp=False, gnulib_cache=False)
database = Database(gnulib.module, config)
# Print some information about modules.
# Determine license incompatibilities, if any.
incompatibilities = set()
- if config.licenses & {"LGPLv2", "LGPLv2+", "LGPLv3", "LGPLv3+"}:
+ if set(config.licenses) & {"LGPLv2", "LGPLv2+", "LGPLv3", "LGPLv3+"}:
acceptable = IGNORED_LICENSES | config.licenses
for (name, licenses) in ((module.name, module.licenses) for module in main):
if not (acceptable & licenses):
print("\n".join(" " + line for line in notice.splitlines()), file=sys.stdout)
# Determine the final file list.
- files = (set(database.main_files) | set(database.test_files))
+ test_files = set()
+ main_files = set(database.main_files)
+ for file in database.test_files:
+ if file.startswith("lib/"):
+ file = "tests=lib/{}".format(file[len("lib/"):])
+ test_files.add(file)
+ files = (main_files | test_files)
if verbosity >= 0:
print("File list:", file=sys.stdout)
for file in sorted(files):
else:
print(" ", file, file=sys.stdout, sep="")
- table = {}
- overrides = []
- project = BaseVFS(config.root, **table)
- overrides = {BaseVFS(override, **table) for override in config.overrides}
- for (name, key) in SUBSTITUTION.items():
- project[name] = config[key] if key else ""
- for override in overrides:
- for (name, key) in SUBSTITUTION.items():
- override[name] = config[key] if key else ""
-
- table = {}
- old_files = set(cache.files)
- if "m4/gnulib-tool.m4" in project:
- old_files |= set(["m4/gnulib-tool.m4"])
- for (tbl_key, cfg_key) in SUBSTITUTION.items():
- table[tbl_key] = cache[cfg_key] if cfg_key else ""
- new_files = frozenset(files | set(["m4/gnulib-tool.m4"]))
-
dry_run = options["dry_run"]
gnulib_copymode = config.copymode
local_copymode = config.local_copymode
transfer_file(local, src_vfs, src_name, dst_vfs, dst_name)
print(fmt.format(file=dst_name), file=sys.stdout)
+ # Adjust the VFS mappings.
+ overrides = []
+ project = BaseVFS(config.root)
+ overrides = {BaseVFS(override) for override in config.overrides}
+ for (alias, key) in SUBSTITUTION.items():
+ path = config[key] if key else ""
+ project[alias] = path
+ for override in overrides:
+ override[alias] = path
+ gnulib["tests=lib"] = "lib"
+
+ # Determine all relevant file lists.
+ old_files = set(cache.files)
+ if "m4/gnulib-tool.m4" in project:
+ old_files |= set(["m4/gnulib-tool.m4"])
+ new_files = (files | set(["m4/gnulib-tool.m4"]))
+
# First the files that are in old_files, but not in new_files.
# Then the files that are in new_files, but not in old_files.
# Then the files that are in new_files and in old_files.
- removed_files = {file for file in old_files if file not in new_files}
- added_files = {file for file in new_files if file not in old_files}
+ origin = lambda file: ("lib/" + file[len("tests=lib/"):]) if file.startswith("tests=lib/") else file
+ removed_files = set(map(origin, old_files)).difference(map(origin, new_files))
+ added_files = new_files.difference(old_files)
kept_files = (old_files & new_files)
for file in sorted(removed_files):
remove_file(project, file)
# Generate the contents of library makefile.
path = os.path.join(config.source_base, config.makefile_name)
with tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False) as tmp:
- modules = database.main_modules
- for line in lib_makefile(path, config, explicit, database, modules, mkedits, False):
+ arguments = {
+ "path": path,
+ "config": config,
+ "explicit": explicit,
+ "modules": database.main_modules,
+ "mkedits": mkedits,
+ "testing": False,
+ }
+ for line in lib_makefile(**arguments):
print(line, file=tmp)
(src, dst) = (tmp.name, path)
present = vfs_exists(project, dst)
action = update_file if present else add_file
action(False, None, src, project, dst, present)
os.unlink(tmp.name)
- # Fetch PO files.
+
po_root = os.path.join(project.absolute, project["po"])
fmt = ("{} gnulib PO files from " + TP_URL)
print(fmt.format("Fetching", "Fetch")[dry_run], file=sys.stdout)
# Create m4/gnulib-cache.m4.
with tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False) as tmp:
- for line in gnulib_cache(config):
+ for line in gnulib_cache(config, explicit):
print(line, file=tmp)
(src, dst) = (tmp.name, "m4/gnulib-cache.m4")
present = vfs_exists(project, dst)
# Create m4/gnulib-comp.m4.
with tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False) as tmp:
- for line in gnulib_comp(config, explicit, database, True):
+ arguments = {
+ "config": config,
+ "explicit": explicit,
+ "database": database,
+ "subdirs": True,
+ }
+ for line in gnulib_comp(**arguments):
print(line, file=tmp)
(src, dst) = (tmp.name, "m4/gnulib-comp.m4")
present = vfs_exists(project, dst)
if config.tests:
path = os.path.join(config.tests_base, config.makefile_name)
with tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False) as tmp:
- modules = database.test_modules
- for line in tests_makefile(path, config, explicit, database, modules, mkedits, False):
+ arguments = {
+ "path": path,
+ "config": config,
+ "explicit": explicit,
+ "modules": database.test_modules,
+ "mkedits": mkedits,
+ "testing": False,
+ "libtests": database.libtests,
+ }
+ for line in tests_makefile(**arguments):
print(line, file=tmp)
(src, dst) = (tmp.name, path)
present = vfs_exists(project, dst)
action = update_file if present else add_file
action(False, None, src, project, dst, present)
os.unlink(tmp.name)
-
return os.EX_OK
def main(script, gnulib, program, arguments, environ):
gnulib = GnulibGitVFS(gnulib)
- gnulib["tests=lib"] = "lib"
parser = CommandLineParser(program)
try:
(namespace, mode, verbosity, options) = parser.parse(arguments)
"verbosity": verbosity,
"options": options,
}
- for (action, callback) in HOOKS.items():
+ for (action, hook) in HOOKS.items():
if mode.startswith(action):
- return callback(**kwargs)
+ return hook(**kwargs)
return os.EX_SOFTWARE
environ = dict(os.environ)
try:
result = main(script, gnulib, program, arguments, environ)
- except BaseException as error:
+ except StopIteration as error:
with codecs.open(log, "wb", "UTF-8") as stream:
program = repr(program) if " " in program else program
arguments = " ".join(repr(arg) if " " in arg else arg for arg in arguments)
print(traceback.format_exc(), file=stream)
print("COMMAND:", program, arguments, file=stream)
- print("VERSION:", gnulib, file=stream)
typeid = type(error)
module = typeid.__module__
name = typeid.__name__
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
from .error import AutoconfVersionError as _AutoconfVersionError
from .error import M4BaseMismatchError as _M4BaseMismatchError
+from .misc import Property as _Property
+from .misc import PathProperty as _PathProperty
+from .misc import BitwiseProperty as _BitwiseProperty
+from .misc import StringListProperty as _StringListProperty
+from .misc import PathListProperty as _PathListProperty
return _re.compile(regex, _re.S | _re.M)
-_ITERABLES = frozenset((list, tuple, set, frozenset, type({}.keys()), type({}.values())))
-
-
LGPLv2_LICENSE = frozenset({"LGPLv2", "LGPLv2+"})
LGPLv3_LICENSE = frozenset({"LGPLv2+", "LGPLv3", "LGPLv3+", "LGPL"})
-KEYS = frozenset({
- "root",
- "overrides",
- "source_base",
- "m4_base",
- "po_base",
- "doc_base",
- "tests_base",
- "auxdir",
- "libname",
- "makefile_name",
- "macro_prefix",
- "po_domain",
- "witness_c_macro",
- "licenses",
- "ac_version",
- "ac_file",
- "modules",
- "avoids",
- "files",
- "copymode",
- "local_copymode",
- "tests",
- "obsolete",
- "cxx_tests",
- "longrunning_tests",
- "privileged_tests",
- "unportable_tests",
- "libtool",
- "conditionals",
- "copyrights",
- "gnumake",
- "single_configure",
- "vc_files",
- "all_tests",
-})
-
-
-
class BaseConfig:
"""gnulib generic configuration"""
- _TABLE = {
+ __slots__ = ("__options", "__flags", "__active")
+
+
+ __PROPERTIES = {
"root" : ".",
- "overrides" : set(),
+ "overrides" : tuple(),
"source_base" : "lib",
"m4_base" : "m4",
- "po_base" : "po",
+ "po_base" : "",
"doc_base" : "doc",
"tests_base" : "tests",
- "auxdir" : "",
+ "auxdir" : "build-aux",
"libname" : "libgnu",
"makefile_name" : "Makefile.am",
"macro_prefix" : "gl",
"po_domain" : "",
"witness_c_macro" : "",
- "licenses" : set(),
+ "licenses" : tuple(),
"ac_version" : "2.59",
"ac_file" : "configure.ac",
- "modules" : set(),
- "avoids" : set(),
- "files" : set(),
+ "modules" : tuple(),
+ "avoids" : tuple(),
+ "files" : tuple(),
"copymode" : None,
"local_copymode" : None,
"tests" : False,
- "obsolete" : False,
"cxx_tests" : False,
"longrunning_tests" : False,
"privileged_tests" : False,
"unportable_tests" : False,
+ "all_tests" : False,
+ "obsolete" : False,
"libtool" : False,
- "conditionals" : True,
+ "conditionals" : False,
"copyrights" : False,
"gnumake" : False,
"single_configure" : False,
"vc_files" : False,
}
+ __OPTIONS = {
+ "root",
+ "overrides",
+ "source_base",
+ "m4_base",
+ "po_base",
+ "doc_base",
+ "tests_base",
+ "auxdir",
+ "libname",
+ "makefile_name",
+ "macro_prefix",
+ "po_domain",
+ "witness_c_macro",
+ "licenses",
+ "ac_version",
+ "ac_file",
+ "modules",
+ "avoids",
+ "files",
+ "copymode",
+ "local_copymode",
+ }
+ __FLAGS = {
+ "tests",
+ "cxx_tests",
+ "longrunning_tests",
+ "privileged_tests",
+ "unportable_tests",
+ "all_tests",
+ "obsolete",
+ "libtool",
+ "conditionals",
+ "copyrights",
+ "gnumake",
+ "single_configure",
+ "vc_files",
+ }
- class _Option:
- """gnulib configuration options"""
- Tests = (1 << 0)
- Obsolete = (1 << 1)
- CXX = (1 << 2)
- Longrunning = (1 << 3)
- Privileged = (1 << 4)
- Unportable = (1 << 5)
- 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.__options = 0
- self.__table = {}
- for (key, value) in BaseConfig._TABLE.items():
- self[key] = kwargs.get(key, value)
+ def __init__(self, root, **kwargs):
+ if not isinstance(root, str):
+ raise TypeError("root: str expected")
+ if not root:
+ raise ValueError("root: empty path")
+ root = _os.path.normpath(root)
+ self.__active = {}
+ self.__flags = 0
+ self.__options = {}
+ for key in BaseConfig.__OPTIONS:
+ value = BaseConfig.__PROPERTIES[key]
+ self.__set_option_pure(key, value)
+ for key in BaseConfig.__FLAGS:
+ state = BaseConfig.__PROPERTIES[key]
+ mask = getattr(self.__class__, key).mask
+ self.__set_flags_pure(mask, state)
+ self.__set_option("root", root)
- def __repr__(self):
- module = self.__class__.__module__
- name = self.__class__.__name__
- return "{}.{}{}".format(module, name, repr(self.__table))
+ for (key, value) in kwargs.items():
+ setattr(self, key, value)
def __enter__(self):
return self
- def __exit__(self, exctype, excval, exctrace):
- pass
-
-
- @property
- def root(self):
- """target directory"""
- return self.__table["root"]
-
- @root.setter
- def root(self, value):
- _type_assert("root", value, str)
- if not value:
- raise ValueError("non-empty path not allowed")
- self.__table["root"] = _os.path.normpath(value)
-
-
- @property
- def overrides(self):
- """local override directories"""
- return self.__table["overrides"]
-
- @overrides.setter
- def overrides(self, value):
- _type_assert("overrides", value, _ITERABLES)
- result = list()
- for item in value:
- _type_assert("override", item, str)
- result.append(_os.path.normpath(item))
- result = _OrderedDict.fromkeys(result)
- self.__table["overrides"] = tuple(result)
-
-
- @property
- def source_base(self):
- """directory relative to ROOT where source code is placed; defaults to 'lib'"""
- return self.__table["source_base"]
-
- @source_base.setter
- def source_base(self, value):
- _type_assert("source_base", value, str)
- value = _os.path.normpath(value)
- if _os.path.isabs(value):
- raise ValueError("source_base cannot be an absolute path")
- self.__table["source_base"] = _os.path.normpath(value) if value else "lib"
-
-
- @property
- def m4_base(self):
- """directory relative to ROOT where *.m4 macros are placed; defaults to 'm4'"""
- return self.__table["m4_base"]
-
- @m4_base.setter
- def m4_base(self, value):
- _type_assert("m4_base", value, str)
- value = _os.path.normpath(value)
- if _os.path.isabs(value):
- raise ValueError("m4_base cannot be an absolute path")
- self.__table["m4_base"] = _os.path.normpath(value) if value else "m4"
-
-
- @property
- def po_base(self):
- """directory relative to ROOT where *.po files are placed; defaults to 'po'"""
- return self.__table["po_base"]
-
- @po_base.setter
- def po_base(self, value):
- _type_assert("po_base", value, str)
- value = _os.path.normpath(value)
- if _os.path.isabs(value):
- raise ValueError("po_base cannot be an absolute path")
- self.__table["po_base"] = _os.path.normpath(value) if value else "po"
-
-
- @property
- def doc_base(self):
- """directory relative to ROOT where doc files are placed; defaults to 'doc'"""
- return self.__table["doc_base"]
-
- @doc_base.setter
- def doc_base(self, value):
- _type_assert("doc_base", value, str)
- value = _os.path.normpath(value)
- if _os.path.isabs(value):
- raise ValueError("doc_base cannot be an absolute path")
- self.__table["doc_base"] = _os.path.normpath(value) if value else "doc"
-
-
- @property
- def tests_base(self):
- """directory relative to ROOT where unit tests are placed; defaults to 'tests'"""
- return self.__table["tests_base"]
-
- @tests_base.setter
- def tests_base(self, value):
- _type_assert("tests_base", value, str)
- value = _os.path.normpath(value)
- if _os.path.isabs(value):
- raise ValueError("tests_base cannot be an absolute path")
- self.__table["tests_base"] = _os.path.normpath(value) if value else "tests"
-
-
- @property
- def auxdir(self):
- """directory relative to ROOT where auxiliary build tools are placed"""
- return self.__table["auxdir"]
-
- @auxdir.setter
- def auxdir(self, value):
- _type_assert("auxdir", value, str)
- value = _os.path.normpath(value)
- if _os.path.isabs(value):
- raise ValueError("auxdir cannot be an absolute path")
- self.__table["auxdir"] = _os.path.normpath(value) if value else "build-aux"
-
-
- @property
- def libname(self):
- """library name; defaults to 'libgnu'"""
- return self.__table["libname"]
-
- @libname.setter
- def libname(self, value):
- _type_assert("libname", value, str)
- self.__table["libname"] = value if value else "libgnu"
-
-
- @property
- def makefile_name(self):
- """name of makefile in automake syntax in the source-base and tests-base directories"""
- return self.__table["makefile_name"]
-
- @makefile_name.setter
- def makefile_name(self, value):
- _type_assert("makefile_name", value, str)
- value = _os.path.normpath(value)
- if _os.path.isabs(value):
- raise ValueError("makefile_name cannot be an absolute path")
- self.__table["makefile_name"] = value
-
-
- @property
- def macro_prefix(self):
- """
- the prefix of the macros 'gl_EARLY' and 'gl_INIT' (default is 'gl');
- the change of this parameter also affects include_guard_prefix parameter
- """
- return self.__table["macro_prefix"]
-
- @macro_prefix.setter
- def macro_prefix(self, value):
- _type_assert("macro_prefix", value, str)
- self.__table["macro_prefix"] = value
-
-
- @property
- def po_domain(self):
- """the prefix of the i18n domain"""
- return self.__table["po_domain"]
-
- @po_domain.setter
- def po_domain(self, value):
- _type_assert("po_domain", value, str)
- self.__table["po_domain"] = value
-
-
- @property
- def witness_c_macro(self):
- """the C macro that is defined when the sources are compiled or used"""
- return self.__table["witness_c_macro"]
-
- @witness_c_macro.setter
- def witness_c_macro(self, value):
- _type_assert("witness_c_macro", value, str)
- self.__table["witness_c_macro"] = value
-
-
- @property
- def licenses(self):
- """abort if modules aren't available under the LGPL; also modify license template"""
- return self.__table["licenses"]
-
- @licenses.setter
- def licenses(self, value):
- _type_assert("licenses", value, _ITERABLES)
- result = set()
- for item in value:
- _type_assert("license", item, str)
- result.add(item)
- 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
-
+ def __repr__(self):
+ module = self.__class__.__module__
+ name = self.__class__.__name__
+ table = ", ".join(f"{key}={value}" for (key, value) in self.items())
+ return f"{module}.{name}{{{table}}}"
- @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
+ def __getitem__(self, key):
+ if key not in BaseConfig.__PROPERTIES:
+ key = key.replace("-", "_")
+ if key not in BaseConfig.__PROPERTIES:
+ raise KeyError("unsupported option: {0}".format(key))
+ return getattr(self, key)
- @property
- def modules(self):
- """list of modules"""
- return self.__table["modules"]
+ def __setitem__(self, key, value):
+ if key not in BaseConfig.__PROPERTIES:
+ key = key.replace("_", "-")
+ if key not in BaseConfig.__PROPERTIES:
+ raise KeyError("unsupported option: {0}".format(key))
+ return setattr(self, key, value)
- @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)
+ def __get_option(self, key):
+ return self.__options[key]
- @property
- def avoids(self):
- """list of modules to avoid"""
- return self.__table["avoids"]
+ def __set_option_pure(self, key, value):
+ self.__options[key] = value
- @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)
+ def __set_option(self, key, value):
+ self.__active[key] = True
+ return self.__set_option_pure(key, value)
- @property
- def files(self):
- """list of files to be processed"""
- return self.__table["files"]
+ def __get_flags(self, mask):
+ return self.__flags & mask
- @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)
+ def __set_flags_pure(self, mask, state):
+ if state:
+ self.__flags |= mask
+ else:
+ self.__flags &= ~mask
+
+ def __set_flags(self, mask, state):
+ key = {getattr(BaseConfig, key).mask:key for key in BaseConfig.__FLAGS}[mask]
+ self.__active[key] = True
+ return self.__set_flags_pure(mask, state)
+
+
+ root = _PathProperty(
+ fget=lambda self: self.__get_option("root"),
+ fset=lambda self, path: self.__set_option("root", path),
+ doc="target directory (root project directory)",
+ )
+ overrides = _PathListProperty(
+ sorted=False,
+ unique=True,
+ fget=lambda self: self.__get_option("overrides"),
+ fset=lambda self, paths: self.__set_option("overrides", paths),
+ doc="local override directories",
+ )
+ source_base = _PathProperty(
+ fget=lambda self: self.__get_option("source_base"),
+ fset=lambda self, path: self.__set_option("source_base", path),
+ doc="directory relative to ROOT where source code is placed; defaults to 'lib'",
+ )
+ m4_base = _PathProperty(
+ fget=lambda self: self.__get_option("m4_base"),
+ fset=lambda self, path: self.__set_option("m4_base", path),
+ doc="directory relative to ROOT where *.m4 macros are placed; defaults to 'm4'",
+ )
+ po_base = _Property(
+ fget=lambda self: self.__get_option("po_base"),
+ fset=lambda self, path: self.__set_option("po_base", path),
+ check=lambda value: isinstance(value, str),
+ doc="directory relative to ROOT where *.po files are placed; defaults to 'po'",
+ )
+ doc_base = _PathProperty(
+ fget=lambda self: self.__get_option("doc_base"),
+ fset=lambda self, path: self.__set_option("doc_base", path),
+ doc="directory relative to ROOT where doc files are placed; defaults to 'doc'",
+ )
+ tests_base = _PathProperty(
+ fget=lambda self: self.__get_option("tests_base"),
+ fset=lambda self, path: self.__set_option("tests_base", path),
+ doc="directory relative to ROOT where unit tests are placed; defaults to 'tests'",
+ )
+ auxdir = _PathProperty(
+ fget=lambda self: self.__get_option("auxdir"),
+ fset=lambda self, path: self.__set_option("auxdir", path),
+ doc="directory relative to ROOT where auxiliary build tools are placed; defaults to 'build-aux'",
+ )
+ libname = _Property(
+ fget=lambda self: self.__get_option("libname"),
+ fset=lambda self, name: self.__set_option("libname", name),
+ check=lambda value: isinstance(value, str) and value,
+ doc="library name; defaults to 'libgnu'",
+ )
+ makefile_name = _Property(
+ fget=lambda self: self.__get_option("makefile_name"),
+ fset=lambda self, name: self.__set_option("makefile_name", name),
+ check=lambda value: isinstance(value, str) and value,
+ doc="name of makefile in automake syntax in the source-base and tests-base directories",
+ )
+ macro_prefix = _Property(
+ fget=lambda self: self.__get_option("macro_prefix"),
+ fset=lambda self, name: self.__set_option("macro_prefix", name),
+ check=lambda value: isinstance(value, str) and value,
+ doc="the prefix of the macros 'gl_EARLY' and 'gl_INIT'; defaults to 'gl'",
+ )
+ po_domain = _Property(
+ fget=lambda self: self.__get_option("po_domain"),
+ fset=lambda self, name: self.__set_option("po_domain", name),
+ check=lambda value: value is None or isinstance(value, str),
+ doc="the prefix of the i18n domain",
+ )
+ witness_c_macro = _Property(
+ fget=lambda self: self.__get_option("witness_c_macro"),
+ fset=lambda self, name: self.__set_option("witness_c_macro", name),
+ check=lambda value: isinstance(value, str) and (value == "") or not value[0].isdigit(),
+ doc="the C macro that is defined when the sources are compiled or used",
+ )
+ modules = _StringListProperty(
+ sorted=True,
+ unique=True,
+ fget=lambda self: self.__get_option("modules"),
+ fset=lambda self, name: self.__set_option("modules", name),
+ doc="sorted set of the desired modules",
+ )
+ avoids = _StringListProperty(
+ sorted=True,
+ unique=True,
+ fget=lambda self: self.__get_option("avoids"),
+ fset=lambda self, name: self.__set_option("avoids", name),
+ doc="sorted set of the modules that must be avoided",
+ )
+ files = _PathListProperty(
+ sorted=True,
+ unique=True,
+ fget=lambda self: self.__get_option("files"),
+ fset=lambda self, name: self.__set_option("files", name),
+ doc="a list of files to be processed",
+ )
+ copymode = _Property(
+ fget=lambda self: self.__get_option("copymode"),
+ fset=lambda self, mode: self.__set_option("copymode", mode),
+ check=lambda value: value in {None, "hardlink", "symlink"},
+ doc="file copy mode ('symlink', 'hardlink' or None)",
+ )
+ local_copymode = _Property(
+ fget=lambda self: self.__get_option("copymode"),
+ fset=lambda self, mode: self.__set_option("copymode", mode),
+ check=lambda value: value in {None, "hardlink", "symlink"},
+ doc="file copy mode for local directory ('symlink', 'hardlink' or None)",
+ )
+ ac_file = _PathProperty(
+ fget=lambda self: self.__get_option("ac_file"),
+ fset=lambda self, path: self.__set_option("ac_file", path),
+ doc="autoconf file (usually configure.ac or configure.in)",
+ )
+ ac_version = _Property(
+ fget=lambda self: self.__get_option("ac_version"),
+ fset=lambda self, version: self.__set_option("ac_version", str(_version.LooseVersion(version))),
+ doc="minimal supported autoconf version",
+ )
+ licenses = _StringListProperty(
+ sorted=True,
+ unique=True,
+ fget=lambda self: self.__get_option("licenses"),
+ fset=lambda self, name: self.__set_option("licenses", name),
+ doc="acceptable licenses for modules",
+ )
+ tests = _BitwiseProperty(
+ mask=(1 << 0),
+ fget=lambda self, mask: bool(self.__get_flags(mask)),
+ fset=lambda self, mask, state: self.__set_flags(mask, state),
+ doc="include unit tests for the included modules?",
+ )
+ obsolete = _BitwiseProperty(
+ mask=(1 << 1),
+ fget=lambda self, mask: bool(self.__get_flags(mask)),
+ fset=lambda self, mask, state: self.__set_flags(mask, state),
+ doc="include obsolete modules when they occur among the modules?",
+ )
+ cxx_tests = _BitwiseProperty(
+ mask=(1 << 2),
+ fget=lambda self, mask: bool(self.__get_flags(mask)),
+ fset=lambda self, mask, state: self.__set_flags(mask, state),
+ doc="include even unit tests for C++ interoperability?",
+ )
+ longrunning_tests = _BitwiseProperty(
+ mask=(1 << 3),
+ fget=lambda self, mask: bool(self.__get_flags(mask)),
+ fset=lambda self, mask, state: self.__set_flags(mask, state),
+ doc="include even unit tests for C++ interoperability?",
+ )
+ privileged_tests = _BitwiseProperty(
+ mask=(1 << 4),
+ fget=lambda self, mask: bool(self.__get_flags(mask)),
+ fset=lambda self, mask, state: self.__set_flags(mask, state),
+ doc="include even unit tests that require root privileges?",
+ )
+ unportable_tests = _BitwiseProperty(
+ mask=(1 << 5),
+ fget=lambda self, mask: bool(self.__get_flags(mask)),
+ fset=lambda self, mask, state: self.__set_flags(mask, state),
+ doc="include even unit tests that fail on some platforms?",
+ )
+ mask = _BitwiseProperty(
+ mask=(obsolete.mask | cxx_tests.mask | longrunning_tests.mask | privileged_tests.mask | unportable_tests.mask),
+ fget=lambda self, mask: self.__get_flags(mask),
+ doc="configuration acceptibility mask",
+ )
+ all_tests = _BitwiseProperty(
+ mask=(tests.mask | cxx_tests.mask | longrunning_tests.mask | privileged_tests.mask | unportable_tests.mask),
+ fget=lambda self, mask: bool(self.__get_flags(mask) == mask),
+ fset=lambda self, mask, state: self.__set_flags(mask, state),
+ doc="include all kinds of unit tests?",
+ )
+ libtool = _BitwiseProperty(
+ mask=(1 << 6),
+ fget=lambda self, mask: bool(self.__get_flags(mask)),
+ fset=lambda self, mask, state: self.__set_flags(mask, state),
+ doc="use libtool rules?",
+ )
+ conditionals = _BitwiseProperty(
+ mask=(1 << 7),
+ fget=lambda self, mask: bool(self.__get_flags(mask)),
+ fset=lambda self, mask, state: self.__set_flags(mask, state),
+ doc="support conditional dependencies (may save configure time and object code)?",
+ )
+ copyrights = _BitwiseProperty(
+ mask=(1 << 8),
+ fget=lambda self, mask: bool(self.__get_flags(mask)),
+ fset=lambda self, mask, state: self.__set_flags(mask, state),
+ doc="update the license copyright text?",
+ )
+ gnumake = _BitwiseProperty(
+ mask=(1 << 9),
+ fget=lambda self, mask: bool(self.__get_flags(mask)),
+ fset=lambda self, mask, state: self.__set_flags(mask, state),
+ doc="output for GNU Make instead of for the default Automake?",
+ )
+ single_configure = _BitwiseProperty(
+ mask=(1 << 10),
+ fget=lambda self, mask: bool(self.__get_flags(mask)),
+ fset=lambda self, mask, state: self.__set_flags(mask, state),
+ doc="generate a single configure file?",
+ )
+ vc_files = _BitwiseProperty(
+ mask=(1 << 11),
+ fget=lambda self, mask: bool(self.__get_flags(mask)),
+ fset=lambda self, mask, state: self.__set_flags(mask, state),
+ doc="update version control related files?",
+ )
@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
+ prefix = self.macro_prefix.upper()
+ return "GL" if prefix == "GL" else f"GL_{prefix}"
- @property
- def tests(self):
- """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.__options |= BaseConfig._Option.Tests
- else:
- self.__options &= ~BaseConfig._Option.Tests
-
-
- @property
- def obsolete(self):
- """include obsolete modules when they occur among the modules?"""
- return bool(self.__options & BaseConfig._Option.Obsolete)
-
- @obsolete.setter
- def obsolete(self, value):
- _type_assert("obsolete", value, bool)
- if value:
- self.__options |= BaseConfig._Option.Obsolete
- else:
- self.__options &= ~BaseConfig._Option.Obsolete
-
-
- @property
- def cxx_tests(self):
- """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.__options |= BaseConfig._Option.CXX
- else:
- self.__options &= ~BaseConfig._Option.CXX
-
-
- @property
- def longrunning_tests(self):
- """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.__options |= BaseConfig._Option.Longrunning
- else:
- self.__options &= ~BaseConfig._Option.Longrunning
-
-
- @property
- def privileged_tests(self):
- """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.__options |= BaseConfig._Option.Privileged
- else:
- self.__options &= ~BaseConfig._Option.Privileged
-
-
- @property
- def unportable_tests(self):
- """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.__options |= BaseConfig._Option.Unportable
- else:
- self.__options &= ~BaseConfig._Option.Unportable
-
-
- @property
- def all_tests(self):
- """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.__options |= BaseConfig._Option.AllTests
- else:
- self.__options &= BaseConfig._Option.AllTests
-
-
- @property
- def libtool(self):
- """use libtool rules?"""
- return bool(self.__options & BaseConfig._Option.Libtool)
-
- @libtool.setter
- def libtool(self, value):
- _type_assert("libtool", value, bool)
- 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 bool(self.__options & BaseConfig._Option.Conditionals)
-
- @conditionals.setter
- def conditionals(self, value):
- _type_assert("conditionals", value, bool)
- if value:
- self.__options |= BaseConfig._Option.Conditionals
- else:
- self.__options &= ~BaseConfig._Option.Conditionals
-
-
- @property
- def copyrights(self):
- """update the license copyright text?"""
- return bool(self.__options & BaseConfig._Option.Copyrights)
-
- @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.__options & BaseConfig._Option.GNUMake)
-
- @gnumake.setter
- def gnumake(self, value):
- _type_assert("gnumake", value, bool)
- if value:
- self.__options |= BaseConfig._Option.GNUMake
- else:
- self.__options &= ~BaseConfig._Option.GNUMake
-
-
- @property
- def single_configure(self):
- """generate a single configure file?"""
- return bool(self.__options & BaseConfig._Option.SingeConfigure)
-
- @single_configure.setter
- def single_configure(self, value):
- _type_assert("single_configure", value, bool)
- if value:
- self.__options |= BaseConfig._Option.SingeConfigure
- else:
- 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):
- if key not in KEYS:
+ def explicit(self, key):
+ """Determine if option is set to a non-default value."""
+ if key not in BaseConfig.__PROPERTIES:
key = key.replace("-", "_")
- if key not in KEYS:
- raise KeyError("unsupported option: {0}".format(key))
- return getattr(self, key)
-
-
- def __setitem__(self, key, value):
- if key not in KEYS:
- key = key.replace("_", "-")
- if key not in KEYS:
+ if key not in BaseConfig.__PROPERTIES:
raise KeyError("unsupported option: {0}".format(key))
- return setattr(self, key, value)
+ return self.__active.get(key, False)
def items(self):
"""a set-like object providing a view on configuration items"""
- return self.__table.items()
+ for key in BaseConfig.__PROPERTIES:
+ value = self[key]
+ yield (key, value)
def keys(self):
"""a set-like object providing a view on configuration keys"""
- return self.__table.keys()
+ return iter(BaseConfig.__PROPERTIES)
def values(self):
"""a set-like object providing a view on configuration values"""
- return self.__table.values()
+ for key in BaseConfig.__PROPERTIES:
+ yield self[key]
class CachedConfig(BaseConfig):
"""gnulib cached configuration"""
- _COMMENTS = _compile(r"((?:(?:#)|(?:^dnl\s+)|(?:\s+dnl\s+)).*?)$")
- _AUTOCONF = {
+ __slots__ = ()
+
+
+ __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")
}
- _GNULIB_CACHE = {
+ __GNULIB_CACHE = {
"overrides" : (list, _compile(r"gl_LOCAL_DIR\(\[(.*?)\]\)")),
"libtool" : (bool, _compile(r"gl_LIBTOOL\(\[(.*?)\]\)")),
"conditionals" : (bool, _compile(r"gl_CONDITIONAL_DEPENDENCIES\(\[(.*?)\]\)")),
}
- def __init__(self, root, ac_file=None, **kwargs):
+ def __init__(self, root, gnulib_comp=False, gnulib_cache=False, ac_file=None, **kwargs):
if ac_file is None:
ac_file = "configure.ac"
- _type_assert("ac_file", ac_file, str)
+ if not isinstance(ac_file, str):
+ raise TypeError("ac_file: str expected")
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)
+ try:
+ self.__gnulib_cache(kwargs)
+ except FileNotFoundError:
+ if gnulib_cache:
+ raise
+ try:
+ self.__gnulib_comp(kwargs)
+ except FileNotFoundError:
+ if gnulib_comp:
+ raise
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():
+ 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]
if not _os.path.exists(path):
raise FileNotFoundError(path)
with _codecs.open(path, "rb", "UTF-8") as stream:
- data = CachedConfig._COMMENTS.sub("", stream.read())
- for (key, (typeid, pattern)) in CachedConfig._GNULIB_CACHE.items():
+ data = CachedConfig.__COMMENTS.sub("", stream.read())
+ for (key, (typeid, pattern)) in CachedConfig.__GNULIB_CACHE.items():
match = pattern.findall(data)
if match and key not in explicit:
if key == "licenses":
if not _os.path.exists(path):
raise FileNotFoundError(path)
with _codecs.open(path, "rb", "UTF-8") as stream:
- data = CachedConfig._COMMENTS.sub("", stream.read())
+ data = CachedConfig.__COMMENTS.sub("", stream.read())
pattern = _compile(r"AC_DEFUN\(\[{0}_FILE_LIST\], \[(.*?)\]\)".format(macro_prefix))
match = pattern.findall(data)
if match and "files" not in explicit:
-def type_assert(key, value, types):
- """panic if value has a type different than mentioned in types"""
- typeset = []
- if isinstance(types, type):
- types = [types]
- types = tuple(types)
- if not isinstance(value, types):
- for typeid in types:
- module = typeid.__module__
- name = typeid.__name__
- if module == "builtins":
- typeset += [name]
- else:
- typeset += [module + "." + name]
- typeset = "{0}".format(", ".join(typeset))
- raise TypeError("{0}: {1} expected".format(key, typeset))
-
-
-
class AutoconfVersionError(Exception):
"""minimum supported autoconf version mismatch"""
def __init__(self, version):
from datetime import datetime as _datetime
-from .error import type_assert as _type_assert
from .config import BaseConfig as _BaseConfig
from .module import BaseModule as _BaseModule
from .module import Database as _Database
def po_make_vars(config, **override):
"""Generate PO Makefile parameterization."""
- _type_assert("config", config, _BaseConfig)
config = _BaseConfig(**config)
for (key, value) in override.items():
config[key] = value
def POTFILES(config, files, **override):
"""Generate file list to be passed to xgettext."""
- _type_assert("config", config, _BaseConfig)
config = _BaseConfig(**config)
for (key, value) in override.items():
config[key] = value
def autoconf_snippet(config, module, toplevel, no_libtool, no_gettext, **override):
"""
- Generate autoconf snippet for a standalone module.
-
- <config> gnulib configuration
- <module> gnulib module instance
- <toplevel> make a subordinate use of gnulib if False
- <no_libtool> disable libtool (regardless of configuration)
- <no_gettext> disable AM_GNU_GETTEXT invocations if True
- """
- _type_assert("config", config, _BaseConfig)
- _type_assert("module", module, _BaseModule)
- _type_assert("toplevel", toplevel, bool)
- _type_assert("no_libtool", no_libtool, bool)
- _type_assert("no_gettext", no_gettext, bool)
+ Generate autoconf snippet for a standalone module."""
config = _BaseConfig(**config)
for (key, value) in override.items():
config[key] = value
- libtool = config.libtool and not no_libtool
gettext = not no_gettext
if module.name not in ("gnumakefile", "maintainer-makefile") or toplevel:
snippet = module.autoconf_snippet
include_guard_prefix = config.include_guard_prefix
snippet.replace(r"${gl_include_guard_prefix}", include_guard_prefix)
- if not config.libtool:
+ if no_libtool:
table = (
(r"$gl_cond_libtool", "false"),
(r"gl_libdeps", "gltests_libdeps"),
)
for (src, dst) in table:
snippet = snippet.replace(src, dst)
- if not gettext:
+ if no_gettext:
src = "AM_GNU_GETTEXT([external])"
dst = "dnl you must add AM_GNU_GETTEXT([external]) or similar to configure.ac.'"
snippet = snippet.replace(src, dst)
lines = filter(lambda line: line.strip(), snippet.split("\n"))
for line in lines:
yield line
- if module.name == "alloca" and libtool:
+ if module.name == "alloca" and config.libtool and not no_libtool:
yield "changequote(,)dnl"
yield "LTALLOCA=`echo \"$ALLOCA\" | sed -e 's/\\.[^.]* /.lo /g;s/\\.[^.]*$/.lo/'`"
yield "changequote([, ])dnl"
-def autoconf_snippet_sequence(config, database, modules, toplevel, no_libtool, no_gettext, **override):
- """
- Generate an autoconf snippet for multiple modules.
-
- <config> gnulib configuration
- <module> gnulib module instance
- <toplevel> make a subordinate use of gnulib if False
- <no_libtool> disable libtool (regardless of configuration)
- <no_gettext> disable AM_GNU_GETTEXT invocations if True
- [macro_prefix] the prefix of the macros 'gl_EARLY' and 'gl_INIT'
- """
- _type_assert("config", config, _BaseConfig)
- _type_assert("database", database, _Database)
- _type_assert("modules", modules, _ITERABLES)
- _type_assert("toplevel", toplevel, bool)
- _type_assert("no_libtool", no_libtool, bool)
- _type_assert("no_gettext", no_gettext, bool)
+def autoconf_snippets(config, database, modules, toplevel, no_libtool, no_gettext, **override):
+ """Generate an autoconf snippet for multiple modules."""
config = _BaseConfig(**config)
for (key, value) in override.items():
config[key] = value
)
def init_macro_header(config, **override):
- """
- Generate the first few statements of the gl_INIT macro.
-
- [macro_prefix] the prefix of the macros 'gl_EARLY' and 'gl_INIT'
- """
- _type_assert("config", config, _BaseConfig)
+ """Generate the first few statements of the gl_INIT macro."""
config = _BaseConfig(**config)
for (key, value) in override.items():
config[key] = value
def init_macro_footer(config, **override):
"""Generate the last few statements of the gl_INIT macro."""
- _type_assert("config", config, _BaseConfig)
config = _BaseConfig(**config)
for (key, value) in override.items():
config[key] = value
[macro_prefix] the prefix of the macros 'gl_EARLY' and 'gl_INIT'
"""
- _type_assert("config", config, _BaseConfig)
config = _BaseConfig(**config)
for (key, value) in override.items():
config[key] = value
__COMMAND_LINE_PATHS = (
("libname", lambda k, v, d: f"--lib={v}"),
- ("source_base", lambda k, v, d: f"--source_base={v}"),
+ ("source_base", lambda k, v, d: f"--source-base={v}"),
("m4_base", lambda k, v, d: f"--m4-base={v}"),
("po_base", lambda k, v, d: f"--po-base={v}" if k in d else None),
("doc_base", lambda k, v, d: f"--doc-base={v}"),
("all_tests", lambda v: "--with-all-tests" if v else None),
)
__COMMAND_LINE_MISC = (
- ("conditionals", lambda k, v, d: ("--no", "--")[v] + "conditional-dependencies"),
- ("libtool", lambda k, v, d: ("--no", "--")[v] + "libtool"),
+ ("makefile_name", lambda k, v, d: f"--makefile-name={v}"),
+ ("conditionals", lambda k, v, d: ("--no-", "--")[v] + "conditional-dependencies"),
+ ("libtool", lambda k, v, d: ("--no-", "--")[v] + "libtool"),
("macro_prefix", lambda k, v, d: f"--macro-prefix={v}"),
("gnumake", lambda k, v, d: "--gnu-make" if v else None),
- ("makefile_name", lambda k, v, d: f"--makefile-name={v}"),
("po_domain", lambda k, v, d: f"--po-domain={v}" if k in d else None),
("witness_c_macro", lambda k, v, d: f"--witness-c-macro={v}" if k in d else None),
- ("vc_files", lambda k, v, d: ("--no", "--")[v] + "vc-files" if k in d else None),
+ ("vc_files", lambda k, v, d: ("--no-", "--")[v] + "vc-files" if k in d else None),
)
def command_line(config, explicit, **override):
"""Generate gnulib command-line invocation."""
- _type_assert("config", config, _BaseConfig)
- _type_assert("explicit", explicit, _ITERABLES)
-
yield "gnulib-tool --import"
for path in config.overrides:
yield f"--local-dir={path}"
yield option
for module in config.avoids:
yield "--avoid={module.name}"
- if config.licenses in _LGPL:
+ if frozenset(config.licenses) in _LGPL:
lgpl = _LGPL[config.licenses]
if lgpl != "yes":
yield f"--lgpl={lgpl}"
option = hook(key, value, explicit)
if option is not None:
yield option
- for module in sorted(config.modules):
- yield "{}".format(module)
+ for module in config.modules:
+ yield f"{module}"
return callbacks[conditionals][gnumake]
-def lib_makefile(path, config, explicit, database, modules, mkedits, testing, **override):
+def lib_makefile(path, config, explicit, modules, mkedits, testing, **override):
"""Generate library Makefile.am file."""
- _type_assert("path", path, str)
- _type_assert("config", config, _BaseConfig)
- _type_assert("explicit", explicit, _ITERABLES)
- _type_assert("database", database, _Database)
- _type_assert("modules", modules, _ITERABLES)
- _type_assert("mkedits", mkedits, _ITERABLES)
- _type_assert("testing", testing, bool)
-
date = _datetime.now()
- kwargs = {
- "libname": config.libname,
- "macro_prefix": config.macro_prefix,
- "libext": "la" if config.libtool else "a",
- "perhaps_LT": "LT" if config.libtool else "",
- }
+ libname = config.libname
+ po_domain = config.po_domain
+ macro_prefix = config.macro_prefix
+ libext = "la" if config.libtool else "a"
+ perhaps_LT = "LT" if config.libtool else ""
assign = "+=" if config.gnumake or "makefile_name" in explicit else "="
eliminate_LDFLAGS = True if config.libtool else False
yield "## DO NOT EDIT! GENERATED AUTOMATICALLY!"
yield "## Process this file with automake to produce Makefile.in."
- yield "# Copyright (C) 2002-{} Free Software Foundation, Inc.".format(date.year)
+ yield f"# Copyright (C) 2002-{date.year} Free Software Foundation, Inc."
for line in __DISCLAIMER[1:]:
yield line
yield ""
# similarly, the IRIX 6.5 awk fails if a line has length >= 3072.
actioncmd = " ".join(command_line(config, explicit))
if len(actioncmd) <= 3000:
- yield "# Reproduce by: {}".format(actioncmd)
+ yield f"# Reproduce by: {actioncmd}"
yield ""
callback = _lib_makefile_callback(config.conditionals, config.gnumake)
def _snippet():
lines = []
subdirs = False
- for module in database.main_modules:
+ for module in modules:
if module.test:
continue
conditional = module.conditional_automake_snippet
conditional = conditional.replace("lib_LTLIBRARIES", "lib%_LTLIBRARIES")
if eliminate_LDFLAGS:
conditional = __MAKEFILE_LDFLAGS.sub("", conditional)
- conditional = __MAKEFILE_LIBNAME.sub("{libname}_{libext}_\\1".format(**kwargs), conditional)
+ conditional = __MAKEFILE_LIBNAME.sub(f"{libname}_{libext}_\\1", conditional)
conditional = conditional.replace("lib%_LIBRARIES", "lib_LIBRARIES")
conditional = conditional.replace("lib%_LTLIBRARIES", "lib_LTLIBRARIES")
if transform_check_PROGRAMS:
conditional = conditional.replace("check_PROGRAMS", "noinst_PROGRAMS")
conditional = conditional.replace(r"${gl_include_guard_prefix}", config.include_guard_prefix)
unconditional = module.unconditional_automake_snippet.format(auxdir=config.auxdir)
- unconditional = __MAKEFILE_LIBNAME.sub("{libname}_{libext}_\\1".format(**kwargs), unconditional)
+ unconditional = __MAKEFILE_LIBNAME.sub(f"{libname}_{libext}_\\1", unconditional)
if (conditional + unconditional).strip():
- lines.append("## begin gnulib module {}".format(module.name))
+ lines.append(f"## begin gnulib module {module.name}")
if module.name == "alloca":
- lines.append("{libname}_{libext}_LIBADD += @{perhaps_LT}ALLOCA@".format(**kwargs))
- lines.append("{libname}_{libext}_DEPENDENCIES += @{perhaps_LT}ALLOCA@".format(**kwargs))
+ lines.append(f"{libname}_{libext}_LIBADD += @{perhaps_LT}ALLOCA@")
+ lines.append(f"{libname}_{libext}_DEPENDENCIES += @{perhaps_LT}ALLOCA@")
lines += list(callback(module, conditional, unconditional, config.macro_prefix))
- lines.append("## end gnulib module {}".format(module.name))
+ lines.append(f"## end gnulib module {module.name}")
lines.append("")
subdirs |= any(__MAKEFILE_SUBDIRS.match(file) for file in module.files)
return (subdirs, lines)
yield "EXTRA_DIST ="
yield "BUILT_SOURCES ="
yield "SUFFIXES ="
- yield "MOSTLYCLEANFILES {} core *.stackdump".format(assign)
+ yield f"MOSTLYCLEANFILES {assign} core *.stackdump"
if "makefile_name" not in explicit:
yield "MOSTLYCLEANDIRS ="
yield "CLEANFILES ="
else:
yield "== gnulib-tool GNU Make output failed as follows =="
for line in stderr.splitlines():
- yield "# stderr: {}".format(line)
+ yield f"# stderr: {line}"
yield "# End of GNU Make output."
else:
yield "# No GNU Make output."
))
if "makefile_name" not in explicit:
yield ""
- yield "AM_CPPFLAGS ={}".format(cppflags)
+ yield f"AM_CPPFLAGS ={cppflags}"
yield "AM_CFLAGS ="
elif "".join(cppflags):
yield ""
- yield "AM_CPPFLAGS +={}".format(cppflags)
+ yield f"AM_CPPFLAGS +={cppflags}"
yield ""
snippet = "\n".join(lines)
# installation location for the library. Don't confuse automake by saying
# it should not be installed.
# By default, the generated library should not be installed.
- regex = "^[a-zA-Z0-9_]*_{perhaps_LT}LIBRARIES\\s*\\+?\\=\\s*{libname}\\.{libext}$"
- pattern = _re.compile(regex.format(**kwargs), _re.S)
+ pattern = _re.compile(f"^[a-zA-Z0-9_]*_{perhaps_LT}LIBRARIES\\s*\\+?\\=\\s*{libname}\\.{libext}$", _re.S)
if not pattern.findall(snippet):
- yield "noinst_{perhaps_LT}LIBRARIES += {libname}.{libext}".format(**kwargs)
+ yield f"noinst_{perhaps_LT}LIBRARIES += {libname}.{libext}"
yield ""
- yield "{libname}_{libext}_SOURCES =".format(**kwargs)
+ yield f"{libname}_{libext}_SOURCES ="
# Here we use $(LIBOBJS), not @LIBOBJS@. The value is the same. However,
# automake during its analysis looks for $(LIBOBJS), not for @LIBOBJS@.
- yield "{libname}_{libext}_LIBADD = $({macro_prefix}_{perhaps_LT}LIBOBJS)".format(**kwargs)
- yield "{libname}_{libext}_DEPENDENCIES = $({macro_prefix}_{perhaps_LT}LIBOBJS)".format(**kwargs)
- yield "EXTRA_{libname}_{libext}_SOURCES =".format(**kwargs)
+ yield f"{libname}_{libext}_LIBADD = $({macro_prefix}_{perhaps_LT}LIBOBJS)"
+ yield f"{libname}_{libext}_DEPENDENCIES = $({macro_prefix}_{perhaps_LT}LIBOBJS)"
+ yield f"EXTRA_{libname}_{libext}_SOURCES ="
if config.libtool:
- yield "{libname}_{libext}_LDFLAGS = $(AM_LDFLAGS)".format(**kwargs)
- yield "{libname}_{libext}_LDFLAGS += -no-undefined".format(**kwargs)
+ yield f"{libname}_{libext}_LDFLAGS = $(AM_LDFLAGS)"
+ yield f"{libname}_{libext}_LDFLAGS += -no-undefined"
# Synthesize an ${libname}_${libext}_LDFLAGS augmentation by combining
# the link dependencies of all modules.
def _directives(modules):
- directives = (module.link_directive for module in sorted(modules))
- for directive in filter(lambda directive: directive.strip(), directives):
- index = directive.find("when linking with libtool")
- if index != -1:
- directive = directive[:index].strip(" ")
- yield directive
- for directive in _directives(database.main_modules):
- yield "{libname}_{libext}_LDFLAGS += {directive}".format(directive=directive, **kwargs)
+ for module in modules:
+ for directive in module.link_directives:
+ index = directive.find("when linking with libtool")
+ if index != -1:
+ directive = directive[:index].strip(" ")
+ yield directive
+ for directive in sorted(set(_directives(modules))):
+ yield f"{libname}_{libext}_LDFLAGS += {directive}"
yield ""
if "po_base" in explicit:
- yield "AM_CPPFLAGS += -DDEFAULT_TEXT_DOMAIN=\\\"{}-gnulib\\\"".format(config.po_domain)
+ yield f"AM_CPPFLAGS += -DDEFAULT_TEXT_DOMAIN=\\\"{po_domain}-gnulib\\\""
yield ""
for line in lines:
yield snippet
def _gnumake(module, snippet):
- yield "ifeq (,$(OMIT_GNULIB_MODULE_{}))".format(module.name)
+ yield f"ifeq (,$(OMIT_GNULIB_MODULE_{module.name}))"
yield ""
yield snippet
yield "endif"
return _gnumake if gnumake else _automake
-def tests_makefile(path, config, explicit, database, modules, mkedits, testing):
+def tests_makefile(path, config, explicit, modules, mkedits, testing, libtests):
"""Generate tests Makefile.am file."""
- _type_assert("path", path, str)
- _type_assert("config", config, _BaseConfig)
- _type_assert("explicit", explicit, _ITERABLES)
- _type_assert("database", database, _Database)
- _type_assert("modules", modules, _ITERABLES)
- _type_assert("mkedits", mkedits, _ITERABLES)
- _type_assert("testing", testing, bool)
-
- if testing and not config.single_configure:
+ single_configure = config.single_configure
+ if testing and not single_configure:
modules = sorted(filter(lambda module: module.test, modules))
date = _datetime.now()
witness_c_macro = config.witness_c_macro
tests_base_inverse = "/".join(".." for _ in config.tests_base.split(_os.path.sep))
(libname, libext) = (config.libname, "la" if config.libtool else "a")
- kwargs = {
- "libname": config.libname,
- "macro_prefix": config.macro_prefix,
- "libext": "la" if config.libtool else "a",
- "perhaps_LT": "LT" if config.libtool else "",
- }
assign = "+=" if config.gnumake or "makefile_name" in explicit else "="
eliminate_LDFLAGS = True if config.libtool else False
yield "## DO NOT EDIT! GENERATED AUTOMATICALLY!"
yield "## Process this file with automake to produce Makefile.in."
- yield "# Copyright (C) 2002-{} Free Software Foundation, Inc.".format(date.year)
+ yield f"# Copyright (C) 2002-{date.year} Free Software Foundation, Inc."
for line in __DISCLAIMER[1:]:
yield line
yield ""
if transform_check_PROGRAMS:
snippet = snippet.replace("check_PROGRAMS", "noinst_PROGRAMS")
snippet = snippet.replace(r"${gl_include_guard_prefix}", config.include_guard_prefix)
- if database.libtests and module.name == "alloca":
+ if libtests and module.name == "alloca":
lines += ["libtests_a_LIBADD += @ALLOCA@"]
lines += ["libtests_a_DEPENDENCIES += @ALLOCA@"]
subdirs |= any(__MAKEFILE_SUBDIRS.match(file) for file in module.files)
if snippet.strip():
- lines.append("## begin gnulib module {}".format(module.name))
+ lines.append(f"## begin gnulib module {module.name}")
lines += list(callback(module, snippet))
- lines.append("## end gnulib module {}".format(module.name))
+ lines.append(f"## end gnulib module {module.name}")
lines.append("")
if module.longrunning_test:
longrunning += lines
yield "EXTRA_PROGRAMS ="
yield "noinst_HEADERS ="
yield "noinst_LIBRARIES ="
- if database.libtests:
+ if libtests:
if testing:
yield "noinst_LIBRARIES += libtests.a"
else:
# module whose dependency to 'progname' is voluntarily omitted).
# The LIBTESTS_LIBDEPS can be passed to the linker once or twice, it does
# not matter.
- local_ldadd_before = " libtests.a" if database.libtests else ""
- local_ldadd_after = " libtests.a $(LIBTESTS_LIBDEPS)" if database.libtests else ""
+ local_ldadd_before = " libtests.a" if libtests else ""
+ local_ldadd_after = " libtests.a $(LIBTESTS_LIBDEPS)" if libtests else ""
yield f"LDADD ={local_ldadd_before} {tests_base_inverse}/{source_base}/{libname}.{libext}{local_ldadd_after}"
yield ""
- if database.libtests:
+ if libtests:
yield "libtests_a_SOURCES ="
# Here we use $(LIBOBJS), not @LIBOBJS@. The value is the same. However,
# automake during its analysis looks for $(LIBOBJS), not for @LIBOBJS@.
("privileged", "gl_WITH_PRIVILEGED_TESTS"),
)
-def gnulib_cache(config):
+def gnulib_cache(config, explicit):
"""
Generate gnulib-cache.m4 file.
"""
- _type_assert("config", config, _BaseConfig)
-
date = _datetime.now()
yield "## DO NOT EDIT! GENERATED AUTOMATICALLY!"
yield "## Process this file with automake to produce Makefile.in."
- yield "# Copyright (C) 2002-{} Free Software Foundation, Inc.".format(date.year)
+ yield f"# Copyright (C) 2002-{date.year} Free Software Foundation, Inc."
for line in __DISCLAIMER:
yield line
yield "#"
yield ""
yield ""
yield "# Specification in the form of a command-line invocation:"
- yield "gl_LOCAL_DIR([$relative_local_gnulib_path])"
+ yield "# " + " ".join(command_line(config, explicit))
+ yield ""
+ yield "# Specification in the form of a few gnulib-tool.m4 macro invocations:"
+ yield "gl_LOCAL_DIR([{}])".format(" ".join(config.overrides))
yield "gl_MODULES(["
for module in sorted(config.modules):
- yield " {}".format(module)
+ yield f" {module}"
yield "])"
for key in ("obsolete", "cxx_tests", "longrunning_tests", "privileged_tests", "unportable_tests"):
if config[key]:
if config.all_tests:
yield "gl_WITH_ALL_TESTS"
yield "gl_AVOID([{}])".format(" ".join(sorted(config.avoids)))
- yield "gl_SOURCE_BASE([{}])".format(config.source_base)
- yield "gl_M4_BASE([{}])".format(config.m4_base)
- yield "gl_PO_BASE([{}])".format(config.po_base)
- yield "gl_DOC_BASE([{}])".format(config.doc_base)
- yield "gl_TESTS_BASE([{}])".format(config.tests_base)
+ yield f"gl_SOURCE_BASE([{config.source_base}])"
+ yield f"gl_M4_BASE([{config.m4_base}])"
+ yield f"gl_PO_BASE([{config.po_base}])"
+ yield f"gl_DOC_BASE([{config.doc_base}])"
+ yield f"gl_TESTS_BASE([{config.tests_base}])"
if config.tests:
yield "gl_WITH_TESTS"
yield "gl_LIB([{}])".format(config.libname)
- if config.licenses in _LGPL:
+ if frozenset(config.licenses) in _LGPL:
lgpl = _LGPL[config.licenses]
yield "gl_LGPL([{}])".format(lgpl) if lgpl != "yes" else "gl_LGPL"
- yield "gl_MAKEFILE_NAME([{}])".format(config.makefile_name)
+ yield f"gl_MAKEFILE_NAME([{config.makefile_name}])"
if config.conditionals:
yield "gl_CONDITIONAL_DEPENDENCIES"
if config.libtool:
yield "gl_LIBTOOL"
- yield "gl_MACRO_PREFIX([{}])".format(config.macro_prefix)
- yield "gl_PO_DOMAIN([{}])".format(config.po_domain)
- yield "gl_WITNESS_C_MACRO([{}])".format(config.witness_c_macro)
+ yield f"gl_MACRO_PREFIX([{config.macro_prefix}])"
+ yield f"gl_PO_DOMAIN([{config.po_domain}])"
+ yield f"gl_WITNESS_C_MACRO([{config.witness_c_macro}])"
if config.vc_files:
- yield "gl_VC_FILES([{}])".format(" ".join(sorted(config.vc_files)))
+ yield "gl_VC_FILES([{}])".format(" ".join(config.vc_files))
def gnulib_comp(config, explicit, database, subdirs, **override):
"""gnulib-comp.m4 generator"""
- _type_assert("config", config, _BaseConfig)
- _type_assert("explicit", explicit, _ITERABLES)
- _type_assert("database", database, _Database)
config = _BaseConfig(**config)
for (key, value) in override.items():
config[key] = value
test_modules = database.test_modules
date = _datetime.now()
+ ac_file = config.ac_file
yield "# DO NOT EDIT! GENERATED AUTOMATICALLY!"
- yield "# Copyright (C) 2002-{} Free Software Foundation, Inc.".format(date.year)
+ yield f"# Copyright (C) 2002-{date.year} Free Software Foundation, Inc."
for line in __DISCLAIMER[1:]:
yield line
yield "# other built files."
yield ""
yield ""
- yield "# This macro should be invoked from {}, in the section".format(config.ac_file)
+ yield f"# This macro should be invoked from {ac_file}, in the section"
yield "# \"Checks for programs\", right after AC_PROG_CC, and certainly before"
yield "# any checks for libraries, header files, types and library functions."
- yield "AC_DEFUN([{}_EARLY],".format(config.macro_prefix)
+ yield f"AC_DEFUN([{macro_prefix}_EARLY],"
yield "["
yield " m4_pattern_forbid([^gl_[A-Z]])dnl the gnulib macro namespace"
yield " m4_pattern_allow([^gl_ES$])dnl a valid locale name"
if not config.gnumake and subdirs:
yield " AC_REQUIRE([AM_PROG_CC_C_O])"
for module in database.final_modules:
- yield " # Code from module {}:".format(module.name)
+ yield f" # Code from module {module.name}:"
lines = module.early_autoconf_snippet.split("\n")
for line in filter(lambda line: line.strip(), lines):
- yield " {}".format(line)
+ yield f" {line}"
yield "])"
yield ""
- yield "# This macro should be invoked from {}, in the section".format(config.ac_file)
+ yield f"# This macro should be invoked from {ac_file}, in the section"
yield "# \"Check for header files, types and library functions\"."
- yield "AC_DEFUN([{}_INIT],".format(macro_prefix)
+ yield f"AC_DEFUN([{macro_prefix}_INIT],"
yield "["
if config.libtool:
yield " AM_CONDITIONAL([GL_COND_LIBTOOL], [true])"
yield " gl_cond_libtool=false"
yield " gl_libdeps="
yield " gl_ltlibdeps="
- yield " gl_m4_base='{}'".format(config.m4_base)
+ yield f" gl_m4_base='{config.m4_base}'"
for line in init_macro_header(config, macro_prefix=macro_prefix):
yield line
- yield " gl_source_base='{}'".format(config.source_base)
+ yield f" gl_source_base='{config.source_base}'"
if "witness_c_macro" in explicit:
- yield " m4_pushdef([gl_MODULE_INDICATOR_CONDITION], [{}])".format(config.witness_c_macro)
- for line in autoconf_snippet_sequence(config, database, main_modules, True, False, True, macro_prefix=macro_prefix):
+ yield f" m4_pushdef([gl_MODULE_INDICATOR_CONDITION], [{config.witness_c_macro}])"
+ for line in autoconf_snippets(config, database, main_modules, True, False, True, macro_prefix=macro_prefix):
yield line
if "witness_c_macro" in explicit:
yield " m4_popdef([gl_MODULE_INDICATOR_CONDITION])"
yield " gltests_ltlibdeps="
for line in init_macro_header(config, macro_prefix=(macro_prefix + "tests")):
yield line
- yield " gl_source_base='{}'".format(config.tests_base)
+ yield f" gl_source_base='{config.tests_base}'"
# Define a tests witness macro that depends on the package.
# PACKAGE is defined by AM_INIT_AUTOMAKE, PACKAGE_TARNAME is defined by AC_INIT.
# See <http://lists.gnu.org/archive/html/automake/2009-05/msg00145.html>.
yield "changequote(,)dnl"
yield "".join((
- " {}tests_WITNESS=IN_`".format(macro_prefix),
+ f" {macro_prefix}tests_WITNESS=IN_`",
"echo \"${PACKAGE-$PACKAGE_TARNAME}\"",
" | ",
"LC_ALL=C tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"`_GNULIB_TESTS",
))
yield "changequote([, ])dnl"
- yield " AC_SUBST([{}tests_WITNESS])".format(macro_prefix)
- yield " gl_module_indicator_condition=${}tests_WITNESS".format(macro_prefix)
+ yield f" AC_SUBST([{macro_prefix}tests_WITNESS])"
+ yield f" gl_module_indicator_condition=${macro_prefix}tests_WITNESS"
yield " m4_pushdef([gl_MODULE_INDICATOR_CONDITION], [$gl_module_indicator_condition])"
- for line in autoconf_snippet_sequence(config, database, test_modules, True, True, True, macro_prefix=macro_prefix):
+ for line in autoconf_snippets(config, database, test_modules, True, True, True, macro_prefix=macro_prefix):
yield line
yield " m4_popdef([gl_MODULE_INDICATOR_CONDITION])"
for line in init_macro_footer(config, macro_prefix=(macro_prefix + "tests")):
# created using libtool, because libtool already handles the dependencies.
if not config.libtool:
libname = config.libname.upper()
- yield " {}_LIBDEPS=\"$gl_libdeps\"".format(libname)
- yield " AC_SUBST([{}_LIBDEPS])".format(libname)
- yield " {}_LTLIBDEPS=\"$gl_ltlibdeps\"".format(libname)
- yield " AC_SUBST([{}_LTLIBDEPS])".format(libname)
+ yield f" {libname}_LIBDEPS=\"$gl_libdeps\""
+ yield f" AC_SUBST([{libname}_LIBDEPS])"
+ yield f" {libname}_LTLIBDEPS=\"$gl_ltlibdeps\""
+ yield f" AC_SUBST([{libname}_LTLIBDEPS])"
if database.libtests:
yield " LIBTESTS_LIBDEPS=\"$gltests_libdeps\""
yield " AC_SUBST([LIBTESTS_LIBDEPS])"
yield ""
yield "# This macro records the list of files which have been installed by"
yield "# gnulib-tool and may be removed by future gnulib-tool invocations."
- yield "AC_DEFUN([{}_FILE_LIST], [".format(macro_prefix)
- for file in sorted(set(database.main_files + database.test_files)):
- yield " {}".format(file)
+ yield f"AC_DEFUN([{macro_prefix}_FILE_LIST], ["
+ test_files = set()
+ main_files = set(database.main_files)
+ for file in database.test_files:
+ if file.startswith("lib/"):
+ file = ("tests=lib/" + file[len("lib/"):])
+ test_files.add(file)
+ for file in sorted(main_files | test_files):
+ yield f" {file}"
yield "])"
--- /dev/null
+#!/usr/bin/python
+# encoding: UTF-8
+"""miscellaneous tools and traits"""
+
+
+
+import os as _os
+from collections import OrderedDict as _OrderedDict
+
+
+
+class Property(property):
+ """generic property"""
+ @staticmethod
+ def __fget(*args, **kwargs):
+ raise AttributeError("unreadable attribute")
+
+ @staticmethod
+ def __fset(*args, **kwargs):
+ raise AttributeError("can't set attribute")
+
+
+ def __init__(self, fget=None, fset=None, doc=None, check=None):
+ """
+ doc : any arbitrary string
+ fget : function to get the value
+ fset : function to set the value
+
+ def fget(self):
+ return self.value
+
+ def fset(self, value):
+ self.value = value
+ """
+ fget = Property.__fget if fget is None else fget
+ fset = Property.__fset if fset is None else fset
+ # if not callable(fget):
+ # raise TypeError("fget: callable expected")
+ # if not callable(fset):
+ # raise TypeError("fset: callable expected")
+ super().__init__(fget=fget, fset=fset, doc=doc)
+ self.__check = check
+ self.__fget = fget
+ self.__fset = fset
+ self.__doc__ = doc
+
+
+ def __set__(self, obj, val):
+ if self.__check:
+ if not self.__check(val):
+ raise ValueError("value check failed")
+ return super().__set__(obj, val)
+
+
+class PathProperty(Property):
+ """file system path property"""
+ def __init__(self, fget=None, fset=None, doc=None):
+ """
+ doc : any arbitrary string
+ fget : function to get the value
+ fset : function to set the value
+
+ def fget(self):
+ return self.path
+
+ def fset(self, path):
+ self.path = path
+ """
+ super().__init__(fget=fget, fset=fset, doc=doc)
+
+
+ def __set__(self, obj, val):
+ if not isinstance(val, str):
+ raise TypeError("value: str expected")
+ if not val:
+ raise ValueError("value: empty path")
+ val = _os.path.normpath(val)
+ return super().__set__(obj, val)
+
+
+
+class BitwiseProperty(Property):
+ """bitwise flag property"""
+ def __init__(self, mask, fget=None, fset=None, doc=None):
+ """
+ mask : int bitwise mask
+ doc : any arbitrary string
+ fget : function to get the value
+ fset : function to set the value
+
+ def fget(self, mask):
+ return bool(self.mask & mask)
+
+ def fset(self, mask, state):
+ if state:
+ self.mask |= mask
+ else:
+ self.mask &= ~mask
+ """
+ if not isinstance(mask, int):
+ raise TypeError("mask: int expected")
+
+ def _fget(obj):
+ return self.__fget(obj, self.__mask)
+
+ def _fset(obj, state):
+ return self.__fset(obj, self.__mask, state)
+
+ super().__init__(doc=doc, fget=_fget, fset=_fset)
+ self.__mask = mask
+ self.__fget = fget
+ self.__fset = fset
+
+
+ @property
+ def mask(self):
+ return self.__mask
+
+
+ def __set__(self, obj, val):
+ if not isinstance(val, bool):
+ raise TypeError("value: bool expected")
+ return super().__set__(obj, val)
+
+
+
+class StringListProperty(Property):
+ """string sequence property"""
+ __TYPES = (list, tuple, set, frozenset, type({}.keys()), type({}.values()))
+
+ def __init__(self, sorted=False, unique=False, fget=None, fset=None, doc=None):
+ if not isinstance(sorted, bool):
+ raise TypeError("sorted: bool expected")
+ if not isinstance(unique, bool):
+ raise TypeError("unique: bool expected")
+ super().__init__(fget=fget, fset=fset, doc=doc)
+ self.__sorted = sorted
+ self.__unique = unique
+
+
+ def __set__(self, obj, val):
+ if not isinstance(val, StringListProperty.__TYPES):
+ raise TypeError("value: iterable expected")
+ res = list()
+ for item in val:
+ if not isinstance(item, str):
+ raise TypeError("item: str expected")
+ res.append(item)
+ res = tuple(_OrderedDict.fromkeys(res) if self.__unique else res)
+ val = tuple(sorted(res) if self.__sorted else res)
+ return super().__set__(obj, val)
+
+
+
+class PathListProperty(StringListProperty):
+ """path sequence property"""
+ __TYPES = (list, tuple, set, frozenset, type({}.keys()), type({}.values()))
+
+
+ def __init__(self, sorted=False, unique=False, fget=None, fset=None, doc=None):
+ super().__init__(sorted=sorted, unique=unique, fget=fget, fset=fset, doc=doc)
+
+
+ def __set__(self, obj, val):
+ if not isinstance(val, PathListProperty.__TYPES):
+ raise TypeError("value: iterable expected")
+ res = list()
+ for item in val:
+ if not isinstance(item, str):
+ raise TypeError("item: str expected")
+ if not item:
+ raise TypeError("item: empty path")
+ item = _os.path.normpath(item)
+ res.append(item)
+ return super().__set__(obj, res)
import json as _json
import os as _os
import re as _re
+import sys as _sys
-from .error import type_assert as _type_assert
from .error import UnknownModuleError as _UnknownModuleError
from .config import BaseConfig as _BaseConfig
-
-
-
-_ITERABLES = frozenset((list, tuple, set, frozenset, type({}.keys()), type({}.values())))
+from .misc import Property as _Property
+from .misc import PathProperty as _PathProperty
+from .misc import StringListProperty as _StringListProperty
+from .misc import PathListProperty as _PathListProperty
class BaseModule:
"""base module"""
- _TABLE = {
- "description" : (0x00, str, "Description"),
- "comment" : (0x01, str, "Comment"),
- "status" : (0x02, tuple, "Status"),
- "notice" : (0x03, str, "Notice"),
- "applicability" : (0x04, str, "Applicability"),
- "files" : (0x05, tuple, "Files"),
- "dependencies" : (0x06, tuple, "Depends-on"),
- "early_autoconf_snippet" : (0x07, str, "configure.ac-early"),
- "autoconf_snippet" : (0x08, str, "configure.ac"),
- "conditional_automake_snippet" : (0x09, str, "Makefile.am"),
- "include_directive" : (0x0A, str, "Include"),
- "link_directive" : (0x0B, str, "Link"),
- "licenses" : (0x0C, tuple, "License"),
- "maintainers" : (0x0D, tuple, "Maintainer"),
- # unconditional_automake_snippet
- # automake_snippet
+ __slots__ = ("__options", "__flags")
+
+
+ __PROPERTIES = {
+ "name" : None,
+ "description" : "",
+ "comment" : "",
+ "status" : "",
+ "notice" : "",
+ "applicability" : "",
+ "files" : "",
+ "dependencies" : "",
+ "early_autoconf_snippet" : "",
+ "autoconf_snippet" : "",
+ "conditional_automake_snippet" : "",
+ "unconditional_automake_snippet" : None,
+ "automake_snippet" : None,
+ "include_directives" : "",
+ "link_directives" : "",
+ "licenses" : "",
+ "maintainers" : "",
+ "test" : "",
}
- _PROPERTIES = {
+ __OPTIONS = {
"name",
"description",
"comment",
"conditional_automake_snippet",
"unconditional_automake_snippet",
"automake_snippet",
- "include_directive",
- "link_directive",
+ "include_directives",
+ "link_directives",
"licenses",
"maintainers",
"test",
}
+ __FLAGS = {}
__LIB_SOURCES = _re.compile(r"^lib_SOURCES\s*\+\=\s*(.*?)$", _re.S | _re.M)
def __init__(self, name, **kwargs):
- _type_assert("name", name, str)
- if "licenses" in kwargs:
- licenses = set()
- for license in kwargs.get("licenses", tuple()):
- _type_assert("license", license, str)
- licenses.add(license)
- kwargs["licenses"] = licenses
- if "maintainers" not in kwargs:
- kwargs["maintainers"] = {"all"}
- self.__name = name
- self.__table = _collections.OrderedDict()
- for (key, (_, typeid, _)) in BaseModule._TABLE.items():
- self.__table[key] = typeid(kwargs.get(key, typeid()))
-
-
- def __repr__(self):
- return "<" + self.__name + ">"
+ if not isinstance(name, str):
+ raise TypeError("name: str expected")
+ if not name.strip():
+ raise ValueError("name: invalid name")
+
+ self.__flags = 0
+ self.__options = {}
+ for key in BaseModule.__OPTIONS:
+ value = BaseModule.__PROPERTIES[key]
+ if value is not None:
+ self.__set_option(key, value)
+ for key in BaseModule.__FLAGS:
+ state = BaseModule.__PROPERTIES[key]
+ mask = getattr(self.__class__, key).mask
+ self.__set_flags(mask, state)
+ self.__set_option("name", name)
-
- def __str__(self):
- return "<" + self.__name + ">"
+ for (key, value) in kwargs.items():
+ setattr(self, key, value)
def __enter__(self):
return self
- def __exit__(self, exctype, excval, exctrace):
- pass
+ def __hash__(self):
+ return hash(_json.dumps(dict(self.__options), sort_keys=True))
- def __hash__(self):
- return hash(self.__name) ^ hash(_json.dumps(dict(self.__table), sort_keys=True))
+ def __repr__(self):
+ module = self.__class__.__module__
+ name = self.__class__.__name__
+ return f"{module}.{name}[{self.name}]"
def __getitem__(self, key):
- _type_assert("key", key, str)
- if key not in BaseModule._PROPERTIES:
+ if key not in BaseModule.__PROPERTIES:
key = key.replace("-", "_")
- if key not in BaseModule._PROPERTIES:
+ if key not in BaseModule.__PROPERTIES:
raise KeyError(repr(key))
return getattr(self, key)
def __setitem__(self, key, value):
- _type_assert("key", key, str)
- if key not in BaseModule._PROPERTIES:
+ if key not in BaseModule.__PROPERTIES:
key = key.replace("-", "_")
- if key not in BaseModule._PROPERTIES:
+ if key not in BaseModule.__PROPERTIES:
raise KeyError(repr(key))
return setattr(self, key, value)
+ def __get_option(self, key):
+ return self.__options[key]
+
+ def __set_option(self, key, value):
+ self.__options[key] = value
+
+
@property
- def gnulib(self):
+ def gnulib_package(self):
"""gnulib-compatible module textual representation"""
def _gnulib():
yield "Description:"
- yield self.__table["description"]
+ yield self.description
yield "Comment:"
- yield self.__table["comment"]
+ yield self.comment
yield "Status:"
- for status in sorted(self.__table["status"]):
+ for status in sorted(self.status):
yield status
yield "Notice:"
- yield self.__table["notice"]
+ yield self.notice
yield "Applicability:"
- yield self.__table["applicability"]
+ yield self.applicability
yield "Files:"
- for file in sorted(self.__table["files"]):
+ for file in self.files:
yield file
yield "Depends-on:"
- for (module, condition) in self.__table["dependencies"]:
+ for (module, condition) in self.dependencies:
yield "{} {}".format(module, condition)
yield "configure.ac-early:"
- yield self.__table["early_autoconf_snippet"]
+ yield self.early_autoconf_snippet
yield "configure.ac:"
- yield self.__table["autoconf_snippet"]
+ yield self.autoconf_snippet
yield "Makefile.am:"
- yield self.__table["conditional_automake_snippet"]
+ yield self.conditional_automake_snippet
yield "Include:"
- yield self.__table["include_directive"]
+ for include in self.include_directives:
+ yield include
yield "Link:"
- yield self.__table["link_directive"]
+ for link in self.link_directives:
+ yield link
yield "License:"
- for license in sorted(self.__table["licenses"]):
+ for license in self.licenses:
yield license
yield "Maintainer:"
- for maintainer in self.__table["maintainers"]:
+ for maintainer in maintainers:
yield maintainer
return "\n".join(_gnulib())
- @property
- def name(self):
- """name"""
- return self.__name
-
- @name.setter
- def name(self, value):
- _type_assert("name", value, str)
- self.__name = value
-
-
- @property
- def description(self):
- """description"""
- return self.__table["description"]
-
- @description.setter
- def description(self, value):
- _type_assert("description", value, str)
- self.__table["description"] = value
-
-
- @property
- def comment(self):
- """comment"""
- return self.__table["comment"]
-
- @comment.setter
- def comment(self, value):
- _type_assert("comment", value, str)
- self.__table["comment"] = value
-
-
- @property
- def status(self):
- """status"""
- return self.__table["status"]
-
- @status.setter
- def status(self, value):
- _type_assert("status", value, _ITERABLES)
- result = set()
- for item in value:
- _type_assert("status", item, str)
- result.add(item)
- self.__table["status"] = tuple(result)
-
-
- @property
- def obsolete(self):
- """module is obsolete?"""
- return "obsolete" in self.status
-
- @property
- def cxx_test(self):
- """module is a C++ test?"""
- return "c++-test" in self.status
-
- @property
- def longrunning_test(self):
- """module is a C++ test?"""
- return "longrunning-test" in self.status
-
- @property
- def privileged_test(self):
- """module is a privileged test?"""
- return "privileged-test" in self.status
-
- @property
- def unportable_test(self):
- """module is a unportable test?"""
- return "unportable-test" in self.status
-
-
- @property
- def notice(self):
- """notice"""
- return self.__table["notice"]
-
- @notice.setter
- def notice(self, value):
- _type_assert("notice", value, str)
- self.__table["notice"] = value
-
-
- @property
- def applicability(self):
- """applicability (usually "main" or "tests")"""
- return self.__table["applicability"]
-
- @applicability.setter
- def applicability(self, value):
- _type_assert("applicability", value, str)
- if value not in ("all", "main", "tests"):
- raise ValueError("applicability: \"main\", \"tests\" or \"all\"")
- self.__table["applicability"] = value
-
-
- @property
- def files(self):
- """file dependencies iterator (set of strings)"""
- 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"] = tuple(result)
-
-
- @property
+ name = _Property(
+ fget=lambda self: self.__get_option("name"),
+ fset=lambda self, string: self.__set_option("name", string),
+ check=lambda value: isinstance(value, str) and value,
+ doc="name",
+ )
+ description = _Property(
+ fget=lambda self: self.__get_option("description"),
+ fset=lambda self, string: self.__set_option("description", string),
+ check=lambda value: isinstance(value, str),
+ doc="description",
+ )
+ comment = _Property(
+ fget=lambda self: self.__get_option("comment"),
+ fset=lambda self, string: self.__set_option("comment", string),
+ check=lambda value: isinstance(value, str),
+ doc="comment",
+ )
+ status = _StringListProperty(
+ sorted=True,
+ unique=True,
+ fget=lambda self: self.__get_option("comment"),
+ fset=lambda self, string: self.__set_option("comment", string),
+ doc="status list",
+ )
+ obsolete = _Property(
+ fget=lambda self: "obsolete" in self.status,
+ doc="module is obsolete?",
+ )
+ cxx_test = _Property(
+ fget=lambda self: "c++-test" in self.status,
+ doc="module is a C++ test?",
+ )
+ longrunning_test = _Property(
+ fget=lambda self: "longrunning-test" in self.status,
+ doc="module is a longrunning test?",
+ )
+ privileged_test = _Property(
+ fget=lambda self: "privileged-test" in self.status,
+ doc="module is a privileged test?",
+ )
+ unportable_test = _Property(
+ fget=lambda self: "unportable-test" in self.status,
+ doc="module is an unportable test?",
+ )
+ mask = _Property(
+ fget=lambda self: ((0, (1 << 0))[self.obsolete]
+ |(0, (1 << 1))[self.cxx_test]
+ |(0, (1 << 2))[self.longrunning_test]
+ |(0, (1 << 3))[self.privileged_test]
+ |(0, (1 << 4))[self.unportable_test]),
+ doc="module acceptibility mask",
+ )
+ notice = _Property(
+ fget=lambda self: self.__get_option("notice"),
+ fset=lambda self, string: self.__set_option("notice", string),
+ check=lambda value: isinstance(value, str),
+ doc="module notice or disclaimer",
+ )
+ applicability = _Property(
+ fget=lambda self: self.__get_option("applicability"),
+ fset=lambda self, string: self.__set_option("applicability", string),
+ check=lambda value: isinstance(value, str) and value in {"main", "tests", "all"},
+ doc="applicability ('main', 'tests' or 'all')",
+ )
+ files = _PathListProperty(
+ sorted=True,
+ unique=True,
+ fget=lambda self: self.__get_option("files"),
+ fset=lambda self, string: self.__set_option("files", string),
+ doc="file dependencies",
+ )
+ early_autoconf_snippet = _Property(
+ fget=lambda self: self.__get_option("early_autoconf_snippet"),
+ fset=lambda self, string: self.__set_option("early_autoconf_snippet", string),
+ check=lambda value: isinstance(value, str),
+ doc="early configure.ac snippet",
+ )
+ autoconf_snippet = _Property(
+ fget=lambda self: self.__get_option("autoconf_snippet"),
+ fset=lambda self, string: self.__set_option("autoconf_snippet", string),
+ check=lambda value: isinstance(value, str),
+ doc="configure.ac snippet",
+ )
+ conditional_automake_snippet = _Property(
+ fget=lambda self: self.__get_option("conditional_automake_snippet"),
+ fset=lambda self, string: self.__set_option("conditional_automake_snippet", string),
+ check=lambda value: isinstance(value, str),
+ doc="configure.ac snippet",
+ )
+ automake_snippet = _Property(
+ fget=lambda self: "\n".join((self.conditional_automake_snippet, self.unconditional_automake_snippet)),
+ doc="full automake snippet (conditional + unconditional parts)",
+ )
+ include_directives = _StringListProperty(
+ fget=lambda self: self.__get_option("include_directives"),
+ fset=lambda self, string: self.__set_option("include_directives", string),
+ doc="include directive",
+ )
+ link_directives = _StringListProperty(
+ fget=lambda self: self.__get_option("link_directives"),
+ fset=lambda self, string: self.__set_option("link_directives", string),
+ doc="link directive",
+ )
+ licenses = _StringListProperty(
+ sorted=True,
+ unique=True,
+ fget=lambda self: self.__get_option("licenses"),
+ fset=lambda self, name: self.__set_option("licenses", name),
+ doc="acceptable licenses for modules",
+ )
+ maintainers = _StringListProperty(
+ sorted=False,
+ unique=True,
+ fget=lambda self: self.__get_option("maintainers"),
+ fset=lambda self, name: self.__set_option("maintainers", name),
+ doc="module maintainers list",
+ )
+ test = _Property(
+ fget=lambda self: self.__get_option("test"),
+ fset=lambda self, name: self.__set_option("test", name),
+ check=lambda value: isinstance(value, bool),
+ doc="module is a test?",
+ )
+
+
+ @_Property
def dependencies(self):
"""dependencies iterator (name, condition)"""
- entries = set()
- for line in self.__table["dependencies"]:
- line = line.replace("\t", " ").strip()
- index = line.find(" ")
- if index == -1:
- module = line
- condition = ""
- else:
- module = line[:index].strip()
- condition = line[index:].strip()
- if condition.startswith("["):
- condition = condition[1:]
- if condition.endswith("]"):
- condition = condition[:-1]
- if not condition:
- condition = None
- entries.add((module, condition))
- return tuple(entries)
+ return self.__options["dependencies"]
@dependencies.setter
def dependencies(self, value):
- _type_assert("files", value, _ITERABLES)
- result = set()
- for (name, condition) in value:
- _type_assert("name", name, str)
- _type_assert("condition", condition, str)
- result.add((name, condition))
- self.__table["dependencies"] = tuple(result)
-
-
- @property
- def early_autoconf_snippet(self):
- """early configure.ac snippet"""
- return self.__table["early_autoconf_snippet"]
-
- @early_autoconf_snippet.setter
- def early_autoconf_snippet(self, value):
- _type_assert("early_autoconf_snippet", value, str)
- self.__table["early_autoconf_snippet"] = value
-
-
- @property
- def autoconf_snippet(self):
- """configure.ac snippet"""
- return self.__table["autoconf_snippet"]
-
- @autoconf_snippet.setter
- def autoconf_snippet(self, value):
- _type_assert("autoconf_snippet", value, str)
- self.__table["autoconf_snippet"] = value
-
-
- @property
- def conditional_automake_snippet(self):
- """Makefile.am snippet that can be put inside Automake conditionals"""
- return self.__table["conditional_automake_snippet"]
-
- @conditional_automake_snippet.setter
- def conditional_automake_snippet(self, value):
- _type_assert("conditional_automake_snippet", value, str)
- self.__table["conditional_automake_snippet"] = value
-
-
- @property
+ result = []
+ types = (list, tuple, set, frozenset, type({}.keys()), type({}.values()))
+ if not isinstance(value, types):
+ raise TypeError("value: iterable expected")
+ for item in value:
+ if not isinstance(value, (list, tuple)):
+ raise TypeError("item: pair expected")
+ (module, condition) = item
+ if not isinstance(module, str):
+ raise TypeError("module: str expected")
+ if condition is not None and not isinstance(condition, str):
+ raise TypeError("condition: str or None expected")
+ result.append((module, condition))
+ self.__options["dependencies"] = tuple(result)
+
+
+ @_Property
def unconditional_automake_snippet(self):
"""Makefile.am snippet that must stay outside of Automake conditionals"""
result = ""
return result
- @property
- def automake_snippet(self):
- """full automake snippet (conditional + unconditional parts)"""
- return "\n".join((self.conditional_automake_snippet, self.unconditional_automake_snippet))
-
-
- @property
- def include_directive(self):
- """include directive"""
- value = self.__table["include_directive"]
- if value.startswith("<") or value.startswith("\""):
- return "#include {0}".format(value)
- return self.__table["include_directive"]
-
- @include_directive.setter
- def include_directive(self, value):
- _type_assert("include_directive", value, str)
- self.__table["include_directive"] = value
-
-
- @property
- def link_directive(self):
- """link directive"""
- return self.__table["link_directive"]
-
- @link_directive.setter
- def link_directive(self, value):
- _type_assert("link_directive", value, str)
- self.__table["link_directive"] = value
-
-
- @property
- def licenses(self):
- """licenses set"""
- return self.__table["licenses"]
-
- @licenses.setter
- def licenses(self, value):
- _type_assert("licenses", value, _ITERABLES)
- result = set()
- for item in value:
- _type_assert("license", item, str)
- result.add(value)
- self.__table["licenses"] = tuple(result)
-
-
- @property
- def maintainers(self):
- """maintainers"""
- return self.__table["maintainers"]
-
- @maintainers.setter
- def maintainers(self, value):
- _type_assert("maintainers", value, _ITERABLES)
- result = set()
- for item in value:
- _type_assert("maintainer", item, str)
- result.add(item)
- self.__table["maintainers"] = tuple(result)
-
-
- @property
- def test(self):
- """module is tests-related?"""
- return self.__table["test"]
-
- @test.setter
- def test(self, value):
- _type_assert("test", value, bool)
- self.__table["test"] = value
-
-
def shell_variable(self, macro_prefix="gl"):
"""Get the name of the shell variable set to true once m4 macros have been executed."""
module = self.name
def items(self):
"""a set-like object providing a view on module items"""
- for key in BaseModule._PROPERTIES:
+ for key in BaseModule.__PROPERTIES:
yield (key, self[key])
+ @classmethod
def keys(self):
"""a set-like object providing a view on module keys"""
- for (key, _) in self.items():
+ for key in BaseModule.__PROPERTIES:
yield key
def values(self):
"""a set-like object providing a view on module values"""
- for (_, value) in self.items():
- yield value
+ for key in BaseModule.__PROPERTIES:
+ yield self[key]
def __lt__(self, value):
- _type_assert("value", value, BaseModule)
- return self.name < value.name
+ if value is not None:
+ return self.name < value.name
+ return False
def __le__(self, value):
return self.__lt__(value) or self.__eq__(value)
def __eq__(self, value):
- _type_assert("value", value, BaseModule)
- if self.name != value.name:
- return False
- for key in BaseModule._PROPERTIES:
- if self[key] != value[key]:
+ if value is not None:
+ if self.name != value.name:
return False
- return True
+ for key in BaseModule.__PROPERTIES:
+ if self[key] != value[key]:
+ return False
+ return True
+ return False
def __ne__(self, value):
return not self.__eq__(value)
+class FileModule(BaseModule):
+ """text-based module"""
+ __slots__ = ("__path")
+
+
+ __DEPENDENCY = _re.compile(r"(\S+)(?:\s+(\[.*?\]))?$", _re.M)
+ __STRING = lambda text: text.strip()
+ __MULTILINE = lambda text: tuple(filter(
+ lambda line: line.strip() and not line.strip().startswith("#"),
+ [line.strip() for line in text.strip().splitlines()],
+ ))
+ __INCLUDE_DIRECTIVES = lambda text: tuple(filter(
+ lambda line: line.strip(),
+ [line.strip() for line in text.strip().splitlines()],
+ ))
+ __DEPENDENCIES = lambda text: FileModule.__DEPENDENCY.findall("\n".join(FileModule.__MULTILINE(text)))
+ __MAINTAINERS = lambda text: tuple(filter(
+ lambda line: line.strip() and not line.strip().startswith("#"),
+ {line.strip() for line in text.split((",", "\n")["\n" in text.strip()])},
+ ))
+ __TABLE = {
+ "Description": (
+ "description",
+ __STRING,
+ ),
+ "Comment": (
+ "comment",
+ __STRING,
+ ),
+ "Status": (
+ "status",
+ __MULTILINE,
+ ),
+ "Notice": (
+ "notice",
+ __STRING,
+ ),
+ "Applicability": (
+ "applicability",
+ __STRING,
+ ),
+ "Files": (
+ "files",
+ __MULTILINE,
+ ),
+ "Depends-on": (
+ "dependencies",
+ __DEPENDENCIES,
+ ),
+ "configure.ac-early": (
+ "early_autoconf_snippet",
+ __STRING,
+ ),
+ "configure.ac": (
+ "autoconf_snippet",
+ __STRING,
+ ),
+ "Makefile.am": (
+ "conditional_automake_snippet",
+ __STRING,
+ ),
+ "Include": (
+ "include_directives",
+ __INCLUDE_DIRECTIVES,
+ ),
+ "Link": (
+ "link_directives",
+ __MULTILINE,
+ ),
+ "License": (
+ "licenses",
+ __MULTILINE,
+ ),
+ "Maintainer": (
+ "maintainers",
+ __MAINTAINERS,
+ ),
+ }
+ __PATTERN = _re.compile("({}):".format("|".join(__TABLE)))
+
+
+ path = _Property(
+ fget=lambda self: self.__path,
+ doc="module file path",
+ )
+
+
+ def __init__(self, path, name, **kwargs):
+ if not isinstance(path, str):
+ raise TypeError("path: str expected")
+ if not isinstance(name, str):
+ raise TypeError("name: str expected")
+ with _codecs.open(path, "rb", "UTF-8") as stream:
+ match = FileModule.__PATTERN.split(stream.read())[1:]
+ for (group, text) in zip(match[::2], match[1::2]):
+ (key, hook) = FileModule.__TABLE[group]
+ kwargs.setdefault(key, hook(text))
+ super().__init__(name=name, **kwargs)
+ self.__path = path
+
+
+
class _DummyModuleMeta(type):
__INSTANCE = None
__PROPERTIES = {
"dependencies": tuple(),
"early_autoconf_snippet": "",
"autoconf_snippet": "",
- "include_directive": "",
- "link_directive": "",
+ "include_directives": "",
+ "link_directives": "",
"licenses": tuple({"public domain"}),
"maintainers": tuple({"all"}),
"automake_snippet": "lib_SOURCES += dummy.c",
"unconditional_automake_snippet": "",
}
+
def __new__(mcs, name, parents, attributes):
- cls = super().__new__(mcs, name, parents, attributes)
for (key, value) in _DummyModuleMeta.__PROPERTIES.items():
- setattr(cls, key, property(lambda cls, value=value: value))
- return cls
+ fget=lambda self, value=value: value
+ doc = getattr(BaseModule, key).__doc__
+ attributes[key] = _Property(fget=fget, doc=doc)
+ return super().__new__(mcs, name, parents, attributes)
+
def __call__(cls, *args, **kwargs):
if _DummyModuleMeta.__INSTANCE is None:
- _DummyModuleMeta.__INSTANCE = super(_DummyModuleMeta, cls).__call__(*args, **kwargs)
+ _DummyModuleMeta.__INSTANCE = super().__call__(*args, **kwargs)
return _DummyModuleMeta.__INSTANCE
-class FileModule(BaseModule):
- """text-based module"""
- _TABLE = {_value[2]:(_value[1], _key) for (_key, _value) in BaseModule._TABLE.items()}
- _FIELDS = [_field for (_, _, _field) in BaseModule._TABLE.values()]
- _PATTERN = _re.compile("({}):".format("|".join(_FIELDS)))
+class _GnulibModuleMeta(type):
+ def __new__(mcs, name, parents, attributes):
+ for key in BaseModule.keys():
+ if key in attributes:
+ continue
+ fget = lambda self, key=key: self.__getitem__(key)
+ doc = getattr(BaseModule, key).__doc__
+ attributes[key] = _Property(fget=fget, doc=doc)
+ return super().__new__(mcs, name, parents, attributes)
- def __init__(self, path, name=None, **kwargs):
- _type_assert("path", path, str)
- _type_assert("name", name, str)
- self.__path = path
- with _codecs.open(path, "rb", "UTF-8") as stream:
- match = FileModule._PATTERN.split(stream.read())[1:]
- table = {}
- for (group, value) in zip(match[::2], match[1::2]):
- (typeid, key) = FileModule._TABLE[group]
- if typeid in _ITERABLES:
- lines = []
- for line in value.splitlines():
- if line.strip() and not line.startswith("#"):
- lines.append(line)
- table[key] = typeid(lines)
- else:
- table[key] = value.strip()
- for (key, value) in kwargs.items():
- table[key] = value
- super().__init__(name, **table)
+class GnulibModule(FileModule, metaclass=_GnulibModuleMeta):
+ """read-only gnulib standard module"""
+ __slots__ = ("__cache", "__hash", "__mask", "__path", "__test")
+ __OBSOLETE = (1 << 0)
+ __CXX_TEST = (1 << 1)
+ __LONGRUNNING_TEST = (1 << 2)
+ __PRIVILEGED_TEST = (1 << 3)
+ __UNPORTABLE_TEST = (1 << 4)
+
-class GnulibModule(FileModule):
- """read-only gnulib standard module"""
def __init__(self, path, name):
- super().__init__(path=path, name=name)
+ super(FileModule, self).__init__(name=name)
+ try:
+ module = FileModule(path=path, name=name, test=name.endswith("-tests"))
+ except FileNotFoundError:
+ raise _UnknownModuleError(name)
+ self.__cache = {_sys.intern(k):v for (k,v) in module.items()}
self.__hash = super().__hash__()
+ self.__mask = module.mask
+ self.__path = module.path
+ self.__test = module.test
- def __setattr__(self, key, value):
- if key in BaseModule._PROPERTIES:
- raise AttributeError("can't set property")
- super().__setattr__(key, value)
+ def __repr__(self):
+ module = self.__class__.__module__
+ name = self.__class__.__name__
+ return f"{module}.{name}{{{self.name}}}"
+
+
+ def __getitem__(self, key):
+ return self.__cache[key]
def __hash__(self):
def __eq__(self, other):
if isinstance(other, GnulibModule):
- return hash(self) == hash(other)
+ return self.__hash == hash(other)
return super().__eq__(other)
- @property
+ obsolete = _Property(
+ fget=lambda self: bool(self.__mask & GnulibModule.__OBSOLETE),
+ doc="module is obsolete?",
+ )
+ cxx_test = _Property(
+ fget=lambda self: bool(self.__mask & GnulibModule.__CXX_TEST),
+ doc="module is a C++ test?",
+ )
+ longrunning_test = _Property(
+ fget=lambda self: bool(self.__mask & GnulibModule.__LONGRUNNING_TEST),
+ doc="module is a longrunning test?",
+ )
+ privileged_test = _Property(
+ fget=lambda self: bool(self.__mask & GnulibModule.__PRIVILEGED_TEST),
+ doc="module is a privileged test?",
+ )
+ unportable_test = _Property(
+ fget=lambda self: bool(self.__mask & GnulibModule.__UNPORTABLE_TEST),
+ doc="module is an unportable test?",
+ )
+ mask = _Property(
+ fget=lambda self: self.__mask,
+ doc="module acceptibility mask",
+ )
+ test = _Property(
+ fget=lambda self: self.name.endswith("-tests"),
+ doc="module is tests-related?",
+ )
+ path = _Property(
+ fget=lambda self: self.__path,
+ doc="module file path",
+ )
+
+
+ @_Property
def applicability(self):
- """applicability (usually "main" or "tests")"""
+ """WAGH applicability (usually "main" or "tests")"""
default = "tests" if self.test else "main"
- result = super().applicability
- return result if result else default
-
-
- @property
- def test(self):
- """module is tests-related?"""
- return self.name.endswith("-tests")
+ current = self.__cache["applicability"]
+ return current if current else default
class TransitiveClosure:
"""transitive closure table"""
- def __init__(self, lookup, config, tests):
+ def __init__(self, lookup, modules, mask, gnumake, tests=False):
if not callable(lookup):
raise TypeError("lookup: callable expected")
- _type_assert("config", config, _BaseConfig)
- _type_assert("tests", tests, bool)
+ demanders = _collections.defaultdict(dict)
+ dependencies = _collections.defaultdict(dict)
def _exclude(module):
- return any((
- (not config.obsolete and module.obsolete),
- (not config.cxx_tests and module.cxx_test),
- (not config.longrunning_tests and module.longrunning_test),
- (not config.privileged_tests and module.privileged_test),
- (not config.unportable_tests and module.unportable_test),
- ))
+ return mask != module.mask
def _lookup(module):
if not (module is None or isinstance(module, BaseModule)):
dependencies[dependency][demander] = condition
current.add(dependency)
+ testdb = {}
+ mapping = {}
current = set()
previous = set()
- demanders = _collections.defaultdict(dict)
- dependencies = _collections.defaultdict(dict)
- for module in config.modules:
+ for module in modules:
dependency = lookup(module)
_update(None, dependency, None)
while current != previous:
previous.update(current)
for demander in previous:
- if tests and not demander.test:
+ if tests and not demander.test and testdb.get(demander.name, None) is None:
try:
- dependency = lookup("{}-tests".format(demander.name))
+ name = (demander.name + "-tests")
+ path = (demander.path + "-tests")
+ dependency = GnulibModule(name=name, path=path)
if not _exclude(dependency):
- _update(None, dependency, None)
+ _update(demander, dependency, None)
+ testdb[demander.name] = True
+ else:
+ testdb[demander.name] = False
except _UnknownModuleError:
- pass # ignore non-existent tests
+ testdb[demander.name] = False
for (dependency, condition) in demander.dependencies:
dependency = lookup(dependency)
- if config.gnumake and condition and condition.startswith("if "):
+ if gnumake and condition and condition.startswith("if "):
# A module whose Makefile.am snippet contains a reference to an
# automake conditional. If we were to use it conditionally, we
# would get an error
conditional = set()
unconditional = set()
for (dependency, demanders) in self.__dependencies.items():
- for (demander, _) in demanders.items():
+ for demander in demanders:
if demander is None:
- unconditional.add(demander)
+ unconditional.add(dependency)
break
+
previous = set()
current = set(unconditional)
while previous != current:
demanders = _collections.defaultdict(dict)
dependencies = _collections.defaultdict(dict)
collection = _ast.literal_eval(string)
- _type_assert("collection", collection, dict)
for key in collection:
- _type_assert("key", key, str)
value = collection[key]
- _type_assert("value", value, dict)
for (subkey, subvalue) in value.items():
- _type_assert("key", key, str)
- _type_assert("dict", value, dict)
(dependency, demander, condition) = (key, subkey, subvalue)
dependency = self.__lookup(dependency)
demander = self.__lookup(demander)
def __init__(self, lookup, config):
if not callable(lookup):
raise TypeError("lookup: callable expected")
- _type_assert("config", config, _BaseConfig)
+
+ mask = config.mask
+ gnumake = config.gnumake
+ lookup = lambda module, lookup=lookup: module if isinstance(module, BaseModule) else lookup(module)
def _applicability(module):
return module.applicability in ({"main", "all"}, {"main"})[config.tests]
return True
return False
- base_closure = TransitiveClosure(lookup, config, False)
- full_closure = TransitiveClosure(lookup, config, True)
+ # Perform a transitive closure for modules from the configuration.
+ # The result of this transitive closure is a set of main modules.
+ explicit_modules = {lookup(module) for module in config.modules}
+ base_closure = TransitiveClosure(lookup, explicit_modules, mask, gnumake)
+ full_closure = TransitiveClosure(lookup, set(base_closure), mask, gnumake, True)
+
+ # Once the full transitive closure is completed, populate the database.
main_modules = set(base_closure)
- explicit_modules = set()
- for module in full_closure:
- if module.name in config.modules:
- explicit_modules.add(module)
final_modules = set(full_closure) if config.tests else main_modules
- test_modules = (final_modules - set(filter(_applicability, main_modules)))
+ test_modules = (final_modules - set(filter(_applicability, sorted(main_modules))))
libtests = _libtests(test_modules)
if _dummy(main_modules):
main_modules.add(DummyModule())
if _dummy(test_modules) and libtests:
test_modules.add(DummyModule())
main_files = _files(main_modules)
- test_files = set()
- for file in _files(test_modules):
- if file.startswith("lib/"):
- file = ("tests=lib/" + file[len("lib/"):])
- test_files.add(file)
+ test_files = _files(test_modules)
self.__libtests = libtests
self.__closure = full_closure
self.__test_files = tuple(sorted(test_files))
+ def __iter__(self):
+ def _iter():
+ for dependency in self.__closure:
+ for (demander, condition) in self.__closure.demanders(dependency):
+ yield (dependency, demander, condition)
+ return iter(sorted(_iter()))
+
+
def conditional(self, module):
"""
Test whether module is a conditional dependency.
def demanders(self, module):
"""For each demander which requires the module yield the demander and the corresponding condition."""
- return self.__closure.demanders(module)
+ return sorted(self.__closure.demanders(module))
def dependencies(self, module):
"""For each dependency of the module yield this dependency and the corresponding condition."""
- return self.__closure.dependencies(module)
+ return sorted(self.__closure.dependencies(module))
@property
"specify the library name; defaults to 'libgnu'",
),
"action": _Option,
+ "dest": "libname",
"metavar": "LIBRARY",
}),
(["--source-base"], {
fmt = "--{0}: expected at least one argument"
self.__parser.error(fmt.format(mode))
verbosity = namespace.pop("verbosity", 0)
+ namespace.pop("no_changelog", None)
+
options = dict(namespace)
+ namespace.setdefault("root", ".")
options.setdefault("dry_run", False)
namespace["overrides"] = list(reversed(namespace.get("overrides", [])))
return (namespace, mode, verbosity, options)
-import os as _os
import subprocess as _sp
-from .error import type_assert as _type_assert
-
-
class _PipeMeta(type):
__INSTANCE = None
class Executable:
"""command-line program or script"""
def __init__(self, name, path=None, encoding=None):
- _type_assert("name", name, str)
- if path is not None:
- _type_assert("path", path, str)
- if encoding is not None:
- _type_assert("encoding", encoding, str)
self.__name = name
self.__path = path
self.__encoding = encoding
import subprocess as _sp
-from .error import type_assert as _type_assert
from .error import UnknownModuleError as _UnknownModuleError
from .module import DummyModule as _DummyModule
from .module import GnulibModule as _GnulibModule
class BaseVFS:
"""gnulib generic virtual file system"""
- def __init__(self, prefix, **table):
- _type_assert("prefix", prefix, str)
+ __slots__ = ("__origin", "__root", "__table")
+
+
+ def __init__(self, origin, **table):
self.__table = {}
for (key, value) in table.items():
- _type_assert(key, value, str)
self.__table[key] = _os.path.normpath(value)
- self.__prefix = prefix
+ self.__origin = _os.path.normpath(origin)
+ self.__root = _os.path.abspath(origin)
def __repr__(self):
module = self.__class__.__module__
name = self.__class__.__name__
- return "{}.{}{{{}}}".format(module, name, repr(self.__prefix))
+ return "{}.{}{{{}}}".format(module, name, repr(self.__origin))
def __enter__(self):
def __contains__(self, name):
- path = _os.path.normpath(name)
if _os.path.isabs(name):
raise ValueError("name must be a relative path")
- path = _os.path.join(self.absolute, self[name])
+ path = _os.path.join(self.__root, self[name])
return _os.path.exists(path)
def __getitem__(self, name):
- _type_assert("name", name, str)
parts = []
replaced = False
name = _os.path.normpath(name)
part = self.__table[part]
replaced = True
parts += [part]
- name = _os.path.sep.join(parts)
- return _os.path.normpath(name)
+ return _os.path.sep.join(parts)
def __setitem__(self, src, dst):
for name in (src, dst):
- _type_assert("name", name, str)
if _os.path.isabs(name):
raise ValueError("name cannot be an absoule path")
src = _os.path.normpath(src)
@property
- def relative(self):
- """BaseVFS VFS name"""
- return self.__prefix
+ def origin(self):
+ """origin VFS path"""
+ return self.__origin
@property
- def absolute(self):
+ def root(self):
"""absolute VFS path"""
- return _os.path.abspath(self.__prefix)
+ return self.__root
NOTE: It is up to the caller to unlink files obtained after dynamic patching.
"""
- _type_assert("primary", primary, BaseVFS)
- _type_assert("secondary", secondary, BaseVFS)
if name in secondary:
return (secondary, name)
diff = "{}.diff".format(name)
def mkdir(root, name):
"""Create a leaf directory and all intermediate ones recursively."""
root = BaseVFS(".") if root is None else root
- path = name if _os.path.isabs(name) else _os.path.join(root.absolute, root[name])
+ path = name if _os.path.isabs(name) else _os.path.join(root.root, root[name])
_os.makedirs(root[name], exist_ok=True)
def backup(root, name):
"""Backup the given file."""
root = BaseVFS(".") if root is None else root
- original_path = _os.path.join(root.absolute, root[name])
+ original_path = _os.path.join(root.root, root[name])
backup_path = "{}~".format(original_path)
try:
_os.unlink(backup_path)
rhs_root = BaseVFS(".") if rhs_root is None else rhs_root
(lhs_path, rhs_path) = (lhs_name, rhs_name)
if not _os.path.isabs(lhs_name):
- lhs_path = _os.path.join(lhs_root.absolute, lhs_root[lhs_name])
+ lhs_path = _os.path.join(lhs_root.root, lhs_root[lhs_name])
if not _os.path.isabs(rhs_name):
- rhs_path = _os.path.join(rhs_root.absolute, rhs_root[rhs_name])
+ rhs_path = _os.path.join(rhs_root.root, rhs_root[rhs_name])
return _filecmp.cmp(lhs_path, rhs_path, shallow=False)
mkdir(dst_root, _os.path.dirname(dst_name))
(src_path, dst_path) = (src_name, dst_name)
if not _os.path.isabs(src_name):
- src_path = _os.path.join(src_root.absolute, src_root[src_name])
+ src_path = _os.path.join(src_root.root, src_root[src_name])
if not _os.path.isabs(dst_name):
- dst_path = _os.path.join(dst_root.absolute, dst_root[dst_name])
+ dst_path = _os.path.join(dst_root.root, dst_root[dst_name])
with _codecs.open(src_path, "rb") as istream:
with _codecs.open(dst_path, "wb") as ostream:
while 1:
def exists(root, name):
"""Check whether the given file exists."""
root = BaseVFS(".") if root is None else root
- path = name if _os.path.isabs(name) else _os.path.join(root.absolute, root[name])
+ path = name if _os.path.isabs(name) else _os.path.join(root.root, root[name])
return _os.path.exists(path)
mkdir(dst_root, _os.path.dirname(dst_name))
(src_path, dst_path) = (src_name, dst_name)
if not _os.path.isabs(src_name):
- src_path = _os.path.join(src_root.absolute, src_root[src_name])
+ src_path = _os.path.join(src_root.root, src_root[src_name])
if not _os.path.isabs(dst_name):
- dst_path = _os.path.join(dst_root.absolute, dst_root[dst_name])
+ dst_path = _os.path.join(dst_root.root, dst_root[dst_name])
_os.link(src_path, dst_path)
mkdir(dst_root, _os.path.dirname(dst_name))
(src_path, dst_path) = (src_name, dst_name)
if not _os.path.isabs(src_name):
- src_path = _os.path.join(src_root.absolute, src_root[src_name])
+ src_path = _os.path.join(src_root.root, src_root[src_name])
if not _os.path.isabs(dst_name):
- dst_path = _os.path.join(dst_root.absolute, dst_root[dst_name])
+ dst_path = _os.path.join(dst_root.root, dst_root[dst_name])
_os.rename(src_path, dst_path)
def iostream(root, name, mode="r", encoding=None):
"""Open file and return a stream. Raise IOError upon failure."""
root = BaseVFS(".") if root is None else root
- path = name if _os.path.isabs(name) else _os.path.join(root.absolute, root[name])
+ path = name if _os.path.isabs(name) else _os.path.join(root.root, root[name])
return _codecs.open(path, mode, encoding)
"""Obtain the path to which the symbolic link points."""
root = BaseVFS(".") if root is None else root
mkdir(root, _os.path.dirname(name))
- path = name if _os.path.isabs(name) else _os.path.join(root.absolute, root[name])
+ path = name if _os.path.isabs(name) else _os.path.join(root.root, root[name])
return _os.readlink(path)
if not relative:
(src_path, dst_path) = (src_name, dst_name)
if not _os.path.isabs(src_name):
- src_path = _os.path.join(src_root.absolute, src_root[src_name])
+ src_path = _os.path.join(src_root.root, src_root[src_name])
if not _os.path.isabs(dst_name):
- dst_path = _os.path.join(dst_root.absolute, dst_root[dst_name])
+ dst_path = _os.path.join(dst_root.root, dst_root[dst_name])
else:
- src_path = _os.path.join(src_root.relative, src_root[src_name])
- dst_path = _os.path.join(dst_root.relative, dst_root[dst_name])
+ src_path = _os.path.join(src_root.origin, src_root[src_name])
+ dst_path = _os.path.join(dst_root.origin, dst_root[dst_name])
prefix = _os.path.relpath(_os.path.dirname(src_path), _os.path.dirname(dst_path))
suffix = _os.path.basename(src_root[src_name])
src_path = _os.path.join(prefix, suffix)
- dst_path = _os.path.join(dst_root.absolute, dst_root[dst_name])
+ dst_path = _os.path.join(dst_root.root, dst_root[dst_name])
_os.symlink(src_path, dst_path)
"""Unlink a file, backing it up if necessary."""
root = BaseVFS(".") if root is None else root
mkdir(root, _os.path.dirname(name))
- path = name if _os.path.isabs(name) else _os.path.join(root.absolute, root[name])
+ path = name if _os.path.isabs(name) else _os.path.join(root.root, root[name])
_os.unlink(path)
class GnulibGitVFS(BaseVFS):
"""gnulib git repository"""
+ __slots__ = ("__cache", "__modules")
+
+
_EXCLUDE = {
"." : str.startswith,
"~" : str.endswith,
}
- def __init__(self, prefix, **table):
- super().__init__(prefix, **table)
+ def __init__(self, origin):
+ super().__init__(origin=origin)
self.__cache = {"dummy": _DummyModule()}
- if not _os.path.exists(self.absolute):
- raise FileNotFoundError(self.absolute)
- if not _os.path.isdir(self.absolute):
- raise NotADirectoryError(self.absolute)
- if not _os.path.isdir(_os.path.join(self.absolute, ".git")):
+ self.__modules = _os.path.join(self.root, "modules")
+ if not _os.path.exists(self.root):
+ raise FileNotFoundError(self.root)
+ if not _os.path.isdir(self.root):
+ raise NotADirectoryError(self.root)
+ if not _os.path.isdir(_os.path.join(self.root, ".git")):
raise TypeError("{} is not a gnulib repository".format(prefix))
def module(self, name):
"""instantiate a module"""
- _type_assert("name", name, str)
if name in self.__cache:
return self.__cache[name]
- path = _os.path.join(self.absolute, self["modules"], name)
+ path = _os.path.join(self.__modules, name)
try:
self.__cache[name] = _GnulibModule(path=path, name=name)
return self.__cache[name]
def modules(self):
"""iterate over all available modules"""
- prefix = _os.path.join(self.absolute, self["modules"])
- for root, _, files in _os.walk(prefix):
+ for root, _, files in _os.walk(self.__modules):
names = []
for name in files:
exclude = False
names += [name]
for name in names:
path = _os.path.join(root, name)
- name = path[len(prefix) + 1:]
+ name = path[len(self.__modules) + 1:]
yield self.module(name)