unofficial mirror of guix-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Federico Beffa <beffa@ieee.org>
To: "Ludovic Courtès" <ludo@gnu.org>
Cc: Guix-devel <guix-devel@gnu.org>
Subject: Re: [PATCH] build-system: Add haskell-build-system.
Date: Mon, 30 Mar 2015 18:48:17 +0200	[thread overview]
Message-ID: <CAKrPhPNOLVBxwYti5ody3+uBkhg9Zv6uDvHVFLXqy-qPR-kioA@mail.gmail.com> (raw)
In-Reply-To: <87k2xyhpce.fsf@gnu.org>

[-- Attachment #1: Type: text/plain, Size: 1042 bytes --]

On Mon, Mar 30, 2015 at 10:16 AM, Ludovic Courtès <ludo@gnu.org> wrote:
> Handling it during profile creation, as you suggest, and avoiding the
> use of a wrapper sounds preferable to me.

OK, lets go for that.

Please find attached an updated patch taking into account all of your
earlier comments.

In the mean time I've found that to generate the documentation an
additional step is required (phase haddock; the name is also the name
of the tool used to generate the doc). I've added a keyword argument
#:haddock? with default value of #t and #:haddock-flags for doc
specific flags.

The documentation is in the form of html files and does seems to
require a fair amount of space. Some random examples:

- mtl libs: 1.2MB / doc: 0.772MB
- text libs: 13MB / doc: 1.7MB
- HTTP libs: 3.9MB / doc: 0.804MB
- network-uri: libs: 1.5MB / doc: 0.168MB
- parsec libs: 3.1MB / doc: 1.1MB

Given that I'm starting to package a bunch of libraries, do we want to
generate a separate output for all of them?

Regards,
Fede

[-- Attachment #2: 0001-build-system-Add-haskell-build-system.patch --]
[-- Type: text/x-diff, Size: 15439 bytes --]

From 87f567c0c3c06da73bd70e2498f7829258ae7d61 Mon Sep 17 00:00:00 2001
From: Federico Beffa <beffa@fbengineering.ch>
Date: Fri, 27 Mar 2015 09:36:56 +0100
Subject: [PATCH 01/22] build-system: Add haskell-build-system.

* guix/build-system/haskell.scm: New file.
* guix/build/haskell-build-system.scm: New file.
---
 guix/build-system/haskell.scm       | 135 ++++++++++++++++++++++
 guix/build/haskell-build-system.scm | 220 ++++++++++++++++++++++++++++++++++++
 2 files changed, 355 insertions(+)
 create mode 100644 guix/build-system/haskell.scm
 create mode 100644 guix/build/haskell-build-system.scm

diff --git a/guix/build-system/haskell.scm b/guix/build-system/haskell.scm
new file mode 100644
index 0000000..79faa5a
--- /dev/null
+++ b/guix/build-system/haskell.scm
@@ -0,0 +1,135 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2015 Federico Beffa <beffa@fbengineering.ch>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix build-system haskell)
+  #:use-module (guix store)
+  #:use-module (guix utils)
+  #:use-module (guix packages)
+  #:use-module (guix derivations)
+  #:use-module (guix build-system)
+  #:use-module (guix build-system gnu)
+  #:use-module (ice-9 match)
+  #:use-module (srfi srfi-26)
+  #:export (haskell-build
+            haskell-build-system))
+
+;; Commentary:
+;;
+;; Standard build procedure for Haskell packages using 'Setup.hs'.  This is
+;; implemented as an extension of 'gnu-build-system'.
+;;
+;; Code:
+
+(define (default-haskell)
+  "Return the default Haskell package."
+  ;; Lazily resolve the binding to avoid a circular dependency.
+  (let ((haskell (resolve-interface '(gnu packages haskell))))
+    (module-ref haskell 'ghc)))
+
+(define* (lower name
+                #:key source inputs native-inputs outputs system target
+                (haskell (default-haskell))
+                #:allow-other-keys
+                #:rest arguments)
+  "Return a bag for NAME."
+  (define private-keywords
+    '(#:target #:haskell #:inputs #:native-inputs))
+
+  (and (not target)                               ;XXX: no cross-compilation
+       (bag
+         (name name)
+         (system system)
+         (host-inputs `(,@(if source
+                              `(("source" ,source))
+                              '())
+                        ,@inputs
+
+                        ;; Keep the standard inputs of 'gnu-build-system'.
+                        ,@(standard-packages)))
+         (build-inputs `(("haskell" ,haskell)
+                         ,@native-inputs))
+         (outputs outputs)
+         (build haskell-build)
+         (arguments (strip-keyword-arguments private-keywords arguments)))))
+
+(define* (haskell-build store name inputs
+                        #:key source
+                        (haddock? #t)
+                        (haddock-flags ''())
+                        (tests? #t)
+                        (test-target "test")
+                        (configure-flags ''())
+                        (phases '(@ (guix build haskell-build-system)
+                                    %standard-phases))
+                        (outputs '("out"))
+                        (search-paths '())
+                        (system (%current-system))
+                        (guile #f)
+                        (imported-modules '((guix build haskell-build-system)
+                                            (guix build gnu-build-system)
+                                            (guix build utils)))
+                        (modules '((guix build haskell-build-system)
+                                   (guix build utils))))
+  "Build SOURCE using HASKELL, and with INPUTS.  This assumes that SOURCE
+provides a 'Setup.hs' file as its build system."
+  (define builder
+    `(begin
+       (use-modules ,@modules)
+       (haskell-build #:name ,name
+                      #:source ,(match (assoc-ref inputs "source")
+                                  (((? derivation? source))
+                                   (derivation->output-path source))
+                                  ((source)
+                                   source)
+                                  (source
+                                   source))
+                      #:configure-flags ,configure-flags
+                      #:haddock-flags ,haddock-flags
+                      #:system ,system
+                      #:test-target ,test-target
+                      #:tests? ,tests?
+                      #:haddock? ,haddock?
+                      #:phases ,phases
+                      #:outputs %outputs
+                      #:search-paths ',(map search-path-specification->sexp
+                                            search-paths)
+                      #:inputs %build-inputs)))
+
+  (define guile-for-build
+    (match guile
+      ((? package?)
+       (package-derivation store guile system #:graft? #f))
+      (#f                                         ; the default
+       (let* ((distro (resolve-interface '(gnu packages commencement)))
+              (guile  (module-ref distro 'guile-final)))
+         (package-derivation store guile system #:graft? #f)))))
+
+  (build-expression->derivation store name builder
+                                #:inputs inputs
+                                #:system system
+                                #:modules imported-modules
+                                #:outputs outputs
+                                #:guile-for-build guile-for-build))
+
+(define haskell-build-system
+  (build-system
+    (name 'haskell)
+    (description "The standard Haskell build system")
+    (lower lower)))
+
+;;; haskell.scm ends here
diff --git a/guix/build/haskell-build-system.scm b/guix/build/haskell-build-system.scm
new file mode 100644
index 0000000..d2aba25
--- /dev/null
+++ b/guix/build/haskell-build-system.scm
@@ -0,0 +1,220 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2015 Federico Beffa <beffa@fbengineering.ch>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix build haskell-build-system)
+  #:use-module ((guix build gnu-build-system) #:prefix gnu:)
+  #:use-module (guix build utils)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-26)
+  #:use-module (ice-9 rdelim)
+  #:use-module (ice-9 regex)
+  #:use-module (ice-9 match)
+  #:export (%standard-phases
+            haskell-build))
+
+;; Commentary:
+;;
+;; Builder-side code of the standard Haskell package build procedure.
+;;
+;; The Haskell compiler, to find libraries, relies on a library database with
+;; a binary cache. For GHC the cache has to be named 'package.cache'. If every
+;; library would generate the cache at build time, then they would clash in
+;; profiles. For this reason we do not generate the cache when we generate
+;; libraries substitutes. Instead:
+;;
+;; - At build time we use the 'setup-compiler' phase to generate a temporary
+;;   library database and its cache.
+;;
+;; - We generate the cache when a profile is created.
+
+;; Code:
+;;
+;; Directory where we create the temporary libraries database with its cache
+;; as required by the compiler.
+(define %tmp-db-dir
+  (string-append (or (getenv "TMP") "/tmp")
+                 "/package.conf.d"))
+
+(define (run-setuphs command params)
+  (let ((setup-file (cond
+                     ((file-exists? "Setup.hs")
+                      "Setup.hs")
+                     ((file-exists? "Setup.lhs")
+                      "Setup.lhs")
+                     (else
+                      #f))))
+    (if setup-file
+        (begin
+          (format #t "running \"runhaskell Setup.hs\" with command ~s \
+and parameters ~s~%"
+                  command params)
+          (zero? (apply system* "runhaskell" setup-file command params)))
+        (error "no Setup.hs nor Setup.lhs found"))))
+
+(define* (configure #:key outputs inputs tests? (configure-flags '())
+                    #:allow-other-keys)
+  "Configure a given Haskell package."
+  (let* ((out (assoc-ref outputs "out"))
+         (input-dirs (match inputs
+                       (((_ . dir) ...)
+                        dir)
+                       (_ '())))
+         (params (append `(,(string-append "--prefix=" out))
+                         `(,(string-append
+                             "--docdir=" out "/share/doc/"
+                             (package-name-version out)))
+                         `(,(string-append "--package-db=" %tmp-db-dir))
+                         '("--global")
+                         `(,(string-append
+                             "--extra-include-dirs="
+                             (list->search-path-as-string
+                              (search-path-as-list '("include") input-dirs)
+                              ":")))
+                         `(,(string-append
+                             "--extra-lib-dirs="
+                             (list->search-path-as-string
+                              (search-path-as-list '("lib") input-dirs)
+                              ":")))
+                         (if tests?
+                             '("--enable-tests")
+                             '())
+                         configure-flags)))
+    (run-setuphs "configure" params)))
+
+(define* (build #:rest empty)
+  "Build a given Haskell package."
+  (run-setuphs "build" '()))
+
+(define* (install #:rest empty)
+  "Install a given Haskell package."
+  (run-setuphs "copy" '()))
+
+(define (package-name-version store-dir)
+  "Given a store directory STORE-DIR return 'name-version' of the package."
+  (let* ((base (basename store-dir)))
+    (string-drop base
+                 (+ 1 (string-index base #\-)))))
+
+(define (grep rx port)
+  "Given a regular-expression RX including a group, read from PORT until the
+first match and return the content of the group."
+  (let ((line (read-line port)))
+    (if (eof-object? line)
+        #f
+        (let ((rx-result (regexp-exec rx line)))
+          (if rx-result
+              (match:substring rx-result 1)
+              (grep rx port))))))
+
+(define* (setup-compiler #:key system inputs outputs #:allow-other-keys)
+  "Setup the compiler environment."
+  (let* ((haskell (assoc-ref inputs "haskell"))
+         (name-version (package-name-version haskell)))
+    (cond
+     ((string-match "ghc" name-version)
+      (make-ghc-package-database system inputs outputs))
+     (else
+      (format #t
+              "Compiler ~a not supported~%" name-version)))))
+
+(define (make-ghc-package-database system inputs outputs)
+  "Generate the GHC package database."
+  (let* ((haskell  (assoc-ref inputs "haskell"))
+         (input-dirs (match inputs
+                       (((_ . dir) ...)
+                        dir)
+                       (_ '())))
+         (conf-dirs (search-path-as-list
+                     `(,(string-append "lib/" system "-"
+                                       (package-name-version haskell)
+                                       "/package.conf.d"))
+                     input-dirs))
+         (conf-files (append-map (cut find-files <> "\\.conf$") conf-dirs)))
+    (mkdir-p %tmp-db-dir)
+    (for-each (lambda (file)
+                (copy-file file
+                           (string-append %tmp-db-dir "/" (basename file))))
+              conf-files)
+    (zero? (system* "ghc-pkg"
+                    (string-append "--package-db=" %tmp-db-dir)
+                    "recache"))))
+
+(define* (register #:key name system inputs outputs #:allow-other-keys)
+  "Generate the compiler registration file for a given Haskell package.  Don't
+generate the cache as it would clash in user profiles."
+  (let* ((out (assoc-ref outputs "out"))
+         (haskell  (assoc-ref inputs "haskell"))
+         (lib (string-append out "/lib"))
+         (config-dir (string-append lib "/" system
+                                    "-" (package-name-version haskell)
+                                    "/package.conf.d"))
+         (id-rx (make-regexp "^id: *(.*)$"))
+         (lib-rx (make-regexp "lib.*\\.(a|so)"))
+         (config-file (string-append config-dir "/" name ".conf"))
+         (params
+          (list (string-append "--gen-pkg-config=" config-file))))
+    (unless (null? (find-files lib lib-rx))
+      (mkdir-p config-dir)
+      (run-setuphs "register" params)
+      (let ((config-file-name+id
+             (call-with-ascii-input-file config-file (cut grep id-rx <>))))
+        (rename-file config-file
+                     (string-append config-dir "/" config-file-name+id
+                                    ".conf"))))
+    #t))
+
+(define* (check #:key tests? test-target #:allow-other-keys)
+  "Run the test suite of a given Haskell package."
+  (if tests?
+      (run-setuphs test-target '())
+      (begin
+        (format #t "test suite not run~%")
+        #t)))
+
+(define* (haddock #:key outputs haddock? haddock-flags #:allow-other-keys)
+  "Run the test suite of a given Haskell package."
+  (if haddock?
+      (let* ((out (assoc-ref outputs "out"))
+             (doc-src (string-append (getcwd) "/dist/doc"))
+             (doc-dest (string-append out "/share/doc/"
+                                      (package-name-version out))))
+        (if (run-setuphs "haddock" haddock-flags)
+            (begin
+              (copy-recursively doc-src doc-dest)
+              #t)
+            #f))
+      #t))
+
+(define %standard-phases
+  (modify-phases gnu:%standard-phases
+    (add-before configure setup-compiler setup-compiler)
+    (add-after install haddock haddock)
+    (add-after install register register)
+    (replace install install)
+    (replace check check)
+    (replace build build)
+    (replace configure configure)))
+
+(define* (haskell-build #:key inputs (phases %standard-phases)
+                        #:allow-other-keys #:rest args)
+  "Build the given Haskell package, applying all of PHASES in order."
+  (apply gnu:gnu-build
+         #:inputs inputs #:phases phases
+         args))
+
+;;; haskell-build-system.scm ends here
-- 
2.2.1


  reply	other threads:[~2015-03-30 16:48 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-03-27  8:59 [PATCH] build-system: Add haskell-build-system Federico Beffa
2015-03-29 13:44 ` Ludovic Courtès
2015-03-29 14:00   ` Ricardo Wurmus
2015-03-29 14:45     ` Federico Beffa
2015-03-30  8:16       ` Ludovic Courtès
2015-03-30 16:48         ` Federico Beffa [this message]
2015-04-02 21:31           ` Ludovic Courtès

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://guix.gnu.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAKrPhPNOLVBxwYti5ody3+uBkhg9Zv6uDvHVFLXqy-qPR-kioA@mail.gmail.com \
    --to=beffa@ieee.org \
    --cc=guix-devel@gnu.org \
    --cc=ludo@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).