Subject: [bug#77093] [PATCH v4 rust-team 13/22] build/cargo: Support non-workspace directory source inputs.

From: Hilton Chain <[email-masked]>
Date: Tue, 29 Apr 2025 00:23:10 +0800

* guix/build/cargo-build-system.scm (single-crate?): New procedure.
(crate-src?): Support non-workspace directory source inputs.
(configure): Likewise.

Change-Id: If2bd318b44ed765baec69309f25d320edceee116
---
 guix/build/cargo-build-system.scm | 60 ++++++++++++++++++++-----------
 1 file changed, 39 insertions(+), 21 deletions(-)

diff --git a/guix/build/cargo-build-system.scm b/guix/build/cargo-build-system.scm
index 7b519607f2..d1020bab52 100644
--- a/guix/build/cargo-build-system.scm
+++ b/guix/build/cargo-build-system.scm
@@ -34,6 +34,7 @@ (define-module (guix build cargo-build-system)
   #:use-module (ice-9 ftw)
   #:use-module (ice-9 format)
   #:use-module (ice-9 match)
+  #:use-module (ice-9 textual-ports)
   #:use-module (ice-9 threads)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
@@ -61,22 +62,35 @@ (define (has-executable-target?)
          (bin-dep? (lambda (dep) (find bin? (get-kinds dep)))))
     (find bin-dep? (manifest-targets))))
 
+(define (single-crate? dir)
+  "Check if directory DIR contains 'Cargo.toml' and is not a workspace."
+  (let ((manifest-file (in-vicinity dir "Cargo.toml")))
+    (and (file-exists? manifest-file)
+         (not (string-contains
+               (call-with-input-file manifest-file get-string-all)
+               "[workspace]")))))
+
 (define (crate-src? path)
   "Check if PATH refers to a crate source, namely a gzipped tarball with a
 Cargo.toml file present at its root."
-    (and (not (directory-exists? path)) ; not a tarball
-         (not (string-suffix? "py" path))   ; sanity-check.py
-         ;; First we print out all file names within the tarball to see if it
-         ;; looks like the source of a crate. However, the tarball will include
-         ;; an extra path component which we would like to ignore (since we're
-         ;; interested in checking if a Cargo.toml exists at the root of the
-         ;; archive, but not nested anywhere else). We do this by cutting up
-         ;; each output line and only looking at the second component. We then
-         ;; check if it matches Cargo.toml exactly and short circuit if it does.
-         (apply invoke (list "sh" "-c"
-                             (string-append "tar -tf " path
-                                            " | cut -d/ -f2"
-                                            " | grep -q '^Cargo.toml$'")))))
+  (if (directory-exists? path)
+      ;; The build system only handles sources containing single crate.
+      ;; Workspaces should be packaged into crates (via 'package phase)
+      ;; and used in inputs.
+      (single-crate? path)
+      (and (not (string-suffix? "py" path)) ;sanity-check.py
+           ;; First we print out all file names within the tarball to see
+           ;; if it looks like the source of a crate. However, the tarball
+           ;; will include an extra path component which we would like to
+           ;; ignore (since we're interested in checking if a Cargo.toml
+           ;; exists at the root of the archive, but not nested anywhere
+           ;; else). We do this by cutting up each output line and only
+           ;; looking at the second component. We then check if it matches
+           ;; Cargo.toml exactly and short circuit if it does.
+           (invoke "sh" "-c"
+                   (string-append "tar -tf " path
+                                  " | cut -d/ -f2"
+                                  " | grep -q '^Cargo.toml$'")))))
 
 (define* (unpack-rust-crates #:key inputs (vendor-dir "guix-vendor")
                              #:allow-other-keys)
@@ -161,14 +175,18 @@ (define* (configure #:key inputs
          (and (crate-src? path)
               ;; Gracefully handle duplicate inputs
               (not (file-exists? crate-dir))
-              (mkdir-p crate-dir)
-              ;; Cargo crates are simply gzipped tarballs but with a .crate
-              ;; extension. We expand the source to a directory name we control
-              ;; so that we can generate any cargo checksums.
-              ;; The --strip-components argument is needed to prevent creating
-              ;; an extra directory within `crate-dir`.
-              (format #t "Unpacking ~a~%" name)
-              (invoke "tar" "xf" path "-C" crate-dir "--strip-components" "1")))))
+              (if (directory-exists? path)
+                  (copy-recursively path crate-dir)
+                  (begin
+                    (mkdir-p crate-dir)
+                    ;; Cargo crates are simply gzipped tarballs but with a
+                    ;; .crate extension. We expand the source to a directory
+                    ;; name we control so that we can generate any cargo
+                    ;; checksums.  The --strip-components argument is needed to
+                    ;; prevent creating an extra directory within `crate-dir`.
+                    (format #t "Unpacking ~a~%" name)
+                    (invoke "tar" "xf" path "-C" crate-dir
+                            "--strip-components" "1")))))))
     inputs)
 
   ;; For cross-building
-- 
2.49.0