# 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