Subject: [PATCH v2 13/19] import: cpan: Represent dependencies as <upstream-input> records.

From: Ludovic Courtès <[email-masked]>
Date: Mon, 29 May 2023 16:45:24 +0200

* guix/import/cpan.scm (cpan-name->downstream-name)
(cran-dependency->upstream-input, cran-module-inputs): New procedures.
(cpan-module->sexp)[guix-name, convert-inputs]: Remove.
[maybe-inputs]: Adjust to deal with <upstream-input>.
Use 'cpan-name->downstream-name' instead of 'guix-name'.  Add call to
'cpan-module-inputs' and adjust calls to 'maybe-inputs'.  No longer emit
input labels.
* tests/cpan.scm ("cpan->guix-package"): Adjust test accordingly.
---
 guix/import/cpan.scm | 98 +++++++++++++++++++++++++-------------------
 tests/cpan.scm       |  7 +---
 2 files changed, 58 insertions(+), 47 deletions(-)

diff --git a/guix/import/cpan.scm b/guix/import/cpan.scm
index d7f300777e..b6587d6821 100644
--- a/guix/import/cpan.scm
+++ b/guix/import/cpan.scm
@@ -3,7 +3,7 @@
 ;;; Copyright © 2015 Mark H Weaver <[email-masked]>
 ;;; Copyright © 2016 Alex Sassmannshausen <[email-masked]>
 ;;; Copyright © 2017, 2018 Tobias Geerinckx-Rice <[email-masked]>
-;;; Copyright © 2020, 2021 Ludovic Courtès <[email-masked]>
+;;; Copyright © 2020, 2021, 2023 Ludovic Courtès <[email-masked]>
 ;;; Copyright © 2022 Hartmut Goebel <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
@@ -222,56 +222,73 @@ (define core-module?
                                        first perl-version last))))
                            (loop)))))))))))
 
+(define (cpan-name->downstream-name name)
+  "Return the Guix package name corresponding to NAME."
+  (if (string-prefix? "perl-" name)
+      (string-downcase name)
+      (string-append "perl-" (string-downcase name))))
+
+(define (cran-dependency->upstream-input dependency)
+  "Return the <upstream-input> corresponding to DEPENDENCY, or #f if
+DEPENDENCY denotes an implicit or otherwise unnecessary dependency."
+  (match (cpan-dependency-module dependency)
+    ("perl" #f)                                   ;implicit dependency
+    (module
+     (let ((type (match (cpan-dependency-phase dependency)
+                   ((or 'configure 'build 'test)
+                    ;; "runtime" may also be needed here.  See
+                    ;; https://metacpan.org/pod/CPAN::Meta::Spec#Phases,
+                    ;; which says they are required during
+                    ;; building.  We have not yet had a need for
+                    ;; cross-compiled Perl modules, however, so
+                    ;; we leave it out.
+                    'native)
+                   ('runtime
+                    'propagated)
+                   (_
+                    #f))))
+       (and type
+            (not (core-module? module))           ;expensive call!
+            (upstream-input
+             (name (module->dist-name module))
+             (downstream-name (cpan-name->downstream-name name))
+             (type type)))))))
+
+(define (cpan-module-inputs release)
+  "Return the list of <upstream-input> for dependencies of RELEASE, a
+<cpan-release>."
+  (define (upstream-input<? a b)
+    (string<? (upstream-input-downstream-name a)
+              (upstream-input-downstream-name b)))
+
+  (sort (delete-duplicates
+         (filter-map cran-dependency->upstream-input
+                     (cpan-release-dependencies release)))
+        upstream-input<?))
+
 (define (cpan-module->sexp release)
   "Return the 'package' s-expression for a CPAN module from the release data
 in RELEASE, a <cpan-release> record."
   (define name
     (cpan-release-distribution release))
 
-  (define (guix-name name)
-    (if (string-prefix? "perl-" name)
-        (string-downcase name)
-        (string-append "perl-" (string-downcase name))))
-
   (define version (cpan-release-version release))
   (define source-url (cpan-source-url release))
 
-  (define (convert-inputs phases)
-    ;; Convert phase dependencies into a list of name/variable pairs.
-    (match (filter-map (lambda (dependency)
-                         (and (memq (cpan-dependency-phase dependency)
-                                    phases)
-                              (cpan-dependency-module dependency)))
-                       (cpan-release-dependencies release))
-      ((inputs ...)
-       (sort
-        (delete-duplicates
-         ;; Listed dependencies may include core modules.  Filter those out.
-         (filter-map (match-lambda
-                       ("perl" #f)                ;implicit dependency
-                       ((? core-module?) #f)
-                       (module
-                         (let ((name (guix-name (module->dist-name module))))
-                           (list name
-                                 (list 'unquote (string->symbol name))))))
-                     inputs))
-        (lambda args
-          (match args
-            (((a _ ...) (b _ ...))
-             (string<? a b))))))))
-
-  (define (maybe-inputs guix-name inputs)
+  (define (maybe-inputs input-type inputs)
     (match inputs
       (()
        '())
       ((inputs ...)
-       (list (list guix-name
-                   (list 'quasiquote inputs))))))
+       `((,input-type (list ,@(map (compose string->symbol
+                                            upstream-input-downstream-name)
+                                   inputs)))))))
 
   (let ((tarball (with-store store
-                   (download-to-store store source-url))))
+                   (download-to-store store source-url)))
+        (inputs (cpan-module-inputs release)))
     `(package
-       (name ,(guix-name name))
+       (name ,(cpan-name->downstream-name name))
        (version ,version)
        (source (origin
                  (method url-fetch)
@@ -281,14 +298,11 @@ (define (cpan-module->sexp release)
                    ,(bytevector->nix-base32-string (file-sha256 tarball))))))
        (build-system perl-build-system)
        ,@(maybe-inputs 'native-inputs
-                       ;; "runtime" may also be needed here.  See
-                       ;; https://metacpan.org/pod/CPAN::Meta::Spec#Phases,
-                       ;; which says they are required during building.  We
-                       ;; have not yet had a need for cross-compiled perl
-                       ;; modules, however, so we leave it out.
-                       (convert-inputs '(configure build test)))
+                       (filter (upstream-input-type-predicate 'native)
+                               inputs))
        ,@(maybe-inputs 'propagated-inputs
-                       (convert-inputs '(runtime)))
+                       (filter (upstream-input-type-predicate 'propagated)
+                               inputs))
        (home-page ,(cpan-home name))
        (synopsis ,(cpan-release-abstract release))
        (description fill-in-yourself!)
diff --git a/tests/cpan.scm b/tests/cpan.scm
index bbcd108e12..c9dd6d36de 100644
--- a/tests/cpan.scm
+++ b/tests/cpan.scm
@@ -1,7 +1,7 @@
 ;;; GNU Guix --- Functional package management for GNU
 ;;; Copyright © 2015 Eric Bavier <[email-masked]>
 ;;; Copyright © 2016 Alex Sassmannshausen <[email-masked]>
-;;; Copyright © 2020 Ludovic Courtès <[email-masked]>
+;;; Copyright © 2020, 2023 Ludovic Courtès <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -64,7 +64,6 @@ (define test-source
 (test-begin "cpan")
 
 (test-assert "cpan->guix-package"
-  ;; Replace network resources with sample data.
   (with-http-server `((200 ,test-json)
                       (200 ,test-source)
                       (200 "{ \"distribution\" : \"Test-Script\" }"))
@@ -82,9 +81,7 @@ (define test-source
                        ('base32
                         (? string? hash)))))
            ('build-system 'perl-build-system)
-           ('propagated-inputs
-            ('quasiquote
-             (("perl-test-script" ('unquote 'perl-test-script)))))
+           ('propagated-inputs ('list 'perl-test-script))
            ('home-page "https://metacpan.org/release/Foo-Bar")
            ('synopsis "Fizzle Fuzz")
            ('description 'fill-in-yourself!)
-- 
2.40.1