This patch implements the --extract-recursive-link-directive and
--extract-recursive-link-directive options.

For --extract-recursive-dependencies, the 'canonicalize' macro seemed
to work well since it has many dependencies:

$ gnulib-tool.py --extract-recursive-dependencies canonicalize > test-python 
2>&1
gnulib-tool.sh --extract-recursive-dependencies canonicalize > test-shell 2>&1
git diff --no-index test-python test-shell

I'm not sure if there is a very good test case for
--extract-recursive-link-directive. I fixed some sorting issues using
this, but even that was only a two line diff:

gnulib-tool.py --extract-recursive-link-directive unistdio/ulc-vasnprintf uname 
pthread sys_socket j0 exp jn fma pow trim > test-python 2>&1
gnulib-tool.sh --extract-recursive-link-directive unistdio/ulc-vasnprintf uname 
pthread sys_socket j0 exp jn fma pow trim > test-shell 2>&1
git diff --no-index test-python test-shell

Collin
From 6847254ed7ed967725f90755962034af10147996 Mon Sep 17 00:00:00 2001
From: Collin Funk <collin.fu...@gmail.com>
Date: Thu, 14 Mar 2024 18:41:05 -0700
Subject: [PATCH] gnulib-tool.py: Follow gnulib-tool changes, part 57.

Follow gnulib-tool change
2019-01-04  Bruno Haible  <br...@clisp.org>
gnulib-tool: New option --extract-recursive-link-directive.

* pygnulib/GLModuleSystem.py (GLModule.getDependenciesRecursively): New
function. Implements the --extract-recursive-dependencies option.
(GLModule.getLinkDirectiveRecursively): New function. Implements the
--extract-recursive-link-directive.
* pygnulib/main.py (main): Add the --extract-recursive-dependencies and
--extract-recursive-link-directive options.
* pygnulib/GLInfo.py (GLInfo.usage): Add new options to help message.
---
 ChangeLog                  | 14 ++++++++
 gnulib-tool.py.TODO        | 34 -------------------
 pygnulib/GLInfo.py         |  8 +++++
 pygnulib/GLModuleSystem.py | 69 ++++++++++++++++++++++++++++++++++++++
 pygnulib/main.py           | 40 ++++++++++++++++++++++
 5 files changed, 131 insertions(+), 34 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index b39f16566e..08a8276629 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2024-03-14  Collin Funk  <collin.fu...@gmail.com>
+
+	gnulib-tool.py: Follow gnulib-tool changes, part 57.
+	Follow gnulib-tool change
+	2019-01-04  Bruno Haible  <br...@clisp.org>
+	gnulib-tool: New option --extract-recursive-link-directive.
+	* pygnulib/GLModuleSystem.py (GLModule.getDependenciesRecursively): New
+	function. Implements the --extract-recursive-dependencies option.
+	(GLModule.getLinkDirectiveRecursively): New function. Implements the
+	--extract-recursive-link-directive.
+	* pygnulib/main.py (main): Add the --extract-recursive-dependencies and
+	--extract-recursive-link-directive options.
+	* pygnulib/GLInfo.py (GLInfo.usage): Add new options to help message.
+
 2024-03-14  Bruno Haible  <br...@clisp.org>
 
 	gnulib-tool: Obey environment variable GNULIB_TOOL_IMPL.
diff --git a/gnulib-tool.py.TODO b/gnulib-tool.py.TODO
index d4de551976..b5796b24a3 100644
--- a/gnulib-tool.py.TODO
+++ b/gnulib-tool.py.TODO
@@ -16,8 +16,6 @@ The following commits to gnulib-tool have not yet been reflected in
 --------------------------------------------------------------------------------
 
 Implement the options:
-  --extract-recursive-dependencies
-  --extract-recursive-link-directive
   -h | --hardlink
   --local-hardlink
   -S | --more-symlinks
@@ -96,21 +94,6 @@ Date:   Sun Jan 9 15:38:51 2022 +0100
 
 --------------------------------------------------------------------------------
 
-commit 87e6634b28df65084321fed22f60d114a0931d21
-Author: Bernhard Voelker <m...@bernhard-voelker.de>
-Date:   Tue Jan 4 00:16:50 2022 +0100
-
-    license: fix GPLv3 texts to use a comma instead of semicolon.
-
-    See: https://www.gnu.org/licenses/gpl-3.0.html#howto
-    Run:
-      $ git grep -l 'Foundation; either version 3' \
-          | xargs sed -i '/Foundation; either version 3/ s/n; e/n, e/'
-
-    * All files using GPLv3: Adjust via the above command.
-
---------------------------------------------------------------------------------
-
 commit 9af17c55629c4cbe2facdc9edb5242136567ebba
 Author: Bruno Haible <br...@clisp.org>
 Date:   Sat Dec 25 14:30:57 2021 +0100
@@ -192,23 +175,6 @@ Date:   Mon Nov 18 13:32:46 2019 +0100
 
 --------------------------------------------------------------------------------
 
-commit ce8a5edbc49dea0cb859207c2d063dbd3be0f96c
-Author: Bruno Haible <br...@clisp.org>
-Date:   Fri Jan 4 19:34:19 2019 +0100
-
-    gnulib-tool: New option --extract-recursive-link-directive.
-
-    * gnulib-tool (func_usage): Document the new options
-    --extract-recursive-dependencies, --extract-recursive-link-directive.
-    (func_verify_module): Document output variables.
-    (func_get_dependencies_recursively): New function.
-    (func_get_link_directive_recursively): New function.
-    Use them to implement the new options
-    --extract-recursive-dependencies, --extract-recursive-link-directive.
-    * doc/gnulib-tool.texi (Link-time requirements): New section.
-
---------------------------------------------------------------------------------
-
 commit cd58dba367a3b8ffbebb23f2099a820106197fae
 Author: Bruno Haible <br...@clisp.org>
 Date:   Sun Oct 29 16:57:32 2017 +0100
diff --git a/pygnulib/GLInfo.py b/pygnulib/GLInfo.py
index 11fdfa60e6..cb77c5e923 100644
--- a/pygnulib/GLInfo.py
+++ b/pygnulib/GLInfo.py
@@ -149,10 +149,12 @@ Usage: gnulib-tool --list
        gnulib-tool --extract-applicability module
        gnulib-tool --extract-filelist module
        gnulib-tool --extract-dependencies module
+       gnulib-tool --extract-recursive-dependencies module
        gnulib-tool --extract-autoconf-snippet module
        gnulib-tool --extract-automake-snippet module
        gnulib-tool --extract-include-directive module
        gnulib-tool --extract-link-directive module
+       gnulib-tool --extract-recursive-link-directive module
        gnulib-tool --extract-license module
        gnulib-tool --extract-maintainer module
        gnulib-tool --extract-tests-module module
@@ -185,10 +187,16 @@ Operation modes:
       --extract-applicability      extract the applicability
       --extract-filelist           extract the list of files
       --extract-dependencies       extract the dependencies
+      --extract-recursive-dependencies  extract the dependencies of the module
+                                        and its dependencies, recursively, all
+                                        together, but without the conditions
       --extract-autoconf-snippet   extract the snippet for configure.ac
       --extract-automake-snippet   extract the snippet for library makefile
       --extract-include-directive  extract the #include directive
       --extract-link-directive     extract the linker directive
+      --extract-recursive-link-directive  extract the linker directive of the
+                                          module and its dependencies,
+                                          recursively, all together
       --extract-license            report the license terms of the source files
                                    under lib/
       --extract-maintainer         report the maintainer(s) inside gnulib
diff --git a/pygnulib/GLModuleSystem.py b/pygnulib/GLModuleSystem.py
index 86794cf80e..aff839d145 100644
--- a/pygnulib/GLModuleSystem.py
+++ b/pygnulib/GLModuleSystem.py
@@ -329,6 +329,75 @@ class GLModule(object):
         result = self.modulesystem.find(self.getTestsName())
         return result
 
+    def getDependenciesRecursively(self) -> str:
+        '''Return a list of recursive dependencies of this module separated
+        by a newline.'''
+        handledmodules = set()
+        inmodules = set()
+        outmodules = set()
+
+        # 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.
+        inmodules.add(self)
+        while len(inmodules) > 0:
+            inmodules_this_round = inmodules
+            inmodules = set()  # Accumulator, queue for next round
+            for module in inmodules_this_round:
+                outmodules.add(module)
+                inmodules = inmodules.union(module.getDependenciesWithoutConditions())
+            handledmodules = handledmodules.union(inmodules_this_round)
+            # Remove $handledmodules from $inmodules.
+            inmodules = inmodules.difference(handledmodules)
+
+        if len(outmodules) > 0:
+            module_names = sorted([ str(module)
+                                    for module in outmodules ])
+            return '\n'.join(module_names) + '\n'
+        else:
+            return ''
+
+    def getLinkDirectiveRecursively(self) -> str:
+        '''Return a list of the link directives of this module separated
+        by a newline.'''
+        handledmodules = set()
+        inmodules = set()
+        outmodules = set()
+
+        # 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.
+        inmodules.add(self)
+        while len(inmodules) > 0:
+            inmodules_this_round = inmodules
+            inmodules = set()  # Accumulator, queue for next round
+            for module in inmodules_this_round:
+                if self.getLink() != '':
+                    # The module description has a 'Link:' field. Ignore the dependencies.
+                    outmodules.add(module)
+                else:
+                    # The module description has no 'Link:' field. Recurse through the dependencies.
+                    inmodules = inmodules.union(module.getDependenciesWithoutConditions())
+            handledmodules = handledmodules.union(inmodules_this_round)
+            # Remove $handledmodules from $inmodules.
+            inmodules = inmodules.difference(handledmodules)
+
+        if len(outmodules) > 0:
+            # Remove whitespace from sections.
+            link_sections = [ module.getLink().strip()
+                              for module in outmodules ]
+            # Sort the link directives.
+            directives = sorted([ line
+                                  for section in link_sections
+                                  for line in section.splitlines() ])
+            return '\n'.join(directives) + '\n'
+        else:
+            return ''
+
     def getShellFunc(self):
         '''GLModule.getShellFunc() -> str
 
diff --git a/pygnulib/main.py b/pygnulib/main.py
index b145675f78..5d65a45b25 100644
--- a/pygnulib/main.py
+++ b/pygnulib/main.py
@@ -176,6 +176,10 @@ def main():
                         dest='mode_xdependencies',
                         default=None,
                         action='store_true')
+    parser.add_argument('--extract-recursive-dependencies',
+                        dest='mode_xrecursive_dependencies',
+                        default=None,
+                        action='store_true')
     parser.add_argument('--extract-autoconf-snippet',
                         dest='mode_xautoconf',
                         default=None,
@@ -192,6 +196,10 @@ def main():
                         dest='mode_xlink',
                         default=None,
                         action='store_true')
+    parser.add_argument('--extract-recursive-link-directive',
+                        dest='mode_xrecursive_link',
+                        default=None,
+                        action='store_true')
     parser.add_argument('--extract-license',
                         dest='mode_xlicense',
                         default=None,
@@ -572,12 +580,18 @@ def main():
     if cmdargs.mode_xdependencies != None:
         mode = 'extract-dependencies'
         modules = list(cmdargs.non_option_arguments)
+    if cmdargs.mode_xrecursive_dependencies != None:
+        mode = 'extract-recursive-dependencies'
+        modules = list(cmdargs.non_option_arguments)
     if cmdargs.mode_xinclude != None:
         mode = 'extract-include-directive'
         modules = list(cmdargs.non_option_arguments)
     if cmdargs.mode_xlink != None:
         mode = 'extract-link-directive'
         modules = list(cmdargs.non_option_arguments)
+    if cmdargs.mode_xrecursive_link != None:
+        mode = 'extract-recursive-link-directive'
+        modules = list(cmdargs.non_option_arguments)
     if cmdargs.mode_xlicense != None:
         mode = 'extract-license'
         modules = list(cmdargs.non_option_arguments)
@@ -1101,6 +1115,19 @@ def main():
             if module:
                 sys.stdout.write(module.getDependencies())
 
+    elif mode == 'extract-recursive-dependencies':
+        if avoids:
+            message = '%s: *** ' % constants.APP['name']
+            message += 'cannot combine --avoid and --extract-recursive-dependencies\n'
+            message += '%s: *** Stop.\n' % constants.APP['name']
+            sys.stderr.write(message)
+            sys.exit(1)
+        modulesystem = classes.GLModuleSystem(config)
+        for name in modules:
+            module = modulesystem.find(name)
+            if module:
+                sys.stdout.write(module.getDependenciesRecursively())
+
     elif mode == 'extract-autoconf-snippet':
         modulesystem = classes.GLModuleSystem(config)
         for name in modules:
@@ -1129,6 +1156,19 @@ def main():
             if module:
                 sys.stdout.write(module.getLink())
 
+    elif mode == 'extract-recursive-link-directive':
+        if avoids:
+            message = '%s: *** ' % constants.APP['name']
+            message += 'cannot combine --avoid and --extract-recursive-link-directive\n'
+            message += '%s: *** Stop.\n' % constants.APP['name']
+            sys.stderr.write(message)
+            sys.exit(1)
+        modulesystem = classes.GLModuleSystem(config)
+        for name in modules:
+            module = modulesystem.find(name)
+            if module:
+                sys.stdout.write(module.getLinkDirectiveRecursively())
+
     elif mode == 'extract-license':
         modulesystem = classes.GLModuleSystem(config)
         for name in modules:
-- 
2.44.0

Reply via email to