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

Message ID 9e07061c7a9a4dacaa437acca4d951047c599573.1742281797.git.hako@ultrarare.space
State New
Headers
Series New Rust packaging workflow based on lockfile importer. |

Commit Message

Hilton Chain March 18, 2025, 7:24 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                 | 15 ++++++
 guix/scripts/import/crate.scm | 58 +++++++++++++++++++----
 tests/crate.scm               | 88 +++++++++++++++++++++++++++++++++++
 3 files changed, 151 insertions(+), 10 deletions(-)
  

Comments

Efraim Flashner March 18, 2025, 11:49 a.m. UTC | #1
On Tue, Mar 18, 2025 at 03:24:30PM +0800, Hilton Chain wrote:
> * 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                 | 15 ++++++
>  guix/scripts/import/crate.scm | 58 +++++++++++++++++++----
>  tests/crate.scm               | 88 +++++++++++++++++++++++++++++++++++
>  3 files changed, 151 insertions(+), 10 deletions(-)
> 
> diff --git a/doc/guix.texi b/doc/guix.texi
> index c20e1d7f9c..029c8cf59c 100644
> --- a/doc/guix.texi
> +++ b/doc/guix.texi
> @@ -14703,6 +14703,21 @@ 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, importer will ignore other
> +options and won't output package definitions, instead generating source
> +definition for all dependencies in @var{file}, a @file{Cargo.lock} 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 package's base @code{inputs}, replacing
> +@code{#:cargo-inputs} and @code{#:cargo-development-inputs}.

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..d46f753f9c 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-utils-" (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))
> -- 
> 2.48.1
> 
> 
>
  

Patch

diff --git a/doc/guix.texi b/doc/guix.texi
index c20e1d7f9c..029c8cf59c 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -14703,6 +14703,21 @@  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, importer will ignore other
+options and won't output package definitions, instead generating source
+definition 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 package's base @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..d46f753f9c 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-utils-" (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))