From patchwork Thu Dec 21 22:01:52 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Elsing X-Patchwork-Id: 57969 Return-Path: X-Original-To: patchwork@mira.cbaines.net Delivered-To: patchwork@mira.cbaines.net Received: by mira.cbaines.net (Postfix, from userid 113) id ED68427BBEC; Thu, 21 Dec 2023 22:03:33 +0000 (GMT) X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on mira.cbaines.net X-Spam-Level: X-Spam-Status: No, score=-2.7 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,SPF_HELO_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mira.cbaines.net (Postfix) with ESMTPS id 06A0C27BBE2 for ; Thu, 21 Dec 2023 22:03:30 +0000 (GMT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rGR88-0004gu-J4; Thu, 21 Dec 2023 17:03:00 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rGR87-0004fw-I4 for guix-patches@gnu.org; Thu, 21 Dec 2023 17:02:59 -0500 Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1rGR87-0005JI-AP for guix-patches@gnu.org; Thu, 21 Dec 2023 17:02:59 -0500 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1rGR8B-0004iN-E9 for guix-patches@gnu.org; Thu, 21 Dec 2023 17:03:03 -0500 X-Loop: help-debbugs@gnu.org Subject: [bug#67960] [PATCH 4/4] guix: import: Optionally import necessary yanked crates. Resent-From: David Elsing Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Thu, 21 Dec 2023 22:03:03 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 67960 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: 67960@debbugs.gnu.org Cc: David Elsing Received: via spool by 67960-submit@debbugs.gnu.org id=B67960.170319617218085 (code B ref 67960); Thu, 21 Dec 2023 22:03:03 +0000 Received: (at 67960) by debbugs.gnu.org; 21 Dec 2023 22:02:52 +0000 Received: from localhost ([127.0.0.1]:45622 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rGR7y-0004ha-Uo for submit@debbugs.gnu.org; Thu, 21 Dec 2023 17:02:52 -0500 Received: from mout01.posteo.de ([185.67.36.65]:57003) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rGR7s-0004gj-W6 for 67960@debbugs.gnu.org; Thu, 21 Dec 2023 17:02:46 -0500 Received: from submission (posteo.de [185.67.36.169]) by mout01.posteo.de (Postfix) with ESMTPS id DA432240027 for <67960@debbugs.gnu.org>; Thu, 21 Dec 2023 23:02:34 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.net; s=2017; t=1703196154; bh=kf/QQ3w2o3n41m+q1lqJnC3/E83qa71KG/PfIeEZkLY=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version: Content-Transfer-Encoding:From; b=khquDJgR+/YoB7X7bvvv0wxph3szue740Crdr0RAUwmZNgFeWH0Ih9NPkRjjPgIHj acuyKCplauLnS/CxPbm+LiR++2C0MbvGNNIGXSwZ9UcynsPltvB14fNTfCagKrXIpn 1A+/9TuPshbwU18F8iu5XLkz7lrzMOzx5sRPtzvPoUNhi2G4w4s/ieoJBX9iVX5Wrb QZpYOvVDaRdrm3iif//G1z/JXVU7sOfy57E0OCCeCbFlLMP6hlTg9zcYoZM4n04n2s M7WFy8dV1s4yQZle/goeALUIW5gtTxk7ePRGmHqCQr97faSQ0uqPk6LRmewuCkh3aW CrFisQ92XOR6A== Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4Sx4DB23rzz6twM; Thu, 21 Dec 2023 23:02:34 +0100 (CET) From: David Elsing Date: Thu, 21 Dec 2023 22:01:52 +0000 Message-ID: <7b351acd4d85a1b934ac898c217fe7b9b40bedf5.1703195451.git.david.elsing@posteo.net> In-Reply-To: References: MIME-Version: 1.0 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org Sender: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org X-getmail-retrieved-from-mailbox: Patches * doc/guix.texi (Invoking guix import): Mention '--allow-yanked'. * guix/import/crate.scm (make-crate-sexp): Add yanked? argument. For yanked packages, use the full version suffixed by "-yanked" for generated variable names and add a comment and package property. (crate->guix-package): Add allow-yanked? argument and if it is set to #t, allow importing yanked crates if no other version matching the requirements exists. [find-package-version]: Packages previously marked as yanked are only included if allow-yanked? is #t and then take the lowest priority. [find-crate-version]: If allow-yanked? is #t, also consider yanked versions with the lowest priority. [dependency-name+version]: Rename to ... [dependency-name+version+yanked] ...this. Honor allow-yanked? and choose between an existing package and an upstream package. Exit with an error message if no version fulfilling the requirement is found. [version*]: Exit with an error message if the crate version is not found. (cargo-recursive-import): Add allow-yanked? argument. * guix/read-print.scm: Export . * guix/scripts/import/crate.scm: Add "--allow-yanked". * tests/crate.scm: Add test 'crate-recursive-import-only-yanked-available'. [sort-map-dependencies]: Adjust accordingly. [remove-yanked-info]: New variable. Adjust test 'crate-recursive-import-honors-existing-packages'. (test-bar-dependencies): Add yanked dev-dependencies. (test-leaf-bob-crate): Add yanked versions. (rust-leaf-bob-3.0.2-yanked): New variable. --- doc/guix.texi | 3 + guix/import/crate.scm | 139 ++++++++++++++++++------ guix/read-print.scm | 1 + guix/scripts/import/crate.scm | 14 ++- tests/crate.scm | 193 +++++++++++++++++++++++++++++++++- 5 files changed, 310 insertions(+), 40 deletions(-) diff --git a/doc/guix.texi b/doc/guix.texi index a19671643b..da36f90e9b 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -14516,6 +14516,9 @@ in Guix. If @option{--recursive} is specified, also the recursively imported packages contain their development dependencies, which are recursively imported as well. +@item --allow-yanked +If no non-yanked version of a crate is available, use the latest yanked +version instead instead of aborting. @end table @item elm diff --git a/guix/import/crate.scm b/guix/import/crate.scm index db5461312f..e3b8286350 100644 --- a/guix/import/crate.scm +++ b/guix/import/crate.scm @@ -26,12 +26,15 @@ (define-module (guix import crate) #:use-module (guix base32) #:use-module (guix build-system cargo) + #:use-module (guix diagnostics) #:use-module (gcrypt hash) #:use-module (guix http-client) + #:use-module (guix i18n) #:use-module (guix import json) #:use-module (guix import utils) #:use-module (guix memoization) #:use-module (guix packages) + #:use-module (guix read-print) #:use-module (guix upstream) #:use-module (guix utils) #:use-module (gnu packages) @@ -41,6 +44,7 @@ (define-module (guix import crate) #:use-module (srfi srfi-1) #:use-module (srfi srfi-2) #:use-module (srfi srfi-26) + #:use-module (srfi srfi-69) #:use-module (srfi srfi-71) #:export (crate->guix-package guix-package->crate-name @@ -100,7 +104,7 @@ (define-json-mapping make-crate-dependency ;; Autoload Guile-Semver so we only have a soft dependency. (module-autoload! (current-module) - '(semver) '(string->semver semver->string semversemver semver->string semversemver-range semver-range-contains?)) @@ -165,16 +169,18 @@ (define (version->semver-prefix version) (list-matches "^(0+\\.){,2}[0-9]+" version)))) (define* (make-crate-sexp #:key name version cargo-inputs cargo-development-inputs - home-page synopsis description license build?) + home-page synopsis description license build? yanked?) "Return the `package' s-expression for a rust package with the given NAME, VERSION, CARGO-INPUTS, CARGO-DEVELOPMENT-INPUTS, HOME-PAGE, SYNOPSIS, DESCRIPTION, and LICENSE." (define (format-inputs inputs) (map (match-lambda - ((name version) + ((name version yanked) (list (crate-name->package-name name) - (version->semver-prefix version)))) + (if yanked + (string-append version "-yanked") + (version->semver-prefix version))))) inputs)) (let* ((port (http-fetch (crate-uri name version))) @@ -184,6 +190,9 @@ (define (format-inputs inputs) (pkg `(package (name ,guix-name) (version ,version) + ,@(if yanked? + `(,(comment "; This version was yanked!\n" #t)) + '()) (source (origin (method url-fetch) (uri (crate-uri ,name version)) @@ -191,6 +200,9 @@ (define (format-inputs inputs) (sha256 (base32 ,(bytevector->nix-base32-string (port-sha256 port)))))) + ,@(if yanked? + `((properties '((crate-version-yanked? . #t)))) + '()) (build-system cargo-build-system) ,@(maybe-arguments (append (if build? '() @@ -207,7 +219,10 @@ (define (format-inputs inputs) ((license) license) (_ `(list ,@license))))))) (close-port port) - (package->definition pkg (version->semver-prefix version)))) + (package->definition pkg + (if yanked? + (string-append version "-yanked") + (version->semver-prefix version))))) (define (string->license string) (filter-map (lambda (license) @@ -218,8 +233,9 @@ (define (string->license string) 'unknown-license!))) (string-split string (string->char-set " /")))) -(define* (crate->guix-package crate-name #:key version include-dev-deps? - #:allow-other-keys) +(define* (crate->guix-package + crate-name + #:key version include-dev-deps? allow-yanked? #:allow-other-keys) "Fetch the metadata for CRATE-NAME from crates.io, and return the `package' s-expression corresponding to that package, or #f on failure. When VERSION is specified, convert it into a semver range and attempt to fetch @@ -243,63 +259,112 @@ (define version-number (or version (crate-latest-version crate)))) - ;; find the highest existing package that fulfills the semver + ;; Find the highest existing package that fulfills the semver + ;; . Packages previously marked as yanked take lower priority. (define (find-package-version name range) (let* ((semver-range (string->semver-range range)) - (versions + (package-versions (sort - (filter (lambda (version) - (semver-range-contains? semver-range version)) + (filter (match-lambda ((semver yanked) + (and + (or allow-yanked? (not yanked)) + (semver-range-contains? semver-range semver)))) (map (lambda (pkg) - (string->semver (package-version pkg))) + (let ((version (package-version pkg))) + (list + (string->semver version) + (assoc-ref (package-properties pkg) 'crate-version-yanked?)))) (find-packages-by-name (crate-name->package-name name)))) - semverstring (last versions))))) - - ;; Find the highest version of a crate that fulfills the semver - ;; and hasn't been yanked. + (match-lambda* (((semver1 yanked1) (semver2 yanked2)) + (or + (and yanked1 (not yanked2)) + (and + (eq? yanked1 yanked2) + (semverstring semver) yanked))))) + + ;; Find the highest version of a crate that fulfills the semver . If + ;; no matching non-yanked version has been found and allow-yanked? is #t, + ;; also consider yanked packages. (define (find-crate-version crate range) (let* ((semver-range (string->semver-range range)) (versions (sort (filter (lambda (entry) (and - (not (crate-version-yanked? (second entry))) - (semver-range-contains? semver-range (first entry)))) + (or allow-yanked? (not (crate-version-yanked? (second entry)))) + (semver-range-contains? semver-range (first entry)))) (map (lambda (ver) (list (string->semver (crate-version-number ver)) ver)) (crate-versions crate))) - (match-lambda* (((semver _) ...) - (apply semversemver (first existing-version)) + (string->semver (crate-version-number ver))) + (begin + (warning (G_ "~A: version ~a is no longer yanked~%") name (first existing-version)) + (cons name existing-version)) + (list name + (crate-version-number ver) + (crate-version-yanked? ver))) + (begin + (warning (G_ "~A: using existing version ~a, which was yanked~%") name (first existing-version)) + (cons name existing-version))) + (begin + (unless ver + (leave (G_ "~A: no version found for requirement ~a~%") name req)) + (if (crate-version-yanked? ver) + (warning (G_ "~A: imported version ~a was yanked~%") name (crate-version-number ver))) + (list name + (crate-version-number ver) + (crate-version-yanked? ver)))))))) (define version* (and crate - (find-crate-version crate version-number))) + (or + (find-crate-version crate version-number) + (leave (G_ "~A: version ~a not found~%") crate-name version-number)))) ;; sort and map the dependencies to a list containing ;; pairs of (name version) (define (sort-map-dependencies deps) - (sort (map dependency-name+version + (sort (map dependency-name+version+yanked deps) - (match-lambda* (((name _) ...) + (match-lambda* (((name _ _) ...) (apply string-ci (crate-version-license version*) string->license)) - (append cargo-inputs cargo-development-inputs))) + (append + (remove-yanked-info cargo-inputs) + (remove-yanked-info cargo-development-inputs)))) (values #f '()))) (define* (crate-recursive-import - crate-name #:key version recursive-dev-dependencies?) + crate-name #:key version recursive-dev-dependencies? allow-yanked?) (recursive-import crate-name #:repo->guix-package @@ -340,7 +408,8 @@ (define* (crate-recursive-import (or (equal? (car params) crate-name) recursive-dev-dependencies?))) (apply crate->guix-package* - (append params `(#:include-dev-deps? ,include-dev-deps?)))))) + (append params `(#:include-dev-deps? ,include-dev-deps? + #:allow-yanked? ,allow-yanked?)))))) #:version version #:guix-name crate-name->package-name)) diff --git a/guix/read-print.scm b/guix/read-print.scm index 690f5dacdd..6421b79737 100644 --- a/guix/read-print.scm +++ b/guix/read-print.scm @@ -46,6 +46,7 @@ (define-module (guix read-print) page-break page-break? + comment comment? comment->string diff --git a/guix/scripts/import/crate.scm b/guix/scripts/import/crate.scm index b13b6636a6..082a973aee 100644 --- a/guix/scripts/import/crate.scm +++ b/guix/scripts/import/crate.scm @@ -51,6 +51,10 @@ (define (show-help) (display (G_ " --recursive-dev-dependencies include dev-dependencies recursively")) + (display (G_ " + --allow-yanked + allow importing yanked crates if no alternative + satisfying the version requirement exists")) (newline) (display (G_ " -h, --help display this help and exit")) @@ -74,6 +78,9 @@ (define %options (option '("recursive-dev-dependencies") #f #f (lambda (opt name arg result) (alist-cons 'recursive-dev-dependencies #t result))) + (option '("allow-yanked") #f #f + (lambda (opt name arg result) + (alist-cons 'allow-yanked #t result))) %standard-import-options)) @@ -102,8 +109,11 @@ (define-values (name version) (crate-recursive-import name #:version version #:recursive-dev-dependencies? - (assoc-ref opts 'recursive-dev-dependencies)) - (crate->guix-package name #:version version #:include-dev-deps? #t)) + (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))) ((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 e779f738b3..ce2f08aade 100644 --- a/tests/crate.scm +++ b/tests/crate.scm @@ -28,6 +28,7 @@ (define-module (test-crate) #:use-module ((gcrypt hash) #:select ((sha256 . gcrypt-sha256))) #:use-module (guix packages) + #:use-module (guix read-print) #:use-module (guix tests) #:use-module (gnu packages) #:use-module (ice-9 iconv) @@ -42,6 +43,8 @@ (define-module (test-crate) ;; leaf-alice 0.7.5 ;; bar-1.0.0 ;; leaf-bob 3.0.1 +;; leaf-bob 3.0.2 (dev-dependency) +;; leaf-bob 4.0.0 (dev-dependency) ;; ;; root-1.0.0 ;; root-1.0.4 @@ -68,6 +71,8 @@ (define-module (test-crate) ;; leaf-alice-0.7.5 ;; ;; leaf-bob-3.0.1 +;; leaf-bob-3.0.2 (yanked) +;; leaf-bob-4.0.0 (yanked) (define test-foo-crate @@ -150,6 +155,16 @@ (define test-bar-dependencies \"crate_id\": \"leaf-bob\", \"kind\": \"normal\", \"req\": \"3.0.1\" + }, + { + \"crate_id\": \"leaf-bob\", + \"kind\": \"dev\", + \"req\": \"^3.0.2\" + }, + { + \"crate_id\": \"leaf-bob\", + \"kind\": \"dev\", + \"req\": \"^4.0.0\" } ] }") @@ -398,6 +413,22 @@ (define test-leaf-bob-crate \"dependencies\": \"/api/v1/crates/leaf-bob/3.0.1/dependencies\" }, \"yanked\": false + }, + { \"id\": 234281, + \"num\": \"3.0.2\", + \"license\": \"MIT OR Apache-2.0\", + \"links\": { + \"dependencies\": \"/api/v1/crates/leaf-bob/3.0.2/dependencies\" + }, + \"yanked\": true + }, + { \"id\": 234282, + \"num\": \"4.0.0\", + \"license\": \"MIT OR Apache-2.0\", + \"links\": { + \"dependencies\": \"/api/v1/crates/leaf-bob/4.0.0/dependencies\" + }, + \"yanked\": true } ] } @@ -863,6 +894,18 @@ (define rust-leaf-bob-3 (description #f) (license #f))) +(define rust-leaf-bob-3.0.2-yanked + (package + (name "rust-leaf-bob") + (version "3.0.2") + (source #f) + (properties '((crate-version-yanked? . #t))) + (build-system #f) + (home-page #f) + (synopsis #f) + (description #f) + (license #f))) + (unless have-guile-semver? (test-skip 1)) (test-assert "crate-recursive-import-honors-existing-packages" (mock @@ -870,7 +913,7 @@ (define rust-leaf-bob-3 (lambda* (name #:optional version) (match name ("rust-leaf-bob" - (list rust-leaf-bob-3)) + (list rust-leaf-bob-3 rust-leaf-bob-3.0.2-yanked)) (_ '())))) (mock ((guix http-client) http-fetch @@ -894,8 +937,16 @@ (define rust-leaf-bob-3 (open-input-string "empty file\n")) ("https://crates.io/api/v1/crates/leaf-bob/3.0.2/dependencies" (open-input-string test-leaf-bob-dependencies)) + ("https://crates.io/api/v1/crates/leaf-bob/4.0.0/download" + (set! test-source-hash + (bytevector->nix-base32-string + (gcrypt-sha256 (string->bytevector "empty file\n" "utf-8")))) + (open-input-string "empty file\n")) + ("https://crates.io/api/v1/crates/leaf-bob/4.0.0/dependencies" + (open-input-string test-leaf-bob-dependencies)) (_ (error "Unexpected URL: " url))))) - (match (crate-recursive-import "bar") + (match (crate-recursive-import "bar" + #:allow-yanked? #t) (((define-public 'rust-bar-1 (package (name "rust-bar") @@ -913,7 +964,12 @@ (define rust-leaf-bob-3 (arguments ('quasiquote (#:cargo-inputs (("rust-leaf-bob" - ('unquote 'rust-leaf-bob-3)))))) + ('unquote 'rust-leaf-bob-3))) + #:cargo-development-inputs + (("rust-leaf-bob" + ('unquote 'rust-leaf-bob-3.0.2-yanked)) + ("rust-leaf-bob" + ('unquote 'rust-leaf-bob-4.0.0-yanked)))))) (home-page "http://example.com") (synopsis "summary") (description "summary") @@ -922,4 +978,135 @@ (define rust-leaf-bob-3 (x (pk 'fail x #f)))))) +(unless have-guile-semver? (test-skip 1)) +(test-assert "crate-import-only-yanked-available" + (mock + ((guix http-client) http-fetch + (lambda (url . rest) + (match url + ("https://crates.io/api/v1/crates/bar" + (open-input-string test-bar-crate)) + ("https://crates.io/api/v1/crates/bar/1.0.0/download" + (set! test-source-hash + (bytevector->nix-base32-string + (gcrypt-sha256 (string->bytevector "empty file\n" "utf-8")))) + (open-input-string "empty file\n")) + ("https://crates.io/api/v1/crates/bar/1.0.0/dependencies" + (open-input-string test-bar-dependencies)) + ("https://crates.io/api/v1/crates/leaf-bob" + (open-input-string test-leaf-bob-crate)) + ("https://crates.io/api/v1/crates/leaf-bob/3.0.1/download" + (set! test-source-hash + (bytevector->nix-base32-string + (gcrypt-sha256 (string->bytevector "empty file\n" "utf-8")))) + (open-input-string "empty file\n")) + ("https://crates.io/api/v1/crates/leaf-bob/3.0.1/dependencies" + (open-input-string test-leaf-bob-dependencies)) + ("https://crates.io/api/v1/crates/leaf-bob/3.0.2/download" + (set! test-source-hash + (bytevector->nix-base32-string + (gcrypt-sha256 (string->bytevector "empty file\n" "utf-8")))) + (open-input-string "empty file\n")) + ("https://crates.io/api/v1/crates/leaf-bob/3.0.2/dependencies" + (open-input-string test-leaf-bob-dependencies)) + ("https://crates.io/api/v1/crates/leaf-bob/4.0.0/download" + (set! test-source-hash + (bytevector->nix-base32-string + (gcrypt-sha256 (string->bytevector "empty file\n" "utf-8")))) + (open-input-string "empty file\n")) + ("https://crates.io/api/v1/crates/leaf-bob/4.0.0/dependencies" + (open-input-string test-leaf-bob-dependencies)) + (_ (error "Unexpected URL: " url))))) + (match (crate-recursive-import "bar" + #:recursive-dev-dependencies? #t + #:allow-yanked? #t) + (((define-public 'rust-leaf-bob-4.0.0-yanked + (package + (name "rust-leaf-bob") + (version "4.0.0") + ($ "; This version was yanked!\n" #t) + (source + (origin + (method url-fetch) + (uri (crate-uri "leaf-bob" version)) + (file-name + (string-append name "-" version ".tar.gz")) + (sha256 + (base32 + (? string? hash))))) + (properties ('quote (('crate-version-yanked? . #t)))) + (build-system cargo-build-system) + (home-page "http://example.com") + (synopsis "summary") + (description "summary") + (license (list license:expat license:asl2.0)))) + (define-public 'rust-leaf-bob-3.0.2-yanked + (package + (name "rust-leaf-bob") + (version "3.0.2") + ($ "; This version was yanked!\n" #t) + (source + (origin + (method url-fetch) + (uri (crate-uri "leaf-bob" version)) + (file-name + (string-append name "-" version ".tar.gz")) + (sha256 + (base32 + (? string? hash))))) + (properties ('quote (('crate-version-yanked? . #t)))) + (build-system cargo-build-system) + (home-page "http://example.com") + (synopsis "summary") + (description "summary") + (license (list license:expat license:asl2.0)))) + (define-public 'rust-leaf-bob-3 + (package + (name "rust-leaf-bob") + (version "3.0.1") + (source + (origin + (method url-fetch) + (uri (crate-uri "leaf-bob" version)) + (file-name + (string-append name "-" version ".tar.gz")) + (sha256 + (base32 + (? string? hash))))) + (build-system cargo-build-system) + (home-page "http://example.com") + (synopsis "summary") + (description "summary") + (license (list license:expat license:asl2.0)))) + (define-public 'rust-bar-1 + (package + (name "rust-bar") + (version "1.0.0") + (source + (origin + (method url-fetch) + (uri (crate-uri "bar" version)) + (file-name + (string-append name "-" version ".tar.gz")) + (sha256 + (base32 + (? string? hash))))) + (build-system cargo-build-system) + (arguments + ('quasiquote (#:cargo-inputs + (("rust-leaf-bob" + ('unquote 'rust-leaf-bob-3))) + #:cargo-development-inputs + (("rust-leaf-bob" + ('unquote 'rust-leaf-bob-3.0.2-yanked)) + ("rust-leaf-bob" + ('unquote 'rust-leaf-bob-4.0.0-yanked)))))) + (home-page "http://example.com") + (synopsis "summary") + (description "summary") + (license (list license:expat license:asl2.0))))) + #t) + (x + (pk 'fail (pretty-print-with-comments (current-output-port) x) #f))))) + (test-end "crate")