Hi Bruno,

Here is a patch with some updates now that I looked into the issue a
bit more. I've also added the comments and fixed the build-aux issue
you noticed.

On 3/21/24 3:20 PM, Collin Funk wrote:
> This is
> because filter_filelist can return a non-empty list with an empty
> string:

This isn't fully accurate. The real reason this problem exists is
that filter_filelist will join an empty list. In
GLModule.getAutomakeSnippet_Unconditional we split by the same
separator which results in a list with an empty string.

In other words, we basically do this:

print('\n'.join([]).split('\n'))
['']

To fix this I have made the following change to filter_filelist:

-    result = separator.join(listing)
+    # Return an empty string if no files were matched, else combine them
+    # with the given separator.
+    if listing:
+        result = separator.join(listing)
+    else:
+        result = ''
     return result

Still not the prettiest code, but maybe I will think of something
better later...

Collin
From 8f0946720b61407b5628aeadc224addc71a4dcfe Mon Sep 17 00:00:00 2001
From: Collin Funk <collin.fu...@gmail.com>
Date: Fri, 22 Mar 2024 02:15:24 -0700
Subject: [PATCH] gnulib-tool.py: Fix unconditional Automake snippets for
 non-tests.

* pygnulib/GLModuleSystem.py
(GLModule.getAutomakeSnippet_Unconditional): Fix the file lookups used
to determine the EXTRA_DIST and EXTRA_lib_SOURCES Automake variables.
Update comment to match gnulib-tool.sh.
* pygnulib/constants.py (filter_filelist): Fix misleading return type in
docstring. Return an empty string if no files were found.
---
 ChangeLog                  | 10 ++++++
 pygnulib/GLModuleSystem.py | 71 +++++++++++++++++++++-----------------
 pygnulib/constants.py      |  9 +++--
 3 files changed, 57 insertions(+), 33 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 46379c5a4c..82e998ed25 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2024-03-22  Collin Funk  <collin.fu...@gmail.com>
+
+	gnulib-tool.py: Fix unconditional Automake snippets for non-tests.
+	* pygnulib/GLModuleSystem.py
+	(GLModule.getAutomakeSnippet_Unconditional): Fix the file lookups used
+	to determine the EXTRA_DIST and EXTRA_lib_SOURCES Automake variables.
+	Update comment to match gnulib-tool.sh.
+	* pygnulib/constants.py (filter_filelist): Fix misleading return type in
+	docstring. Return an empty string if no files were found.
+
 2024-03-21  Collin Funk  <collin.fu...@gmail.com>
 
 	gnulib-tool.sh: Avoid a redundant space in gl_AVOID in gnulib-cache.m4.
diff --git a/pygnulib/GLModuleSystem.py b/pygnulib/GLModuleSystem.py
index bfef6e692b..c0d9b0365a 100644
--- a/pygnulib/GLModuleSystem.py
+++ b/pygnulib/GLModuleSystem.py
@@ -649,52 +649,61 @@ class GLModule(object):
         result = ''
         if 'makefile-unconditional' not in self.cache:
             if self.getName().endswith('-tests'):
+                # *-tests module live in tests/, not lib/.
+                # Synthesize an EXTRA_DIST augmentation.
                 files = self.getFiles()
                 extra_files = filter_filelist(constants.NL, files,
-                                              'tests/', '', 'tests/', '').split(constants.NL)
-                if extra_files:
-                    result += 'EXTRA_DIST += %s' % ' '.join(extra_files)
+                                              'tests/', '', 'tests/', '')
+                if extra_files != '':
+                    result += 'EXTRA_DIST += %s' % ' '.join(extra_files.split(constants.NL))
                     result += constants.NL * 2
             else:  # if not tests module
-                # TODO: unconditional automake snippet for nontests modules
+                # Synthesize an EXTRA_DIST augmentation.
                 snippet = self.getAutomakeSnippet_Conditional()
                 snippet = constants.combine_lines(snippet)
-                pattern = re.compile('^lib_SOURCES[\t ]*\\+=[\t ]*(.*)$', re.M)
-                mentioned_files = pattern.findall(snippet)
-                if mentioned_files != list():
-                    mentioned_files = mentioned_files[-1].split(' ')
-                    mentioned_files = [ f.strip()
-                                        for f in mentioned_files ]
-                    mentioned_files = [ f
-                                        for f in mentioned_files
-                                        if f != '' ]
-                    mentioned_files = sorted(set(mentioned_files))
+                pattern = re.compile(r'^lib_SOURCES[\t ]*\+=[\t ]*(.*)$', re.MULTILINE)
+                mentioned_files = set(pattern.findall(snippet))
+                if mentioned_files:
+                    # Get all the file names from 'lib_SOURCES += ...'.
+                    mentioned_files = { filename
+                                        for line in mentioned_files
+                                        for filename in line.split() }
                 all_files = self.getFiles()
                 lib_files = filter_filelist(constants.NL, all_files,
-                                            'lib/', '', 'lib/', '').split(constants.NL)
-                extra_files = [ f
-                                for f in lib_files
-                                if f not in mentioned_files ]
-                extra_files = sorted(set(extra_files))
-                if extra_files != [''] and extra_files:
+                                            'lib/', '', 'lib/', '')
+                if lib_files != '':
+                    lib_files = set(lib_files.split(constants.NL))
+                else:
+                    lib_files = set()
+                # Remove mentioned_files from lib_files.
+                extra_files = sorted(lib_files.difference(mentioned_files))
+                if extra_files:
                     result += 'EXTRA_DIST += %s' % ' '.join(extra_files)
                     result += '\n\n'
-                # Synthesize also an EXTRA_lib_SOURCES augmentation
+                # Synthesize also an EXTRA_lib_SOURCES augmentation.
+                # This is necessary so that automake can generate the right list of
+                # dependency rules.
+                # A possible approach would be to use autom4te --trace of the redefined
+                # AC_LIBOBJ and AC_REPLACE_FUNCS macros when creating the Makefile.am
+                # (use autom4te --trace, not just grep, so that AC_LIBOBJ invocations
+                # inside autoconf's built-in macros are not missed).
+                # But it's simpler and more robust to do it here, based on the file list.
+                # If some .c file exists and is not used with AC_LIBOBJ - for example,
+                # a .c file is preprocessed into another .c file for BUILT_SOURCES -,
+                # automake will generate a useless dependency; this is harmless.
                 if str(self) != 'relocatable-prog-wrapper' and str(self) != 'pt_chown':
                     extra_files = filter_filelist(constants.NL, extra_files,
-                                                  '', '.c', '', '').split(constants.NL)
-                    extra_files = sorted(set(extra_files))
-                    if extra_files != ['']:
-                        result += 'EXTRA_lib_SOURCES += %s' % ' '.join(extra_files)
+                                                  '', '.c', '', '')
+                    if extra_files != '':
+                        result += 'EXTRA_lib_SOURCES += %s' % ' '.join(sorted(set(extra_files.split(constants.NL))))
                         result += '\n\n'
                 # Synthesize an EXTRA_DIST augmentation also for the files in build-aux
                 buildaux_files = filter_filelist(constants.NL, all_files,
-                                                 'build-aux/', '', 'build-aux/', '').split(constants.NL)
-                buildaux_files = sorted(set(buildaux_files))
-                if buildaux_files != ['']:
-                    buildaux_files = ''.join(buildaux_files)
-                    buildaux_files = joinpath('$(top_srcdir)', auxdir, buildaux_files)
-                    result += 'EXTRA_DIST += %s' % buildaux_files
+                                                 'build-aux/', '', 'build-aux/', '')
+                if buildaux_files != '':
+                    buildaux_files = [ joinpath('$(top_srcdir)', auxdir, filename)
+                                       for filename in sorted(set(buildaux_files.split(constants.NL))) ]
+                    result += 'EXTRA_DIST += %s' % ' '.join(buildaux_files)
                     result += '\n\n'
             result = constants.nlconvert(result)
             self.cache['makefile-unconditional'] = result
diff --git a/pygnulib/constants.py b/pygnulib/constants.py
index b7434155d8..c3a5aeae09 100644
--- a/pygnulib/constants.py
+++ b/pygnulib/constants.py
@@ -447,7 +447,7 @@ def hardlink(src: str, dest: str) -> None:
 def filter_filelist(separator, filelist,
                     prefix, suffix, removed_prefix, removed_suffix,
                     added_prefix='', added_suffix=''):
-    '''filter_filelist(*args) -> list
+    '''filter_filelist(*args) -> str
 
     Filter the given list of files. Filtering: Only the elements starting with
     prefix and ending with suffix are considered. Processing: removed_prefix
@@ -461,7 +461,12 @@ def filter_filelist(separator, filelist,
             result = pattern.sub('%s\\1%s'
                                  % (added_prefix, added_suffix), filename)
             listing += [result]
-    result = separator.join(listing)
+    # Return an empty string if no files were matched, else combine them
+    # with the given separator.
+    if listing:
+        result = separator.join(listing)
+    else:
+        result = ''
     return result
 
 
-- 
2.44.0

Reply via email to