gem.scm 7.8 KB

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