1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2015 Federico Beffa <beffa@fbengineering.ch>
4 ;;; This file is part of GNU Guix.
6 ;;; GNU Guix is free software; you can redistribute it and/or modify it
7 ;;; under the terms of the GNU General Public License as published by
8 ;;; the Free Software Foundation; either version 3 of the License, or (at
9 ;;; your option) any later version.
11 ;;; GNU Guix is distributed in the hope that it will be useful, but
12 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;;; GNU General Public License for more details.
16 ;;; You should have received a copy of the GNU General Public License
17 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19 (define-module (guix import hackage)
20 #:use-module (ice-9 match)
21 #:use-module (ice-9 regex)
22 #:use-module (ice-9 rdelim)
23 #:use-module (ice-9 receive)
24 #:use-module (ice-9 pretty-print)
25 #:use-module (srfi srfi-26)
26 #:use-module (srfi srfi-34)
27 #:use-module (srfi srfi-35)
28 #:use-module (srfi srfi-11)
29 #:use-module (srfi srfi-1)
30 #:use-module ((guix download) #:select (download-to-store))
31 #:use-module ((guix utils) #:select (package-name->name+version))
32 #:use-module (guix import utils)
33 #:use-module (guix store)
34 #:use-module (guix hash)
35 #:use-module (guix base32)
36 #:use-module ((guix utils) #:select (call-with-temporary-output-file))
37 #:export (hackage->guix-package))
41 ;; Functions used to read a Cabal file.
43 (define ghc-standard-libraries
44 ;; List of libraries distributed with ghc (7.8.4). We include GHC itself as
45 ;; some packages list it.
76 (define package-name-prefix "ghc-")
79 ;; Regular expression matching "key: value"
80 (make-regexp "([a-zA-Z0-9-]+):[ \t]*(\\w?.*)$"))
83 ;; Regular expression matching a section "head sub-head ..."
84 (make-regexp "([a-zA-Z0-9\\(\\)-]+)"))
87 ;; Regexp matching Cabal comment lines.
88 (make-regexp "^ *--"))
90 (define (has-key? line)
91 "Check if LINE includes a key."
92 (regexp-exec key-value-rx line))
94 (define (comment-line? line)
95 "Check if LINE is a comment line."
96 (regexp-exec comment-rx line))
98 (define (line-indentation+rest line)
99 "Returns two results: The number of indentation spaces and the rest of the
100 line (without indentation)."
101 (let loop ((line-lst (string->list line))
103 ;; Sometimes values are spread over multiple lines and new lines start
104 ;; with a comma ',' with the wrong indentation. See e.g. haddock-api.
105 (if (or (null? line-lst)
107 (eqv? (first line-lst) #\space)
108 (eqv? (first line-lst) #\,) ; see, e.g., haddock-api.cabal
109 (eqv? (first line-lst) #\tab))))
110 (values count (list->string line-lst))
111 (loop (cdr line-lst) (+ count 1)))))
113 (define (multi-line-value lines seed)
114 "Function to read a value split across multiple lines. LINES are the
115 remaining input lines to be read. SEED is the value read on the same line as
116 the key. Return two values: A list with values and the remaining lines to be
118 (define (multi-line-value-with-min-indent lines seed min-indent)
121 (let-values (((current-indent value) (line-indentation+rest (first lines)))
122 ((next-line-indent next-line-value)
123 (if (null? (cdr lines))
125 (line-indentation+rest (second lines)))))
126 (if (or (not next-line-indent) (< next-line-indent min-indent)
127 (regexp-exec condition-rx next-line-value))
128 (values (reverse (cons value seed)) (cdr lines))
129 (multi-line-value-with-min-indent (cdr lines) (cons value seed)
132 (let-values (((current-indent value) (line-indentation+rest (first lines))))
133 (multi-line-value-with-min-indent lines seed current-indent)))
135 (define (read-cabal port)
136 "Parses a Cabal file from PORT. Return a list of list pairs:
138 (((head1 sub-head1 ... key1) (value))
139 ((head2 sub-head2 ... key2) (value2))
142 We try do deduce the Cabal format from the following document:
143 https://www.haskell.org/cabal/users-guide/developing-packages.html
145 Keys are case-insensitive. We therefore lowercase them. Values are
146 case-sensitive. Currently only indentation-structured files are parsed.
147 Braces structured files are not handled." ;" <- make emacs happy.
148 (define (read-and-trim-line port)
149 (let ((line (read-line port)))
151 (string-trim-both line #\return)
154 (define (strip-insignificant-lines port)
155 (let loop ((line (read-and-trim-line port))
160 ((or (string-null? line) (comment-line? line))
161 (loop (read-and-trim-line port) result))
163 (loop (read-and-trim-line port) (cons line result))))))
166 ((lines (strip-insignificant-lines port))
167 (indents '()) ; only includes indents at start of section heads.
171 (((current-indent line)
174 (line-indentation+rest (first lines))))
175 ((next-line-indent next-line)
176 (if (or (null? lines) (null? (cdr lines)))
178 (line-indentation+rest (second lines)))))
181 (let ((rx-result (has-key? line)))
184 (let ((key (string-downcase (match:substring rx-result 1)))
185 (value (match:substring rx-result 2)))
187 ;; Simple single line "key: value".
188 ((= next-line-indent current-indent)
189 (loop (cdr lines) indents sections
191 (list (reverse (cons key sections)) (list value))
193 ;; Multi line "key: value\n value cont...".
194 ((> next-line-indent current-indent)
195 (let*-values (((value-lst lines)
196 (multi-line-value (cdr lines)
197 (if (string-null? value)
200 ;; multi-line-value returns to the first line after the
202 (loop lines indents sections
204 (list (reverse (cons key sections)) value-lst)
208 ;; Indentation is reduced. Check by how many levels.
209 (let* ((idx (and=> (list-index
210 (lambda (x) (= next-line-indent x))
213 (if (has-key? next-line) 1 0))))
220 (message "unable to parse Cabal file"))))))
221 (ind (drop indents idx)))
222 (loop (cdr lines) ind sec
224 (list (reverse (cons key sections)) (list value))
226 ;; Start of a new section.
228 (> current-indent (first indents)))
229 (loop (cdr lines) (cons current-indent indents)
230 (cons (string-downcase line) sections) result))
232 (loop (cdr lines) indents
233 (cons (string-downcase line) (cdr sections))
237 ;; Regexp for conditionals.
238 (make-regexp "^if +(.*)$"))
240 (define (split-section section)
241 "Split SECTION in individual words with exception for the predicate of an
243 (let ((rx-result (regexp-exec condition-rx section)))
245 `("if" ,(match:substring rx-result 1))
246 (map match:substring (list-matches sections-rx section)))))
248 (define (join-sections sec1 sec2)
249 (fold-right cons sec2 sec1))
251 (define (pre-process-keys key)
255 (join-sections (split-section sec1) (pre-process-keys rest)))))
257 (define (pre-process-entry-keys entry)
260 (list (pre-process-keys key) value))
263 (define (pre-process-entries-keys entries)
264 "ENTRIES is a list of list pairs, a keys list and a valules list, as
265 produced by 'read-cabal'. Split each element of the keys list into individual
266 words. This pre-processing is used to read flags."
269 (cons (pre-process-entry-keys entry)
270 (pre-process-entries-keys rest)))
274 (define (get-flags pre-processed-entries)
275 "PRE-PROCESSED-ENTRIES is a list of list pairs, a keys list and a values
276 list, as produced by 'read-cabal' and pre-processed by
277 'pre-process-entries-keys'. Return a list of pairs with the name of flags and
278 their default value (one of \"False\" or \"True\") as specified in the Cabal file:
280 ((\"flag1-name\" . \"False-or-True\") ...)." ;" <- make emacs happy
281 (match pre-processed-entries
283 (((("flag" flag-name "default") (flag-val)) rest ...)
284 (cons (cons flag-name flag-val)
292 ;; Functions to read information from the Cabal object created by 'read-cabal'
293 ;; and convert Cabal format dependencies conditionals into equivalent
297 ;; Cabal test keywords
298 (make-regexp "(os|arch|flag|impl) *\\(([ a-zA-Z0-9_.<>=-]+)\\)"))
301 ;; Parentheses within conditions
302 (make-regexp "\\((.+)\\)"))
305 ;; OR operator in conditions
306 (make-regexp " +\\|\\| +"))
309 ;; AND operator in conditions
310 (make-regexp " +&& +"))
313 ;; NOT operator in conditions
314 (make-regexp "^!.+"))
316 (define (bi-op-args str match-lst)
317 "Return a list with the arguments of (logic) bianry operators. MATCH-LST
318 is the result of 'list-match' against a binary operator regexp on STR."
319 (let ((operators (length match-lst)))
320 (map (lambda (from to)
321 (substring str from to))
322 (cons 0 (map match:end match-lst))
323 (append (map match:start match-lst) (list (string-length str))))))
325 (define (bi-op->sexp-like bi-op args)
326 "BI-OP is a string with the name of a Scheme operator which in a Cabal file
327 is represented by a binary operator. ARGS are the arguments of said operator.
328 Return a string representing an S-expression of the operator applied to its
330 (if (= (length args) 1)
332 (string-append "(" bi-op
333 (fold (lambda (arg seed) (string-append seed " " arg))
336 (define (not->sexp-like arg)
337 "If the string ARG is prefixed by a Cabal negation operator, convert it to
338 an equivalent Scheme S-expression string."
339 (if (regexp-exec not-rx arg)
340 (string-append "(not "
341 (substring arg 1 (string-length arg))
345 (define (parens-less-cond->sexp-like conditional)
346 "Convert a Cabal CONDITIONAL string into a string with equivalent Scheme
347 syntax. This procedure accepts only simple conditionals without parentheses."
348 ;; The outher operation is the one with the lowest priority: OR
351 ;; each OR argument may be an AND operation
352 (map (lambda (or-arg)
353 (let ((m-lst (list-matches and-rx or-arg)))
354 ;; is there an AND operation?
355 (if (> (length m-lst) 0)
358 ;; expand NOT operators when there are ANDs
359 (map not->sexp-like (bi-op-args or-arg m-lst)))
360 ;; ... and when there aren't.
361 (not->sexp-like or-arg))))
362 ;; list of OR arguments
363 (bi-op-args conditional (list-matches or-rx conditional)))))
365 (define test-keyword-ornament "__")
367 (define (conditional->sexp-like conditional)
368 "Convert a Cabal CONDITIONAL string into a string with equivalent Scheme
370 ;; First we substitute TEST-KEYWORD-ORNAMENT for parentheses around tests
371 ;; keywords so that parentheses are only used to set precedences. This
372 ;; substantially simplify parsing.
374 (regexp-substitute/global #f tests-rx conditional
375 'pre 1 test-keyword-ornament 2
376 test-keyword-ornament 'post)))
377 (let loop ((sub-cond conditional))
378 (let ((rx-result (regexp-exec parens-rx sub-cond)))
381 (parens-less-cond->sexp-like
383 (match:prefix rx-result)
384 (loop (match:substring rx-result 1))
385 (match:suffix rx-result))))
387 (parens-less-cond->sexp-like sub-cond)))))))
389 (define (eval-flags sexp-like-cond flags)
390 "SEXP-LIKE-COND is a string representing an S-expression conditional. FLAGS
391 is a list of flag name and value pairs as produced by 'get-flags'. Substitute
392 \"#t\" or \"#f\" according to the value of flags. (Default to \"True\")."
397 (let ((rx (make-regexp
398 (string-append "flag" test-keyword-ornament name
399 test-keyword-ornament))))
400 (regexp-substitute/global
402 'pre (if (string-ci= value "False") "#f" "#t") 'post)))
405 (cons '("[a-zA-Z0-9_-]+" . "True") flags)))
407 (define (eval-tests->sexp sexp-like-cond)
408 "In the string SEXP-LIKE-COND substitute test keywords \"os(...)\" and
409 \"arch(...)\" with equivalent Scheme checks. Retrun an S-expression."
410 (with-input-from-string
414 ((type pre-match post-match)
415 (let ((rx (make-regexp
416 (string-append type test-keyword-ornament "(\\w+)"
417 test-keyword-ornament))))
418 (regexp-substitute/global
420 'pre pre-match 2 post-match 'post)))
423 ;; (%current-system) returns, e.g., "x86_64-linux" or "i686-linux".
424 '(("(os|arch)" "(string-match \"" "\" (%current-system))")))
427 (define (eval-impl sexp-like-cond)
428 "Check for the Cabal test \"impl(...)\" in the string SEXP-LIKE-COND.
429 Assume the module declaring the generated package includes a local variable
430 called \"haskell-implementation\" with a string value of the form NAME-VERSION
431 against which we compare."
432 (with-output-to-string
435 (with-input-from-string
439 ((pre-match post-match)
440 (let ((rx-with-version
443 "impl" test-keyword-ornament
444 "([a-zA-Z0-9_-]+) *([<>=]+) *([0-9.]+) *"
445 test-keyword-ornament)))
448 (string-append "impl" test-keyword-ornament "(\\w+)"
449 test-keyword-ornament))))
450 (if (regexp-exec rx-with-version sexp)
451 (regexp-substitute/global
452 #f rx-with-version sexp
453 'pre pre-match 2 " " post-match " \"" 1 "-" 3 "\")" 'post)
454 (regexp-substitute/global
455 #f rx-without-version sexp
456 'pre pre-match "-match \"" 1 "\" " post-match ")" 'post))))
459 '(("(string" "haskell-implementation")))
462 (define (eval-cabal-keywords sexp-like-cond flags)
463 ((compose eval-tests->sexp eval-impl (cut eval-flags <> flags))
466 (define (key->values meta key)
467 "META is the representation of a Cabal file as produced by 'read-cabal'.
468 Return the list of values associated with a specific KEY (a string)."
471 (((((? (lambda(x) (equal? x key)))) v) r ...)
474 (key->values (cdr meta) key))
475 (_ "key Not fount")))
477 (define (key-start-end->entries meta key-start-rx key-end-rx)
478 "META is the representation of a Cabal file as produced by 'read-cabal'.
479 Return all entries whose keys list starts with KEY-START and ends with
483 (and (regexp-exec key-start-rx (first x))
484 (regexp-exec key-end-rx (last x))))))
485 ;; (equal? (list key-start key-end) (list (first x) (last x))))))
488 ((((? pred k) v) r ...)
490 (key-start-end->entries (cdr meta) key-start-rx key-end-rx)))
492 (key-start-end->entries (cdr meta) key-start-rx key-end-rx))
493 (_ "key Not fount"))))
496 (make-regexp "^else$"))
498 (define (count-if-else rx-result-ls)
499 (apply + (map (lambda (m) (if m 1 0)) rx-result-ls)))
501 (define (analyze-entry-cond entry)
502 (let* ((keys (first entry))
503 (vals (second entry))
505 (map (cut regexp-exec condition-rx <>) keys))
507 (map (cut regexp-exec else-rx <>) keys))
508 (cond-no (count-if-else rx-cond-result))
509 (else-no (count-if-else rx-else-result))
510 (cond-idx (list-index (lambda (rx) (if rx #t #f)) rx-cond-result))
511 (else-idx (list-index (lambda (rx) (if rx #t #f)) rx-else-result))
514 ((or (and cond-idx else-idx (< cond-idx else-idx))
515 (and cond-idx (not else-idx)))
518 (split-at rx-cond-result cond-idx) (first tail))))
519 ((or (and cond-idx else-idx (> cond-idx else-idx))
520 (and (not cond-idx) else-idx))
523 (split-at rx-else-result else-idx) (first tail))))
526 (values keys vals rx-cond-result
527 rx-else-result cond-no else-no key-cond)))
529 (define (remove-cond entry cond)
532 (list (cdr (member cond k)) v))))
534 (define (group-and-reduce-level entries group group-cond)
540 (values (reverse true-group) (reverse false-group) entries)
541 (let*-values (((entry) (first entries))
542 ((keys vals rx-cond-result rx-else-result
543 cond-no else-no key-cond)
544 (analyze-entry-cond entry)))
546 ((and (>= (+ cond-no else-no) 1) (string= group-cond key-cond))
547 (loop (cons (remove-cond entry group-cond) true-group) false-group
549 ((and (>= (+ cond-no else-no) 1) (string= key-cond "else"))
550 (loop true-group (cons (remove-cond entry "else") false-group)
553 (values (reverse true-group) (reverse false-group) entries)))))))
555 (define dependencies-rx
556 (make-regexp "([a-zA-Z0-9_-]+) *[^,]*,?"))
558 (define (hackage-name->package-name name)
559 (if (string-prefix? package-name-prefix name)
560 (string-downcase name)
561 (string-append package-name-prefix (string-downcase name))))
563 (define (split-and-filter-dependencies ls names-to-filter)
564 "Split the comma separated list of dependencies LS coming from the Cabal
565 file, filter packages included in NAMES-TO-FILTER and return a list with
566 inputs suitable for the Guix package. Currently the version information is
568 (define (split-at-comma-and-filter d)
571 (let* ((name (string-downcase (match:substring m 1)))
572 (pkg-name (hackage-name->package-name name)))
573 (if (member name names-to-filter)
575 (cons (list pkg-name (list 'unquote (string->symbol pkg-name)))
578 (list-matches dependencies-rx d)))
580 (fold (lambda (d p) (append (split-at-comma-and-filter d) p)) '() ls))
582 (define* (dependencies-cond->sexp meta #:key (include-test-dependencies? #t))
583 "META is the representation of a Cabal file as produced by 'read-cabal'.
584 Return an S-expression containing the list of dependencies as expected by the
585 'inputs' field of a package. The generated S-expressions may include
586 conditionals as defined in the cabal file. During this process we discard the
587 version information of the packages."
588 (define (take-dependencies meta)
589 (let ((key-start-exe (make-regexp "executable"))
590 (key-start-lib (make-regexp "library"))
591 (key-start-tests (make-regexp "test-suite"))
592 (key-end (make-regexp "build-depends")))
594 (key-start-end->entries meta key-start-exe key-end)
595 (key-start-end->entries meta key-start-lib key-end)
596 (if include-test-dependencies?
597 (key-start-end->entries meta key-start-tests key-end)
600 (let ((flags (get-flags (pre-process-entries-keys meta)))
601 (augmented-ghc-std-libs (append (key->values meta "name")
602 ghc-standard-libraries)))
604 (let loop ((entries (take-dependencies meta))
608 (let*-values (((entry) (first entries))
609 ((keys vals rx-cond-result rx-else-result
610 cond-no else-no key-cond)
611 (analyze-entry-cond entry)))
613 ((= (+ cond-no else-no) 0)
616 (split-and-filter-dependencies vals
617 augmented-ghc-std-libs)
620 (let-values (((true-group false-group entries)
621 (group-and-reduce-level entries '()
623 ((cond-final) (eval-cabal-keywords
624 (conditional->sexp-like
625 (last (split-section key-cond)))
629 ((or (eq? cond-final #t) (equal? cond-final '(not #f)))
630 (append (loop true-group '()) result))
631 ((or (eq? cond-final #f) (equal? cond-final '(not #t)))
632 (append (loop false-group '()) result))
634 (let ((true-group-result (loop true-group '()))
635 (false-group-result (loop false-group '())))
637 ((and (null? true-group-result)
638 (null? false-group-result))
640 ((null? false-group-result)
641 (cons `(unquote-splicing
642 (when ,cond-final ,true-group-result))
644 ((null? true-group-result)
645 (cons `(unquote-splicing
646 (unless ,cond-final ,false-group-result))
649 (cons `(unquote-splicing
652 ,false-group-result))
653 result))))))))))))))))
657 ;; Retrive the desired package and its Cabal file from
658 ;; http://hackage.haskell.org and construct the Guix package S-expression.
660 (define (hackage-fetch name-version)
661 "Return the Cabal file for the package NAME-VERSION, or #f on failure. If
662 the version part is omitted from the package name, then return the latest
664 (let*-values (((name version) (package-name->name+version name-version))
667 (string-append "http://hackage.haskell.org/package/"
668 name "-" version "/" name ".cabal")
669 (string-append "http://hackage.haskell.org/package/"
670 name "/" name ".cabal"))))
671 (call-with-temporary-output-file
673 (and (url-fetch url temp)
674 (call-with-input-file temp read-cabal))))))
676 (define string->license
677 ;; List of valid values from
678 ;; https://www.haskell.org
679 ;; /cabal/release/cabal-latest/doc/API/Cabal/Distribution-License.html.
686 ("LGPL-2.1" 'lgpl2.1)
694 ("Apache-2.0" 'asl2.0)
695 ((x) (string->license x))
696 ((lst ...) `(list ,@(map string->license lst)))
699 (define* (hackage-module->sexp meta #:key (include-test-dependencies? #t))
700 "Return the `package' S-expression for a Cabal package. META is the
701 representation of a Cabal file as produced by 'read-cabal'."
704 (first (key->values meta "name")))
707 (first (key->values meta "version")))
710 (let*-values (((description) (key->values meta "description"))
712 (split-at description (- (length description) 1))))
713 (fold-right (lambda (line seed) (string-append line "\n" seed))
714 (first last) lines)))
717 (string-append "http://hackage.haskell.org/package/" name
718 "/" name "-" version ".tar.gz"))
720 ;; Several packages do not have an official home-page other than on Hackage.
722 (let ((home-page-entry (key->values meta "homepage")))
723 (if (null? home-page-entry)
724 (string-append "http://hackage.haskell.org/package/" name)
725 (first home-page-entry))))
727 (define (maybe-inputs input-type inputs)
732 (list (list input-type
733 (list 'quasiquote inputs))))))
735 (let ((tarball (with-store store
736 (download-to-store store source-url))))
738 (name ,(hackage-name->package-name name))
742 (uri (string-append ,@(factorize-uri source-url version)))
746 (bytevector->nix-base32-string (file-sha256 tarball))
747 "failed to download tar archive")))))
748 (build-system haskell-build-system)
749 ,@(maybe-inputs 'inputs
750 (dependencies-cond->sexp meta
751 #:include-test-dependencies?
752 include-test-dependencies?))
753 (home-page ,home-page)
754 (synopsis ,@(key->values meta "synopsis"))
755 (description ,description)
756 (license ,(string->license (key->values meta "license"))))))
758 (define* (hackage->guix-package module-name
759 #:key (include-test-dependencies? #t))
760 "Fetch the Cabal file for PACKAGE-NAME from hackage.haskell.org, and return
761 the `package' S-expression corresponding to that package, or #f on failure."
762 (let ((module-meta (hackage-fetch module-name)))
763 (and=> module-meta (cut hackage-module->sexp <>
764 #:include-test-dependencies?
765 include-test-dependencies?))))
767 ;;; cabal.scm ends here