#!/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 "])"