# HG changeset patch # User Dmitry Selyutin # Date 1517242062 -10800 # Node ID 915fae496f3c48803fcb33a77956bd6e510113d6 # Parent 9085df9174210db8fc327f1ae692d2dd9ed5397f refactored generators; bugfix; tests Makefile.am diff -r 9085df917421 -r 915fae496f3c pygnulib.py --- a/pygnulib.py Sun Jan 21 20:43:31 2018 +0300 +++ b/pygnulib.py Mon Jan 29 19:07:42 2018 +0300 @@ -12,23 +12,26 @@ import traceback import subprocess as sp + from pygnulib.error import CommandLineError from pygnulib.error import UnknownModuleError from pygnulib.config import BaseConfig from pygnulib.config import CachedConfig -from pygnulib.generator import CommandLineGenerator -from pygnulib.generator import GnulibCacheGenerator -from pygnulib.generator import LibMakefileGenerator -from pygnulib.generator import POMakevarsGenerator -from pygnulib.generator import GnulibCompGenerator +from pygnulib.generator import gnulib_cache +from pygnulib.generator import gnulib_comp +from pygnulib.generator import lib_makefile +from pygnulib.generator import po_make_vars +from pygnulib.generator import tests_makefile from pygnulib.module import DummyModule from pygnulib.module import Database from pygnulib.parser import CommandLine as CommandLineParser +from pygnulib.tools import Executable + from pygnulib.vfs import BaseVFS from pygnulib.vfs import GnulibGitVFS from pygnulib.vfs import backup as vfs_backup @@ -44,6 +47,23 @@ +class GnulibExecutable(Executable): + def __init__(self, name, encoding=None, shell_name=None, shell_path=None): + path = None + if shell_name is None: + shell_name = shell_name.upper() + if shell_path is None: + shell_path = "{}PATH".format(shell_name) + environ = dict(ENVIRON) + environ.update(os.environ) + if shell_name in environ: + path = shell_name + elif shell_path in environ: + path = "{}{}".format(shell_path) + super().__init__(name, path) + + + AC_VERSION_PATTERN = re.compile(r"AC_PREREQ\(\[(.*?)\]\)", re.S | re.M) IGNORED_LICENSES = { "GPLed build tool", @@ -56,7 +76,7 @@ "hardlink": vfs_hardlink, "symlink": vfs_symlink, } -SUBSTITUTION_RULES = { +SUBSTITUTION = { "build-aux": "auxdir", "doc": "doc_base", "lib": "source_base", @@ -96,7 +116,12 @@ def import_hook(script, gnulib, namespace, explicit, verbosity, options, *args, **kwargs): (_, _) = (args, kwargs) config = BaseConfig(**namespace) - cache = CachedConfig(root=config.root) + 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] @@ -165,16 +190,19 @@ table = {} overrides = [] - for (tbl_key, cfg_key) in SUBSTITUTION_RULES.items(): - table[tbl_key] = config[cfg_key] if cfg_key else "" 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_RULES.items(): + 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"])) @@ -271,7 +299,8 @@ # 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: - for line in LibMakefileGenerator(path, config, explicit, database, mkedits, False): + modules = database.main_modules + for line in lib_makefile(path, config, explicit, database, modules, mkedits, False): print(line, file=tmp) (src, dst) = (tmp.name, path) present = vfs_exists(project, dst) @@ -297,7 +326,7 @@ action(bool(match), vfs, src, project, dst, present) # Create po makefile parameterization, part 1. with tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False) as tmp: - for line in POMakevarsGenerator(config): + for line in po_make_vars(config): print(line, file=tmp) (src, dst) = (tmp.name, "po/Makevars") present = vfs_exists(project, dst) @@ -308,7 +337,7 @@ os.unlink(tmp.name) # Create po makefile parameterization, part 2. with tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False) as tmp: - for line in POMakevarsGenerator(config): + for line in po_make_vars(config): print(line, file=tmp) (src, dst) = (tmp.name, "po/POTFILESGenerator.in") present = vfs_exists(project, dst) @@ -353,7 +382,7 @@ # Create m4/gnulib-cache.m4. with tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False) as tmp: - for line in GnulibCacheGenerator(config): + for line in gnulib_cache(config): print(line, file=tmp) (src, dst) = (tmp.name, "m4/gnulib-cache.m4") present = vfs_exists(project, dst) @@ -365,7 +394,7 @@ # Create m4/gnulib-comp.m4. with tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False) as tmp: - for line in GnulibCompGenerator(config, explicit, database): + for line in gnulib_comp(config, explicit, database, True): print(line, file=tmp) (src, dst) = (tmp.name, "m4/gnulib-comp.m4") present = vfs_exists(project, dst) @@ -375,6 +404,21 @@ action(False, None, src, project, dst, present) os.unlink(tmp.name) + # Generate the contents of tests makefile. + 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): + print(line, file=tmp) + (src, dst) = (tmp.name, path) + present = vfs_exists(project, dst) + if present: + added_files.add(dst) + action = update_file if present else add_file + action(False, None, src, project, dst, present) + os.unlink(tmp.name) + return os.EX_OK @@ -420,6 +464,7 @@ 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) diff -r 9085df917421 -r 915fae496f3c pygnulib/config.py --- a/pygnulib/config.py Sun Jan 21 20:43:31 2018 +0300 +++ b/pygnulib/config.py Mon Jan 29 19:07:42 2018 +0300 @@ -41,6 +41,45 @@ +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 = { @@ -424,7 +463,7 @@ @property def obsolete(self): """include obsolete modules when they occur among the modules?""" - return bool(self.__options & BaseConfig._Option.Tests) + return bool(self.__options & BaseConfig._Option.Obsolete) @obsolete.setter def obsolete(self, value): @@ -586,17 +625,17 @@ def __getitem__(self, key): - if key not in BaseConfig._TABLE: + if key not in KEYS: key = key.replace("-", "_") - if key not in BaseConfig._TABLE: + 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 BaseConfig._TABLE: + if key not in KEYS: key = key.replace("_", "-") - if key not in BaseConfig._TABLE: + if key not in KEYS: raise KeyError("unsupported option: {0}".format(key)) return setattr(self, key, value) @@ -657,6 +696,7 @@ if ac_file is None: ac_file = "configure.ac" _type_assert("ac_file", ac_file, str) + ac_path = _os.path.join(root, ac_file) if not _os.path.exists(ac_path): ac_file = "configure.in" diff -r 9085df917421 -r 915fae496f3c pygnulib/generator.py --- a/pygnulib/generator.py Sun Jan 21 20:43:31 2018 +0300 +++ b/pygnulib/generator.py Mon Jan 29 19:07:42 2018 +0300 @@ -29,11 +29,7 @@ _LGPL_LICENSE: "yes", (_GPLv2_LICENSE | _LGPLv3_LICENSE): "3orGPLv2", } -_ITERABLES = frozenset((list, tuple, set, frozenset, type({}.keys()), type({}.values()))) - - - -_DISCLAIMER = ( +__DISCLAIMER = ( "## DO NOT EDIT! GENERATED AUTOMATICALLY!", "#", "# This file is free software; you can redistribute it and/or modify", @@ -56,1053 +52,1086 @@ "#", "# Generated by gnulib-tool.", ) - - - -class BaseGenerator: - """gnulib file content generator""" - def __repr__(self): - module = self.__class__.__module__ - name = self.__class__.__name__ - return "{0}.{1}".format(module, name) - - - def __str__(self): - return "\n".join([_ for _ in self]) - - - def __enter__(self): - return self - - - def __exit__(self, exctype, excval, exctrace): - pass - - - def __iter__(self): - yield - - - -class POMakevarsGenerator(BaseGenerator): - """PO Makefile parameterization""" - _TEMPLATE = ( - "# These options get passed to xgettext.", - "XGETTEXT_OPTIONS = \\", - " --keyword=_ --flag=_:1:pass-c-format \\", - " --keyword=N_ --flag=N_:1:pass-c-format \\", - " --keyword='proper_name:1,\"This is a proper name." # comma omitted - " See the gettext manual, section Names.\"' \\", - " --keyword='proper_name_utf8:1,\"This is a proper name." # comma omitted - " See the gettext manual, section Names.\"' \\", - " --flag=error:3:c-format --flag=error_at_line:5:c-format", - "", - "# This is the copyright holder that gets inserted into the header of the", - "# $(DOMAIN).pot file. gnulib is copyrighted by the FSF.", - "COPYRIGHT_HOLDER = Free Software Foundation, Inc.", - "", - "# This is the email address or URL to which the translators shall report", - "# bugs in the untranslated strings:", - "# - Strings which are not entire sentences, see the maintainer guidelines", - "# in the GNU gettext documentation, section 'Preparing Strings'.", - "# - Strings which use unclear terms or require additional context to be", - "# understood.", - "# - Strings which make invalid assumptions about notation of date, time or", - "# money.", - "# - Pluralisation problems.", - "# - Incorrect English spelling.", - "# - Incorrect formatting.", - "# It can be your email address, or a mailing list address where translators", - "# can write to without being subscribed, or the URL of a web page through", - "# which the translators can contact you.", - "MSGID_BUGS_ADDRESS = bug-gnulib@gnu.org", - "", - "# This is the list of locale categories, beyond LC_MESSAGES, for which the", - "# message catalogs shall be used. It is usually empty.", - "EXTRA_LOCALE_CATEGORIES =", - "", - "# This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt'", - "# context. Possible values are \"yes\" and \"no\". Set this to yes if the", - "# package uses functions taking also a message context, like pgettext(), or", - "# if in $(XGETTEXT_OPTIONS) you define keywords with a context argument.", - "USE_MSGCTXT = no" - ) - - - def __init__(self, config): - _type_assert("config", config, _BaseConfig) - super().__init__() - self.__config = config - - - @property - def po_base(self): - """directory relative to ROOT where *.po files are placed; defaults to 'po'""" - return self.__config.po_base - - - @property - def po_domain(self): - """the prefix of the i18n domain""" - return self.__config.po_domain - - - def __repr__(self): - module = self.__class__.__module__ - name = self.__class__.__name__ - fmt = "{}.{}{po_base={}, po_domain={}}" - return fmt.format(module, name, repr(self.__config.po_base), repr(self.__config.po_domain)) - - - def __iter__(self): - for line in _DISCLAIMER: - yield line - yield "# Usually the message domain is the same as the package name." - yield "# But here it has a '-gnulib' suffix." - yield "DOMAIN = {}-gnulib".format(self.po_domain) - yield "" - yield "# These two variables depend on the location of this directory." - yield "subdir = {}".format(self.po_domain) - yield "top_subdir = {}".format("/".join(".." for _ in self.po_base.split(_os.path.sep))) - for line in POMakevarsGenerator._TEMPLATE: - yield line +_ITERABLES = frozenset((list, tuple, set, frozenset, type({}.keys()), type({}.values()))) -class POTFILESGenerator(BaseGenerator): - """file list to be passed to xgettext""" - def __init__(self, config, files): - _type_assert("config", config, _BaseConfig) - super().__init__() - self.__config = config - self.__files = tuple(files) - - - @property - def files(self): - """list of files""" - return tuple(self.files) +__PO_MAKE_VARS = ( + "# These options get passed to xgettext.", + "XGETTEXT_OPTIONS = \\", + " --keyword=_ --flag=_:1:pass-c-format \\", + " --keyword=N_ --flag=N_:1:pass-c-format \\", + " --keyword='proper_name:1,\"This is a proper name. See the gettext manual, section Names.\"' \\", + " --keyword='proper_name_utf8:1,\"This is a proper name. See the gettext manual, section Names.\"' \\", + " --flag=error:3:c-format --flag=error_at_line:5:c-format", + "", + "# This is the copyright holder that gets inserted into the header of the", + "# $(DOMAIN).pot file. gnulib is copyrighted by the FSF.", + "COPYRIGHT_HOLDER = Free Software Foundation, Inc.", + "", + "# This is the email address or URL to which the translators shall report", + "# bugs in the untranslated strings:", + "# - Strings which are not entire sentences, see the maintainer guidelines", + "# in the GNU gettext documentation, section 'Preparing Strings'.", + "# - Strings which use unclear terms or require additional context to be", + "# understood.", + "# - Strings which make invalid assumptions about notation of date, time or", + "# money.", + "# - Pluralisation problems.", + "# - Incorrect English spelling.", + "# - Incorrect formatting.", + "# It can be your email address, or a mailing list address where translators", + "# can write to without being subscribed, or the URL of a web page through", + "# which the translators can contact you.", + "MSGID_BUGS_ADDRESS = bug-gnulib@gnu.org", + "", + "# This is the list of locale categories, beyond LC_MESSAGES, for which the", + "# message catalogs shall be used. It is usually empty.", + "EXTRA_LOCALE_CATEGORIES =", + "", + "# This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt'", + "# context. Possible values are \"yes\" and \"no\". Set this to yes if the", + "# package uses functions taking also a message context, like pgettext(), or", + "# if in $(XGETTEXT_OPTIONS) you define keywords with a context argument.", + "USE_MSGCTXT = no" +) - - def __repr__(self): - module = self.__class__.__module__ - name = self.__class__.__name__ - fmt = "{}.{}{files={}}" - return fmt.format(module, name, repr(self.__files)) +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 __iter__(self): - for line in _DISCLAIMER: - yield line - yield "# List of files which contain translatable strings." - for file in [_ for _ in self.files if _.startswith("lib/")]: - yield _os.path.join(self.__config.source_base, file[4:]) + po_base = config.po_base + po_domain = comnfig.po_domain + for line in __DISCLAIMER: + yield line + yield "# Usually the message domain is the same as the package name." + yield "# But here it has a '-gnulib' suffix." + yield f"DOMAIN = {po_domain}-gnulib" + yield "" + yield "# These two variables depend on the location of this directory." + yield f"subdir = {po_base}" + top_builddir = "/".join(".." for _ in config.po_base.split(_os.path.sep)) + yield f"top_builddir = {top_builddir}" + for line in __PO_MAKE_VARS: + yield line -class AutoconfSnippetGenerator(BaseGenerator): - """autoconf snippet generator for standalone module""" - def __init__(self, config, module, toplevel, no_libtool, no_gettext): - """ - 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) - super().__init__() - self.__config = config - self.__module = module - self.__toplevel = toplevel - self.__no_libtool = no_libtool - self.__no_gettext = no_gettext +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 - - @property - def toplevel(self): - """top level indicator; subordinate use of pygnulib""" - return self.__toplevel - + source_base = config.source_base + for line in __DISCLAIMER: + yield line + yield "# List of files which contain translatable strings." + for file in filter(lambda file: file.startswith("lib/"), files): + yield _os.path.join(source_base, file[4:]) - @property - def libtool(self): - """libtool switch, disabling libtool configuration parameter""" - return self.__config.libtool and not self.__no_libtool - - @property - def gettext(self): - """gettext switch, disabling AM_GNU_GETTEXT invocations""" - return not self.__no_gettext - def __repr__(self): - flags = [] - module = self.__class__.__module__ - name = self.__class__.__name__ - if self.toplevel: - flags += ["toplevel"] - if self.libtool: - flags += ["libtool"] - if self.gettext: - flags += ["gettext"] - include_guard_prefix = self.__config.include_guard_prefix - flags = "|".join(flags) - fmt = "{}.{}{include_guard_prefix={}, flags={}}" - return fmt.format(module, name, repr(include_guard_prefix), flags) +def autoconf_snippet(config, module, toplevel, no_libtool, no_gettext, **override): + """ + Generate autoconf snippet for a standalone module. + gnulib configuration + gnulib module instance + make a subordinate use of gnulib if False + disable libtool (regardless of configuration) + 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) + config = _BaseConfig(**config) + for (key, value) in override.items(): + config[key] = value - def __iter__(self): - module = self.__module - if module.name not in ("gnumakefile", "maintainer-makefile") or self.toplevel: - snippet = module.autoconf_snippet - include_guard_prefix = self.__config.include_guard_prefix - snippet.replace(r"${gl_include_guard_prefix}", include_guard_prefix) - if not self.libtool: - table = ( - (r"$gl_cond_libtool", "false"), - ("gl_libdeps", "gltests_libdeps"), - ("gl_ltlibdeps", "gltests_ltlibdeps"), - ) - for (src, dst) in table: - snippet = snippet.replace(src, dst) - if not self.gettext: - src = "AM_GNU_GETTEXT([external])" - dst = "dnl you must add AM_GNU_GETTEXT([external]) or similar to configure.ac.'" + 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: + table = ( + (r"$gl_cond_libtool", "false"), + (r"gl_libdeps", "gltests_libdeps"), + (r"gl_ltlibdeps", "gltests_ltlibdeps"), + ) + for (src, dst) in table: snippet = snippet.replace(src, dst) - else: - # Don't indent AM_GNU_GETTEXT_VERSION line, as that confuses - # autopoint through at least GNU gettext version 0.18.2. - snippet = snippet.lstrip() - lines = (_ for _ in snippet.split("\n") if _) - for line in lines: - yield line - if module.name == "alloca" and self.libtool: - yield "changequote(,)dnl" - yield "LTALLOCA=`echo \"$ALLOCA\" | sed -e 's/\\.[^.]* /.lo /g;s/\\.[^.]*$/.lo/'`" - yield "changequote([, ])dnl" - yield "AC_SUBST([LTALLOCA])" + if not 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) + else: + # Don't indent AM_GNU_GETTEXT_VERSION line, as that confuses + # autopoint through at least GNU gettext version 0.18.2. + snippet = snippet.lstrip() + lines = filter(lambda line: line.strip(), snippet.split("\n")) + for line in lines: + yield line + if module.name == "alloca" and libtool: + yield "changequote(,)dnl" + yield "LTALLOCA=`echo \"$ALLOCA\" | sed -e 's/\\.[^.]* /.lo /g;s/\\.[^.]*$/.lo/'`" + yield "changequote([, ])dnl" + yield "AC_SUBST([LTALLOCA])" -class AutoconfMultisnippetGenerator(BaseGenerator): - """multi-module autoconf snippets generator""" - def __init__(self, config, database, modules, toplevel, no_libtool, no_gettext, macro_prefix=None): - _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) - if macro_prefix is None: - macro_prefix = config.macro_prefix - _type_assert("macro_prefix", macro_prefix, str) - super().__init__() - self.__config = config - self.__database = database - self.__modules = set() +def autoconf_snippet_sequence(config, database, modules, toplevel, no_libtool, no_gettext, **override): + """ + Generate an autoconf snippet for multiple modules. + + gnulib configuration + gnulib module instance + make a subordinate use of gnulib if False + disable libtool (regardless of configuration) + 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) + config = _BaseConfig(**config) + for (key, value) in override.items(): + config[key] = value + macro_prefix = config.macro_prefix + + arguments = { + "config": config, + "module": None, + "toplevel": toplevel, + "no_libtool": no_libtool, + "no_gettext": no_gettext, + } + if not config.conditionals: for module in modules: - _type_assert("module", module, _BaseModule) - self.__modules.add(module) - self.__modules = sorted(self.__modules) - self.__toplevel = toplevel - self.__no_libtool = no_libtool - self.__no_gettext = no_gettext - self.__macro_prefix = macro_prefix + arguments["module"] = module + for line in autoconf_snippet(**arguments): + yield f" {line}" + return + + conditional = set() + unconditional = set() + for dependency in modules: + if database.conditional(dependency): + conditional.add(dependency) + else: + unconditional.add(dependency) + conditional = sorted(conditional) + unconditional = sorted(unconditional) + + # Emit the autoconf code for the unconditional modules. + for module in unconditional: + arguments["module"] = module + for line in autoconf_snippet(**arguments): + yield f" {line}" + + # Initialize the shell variables indicating that the modules are enabled. + for module in conditional: + shellvar = module.shell_variable(macro_prefix) + yield f" {shellvar}=false" + + # Emit the autoconf code for the conditional modules, each in a separate + # function. This makes it possible to support cycles among conditional + # modules. + for demander in conditional: + arguments["module"] = demander + shellvar = demander.shell_variable(macro_prefix) + shellfunc = demander.shell_function(macro_prefix) + yield f" {shellfunc} ()" + yield f" {{" + yield f" if ! ${shellvar}; then" + for line in autoconf_snippet(**arguments): + yield f" {line}" + yield f" {shellvar}=true" + for (dependency, condition) in sorted(database.dependencies(demander)): + if database.conditional(dependency): + shellfunc = dependency.shell_function(macro_prefix) + if condition is not None: + yield f" if {condition}; then" + yield f" {shellfunc}" + yield f" fi" + else: + yield f" {shellfunc}" + yield f" fi" + yield f" }}" + + # Emit the dependencies from the unconditional to the conditional modules. + for demander in unconditional: + for (dependency, condition) in sorted(database.dependencies(demander)): + if dependency in modules and database.conditional(dependency): + condname = dependency.conditional_name(macro_prefix) + shellfunc = dependency.shell_function(macro_prefix) + if condition is not None: + yield f" if {condition}; then" + yield f" {shellfunc}" + yield f" fi" + continue + else: + yield f" {shellfunc}" + + # Define the Automake conditionals. + yield f" m4_pattern_allow([^{macro_prefix}_GNULIB_ENABLED_])" + for module in conditional: + condname = module.conditional_name(macro_prefix) + shellvar = module.shell_variable(macro_prefix) + yield f" AM_CONDITIONAL([{condname}], [${shellvar}])" - def __iter__(self): - config = self.__config - database = self.__database - modules = self.__modules - macro_prefix = self.__macro_prefix - - arguments = { - "config": config, - "module": None, - "toplevel": self.__toplevel, - "no_libtool": self.__no_libtool, - "no_gettext": self.__no_gettext, - } - if not config.conditionals: - for module in sorted(modules): - arguments["module"] = module - for line in AutoconfSnippetGenerator(**arguments): - yield " {}".format(line) - return - conditional = set() - unconditional = set() - for dependency in modules: - if database.conditional(dependency): - conditional.add(dependency) - else: - unconditional.add(dependency) - conditional = sorted(conditional) - unconditional = sorted(unconditional) +__INIT_MACRO_HEADER = ( + # Overriding AC_LIBOBJ and AC_REPLACE_FUNCS has the effect of storing + # platform-dependent object files in ${macro_prefix_arg}_LIBOBJS instead + # of LIBOBJS. The purpose is to allow several gnulib instantiations under + # a single configure.ac file. (AC_CONFIG_LIBOBJ_DIR does not allow this + # flexibility). + # Furthermore it avoids an automake error like this when a Makefile.am + # that uses pieces of gnulib also uses $(LIBOBJ): + # automatically discovered file `error.c' should not be explicitly + # mentioned. + " m4_pushdef([AC_LIBOBJ], m4_defn([{macro_prefix}_LIBOBJ]))", + " m4_pushdef([AC_REPLACE_FUNCS], m4_defn([{macro_prefix}_REPLACE_FUNCS]))", - # Emit the autoconf code for the unconditional modules. - for module in unconditional: - arguments["module"] = module - for line in AutoconfSnippetGenerator(**arguments): - yield " {}".format(line) - - # Initialize the shell variables indicating that the modules are enabled. - for module in conditional: - shellvar = module.shell_variable(macro_prefix) - yield " {}=false".format(shellvar) + # Overriding AC_LIBSOURCES has the same purpose of avoiding the automake + # error when a Makefile.am that uses pieces of gnulib also uses $(LIBOBJ): + # automatically discovered file `error.c' should not be explicitly + # mentioned + # We let automake know about the files to be distributed through the + # EXTRA_lib_SOURCES variable. + " m4_pushdef([AC_LIBSOURCES], m4_defn([{macro_prefix}_LIBSOURCES]))", - # Emit the autoconf code for the conditional modules, each in a separate - # function. This makes it possible to support cycles among conditional - # modules. - for demander in conditional: - shellvar = demander.shell_variable(macro_prefix) - shellfunc = demander.shell_function(macro_prefix) - yield " {} ()".format(shellfunc) - yield " {" - yield " if ! ${}; then".format(shellvar) - arguments["module"] = demander - for line in AutoconfSnippetGenerator(**arguments): - yield " {}".format(line) - yield " {}=true".format(shellvar) - for (dependency, condition) in sorted(database.dependencies(demander)): - if database.conditional(dependency): - shellfunc = dependency.shell_function(macro_prefix) - if condition is not None: - yield " if {}; then".format(condition) - yield " {}".format(shellfunc) - yield " fi" - else: - yield " {}".format(shellfunc) - yield " fi" - yield " }" + # Create data variables for checking the presence of files that are + # mentioned as AC_LIBSOURCES arguments. These are m4 variables, not shell + # variables, because we want the check to happen when the configure file is + # created, not when it is run. ${macro_prefix_arg}_LIBSOURCES_LIST is the + # list of files to check for. ${macro_prefix_arg}_LIBSOURCES_DIR is the + # subdirectory in which to expect them. + " m4_pushdef([{macro_prefix}_LIBSOURCES_LIST], [])", + " m4_pushdef([{macro_prefix}_LIBSOURCES_DIR], [])", + " gl_COMMON", +) - # Emit the dependencies from the unconditional to the conditional modules. - for demander in unconditional: - for (dependency, condition) in sorted(database.dependencies(demander)): - if dependency in modules and database.conditional(dependency): - condname = dependency.conditional_name(macro_prefix) - shellfunc = dependency.shell_function(macro_prefix) - if condition is not None: - yield " if {}; then".format(condition) - yield " {}".format(shellfunc) - yield " fi" - else: - yield " {}".format(shellfunc) +def init_macro_header(config, **override): + """ + Generate the first few statements of the gl_INIT macro. - # Define the Automake conditionals. - yield " m4_pattern_allow([^{}_GNULIB_ENABLED_])".format(macro_prefix) - for module in conditional: - condname = module.conditional_name(macro_prefix) - shellvar = module.shell_variable(macro_prefix) - yield " AM_CONDITIONAL([{}], [${}])".format(condname, shellvar) + [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 + macro_prefix = config.macro_prefix + + for line in __INIT_MACRO_HEADER: + yield line.format(**config) -class InitMacroGenerator(BaseGenerator): - """basic gl_INIT macro generator""" - def __init__(self, config, macro_prefix=None): - """ - config: gnulib configuration - macro_prefix: macro prefix; if None, consider configuration - """ - _type_assert("config", config, _BaseConfig) - if macro_prefix is None: - macro_prefix = config.macro_prefix - _type_assert("macro_prefix", macro_prefix, str) - super().__init__() - self.__macro_prefix = macro_prefix +__INIT_MACRO_FOOTER = ( + # Check the presence of files that are mentioned as AC_LIBSOURCES + # arguments. The check is performed only when autoconf is run from the + # directory where the configure.ac resides; if it is run from a different + # directory, the check is skipped. + " m4_ifval({macro_prefix}_LIBSOURCES_LIST, [", + " m4_syscmd([test ! -d ]m4_defn([{macro_prefix}_LIBSOURCES_DIR])[ ||", + " for gl_file in ]{macro_prefix}_LIBSOURCES_LIST[ ; do", + " if test ! -r ]m4_defn([{macro_prefix}_LIBSOURCES_DIR])[/$gl_file ; then", + " echo \"missing file ]m4_defn([{macro_prefix}_LIBSOURCES_DIR])[/$gl_file\" >&2", + " exit 1", + " fi", + " done])dnl", + " m4_if(m4_sysval, [0], [],", + " [AC_FATAL([expected source file, required through AC_LIBSOURCES, not found])])", + " ])", + " m4_popdef([{macro_prefix}_LIBSOURCES_DIR])", + " m4_popdef([{macro_prefix}_LIBSOURCES_LIST])", + " m4_popdef([AC_LIBSOURCES])", + " m4_popdef([AC_REPLACE_FUNCS])", + " m4_popdef([AC_LIBOBJ])", + " AC_CONFIG_COMMANDS_PRE([", + " {macro_prefix}_libobjs=", + " {macro_prefix}_ltlibobjs=", + " if test -n \"${macro_prefix}_LIBOBJS\"; then", + " # Remove the extension.", + " sed_drop_objext='s/\\.o$//;s/\\.obj$//'", + " for i in `for i in ${macro_prefix}_LIBOBJS; " # comma omitted + "do echo \"$i\"; done | sed -e \"$sed_drop_objext\" | sort | uniq`; do", + " {macro_prefix}_libobjs=\"${macro_prefix}_libobjs $i.$ac_objext\"", + " {macro_prefix}_ltlibobjs=\"${macro_prefix}_ltlibobjs $i.lo\"", + " done", + " fi", + " AC_SUBST([{macro_prefix}_LIBOBJS], [${macro_prefix}_libobjs])", + " AC_SUBST([{macro_prefix}_LTLIBOBJS], [${macro_prefix}_ltlibobjs])", + " ])", +) - - @property - def macro_prefix(self): - """the prefix of the macros 'gl_EARLY' and 'gl_INIT'""" - return self.__macro_prefix +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 = config.macro_prefix - - def __repr__(self): - module = self.__class__.__module__ - name = self.__class__.__name__ - fmt = "{}.{}{macro_prefix={}}" - return fmt.format(module, name, repr(self.__macro_prefix)) + for line in __INIT_MACRO_FOOTER: + yield line.format(**config) -class InitMacroHeaderGenerator(InitMacroGenerator): - """the first few statements of the gl_INIT macro""" - _TEMPLATE = ( - # Overriding AC_LIBOBJ and AC_REPLACE_FUNCS has the effect of storing - # platform-dependent object files in ${macro_prefix_arg}_LIBOBJS instead - # of LIBOBJS. The purpose is to allow several gnulib instantiations under - # a single configure.ac file. (AC_CONFIG_LIBOBJ_DIR does not allow this - # flexibility). - # Furthermore it avoids an automake error like this when a Makefile.am - # that uses pieces of gnulib also uses $(LIBOBJ): - # automatically discovered file `error.c' should not be explicitly - # mentioned. - " m4_pushdef([AC_LIBOBJ], m4_defn([{macro_prefix}_LIBOBJ]))", - " m4_pushdef([AC_REPLACE_FUNCS], m4_defn([{macro_prefix}_REPLACE_FUNCS]))", - - # Overriding AC_LIBSOURCES has the same purpose of avoiding the automake - # error when a Makefile.am that uses pieces of gnulib also uses $(LIBOBJ): - # automatically discovered file `error.c' should not be explicitly - # mentioned - # We let automake know about the files to be distributed through the - # EXTRA_lib_SOURCES variable. - " m4_pushdef([AC_LIBSOURCES], m4_defn([{macro_prefix}_LIBSOURCES]))", +__INIT_MACRO_DONE = ( + "", + "# Like AC_LIBOBJ, except that the module name goes", + "# into {macro_prefix}_LIBOBJS instead of into LIBOBJS.", + "AC_DEFUN([{macro_prefix}_LIBOBJ], [", + " AS_LITERAL_IF([$1], [{macro_prefix}_LIBSOURCES([$1.c])])dnl", + " {macro_prefix}_LIBOBJS=\"${macro_prefix}_LIBOBJS $1.$ac_objext\"", + "])", + "", + "# Like AC_REPLACE_FUNCS, except that the module name goes", + "# into {macro_prefix}_LIBOBJS instead of into LIBOBJS.", + "AC_DEFUN([{macro_prefix}_REPLACE_FUNCS], [", + " m4_foreach_w([gl_NAME], [$1], [AC_LIBSOURCES(gl_NAME[.c])])dnl", + " AC_CHECK_FUNCS([$1], , [{macro_prefix}_LIBOBJ($ac_func)])", + "])", + "", + "# Like AC_LIBSOURCES, except the directory where the source file is", + "# expected is derived from the gnulib-tool parameterization,", + "# and alloca is special cased (for the alloca-opt module).", + "# We could also entirely rely on EXTRA_lib..._SOURCES.", + "AC_DEFUN([{macro_prefix}_LIBSOURCES], [", + " m4_foreach([_gl_NAME], [$1], [", + " m4_if(_gl_NAME, [alloca.c], [], [", + " m4_define([{macro_prefix}_LIBSOURCES_DIR], [{source_base}])", + " m4_append([{macro_prefix}_LIBSOURCES_LIST], _gl_NAME, [ ])", + " ])", + " ])", + "])", +) - # Create data variables for checking the presence of files that are - # mentioned as AC_LIBSOURCES arguments. These are m4 variables, not shell - # variables, because we want the check to happen when the configure file is - # created, not when it is run. ${macro_prefix_arg}_LIBSOURCES_LIST is the - # list of files to check for. ${macro_prefix_arg}_LIBSOURCES_DIR is the - # subdirectory in which to expect them. - " m4_pushdef([{macro_prefix}_LIBSOURCES_LIST], [])", - " m4_pushdef([{macro_prefix}_LIBSOURCES_DIR], [])", - " gl_COMMON", - ) - +def init_macro_done(config, **override): + """ + Generate few statements after the gl_INIT macro. - def __init__(self, config, macro_prefix=None): - """ - config: gnulib configuration - macro_prefix: macro prefix; if None, consider configuration - """ - super().__init__(config=config, macro_prefix=macro_prefix) + [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 - - def __iter__(self): - macro_prefix = self.macro_prefix - for line in InitMacroHeaderGenerator._TEMPLATE: - yield line.format(macro_prefix=macro_prefix) + for line in __INIT_MACRO_DONE: + yield line.format(**config) -class InitMacroFooterGenerator(InitMacroGenerator): - """the last few statements of the gl_INIT macro""" - _TEMPLATE = ( - " m4_ifval({macro_prefix}_LIBSOURCES_LIST, [", - " m4_syscmd([test ! -d ]m4_defn([{macro_prefix}_LIBSOURCES_DIR])[ ||", - " for gl_file in ]{macro_prefix}_LIBSOURCES_LIST[ ; do", - " if test ! -r ]m4_defn([{macro_prefix}_LIBSOURCES_DIR])[/$gl_file ; then", - " echo \"missing file ]m4_defn([{macro_prefix}_LIBSOURCES_DIR])[/$gl_file\" >&2", - " exit 1", - " fi", - " done])dnl", - " m4_if(m4_sysval, [0], [],", - " [AC_FATAL([expected source file, required through AC_LIBSOURCES, not found])])", - " ])", - " m4_popdef([{macro_prefix}_LIBSOURCES_DIR])", - " m4_popdef([{macro_prefix}_LIBSOURCES_LIST])", - " m4_popdef([AC_LIBSOURCES])", - " m4_popdef([AC_REPLACE_FUNCS])", - " m4_popdef([AC_LIBOBJ])", - " AC_CONFIG_COMMANDS_PRE([", - " {macro_prefix}_libobjs=", - " {macro_prefix}_ltlibobjs=", - " if test -n \"${macro_prefix}_LIBOBJS\"; then", - " # Remove the extension.", - " sed_drop_objext='s/\\.o$//;s/\\.obj$//'", - " for i in `for i in ${macro_prefix}_LIBOBJS; " # comma omitted - "do echo \"$i\"; done | sed -e \"$sed_drop_objext\" | sort | uniq`; do", - " {macro_prefix}_libobjs=\"${macro_prefix}_libobjs $i.$ac_objext\"", - " {macro_prefix}_ltlibobjs=\"${macro_prefix}_ltlibobjs $i.lo\"", - " done", - " fi", - " AC_SUBST([{macro_prefix}_LIBOBJS], [${macro_prefix}_libobjs])", - " AC_SUBST([{macro_prefix}_LTLIBOBJS], [${macro_prefix}_ltlibobjs])", - " ])", - ) +__COMMAND_LINE_PATHS = ( + ("libname", lambda k, v, d: f"--lib={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}"), + ("tests_base", lambda k, v, d: f"--tests-base={v}"), + ("auxdir", lambda k, v, d: f"--aux-dir={v}"), +) +__COMMAND_LINE_TESTS = ( + ("tests", lambda v: "--with-tests" if v else None), + ("cxx_tests", lambda v: "--with-c++-tests" if v else None), + ("longrunning_tests", lambda v: "--with-longrunning-tests" if v else None), + ("privileged_tests", lambda v: "--with-privileged-tests" if v else None), + ("unportable_tests", lambda v: "--with-unportable-tests" if v else None), + ("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"), + ("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), +) - - def __init__(self, config, macro_prefix=None): - """ - config: gnulib configuration - macro_prefix: macro prefix; if None, consider configuration - """ - super().__init__(config=config, macro_prefix=macro_prefix) +def command_line(config, explicit, **override): + """Generate gnulib command-line invocation.""" + _type_assert("config", config, _BaseConfig) + _type_assert("explicit", explicit, _ITERABLES) - - def __iter__(self): - # Check the presence of files that are mentioned as AC_LIBSOURCES - # arguments. The check is performed only when autoconf is run from the - # directory where the configure.ac resides; if it is run from a different - # directory, the check is skipped. - for line in InitMacroFooterGenerator._TEMPLATE: - yield line.format(macro_prefix=self.macro_prefix) + yield "gnulib-tool --import" + for path in config.overrides: + yield f"--local-dir={path}" + for (key, hook) in __COMMAND_LINE_PATHS: + value = config[key] + option = hook(key, value, explicit) + if option is not None: + yield option + for (key, hook) in __COMMAND_LINE_TESTS: + value = config[key] + option = hook(value) + if option is not None: + yield option + for module in config.avoids: + yield "--avoid={module.name}" + if config.licenses in _LGPL: + lgpl = _LGPL[config.licenses] + if lgpl != "yes": + yield f"--lgpl={lgpl}" + else: + yield "--lgpl" + for (key, hook) in __COMMAND_LINE_MISC: + value = config[key] + option = hook(key, value, explicit) + if option is not None: + yield option + for module in sorted(config.modules): + yield "{}".format(module) -class InitMacroDoneGenerator(InitMacroGenerator): - """few statements AFTER the gl_INIT macro""" - _TEMPLATE = ( - "", - "# Like AC_LIBOBJ, except that the module name goes", - "# into {macro_prefix}_LIBOBJS instead of into LIBOBJS.", - "AC_DEFUN([{macro_prefix}_LIBOBJ], [", - " AS_LITERAL_IF([$1], [{macro_prefix}_LIBSOURCES([$1.c])])dnl", - " {macro_prefix}_LIBOBJS=\"${macro_prefix}_LIBOBJS $1.$ac_objext\"", - "])", - "", - "# Like AC_REPLACE_FUNCS, except that the module name goes", - "# into {macro_prefix}_LIBOBJS instead of into LIBOBJS.", - "AC_DEFUN([{macro_prefix}_REPLACE_FUNCS], [", - " m4_foreach_w([gl_NAME], [$1], [AC_LIBSOURCES(gl_NAME[.c])])dnl", - " AC_CHECK_FUNCS([$1], , [{macro_prefix}_LIBOBJ($ac_func)])", - "])", - "", - "# Like AC_LIBSOURCES, except the directory where the source file is", - "# expected is derived from the gnulib-tool parameterization,", - "# and alloca is special cased (for the alloca-opt module).", - "# We could also entirely rely on EXTRA_lib..._SOURCES.", - "AC_DEFUN([{macro_prefix}_LIBSOURCES], [", - " m4_foreach([_gl_NAME], [$1], [", - " m4_if(_gl_NAME, [alloca.c], [], [", - " m4_define([{macro_prefix}_LIBSOURCES_DIR], [{source_base}])", - " m4_append([{macro_prefix}_LIBSOURCES_LIST], _gl_NAME, [ ])", - " ])", - " ])", - "])", - ) +__MAKEFILE_PKGDATA = _re.compile(r"^pkgdata_DATA\s+\=") +__MAKEFILE_SUBDIRS = _re.compile(r"lib/.*/.*\.c", _re.S) +__MAKEFILE_LDFLAGS = _re.compile(r"^lib_LDFLAGS\s*\+\=.*?$", _re.S) +__MAKEFILE_LIBNAME = _re.compile(r"lib_([A-Z][A-Z]*)", _re.S) +__MAKEFILE_GNUMAKE = _re.compile(r"^if (.*?)$", _re.S) - def __init__(self, config, source_base=None, macro_prefix=None): - if source_base is None: - source_base = config.source_base - _type_assert("source_base", source_base, str) - super().__init__(config=config, macro_prefix=macro_prefix) - self.__source_base = source_base +def _lib_makefile_callback(conditionals, gnumake): + def _automake_conditional(module, conditional, unconditional, macro_prefix): + yield "" + yield "if {}".format(module.conditional_name(macro_prefix)) + yield conditional + yield "endif" + yield unconditional + + def _automake_unconditional(module, conditional, unconditional, macro_prefix): + yield "" + yield conditional + yield unconditional - @property - def source_base(self): - """directory relative to ROOT where source code is placed; defaults to 'lib'""" - return self.__source_base + def _gnumake_conditional(module, conditional, unconditional, macro_prefix): + yield "ifeq (,$(OMIT_GNULIB_MODULE_{}))".format(module.name) + yield "" + yield "ifneq (,$({}))".format(module.conditional_name(macro_prefix)) + yield __MAKEFILE_GNUMAKE.sub("ifneq (,$(\\1))", conditional) + yield "endif" + yield "endif" + yield __MAKEFILE_GNUMAKE.sub("ifneq (,$(\\1))", unconditional) + def _gnumake_unconditional(module, conditional, unconditional, macro_prefix): + yield "" + yield __MAKEFILE_GNUMAKE.sub("ifneq (,$(\\1))", conditional) + yield __MAKEFILE_GNUMAKE.sub("ifneq (,$(\\1))", unconditional) - def __iter__(self): - for line in InitMacroDoneGenerator._TEMPLATE: - yield line.format(source_base=self.__source_base, macro_prefix=self.macro_prefix) - + callbacks = ( + (_automake_unconditional, _gnumake_unconditional), + (_automake_conditional, _gnumake_conditional), + ) + return callbacks[conditionals][gnumake] -class CommandLineGenerator(BaseGenerator): - """gnulib command-line invocation generator""" - _TESTS = { - "tests": "tests", - "obsolete": "obsolete", - "cxx_tests": "c++-tests", - "longrunning_tests": "longrunning-tests", - "privileged_tests": "privileged-tests", - "unportable_tests": "unportable-tests", +def lib_makefile(path, config, explicit, database, 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 "", } + assign = "+=" if config.gnumake or "makefile_name" in explicit else "=" + eliminate_LDFLAGS = True if config.libtool else False + # When creating a package for testing: Attempt to provoke failures, + # especially link errors, already during "make" rather than during + # "make check", because "make check" is not possible in a cross-compiling + # situation. Turn check_PROGRAMS into noinst_PROGRAMS. + transform_check_PROGRAMS = True if testing 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) + for line in __DISCLAIMER[1:]: + yield line + yield "" + + # The maximum line length (excluding the terminating newline) of any file + # that is to be preprocessed by config.status is 3070. config.status uses + # awk, and the HP-UX 11.00 awk fails if a line has length >= 3071; + # 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 "" + + callback = _lib_makefile_callback(config.conditionals, config.gnumake) + def _snippet(): + lines = [] + subdirs = False + for module in database.main_modules: + if module.test: + continue + conditional = module.conditional_automake_snippet + conditional = conditional.replace("lib_LIBRARIES", "lib%_LIBRARIES") + 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 = 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) + if (conditional + unconditional).strip(): + lines.append("## begin gnulib module {}".format(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 += list(callback(module, conditional, unconditional, config.macro_prefix)) + lines.append("## end gnulib module {}".format(module.name)) + lines.append("") + subdirs |= any(__MAKEFILE_SUBDIRS.match(file) for file in module.files) + return (subdirs, lines) + + (subdirs, lines) = _snippet() + if "makefile_name" not in explicit: + # If there are source files in subdirectories, prevent collision of the + # object files (example: hash.c and libxml/hash.c). + yield "AUTOMAKE_OPTIONS = 1.9.6 gnits{}".format(" subdir-objects" if subdirs else "") + yield "" + if "makefile_name" not in explicit: + yield "SUBDIRS =" + yield "noinst_HEADERS =" + yield "noinst_LIBRARIES =" + yield "noinst_LTLIBRARIES =" + # Automake versions < 1.11.4 create an empty pkgdatadir at + # installation time if you specify pkgdata_DATA to empty. + # See automake bugs #10997 and #11030: + # * http://debbugs.gnu.org/10997 + # * http://debbugs.gnu.org/11030 + # So we need this workaround. + if __MAKEFILE_PKGDATA.match("\n".join(lines)): + yield "pkgdata_DATA =" + yield "EXTRA_DIST =" + yield "BUILT_SOURCES =" + yield "SUFFIXES =" + yield "MOSTLYCLEANFILES {} core *.stackdump".format(assign) + if "makefile_name" not in explicit: + yield "MOSTLYCLEANDIRS =" + yield "CLEANFILES =" + yield "DISTCLEANFILES =" + yield "MAINTAINERCLEANFILES =" - def __init__(self, config, explicit): - _type_assert("config", config, _BaseConfig) - _type_assert("explicit", explicit, _ITERABLES) - super().__init__() - self.__config = config - self.__explicit = explicit + if config.gnumake: + yield "# Start of GNU Make output." + autoconf = "autoconf" + cmdargs = (autoconf, "-t", "AC_SUBST:$1 = @$1@", config.ac_file) + with _sp.Popen(cmdargs, stdout=_sp.PIPE, stderr=_sp.PIPE) as process: + (stdout, stderr) = process.communicate() + stdout = stdout.decode("UTF-8") + stderr = stderr.decode("UTF-8") + if process.returncode == 0: + for line in sorted(stdout.splitlines()): + yield line + else: + yield "== gnulib-tool GNU Make output failed as follows ==" + for line in stderr.splitlines(): + yield "# stderr: {}".format(line) + yield "# End of GNU Make output." + else: + yield "# No GNU Make output." + + for (directory, key, value) in mkedits: + if key and _os.path.join(directory, "Makefile.am") == path: + del (directory, key, value) + yield f"{key} += {value}" + + cppflags = "".join(( + " -D{}=1".format(config.witness_c_macro) if "witness_c_macro" in explicit else "", + " -DGNULIB_STRICT_CHECKING=1" if testing else "", + )) + if "makefile_name" not in explicit: + yield "" + yield "AM_CPPFLAGS ={}".format(cppflags) + yield "AM_CFLAGS =" + elif "".join(cppflags): + yield "" + yield "AM_CPPFLAGS +={}".format(cppflags) + yield "" + + snippet = "\n".join(lines) + if "makefile_name" in explicit: + makefile = _os.path.join(config.source_base, "Makefile.am") + if _os.path.exists(makefile): + with _codecs.open(makefile, "rb", "UTF-8") as stream: + snippet += ("\n" + stream.read()) + # One of the snippets or the user's Makefile.am already specifies an + # 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) + if not pattern.findall(snippet): + yield "noinst_{perhaps_LT}LIBRARIES += {libname}.{libext}".format(**kwargs) + + yield "" + yield "{libname}_{libext}_SOURCES =".format(**kwargs) + # 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) + if config.libtool: + yield "{libname}_{libext}_LDFLAGS = $(AM_LDFLAGS)".format(**kwargs) + yield "{libname}_{libext}_LDFLAGS += -no-undefined".format(**kwargs) + # 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) + yield "" + + if "po_base" in explicit: + yield "AM_CPPFLAGS += -DDEFAULT_TEXT_DOMAIN=\\\"{}-gnulib\\\"".format(config.po_domain) + yield "" + + for line in lines: + src = "$(top_srcdir)/build-aux/" + dst = _os.path.join("$(top_srcdir)", config.auxdir) + yield line.replace(src, dst) + yield "" + yield "mostlyclean-local: mostlyclean-generic" + yield "\t@for dir in '' $(MOSTLYCLEANDIRS); do \\" + yield "\t if test -n \"$$dir\" && test -d $$dir; then \\" + yield "\t echo \"rmdir $$dir\"; rmdir $$dir; \\" + yield "\t fi; \\" + yield "\tdone; \\" + yield "\t:" - def __iter__(self): - config = self.__config - explicit = self.__explicit - yield "gnulib-tool --import" - for path in config.overrides: - yield "--local-dir={}".format(path) - yield "--lib={}".format(config.libname) - yield "--source-base={}".format(config.source_base) - yield "--m4-base={}".format(config.m4_base) - if "po_base" in explicit: - yield "--po-base={}".format(config.po_base) - yield "--doc-base={}".format(config.doc_base) - yield "--tests-base={}".format(config.tests_base) - yield "--aux-dir={}".format(config.auxdir) - for (key, value) in CommandLineGenerator._TESTS.items(): - if config[key]: - yield "--with-{}".format(value) - if config.all_tests: - yield "--with-all-tests" - for module in config.avoids: - yield "--avoid={}".format(module) - if config.licenses in _LGPL: - lgpl = _LGPL[config.licenses] - yield "--lgpl={}".format(lgpl) if lgpl != "yes" else "--lgpl" - if config.gnumake: - yield "--gnu-make" - if "makefile_name" in explicit: - yield "--makefile-name={}".format(config.makefile_name) - yield "--{}conditional-dependencies".format("" if config.conditionals else "no-") - yield "--{}libtool".format("" if config.libtool else "no-") - yield "--macro-prefix={}".format(config.macro_prefix) - if "po_domain" in explicit: - yield "--po-domain={}".format(config.po_domain) - if "witness_c_macro" in explicit: - yield "--witness-c-macro={}".format(config.witness_c_macro) - if "vc_files" in explicit: - yield "--{}vc-files".format("" if config.vc_files else "no-") - for module in sorted(config.modules): - yield "{}".format(module) +def _tests_makefile_callback(gnumake): + + def _automake(module, snippet): + yield "" + yield snippet + + def _gnumake(module, snippet): + yield "ifeq (,$(OMIT_GNULIB_MODULE_{}))".format(module.name) + yield "" + yield snippet + yield "endif" + + return _gnumake if gnumake else _automake + +def tests_makefile(path, config, explicit, database, modules, mkedits, testing): + """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: + modules = sorted(filter(lambda module: module.test, modules)) + + date = _datetime.now() + tests_base = config.tests_base + source_base = config.source_base + m4_base = config.m4_base + macro_prefix = config.macro_prefix + 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 + + # When creating a package for testing: Attempt to provoke failures, + # especially link errors, already during "make" rather than during + # "make check", because "make check" is not possible in a cross-compiling + # situation. Turn check_PROGRAMS into noinst_PROGRAMS. + transform_check_PROGRAMS = True if testing 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) + for line in __DISCLAIMER[1:]: + yield line + yield "" + + callback = _tests_makefile_callback(config.gnumake) + def _snippet(): + main = [] + longrunning = [] + subdirs = False + for module in modules: + lines = [] + snippet = module.automake_snippet + snippet = snippet.replace("lib_LIBRARIES", "lib%_LIBRARIES") + snippet = snippet.replace("lib_LTLIBRARIES", "lib%_LTLIBRARIES") + if eliminate_LDFLAGS: + snippet = __MAKEFILE_LDFLAGS.sub("", snippet) + snippet = __MAKEFILE_LIBNAME.sub("libtests_a_\\1", snippet) + snippet = snippet.replace("lib%_LIBRARIES", "lib_LIBRARIES") + snippet = snippet.replace("lib%_LTLIBRARIES", "lib_LTLIBRARIES") + 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": + 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 += list(callback(module, snippet)) + lines.append("## end gnulib module {}".format(module.name)) + lines.append("") + if module.longrunning_test: + longrunning += lines + else: + main += lines + lines = (main + longrunning) + return (subdirs, lines) + + (subdirs, lines) = _snippet() + + # Generate dependencies here, since it eases the debugging of test failures. + # If there are source files in subdirectories, prevent collision of the + # object files (example: hash.c and libxml/hash.c). + subdir_options = " subdir-objects" if subdirs else "" + yield f"AUTOMAKE_OPTIONS = 1.9.6 foreign{subdir_options}" + yield "" + if testing and not single_configure: + yield f"ACLOCAL_AMFLAGS = -I {tests_base_inverse}/{m4_base}" + yield "" + + # Nothing is being added to SUBDIRS; nevertheless the existence of this + # variable is needed to avoid an error from automake: + # "AM_GNU_GETTEXT used but SUBDIRS not defined" + yield "SUBDIRS = ." + yield "TESTS =" + yield "XFAIL_TESTS =" + yield "TESTS_ENVIRONMENT =" + yield "noinst_PROGRAMS =" + if not testing: + yield "check_PROGRAMS =" + yield "EXTRA_PROGRAMS =" + yield "noinst_HEADERS =" + yield "noinst_LIBRARIES =" + if database.libtests: + if testing: + yield "noinst_LIBRARIES += libtests.a" + else: + yield "check_LIBRARIES = libtests.a" + + # Automake versions < 1.11.4 create an empty pkgdatadir at + # installation time if you specify pkgdata_DATA to empty. + # See automake bugs #10997 and #11030: + # * http://debbugs.gnu.org/10997 + # * http://debbugs.gnu.org/11030 + # So we need this workaround. + if __MAKEFILE_PKGDATA.match("\n".join(lines)): + yield "pkgdata_DATA =" + + yield "EXTRA_DIST =" + yield "BUILT_SOURCES =" + yield "SUFFIXES =" + yield "MOSTLYCLEANFILES = core *.stackdump" + yield "MOSTLYCLEANDIRS =" + yield "CLEANFILES =" + yield "DISTCLEANFILES =" + yield "MAINTAINERCLEANFILES =" + + # Execute edits that apply to the Makefile.am being generated. + for (directory, key, value) in mkedits: + if key and _os.path.join(directory, "Makefile.am") == path: + del (directory, key, value) + yield f"{key} += {value}" + + yield "" + yield "AM_CPPFLAGS = \\" + if testing: + yield " -DGNULIB_STRICT_CHECKING=1 \\" + if "witness_c_macro" in explicit: + yield " -D{witness_c_macro}=1 \\" + yield " -D@{witness_c_macro}@=1 \\" + yield f" -I. -I$(srcdir) \\" + yield f" -I{tests_base_inverse} -I$(srcdir)/{tests_base_inverse} \\" + yield f" -I{tests_base_inverse}/{source_base} -I$(srcdir)/{tests_base_inverse}/{source_base}" + yield f"" + + # All test programs need to be linked with libtests.a. + # It needs to be passed to the linker before ${libname}.${libext}, since + # the tests-related modules depend on the main modules. + # It also needs to be passed to the linker after ${libname}.${libext} + # because the latter might contain incomplete modules (such as the 'error' + # 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 "" + yield f"LDADD ={local_ldadd_before} {tests_base_inverse}/{source_base}/{libname}.{libext}{local_ldadd_after}" + yield "" + + if database.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@. + yield f"libtests_a_LIBADD = $({macro_prefix}tests_LIBOBJS)" + yield f"libtests_a_DEPENDENCIES = $({macro_prefix}tests_LIBOBJS)" + yield "EXTRA_libtests_a_SOURCES =" + # The circular dependency in LDADD requires this. + yield "AM_LIBTOOLFLAGS = --preserve-dup-deps" + yield "" + + # Many test scripts use ${EXEEXT} or ${srcdir}. + # EXEEXT is defined by AC_PROG_CC through autoconf. + # srcdir is defined by autoconf and automake. + yield "TESTS_ENVIRONMENT += EXEEXT='@EXEEXT@' srcdir='$(srcdir)'" + yield "" + + for line in lines: + src = "$(top_srcdir)/build-aux/" + dst = _os.path.join("$(top_srcdir)", config.auxdir) + yield line.replace(src, dst) + yield "" + yield "# Clean up after Solaris cc." + yield "clean-local:" + yield "\trm -rf SunWS_cache" + yield "" + yield "mostlyclean-local: mostlyclean-generic" + yield "\t@for dir in '' $(MOSTLYCLEANDIRS); do \\" + yield "\t if test -n \"$$dir\" && test -d $$dir; then \\" + yield "\t echo \"rmdir $$dir\"; rmdir $$dir; \\" + yield "\t fi; \\" + yield "\tdone; \\" + yield "\t:" -class LibMakefileGenerator(BaseGenerator): - """library Makefile.am generator""" - _LDFLAGS = _re.compile(r"^lib_LDFLAGS\s*\+\=.*?$", _re.S) - _LIBNAME = _re.compile(r"lib_([A-Z][A-Z]*)", _re.S) - _GNUMAKE = _re.compile(r"^if (.*?)$", _re.S) - - - def __init__(self, path, config, explicit, database, mkedits, testing): - _type_assert("path", path, str) - _type_assert("config", config, _BaseConfig) - _type_assert("explicit", explicit, _ITERABLES) - _type_assert("database", database, _Database) - _type_assert("mkedits", mkedits, _ITERABLES) - _type_assert("testing", testing, bool) - super().__init__() - self.__config = config - self.__explicit = explicit - self.__database = database - self.__path = path - self.__mkedits = mkedits - self.__testing = testing - - - def __iter__(self): - date = _datetime.now() - config = self.__config - explicit = self.__explicit - testing = self.__testing - database = self.__database - - gnumake = config.gnumake - libtool = config.libtool - kwargs = { - "libname": config.libname, - "macro_prefix": config.macro_prefix, - "libext": "la" if libtool else "a", - "perhaps_LT": "LT" if libtool else "", - } - assign = "+=" if gnumake or "makefile_name" in explicit else "=" - eliminate_LDFLAGS = True if libtool else False +__GNULIB_CACHE_OPTIONS = ( + ("obsolete", "gl_WITH_OBSOLETE"), + ("cxx_tests", "gl_WITH_CXX_TESTS"), + ("longrunning", "gl_WITH_LONGRUNNING_TESTS"), + ("privileged", "gl_WITH_PRIVILEGED_TESTS"), +) - # When creating a package for testing: Attempt to provoke failures, - # especially link errors, already during "make" rather than during - # "make check", because "make check" is not possible in a cross-compiling - # situation. Turn check_PROGRAMS into noinst_PROGRAMS. - transform_check_PROGRAMS = True if testing 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) - for line in _DISCLAIMER: - yield line - - # The maximum line length (excluding the terminating newline) of any file - # that is to be preprocessed by config.status is 3070. config.status uses - # awk, and the HP-UX 11.00 awk fails if a line has length >= 3071; - # similarly, the IRIX 6.5 awk fails if a line has length >= 3072. - actioncmd = " ".join(CommandLineGenerator(config, explicit)) - if len(actioncmd) <= 3000: - yield "# Reproduce by: {}".format(actioncmd) - yield "" - - def _snippet(): - lines = [] - - def _common_conditional(module, conditional, unconditional): - yield "" - yield "if {}".format(module.conditional_name(config.macro_prefix)) - yield conditional - yield "endif" - yield unconditional - - def _common_unconditional(module, conditional, unconditional): - yield "" - yield conditional - yield unconditional - - def _gnumake_conditional(module, conditional, unconditional): - yield "ifeq (,$(OMIT_GNULIB_MODULE_{}))".format(module.name) - yield "" - yield "ifneq (,$({}))".format(module.conditional_name(config.macro_prefix)) - yield LibMakefileGenerator._GNUMAKE.sub("ifneq (,$(\\1))", conditional) - yield "endif" - yield "endif" - yield LibMakefileGenerator._GNUMAKE.sub("ifneq (,$(\\1))", unconditional) - - def _gnumake_unconditional(module, conditional, unconditional): - yield "" - yield LibMakefileGenerator._GNUMAKE.sub("ifneq (,$(\\1))", conditional) - yield LibMakefileGenerator._GNUMAKE.sub("ifneq (,$(\\1))", unconditional) +def gnulib_cache(config): + """ + Generate gnulib-cache.m4 file. + """ + _type_assert("config", config, _BaseConfig) - uses_subdirs = False - process = ( - (_common_unconditional, _gnumake_unconditional), - (_common_conditional, _gnumake_conditional), - )[config.conditionals][gnumake] - for module in database.main_modules: - if module.test: - continue - conditional = module.conditional_automake_snippet - conditional = conditional.replace("lib_LIBRARIES", "lib%_LIBRARIES") - conditional = conditional.replace("lib_LTLIBRARIES", "lib%_LTLIBRARIES") - if eliminate_LDFLAGS: - conditional = LibMakefileGenerator._LDFLAGS.sub("", conditional) - conditional = LibMakefileGenerator._LIBNAME.sub("{libname}_{libext}_\\1".format(**kwargs), 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 = LibMakefileGenerator._LIBNAME.sub("{libname}_{libext}_\\1".format(**kwargs), unconditional) - if (conditional + unconditional).strip(): - lines.append("## begin gnulib module {}".format(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 += list(process(module, conditional, unconditional)) - lines.append("## end gnulib module {}".format(module.name)) - lines.append("") - return (uses_subdirs, lines) - - (uses_subdirs, overall_snippet) = _snippet() - if "makefile_name" not in explicit: - # If there are source files in subdirectories, prevent collision of the - # object files (example: hash.c and libxml/hash.c). - yield "AUTOMAKE_OPTIONS = 1.9.6 gnits{}".format(" subdir-objects" if uses_subdirs else "") - yield "" - if "makefile_name" not in explicit: - yield "SUBDIRS =" - yield "noinst_HEADERS =" - yield "noinst_LIBRARIES =" - yield "noinst_LTLIBRARIES =" - # Automake versions < 1.11.4 create an empty pkgdatadir at - # installation time if you specify pkgdata_DATA to empty. - # See automake bugs #10997 and #11030: - # * http://debbugs.gnu.org/10997 - # * http://debbugs.gnu.org/11030 - # So we need this workaround. - if {line for line in overall_snippet if line.startswith()}: - yield "pkgdata_DATA =" - yield "EXTRA_DIST =" - yield "BUILT_SOURCES =" - yield "SUFFIXES =" - yield "MOSTLYCLEANFILES {} core *.stackdump".format(assign) - if "makefile_name" not in explicit: - yield "MOSTLYCLEANDIRS =" - yield "CLEANFILES =" - yield "DISTCLEANFILES =" - yield "MAINTAINERCLEANFILES =" - - if gnumake: - yield "# Start of GNU Make output." - self.__autoconf = "autoconf" - self.__configure_ac = "configure.ac" - cmdargs = (self.__autoconf, "-t", "AC_SUBST:$1 = @$1@", self.__configure_ac) - with _sp.Popen(cmdargs, stdout=_sp.PIPE, stderr=_sp.PIPE) as process: - (stdout, stderr) = process.communicate() - stdout = stdout.decode("UTF-8") - stderr = stderr.decode("UTF-8") - if process.returncode == 0: - for line in sorted(stdout.splitlines()): - yield line - else: - yield "== gnulib-tool GNU Make output failed as follows ==" - for line in stderr.splitlines(): - yield "# stderr: {}".format(line) - yield "# End of GNU Make output." - else: - yield "# No GNU Make output." - - for (directory, key, value) in self.__mkedits: - if key and _os.path.join(directory, "Makefile.am") == self.__path: - yield f"{key} += {value}" - - cppflags = "".join(( - " -D{}=1".format(config.witness_c_macro) if "witness_c_macro" in explicit else "", - " -DGNULIB_STRICT_CHECKING=1" if testing else "", - )) - if "makefile_name" not in explicit: - yield "" - yield "AM_CPPFLAGS ={}".format(cppflags) - yield "AM_CFLAGS =" - elif "".join(cppflags): - yield "" - yield "AM_CPPFLAGS +={}".format(cppflags) - yield "" - - snippet = "\n".join(overall_snippet) - if "makefile_name" in explicit: - makefile = _os.path.join(config.source_base, "Makefile.am") - if _os.path.exists(makefile): - with _codecs.open(makefile, "rb", "UTF-8") as stream: - snippet += ("\n" + stream.read()) - # One of the snippets or the user's Makefile.am already specifies an - # 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) - if not pattern.findall(snippet): - yield "noinst_{perhaps_LT}LIBRARIES += {libname}.{libext}".format(**kwargs) - - yield "" - yield "{libname}_{libext}_SOURCES =".format(**kwargs) - # 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) - if libtool: - yield "{libname}_{libext}_LDFLAGS = $(AM_LDFLAGS)".format(**kwargs) - yield "{libname}_{libext}_LDFLAGS += -no-undefined".format(**kwargs) - # 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)) - yield "" - - if "po_base" in explicit: - yield "AM_CPPFLAGS += -DDEFAULT_TEXT_DOMAIN=\\\"{}-gnulib\\\"".format(config.po_domain) - yield "" - - for line in overall_snippet: - yield line.replace("$(top_srcdir)/build-aux/", _os.path.join("$(top_srcdir)", config.auxdir)) - yield "" - yield "mostlyclean-local: mostlyclean-generic" - yield "\t@for dir in '' $(MOSTLYCLEANDIRS); do \\" - yield "\t if test -n \"$$dir\" && test -d $$dir; then \\" - yield "\t echo \"rmdir $$dir\"; rmdir $$dir; \\" - yield "\t fi; \\" - yield "\tdone; \\" - yield "\t:" + 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) + for line in __DISCLAIMER: + yield line + yield "#" + yield "# This file represents the specification of how gnulib-tool is used." + yield "# It acts as a cache: It is written and read by gnulib-tool." + yield "# In projects that use version control, this file is meant to be put under" + yield "# version control, like the configure.ac and various Makefile.am files." + yield "" + yield "" + yield "# Specification in the form of a command-line invocation:" + yield "gl_LOCAL_DIR([$relative_local_gnulib_path])" + yield "gl_MODULES([" + for module in sorted(config.modules): + yield " {}".format(module) + yield "])" + for key in ("obsolete", "cxx_tests", "longrunning_tests", "privileged_tests", "unportable_tests"): + if config[key]: + yield "gl_WITH_{}".format(key.upper()) + 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) + if config.tests: + yield "gl_WITH_TESTS" + yield "gl_LIB([{}])".format(config.libname) + if 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) + 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) + if config.vc_files: + yield "gl_VC_FILES([{}])".format(" ".join(sorted(config.vc_files))) -class GnulibCacheGenerator(BaseGenerator): - """gnulib-cache.m4 generator""" - _OPTIONS = ( - ("obsolete", "gl_WITH_OBSOLETE"), - ("cxx_tests", "gl_WITH_CXX_TESTS"), - ("longrunning", "gl_WITH_LONGRUNNING_TESTS"), - ("privileged", "gl_WITH_PRIVILEGED_TESTS"), - ) - - - def __init__(self, config): - _type_assert("config", config, _BaseConfig) - super().__init__() - self.__config = config - +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 + macro_prefix = config.macro_prefix + main_modules = database.main_modules + test_modules = database.test_modules - def __iter__(self): - date = _datetime.now() - config = self.__config - 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) - for line in _DISCLAIMER: - yield line - yield "#" - yield "# This file represents the specification of how gnulib-tool is used." - yield "# It acts as a cache: It is written and read by gnulib-tool." - yield "# In projects that use version control, this file is meant to be put under" - yield "# version control, like the configure.ac and various Makefile.am files." - yield "" - yield "" - yield "# Specification in the form of a command-line invocation:" - yield "gl_LOCAL_DIR([$relative_local_gnulib_path])" - yield "gl_MODULES([" - for module in sorted(config.modules): - yield " {}".format(module) - yield "])" - for key in ("obsolete", "cxx_tests", "longrunning_tests", "privileged_tests", "unportable_tests"): - if config[key]: - yield "gl_WITH_{}".format(key.upper()) - 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) - if config.tests: - yield "gl_WITH_TESTS" - yield "gl_LIB([{}])".format(config.libname) - if 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) - 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) - if config.vc_files: - yield "gl_VC_FILES([{}])".format(" ".join(sorted(config.vc_files))) - - - -class GnulibCompGenerator(BaseGenerator): - """gnulib-comp.m4 generator""" - def __init__(self, config, explicit, database, macro_prefix=None): - _type_assert("config", config, _BaseConfig) - _type_assert("explicit", explicit, _ITERABLES) - _type_assert("database", database, _Database) - if macro_prefix is None: - macro_prefix = config.macro_prefix - _type_assert("macro_prefix", macro_prefix, str) - super().__init__() - self.__config = config - self.__explicit = explicit - self.__database = database - self.__macro_prefix = macro_prefix - self.__uses_subdirs = True - - - def __iter__(self): - config = self.__config - explicit = self.__explicit - database = self.__database - main_modules = database.main_modules - test_modules = database.test_modules - macro_prefix = self.__macro_prefix - - date = _datetime.now() - yield "# DO NOT EDIT! GENERATED AUTOMATICALLY!" - yield "# Copyright (C) 2002-{} Free Software Foundation, Inc.".format(date.year) - iterable = super().__iter__() - try: - next(iterable) - except StopIteration: - pass - for line in iterable: - yield line + date = _datetime.now() + yield "# DO NOT EDIT! GENERATED AUTOMATICALLY!" + yield "# Copyright (C) 2002-{} Free Software Foundation, Inc.".format(date.year) + for line in __DISCLAIMER[1:]: + yield line - yield "#" - yield "# This file represents the compiled summary of the specification in" - yield "# gnulib-cache.m4. It lists the computed macro invocations that need" - yield "# to be invoked from configure.ac." - yield "# In projects that use version control, this file can be treated like" - yield "# other built files." - yield "" - yield "" - yield "# This macro should be invoked from {}, in the section".format(config.ac_file) - 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 "[" - yield " m4_pattern_forbid([^gl_[A-Z]])dnl the gnulib macro namespace" - yield " m4_pattern_allow([^gl_ES$])dnl a valid locale name" - yield " m4_pattern_allow([^gl_LIBOBJS$])dnl a variable" - yield " m4_pattern_allow([^gl_LTLIBOBJS$])dnl a variable" - yield "" - yield " # Pre-early section." - if "externsions" not in (module.name for module in database.final_modules): - yield " AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])" - yield " AC_REQUIRE([gl_PROG_AR_RANLIB])" - yield "" - if not config.gnumake and self.__uses_subdirs: - yield " AC_REQUIRE([AM_PROG_CC_C_O])" - for module in database.final_modules: - yield " # Code from module {}:".format(module.name) - lines = module.early_autoconf_snippet.split("\n") - for line in filter(lambda line: line.strip(), lines): - yield " {}".format(line) - yield "])" - yield "" - yield "# This macro should be invoked from {}, in the section".format(config.ac_file) - yield "# \"Check for header files, types and library functions\"." - yield "AC_DEFUN([{}_INIT],".format(macro_prefix) - yield "[" - if config.libtool: - yield " AM_CONDITIONAL([GL_COND_LIBTOOL], [true])" - yield " gl_cond_libtool=true" - else: - yield " AM_CONDITIONAL([GL_COND_LIBTOOL], [false])" - yield " gl_cond_libtool=false" - yield " gl_libdeps=" - yield " gl_ltlibdeps=" - yield " gl_m4_base='{}'".format(config.m4_base) - for line in InitMacroHeaderGenerator(config, macro_prefix): - yield line - yield " gl_source_base='{}'".format(config.source_base) - if "witness_c_macro" in explicit: - yield " m4_pushdef([gl_MODULE_INDICATOR_CONDITION], [{}])".format(config.witness_c_macro) - for line in AutoconfMultisnippetGenerator(config, database, main_modules, True, False, True, macro_prefix): - yield line - if "witness_c_macro" in explicit: - yield " m4_popdef([gl_MODULE_INDICATOR_CONDITION])" - yield " # End of code from modules" - for line in InitMacroFooterGenerator(config, macro_prefix): - yield line - yield " gltests_libdeps=" - yield " gltests_ltlibdeps=" - for line in InitMacroHeaderGenerator(config, (macro_prefix + "tests")): - yield line - yield " gl_source_base='{}'".format(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 . - yield "changequote(,)dnl" - yield "".join(( - " {}tests_WITNESS=IN_`".format(macro_prefix), - "echo \"${PACKAGE-$PACKAGE_TARNAME}\"", - " | ", - "LC_ALL=C tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ", - " | ", - "LC_ALL=C sed -e 's/[^A-Z0-9_]/_/g'", - "`_GNULIB_TESTS", - )) - yield "changequote([, ])dnl" - yield " AC_SUBST([{}tests_WITNESS])".format(macro_prefix) - yield " gl_module_indicator_condition=${}tests_WITNESS".format(macro_prefix) - yield " m4_pushdef([gl_MODULE_INDICATOR_CONDITION], [$gl_module_indicator_condition])" - for line in AutoconfMultisnippetGenerator(config, database, test_modules, True, True, True, macro_prefix): - yield line + yield "#" + yield "# This file represents the compiled summary of the specification in" + yield "# gnulib-cache.m4. It lists the computed macro invocations that need" + yield "# to be invoked from configure.ac." + yield "# In projects that use version control, this file can be treated like" + yield "# other built files." + yield "" + yield "" + yield "# This macro should be invoked from {}, in the section".format(config.ac_file) + 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 "[" + yield " m4_pattern_forbid([^gl_[A-Z]])dnl the gnulib macro namespace" + yield " m4_pattern_allow([^gl_ES$])dnl a valid locale name" + yield " m4_pattern_allow([^gl_LIBOBJS$])dnl a variable" + yield " m4_pattern_allow([^gl_LTLIBOBJS$])dnl a variable" + yield "" + yield " # Pre-early section." + if "externsions" not in (module.name for module in database.final_modules): + yield " AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])" + yield " AC_REQUIRE([gl_PROG_AR_RANLIB])" + yield "" + 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) + lines = module.early_autoconf_snippet.split("\n") + for line in filter(lambda line: line.strip(), lines): + yield " {}".format(line) + yield "])" + yield "" + yield "# This macro should be invoked from {}, in the section".format(config.ac_file) + yield "# \"Check for header files, types and library functions\"." + yield "AC_DEFUN([{}_INIT],".format(macro_prefix) + yield "[" + if config.libtool: + yield " AM_CONDITIONAL([GL_COND_LIBTOOL], [true])" + yield " gl_cond_libtool=true" + else: + yield " AM_CONDITIONAL([GL_COND_LIBTOOL], [false])" + yield " gl_cond_libtool=false" + yield " gl_libdeps=" + yield " gl_ltlibdeps=" + yield " gl_m4_base='{}'".format(config.m4_base) + for line in init_macro_header(config, macro_prefix=macro_prefix): + yield line + yield " gl_source_base='{}'".format(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 line + if "witness_c_macro" in explicit: yield " m4_popdef([gl_MODULE_INDICATOR_CONDITION])" - for line in InitMacroFooterGenerator(config, (macro_prefix + "tests")): - yield line + yield " # End of code from modules" + for line in init_macro_footer(config, macro_prefix=macro_prefix): + yield line + yield " gltests_libdeps=" + 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) + # 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 . + yield "changequote(,)dnl" + yield "".join(( + " {}tests_WITNESS=IN_`".format(macro_prefix), + "echo \"${PACKAGE-$PACKAGE_TARNAME}\"", + " | ", + "LC_ALL=C tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ", + " | ", + "LC_ALL=C sed -e 's/[^A-Z0-9_]/_/g'", + "`_GNULIB_TESTS", + )) + yield "changequote([, ])dnl" + yield " AC_SUBST([{}tests_WITNESS])".format(macro_prefix) + yield " gl_module_indicator_condition=${}tests_WITNESS".format(macro_prefix) + 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): + yield line + yield " m4_popdef([gl_MODULE_INDICATOR_CONDITION])" + for line in init_macro_footer(config, macro_prefix=(macro_prefix + "tests")): + yield line - # _LIBDEPS and _LTLIBDEPS variables are not needed if this library is - # 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) - if database.libtests: - yield " LIBTESTS_LIBDEPS=\"$gltests_libdeps\"" - yield " AC_SUBST([LIBTESTS_LIBDEPS])" - yield "])" - for line in InitMacroDoneGenerator(config, source_base=config.source_base, macro_prefix=macro_prefix): - yield line - for line in InitMacroDoneGenerator(config, source_base=config.tests_base, macro_prefix=(macro_prefix + "tests")): - yield line - 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 "])" + # _LIBDEPS and _LTLIBDEPS variables are not needed if this library is + # 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) + if database.libtests: + yield " LIBTESTS_LIBDEPS=\"$gltests_libdeps\"" + yield " AC_SUBST([LIBTESTS_LIBDEPS])" + yield "])" + for line in init_macro_done(config, source_base=config.source_base, macro_prefix=macro_prefix): + yield line + for line in init_macro_done(config, source_base=config.tests_base, macro_prefix=(macro_prefix + "tests")): + yield line + 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 "])" diff -r 9085df917421 -r 915fae496f3c pygnulib/module.py --- a/pygnulib/module.py Sun Jan 21 20:43:31 2018 +0300 +++ b/pygnulib/module.py Mon Jan 29 19:07:42 2018 +0300 @@ -347,9 +347,9 @@ if self.test: # *-tests module live in tests/, not lib/. # Synthesize an EXTRA_DIST augmentation. - test_files = {file for file in files if file.startswith("tests/")} + test_files = tuple(file[len("tests/"):] for file in files if file.startswith("tests/")) if test_files: - result += ("EXTRA_DIST += {}".format(" ".join(sorted(test_files))) + "\n") + result += ("EXTRA_DIST += {}".format(" ".join(test_files)) + "\n") return result snippet = self.conditional_automake_snippet lib_SOURCES = False @@ -376,7 +376,7 @@ lib_files = tuple(file[len("lib/"):] for file in all_files if file.startswith("lib/")) extra_files = tuple(file for file in lib_files if file not in mentioned_files) if extra_files: - result += ("EXTRA_DIST += {}".format(" ".join(sorted(extra_files))) + "\n") + result += ("EXTRA_DIST += {}".format(" ".join(extra_files)) + "\n") # Synthesize also an EXTRA_lib_SOURCES augmentation. # This is necessary so that automake can generate the right list of @@ -406,7 +406,7 @@ @property def automake_snippet(self): """full automake snippet (conditional + unconditional parts)""" - return self.conditional_automake_snippet + self.unconditional_automake_snippet + return "\n".join((self.conditional_automake_snippet, self.unconditional_automake_snippet)) @property @@ -706,7 +706,7 @@ pass # ignore non-existent tests for (dependency, condition) in demander.dependencies: dependency = lookup(dependency) - if config.gnumake and condition.startswith("if "): + if config.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 @@ -903,7 +903,7 @@ test_files = set() for file in _files(test_modules): if file.startswith("lib/"): - file = ("lib=tests/" + file[len("lib/"):]) + file = ("tests=lib/" + file[len("lib/"):]) test_files.add(file) self.__libtests = libtests diff -r 9085df917421 -r 915fae496f3c pygnulib/tools.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygnulib/tools.py Mon Jan 29 19:07:42 2018 +0300 @@ -0,0 +1,64 @@ +#!/usr/bin/python +# encoding: UTF-8 +"""gnulib command-line tools""" + + + +import os as _os +import subprocess as _sp + + +from .error import type_assert as _type_assert + + + +class _PipeMeta(type): + __INSTANCE = None + def __call__(cls, *args, **kwargs): + if _PipeMeta.__INSTANCE is None: + _PipeMeta.__INSTANCE = super(_PipeMeta, cls).__call__(*args, **kwargs) + return _PipeMeta.__INSTANCE + + +class Pipe(metaclass=_PipeMeta): + """pipe handle singleton""" + pass + + + +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 + + + @property + def name(self): + """executable name""" + return self.__name + + + @property + def path(self): + """executable path""" + return self.__path if self.__path else self.name + + + def __call__(self, *args, **kwargs): + """ + Invoke command-line tool with the given arguments. + Upon execution subprocess.Popen instance is returned. + """ + args = ([self.path] + list(args)) + for key in ("stdin", "stdout", "stderr"): + if isinstance(kwargs.get(key, Pipe()), Pipe): + kwargs[key] = _sp.PIPE + kwargs.setdefault("encoding", self.__encoding) + return _sp.Popen(args, **kwargs) diff -r 9085df917421 -r 915fae496f3c pygnulib/vfs.py --- a/pygnulib/vfs.py Sun Jan 21 20:43:31 2018 +0300 +++ b/pygnulib/vfs.py Mon Jan 29 19:07:42 2018 +0300 @@ -56,10 +56,10 @@ _type_assert("name", name, str) parts = [] replaced = False - path = _os.path.normpath(name) - if _os.path.isabs(path): + name = _os.path.normpath(name) + if _os.path.isabs(name): raise ValueError("name cannot be an absolute path") - for part in path.split(_os.path.sep): + for part in name.split(_os.path.sep): if part == "..": parts += [part] continue @@ -67,8 +67,18 @@ part = self.__table[part] replaced = True parts += [part] - path = _os.path.sep.join(parts) - return _os.path.normpath(path) + name = _os.path.sep.join(parts) + return _os.path.normpath(name) + + + 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) + dst = _os.path.normpath(dst) + self.__table[src] = dst @property @@ -291,7 +301,7 @@ def __init__(self, prefix, **table): super().__init__(prefix, **table) - self.__cache = {} + self.__cache = {"dummy": _DummyModule()} if not _os.path.exists(self.absolute): raise FileNotFoundError(self.absolute) if not _os.path.isdir(self.absolute): @@ -307,10 +317,7 @@ return self.__cache[name] path = _os.path.join(self.absolute, self["modules"], name) try: - if name != "dummy": - self.__cache[name] = _GnulibModule(path=path, name=name) - else: - self.__cache[name] = _DummyModule() + self.__cache[name] = _GnulibModule(path=path, name=name) return self.__cache[name] except FileNotFoundError: raise _UnknownModuleError(name)