import: pypi: Support recursive importing.
* guix/import/pypi.scm (guess-requirements): Use upstream names. (compute-inputs): Return the upstream dependency names as an additional value. (make-pypi-sexp): Likewise. (pypi->guix-package): Memoize it. (pypi-recursive-import): New procedure. * guix/scripts/import/pypi.scm (show-help, %options): Accept "recursive" option. (guix-import-pypi): Use pypi-recursive-import. * doc/guix.texi (Invoking guix import): Document it.
This commit is contained in:
parent
e37f889404
commit
ac906cb7bb
|
@ -6442,6 +6442,14 @@ package:
|
||||||
guix import pypi itsdangerous
|
guix import pypi itsdangerous
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
|
@table @code
|
||||||
|
@item --recursive
|
||||||
|
@itemx -r
|
||||||
|
Traverse the dependency graph of the given upstream package recursively
|
||||||
|
and generate package expressions for all those packages that are not yet
|
||||||
|
in Guix.
|
||||||
|
@end table
|
||||||
|
|
||||||
@item gem
|
@item gem
|
||||||
@cindex gem
|
@cindex gem
|
||||||
Import metadata from @uref{https://rubygems.org/,
|
Import metadata from @uref{https://rubygems.org/,
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#:use-module (ice-9 match)
|
#:use-module (ice-9 match)
|
||||||
#:use-module (ice-9 pretty-print)
|
#:use-module (ice-9 pretty-print)
|
||||||
#:use-module (ice-9 regex)
|
#:use-module (ice-9 regex)
|
||||||
|
#:use-module (ice-9 receive)
|
||||||
#: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 (srfi srfi-26)
|
||||||
|
@ -47,6 +48,7 @@
|
||||||
#:use-module ((guix licenses) #:prefix license:)
|
#:use-module ((guix licenses) #:prefix license:)
|
||||||
#:use-module (guix build-system python)
|
#:use-module (guix build-system python)
|
||||||
#:export (guix-package->pypi-name
|
#:export (guix-package->pypi-name
|
||||||
|
pypi-recursive-import
|
||||||
pypi->guix-package
|
pypi->guix-package
|
||||||
%pypi-updater))
|
%pypi-updater))
|
||||||
|
|
||||||
|
@ -162,7 +164,7 @@ cannot determine package dependencies"))
|
||||||
((or (string-null? line) (comment? line))
|
((or (string-null? line) (comment? line))
|
||||||
(loop result))
|
(loop result))
|
||||||
(else
|
(else
|
||||||
(loop (cons (python->package-name (clean-requirement line))
|
(loop (cons (clean-requirement line)
|
||||||
result))))))))))
|
result))))))))))
|
||||||
|
|
||||||
(define (read-wheel-metadata wheel-archive)
|
(define (read-wheel-metadata wheel-archive)
|
||||||
|
@ -182,9 +184,7 @@ cannot determine package dependencies"))
|
||||||
(hash-ref (list-ref run_requires 0)
|
(hash-ref (list-ref run_requires 0)
|
||||||
"requires")
|
"requires")
|
||||||
'())))
|
'())))
|
||||||
(map (lambda (r)
|
(map clean-requirement requirements)))))
|
||||||
(python->package-name (clean-requirement r)))
|
|
||||||
requirements)))))
|
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(delete-file json-file)
|
(delete-file json-file)
|
||||||
(rmdir dirname))))))
|
(rmdir dirname))))))
|
||||||
|
@ -237,16 +237,21 @@ cannot determine package dependencies"))
|
||||||
|
|
||||||
(define (compute-inputs source-url wheel-url tarball)
|
(define (compute-inputs source-url wheel-url tarball)
|
||||||
"Given the SOURCE-URL of an already downloaded TARBALL, return a list of
|
"Given the SOURCE-URL of an already downloaded TARBALL, return a list of
|
||||||
name/variable pairs describing the required inputs of this package."
|
name/variable pairs describing the required inputs of this package. Also
|
||||||
(sort
|
return the unaltered list of upstream dependency names."
|
||||||
(map (lambda (input)
|
(let ((dependencies
|
||||||
(list input (list 'unquote (string->symbol input))))
|
(remove (cut string=? "argparse" <>)
|
||||||
(remove (cut string=? "python-argparse" <>)
|
(guess-requirements source-url wheel-url tarball))))
|
||||||
(guess-requirements source-url wheel-url tarball)))
|
(values (sort
|
||||||
(lambda args
|
(map (lambda (input)
|
||||||
(match args
|
(let ((guix-name (python->package-name input)))
|
||||||
(((a _ ...) (b _ ...))
|
(list guix-name (list 'unquote (string->symbol guix-name)))))
|
||||||
(string-ci<? a b))))))
|
dependencies)
|
||||||
|
(lambda args
|
||||||
|
(match args
|
||||||
|
(((a _ ...) (b _ ...))
|
||||||
|
(string-ci<? a b)))))
|
||||||
|
dependencies)))
|
||||||
|
|
||||||
(define (make-pypi-sexp name version source-url wheel-url home-page synopsis
|
(define (make-pypi-sexp name version source-url wheel-url home-page synopsis
|
||||||
description license)
|
description license)
|
||||||
|
@ -255,46 +260,58 @@ VERSION, SOURCE-URL, HOME-PAGE, SYNOPSIS, DESCRIPTION, and LICENSE."
|
||||||
(call-with-temporary-output-file
|
(call-with-temporary-output-file
|
||||||
(lambda (temp port)
|
(lambda (temp port)
|
||||||
(and (url-fetch source-url temp)
|
(and (url-fetch source-url temp)
|
||||||
`(package
|
(receive (input-package-names upstream-dependency-names)
|
||||||
(name ,(python->package-name name))
|
(compute-inputs source-url wheel-url temp)
|
||||||
(version ,version)
|
(values
|
||||||
(source (origin
|
`(package
|
||||||
(method url-fetch)
|
(name ,(python->package-name name))
|
||||||
|
(version ,version)
|
||||||
|
(source (origin
|
||||||
|
(method url-fetch)
|
||||||
|
|
||||||
;; Sometimes 'pypi-uri' doesn't quite work due to mixed
|
;; Sometimes 'pypi-uri' doesn't quite work due to mixed
|
||||||
;; cases in NAME, for instance, as is the case with
|
;; cases in NAME, for instance, as is the case with
|
||||||
;; "uwsgi". In that case, fall back to a full URL.
|
;; "uwsgi". In that case, fall back to a full URL.
|
||||||
(uri (pypi-uri ,(string-downcase name) version))
|
(uri (pypi-uri ,(string-downcase name) version))
|
||||||
(sha256
|
(sha256
|
||||||
(base32
|
(base32
|
||||||
,(guix-hash-url temp)))))
|
,(guix-hash-url temp)))))
|
||||||
(build-system python-build-system)
|
(build-system python-build-system)
|
||||||
,@(maybe-inputs (compute-inputs source-url wheel-url temp))
|
,@(maybe-inputs input-package-names)
|
||||||
(home-page ,home-page)
|
(home-page ,home-page)
|
||||||
(synopsis ,synopsis)
|
(synopsis ,synopsis)
|
||||||
(description ,description)
|
(description ,description)
|
||||||
(license ,(license->symbol license)))))))
|
(license ,(license->symbol license)))
|
||||||
|
upstream-dependency-names))))))
|
||||||
|
|
||||||
(define (pypi->guix-package package-name)
|
(define pypi->guix-package
|
||||||
"Fetch the metadata for PACKAGE-NAME from pypi.org, and return the
|
(memoize
|
||||||
|
(lambda* (package-name)
|
||||||
|
"Fetch the metadata for PACKAGE-NAME from pypi.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 ((package (pypi-fetch package-name)))
|
(let ((package (pypi-fetch package-name)))
|
||||||
(and package
|
(and package
|
||||||
(guard (c ((missing-source-error? c)
|
(guard (c ((missing-source-error? c)
|
||||||
(let ((package (missing-source-error-package c)))
|
(let ((package (missing-source-error-package c)))
|
||||||
(leave (G_ "no source release for pypi package ~a ~a~%")
|
(leave (G_ "no source release for pypi package ~a ~a~%")
|
||||||
(assoc-ref* package "info" "name")
|
(assoc-ref* package "info" "name")
|
||||||
(assoc-ref* package "info" "version")))))
|
(assoc-ref* package "info" "version")))))
|
||||||
(let ((name (assoc-ref* package "info" "name"))
|
(let ((name (assoc-ref* package "info" "name"))
|
||||||
(version (assoc-ref* package "info" "version"))
|
(version (assoc-ref* package "info" "version"))
|
||||||
(release (assoc-ref (latest-source-release package) "url"))
|
(release (assoc-ref (latest-source-release package) "url"))
|
||||||
(wheel (assoc-ref (latest-wheel-release package) "url"))
|
(wheel (assoc-ref (latest-wheel-release package) "url"))
|
||||||
(synopsis (assoc-ref* package "info" "summary"))
|
(synopsis (assoc-ref* package "info" "summary"))
|
||||||
(description (assoc-ref* package "info" "summary"))
|
(description (assoc-ref* package "info" "summary"))
|
||||||
(home-page (assoc-ref* package "info" "home_page"))
|
(home-page (assoc-ref* package "info" "home_page"))
|
||||||
(license (string->license (assoc-ref* package "info" "license"))))
|
(license (string->license (assoc-ref* package "info" "license"))))
|
||||||
(make-pypi-sexp name version release wheel home-page synopsis
|
(make-pypi-sexp name version release wheel home-page synopsis
|
||||||
description license))))))
|
description license))))))))
|
||||||
|
|
||||||
|
(define (pypi-recursive-import package-name)
|
||||||
|
(recursive-import package-name #f
|
||||||
|
#:repo->guix-package (lambda (name repo)
|
||||||
|
(pypi->guix-package name))
|
||||||
|
#:guix-name python->package-name))
|
||||||
|
|
||||||
(define (string->license str)
|
(define (string->license str)
|
||||||
"Convert the string STR into a license object."
|
"Convert the string STR into a license object."
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
;;; GNU Guix --- Functional package management for GNU
|
;;; GNU Guix --- Functional package management for GNU
|
||||||
;;; Copyright © 2014 David Thompson <davet@gnu.org>
|
;;; Copyright © 2014 David Thompson <davet@gnu.org>
|
||||||
|
;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
|
||||||
;;;
|
;;;
|
||||||
;;; This file is part of GNU Guix.
|
;;; This file is part of GNU Guix.
|
||||||
;;;
|
;;;
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
#:use-module (srfi srfi-1)
|
#:use-module (srfi srfi-1)
|
||||||
#:use-module (srfi srfi-11)
|
#:use-module (srfi srfi-11)
|
||||||
#:use-module (srfi srfi-37)
|
#:use-module (srfi srfi-37)
|
||||||
|
#:use-module (srfi srfi-41)
|
||||||
#:use-module (ice-9 match)
|
#:use-module (ice-9 match)
|
||||||
#:use-module (ice-9 format)
|
#:use-module (ice-9 format)
|
||||||
#:export (guix-import-pypi))
|
#:export (guix-import-pypi))
|
||||||
|
@ -43,6 +45,8 @@ Import and convert the PyPI package for PACKAGE-NAME.\n"))
|
||||||
(display (G_ "
|
(display (G_ "
|
||||||
-h, --help display this help and exit"))
|
-h, --help display this help and exit"))
|
||||||
(display (G_ "
|
(display (G_ "
|
||||||
|
-r, --recursive import packages recursively"))
|
||||||
|
(display (G_ "
|
||||||
-V, --version display version information and exit"))
|
-V, --version display version information and exit"))
|
||||||
(newline)
|
(newline)
|
||||||
(show-bug-report-information))
|
(show-bug-report-information))
|
||||||
|
@ -56,6 +60,9 @@ Import and convert the PyPI package for PACKAGE-NAME.\n"))
|
||||||
(option '(#\V "version") #f #f
|
(option '(#\V "version") #f #f
|
||||||
(lambda args
|
(lambda args
|
||||||
(show-version-and-exit "guix import pypi")))
|
(show-version-and-exit "guix import pypi")))
|
||||||
|
(option '(#\r "recursive") #f #f
|
||||||
|
(lambda (opt name arg result)
|
||||||
|
(alist-cons 'recursive #t result)))
|
||||||
%standard-import-options))
|
%standard-import-options))
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,11 +88,22 @@ Import and convert the PyPI package for PACKAGE-NAME.\n"))
|
||||||
(reverse opts))))
|
(reverse opts))))
|
||||||
(match args
|
(match args
|
||||||
((package-name)
|
((package-name)
|
||||||
(let ((sexp (pypi->guix-package package-name)))
|
(if (assoc-ref opts 'recursive)
|
||||||
(unless sexp
|
;; Recursive import
|
||||||
(leave (G_ "failed to download meta-data for package '~a'~%")
|
(map (match-lambda
|
||||||
package-name))
|
((and ('package ('name name) . rest) pkg)
|
||||||
sexp))
|
`(define-public ,(string->symbol name)
|
||||||
|
,pkg))
|
||||||
|
(_ #f))
|
||||||
|
(reverse
|
||||||
|
(stream->list
|
||||||
|
(pypi-recursive-import package-name))))
|
||||||
|
;; Single import
|
||||||
|
(let ((sexp (pypi->guix-package package-name)))
|
||||||
|
(unless sexp
|
||||||
|
(leave (G_ "failed to download meta-data for package '~a'~%")
|
||||||
|
package-name))
|
||||||
|
sexp)))
|
||||||
(()
|
(()
|
||||||
(leave (G_ "too few arguments~%")))
|
(leave (G_ "too few arguments~%")))
|
||||||
((many ...)
|
((many ...)
|
||||||
|
|
Loading…
Reference in New Issue