gem.scm 7.7 KB

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