[bug#77093,rust-team,v3,16/17] import: crate: Add ‘--lockfile’ option.

Message ID 81adafb3ac10a3b522c2e1949a91351f34216a9e.1742713356.git.hako@ultrarare.space
State New
Headers
Series [bug#77093,rust-team,v3,01/17] build/cargo: Pass ‘--offline’ to cargo. |

Commit Message

Hilton Chain March 23, 2025, 7:28 a.m. UTC
* guix/scripts/import/crate.scm (%options): Add ‘--lockfile’ option.
* guix/scripts/import/crate.scm (show-help): Add it.
(guix-import-crate): Use it.
* doc/guix.texi (Invoking guix import): Document it.
* tests/crate.scm (temp-file): New variable.
("crate-lockfile-import"): New test.

Change-Id: I291478e04adf9f2df0bf216425a5e8aeba0bedd9
---
 doc/guix.texi                 | 14 ++++++
 guix/scripts/import/crate.scm | 58 +++++++++++++++++++----
 tests/crate.scm               | 88 +++++++++++++++++++++++++++++++++++
 3 files changed, 150 insertions(+), 10 deletions(-)
  

Comments

Ludovic Courtès April 22, 2025, 9:28 p.m. UTC | #1
Hilton Chain <hako@ultrarare.space> writes:

> * guix/scripts/import/crate.scm (%options): Add ‘--lockfile’ option.
> * guix/scripts/import/crate.scm (show-help): Add it.
> (guix-import-crate): Use it.
> * doc/guix.texi (Invoking guix import): Document it.
> * tests/crate.scm (temp-file): New variable.
> ("crate-lockfile-import"): New test.
>
> Change-Id: I291478e04adf9f2df0bf216425a5e8aeba0bedd9

[...]

> +        (match crates-definitions
> +          (((define 'rust-adler2-2.0.0
> +              (crate-source
> +               "adler2" "2.0.0"
> +               "09r6drylvgy8vv8k20lnbvwq8gp09h7smfn6h1rxsy15pgh629si"))

Note that this doesn’t actually testwhat you’d like.  For instance, it
would match:

  ((whatever rust-adler2-2.0.0
             (nonsense "adler2" "2.0.0"
                       "09r6drylvgy8vv8k20lnbvwq8gp09h7smfn6h1rxsy15pgh629si"))
   …)             

But the bug is already here in ‘master’, uh.

So you need to quote every symbol that is meant to be matched literally,
or wrap the pattern in quasiquote, as is done in ‘tests/gem.scm’:

  (match …
    ((`(define rust-adler2-2.0.0 (crate-source …))) #t))

Ludo’.
  
Hilton Chain April 23, 2025, 4:12 a.m. UTC | #2
On Wed, 23 Apr 2025 05:28:17 +0800,
Ludovic Courtès wrote:
>
> Hilton Chain <hako@ultrarare.space> writes:
>
> > * guix/scripts/import/crate.scm (%options): Add ‘--lockfile’ option.
> > * guix/scripts/import/crate.scm (show-help): Add it.
> > (guix-import-crate): Use it.
> > * doc/guix.texi (Invoking guix import): Document it.
> > * tests/crate.scm (temp-file): New variable.
> > ("crate-lockfile-import"): New test.
> >
> > Change-Id: I291478e04adf9f2df0bf216425a5e8aeba0bedd9
>
> [...]
>
> > +        (match crates-definitions
> > +          (((define 'rust-adler2-2.0.0
> > +              (crate-source
> > +               "adler2" "2.0.0"
> > +               "09r6drylvgy8vv8k20lnbvwq8gp09h7smfn6h1rxsy15pgh629si"))
>
> Note that this doesn’t actually testwhat you’d like.  For instance, it
> would match:
>
>   ((whatever rust-adler2-2.0.0
>              (nonsense "adler2" "2.0.0"
>                        "09r6drylvgy8vv8k20lnbvwq8gp09h7smfn6h1rxsy15pgh629si"))
>    …)
>
> But the bug is already here in ‘master’, uh.
>
> So you need to quote every symbol that is meant to be matched literally,
> or wrap the pattern in quasiquote, as is done in ‘tests/gem.scm’:
>
>   (match …
>     ((`(define rust-adler2-2.0.0 (crate-source …))) #t))

Thanks!  Changes here are not applied to master yet, they are currently
available in rust-team only.
  

Patch

diff --git a/doc/guix.texi b/doc/guix.texi
index 7fa986b4b9..218c2ba630 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -14703,6 +14703,20 @@  Invoking guix import
 If a crate dependency is not (yet) packaged, make the corresponding
 input in @code{#:cargo-inputs} or @code{#:cargo-development-inputs} into
 a comment.
+@item --lockfile=@var{file}
+@itemx -f @var{file}
+When @option{--lockfile} is specified, the importer will ignore other options
+and won't output package definitions, instead generating source definitions for
+all dependencies in @var{file}, a @file{Cargo.lock} file.  For example:
+
+@example
+guix import crate --lockfile=/path/to/Cargo.lock my-package
+@end example
+
+generates sources from @file{/path/to/Cargo.lock} and a list
+@code{my-package-cargo-inputs} referencing these sources.  The generated list is
+intended for the package's @code{inputs}, replacing @code{#:cargo-inputs} and
+@code{#:cargo-development-inputs}.
 @end table
 
 @item elm
diff --git a/guix/scripts/import/crate.scm b/guix/scripts/import/crate.scm
index 723cbb3665..3b536e135a 100644
--- a/guix/scripts/import/crate.scm
+++ b/guix/scripts/import/crate.scm
@@ -25,11 +25,13 @@ 
 (define-module (guix scripts import crate)
   #:use-module (guix ui)
   #:use-module (guix utils)
+  #:use-module (guix read-print)
   #:use-module (guix scripts)
   #:use-module (guix import crate)
   #:use-module (guix scripts import)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-11)
+  #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-37)
   #:use-module (ice-9 match)
   #:use-module (ice-9 format)
@@ -60,6 +62,9 @@  (define (show-help)
                          sufficient package exists for it"))
   (newline)
   (display (G_ "
+  -f, --lockfile=FILE    import dependencies from FILE, a 'Cargo.lock' file"))
+  (newline)
+  (display (G_ "
   -h, --help             display this help and exit"))
   (display (G_ "
   -V, --version          display version information and exit"))
@@ -87,6 +92,9 @@  (define %options
          (option '("mark-missing") #f #f
                  (lambda (opt name arg result)
                    (alist-cons 'mark-missing #t result)))
+         (option '(#\f "lockfile") #f #t
+                 (lambda (opt name arg result)
+                   (alist-cons 'lockfile arg result)))
          %standard-import-options))
 
 
@@ -101,6 +109,8 @@  (define (guix-import-crate . args)
                         #:build-options? #f))
 
   (let* ((opts (parse-options))
+         (lockfile (assoc-ref opts 'lockfile))
+         (file-to-insert (assoc-ref opts 'file-to-insert))
          (args (filter-map (match-lambda
                              (('argument . value)
                               value)
@@ -111,16 +121,44 @@  (define (guix-import-crate . args)
        (define-values (name version)
          (package-name->name+version spec))
 
-       (match (if (assoc-ref opts 'recursive)
-                  (crate-recursive-import
-                   name #:version version
-                   #:recursive-dev-dependencies?
-                   (assoc-ref opts 'recursive-dev-dependencies)
-                   #:allow-yanked? (assoc-ref opts 'allow-yanked))
-                  (crate->guix-package
-                   name #:version version #:include-dev-deps? #t
-                   #:allow-yanked? (assoc-ref opts 'allow-yanked)
-                   #:mark-missing? (assoc-ref opts 'mark-missing)))
+       (match (cond
+               ((and=> lockfile
+                       (lambda (file)
+                         (or (file-exists? file)
+                             (leave (G_ "file '~a' does not exist~%") file))))
+                (let-values (((crate-definitions input-list)
+                              (cargo-lock->definitions lockfile name)))
+                  (if file-to-insert
+                      ;; Adjusted from ‘--insert’ option of guix-import.
+                      (let ((term (second input-list)))
+                        (begin
+                          ;; Remove existing input list definition.
+                          (and=> (find-definition-location file-to-insert term)
+                                 delete-expression)
+                          ;; Insert input list alphabetically.
+                          (or (and=> (find-definition-insertion-location
+                                      file-to-insert term)
+                                     (cut insert-expression <> input-list))
+                              (let ((port (open-file file-to-insert "a")))
+                                (newline port)
+                                (pretty-print-with-comments port input-list)
+                                (newline port)
+                                (newline port)
+                                (close-port port))))
+                        crate-definitions)
+                      `(,@crate-definitions
+                        ,input-list))))
+               ((assoc-ref opts 'recursive)
+                (crate-recursive-import
+                 name #:version version
+                 #:recursive-dev-dependencies?
+                 (assoc-ref opts 'recursive-dev-dependencies)
+                 #:allow-yanked? (assoc-ref opts 'allow-yanked)))
+               (else
+                (crate->guix-package
+                 name #:version version #:include-dev-deps? #t
+                 #:allow-yanked? (assoc-ref opts 'allow-yanked)
+                 #:mark-missing? (assoc-ref opts 'mark-missing))))
          ((or #f '())
           (leave (G_ "failed to download meta-data for package '~a'~%")
                  (if version
diff --git a/tests/crate.scm b/tests/crate.scm
index d0dc779cd2..9b7066c3b1 100644
--- a/tests/crate.scm
+++ b/tests/crate.scm
@@ -34,6 +34,7 @@  (define-module (test-crate)
   #:use-module (gnu packages)
   #:use-module (ice-9 iconv)
   #:use-module (ice-9 match)
+  #:use-module (srfi srfi-11)
   #:use-module (srfi srfi-64))
 
 
@@ -476,6 +477,9 @@  (define rust-leaf-bob-3.0.2-yanked
     (description #f)
     (license #f)))
 
+(define temp-file
+  (string-append "t-crate-" (number->string (getpid))))
+
 
 (test-begin "crate")
 
@@ -1178,4 +1182,88 @@  (define rust-leaf-bob-3.0.2-yanked
           (x
            (pk 'fail (pretty-print-with-comments (current-output-port) x) #f)))))
 
+
+(test-assert "crate-lockfile-import"
+  (begin
+    (call-with-output-file temp-file
+      (lambda (port)
+        (display "\
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = \"adler2\"
+version = \"2.0.0\"
+source = \"registry+https://github.com/rust-lang/crates.io-index\"
+checksum = \"512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627\"
+
+[[package]]
+name = \"aho-corasick\"
+version = \"1.1.3\"
+source = \"registry+https://github.com/rust-lang/crates.io-index\"
+checksum = \"8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916\"
+dependencies = [
+ \"memchr\",
+]
+
+[[package]]
+name = \"smithay\"
+version = \"0.4.0\"
+source = \"git+https://github.com/Smithay/smithay.git?rev=\
+0cd3345c59f7cb139521f267956a1a4e33248393#\
+0cd3345c59f7cb139521f267956a1a4e33248393\"
+dependencies = [
+ \"appendlist\",
+]
+
+[[package]]
+name = \"test\"
+version = \"25.2.0\"\n" port)))
+    (mock
+     ((guix scripts download) guix-download
+      (lambda _
+        (format #t "~a~%~a~%"
+                "/gnu/store/in056fyrz6nvy3jpxrxglgj30g0lwniv-smithay-0cd3345"
+                "191h87bpzg0l1ihfb4hmx00b86pfb5mwwc6s8i49al0vigc14l37")))
+     (let-values
+         (((crates-definitions input-list)
+           (cargo-lock->definitions temp-file "test")))
+       (and
+        (match crates-definitions
+          (((define 'rust-adler2-2.0.0
+              (crate-source
+               "adler2" "2.0.0"
+               "09r6drylvgy8vv8k20lnbvwq8gp09h7smfn6h1rxsy15pgh629si"))
+            (define 'rust-aho-corasick-1.1.3
+              (crate-source
+               "aho-corasick" "1.1.3"
+               "05mrpkvdgp5d20y2p989f187ry9diliijgwrs254fs9s1m1x6q4f"))
+            (define 'rust-smithay-0.4.0.0cd3345
+              ($ <comment>
+                 ";; TODO: Define standalone package if this is a workspace.\n"
+                 #f)
+              (origin
+                (method git-fetch)
+                (uri (git-reference
+                      (url "https://github.com/Smithay/smithay.git")
+                      (commit "0cd3345c59f7cb139521f267956a1a4e33248393")))
+                (file-name (git-file-name "rust-smithay" "0.4.0.0cd3345"))
+                (sha256
+                 (base32
+                  "191h87bpzg0l1ihfb4hmx00b86pfb5mwwc6s8i49al0vigc14l37")))))
+           #t)
+          (x
+           (pk 'fail (pretty-print-with-comments (current-output-port) x) #f)))
+        (match input-list
+          ((define-public 'test-cargo-inputs
+             (list rust-adler2-2.0.0
+                   rust-aho-corasick-1.1.3
+                   rust-smithay-0.4.0.0cd3345))
+           #t)
+          (x
+           (pk 'fail x #f))))))))
+
 (test-end "crate")
+
+(false-if-exception (delete-file temp-file))