# Copyright (C) 2002-2023 Free Software Foundation, Inc.
#
# This program 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 program 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 program. If not, see .
#===============================================================================
# Define global imports
#===============================================================================
import os
import re
import codecs
import subprocess as sp
from . import constants
from .GLInfo import GLInfo
from .GLConfig import GLConfig
from .GLModuleSystem import GLModule
from .GLModuleSystem import GLModuleTable
from .GLMakefileTable import GLMakefileTable
from .GLFileSystem import GLFileAssistant
#===============================================================================
# Define module information
#===============================================================================
__author__ = constants.__author__
__license__ = constants.__license__
__copyright__ = constants.__copyright__
#===============================================================================
# Define global constants
#===============================================================================
TESTS = constants.TESTS
joinpath = constants.joinpath
relinverse = constants.relinverse
isfile = os.path.isfile
normpath = os.path.normpath
#===============================================================================
# Define GLEmiter class
#===============================================================================
class GLEmiter(object):
'''This class is used to emit the contents of necessary files.'''
def __init__(self, config):
'''GLEmiter.__init__(config) -> GLEmiter
Create GLEmiter instance.'''
self.info = GLInfo()
if type(config) is not GLConfig:
raise TypeError('config must be a GLConfig, not %s'
% type(config).__name__)
self.config = config
def __repr__(self):
'''x.__repr__() <==> repr(x)'''
result = '' % hex(id(self))
return result
def copyright_notice(self):
'''GLEmiter.copyright_notice() -> str
Emit a header for a generated file.'''
emit = "# %s" % self.info.copyright()
emit += """
#
# 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.\n"""
return emit
def autoconfSnippet(self, module, toplevel,
disable_libtool, disable_gettext, replace_auxdir, indentation):
'''GLEmiter.autoconfSnippet(module, toplevel,
disable_libtool, disable_gettext, replace_auxdir, indentation) -> str
Emit the autoconf snippet of a module.
GLConfig: include_guard_prefix.
module is a GLModule instance, which is processed.
toplevel is a bool variable, False means a subordinate use of pygnulib.
disable_libtool is a bool variable; it tells whether to disable libtool
handling even if it has been specified through the GLConfig class.
disable_gettext is a bool variable; it tells whether to disable
AM_GNU_GETTEXT invocations.
replace_auxdir is a bool variable; it tells whether to replace
'build-aux' directory in AC_CONFIG_FILES.
indentation is a string which contain spaces to prepend on each line.'''
if type(module) is not GLModule:
raise TypeError('module must be a GLModule, not %s'
% type(module).__name__)
if type(toplevel) is not bool:
raise TypeError('toplevel must be a bool, not %s'
% type(toplevel).__name__)
if type(disable_libtool) is not bool:
raise TypeError('disable_libtool must be a bool, not %s'
% type(disable_libtool).__name__)
if type(disable_gettext) is not bool:
raise TypeError('disable_gettext must be a bool, not %s'
% type(disable_gettext).__name__)
if type(indentation) is not str:
raise TypeError('indentation must be a string, not %s'
% type(indentation).__name__)
if not indentation.isspace():
raise ValueError('indentation must contain only whitespaces')
auxdir = self.config['auxdir']
libtool = self.config['libtool']
include_guard_prefix = self.config['include_guard_prefix']
emit = ''
if str(module) in ['gnumakefile', 'maintainer-makefile']:
# These modules are meant to be used only in the top-level directory.
flag = toplevel
else: # if not str(module) in ['gnumakefile', 'maintainer-makefile']
flag = True
if flag:
snippet = module.getAutoconfSnippet()
snippet = snippet.replace('${gl_include_guard_prefix}',
include_guard_prefix)
lines = [ line
for line in snippet.split('\n')
if line.strip() ]
snippet = '%s\n' % '\n'.join(lines)
pattern = re.compile('^(.*)$', re.M)
snippet = pattern.sub('%s\\1' % indentation, snippet)
if disable_libtool:
snippet = snippet.replace('$gl_cond_libtool', 'false')
snippet = snippet.replace('gl_libdeps', 'gltests_libdeps')
snippet = snippet.replace('gl_ltlibdeps', 'gltests_ltlibdeps')
if disable_gettext:
snippet = snippet.replace('AM_GNU_GETTEXT([external])',
'dnl you must add AM_GNU_GETTEXT([external]) or similar to configure.ac.')
else:
# Don't indent AM_GNU_GETTEXT_VERSION line, as that confuses
# autopoint through at least GNU gettext version 0.18.2.
snippet = re.compile('^ *AM_GNU_GETTEXT_VERSION', re.M).sub('AM_GNU_GETTEXT_VERSION', snippet)
emit += snippet
if str(module) == 'alloca' and libtool and not disable_libtool:
emit += 'changequote(,)dnl\n'
emit += "LTALLOCA=`echo \"$ALLOCA\" | sed -e 's/\\.[^.]* /.lo /g;s/\\.[^.]*$/.lo/'`\n"
emit += 'changequote([, ])dnl\n'
emit += 'AC_SUBST([LTALLOCA])'
if replace_auxdir:
regex = 'AC_CONFIG_FILES\\(\\[(.*)\\:build-aux/(.*)\\]\\)'
repl = 'AC_CONFIG_FILES([\\1:%s/\\2])' % auxdir
pattern = re.compile(regex, re.M)
emit = pattern.sub(repl, emit)
lines = [ line
for line in emit.split('\n')
if line.strip() ]
emit = '%s\n' % '\n'.join(lines)
return emit
def autoconfSnippets(self, modules, moduletable,
verifier, toplevel, disable_libtool, disable_gettext, replace_auxdir):
'''GLEmiter.autoconfSnippets(modules,
verifier, toplevel, disable_libtool, disable_gettext, replace_auxdir) -> str
Collect and emit the autoconf snippets of a set of modules.
GLConfig: conddeps.
basemodules argument represents list of modules; every module in this list
must be a GLModule instance; this list of modules is used to sort all
modules after they were processed.
modules argument represents list of modules; every module in this list must
be a GLModule instance.
moduletable is a GLModuleTable instance, which contains necessary
information about dependencies of the modules.
verifier is an integer, which can be 0, 1 or 2.
if verifier == 0, then process every module;
if verifier == 1, then process only non-tests modules;
if verifier == 2, then process only tests modules.
toplevel is a bool variable, False means a subordinate use of pygnulib.
disable_libtool is a bool variable; it tells whether to disable libtool
handling even if it has been specified through the GLConfig class.
disable_gettext is a bool variable; it tells whether to disable
AM_GNU_GETTEXT invocations.
replace_auxdir is a bool variable; it tells whether to replace
'build-aux' directory in AC_CONFIG_FILES.'''
for module in modules:
if type(module) is not GLModule:
raise TypeError('each module must be a GLModule instance')
if type(moduletable) is not GLModuleTable:
raise TypeError('moduletable must be a GLFileAssistant, not %s'
% type(moduletable).__name__)
if type(verifier) is not int:
raise TypeError('verifier must be an int, not %s'
% type(verifier).__name__)
if not (0 <= verifier <= 2):
raise ValueError('verifier must be 0, 1 or 2, not %d' % verifier)
if type(toplevel) is not bool:
raise TypeError('toplevel must be a bool, not %s'
% type(toplevel).__name__)
if type(disable_libtool) is not bool:
raise TypeError('disable_libtool must be a bool, not %s'
% type(disable_libtool).__name__)
if type(disable_gettext) is not bool:
raise TypeError('disable_gettext must be a bool, not %s'
% type(disable_gettext).__name__)
if type(replace_auxdir) is not bool:
raise TypeError('replace_auxdir must be a bool, not %s'
% type(replace_auxdir).__name__)
auxdir = self.config['auxdir']
conddeps = self.config['conddeps']
macro_prefix = self.config['macro_prefix']
emit = ''
if not conddeps:
# Ignore the conditions, and enable all modules unconditionally.
for module in modules:
if verifier == 0:
solution = True
elif verifier == 1:
solution = module.isNonTests()
elif verifier == 2:
solution = module.isTests()
if solution:
emit += self.autoconfSnippet(module, toplevel,
disable_libtool, disable_gettext, replace_auxdir, ' ')
else: # if conddeps
# Emit the autoconf code for the unconditional modules.
for module in modules:
if verifier == 0:
solution = True
elif verifier == 1:
solution = module.isNonTests()
elif verifier == 2:
solution = module.isTests()
if solution:
if not moduletable.isConditional(module):
emit += self.autoconfSnippet(module, toplevel,
disable_libtool, disable_gettext, replace_auxdir, ' ')
# Initialize the shell variables indicating that the modules are enabled.
for module in modules:
if verifier == 0:
solution = True
elif verifier == 1:
solution = module.isNonTests()
elif verifier == 2:
solution = module.isTests()
if solution:
if moduletable.isConditional(module):
shellvar = module.getShellVar()
emit += ' %s=false\n' % module.getShellVar()
# Emit the autoconf code for the conditional modules, each in a separate
# function. This makes it possible to support cycles among conditional
# modules.
for module in modules:
if verifier == 0:
solution = True
elif verifier == 1:
solution = module.isNonTests()
elif verifier == 2:
solution = module.isTests()
if solution:
if moduletable.isConditional(module):
shellfunc = module.getShellFunc()
shellvar = module.getShellVar()
emit += ' %s ()\n' % shellfunc
emit += ' {\n'
emit += ' if $%s; then :; else\n' % shellvar
emit += self.autoconfSnippet(module, toplevel,
disable_libtool, disable_gettext, replace_auxdir, ' ')
emit += ' %s=true\n' % shellvar
depmodules = module.getDependenciesWithoutConditions()
# Intersect dependencies with the modules list.
depmodules = [ dep
for dep in depmodules
if dep in modules ] # TODO should this be basemodules or modules?
for depmodule in depmodules:
if moduletable.isConditional(depmodule):
shellfunc = depmodule.getShellFunc()
condition = moduletable.getCondition(module, depmodule)
if condition != None:
emit += ' if %s; then\n' % condition
emit += ' %s\n' % shellfunc
emit += ' fi\n'
else: # if condition == None
emit += ' %s\n' % shellfunc
# if not moduletable.isConditional(depmodule)
else:
# The autoconf code for $dep has already been emitted above and
# therefore is already executed when this code is run.
pass
emit += ' fi\n'
emit += ' }\n'
# Emit the dependencies from the unconditional to the conditional modules.
for module in modules:
if verifier == 0:
solution = True
elif verifier == 1:
solution = module.isNonTests()
elif verifier == 2:
solution = module.isTests()
if solution:
if not moduletable.isConditional(module):
depmodules = module.getDependenciesWithoutConditions()
# Intersect dependencies with the modules list.
depmodules = [ dep
for dep in depmodules
if dep in modules ] # TODO should this be basemodules or modules?
for depmodule in depmodules:
if moduletable.isConditional(depmodule):
shellfunc = depmodule.getShellFunc()
condition = moduletable.getCondition(module, depmodule)
if condition != None:
emit += ' if %s; then\n' % condition
emit += ' %s\n' % shellfunc
emit += ' fi\n'
else: # if condition == None
emit += ' %s\n' % shellfunc
# if not moduletable.isConditional(depmodule)
else:
# The autoconf code for $dep has already been emitted above and
# therefore is already executed when this code is run.
pass
# Define the Automake conditionals.
emit += ' m4_pattern_allow([^%s_GNULIB_ENABLED_])\n' % macro_prefix
for module in modules:
if verifier == 0:
solution = True
elif verifier == 1:
solution = module.isNonTests()
elif verifier == 2:
solution = module.isTests()
if solution:
if moduletable.isConditional(module):
condname = module.getConditionalName()
shellvar = module.getShellVar()
emit += ' AM_CONDITIONAL([%s], [$%s])\n' % (condname, shellvar)
lines = [ line
for line in emit.split('\n')
if line.strip() ]
emit = '%s\n' % '\n'.join(lines)
return emit
def preEarlyMacros(self, require, indentation, modules):
'''GLEmiter.preEarlyMacros(require, indentation, modules) -> str
Collect and emit the pre-early section.
require parameter can be True (AC_REQUIRE) or False (direct call).
indentation parameter is a string.
modules argument represents list of modules; every module in this list must
be a GLModule instance.'''
emit = '\n' + indentation + '# Pre-early section.\n'
# We need to call gl_USE_SYSTEM_EXTENSIONS before gl_PROG_AR_RANLIB.
# Doing AC_REQUIRE in configure-ac.early is not early enough.
if any(str(module) == 'extensions' for module in modules):
if require:
emit += indentation + 'AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])\n'
else:
emit += indentation + 'gl_USE_SYSTEM_EXTENSIONS\n'
if require:
emit += indentation + 'AC_REQUIRE([gl_PROG_AR_RANLIB])\n'
else:
emit += indentation + 'gl_PROG_AR_RANLIB\n'
emit += '\n'
return emit
def po_Makevars(self):
'''GLEmiter.po_Makevars() -> str
Emit the contents of po/ makefile parameterization.
GLConfig: pobase, podomain.'''
pobase = self.config['pobase']
podomain = self.config['podomain']
emit = ''
emit += "## DO NOT EDIT! GENERATED AUTOMATICALLY!\n"
emit += "%s\n" % self.copyright_notice()
emit += "# Usually the message domain is the same as the package name.\n"
emit += "# But here it has a '-gnulib' suffix.\n"
emit += "DOMAIN = %s-gnulib\n\n" % podomain
emit += "# These two variables depend on the location of this directory.\n"
emit += "subdir = %s\n" % pobase
emit += "top_builddir = %s\n" % relinverse(pobase)
emit += """
# 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\n"""
return emit
def po_POTFILES_in(self, files):
'''GLEmiter.po_POTFILES_in(files) -> str
Emit the file list to be passed to xgettext.
GLConfig: sourcebase.'''
sourcebase = self.config['sourcebase'] + os.path.sep
emit = ''
emit += "## DO NOT EDIT! GENERATED AUTOMATICALLY!\n"
emit += "%s\n" % self.copyright_notice()
emit += "# List of files which contain translatable strings.\n"
for file in files:
if file.startswith('lib/'):
emit += '%s\n' % constants.substart('lib/', sourcebase, file)
return emit
def initmacro_start(self, macro_prefix_arg):
'''GLEmiter.initmacro_start(macro_prefix_arg) -> str
Emit the first few statements of the gl_INIT macro.'''
if type(macro_prefix_arg) is not str:
raise TypeError('macro_prefix_arg must be a string, not %s'
% type(macro_prefix_arg).__name__)
module_indicator_prefix = self.config.getModuleIndicatorPrefix()
emit = ''
# 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.
emit += " m4_pushdef([AC_LIBOBJ], m4_defn([%s_LIBOBJ]))\n" % macro_prefix_arg
emit += " m4_pushdef([AC_REPLACE_FUNCS], m4_defn([%s_REPLACE_FUNCS]))\n" % macro_prefix_arg
# 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.
emit += " m4_pushdef([AC_LIBSOURCES], m4_defn([%s_LIBSOURCES]))\n" % macro_prefix_arg
# 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.
emit += " m4_pushdef([%s_LIBSOURCES_LIST], [])\n" % macro_prefix_arg
emit += " m4_pushdef([%s_LIBSOURCES_DIR], [])\n" % macro_prefix_arg
# Scope for m4 macros.
emit += " m4_pushdef([GL_MACRO_PREFIX], [%s])\n" % macro_prefix_arg
# Scope the GNULIB_ variables.
emit += " m4_pushdef([GL_MODULE_INDICATOR_PREFIX], [%s])\n" % module_indicator_prefix
emit += " gl_COMMON\n"
return emit
def initmacro_end(self, macro_prefix_arg):
'''GLEmiter.initmacro_end(macro_prefix_arg) -> str
Emit the last few statements of the gl_INIT macro.'''
if type(macro_prefix_arg) is not str:
raise TypeError('macro_prefix_arg must be a string, not %s'
% type(macro_prefix_arg).__name__)
emit = ''
# 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.
emit += r"""\
m4_ifval(%V1%_LIBSOURCES_LIST, [
m4_syscmd([test ! -d ]m4_defn([%V1%_LIBSOURCES_DIR])[ ||
for gl_file in ]%V1%_LIBSOURCES_LIST[ ; do
if test ! -r ]m4_defn([%V1%_LIBSOURCES_DIR])[/$gl_file ; then
echo "missing file ]m4_defn([%V1%_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([GL_MODULE_INDICATOR_PREFIX])
m4_popdef([GL_MACRO_PREFIX])
m4_popdef([%V1%_LIBSOURCES_DIR])
m4_popdef([%V1%_LIBSOURCES_LIST])
m4_popdef([AC_LIBSOURCES])
m4_popdef([AC_REPLACE_FUNCS])
m4_popdef([AC_LIBOBJ])
AC_CONFIG_COMMANDS_PRE([
%V1%_libobjs=
%V1%_ltlibobjs=
if test -n "$%V1%_LIBOBJS"; then
# Remove the extension.
sed_drop_objext='s/\.o$//;s/\.obj$//'
for i in `for i in $%V1%_LIBOBJS; do echo "$i"; done | sed -e "$sed_drop_objext" | sort | uniq`; do
%V1%_libobjs="$%V1%_libobjs $i.$ac_objext"
%V1%_ltlibobjs="$%V1%_ltlibobjs $i.lo"
done
fi
AC_SUBST([%V1%_LIBOBJS], [$%V1%_libobjs])
AC_SUBST([%V1%_LTLIBOBJS], [$%V1%_ltlibobjs])
])\n"""
emit = emit.replace('%V1%', macro_prefix_arg)
return emit
def initmacro_done(self, macro_prefix_arg, sourcebase_arg):
'''GLEmiter.initmacro_done(macro_prefix_arg, sourcebase_arg) -> str
Emit a few statements after the gl_INIT macro.
GLConfig: sourcebase.'''
if type(macro_prefix_arg) is not str:
raise TypeError('macro_prefix_arg must be a string, not %s'
% type(macro_prefix_arg).__name__)
if type(sourcebase_arg) is not str:
raise TypeError('sourcebase_arg must be a string, not %s'
% type(sourcebase_arg).__name__)
emit = ''
emit += """\
# Like AC_LIBOBJ, except that the module name goes
# into %V1%_LIBOBJS instead of into LIBOBJS.
AC_DEFUN([%V1%_LIBOBJ], [
AS_LITERAL_IF([$1], [%V1%_LIBSOURCES([$1.c])])dnl
%V1%_LIBOBJS="$%V1%_LIBOBJS $1.$ac_objext"
])
# Like AC_REPLACE_FUNCS, except that the module name goes
# into %V1%_LIBOBJS instead of into LIBOBJS.
AC_DEFUN([%V1%_REPLACE_FUNCS], [
m4_foreach_w([gl_NAME], [$1], [AC_LIBSOURCES(gl_NAME[.c])])dnl
AC_CHECK_FUNCS([$1], , [%V1%_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([%V1%_LIBSOURCES], [
m4_foreach([_gl_NAME], [$1], [
m4_if(_gl_NAME, [alloca.c], [], [
m4_define([%V1%_LIBSOURCES_DIR], [%V2%])
m4_append([%V1%_LIBSOURCES_LIST], _gl_NAME, [ ])
])
])
])\n"""
emit = emit.replace('%V1%', macro_prefix_arg)
emit = emit.replace('%V2%', sourcebase_arg)
return emit
def lib_Makefile_am(self, destfile, modules,
moduletable, makefiletable, actioncmd, for_test):
'''GLEmiter.lib_Makefile_am(destfile, modules, moduletable, makefiletable,
actioncmd, for_test) -> tuple of str and bool
Emit the contents of the library Makefile. Returns str and a bool
variable which shows if subdirectories are used.
GLConfig: localpath, sourcebase, libname, pobase, auxdir, makefile_name, libtool,
macro_prefix, podomain, conddeps, witness_c_macro.
destfile is a filename relative to destdir of Makefile being generated.
modules is a list of GLModule instances.
moduletable is a GLModuleTable instance.
makefiletable is a GLMakefileTable instance.
actioncmd is a string variable, which represents the actioncmd; it can be
an empty string e.g. when user wants to generate files for GLTestDir.
for_test is a bool variable; it must be set to True if creating a package
for testing, False otherwise.'''
if type(destfile) is not str:
raise TypeError('destfile must be a string, not %s'
% type(destfile).__name__)
for module in modules:
if type(module) is not GLModule:
raise TypeError('each module must be a GLModule instance')
if type(moduletable) is not GLModuleTable:
raise TypeError('moduletable must be a GLModuleTable, not %s'
% type(moduletable).__name__)
if type(makefiletable) is not GLMakefileTable:
raise TypeError('makefiletable must be a GLMakefileTable, not %s'
% type(makefiletable).__name__)
if type(actioncmd) is not str:
raise TypeError('actioncmd must be a string, not %s'
% type(actioncmd).__name__)
if type(for_test) is not bool:
raise TypeError('for_test must be a bool, not %s'
% type(for_test).__name__)
sourcebase = self.config['sourcebase']
libname = self.config['libname']
pobase = self.config['pobase']
auxdir = self.config['auxdir']
makefile_name = self.config['makefile_name']
libtool = self.config['libtool']
macro_prefix = self.config['macro_prefix']
podomain = self.config['podomain']
conddeps = self.config['conddeps']
witness_c_macro = self.config['witness_c_macro']
include_guard_prefix = self.config['include_guard_prefix']
module_indicator_prefix = self.config.getModuleIndicatorPrefix()
ac_version = self.config['ac_version']
destfile = os.path.normpath(destfile)
emit = ''
# When creating an includable Makefile.am snippet, augment variables with
# += instead of assigning them.
if makefile_name:
assign = '+='
else: # if not makefile_name
assign = '='
if libtool:
libext = 'la'
perhapsLT = 'LT'
eliminate_LDFLAGS = False
else: # if not libtool
libext = 'a'
perhapsLT = ''
eliminate_LDFLAGS = True
if for_test:
# 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.
edit_check_PROGRAMS = True
else: # if not for_test
edit_check_PROGRAMS = False
emit += "## DO NOT EDIT! GENERATED AUTOMATICALLY!\n"
emit += "## Process this file with automake to produce Makefile.in.\n"
emit += self.copyright_notice()
if actioncmd:
# 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.
if len(actioncmd) <= 3000:
emit += "# Reproduce by: %s\n" % actioncmd
emit += '\n'
uses_subdirs = False
# Compute allsnippets variable.
allsnippets = ''
for module in modules:
if not module.isTests():
# Get conditional snippet, edit it and save to amsnippet1.
amsnippet1 = module.getAutomakeSnippet_Conditional()
amsnippet1 = amsnippet1.replace('lib_LIBRARIES', 'lib%_LIBRARIES')
amsnippet1 = amsnippet1.replace('lib_LTLIBRARIES', 'lib%_LTLIBRARIES')
if eliminate_LDFLAGS:
pattern = re.compile('^(lib_LDFLAGS[\t ]*\\+=.*$\n)', re.M)
amsnippet1 = pattern.sub('', amsnippet1)
pattern = re.compile('lib_([A-Z][A-Z]*)', re.M)
amsnippet1 = pattern.sub('%s_%s_\\1' % (libname, libext),
amsnippet1)
amsnippet1 = amsnippet1.replace('$(GNULIB_', '$(' + module_indicator_prefix + '_GNULIB_')
amsnippet1 = amsnippet1.replace('lib%_LIBRARIES', 'lib_LIBRARIES')
amsnippet1 = amsnippet1.replace('lib%_LTLIBRARIES', 'lib_LTLIBRARIES')
if edit_check_PROGRAMS:
amsnippet1 = amsnippet1.replace('check_PROGRAMS', 'noinst_PROGRAMS')
amsnippet1 = amsnippet1.replace('${gl_include_guard_prefix}',
include_guard_prefix)
if str(module) == 'alloca':
amsnippet1 += '%s_%s_LIBADD += @%sALLOCA@\n' % (libname, libext, perhapsLT)
amsnippet1 += '%s_%s_DEPENDENCIES += @%sALLOCA@\n' % (libname, libext, perhapsLT)
amsnippet1 = constants.combine_lines_matching(re.compile('%s_%s_SOURCES' % (libname, libext)),
amsnippet1)
# Get unconditional snippet, edit it and save to amsnippet2.
amsnippet2 = module.getAutomakeSnippet_Unconditional()
pattern = re.compile('lib_([A-Z][A-Z]*)', re.M)
amsnippet2 = pattern.sub('%s_%s_\\1' % (libname, libext),
amsnippet2)
amsnippet2 = amsnippet2.replace('$(GNULIB_',
'$(' + module_indicator_prefix + '_GNULIB_')
# Skip the contents if it's entirely empty.
if not (amsnippet1 + amsnippet2).isspace():
allsnippets += '## begin gnulib module %s\n\n' % str(module)
if conddeps:
if moduletable.isConditional(module):
name = module.getConditionalName()
allsnippets += 'if %s\n' % name
allsnippets += amsnippet1
if conddeps:
if moduletable.isConditional(module):
allsnippets += 'endif\n'
allsnippets += amsnippet2
allsnippets += '## end gnulib module %s\n\n' % str(module)
# Test whether there are some source files in subdirectories.
for file in module.getFiles():
if (file.startswith('lib/')
and file.endswith('.c')
and file.count('/') > 1):
uses_subdirs = True
break
if not makefile_name:
subdir_options = ''
# If there are source files in subdirectories, prevent collision of the
# object files (example: hash.c and libxml/hash.c).
if uses_subdirs:
subdir_options = ' subdir-objects'
emit += 'AUTOMAKE_OPTIONS = 1.9.6 gnits%s\n' % subdir_options
emit += '\n'
if not makefile_name:
emit += 'SUBDIRS =\n'
emit += 'noinst_HEADERS =\n'
emit += 'noinst_LIBRARIES =\n'
emit += 'noinst_LTLIBRARIES =\n'
# 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:
# * https://debbugs.gnu.org/10997
# * https://debbugs.gnu.org/11030
# So we need this workaround.
pattern = re.compile('^pkgdata_DATA *\\+=', re.M)
if pattern.findall(allsnippets):
emit += 'pkgdata_DATA =\n'
emit += 'EXTRA_DIST =\n'
emit += 'BUILT_SOURCES =\n'
emit += 'SUFFIXES =\n'
emit += 'MOSTLYCLEANFILES %s core *.stackdump\n' % assign
if not makefile_name:
emit += 'MOSTLYCLEANDIRS =\n'
emit += 'CLEANFILES =\n'
emit += 'DISTCLEANFILES =\n'
emit += 'MAINTAINERCLEANFILES =\n'
# Execute edits that apply to the Makefile.am being generated.
for current_edit in range(0, makefiletable.count()):
dictionary = makefiletable[current_edit]
if dictionary['var']:
if destfile == joinpath(dictionary['dir'], 'Makefile.am'):
emit += '%s += %s\n' % (dictionary['var'], dictionary['val'])
dictionary.pop('var')
# Define two parts of cppflags variable.
cppflags_part1 = ''
if witness_c_macro:
cppflags_part1 = ' -D%s=1' % witness_c_macro
cppflags_part2 = ''
if for_test:
cppflags_part2 = ' -DGNULIB_STRICT_CHECKING=1'
cppflags = '%s%s' % (cppflags_part1, cppflags_part2)
if not makefile_name:
emit += '\n'
emit += 'AM_CPPFLAGS =%s\n' % cppflags
emit += 'AM_CFLAGS =\n'
else: # if makefile_name
if cppflags:
emit += '\n'
emit += 'AM_CPPFLAGS +=%s\n' % cppflags
emit += '\n'
# Test if 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.
# First test if allsnippets already specify an installation location.
lib_gets_installed = False
regex = '^[a-zA-Z0-9_]*_%sLIBRARIES *\\+{0,1}= *%s\\.%s' % (perhapsLT, libname, libext)
pattern = re.compile(regex, re.M)
if pattern.findall(allsnippets):
lib_gets_installed = True
else:
# Then test if $sourcebase/Makefile.am (if it exists) specifies it.
if makefile_name:
path = joinpath(sourcebase, 'Makefile.am')
if isfile(path):
with codecs.open(path, 'rb', 'UTF-8') as file:
data = file.read()
if pattern.findall(data):
lib_gets_installed = True
if not lib_gets_installed:
# By default, the generated library should not be installed.
emit += 'noinst_%sLIBRARIES += %s.%s\n' % (perhapsLT, libname, libext)
emit += '\n'
emit += '%s_%s_SOURCES =\n' % (libname, libext)
# Here we use $(LIBOBJS), not @LIBOBJS@. The value is the same. However,
# automake during its analysis looks for $(LIBOBJS), not for @LIBOBJS@.
emit += '%s_%s_LIBADD = $(%s_%sLIBOBJS)\n' % (libname, libext, macro_prefix, perhapsLT)
emit += '%s_%s_DEPENDENCIES = $(%s_%sLIBOBJS)\n' % (libname, libext, macro_prefix, perhapsLT)
emit += 'EXTRA_%s_%s_SOURCES =\n' % (libname, libext)
if libtool:
emit += '%s_%s_LDFLAGS = $(AM_LDFLAGS)\n' % (libname, libext)
emit += '%s_%s_LDFLAGS += -no-undefined\n' % (libname, libext)
# Synthesize an ${libname}_${libext}_LDFLAGS augmentation by combining
# the link dependencies of all modules.
links = [ module.getLink()
for module in modules
if not module.isTests() ]
lines = [ line
for link in links
for line in link.split('\n')
if line != '' ]
pattern = re.compile(' when linking with libtool.*')
lines = [ pattern.sub('', line)
for line in lines ]
lines = sorted(set(lines))
for line in lines:
emit += '%s_%s_LDFLAGS += %s\n' % (libname, libext, line)
emit += '\n'
if pobase:
emit += 'AM_CPPFLAGS += -DDEFAULT_TEXT_DOMAIN=\\"%s-gnulib\\"\n' % podomain
emit += '\n'
allsnippets = allsnippets.replace('$(top_srcdir)/build-aux/',
'$(top_srcdir)/%s/' % auxdir)
emit += allsnippets
emit += '\n'
emit += 'mostlyclean-local: mostlyclean-generic\n'
emit += '\t@for dir in \'\' $(MOSTLYCLEANDIRS); do \\\n'
emit += '\t if test -n "$$dir" && test -d $$dir; then \\\n'
emit += '\t echo "rmdir $$dir"; rmdir $$dir; \\\n'
emit += '\t fi; \\\n'
emit += '\tdone; \\\n'
emit += '\t:\n'
result = tuple([emit, uses_subdirs])
return result
def tests_Makefile_am(self, destfile, modules, makefiletable,
witness_macro, for_test):
'''GLEmiter.tests_Makefile_am(destfile, modules, makefiletable,
witness_c_macro, for_test) -> tuple of string and bool
Emit the contents of the tests Makefile. Returns str and a bool variable
which shows if subdirectories are used.
GLConfig: localpath, modules, libname, auxdir, makefile_name, libtool,
sourcebase, m4base, testsbase, macro_prefix, witness_c_macro,
single_configure, libtests.
destfile is a filename relative to destdir of Makefile being generated.
witness_macro is a string which represents witness_c_macro with the suffix.
modules is a list of GLModule instances.
moduletable is a GLModuleTable instance.
makefiletable is a GLMakefileTable instance.
actioncmd is a string variable, which represents the actioncmd; it can be
an empty string e.g. when user wants to generate files for GLTestDir.
for_test is a bool variable; it must be set to True if creating a package
for testing, False otherwise.'''
if type(destfile) is not str:
raise TypeError('destfile must be a string, not %s'
% type(destfile).__name__)
for module in modules:
if type(module) is not GLModule:
raise TypeError('each module must be a GLModule instance')
if type(makefiletable) is not GLMakefileTable:
raise TypeError('makefiletable must be a GLMakefileTable, not %s'
% type(makefiletable).__name__)
if type(witness_macro) is not str:
raise TypeError('witness_macro must be a string, not %s'
% type(witness_macro).__name__)
if type(for_test) is not bool:
raise TypeError('for_test must be a bool, not %s'
% type(for_test).__name__)
auxdir = self.config['auxdir']
sourcebase = self.config['sourcebase']
libname = self.config['libname']
m4base = self.config['m4base']
pobase = self.config['pobase']
testsbase = self.config['testsbase']
makefile_name = self.config['makefile_name']
libtool = self.config['libtool']
macro_prefix = self.config['macro_prefix']
podomain = self.config['podomain']
conddeps = self.config['conddeps']
witness_c_macro = self.config['witness_c_macro']
include_guard_prefix = self.config['include_guard_prefix']
module_indicator_prefix = self.config.getModuleIndicatorPrefix()
ac_version = self.config['ac_version']
libtests = self.config['libtests']
single_configure = self.config['single_configure']
emit = ''
if libtool:
libext = 'la'
perhapsLT = 'LT'
eliminate_LDFLAGS = False
else: # if not libtool
libext = 'a'
perhapsLT = ''
eliminate_LDFLAGS = True
if for_test:
# 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.
edit_check_PROGRAMS = True
else: # if not for_test
edit_check_PROGRAMS = False
# Compute testsbase_inverse
testsbase_inverse = relinverse(testsbase)
# Begin the generation.
emit += "## DO NOT EDIT! GENERATED AUTOMATICALLY!\n"
emit += "## Process this file with automake to produce Makefile.in.\n"
emit += '%s\n' % self.copyright_notice()
uses_subdirs = False
main_snippets = ''
longrun_snippets = ''
for module in modules:
if for_test and not single_configure:
accept = module.isTests()
else: # if for_test and not single_configure
accept = True
if accept:
snippet = module.getAutomakeSnippet()
snippet = snippet.replace('lib_LIBRARIES', 'lib%_LIBRARIES')
snippet = snippet.replace('lib_LTLIBRARIES', 'lib%_LTLIBRARIES')
if eliminate_LDFLAGS:
pattern = re.compile('^(lib_LDFLAGS[\t ]*\\+=.*$\n)', re.M)
amsnippet1 = pattern.sub('', snippet)
pattern = re.compile('lib_([A-Z][A-Z]*)', re.M)
snippet = pattern.sub('libtests_a_\\1', snippet)
snippet = snippet.replace('$(GNULIB_', '$(' + module_indicator_prefix + '_GNULIB_')
snippet = snippet.replace('lib%_LIBRARIES', 'lib_LIBRARIES')
snippet = snippet.replace('lib%_LTLIBRARIES', 'lib_LTLIBRARIES')
if edit_check_PROGRAMS:
snippet = snippet.replace('check_PROGRAMS', 'noinst_PROGRAMS')
snippet = snippet.replace('${gl_include_guard_prefix}',
include_guard_prefix)
# Check if module is 'alloca'.
if libtests and str(module) == 'alloca':
snippet += 'libtests_a_LIBADD += @%sALLOCA@\n' % perhapsLT
snippet += 'libtests_a_DEPENDENCIES += @%sALLOCA@\n' % perhapsLT
# Skip the contents if it's entirely empty.
if not snippet.isspace():
snippet = ('## begin gnulib module %s\n\n' % str(module)
+ snippet
+ '## end gnulib module %s\n\n' % str(module))
# Mention long-running tests at the end.
if 'longrunning-test' in module.getStatuses():
longrun_snippets += snippet
else:
main_snippets += snippet
# Test whether there are some source files in subdirectories.
for file in module.getFiles():
if ((file.startswith('lib/') or file.startswith('tests/'))
and file.endswith('.c')
and file.count('/') > 1):
uses_subdirs = True
break
# 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 = ''
if uses_subdirs:
subdir_options = ' subdir-objects'
emit += 'AUTOMAKE_OPTIONS = 1.9.6 foreign%s\n\n' % subdir_options
if for_test and not single_configure:
emit += 'ACLOCAL_AMFLAGS = -I %s/%s\n\n' % (testsbase_inverse, m4base)
# 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"
emit += 'SUBDIRS = .\n'
emit += 'TESTS =\n'
emit += 'XFAIL_TESTS =\n'
emit += 'TESTS_ENVIRONMENT =\n'
emit += 'noinst_PROGRAMS =\n'
if not for_test:
emit += 'check_PROGRAMS =\n'
emit += 'noinst_HEADERS =\n'
emit += 'noinst_LIBRARIES =\n'
if libtests:
if for_test:
emit += 'noinst_LIBRARIES += libtests.a\n'
else: # if not for_test
emit += 'check_LIBRARIES = libtests.a\n'
# 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:
# * https://debbugs.gnu.org/10997
# * https://debbugs.gnu.org/11030
# So we need this workaround.
pattern = re.compile('^pkgdata_DATA *\\+=', re.M)
if pattern.findall(main_snippets) or pattern.findall(longrun_snippets):
emit += 'pkgdata_DATA =\n'
emit += 'EXTRA_DIST =\n'
emit += 'BUILT_SOURCES =\n'
emit += 'SUFFIXES =\n'
emit += 'MOSTLYCLEANFILES = core *.stackdump\n'
emit += 'MOSTLYCLEANDIRS =\n'
emit += 'CLEANFILES =\n'
emit += 'DISTCLEANFILES =\n'
emit += 'MAINTAINERCLEANFILES =\n'
# Execute edits that apply to the Makefile.am being generated.
for current_edit in range(0, makefiletable.count()):
dictionary = makefiletable[current_edit]
if dictionary['var']:
if destfile == joinpath(dictionary['dir'], 'Makefile.am'):
emit += '%s += %s\n' % (dictionary['var'], dictionary['val'])
dictionary.pop('var')
emit += '\nAM_CPPFLAGS = \\\n'
if for_test:
emit += ' -DGNULIB_STRICT_CHECKING=1 \\\n'
if witness_c_macro:
emit += ' -D%s=1 \\\n' % witness_c_macro
if witness_macro:
emit += ' -D@%s@=1 \\\n' % witness_macro
emit += ' -I. -I$(srcdir) \\\n'
emit += ' -I%s -I$(srcdir)/%s \\\n' % (testsbase_inverse, testsbase_inverse)
emit += ' -I%s/%s -I$(srcdir)/%s/%s\n' % (testsbase_inverse, sourcebase, testsbase_inverse, sourcebase)
emit += '\n'
ldadd_before = ''
ldadd_after = ''
if libtests:
# 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
# 'version-etc' module whose dependency to 'version-etc-fsf' is
# voluntarily omitted).
# The LIBTESTS_LIBDEPS can be passed to the linker once or twice, it
# does not matter.
ldadd_before = ' libtests.a'
ldadd_after = ' libtests.a $(LIBTESTS_LIBDEPS)'
emit += 'LDADD =%s %s/%s/%s.%s libtests.a %s/%s/%s.%s%s\n\n' \
% (ldadd_before,
testsbase_inverse, sourcebase, libname, libext,
testsbase_inverse, sourcebase, libname, libext,
ldadd_after)
if libtests:
emit += 'libtests_a_SOURCES =\n'
# Here we use $(LIBOBJS), not @LIBOBJS@. The value is the same. However,
# automake during its analysis looks for $(LIBOBJS), not for @LIBOBJS@.
emit += 'libtests_a_LIBADD = $(%stests_LIBOBJS)\n' % macro_prefix
emit += 'libtests_a_DEPENDENCIES = $(%stests_LIBOBJS)\n' % macro_prefix
emit += 'EXTRA_libtests_a_SOURCES =\n'
# The circular dependency in LDADD requires this.
emit += 'AM_LIBTOOLFLAGS = --preserve-dup-deps\n\n'
# Many test scripts use ${EXEEXT} or ${srcdir}.
# EXEEXT is defined by AC_PROG_CC through autoconf.
# srcdir is defined by autoconf and automake.
emit += "TESTS_ENVIRONMENT += EXEEXT='@EXEEXT@' srcdir='$(srcdir)'\n\n"
all_snippets = main_snippets + longrun_snippets
all_snippets = all_snippets.replace('$(top_srcdir)/build-aux/',
'$(top_srcdir)/%s/' % auxdir)
emit += all_snippets
emit += '# Clean up after Solaris cc.\n'
emit += 'clean-local:\n'
emit += '\trm -rf SunWS_cache\n\n'
emit += 'mostlyclean-local: mostlyclean-generic\n'
emit += '\t@for dir in \'\' $(MOSTLYCLEANDIRS); do \\\n'
emit += '\t if test -n "$$dir" && test -d $$dir; then \\\n'
emit += '\t echo "rmdir $$dir"; rmdir $$dir; \\\n'
emit += '\t fi; \\\n'
emit += '\tdone; \\\n'
emit += '\t:\n'
result = tuple([emit, uses_subdirs])
return result