gem.scm 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2015 David Thompson <davet@gnu.org>
  3. ;;; Copyright © 2016 Ben Woodcroft <donttrustben@gmail.com>
  4. ;;; Copyright © 2018 Oleg Pykhalov <go.wigust@gmail.com>
  5. ;;; Copyright © 2020 Ludovic Courtès <ludo@gnu.org>
  6. ;;;
  7. ;;; This file is part of GNU Guix.
  8. ;;;
  9. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  10. ;;; under the terms of the GNU General Public License as published by
  11. ;;; the Free Software Foundation; either version 3 of the License, or (at
  12. ;;; your option) any later version.
  13. ;;;
  14. ;;; GNU Guix is distributed in the hope that it will be useful, but
  15. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ;;; GNU General Public License for more details.
  18. ;;;
  19. ;;; You should have received a copy of the GNU General Public License
  20. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  21. (define-module (guix import gem)
  22. #:use-module (ice-9 match)
  23. #:use-module (srfi srfi-1)
  24. #:use-module (json)
  25. #:use-module ((guix download) #:prefix download:)
  26. #:use-module (guix import utils)
  27. #:use-module (guix import json)
  28. #:use-module (guix packages)
  29. #:use-module (guix upstream)
  30. #:use-module ((guix licenses) #:prefix license:)
  31. #:use-module (guix base16)
  32. #:use-module (guix base32)
  33. #:use-module ((guix build-system ruby) #:select (rubygems-uri))
  34. #:export (gem->guix-package
  35. %gem-updater
  36. gem-recursive-import))
  37. ;; Gems as defined by the API at <https://rubygems.org/api/v1/gems>.
  38. (define-json-mapping <gem> make-gem gem?
  39. json->gem
  40. (name gem-name) ;string
  41. (platform gem-platform) ;string
  42. (version gem-version) ;string
  43. (authors gem-authors) ;string
  44. (licenses gem-licenses "licenses" ;list of strings
  45. (lambda (licenses)
  46. ;; This is sometimes #nil (the JSON 'null' value). Arrange
  47. ;; to always return a list.
  48. (cond ((not licenses) '())
  49. ((vector? licenses) (vector->list licenses))
  50. (else '()))))
  51. (info gem-info)
  52. (sha256 gem-sha256 "sha" ;bytevector
  53. base16-string->bytevector)
  54. (home-page gem-home-page "homepage_uri") ;string
  55. (dependencies gem-dependencies "dependencies" ;<gem-dependencies>
  56. json->gem-dependencies))
  57. (define-json-mapping <gem-dependencies> make-gem-dependencies
  58. gem-dependencies?
  59. json->gem-dependencies
  60. (development gem-dependencies-development ;list of <gem-dependency>
  61. "development"
  62. json->gem-dependency-list)
  63. (runtime gem-dependencies-runtime ;list of <gem-dependency>
  64. "runtime"
  65. json->gem-dependency-list))
  66. (define (json->gem-dependency-list vector)
  67. (if vector
  68. (map json->gem-dependency (vector->list vector))
  69. '()))
  70. (define-json-mapping <gem-dependency> make-gem-dependency gem-dependency?
  71. json->gem-dependency
  72. (name gem-dependency-name) ;string
  73. (requirements gem-dependency-requirements)) ;string
  74. (define (rubygems-fetch name)
  75. "Return a <gem> record for the package NAME, or #f on failure."
  76. (and=> (json-fetch
  77. (string-append "https://rubygems.org/api/v1/gems/" name ".json"))
  78. json->gem))
  79. (define (ruby-package-name name)
  80. "Given the NAME of a package on RubyGems, return a Guix-compliant name for
  81. the package."
  82. (if (string-prefix? "ruby-" name)
  83. (snake-case name)
  84. (string-append "ruby-" (snake-case name))))
  85. (define (make-gem-sexp name version hash home-page synopsis description
  86. dependencies licenses)
  87. "Return the `package' s-expression for a Ruby package with the given NAME,
  88. VERSION, HASH, HOME-PAGE, DESCRIPTION, DEPENDENCIES, and LICENSES."
  89. `(package
  90. (name ,(ruby-package-name name))
  91. (version ,version)
  92. (source (origin
  93. (method url-fetch)
  94. (uri (rubygems-uri ,name version))
  95. (sha256
  96. (base32
  97. ,(bytevector->nix-base32-string hash)))))
  98. (build-system ruby-build-system)
  99. ,@(if (null? dependencies)
  100. '()
  101. `((propagated-inputs
  102. (,'quasiquote
  103. ,(map (lambda (name)
  104. `(,name
  105. (,'unquote
  106. ,(string->symbol name))))
  107. dependencies)))))
  108. (synopsis ,synopsis)
  109. (description ,description)
  110. (home-page ,home-page)
  111. (license ,(match licenses
  112. (() #f)
  113. ((license) (license->symbol license))
  114. (_ `(list ,@(map license->symbol licenses)))))))
  115. (define* (gem->guix-package package-name #:optional (repo 'rubygems) version)
  116. "Fetch the metadata for PACKAGE-NAME from rubygems.org, and return the
  117. `package' s-expression corresponding to that package, or #f on failure."
  118. (let ((gem (rubygems-fetch package-name)))
  119. (if gem
  120. (let* ((dependencies-names (map gem-dependency-name
  121. (gem-dependencies-runtime
  122. (gem-dependencies gem))))
  123. (dependencies (map (lambda (dep)
  124. (if (string=? dep "bundler")
  125. "bundler" ; special case, no prefix
  126. (ruby-package-name dep)))
  127. dependencies-names))
  128. (licenses (map string->license (gem-licenses gem))))
  129. (values (make-gem-sexp (gem-name gem) (gem-version gem)
  130. (gem-sha256 gem) (gem-home-page gem)
  131. (gem-info gem)
  132. (beautify-description (gem-info gem))
  133. dependencies
  134. licenses)
  135. dependencies-names))
  136. (values #f '()))))
  137. (define (guix-package->gem-name package)
  138. "Given a PACKAGE built from rubygems.org, return the name of the
  139. package on RubyGems."
  140. (let ((source-url (and=> (package-source package) origin-uri)))
  141. ;; The URL has the form:
  142. ;; 'https://rubygems.org/downloads/' +
  143. ;; package name + '-' + version + '.gem'
  144. ;; e.g. "https://rubygems.org/downloads/hashery-2.1.1.gem"
  145. (substring source-url 31 (string-rindex source-url #\-))))
  146. (define (string->license str)
  147. "Convert the string STR into a license object."
  148. (match str
  149. ("GNU LGPL" license:lgpl2.0)
  150. ("GPL" license:gpl3)
  151. ((or "BSD" "BSD License") license:bsd-3)
  152. ((or "MIT" "MIT license" "Expat license") license:expat)
  153. ("Public domain" license:public-domain)
  154. ((or "Apache License, Version 2.0" "Apache 2.0") license:asl2.0)
  155. (_ #f)))
  156. (define gem-package?
  157. (url-prefix-predicate "https://rubygems.org/downloads/"))
  158. (define (latest-release package)
  159. "Return an <upstream-source> for the latest release of PACKAGE."
  160. (let* ((gem-name (guix-package->gem-name package))
  161. (gem (rubygems-fetch gem-name))
  162. (version (gem-version gem))
  163. (url (rubygems-uri gem-name version)))
  164. (upstream-source
  165. (package (package-name package))
  166. (version version)
  167. (urls (list url)))))
  168. (define %gem-updater
  169. (upstream-updater
  170. (name 'gem)
  171. (description "Updater for RubyGem packages")
  172. (pred gem-package?)
  173. (latest latest-release)))
  174. (define* (gem-recursive-import package-name #:optional version)
  175. (recursive-import package-name '()
  176. #:repo->guix-package gem->guix-package
  177. #:guix-name ruby-package-name))