cran.scm 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2015, 2016, 2017, 2018, 2019, 2020, 2021 Ricardo Wurmus <rekado@elephly.net>
  3. ;;; Copyright © 2015, 2016, 2017, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
  4. ;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
  5. ;;; Copyright © 2020 Martin Becze <mjbecze@riseup.net>
  6. ;;; Copyright © 2021 Sarah Morgensen <iskarian@mgsn.dev>
  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 cran)
  23. #:use-module (ice-9 match)
  24. #:use-module (ice-9 regex)
  25. #:use-module (ice-9 popen)
  26. #:use-module ((ice-9 rdelim) #:select (read-string read-line))
  27. #:use-module (srfi srfi-1)
  28. #:use-module (srfi srfi-2)
  29. #:use-module (srfi srfi-11)
  30. #:use-module (srfi srfi-26)
  31. #:use-module (srfi srfi-34)
  32. #:use-module (srfi srfi-35)
  33. #:use-module (ice-9 receive)
  34. #:use-module (web uri)
  35. #:use-module (guix memoization)
  36. #:use-module (guix http-client)
  37. #:use-module (guix diagnostics)
  38. #:use-module (guix hash)
  39. #:use-module (guix i18n)
  40. #:use-module (guix store)
  41. #:use-module (guix base32)
  42. #:use-module ((guix download) #:select (download-to-store))
  43. #:use-module (guix import utils)
  44. #:use-module ((guix build utils)
  45. #:select (find-files
  46. delete-file-recursively
  47. with-directory-excursion))
  48. #:use-module (guix utils)
  49. #:use-module (guix git)
  50. #:use-module ((guix build-system r) #:select (cran-uri bioconductor-uri))
  51. #:use-module (guix ui)
  52. #:use-module (guix upstream)
  53. #:use-module (guix packages)
  54. #:use-module (gnu packages)
  55. #:export (%input-style
  56. cran->guix-package
  57. bioconductor->guix-package
  58. cran-recursive-import
  59. %cran-updater
  60. %bioconductor-updater
  61. %bioconductor-version
  62. cran-package?
  63. bioconductor-package?
  64. bioconductor-data-package?
  65. bioconductor-experiment-package?
  66. description->alist
  67. description->package))
  68. ;;; Commentary:
  69. ;;;
  70. ;;; Generate a package declaration template for the latest version of an R
  71. ;;; package on CRAN, using the DESCRIPTION file downloaded from
  72. ;;; cran.r-project.org.
  73. ;;;
  74. ;;; Code:
  75. (define %input-style
  76. (make-parameter 'variable)) ; or 'specification
  77. (define string->license
  78. (match-lambda
  79. ("AGPL-3" 'agpl3+)
  80. ("Artistic-2.0" 'artistic2.0)
  81. ("Apache License 2.0" 'asl2.0)
  82. ("BSD_2_clause" 'bsd-2)
  83. ("BSD_2_clause + file LICENSE" 'bsd-2)
  84. ("BSD_3_clause" 'bsd-3)
  85. ("BSD_3_clause + file LICENSE" 'bsd-3)
  86. ("GPL" '(list gpl2+ gpl3+))
  87. ("GPL (>= 2)" 'gpl2+)
  88. ("GPL (>= 3)" 'gpl3+)
  89. ("GPL-2" 'gpl2)
  90. ("GPL-3" 'gpl3)
  91. ("LGPL-2" 'lgpl2.0)
  92. ("LGPL-2.1" 'lgpl2.1)
  93. ("LGPL-3" 'lgpl3)
  94. ("LGPL (>= 2)" 'lgpl2.0+)
  95. ("LGPL (>= 2.1)" 'lgpl2.1+)
  96. ("LGPL (>= 3)" 'lgpl3+)
  97. ("MIT" 'expat)
  98. ("MIT + file LICENSE" 'expat)
  99. ((x) (string->license x))
  100. ((lst ...) `(list ,@(map string->license lst)))
  101. (_ #f)))
  102. (define (description->alist description)
  103. "Convert a DESCRIPTION string into an alist."
  104. (let ((lines (string-split description #\newline))
  105. (parse (lambda (line acc)
  106. (if (string-null? line) acc
  107. ;; Keys usually start with a capital letter and end with
  108. ;; ":". There are some exceptions, unfortunately (such
  109. ;; as "biocViews"). There are no blanks in a key.
  110. (if (string-match "^[A-Za-z][^ :]+:( |\n|$)" line)
  111. ;; New key/value pair
  112. (let* ((pos (string-index line #\:))
  113. (key (string-take line pos))
  114. (value (string-drop line (+ 1 pos))))
  115. (cons (cons key
  116. (string-trim-both value))
  117. acc))
  118. ;; This is a continuation of the previous pair
  119. (match-let ((((key . value) . rest) acc))
  120. (cons (cons key (string-join
  121. (list value
  122. (string-trim-both line))))
  123. rest)))))))
  124. (fold parse '() lines)))
  125. (define (format-inputs names)
  126. "Generate a sorted list of package inputs from a list of package NAMES."
  127. (map (lambda (name)
  128. (case (%input-style)
  129. ((specification)
  130. `(specification->package ,name))
  131. (else
  132. (string->symbol name))))
  133. (sort names string-ci<?)))
  134. (define* (maybe-inputs package-inputs #:optional (type 'inputs))
  135. "Given a list of PACKAGE-INPUTS, tries to generate the TYPE field of a
  136. package definition."
  137. (match package-inputs
  138. (()
  139. '())
  140. ((package-inputs ...)
  141. `((,type (list ,@(format-inputs package-inputs)))))))
  142. (define %cran-url "https://cran.r-project.org/web/packages/")
  143. (define %cran-canonical-url "https://cran.r-project.org/package=")
  144. (define %bioconductor-url "https://bioconductor.org/packages/")
  145. ;; The latest Bioconductor release is 3.14. Bioconductor packages should be
  146. ;; updated together.
  147. (define %bioconductor-version "3.14")
  148. (define* (bioconductor-packages-list-url #:optional type)
  149. (string-append "https://bioconductor.org/packages/"
  150. %bioconductor-version
  151. (match type
  152. ('annotation "/data/annotation")
  153. ('experiment "/data/experiment")
  154. (_ "/bioc"))
  155. "/src/contrib/PACKAGES"))
  156. (define* (bioconductor-packages-list #:optional type)
  157. "Return the latest version of package NAME for the current bioconductor
  158. release."
  159. (let ((url (string->uri (bioconductor-packages-list-url type))))
  160. (guard (c ((http-get-error? c)
  161. (warning (G_ "failed to retrieve list of packages \
  162. from ~a: ~a (~a)~%")
  163. (uri->string (http-get-error-uri c))
  164. (http-get-error-code c)
  165. (http-get-error-reason c))
  166. #f))
  167. ;; Split the big list on empty lines, then turn each chunk into an
  168. ;; alist of attributes.
  169. (map (lambda (chunk)
  170. (description->alist (string-join chunk "\n")))
  171. (let* ((port (http-fetch/cached url))
  172. (lines (read-lines port)))
  173. (close-port port)
  174. (chunk-lines lines))))))
  175. (define* (latest-bioconductor-package-version name #:optional type)
  176. "Return the version string corresponding to the latest release of the
  177. bioconductor package NAME, or #F if the package is unknown."
  178. (and=> (find (lambda (meta)
  179. (string=? (assoc-ref meta "Package") name))
  180. (bioconductor-packages-list type))
  181. (cut assoc-ref <> "Version")))
  182. ;; Little helper to download URLs only once.
  183. (define download
  184. (memoize
  185. (lambda* (url #:key method)
  186. (with-store store
  187. (cond
  188. ((eq? method 'git)
  189. (latest-repository-commit store url))
  190. ((eq? method 'hg)
  191. (call-with-temporary-directory
  192. (lambda (dir)
  193. (unless (zero? (system* "hg" "clone" url dir))
  194. (leave (G_ "~A: hg download failed~%") url))
  195. (with-directory-excursion dir
  196. (let* ((port (open-pipe* OPEN_READ "hg" "id" "--id"))
  197. (changeset (string-trim-right (read-string port))))
  198. (close-pipe port)
  199. (for-each delete-file-recursively
  200. (find-files dir "^\\.hg$" #:directories? #t))
  201. (let ((store-directory
  202. (add-to-store store (basename url) #t "sha256" dir)))
  203. (values store-directory changeset)))))))
  204. (else
  205. (match url
  206. ((? string?)
  207. (download-to-store store url))
  208. ((urls ...)
  209. ;; Try all the URLs. A use case where this is useful is when one
  210. ;; of the URLs is the /Archive CRAN URL.
  211. (any (cut download-to-store store <>) urls)))))))))
  212. (define (fetch-description-from-tarball url)
  213. "Fetch the tarball at URL, extra its 'DESCRIPTION' file, parse it, and
  214. return the resulting alist."
  215. (match (download url)
  216. (#f #f)
  217. (tarball
  218. (call-with-temporary-directory
  219. (lambda (dir)
  220. (parameterize ((current-error-port (%make-void-port "rw+"))
  221. (current-output-port (%make-void-port "rw+")))
  222. (and (zero? (system* "tar" "--wildcards" "-x"
  223. "--strip-components=1"
  224. "-C" dir
  225. "-f" tarball "*/DESCRIPTION"))
  226. (description->alist
  227. (call-with-input-file (string-append dir "/DESCRIPTION")
  228. read-string)))))))))
  229. (define* (fetch-description repository name #:optional version)
  230. "Return an alist of the contents of the DESCRIPTION file for the R package
  231. NAME at VERSION in the given REPOSITORY, or #f in case of failure. NAME is
  232. case-sensitive."
  233. (case repository
  234. ((cran)
  235. (guard (c ((http-get-error? c)
  236. (warning (G_ "failed to retrieve package information \
  237. from ~a: ~a (~a)~%")
  238. (uri->string (http-get-error-uri c))
  239. (http-get-error-code c)
  240. (http-get-error-reason c))
  241. #f))
  242. ;; When VERSION is true, we have to download the tarball to get at its
  243. ;; 'DESCRIPTION' file; only the latest one is directly accessible over
  244. ;; HTTP.
  245. (if version
  246. (let ((urls (list (string-append "mirror://cran/src/contrib/"
  247. name "_" version ".tar.gz")
  248. (string-append "mirror://cran/src/contrib/Archive/"
  249. name "/"
  250. name "_" version ".tar.gz"))))
  251. (fetch-description-from-tarball urls))
  252. (let* ((url (string-append %cran-url name "/DESCRIPTION"))
  253. (port (http-fetch url))
  254. (result (description->alist (read-string port))))
  255. (close-port port)
  256. result))))
  257. ((bioconductor)
  258. ;; Currently, the bioconductor project does not offer a way to access a
  259. ;; package's DESCRIPTION file over HTTP, so we determine the version,
  260. ;; download the source tarball, and then extract the DESCRIPTION file.
  261. (and-let* ((type (or
  262. (and (latest-bioconductor-package-version name) #t)
  263. (and (latest-bioconductor-package-version name 'annotation) 'annotation)
  264. (and (latest-bioconductor-package-version name 'experiment) 'experiment)))
  265. ;; TODO: Honor VERSION.
  266. (version (latest-bioconductor-package-version name type))
  267. (url (car (bioconductor-uri name version type)))
  268. (meta (fetch-description-from-tarball url)))
  269. (if (boolean? type)
  270. meta
  271. (cons `(bioconductor-type . ,type) meta))))
  272. ((git)
  273. (and (string-prefix? "http" name)
  274. ;; Download the git repository at "NAME"
  275. (call-with-values
  276. (lambda () (download name #:method 'git))
  277. (lambda (dir commit)
  278. (and=> (description->alist (with-input-from-file
  279. (string-append dir "/DESCRIPTION") read-string))
  280. (lambda (meta)
  281. (cons* `(git . ,name)
  282. `(git-commit . ,commit)
  283. meta)))))))
  284. ((hg)
  285. (and (string-prefix? "http" name)
  286. ;; Download the mercurial repository at "NAME"
  287. (call-with-values
  288. (lambda () (download name #:method 'hg))
  289. (lambda (dir changeset)
  290. (and=> (description->alist (with-input-from-file
  291. (string-append dir "/DESCRIPTION") read-string))
  292. (lambda (meta)
  293. (cons* `(hg . ,name)
  294. `(hg-changeset . ,changeset)
  295. meta)))))))))
  296. (define (listify meta field)
  297. "Look up FIELD in the alist META. If FIELD contains a comma-separated
  298. string, turn it into a list and strip off parenthetic expressions. Return the
  299. empty list when the FIELD cannot be found."
  300. (let ((value (assoc-ref meta field)))
  301. (if (not value)
  302. '()
  303. ;; Strip off parentheses
  304. (let ((items (string-split (regexp-substitute/global
  305. #f "( *\\([^\\)]+\\)) *"
  306. value 'pre 'post)
  307. #\,)))
  308. (remove (lambda (item)
  309. (or (string-null? item)
  310. ;; When there is whitespace inside of items it is
  311. ;; probably because this was not an actual list to
  312. ;; begin with.
  313. (string-any char-set:whitespace item)))
  314. (map string-trim-both items))))))
  315. ;; Trick Guile 3 so that it keeps the 'listify' binding accessible *and*
  316. ;; private even though this module is declarative.
  317. (set! listify listify)
  318. (define default-r-packages
  319. (list "base"
  320. "compiler"
  321. "datasets"
  322. "grDevices"
  323. "graphics"
  324. "grid"
  325. "methods"
  326. "parallel"
  327. "splines"
  328. "stats"
  329. "stats4"
  330. "tcltk"
  331. "tools"
  332. "translations"
  333. "utils"))
  334. ;; The field for system dependencies is often abused to specify non-package
  335. ;; dependencies (such as c++11). This list is used to ignore them.
  336. (define invalid-packages
  337. (list "c++11"
  338. "c++14"
  339. "linux"
  340. "getopt::long"
  341. "xquartz"))
  342. (define cran-guix-name (cut guix-name "r-" <>))
  343. (define (tarball-needs-fortran? tarball)
  344. "Check if the TARBALL contains Fortran source files."
  345. (define (check pattern)
  346. (parameterize ((current-error-port (%make-void-port "rw+"))
  347. (current-output-port (%make-void-port "rw+")))
  348. (zero? (system* "tar" "--wildcards" "--list" pattern "-f" tarball))))
  349. (or (check "*.f90")
  350. (check "*.f95")
  351. (check "*.f")))
  352. (define (directory-needs-fortran? dir)
  353. "Check if the directory DIR contains Fortran source files."
  354. (match (find-files dir "\\.f(90|95)$")
  355. (() #f)
  356. (_ #t)))
  357. (define (needs-fortran? thing tarball?)
  358. "Check if the THING contains Fortran source files."
  359. (if tarball?
  360. (tarball-needs-fortran? thing)
  361. (directory-needs-fortran? thing)))
  362. (define (files-match-pattern? directory regexp . file-patterns)
  363. "Return #T if any of the files matching FILE-PATTERNS in the DIRECTORY match
  364. the given REGEXP."
  365. (let ((pattern (make-regexp regexp)))
  366. (any (lambda (file)
  367. (call-with-input-file file
  368. (lambda (port)
  369. (let loop ()
  370. (let ((line (read-line port)))
  371. (cond
  372. ((eof-object? line) #f)
  373. ((regexp-exec pattern line) #t)
  374. (else (loop))))))))
  375. (apply find-files directory file-patterns))))
  376. (define (tarball-files-match-pattern? tarball regexp . file-patterns)
  377. "Return #T if any of the files represented by FILE-PATTERNS in the TARBALL
  378. match the given REGEXP."
  379. (call-with-temporary-directory
  380. (lambda (dir)
  381. (parameterize ((current-error-port (%make-void-port "rw+")))
  382. (apply system* "tar"
  383. "xf" tarball "-C" dir
  384. `("--wildcards" ,@file-patterns)))
  385. (files-match-pattern? dir regexp))))
  386. (define (directory-needs-zlib? dir)
  387. "Return #T if any of the Makevars files in the src directory DIR contain a
  388. zlib linker flag."
  389. (files-match-pattern? dir "-lz" "(Makevars.*|configure.*)"))
  390. (define (tarball-needs-zlib? tarball)
  391. "Return #T if any of the Makevars files in the src directory of the TARBALL
  392. contain a zlib linker flag."
  393. (tarball-files-match-pattern?
  394. tarball "-lz"
  395. "*/src/Makevars*" "*/src/configure*" "*/configure*"))
  396. (define (needs-zlib? thing tarball?)
  397. "Check if the THING contains files indicating a dependency on zlib."
  398. (if tarball?
  399. (tarball-needs-zlib? thing)
  400. (directory-needs-zlib? thing)))
  401. (define (directory-needs-pkg-config? dir)
  402. "Return #T if any of the Makevars files in the src directory DIR reference
  403. the pkg-config tool."
  404. (files-match-pattern? dir "pkg-config"
  405. "(Makevars.*|configure.*)"))
  406. (define (tarball-needs-pkg-config? tarball)
  407. "Return #T if any of the Makevars files in the src directory of the TARBALL
  408. reference the pkg-config tool."
  409. (tarball-files-match-pattern?
  410. tarball "pkg-config"
  411. "*/src/Makevars*" "*/src/configure*" "*/configure*"))
  412. (define (needs-pkg-config? thing tarball?)
  413. "Check if the THING contains files indicating a dependency on pkg-config."
  414. (if tarball?
  415. (tarball-needs-pkg-config? thing)
  416. (directory-needs-pkg-config? thing)))
  417. (define (needs-knitr? meta)
  418. (member "knitr" (listify meta "VignetteBuilder")))
  419. (define (description->package repository meta)
  420. "Return the `package' s-expression for an R package published on REPOSITORY
  421. from the alist META, which was derived from the R package's DESCRIPTION file."
  422. (let* ((base-url (case repository
  423. ((cran) %cran-url)
  424. ((bioconductor) %bioconductor-url)
  425. ((git) #f)
  426. ((hg) #f)))
  427. (canonical-url-base (case repository
  428. ((cran) %cran-canonical-url)
  429. ((bioconductor) %bioconductor-url)
  430. ((git) #f)))
  431. (uri-helper (case repository
  432. ((cran) cran-uri)
  433. ((bioconductor) bioconductor-uri)
  434. ((git) #f)
  435. ((hg) #f)))
  436. (name (assoc-ref meta "Package"))
  437. (synopsis (assoc-ref meta "Title"))
  438. (version (assoc-ref meta "Version"))
  439. (license (string->license (assoc-ref meta "License")))
  440. ;; Some packages have multiple home pages. Some have none.
  441. (home-page (case repository
  442. ((git) (assoc-ref meta 'git))
  443. ((hg) (assoc-ref meta 'hg))
  444. (else (match (listify meta "URL")
  445. ((url rest ...) url)
  446. (_ (string-append canonical-url-base name))))))
  447. (source-url (case repository
  448. ((git) (assoc-ref meta 'git))
  449. ((hg) (assoc-ref meta 'hg))
  450. (else
  451. (match (apply uri-helper name version
  452. (case repository
  453. ((bioconductor)
  454. (list (assoc-ref meta 'bioconductor-type)))
  455. (else '())))
  456. ((urls ...) urls)
  457. ((? string? url) url)
  458. (_ #f)))))
  459. (git? (assoc-ref meta 'git))
  460. (hg? (assoc-ref meta 'hg))
  461. (source (download source-url #:method (cond
  462. (git? 'git)
  463. (hg? 'hg)
  464. (else #f))))
  465. (sysdepends (append
  466. (if (needs-zlib? source (not (or git? hg?))) '("zlib") '())
  467. (filter (lambda (name)
  468. (not (member name invalid-packages)))
  469. (map string-downcase (listify meta "SystemRequirements")))))
  470. (propagate (filter (lambda (name)
  471. (not (member name (append default-r-packages
  472. invalid-packages))))
  473. (lset-union equal?
  474. (listify meta "Imports")
  475. (listify meta "LinkingTo")
  476. (delete "R"
  477. (listify meta "Depends")))))
  478. (package
  479. `(package
  480. (name ,(cran-guix-name name))
  481. (version ,(case repository
  482. ((git)
  483. `(git-version ,version revision commit))
  484. ((hg)
  485. `(string-append ,version "-" revision "." changeset))
  486. (else version)))
  487. (source (origin
  488. (method ,(cond
  489. (git? 'git-fetch)
  490. (hg? 'hg-fetch)
  491. (else 'url-fetch)))
  492. (uri ,(case repository
  493. ((git)
  494. `(git-reference
  495. (url ,(assoc-ref meta 'git))
  496. (commit commit)))
  497. ((hg)
  498. `(hg-reference
  499. (url ,(assoc-ref meta 'hg))
  500. (changeset changeset)))
  501. (else
  502. `(,(procedure-name uri-helper) ,name version
  503. ,@(or (and=> (assoc-ref meta 'bioconductor-type)
  504. (lambda (type)
  505. (list (list 'quote type))))
  506. '())))))
  507. ,@(cond
  508. (git?
  509. '((file-name (git-file-name name version))))
  510. (hg?
  511. '((file-name (string-append name "-" version "-checkout"))))
  512. (else '()))
  513. (sha256
  514. (base32
  515. ,(bytevector->nix-base32-string
  516. (file-hash* source #:recursive? (or git? hg?)))))))
  517. ,@(if (not (and git? hg?
  518. (equal? (string-append "r-" name)
  519. (cran-guix-name name))))
  520. `((properties ,`(,'quasiquote ((,'upstream-name . ,name)))))
  521. '())
  522. (build-system r-build-system)
  523. ,@(maybe-inputs sysdepends)
  524. ,@(maybe-inputs (map cran-guix-name propagate) 'propagated-inputs)
  525. ,@(maybe-inputs
  526. `(,@(if (needs-fortran? source (not (or git? hg?)))
  527. '("gfortran") '())
  528. ,@(if (needs-pkg-config? source (not (or git? hg?)))
  529. '("pkg-config") '())
  530. ,@(if (needs-knitr? meta)
  531. '("r-knitr") '()))
  532. 'native-inputs)
  533. (home-page ,(if (string-null? home-page)
  534. (string-append base-url name)
  535. home-page))
  536. (synopsis ,synopsis)
  537. (description ,(beautify-description (or (assoc-ref meta "Description")
  538. "")))
  539. (license ,license))))
  540. (values
  541. (case repository
  542. ((git)
  543. `(let ((commit ,(assoc-ref meta 'git-commit))
  544. (revision "1"))
  545. ,package))
  546. ((hg)
  547. `(let ((changeset ,(assoc-ref meta 'hg-changeset))
  548. (revision "1"))
  549. ,package))
  550. (else package))
  551. propagate)))
  552. (define cran->guix-package
  553. (memoize
  554. (lambda* (package-name #:key (repo 'cran) version)
  555. "Fetch the metadata for PACKAGE-NAME from REPO and return the `package'
  556. s-expression corresponding to that package, or #f on failure."
  557. (let ((description (fetch-description repo package-name version)))
  558. (if description
  559. (description->package repo description)
  560. (case repo
  561. ((git)
  562. ;; Retry import from Bioconductor
  563. (cran->guix-package package-name #:repo 'bioconductor))
  564. ((hg)
  565. ;; Retry import from Bioconductor
  566. (cran->guix-package package-name #:repo 'bioconductor))
  567. ((bioconductor)
  568. ;; Retry import from CRAN
  569. (cran->guix-package package-name #:repo 'cran))
  570. (else
  571. (raise (condition
  572. (&message
  573. (message "couldn't find meta-data for R package")))))))))))
  574. (define* (cran-recursive-import package-name #:key (repo 'cran) version)
  575. (recursive-import package-name
  576. #:version version
  577. #:repo repo
  578. #:repo->guix-package cran->guix-package
  579. #:guix-name cran-guix-name))
  580. ;;;
  581. ;;; Updater.
  582. ;;;
  583. (define (package->upstream-name package)
  584. "Return the upstream name of the PACKAGE."
  585. (let* ((properties (package-properties package))
  586. (upstream-name (and=> properties
  587. (cut assoc-ref <> 'upstream-name))))
  588. (if upstream-name
  589. upstream-name
  590. (match (package-source package)
  591. ((? origin? origin)
  592. (match (origin-uri origin)
  593. ((or (? string? url) (url _ ...))
  594. (let ((end (string-rindex url #\_))
  595. (start (string-rindex url #\/)))
  596. ;; The URL ends on
  597. ;; (string-append "/" name "_" version ".tar.gz")
  598. (and start end (substring url (+ start 1) end))))
  599. (_ #f)))
  600. (_ #f)))))
  601. (define (latest-cran-release pkg)
  602. "Return an <upstream-source> for the latest release of the package PKG."
  603. (define upstream-name
  604. (package->upstream-name pkg))
  605. (define meta
  606. (fetch-description 'cran upstream-name))
  607. (and meta
  608. (let ((version (assoc-ref meta "Version")))
  609. ;; CRAN does not provide signatures.
  610. (upstream-source
  611. (package (package-name pkg))
  612. (version version)
  613. (urls (cran-uri upstream-name version))
  614. (input-changes
  615. (changed-inputs pkg
  616. (description->package 'cran meta)))))))
  617. (define (latest-bioconductor-release pkg)
  618. "Return an <upstream-source> for the latest release of the package PKG."
  619. (define upstream-name
  620. (package->upstream-name pkg))
  621. (define version
  622. (latest-bioconductor-package-version upstream-name))
  623. (and version
  624. ;; Bioconductor does not provide signatures.
  625. (upstream-source
  626. (package (package-name pkg))
  627. (version version)
  628. (urls (bioconductor-uri upstream-name version))
  629. (input-changes
  630. (changed-inputs
  631. pkg
  632. (cran->guix-package upstream-name #:repo 'bioconductor))))))
  633. (define (cran-package? package)
  634. "Return true if PACKAGE is an R package from CRAN."
  635. (and (string-prefix? "r-" (package-name package))
  636. ;; Check if the upstream name can be extracted from package uri.
  637. (package->upstream-name package)
  638. ;; Check if package uri(s) are prefixed by "mirror://cran".
  639. ((url-predicate (cut string-prefix? "mirror://cran" <>)) package)))
  640. (define (bioconductor-package? package)
  641. "Return true if PACKAGE is an R package from Bioconductor."
  642. (let ((predicate (lambda (uri)
  643. (and (string-prefix? "https://bioconductor.org" uri)
  644. ;; Data packages are neither listed in SVN nor on
  645. ;; the Github mirror, so we have to exclude them
  646. ;; from the set of bioconductor packages that can be
  647. ;; updated automatically.
  648. (not (string-contains uri "/data/annotation/"))
  649. ;; Experiment packages are in a separate repository.
  650. (not (string-contains uri "/data/experiment/"))))))
  651. (and (string-prefix? "r-" (package-name package))
  652. ((url-predicate predicate) package))))
  653. (define (bioconductor-data-package? package)
  654. "Return true if PACKAGE is an R data package from Bioconductor."
  655. (let ((predicate (lambda (uri)
  656. (and (string-prefix? "https://bioconductor.org" uri)
  657. (string-contains uri "/data/annotation/")))))
  658. (and (string-prefix? "r-" (package-name package))
  659. ((url-predicate predicate) package))))
  660. (define (bioconductor-experiment-package? package)
  661. "Return true if PACKAGE is an R experiment package from Bioconductor."
  662. (let ((predicate (lambda (uri)
  663. (and (string-prefix? "https://bioconductor.org" uri)
  664. (string-contains uri "/data/experiment/")))))
  665. (and (string-prefix? "r-" (package-name package))
  666. ((url-predicate predicate) package))))
  667. (define %cran-updater
  668. (upstream-updater
  669. (name 'cran)
  670. (description "Updater for CRAN packages")
  671. (pred cran-package?)
  672. (latest latest-cran-release)))
  673. (define %bioconductor-updater
  674. (upstream-updater
  675. (name 'bioconductor)
  676. (description "Updater for Bioconductor packages")
  677. (pred bioconductor-package?)
  678. (latest latest-bioconductor-release)))
  679. ;;; cran.scm ends here