import: cpan: Add updater.

* guix/import/cpan.scm (module->dist-name): Fetch the field of interest.
(cpan-fetch): Accept release name rather than module name.
(fix-source-url): Rename to ...
(cpan-source-url): ... this.  Take metadata as parameter.
(package->upstream-name, cpan-version, cpan-package?, latest-release):
New procedures.
(cpan-module->sexp): Use cpan-version and cpan-source-url.
(%cpan-updater): New variable.
* guix/scripts/refresh.scm (%updaters): Add %CPAN-UPDATER.
This commit is contained in:
Eric Bavier 2016-12-12 21:59:27 -06:00
parent d391ad57d6
commit ff55fe5599
No known key found for this signature in database
GPG Key ID: 1EBBD204781F962C
2 changed files with 100 additions and 21 deletions

View File

@ -24,18 +24,23 @@
#:use-module ((ice-9 popen) #:select (open-pipe* close-pipe)) #:use-module ((ice-9 popen) #:select (open-pipe* close-pipe))
#:use-module ((ice-9 rdelim) #:select (read-line)) #:use-module ((ice-9 rdelim) #:select (read-line))
#:use-module (srfi srfi-1) #:use-module (srfi srfi-1)
#:use-module (srfi srfi-26)
#:use-module (json) #:use-module (json)
#:use-module (guix hash) #:use-module (guix hash)
#:use-module (guix store) #:use-module (guix store)
#:use-module (guix utils) #:use-module (guix utils)
#:use-module (guix base32) #:use-module (guix base32)
#:use-module ((guix download) #:select (download-to-store)) #:use-module (guix ui)
#:use-module (guix import utils) #:use-module ((guix download) #:select (download-to-store url-fetch))
#:use-module ((guix import utils) #:select (factorize-uri
flatten assoc-ref*))
#:use-module (guix import json) #:use-module (guix import json)
#:use-module (guix packages) #:use-module (guix packages)
#:use-module (guix upstream)
#:use-module (guix derivations) #:use-module (guix derivations)
#:use-module (gnu packages perl) #:use-module (gnu packages perl)
#:export (cpan->guix-package)) #:export (cpan->guix-package
%cpan-updater))
;;; Commentary: ;;; Commentary:
;;; ;;;
@ -84,28 +89,49 @@
module is distributed with 'Test::Simple', so (module->dist-name \"ok\") would module is distributed with 'Test::Simple', so (module->dist-name \"ok\") would
return \"Test-Simple\"" return \"Test-Simple\""
(assoc-ref (json-fetch (string-append "https://api.metacpan.org/module/" (assoc-ref (json-fetch (string-append "https://api.metacpan.org/module/"
module)) module
"?fields=distribution"))
"distribution")) "distribution"))
(define (cpan-fetch module) (define (package->upstream-name package)
"Return the CPAN name of PACKAGE."
(let* ((properties (package-properties package))
(upstream-name (and=> properties
(cut assoc-ref <> 'upstream-name))))
(or upstream-name
(match (package-source package)
((? origin? origin)
(match (origin-uri origin)
((or (? string? url) (url _ ...))
(match (string-match (string-append "([^/]*)-v?[0-9\\.]+") url)
(#f #f)
(m (match:substring m 1))))
(_ #f)))
(_ #f)))))
(define (cpan-fetch name)
"Return an alist representation of the CPAN metadata for the perl module MODULE, "Return an alist representation of the CPAN metadata for the perl module MODULE,
or #f on failure. MODULE should be e.g. \"Test::Script\"" or #f on failure. MODULE should be e.g. \"Test::Script\""
;; This API always returns the latest release of the module. ;; This API always returns the latest release of the module.
(json-fetch (string-append "https://api.metacpan.org/release/" (json-fetch (string-append "https://api.metacpan.org/release/" name)))
;; XXX: The 'release' api requires the "release"
;; name of the package. This substitution seems
;; reasonably consistent across packages.
(module->name module))))
(define (cpan-home name) (define (cpan-home name)
(string-append "http://search.cpan.org/dist/" name)) (string-append "http://search.cpan.org/dist/" name))
(define (fix-source-url download-url) (define (cpan-source-url meta)
"Return a new download URL based on DOWNLOAD-URL which now uses our mirrors, "Return the download URL for a module's source tarball."
if the original's domain was metacpan." (regexp-substitute/global #f "http[s]?://cpan.metacpan.org"
(regexp-substitute/global #f "http[s]?://cpan.metacpan.org" download-url (assoc-ref meta "download_url")
'pre "mirror://cpan" 'post)) 'pre "mirror://cpan" 'post))
(define (cpan-version meta)
"Return the version number from META."
(match (assoc-ref meta "version")
((? number? version)
;; version is sometimes not quoted in the module json, so it gets
;; imported into Guile as a number, so convert it to a string.
(number->string version))
(version version)))
(define %corelist (define %corelist
(delay (delay
@ -152,10 +178,8 @@ META."
(string-downcase name) (string-downcase name)
(string-append "perl-" (string-downcase name)))) (string-append "perl-" (string-downcase name))))
(define version (define version (cpan-version meta))
(match (assoc-ref meta "version") (define source-url (cpan-source-url meta))
((? number? vrs) (number->string vrs))
((? string? vrs) vrs)))
(define (convert-inputs phases) (define (convert-inputs phases)
;; Convert phase dependencies into a list of name/variable pairs. ;; Convert phase dependencies into a list of name/variable pairs.
@ -193,8 +217,6 @@ META."
(list (list guix-name (list (list guix-name
(list 'quasiquote inputs)))))) (list 'quasiquote inputs))))))
(define source-url (fix-source-url (assoc-ref meta "download_url")))
(let ((tarball (with-store store (let ((tarball (with-store store
(download-to-store store source-url)))) (download-to-store store source-url))))
`(package `(package
@ -224,5 +246,61 @@ META."
(define (cpan->guix-package module-name) (define (cpan->guix-package module-name)
"Fetch the metadata for PACKAGE-NAME from metacpan.org, and return the "Fetch the metadata for PACKAGE-NAME from metacpan.org, and return the
`package' s-expression corresponding to that package, or #f on failure." `package' s-expression corresponding to that package, or #f on failure."
(let ((module-meta (cpan-fetch module-name))) (let ((module-meta (cpan-fetch (module->name module-name))))
(and=> module-meta cpan-module->sexp))) (and=> module-meta cpan-module->sexp)))
(define (cpan-package? package)
"Return #t if PACKAGE is a package from CPAN."
(define cpan-url?
(let ((cpan-rx (make-regexp (string-append "("
"mirror://cpan" "|"
"https?://www.cpan.org" "|"
"https?://cpan.metacpan.org"
")"))))
(lambda (url)
(regexp-exec cpan-rx url))))
(let ((source-url (and=> (package-source package) origin-uri))
(fetch-method (and=> (package-source package) origin-method)))
(and (eq? fetch-method url-fetch)
(match source-url
((? string?)
(cpan-url? source-url))
((source-url ...)
(any cpan-url? source-url))))))
(define (latest-release package)
"Return an <upstream-source> for the latest release of PACKAGE."
(match (cpan-fetch (package->upstream-name package))
(#f #f)
(meta
(let ((core-inputs
(match (package-direct-inputs package)
(((_ inputs _ ...) ...)
(filter-map (match-lambda
((and (? package?)
(? cpan-package?)
(= package->upstream-name
(? core-module? name)))
name)
(else #f))
inputs)))))
;; Warn about inputs that are part of perl's core
(unless (null? core-inputs)
(for-each (lambda (module)
(warning (_ "input '~a' of ~a is in Perl core~%")
module (package-name package)))
core-inputs)))
(let ((version (cpan-version meta))
(url (cpan-source-url meta)))
(upstream-source
(package (package-name package))
(version version)
(urls url))))))
(define %cpan-updater
(upstream-updater
(name 'cpan)
(description "Updater for CPAN packages")
(pred cpan-package?)
(latest latest-release)))

View File

@ -206,6 +206,7 @@ unavailable optional dependencies such as Guile-JSON."
%cran-updater %cran-updater
%bioconductor-updater %bioconductor-updater
%hackage-updater %hackage-updater
((guix import cpan) => %cpan-updater)
((guix import pypi) => %pypi-updater) ((guix import pypi) => %pypi-updater)
((guix import gem) => %gem-updater) ((guix import gem) => %gem-updater)
((guix import github) => %github-updater))) ((guix import github) => %github-updater)))