summaryrefslogtreecommitdiff
path: root/pygnulib
diff options
context:
space:
mode:
authorBruno Haible <bruno@clisp.org>2022-08-10 00:51:59 +0200
committerBruno Haible <bruno@clisp.org>2022-08-10 00:53:44 +0200
commit62fa8fc7b2c9db14d8c24d6ec5beedecb27b4802 (patch)
treea9a6b6b66960a69c00ed74aaca0e18b63d7438a1 /pygnulib
parentbc3d94bb2df6fa0c766f6226814c2fc2a55ee049 (diff)
downloadgnulib-62fa8fc7b2c9db14d8c24d6ec5beedecb27b4802.tar.gz
gnulib-tool.py: Finish implementing option --conditional-dependencies.
* gnulib-tool.py (main) Accept options --conditional-dependencies, --no-conditional-dependencies. * pygnulib/GLModuleSystem.py (GLModuleTable.addConditional): Use str(module), not module, as key. Fix logic bug. (GLModuleTable.getCondition): Simplify. (GLModuleTable.transitive_closure): Show a warning when there are duplicate dependencies. Fix logic bug. (GLModuleTable.transitive_closure_separately): Simplify. (GLModuleTable.add_dummy): Ignore tests modules. Cope with multiple lib_SOURCES augmentation lines. Cope with comments at the end of a lib_SOURCES augmentation line. Add the dummy module at the end of the modules list. * pygnulib/GLTestDir.py (GLTestDir.execute): Remove the code that forces the dummy module to the end of the list. * pygnulib/GLEmiter.py (GLEmiter.autoconfSnippets): Add code to terminate the shell functions. Add code for the dependencies from the unconditional to the conditional modules. Don't emit AM_CONDITIONAL for unconditional modules.
Diffstat (limited to 'pygnulib')
-rw-r--r--pygnulib/GLEmiter.py44
-rw-r--r--pygnulib/GLImport.py3
-rw-r--r--pygnulib/GLModuleSystem.py100
-rw-r--r--pygnulib/GLTestDir.py24
4 files changed, 116 insertions, 55 deletions
diff --git a/pygnulib/GLEmiter.py b/pygnulib/GLEmiter.py
index 9b54d5009e..c0fd053077 100644
--- a/pygnulib/GLEmiter.py
+++ b/pygnulib/GLEmiter.py
@@ -202,7 +202,6 @@ class GLEmiter(object):
AM_GNU_GETTEXT invocations.
replace_auxdir is a bool variable; it tells whether to replace
'build-aux' directory in AC_CONFIG_FILES.'''
- emit = ''
for module in modules:
if type(module) is not GLModule:
raise TypeError('each module must be a GLModule instance')
@@ -229,6 +228,7 @@ class GLEmiter(object):
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:
@@ -290,7 +290,39 @@ class GLEmiter(object):
# Intersect dependencies with the modules list.
depmodules = [ dep
for dep in depmodules
- if dep in modules ]
+ 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()
@@ -316,14 +348,14 @@ class GLEmiter(object):
elif verifier == 2:
solution = module.isTests()
if solution:
- condname = module.getConditionalName()
- shellvar = module.getShellVar()
- emit += ' AM_CONDITIONAL([%s], [$%s])\n' % (condname, shellvar)
+ 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)
- emit = constants.nlconvert(emit)
return emit
def preEarlyMacros(self, require, indentation, modules):
diff --git a/pygnulib/GLImport.py b/pygnulib/GLImport.py
index 114605dd8a..5dd73fa8bd 100644
--- a/pygnulib/GLImport.py
+++ b/pygnulib/GLImport.py
@@ -831,9 +831,10 @@ AC_DEFUN([%s_FILE_LIST], [\n''' % macro_prefix
if libtests:
self.config.setLibtests(True)
- # Add dummy package if it is needed.
+ # Add the dummy module to the main module list if needed.
main_modules = self.moduletable.add_dummy(main_modules)
if libtests: # if we need to use libtests.a
+ # Add the dummy module to the tests-related module list if needed.
tests_modules = self.moduletable.add_dummy(tests_modules)
# Check license incompatibilities.
diff --git a/pygnulib/GLModuleSystem.py b/pygnulib/GLModuleSystem.py
index 9bfd5bd266..f9b47a64a5 100644
--- a/pygnulib/GLModuleSystem.py
+++ b/pygnulib/GLModuleSystem.py
@@ -705,6 +705,18 @@ class GLModuleTable(object):
inc_all_indirect_tests = True if all kinds of problematic unit tests
among the unit tests of the dependencies
should be included
+
+ Methods for conditional dependencies:
+ - addUnconditional(B)
+ notes the presence of B as an unconditional module.
+ - addConditional(A, B. cond)
+ notes the presence of a conditional dependency from module A to module B,
+ subject to the condition that A is enabled and cond is true.
+ - isConditional(B)
+ tests whether module B is conditional.
+ - getCondition(A, B)
+ returns the condition when B should be enabled as a dependency of A,
+ once the m4 code for A has been executed.
'''
self.dependers = dict() # Dependencies
self.conditionals = dict() # Conditional modules
@@ -765,9 +777,11 @@ class GLModuleTable(object):
raise TypeError('condition must be a string or True, not %s'
% type(condition).__name__)
if not str(module) in self.unconditionals:
+ # No unconditional dependency to the given module is known at this point.
if str(module) not in self.dependers:
- self.dependers[module] = list()
- self.dependers[module] += [module]
+ self.dependers[str(module)] = list()
+ if str(parent) not in self.dependers[str(module)]:
+ self.dependers[str(module)].append(str(parent))
key = '%s---%s' % (str(parent), str(module))
self.conditionals[key] = condition
@@ -778,9 +792,9 @@ class GLModuleTable(object):
if type(module) is not GLModule:
raise TypeError('module must be a GLModule, not %s'
% type(module).__name__)
+ self.unconditionals[str(module)] = True
if str(module) in self.dependers:
self.dependers.pop(str(module))
- self.unconditionals[str(module)] = True
def isConditional(self, module):
'''GLModuleTable.isConditional(module) -> bool
@@ -804,9 +818,7 @@ class GLModuleTable(object):
raise TypeError('module must be a GLModule, not %s'
% type(module).__name__)
key = '%s---%s' % (str(parent), str(module))
- result = None
- if key in self.conditionals:
- result = self.conditionals[key]
+ result = self.conditionals.get(key, None)
return result
def transitive_closure(self, modules):
@@ -823,6 +835,11 @@ class GLModuleTable(object):
for module in modules:
if type(module) is not GLModule:
raise TypeError('each module must be a GLModule instance')
+ # In order to process every module only once (for speed), process an
+ # "input list" of modules, producing an "output list" of modules. During
+ # each round, more modules can be queued in the input list. Once a
+ # module on the input list has been processed, it is added to the
+ # "handled list", so we can avoid to process it again.
inc_all_tests = self.inc_all_direct_tests
handledmodules = list()
inmodules = modules
@@ -833,7 +850,7 @@ class GLModuleTable(object):
self.addUnconditional(module)
while inmodules:
inmodules_this_round = inmodules
- inmodules = list()
+ inmodules = list() # Accumulator, queue for next round
for module in inmodules_this_round:
if module not in self.avoids:
outmodules += [module]
@@ -842,6 +859,13 @@ class GLModuleTable(object):
module.getAutomakeSnippet_Conditional()
pattern = re.compile('^if', re.M)
if not pattern.findall(automake_snippet):
+ # A module whose Makefile.am snippet contains a
+ # reference to an automake conditional. If we were
+ # to use it conditionally, we would get an error
+ # configure: error: conditional "..." was never defined.
+ # because automake 1.11.1 does not handle nested
+ # conditionals correctly. As a workaround, make the
+ # module unconditional.
self.addUnconditional(module)
conditional = self.isConditional(module)
dependencies = module.getDependenciesWithConditions()
@@ -849,6 +873,16 @@ class GLModuleTable(object):
for pair in dependencies ]
conditions = [ pair[1]
for pair in dependencies ]
+ # Duplicate dependencies are harmless, but Jim wants a warning.
+ duplicate_depmodules = [ depmodule
+ for depmodule in set(depmodules)
+ if depmodules.count(depmodule) > 1 ]
+ if duplicate_depmodules:
+ duplicate_depmodule_names = [ str(depmodule)
+ for depmodule in duplicate_depmodules ]
+ message = ('gnulib-tool: warning: module %s has duplicated dependencies: %s\n'
+ % (module, duplicate_depmodule_names))
+ sys.stderr.write(message)
if self.config.checkInclTestCategory(TESTS['tests']):
testsname = module.getTestsName()
if self.modulesystem.exists(testsname):
@@ -856,6 +890,7 @@ class GLModuleTable(object):
depmodules += [testsmodule]
conditions += [None]
for depmodule in depmodules:
+ # Determine whether to include the dependency or tests module.
include = True
statuses = depmodule.getStatuses()
for word in statuses:
@@ -890,15 +925,15 @@ class GLModuleTable(object):
if self.config['conddeps']:
index = depmodules.index(depmodule)
condition = conditions[index]
+ if condition == True:
+ condition = None
if condition:
self.addConditional(module, depmodule, condition)
else: # if condition
if conditional:
self.addConditional(module, depmodule, True)
else: # if not conditional
- self.addUnconditional(module)
- listing = list() # Create empty list
- inmodules = sorted(set(inmodules))
+ self.addUnconditional(depmodule)
handledmodules = sorted(set(handledmodules + inmodules_this_round))
# Remove handledmodules from inmodules.
inmodules = [module
@@ -934,17 +969,24 @@ class GLModuleTable(object):
for module in finalmodules:
if type(module) is not GLModule:
raise TypeError('each module must be a GLModule instance')
+ # Determine main module list.
saved_inctests = self.config.checkInclTestCategory(TESTS['tests'])
self.config.disableInclTestCategory(TESTS['tests'])
main_modules = self.transitive_closure(basemodules)
self.config.setInclTestCategory(TESTS['tests'], saved_inctests)
+ # Determine tests-related module list.
tests_modules = \
[ m
for m in finalmodules
- if m not in main_modules ] \
- + [ m
- for m in main_modules
- if m.getApplicability() != 'main' ]
+ if not (m in main_modules and m.getApplicability() == 'main') ]
+ # Note: Since main_modules is (hopefully) a subset of finalmodules, this
+ # ought to be the same as
+ # [ m
+ # for m in finalmodules
+ # if m not in main_modules ] \
+ # + [ m
+ # for m in main_modules
+ # if m.getApplicability() != 'main' ]
tests_modules = sorted(set(tests_modules))
result = tuple([main_modules, tests_modules])
return result
@@ -957,24 +999,30 @@ class GLModuleTable(object):
GLConfig: auxdir, ac_version.'''
auxdir = self.config['auxdir']
ac_version = self.config['ac_version']
- have_lib_sources = False
for module in modules:
if type(module) is not GLModule:
raise TypeError('each module must be a GLModule instance')
- snippet = module.getAutomakeSnippet()
- snippet = constants.remove_backslash_newline(snippet)
- pattern = re.compile('^lib_SOURCES[\t ]*\\+=[\t ]*(.*)$', re.M)
- files = pattern.findall(snippet)
- if files: # if source files were found
- files = files[-1].split(' ')
- for file in files:
- if not file.endswith('.h'):
- have_lib_sources = True
- break
+ # Determine whether any module provides a lib_SOURCES augmentation.
+ have_lib_sources = False
+ for module in modules:
+ if not module.isTests():
+ snippet = module.getAutomakeSnippet()
+ # Extract the value of "lib_SOURCES += ...".
+ snippet = constants.remove_backslash_newline(snippet)
+ pattern = re.compile('^lib_SOURCES[\t ]*\\+=([^#]*).*$', re.M)
+ for matching_rhs in pattern.findall(snippet):
+ files = matching_rhs.split(' ')
+ for file in files:
+ # Ignore .h files since they are not compiled.
+ if not file.endswith('.h'):
+ have_lib_sources = True
+ break
+ # Add the dummy module, to make sure the library will be non-empty.
if not have_lib_sources:
dummy = self.modulesystem.find('dummy')
if dummy not in self.avoids:
- modules = sorted(set(modules + [dummy]))
+ if dummy not in modules:
+ modules = sorted(set(modules)) + [dummy]
return list(modules)
def filelist(self, modules):
diff --git a/pygnulib/GLTestDir.py b/pygnulib/GLTestDir.py
index 3c4ad0b9dc..bf00099283 100644
--- a/pygnulib/GLTestDir.py
+++ b/pygnulib/GLTestDir.py
@@ -265,33 +265,13 @@ class GLTestDir(object):
self.config.setLibtests(True)
if single_configure:
- # Add dummy package if it is needed.
+ # Add the dummy module to the main module list if needed.
main_modules = moduletable.add_dummy(main_modules)
- if 'dummy' in [ str(module)
- for module in main_modules ]:
- main_modules = [ m
- for m in main_modules
- if str(m) != 'dummy' ]
- dummy = self.modulesystem.find('dummy')
- main_modules = sorted(set(main_modules)) + [dummy]
if libtests: # if we need to use libtests.a
+ # Add the dummy module to the tests-related module list if needed.
tests_modules = moduletable.add_dummy(tests_modules)
- if 'dummy' in [ str(module)
- for module in tests_modules ]:
- tests_modules = [ m
- for m in tests_modules
- if str(m) != 'dummy' ]
- dummy = self.modulesystem.find('dummy')
- tests_modules = sorted(set(tests_modules)) + [dummy]
else: # if not single_configure
modules = moduletable.add_dummy(modules)
- if 'dummy' in [ str(module)
- for module in modules ]:
- modules = [ m
- for m in modules
- if str(m) != 'dummy' ]
- dummy = self.modulesystem.find('dummy')
- modules = sorted(set(modules)) + [dummy]
# Show banner notice of every module.
if single_configure: