cran.scm 30 KB

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