import: cran: Allow imports of a specific version.

* guix/import/cran.scm (download): Handle the case where URL is a list.
(fetch-description-from-tarball): New procedure.
(fetch-description): Add #:version parameter.  Honor it when REPOSITORY
is 'cran.  Use 'fetch-description-from-tarball' when REPOSITORY is
'bioconductor.
(description->package): SOURCE-URL may now be a list.
(cran->guix-package): Pass VERSION to 'fetch-description'.
(cran-recursive-import): Add #:version parameter.
* guix/scripts/import/cran.scm (guix-import-cran): Expect a spec rather
than a mere package name.
* doc/guix.texi (Invoking guix import): Document it.
This commit is contained in:
Ludovic Courtès 2021-10-29 21:26:16 +02:00
parent b20cd80ff1
commit 450e1dd52e
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5
3 changed files with 83 additions and 47 deletions

View file

@ -11833,6 +11833,12 @@ The command command below imports metadata for the Cairo R package:
guix import cran Cairo guix import cran Cairo
@end example @end example
You can also ask for a specific version:
@example
guix import cran rasterVis@@0.50.3
@end example
When @option{--recursive} is added, the importer will traverse the When @option{--recursive} is added, the importer will traverse the
dependency graph of the given upstream package recursively and generate dependency graph of the given upstream package recursively and generate
package expressions for all those packages that are not yet in Guix. package expressions for all those packages that are not yet in Guix.

View file

@ -229,15 +229,39 @@ (define download
(let ((store-directory (let ((store-directory
(add-to-store store (basename url) #t "sha256" dir))) (add-to-store store (basename url) #t "sha256" dir)))
(values store-directory changeset))))))) (values store-directory changeset)))))))
(else (download-to-store store url))))))) (else
(match url
((? string?)
(download-to-store store url))
((urls ...)
;; Try all the URLs. A use case where this is useful is when one
;; of the URLs is the /Archive CRAN URL.
(any (cut download-to-store store <>) urls)))))))))
(define (fetch-description repository name) (define (fetch-description-from-tarball url)
"Fetch the tarball at URL, extra its 'DESCRIPTION' file, parse it, and
return the resulting alist."
(match (download url)
(#f #f)
(tarball
(call-with-temporary-directory
(lambda (dir)
(parameterize ((current-error-port (%make-void-port "rw+"))
(current-output-port (%make-void-port "rw+")))
(and (zero? (system* "tar" "--wildcards" "-x"
"--strip-components=1"
"-C" dir
"-f" tarball "*/DESCRIPTION"))
(description->alist
(call-with-input-file (string-append dir "/DESCRIPTION")
read-string)))))))))
(define* (fetch-description repository name #:optional version)
"Return an alist of the contents of the DESCRIPTION file for the R package "Return an alist of the contents of the DESCRIPTION file for the R package
NAME in the given REPOSITORY, or #f in case of failure. NAME is NAME at VERSION in the given REPOSITORY, or #f in case of failure. NAME is
case-sensitive." case-sensitive."
(case repository (case repository
((cran) ((cran)
(let ((url (string-append %cran-url name "/DESCRIPTION")))
(guard (c ((http-get-error? c) (guard (c ((http-get-error? c)
(warning (G_ "failed to retrieve package information \ (warning (G_ "failed to retrieve package information \
from ~a: ~a (~a)~%") from ~a: ~a (~a)~%")
@ -245,7 +269,18 @@ (define (fetch-description repository name)
(http-get-error-code c) (http-get-error-code c)
(http-get-error-reason c)) (http-get-error-reason c))
#f)) #f))
(let* ((port (http-fetch url)) ;; When VERSION is true, we have to download the tarball to get at its
;; 'DESCRIPTION' file; only the latest one is directly accessible over
;; HTTP.
(if version
(let ((urls (list (string-append "mirror://cran/src/contrib/"
name "_" version ".tar.gz")
(string-append "mirror://cran/src/contrib/Archive/"
name "/"
name "_" version ".tar.gz"))))
(fetch-description-from-tarball urls))
(let* ((url (string-append %cran-url name "/DESCRIPTION"))
(port (http-fetch url))
(result (description->alist (read-string port)))) (result (description->alist (read-string port))))
(close-port port) (close-port port)
result)))) result))))
@ -257,22 +292,13 @@ (define (fetch-description repository name)
(and (latest-bioconductor-package-version name) #t) (and (latest-bioconductor-package-version name) #t)
(and (latest-bioconductor-package-version name 'annotation) 'annotation) (and (latest-bioconductor-package-version name 'annotation) 'annotation)
(and (latest-bioconductor-package-version name 'experiment) 'experiment))) (and (latest-bioconductor-package-version name 'experiment) 'experiment)))
;; TODO: Honor VERSION.
(version (latest-bioconductor-package-version name type)) (version (latest-bioconductor-package-version name type))
(url (car (bioconductor-uri name version type))) (url (car (bioconductor-uri name version type)))
(tarball (download url))) (meta (fetch-description-from-tarball url)))
(call-with-temporary-directory (if (boolean? type)
(lambda (dir) meta
(parameterize ((current-error-port (%make-void-port "rw+")) (cons `(bioconductor-type . ,type) meta))))
(current-output-port (%make-void-port "rw+")))
(and (zero? (system* "tar" "--wildcards" "-x"
"--strip-components=1"
"-C" dir
"-f" tarball "*/DESCRIPTION"))
(and=> (description->alist (with-input-from-file
(string-append dir "/DESCRIPTION") read-string))
(lambda (meta)
(if (boolean? type) meta
(cons `(bioconductor-type . ,type) meta))))))))))
((git) ((git)
(and (string-prefix? "http" name) (and (string-prefix? "http" name)
;; Download the git repository at "NAME" ;; Download the git repository at "NAME"
@ -485,7 +511,7 @@ (define (description->package repository meta)
((bioconductor) ((bioconductor)
(list (assoc-ref meta 'bioconductor-type))) (list (assoc-ref meta 'bioconductor-type)))
(else '()))) (else '())))
((url rest ...) url) ((urls ...) urls)
((? string? url) url) ((? string? url) url)
(_ #f))))) (_ #f)))))
(git? (assoc-ref meta 'git)) (git? (assoc-ref meta 'git))
@ -592,7 +618,7 @@ (define cran->guix-package
(lambda* (package-name #:key (repo 'cran) version) (lambda* (package-name #:key (repo 'cran) version)
"Fetch the metadata for PACKAGE-NAME from REPO and return the `package' "Fetch the metadata for PACKAGE-NAME from REPO and return the `package'
s-expression corresponding to that package, or #f on failure." s-expression corresponding to that package, or #f on failure."
(let ((description (fetch-description repo package-name))) (let ((description (fetch-description repo package-name version)))
(if description (if description
(description->package repo description) (description->package repo description)
(case repo (case repo
@ -610,8 +636,9 @@ (define cran->guix-package
(&message (&message
(message "couldn't find meta-data for R package"))))))))))) (message "couldn't find meta-data for R package")))))))))))
(define* (cran-recursive-import package-name #:key (repo 'cran)) (define* (cran-recursive-import package-name #:key (repo 'cran) version)
(recursive-import package-name (recursive-import package-name
#:version version
#:repo repo #:repo repo
#:repo->guix-package cran->guix-package #:repo->guix-package cran->guix-package
#:guix-name cran-guix-name)) #:guix-name cran-guix-name))

View file

@ -27,8 +27,8 @@ (define-module (guix scripts import cran)
#:use-module (guix import utils) #:use-module (guix import utils)
#:use-module (guix scripts import) #:use-module (guix scripts import)
#:use-module (srfi srfi-1) #:use-module (srfi srfi-1)
#:use-module (srfi srfi-11)
#:use-module (srfi srfi-37) #:use-module (srfi srfi-37)
#:use-module (srfi srfi-71)
#:use-module (ice-9 match) #:use-module (ice-9 match)
#:use-module (ice-9 format) #:use-module (ice-9 format)
#:export (guix-import-cran)) #:export (guix-import-cran))
@ -98,21 +98,24 @@ (define (parse-options)
(reverse opts)))) (reverse opts))))
(parameterize ((%input-style (assoc-ref opts 'style))) (parameterize ((%input-style (assoc-ref opts 'style)))
(match args (match args
((package-name) ((spec)
(let ((name version (package-name->name+version spec)))
(if (assoc-ref opts 'recursive) (if (assoc-ref opts 'recursive)
;; Recursive import ;; Recursive import
(with-error-handling (with-error-handling
(map package->definition (map package->definition
(filter identity (filter identity
(cran-recursive-import package-name (cran-recursive-import name
#:version version
#:repo (or (assoc-ref opts 'repo) 'cran))))) #:repo (or (assoc-ref opts 'repo) 'cran)))))
;; Single import ;; Single import
(let ((sexp (cran->guix-package package-name (let ((sexp (cran->guix-package name
#:version version
#:repo (or (assoc-ref opts 'repo) 'cran)))) #:repo (or (assoc-ref opts 'repo) 'cran))))
(unless sexp (unless sexp
(leave (G_ "failed to download description for package '~a'~%") (leave (G_ "failed to download description for package '~a'~%")
package-name)) name))
sexp))) sexp))))
(() (()
(leave (G_ "too few arguments~%"))) (leave (G_ "too few arguments~%")))
((many ...) ((many ...)