#!/usr/bin/python # encoding: UTF-8 """gnulib generators API""" import os as _os import re as _re import codecs as _codecs import subprocess as _sp from datetime import datetime as _datetime from .config import BaseConfig as _BaseConfig from .config import LGPLv2_LICENSE as _LGPLv2_LICENSE from .config import LGPLv3_LICENSE as _LGPLv3_LICENSE from .config import GPLv2_LICENSE as _GPLv2_LICENSE from .config import LGPL_LICENSES as _LGPL_LICENSES from .misc import Executable as _Executable from .module import BaseModule as _BaseModule from .module import Database as _Database _LGPL = { tuple(_LGPLv2_LICENSE): "2", tuple(_LGPLv3_LICENSE): "3", tuple(_LGPL_LICENSES): "yes", tuple(_GPLv2_LICENSE | _LGPLv3_LICENSE): "3orGPLv2", } __DISCLAIMER = ( "## DO NOT EDIT! GENERATED AUTOMATICALLY!", "#", "# This file is free software; you can redistribute it and/or modify", "# it under the terms of the GNU General Public License as published by", "# the Free Software Foundation; either version 3 of the License, or", "# (at your option) any later version.", "#", "# This file is distributed in the hope that it will be useful,", "# but WITHOUT ANY WARRANTY; without even the implied warranty of", "# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the", "# GNU General Public License for more details.", "#", "# You should have received a copy of the GNU General Public License", "# along with this file. If not, see .", "#", "# As a special exception to the GNU General Public License,", "# this file may be distributed as part of a program that", "# contains a configuration script generated by Autoconf, under", "# the same distribution terms as the rest of that program.", "#", "# Generated by gnulib-tool.", ) _ITERABLES = frozenset((list, tuple, set, frozenset, type({}.keys()), type({}.values()))) __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 po_make_vars(config, **override): """Generate PO Makefile parameterization.""" config = _BaseConfig(**config) for (key, value) in override.items(): config[key] = value 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 def POTFILES(config, files, **override): """Generate file list to be passed to xgettext.""" config = _BaseConfig(**config) for (key, value) in override.items(): config[key] = value 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:]) def autoconf_snippet(config, module, toplevel, no_libtool, no_gettext, **override): """ Generate autoconf snippet for a standalone module.""" config = _BaseConfig(**config) for (key, value) in override.items(): config[key] = value 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 = snippet.replace(r"${gl_include_guard_prefix}", include_guard_prefix) if no_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) if no_gettext: src = "AM_GNU_GETTEXT([external])" dst = "dnl you must add AM_GNU_GETTEXT([external]) or similar to configure.ac.'" snippet = snippet.replace(src, dst) 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 config.libtool and not no_libtool: yield "changequote(,)dnl" yield "LTALLOCA=`echo \"$ALLOCA\" | sed -e 's/\\.[^.]* /.lo /g;s/\\.[^.]*$/.lo/'`" yield "changequote([, ])dnl" yield "AC_SUBST([LTALLOCA])" def autoconf_snippets(config, database, modules, toplevel, no_libtool, no_gettext, **override): """Generate an autoconf snippet for multiple modules.""" config = _BaseConfig(**config) for (key, value) in override.items(): config[key] = value 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: 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: 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: 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}])" __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]))", # 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]))", # 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_header(config, **override): """Generate the first few statements of the gl_INIT macro.""" 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) __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])", " ])", ) def init_macro_footer(config, **override): """Generate the last few statements of the gl_INIT macro.""" config = _BaseConfig(**config) for (key, value) in override.items(): config[key] = value macro_prefix = config.macro_prefix for line in __INIT_MACRO_FOOTER: yield line.format(**config) __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, [ ])", " ])", " ])", "])", ) def init_macro_done(config, **override): """ Generate few statements after the gl_INIT macro. [macro_prefix] the prefix of the macros 'gl_EARLY' and 'gl_INIT' """ config = _BaseConfig(**config) for (key, value) in override.items(): config[key] = value for line in __INIT_MACRO_DONE: yield line.format(**config) __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 = ( ("makefile_name", lambda k, v, d: f"--makefile-name={v}"), ("conditionals", lambda k, v, d: ("--no-", "--")[v] + "conditional-dependencies"), ("libtool", lambda k, v, d: ("--no-", "--")[v] + "libtool"), ("macro_prefix", lambda k, v, d: f"--macro-prefix={v}"), ("gnumake", lambda k, v, d: "--gnu-make" if v else None), ("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 command_line(config, explicit, **override): """Generate gnulib command-line invocation.""" 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 tuple(config.licenses) in _LGPL: lgpl = _LGPL[tuple(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 config.modules: yield f"{module}" __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\s(.*?)$", _re.S | _re.M) def _lib_makefile_callback(database, macro_prefix, conditionals, gnumake): def _automake_conditional(module, conditional, unconditional): yield "" if database.conditional(module): yield "if {}".format(module.conditional_name(macro_prefix)) yield conditional yield "endif" else: yield conditional yield unconditional def _automake_unconditional(module, conditional, unconditional): yield "" yield conditional yield unconditional def _gnumake_conditional(module, conditional, unconditional): yield "ifeq (,$(OMIT_GNULIB_MODULE_{}))".format(module.name) yield "" if database.conditional(module): yield "ifneq (,$({}))".format(module.conditional_name(macro_prefix)) yield __MAKEFILE_GNUMAKE.sub("ifneq (,$(\\1))", conditional) yield "endif" else: yield __MAKEFILE_GNUMAKE.sub("ifneq (,$(\\1))", conditional) yield "endif" yield __MAKEFILE_GNUMAKE.sub("ifneq (,$(\\1))", unconditional) def _gnumake_unconditional(module, conditional, unconditional): yield "" yield __MAKEFILE_GNUMAKE.sub("ifneq (,$(\\1))", conditional) yield __MAKEFILE_GNUMAKE.sub("ifneq (,$(\\1))", unconditional) callbacks = ( (_automake_unconditional, _gnumake_unconditional), (_automake_conditional, _gnumake_conditional), ) return callbacks[conditionals][gnumake] def lib_makefile(path, config, explicit, database, mkedits, testing, autoconf, **override): """Generate library Makefile.am file.""" if not isinstance(autoconf, _Executable): raise TypeError("autoconf: executable expected") date = _datetime.now() libname = config.libname po_domain = config.po_domain macro_prefix = config.macro_prefix libext = "la" if config.libtool else "a" perhaps_LT = "LT" if config.libtool else "" assign = "+=" if config.gnumake or "makefile_name" in explicit else "=" eliminate_LDFLAGS = True if config.libtool else False # 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 f"# Copyright (C) 2002-{date.year} Free Software Foundation, Inc." 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 f"# Reproduce by: {actioncmd}" yield "" callback = _lib_makefile_callback(database, macro_prefix, 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(f"{libname}_{libext}_\\1", conditional) conditional = conditional.replace("lib%_LIBRARIES", "lib_LIBRARIES") conditional = conditional.replace("lib%_LTLIBRARIES", "lib_LTLIBRARIES") if transform_check_PROGRAMS: conditional = conditional.replace("check_PROGRAMS", "noinst_PROGRAMS") conditional = conditional.replace(r"${gl_include_guard_prefix}", config.include_guard_prefix) unconditional = module.unconditional_automake_snippet.format(auxdir=config.auxdir) unconditional = __MAKEFILE_LIBNAME.sub(f"{libname}_{libext}_\\1", unconditional) if (conditional + unconditional).strip(): lines.append(f"## begin gnulib module {module.name}") if module.name == "alloca": lines.append(f"{libname}_{libext}_LIBADD += @{perhaps_LT}ALLOCA@") lines.append(f"{libname}_{libext}_DEPENDENCIES += @{perhaps_LT}ALLOCA@") lines += list(callback(module, conditional, unconditional)) lines.append(f"## end gnulib module {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 f"MOSTLYCLEANFILES {assign} core *.stackdump" if "makefile_name" not in explicit: yield "MOSTLYCLEANDIRS =" yield "CLEANFILES =" yield "DISTCLEANFILES =" yield "MAINTAINERCLEANFILES =" if config.gnumake: yield "# Start of GNU Make output." with autoconf("-t", "AC_SUBST:$1 = @$1@", config.ac_file) as process: (stdout, stderr) = process.communicate() stdout = stdout.decode("UTF-8") stderr = stderr.decode("UTF-8") if process.returncode == 0: for line in sorted(set(stdout.splitlines())): yield line else: yield "== gnulib-tool GNU Make output failed as follows ==" for line in stderr.splitlines(): yield f"# stderr: {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 f"AM_CPPFLAGS ={cppflags}" yield "AM_CFLAGS =" elif "".join(cppflags): yield "" yield f"AM_CPPFLAGS +={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. pattern = _re.compile(f"^[a-zA-Z0-9_]*_{perhaps_LT}LIBRARIES\\s*\\+?\\=\\s*{libname}\\.{libext}$", _re.S) if not pattern.findall(snippet): yield f"noinst_{perhaps_LT}LIBRARIES += {libname}.{libext}" yield "" yield f"{libname}_{libext}_SOURCES =" # Here we use $(LIBOBJS), not @LIBOBJS@. The value is the same. However, # automake during its analysis looks for $(LIBOBJS), not for @LIBOBJS@. yield f"{libname}_{libext}_LIBADD = $({macro_prefix}_{perhaps_LT}LIBOBJS)" yield f"{libname}_{libext}_DEPENDENCIES = $({macro_prefix}_{perhaps_LT}LIBOBJS)" yield f"EXTRA_{libname}_{libext}_SOURCES =" if config.libtool: yield f"{libname}_{libext}_LDFLAGS = $(AM_LDFLAGS)" yield f"{libname}_{libext}_LDFLAGS += -no-undefined" # Synthesize an ${libname}_${libext}_LDFLAGS augmentation by combining # the link dependencies of all modules. def _directives(modules): for module in modules: for directive in module.link_directives: index = directive.find("when linking with libtool") if index != -1: directive = directive[:index].strip(" ") yield directive for directive in sorted(set(_directives(database.main_modules))): yield f"{libname}_{libext}_LDFLAGS += {directive}" yield "" if "po_base" in explicit: yield f"AM_CPPFLAGS += -DDEFAULT_TEXT_DOMAIN=\\\"{po_domain}-gnulib\\\"" yield "" for line in lines: src = "$(top_srcdir)/build-aux/" dst = _os.path.join("$(top_srcdir)", config.auxdir) dst += _os.path.sep 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 _tests_makefile_callback(gnumake): def _automake(module, snippet): yield "" yield snippet def _gnumake(module, snippet): yield f"ifeq (,$(OMIT_GNULIB_MODULE_{module.name}))" yield "" yield snippet yield "endif" return _gnumake if gnumake else _automake def tests_makefile(path, config, explicit, modules, mkedits, testing, libtests): """Generate tests Makefile.am file.""" single_configure = config.single_configure if testing and not 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") 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 f"# Copyright (C) 2002-{date.year} Free Software Foundation, Inc." 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 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(f"## begin gnulib module {module.name}") lines += list(callback(module, snippet)) lines.append(f"## end gnulib module {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 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 libtests else "" local_ldadd_after = " libtests.a $(LIBTESTS_LIBDEPS)" if libtests else "" yield f"LDADD ={local_ldadd_before} {tests_base_inverse}/{source_base}/{libname}.{libext}{local_ldadd_after}" yield "" if 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) dst += _os.path.sep 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:" __GNULIB_CACHE_OPTIONS = ( ("obsolete", "gl_WITH_OBSOLETE"), ("cxx_tests", "gl_WITH_CXX_TESTS"), ("longrunning", "gl_WITH_LONGRUNNING_TESTS"), ("privileged", "gl_WITH_PRIVILEGED_TESTS"), ) def gnulib_cache(config, explicit): """ Generate gnulib-cache.m4 file. """ date = _datetime.now() yield "## DO NOT EDIT! GENERATED AUTOMATICALLY!" yield "## Process this file with automake to produce Makefile.in." yield f"# Copyright (C) 2002-{date.year} Free Software Foundation, Inc." 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 "# " + " ".join(command_line(config, explicit)) yield "" yield "# Specification in the form of a few gnulib-tool.m4 macro invocations:" yield "gl_LOCAL_DIR([{}])".format(" ".join(config.overrides)) yield "gl_MODULES([" for module in sorted(config.modules): yield f" {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 f"gl_SOURCE_BASE([{config.source_base}])" yield f"gl_M4_BASE([{config.m4_base}])" yield f"gl_PO_BASE([{config.po_base}])" yield f"gl_DOC_BASE([{config.doc_base}])" yield f"gl_TESTS_BASE([{config.tests_base}])" if config.tests: yield "gl_WITH_TESTS" yield "gl_LIB([{}])".format(config.libname) if tuple(config.licenses) in _LGPL: lgpl = _LGPL[tuple(config.licenses)] yield "gl_LGPL([{}])".format(lgpl) if lgpl != "yes" else "gl_LGPL" yield f"gl_MAKEFILE_NAME([{config.makefile_name}])" if config.conditionals: yield "gl_CONDITIONAL_DEPENDENCIES" if config.libtool: yield "gl_LIBTOOL" yield f"gl_MACRO_PREFIX([{config.macro_prefix}])" yield f"gl_PO_DOMAIN([{config.po_domain}])" yield f"gl_WITNESS_C_MACRO([{config.witness_c_macro}])" if config.vc_files: yield "gl_VC_FILES([{true}])" def gnulib_comp(config, explicit, database, subdirs, **override): """gnulib-comp.m4 generator""" 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 date = _datetime.now() ac_file = config.ac_file yield "# DO NOT EDIT! GENERATED AUTOMATICALLY!" yield f"# Copyright (C) 2002-{date.year} Free Software Foundation, Inc." 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 f"# This macro should be invoked from {ac_file}, in the section" yield "# \"Checks for programs\", right after AC_PROG_CC, and certainly before" yield "# any checks for libraries, header files, types and library functions." yield f"AC_DEFUN([{macro_prefix}_EARLY]," yield "[" yield " m4_pattern_forbid([^gl_[A-Z]])dnl the gnulib macro namespace" yield " m4_pattern_allow([^gl_ES$])dnl a valid locale name" 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 f" # Code from module {module.name}:" lines = module.early_autoconf_snippet.split("\n") for line in filter(lambda line: line.strip(), lines): yield f" {line}" yield "])" yield "" yield f"# This macro should be invoked from {ac_file}, in the section" yield "# \"Check for header files, types and library functions\"." yield f"AC_DEFUN([{macro_prefix}_INIT]," 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 f" gl_m4_base='{config.m4_base}'" for line in init_macro_header(config, macro_prefix=macro_prefix): yield line yield f" gl_source_base='{config.source_base}'" if "witness_c_macro" in explicit: yield f" m4_pushdef([gl_MODULE_INDICATOR_CONDITION], [{config.witness_c_macro}])" for line in autoconf_snippets(config, database, main_modules, True, False, True, macro_prefix=macro_prefix): yield line if "witness_c_macro" in explicit: yield " m4_popdef([gl_MODULE_INDICATOR_CONDITION])" yield " # 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 f" gl_source_base='{config.tests_base}'" # Define a tests witness macro that depends on the package. # PACKAGE is defined by AM_INIT_AUTOMAKE, PACKAGE_TARNAME is defined by AC_INIT. # See . yield "changequote(,)dnl" yield "".join(( f" {macro_prefix}tests_WITNESS=IN_`", "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 f" AC_SUBST([{macro_prefix}tests_WITNESS])" yield f" gl_module_indicator_condition=${macro_prefix}tests_WITNESS" yield " m4_pushdef([gl_MODULE_INDICATOR_CONDITION], [$gl_module_indicator_condition])" for line in autoconf_snippets(config, database, test_modules, True, True, True, macro_prefix=macro_prefix): yield line yield " m4_popdef([gl_MODULE_INDICATOR_CONDITION])" for line in init_macro_footer(config, macro_prefix=(macro_prefix + "tests")): 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 f" {libname}_LIBDEPS=\"$gl_libdeps\"" yield f" AC_SUBST([{libname}_LIBDEPS])" yield f" {libname}_LTLIBDEPS=\"$gl_ltlibdeps\"" yield f" AC_SUBST([{libname}_LTLIBDEPS])" if database.libtests: yield " LIBTESTS_LIBDEPS=\"$gltests_libdeps\"" yield " AC_SUBST([LIBTESTS_LIBDEPS])" yield "])" 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 f"AC_DEFUN([{macro_prefix}_FILE_LIST], [" test_files = set() main_files = set(database.main_files) for file in database.test_files: if file.startswith("lib/"): file = ("tests=lib/" + file[len("lib/"):]) test_files.add(file) for file in sorted(main_files | test_files): yield f" {file}" yield "])"