#! /bin/sh
# -*-scheme-*-
MES_ARENA=100000000 exec ${SCHEME-guile} -L . --no-auto-compile -e '(configure)' -s "$0" ${1+"$@"}
!#

;;; GNU Mes --- Maxwell Equations of Software
;;; Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
;;;
;;; configure: This file is part of GNU Mes.
;;;
;;; GNU Mes 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 Mes 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 Mes.  If not, see <http://www.gnu.org/licenses/>.

(define-module (configure)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-9)
  #:use-module (srfi srfi-9 gnu)
  #:use-module (srfi srfi-26)
  #:use-module (ice-9 getopt-long)
  #:use-module (ice-9 optargs)
  #:use-module (ice-9 popen)
  #:use-module (ice-9 rdelim)
  #:export (main))

(define *shell* "sh")
(define PACKAGE "mes")
(define PACKAGE-NAME "GNU Mes")
(define PACKAGE-BUGREPORT "bug-mes@gnu.org")
(define VERSION "0.20")

(cond-expand
 (guile)
 (mes (mes-use-module (srfi srfi-1))
      (mes-use-module (srfi srfi-9))
      (mes-use-module (srfi srfi-9 gnu))
      (mes-use-module (srfi srfi-26))
      (mes-use-module (mes getopt-long))
      (mes-use-module (mes guile))
      (mes-use-module (mes misc))
      (mes-use-module (mes optargs))
      (define %host-type "x86_64-unknown-linux-gnu")
      (define OPEN_READ "r")
      (define (canonicalize-path o)
        (if (string-prefix? "/" o) o
            (string-append (getcwd) "/" o)))
      (define (sort lst less)
        lst)
      (define (close-pipe o) 0)
      (define (open-pipe* OPEN_READ . commands)
        (let ((fake-pipe ".pipe"))
          (with-output-to-file fake-pipe
            (lambda _
              (let ((status (apply system* (append commands))))
                (set! close-pipe (lambda _ status)))))
          (open-input-file fake-pipe)))))

(define* (PATH-search-path name #:key (default name) warn?)
  (or (search-path (string-split (getenv "PATH") #\:) name)
      (and (and warn? (format (current-error-port) "warning: not found: ~a\n" name))
           default)))

;;; Utility
(define (logf port string . rest)
  (apply format (cons* port string rest))
  (force-output port)
  #t)

(define (stderr string . rest)
  (apply logf (cons* (current-error-port) string rest)))

(define (stdout string . rest)
  (apply logf (cons* (current-output-port) string rest)))

(define %verbose? #f)

(define (verbose string . rest)
  (if %verbose? (apply stderr (cons string rest))))

(define (gulp-pipe* . command)
  (let* ((err (current-error-port))
         (foo (set-current-error-port (open-output-file ".error")))
         (port (apply open-pipe* OPEN_READ command))
         (output (read-string port))
         (status (close-pipe port))
         (error (with-input-from-file ".error" read-string)))
    (when (file-exists? ".error")
      (delete-file ".error"))
    (set-current-error-port err)
    (verbose "command[~a]: ~s => ~a [~a]\n" status command output error)
    (if (not (zero? status)) ""
        (string-trim-right (string-append output error)))))

(define (tuple< a b)
  (cond
   ((and (null? a) (null? b)) #t)
   ((null? a) (not (null? b)))
   ((null? b) #f)
   ((and (not (< (car a) (car b)))
         (not (< (car b) (car a))))
    (tuple< (cdr a) (cdr b)))
   (else (< (car a) (car b)))))

(define (tuple<= a b)
  (or (equal? a b) (tuple< a b)))

(define (conjoin . predicates)
  (lambda (. arguments)
    (every (cut apply <> arguments) predicates)))

(define (char->char from to char)
  (if (eq? char from) to char))

(define (string-replace-char string from to)
  (string-map (cut char->char from to <>) string))

(define (string-replace-string string from to)
  (cond ((string-contains string from)
         => (lambda (i) (string-replace string to i (+ i (string-length from)))))
        (else string)))

(define (string-replace-string/all string from to)
  (or (and=> (string-contains string from)
             (lambda (i)
               (string-append
                (substring string 0 i)
                to
                (string-replace-string/all
                 (substring string (+ i (string-length from))) from to))))
      string))

;;; Configure

(define-immutable-record-type <dependency>
  (make-dependency name version-expected optional? version-option commands file-name data version-found)
  dependency?
  (name dependency-name)
  (version-expected dependency-version-expected)
  (optional? dependency-optional?)
  (version-option dependency-version-option)
  (commands dependency-commands)
  (file-name dependency-file-name)
  (data dependency-data)
  (version-found dependency-version-found))

(define* (make-dep name #:key (version '(0)) optional? (version-option "--version") (commands (list name)) file-name data)
  (let* ((env-var (getenv (name->shell-name name)))
         (commands (if env-var (cons env-var commands) commands)))
    (make-dependency name version optional? version-option commands file-name data #f)))

(define (find-dep name deps)
  (find (compose (cut equal? <> name) dependency-name) deps))

(define (file-name name deps)
  (and=> (find-dep name deps) dependency-file-name))

(define (variable-name dependency)
  (and=>
   (dependency-name dependency)
   name->shell-name))

(define (name->shell-name name)
  (string-upcase (string-replace-char name #\- #\_)))

(define (->string o)
  (cond ((number? o) (number->string o))
        ((string? o) o)
        (else (format #f "~a" o))))

(define (version->string version)
  (and version (string-join (map ->string version) ".")))

(define (string->version string)
  (let ((split (string-tokenize string
                                (char-set-adjoin char-set:digit #\.))))
    (and (pair? split)
         (let* ((version (sort split (lambda (a b) (> (string-length a) (string-length b)))))
                (version (car version))
                (version (string-tokenize version
                                          (char-set-complement (char-set #\.)))))
           (map string->number version)))))

(define (check-program-version dependency)
  (let ((name (dependency-name dependency))
        (expected (dependency-version-expected dependency))
        (version-option (dependency-version-option dependency))
        (commands (dependency-commands dependency)))
    (let loop ((commands commands))
      (if (null? commands) dependency
          (let ((command (car commands)))
            (stdout "checking for ~a~a... " name
                    (if (null? expected) ""
                        (format #f " [~a]" (version->string expected))))
            (let* ((output (gulp-pipe* command version-option))
                   ;;(foo (stderr "output=~s\n" output))
                   (actual (string->version output))
                   ;;(foo (stderr "actual=~s\n" actual))
                   ;;(foo (stderr "expected=~s\n" expected))
                   (pass? (and actual (tuple< expected actual)))
                   ;;(foo (stderr "PASS?~s\n" pass?))
                   (dependency (set-field dependency (dependency-version-found) actual)))
              (stdout "~a ~a\n" (if pass? (if (pair? actual) "" " yes")
                                    (if actual " no, found" "no"))
                      (or (version->string actual) ""))
              (if pass? (let ((file-name (or (PATH-search-path command)
                                             (dependency-file-name dependency))))
                          (set-field dependency (dependency-file-name) file-name))
                  (loop (cdr commands)))))))))

(define (check-file dependency)
  (stdout "checking for ~a... " (dependency-name dependency))
  (let ((file-name (and (file-exists? (dependency-file-name dependency))
                        (dependency-file-name dependency))))
    (stdout "~a\n" (or file-name ""))
    (set-field dependency (dependency-file-name) file-name)))

(define* (check-header-c cc dependency #:optional (check check-preprocess-header-c))
  (let ((name (dependency-name dependency)))
    (stderr "checking for ~a..." name)
    (let ((result (check cc name)))
      (when (file-exists? ".config.c")
        (delete-file ".config.c"))
      (stderr " ~a\n" (if result "yes" "no"))
      (if result (set-field dependency (dependency-file-name) name)
          dependency))))

(define* (check-compile-c cc dependency #:optional (check check-compile-string-c))
  (let ((name (dependency-name dependency)))
    (stderr "checking for ~a..." name)
    (let ((result (check cc (dependency-data dependency))))
      (when (file-exists? ".config.c")
        (delete-file ".config.c"))
      (stderr " ~a\n" (if result "yes" "no"))
      (if result (set-field dependency (dependency-file-name) name)
          dependency))))

(define* (check-link-c cc dependency #:optional (check check-link-string-c))
  (let ((name (dependency-name dependency)))
    (stderr "checking for ~a..." name)
    (let ((result (check cc (dependency-data dependency))))
      (when (file-exists? ".config.c")
        (delete-file ".config.c"))
      (stderr " ~a\n" (if result "yes" "no"))
      (if result (set-field dependency (dependency-file-name) name)
          dependency))))

(define (check-preprocess-header-c cc header)
  (with-output-to-file ".config.c"
    (cut format #t "#include \"~a\"" header))
  (with-error-to-file "/dev/null"
    (cut zero? (system* cc "-E" "-o" ".config.E" ".config.c"))))

(define (check-compile-string-c cc string)
  (with-output-to-file ".config.c"
    (cut display string))
  (with-error-to-file "/dev/null"
    (cut zero? (system* cc "--std=gnu99" "-c" "-o" ".config.o" ".config.c"))))

(define (check-link-string-c cc string)
  (with-output-to-file ".config.c"
    (cut display string))
  (with-error-to-file "/dev/null"
    (cut zero? (system* cc "--std=gnu99" "-o" ".config" ".config.c"))))

(define (parse-opts args)
  (let* ((option-spec
  	  '((build (value #t))
            (host (value #t))

            (prefix (value #t))
            (program-prefix (value #t))
            (bindir (value #t))
            (datadir (value #t))
            (docdir (value #t))
            (includedir (value #t))
            (libdir (value #t))
            (srcdir (value #t))
            (sysconfdir (value #t))

            (mes)
            (help (single-char #\h))
            (verbose (single-char #\v))
            (with-cheating)
            (with-courage)
            (infodir (value #t))
            (mandir (value #t))
            (disable-colors)
            (enable-colors)
            (disable-silent-rules)
            (enable-silent-rules)
            (with-system-libc)

            (enable-fast-install)       ; Ignored for Guix
            (mandir (value #t))         ; Ignored for Debian
            (localstatedir (value #t))  ; Ignored for Debian
            (libexecdir (value #t))     ; Ignored for Debian
            (runstatedir (value #t))    ; Ignored for Debian
            (disable-maintainer-mode)   ; Ignored for Debian
            (disable-dependency-tracking) ; Ignored for Debian
            )))

    (getopt-long args option-spec)))

(define* (print-help #:optional (port (current-output-port)))
  (format port "\
`configure' configures ~a ~a to adapt to many kinds of systems.

Usage: ./configure [OPTION]... [VAR=VALUE]

To assign environment variables (e.g., CC, CFLAGS...), specify them as
VAR=VALUE.  See below for descriptions of some of the useful variables.

Defaults for the options are specified in brackets.

Options:
  -h, --help           display this help
      --build=BUILD    configure for building on BUILD [guessed]
      --colors         no colorized output
      --disable-silent-rules
                       verbose build output [V=1]
      --host=HOST      cross-compile to build programs to run on HOST [BUILD]
  -v, --verbose        be verbose
  --with-courage       Assert that even if this platform is unsupported,
                       you will be courageous and port GNU Mes to it
                       (see \"Porting GNU Mes\" in the manual.)
  --with-cheating      cheat using Guile instead of Mes
  --with-system-libc   use system libc

Installation directories:
  --prefix=DIR         install in prefix DIR [~a]

  --bindir=DIR         user executables [PREFIX/bin]
  --includedir=DIR     C header files [PREFIX/include]
  --infodir=DIR        info documentation [PREFIX/share/info]
  --libdir=DIR         object code libraries [EPREFIX/lib]
  --mandir=DIR         man pages [PREFIX/share/man]

Program names:
  --program-prefix=PREFIX            prepend PREFIX to installed program names
  --program-suffix=SUFFIX            append SUFFIX to installed program names

Ignored for Guix:
  --enable-fast-install

Ignored for Debian:
  --disable-dependency-tracking
  --disable-maintainer-mode
  --libexecdir=DIR
  --localstatedir=DIR
  --runstatedir=DIR

Some influential environment variables:
  CC                C compiler command
  CFLAGS            C compiler flags
  GUILE             guile command
  GUILD             guild command
  MES_FOR_BUILD     build system MES [can be mes or guile]
  MES_SEED          location of mes-seed
" PACKAGE VERSION (getenv "prefix")))

(define (main args)
  (let* ((options (parse-opts args))
         (build-type (option-ref options 'build %host-type))
         (host-type (option-ref options 'host build-type))

         (prefix "/usr/local")
         (prefix (option-ref options 'prefix prefix))
         (program-prefix (option-ref options 'program-prefix ""))
         (program-suffix (option-ref options 'program-suffix ""))
         (infodir (option-ref options 'infodir "${prefix}/share/info"))
         (mandir (option-ref options 'mandir "${prefix}/share/man"))
         (sysconfdir (option-ref options 'sysconfdir "${prefix}/etc"))

         (bindir (option-ref options 'bindir "${prefix}/bin"))
         (datadir (option-ref options 'datadir "${prefix}/share"))
         (docdir (option-ref options 'docdir "${datadir}/doc/mes-${VERSION}"))
         (includedir (option-ref options 'libdir "${prefix}/include"))
         (libdir (option-ref options 'libdir "${prefix}/lib"))
         (pkgdatadir (string-append datadir "/mes"))
         (guile-effective-version (effective-version))
         (guile-site-dir (if (equal? prefix ".") (canonicalize-path ".")
                             (string-append prefix "/share/guile/site/" guile-effective-version)))
         (guile-site-ccache-dir (if (equal? prefix ".") (canonicalize-path ".")
                                    (string-append prefix "/lib/guile/" guile-effective-version "/site-ccache")))

         (srcdir (dirname (car (command-line))))
         (srcdest (if (equal? srcdir ".") ""
                      (string-append srcdir "/")))
         (abs-top-srcdir (canonicalize-path srcdir))
         (abs-top-builddir (canonicalize-path (getcwd)))
         (top-builddir (if (equal? srcdir ".") "."
                           abs-top-builddir))

         (with-cheating? (option-ref options 'with-cheating #f))
         (with-courage? (option-ref options 'with-courage #f))
         (disable-colors? (option-ref options 'disable-colors #f))
         (enable-colors? (option-ref options 'enable-colors #f))
         (disable-silent-rules? (option-ref options 'disable-silent-rules #f))
         (enable-silent-rules? (option-ref options 'enable-silent-rules #f))
         (with-system-libc? (option-ref options 'with-system-libc #f))
         (vars (filter (cut string-index <> #\=) (option-ref options '() '())))
         (help? (option-ref options 'help #f))
         (mes? (option-ref options 'mes #f)))
    (when help?
      (print-help)
      (exit 0))
    (set! %verbose? (option-ref options 'verbose #f))
    (when %verbose?
      (stderr "configure args=~s\n" args))
    (for-each (lambda (v) (apply setenv (string-split v #\=))) vars)
    (let* ((mes-seed (or (getenv "MES_SEED")
                         (string-append srcdest "../mes-seed")))
           (mes-seed (and mes-seed
                          (file-exists? (string-append mes-seed "/x86-mes/mes.S"))
                          mes-seed))
           (tinycc-prefix (or (getenv "TINYCC_PREFIX")
                              (string-append srcdest "../tinycc-prefix")))
           (gcc (or (getenv "CC") "gcc"))
           (tcc (or (getenv "TCC") "tcc"))
           (mescc (or (getenv "MESCC") "mescc"))
           (deps (fold (lambda (program results)
                         (cons (check-program-version program) results))
                       '()
                       (list (make-dep "hex2" #:version '(0 6))
                             (make-dep "M1" #:version '(0 6))
                             (make-dep "blood-elf" #:version '(0 6))
                             (make-dep "diff" #:optional? #t)
                             (make-dep "guile" #:version '(2 0) #:commands '("guile-2.2" "guile-2.0" "guile-2" "guile") #:optional? #t)
                             (make-dep "mes" #:version '(0 20) #:optional? #t)
                             (make-dep "guix" #:version '(0 13) #:optional? #t)
                             (make-dep "ar" #:version '(2 10) #:optional? #t)
                             (make-dep "sh" #:version '(0) #:optional? #t)
                             (make-dep "bash" #:version '(2 0) #:optional? #t)
                             (make-dep "guild" #:version '(2 0) #:commands '("guild" "guile-tools"))
                             (make-dep "cc" #:commands (list gcc tcc mescc) #:optional? #t)
                             (make-dep "make" #:optional? #t)
                             (make-dep "makeinfo" #:optional? #t)
                             (make-dep "dot" #:version-option "-V" #:optional? #t)
                             (make-dep "help2man" #:version '(1 47) #:optional? #t)
                             (make-dep "perl" #:version '(5) #:optional? #t)
                             (make-dep "git" #:version '(2) #:optional? #t))))
           (guile (file-name "guile" deps))
           (deps (if guile (cons (check-program-version (make-dep "nyacc" #:version '(0 86 0) #:commands (list (string-append guile " -c '(use-modules (nyacc lalr)) (display *nyacc-version*)'")) #:file-name #t))
                                 deps)
                     deps))
           (guile (or guile "guile"))
           (cc (file-name "cc" deps))
           (deps (if cc
                     (cons* (check-header-c cc (make-dep "limits.h"))
                            (check-header-c cc (make-dep "stdio.h" #:optional? #t))
                            deps)
                     deps))
           (deps (cons (check-file (make-dep "tinycc-prefix" #:optional? #t
                                             #:file-name tinycc-prefix))
                       deps))
           (missing (filter (conjoin (negate dependency-file-name)
                                     (negate dependency-optional?)) deps))
           (deps (if cc
                     (cons (check-compile-c cc (make-dep "cc is GNU C" #:data "#if !defined (__GNUC__)
#error no gnuc
#endif
"))
                           deps)
                     deps))
           (gcc? (file-name "cc is GNU C" deps))
           (deps (if cc
                     (cons (check-compile-c cc (make-dep "cc is Mes C" #:data "#if !defined (__MESC__)
#error no mesc
#endif
"))
                           deps)
                     deps))
           (mesc? (file-name "cc is Mes C" deps))
           (deps (if cc
                     (cons (check-compile-c cc (make-dep "cc is Tiny CC" #:data "#if !defined (__TINYCC__)
#error no tinycc
#endif
"))
                           deps)
                     deps))
           (tcc? (file-name "cc is Tiny CC" deps))
           (deps (if cc
                     (cons (check-link-c cc (make-dep "if cc can create executables" #:data "int main () {return 0;}"))
                           deps)
                     deps))
           (system-libc? (and with-system-libc? (file-name "if cc can create executables" deps)))
           (build-type (or (and cc (gulp-pipe* cc "-dumpmachine")) build-type))
           (build-type-list (string-split build-type #\-))
           (mes-cpu (car build-type-list))
           (mes-cpu (cond ((member mes-cpu '("i386" "i486" "i586" "i686")) "x86")
                          ((member mes-cpu '("arm" "armv4" "armv7l")) "arm")
                          (else mes-cpu)))
           (mes-bits (if (member mes-cpu '("x86_64")) "64"
                         "32"))
           (mes-libc (if system-libc? "system" "mes"))

           (kernel-list (filter (compose not (cut equal? <> "unknown")) (cdr build-type-list)))
           (mes-kernel (cond ((or (equal? '("linux" "gnu") kernel-list)
                                  (equal? "linux" (car kernel-list))) "linux")
                             ((equal? '( "gnu") kernel-list) "gnu")
                             (else "unknown")))
           (compiler (if gcc? "gcc" "mescc"))
           (mes-system (string-join (list mes-cpu mes-kernel "mes") "-")))

      (define* (substitute file-name pairs
                           #:key (target (if (string-suffix? ".in" file-name)
                                             (string-drop-right file-name 3) file-name)))
        (system* "mkdir" "-p" (dirname target))
        (with-output-to-file target
          (lambda _
            (let ((in (open-input-file file-name)))
              (let loop ((line (read-line in 'concat)))
                (when (not (eof-object? line))
                  (display (fold (lambda (o result)
                                   (string-replace-string/all result (car o) (cdr o)))
                                 line pairs))
                  (loop (read-line in 'concat))))))))

      (when (and (not (member mes-system '("arm-linux-mes"
                                           "x86-linux-mes"
                                           "x86_64-linux-mes")))
                 (not with-courage?))
        (stderr "platform not supported: ~a
See \"Porting GNU Mes\" in the manual, or try --with-courage\n" mes-system)
        (exit 1))
      (when (pair? missing)
        (stderr "\nMissing dependencies: ~a\n" (string-join (map dependency-name missing)))
        (exit 1))
      (let ((git (find-dep "git" deps)))
        (when (and git
                   (not (file-exists? ".git")))
          ;; Debian wants to run `make clean' from a tarball
          (and (zero? (system* "git" "init"))
               (zero? (system* "git" "add" "."))
               (zero? (system* "touch" ".git-for-build"))
               (zero? (system* "git" "commit" "--allow-empty" "-m" "Import mes")))))

      (let ((pairs `(("@PACKAGE@" . ,PACKAGE)
                     ("@PACKAGE_NAME@" . ,PACKAGE-NAME)
                     ("@PACKAGE_BUGREPORT@" . ,PACKAGE-BUGREPORT)
                     ("@VERSION@" . ,VERSION)

                     ("@build@" . ,build-type)
                     ("@host@" . ,host-type)

                     ("@courageous@" . ,(if with-courage? "true" "false"))
                     ("@compiler@" . ,compiler)
                     ("@mes_bits@" . ,mes-bits)
                     ("@mes_kernel@" . ,mes-kernel)
                     ("@mes_cpu@" . ,mes-cpu)
                     ("@mes_libc@" . ,mes-libc)
                     ("@mes_system@" . ,mes-system)

                     ("@abs_top_srcdir@" . ,abs-top-srcdir)
                     ("@abs_top_builddir@" . ,abs-top-builddir)
                     ("@top_builddir@" . ,top-builddir)

                     ("@srcdest@" . ,srcdest)
                     ("@srcdir@" . ,srcdir)

                     ("@prefix@" . ,prefix)
                     ("@program_prefix@" . ,program-prefix)
                     ("@bindir@" . ,bindir)
                     ("@datadir@" . ,datadir)
                     ("@pkgdatadir@" . ,pkgdatadir)
                     ("@docdir@" . ,docdir)
                     ("@guile_site_ccache_dir@" . ,guile-site-ccache-dir)
                     ("@guile_site_dir@" . ,guile-site-dir)
                     ("@infodir@" . ,infodir)
                     ("@includedir@" . ,includedir)
                     ("@libdir@" . ,libdir)
                     ("@mandir@" . ,mandir)
                     ("@sysconfdir@" . ,sysconfdir)

                     ("@GUILE_EFFECTIVE_VERSION@" . ,(effective-version))
                     ("@colors@" . ,(if disable-colors? "no" "yes"))
                     ("@V@" . ,(if disable-silent-rules? "1" "0"))

                     ("@AR@" . ,(or (file-name "ar" deps) ""))
                     ("@BASH@" . ,(or (file-name "bash" deps) ""))
                     ("@CC@" . ,(or (file-name "cc" deps) ""))
                     ("@DIFF@" . ,(or (file-name "diff" deps) (string-append abs-top-builddir "/pre-inst-env diff.scm")))
                     ("@DOT@" . ,(or (file-name "dot" deps) ""))
                     ("@GIT@" . ,(or (file-name "git" deps) ""))
                     ("@GUILE@" . ,guile)
                     ("@GUIX@" . ,(or (file-name "guix" deps) ""))
                     ("@HELP2MAN@" . ,(or (file-name "help2man" deps) ""))
                     ("@MAKEINFO@" . ,(or (file-name "makeinfo" deps) ""))
                     ("@MES_FOR_BUILD@" . ,(or (file-name "mes" deps)
                                               guile))
                     ("@MES_SEED@" . ,(or mes-seed ""))
                     ("@PERL@" . ,(or (file-name "perl" deps) ""))
                     ("#SCHEME=\"@SCHEME@\"" . ,(if with-cheating? (string-append "\nSCHEME=\"" guile "\"") ""))
                     ("@SCHEME@" . ,(if with-cheating? guile ""))
                     ("@SHELL@" . ,(or (file-name "bash" deps)
                                       (file-name "sh" deps)
                                       "sh"))

                     ("@CFLAGS@" . ,(or (getenv "CFLAGS") ""))
                     ("@HEX2FLAGS@" . ,(or (getenv "HEX2FLAGS") ""))
                     ("@M1FLAGS@" . ,(or (getenv "M1FLAGS") ""))

                     ,@(map
                        (lambda (o)
                          (cons (string-append "@" (variable-name o) "@") (or (format #f "~a" (dependency-file-name o)) "")))
                        deps))))

        (when (and (not cc)
                   (not mes-seed))
          (format (current-error-port) "must supply C compiler or MES_SEED/x86-mes/mes.S\n")
          (exit 2))
        (for-each (lambda (o)
                    (let* ((src (string-append srcdest o))
                           (target (string-drop-right o 3))
                           (target (if (not (string-prefix? "build-aux/" target)) target
                                       (string-drop target (string-length "build-aux/")))))
                      (substitute src pairs #:target target)))
                  '(
                    "build-aux/GNUmakefile.in"
                    "build-aux/config.sh.in"
                    "build-aux/build.sh.in"
                    "build-aux/check.sh.in"
                    "build-aux/install.sh.in"
                    "build-aux/pre-inst-env.in"
                    "build-aux/uninstall.sh.in"
                    "scripts/mesar.in"
                    "scripts/mescc.scm.in"
                    "scripts/mescc.in"
                    ))
        (chmod "pre-inst-env" #o755)
        (chmod "scripts/mesar" #o755)
        (chmod "scripts/mescc" #o755)
        (chmod "scripts/mescc.scm" #o755)
        (chmod "build.sh" #o755)
        (chmod "check.sh" #o755)
        (chmod "install.sh" #o755)
        (chmod "uninstall.sh" #o755)

        (system* "mkdir" "-p" "include/mes")
        (let ((pkgdatadir (gulp-pipe* "sh" "-c" (string-append "prefix=" prefix
                                                               ";datadir=" datadir
                                                               ";echo ${datadir}/mes"))))
          (with-output-to-file "include/mes/config.h"
            (lambda _
              (if system-libc?
                  (display "#define SYSTEM_LIBC 1
")
                  (display "#undef SYSTEM_LIBC
"))
              (display (string-append "
#define MES_VERSION \"" VERSION "\"
#define MES_PKGDATADIR \"" pkgdatadir "\"
")))))
        (substitute (string-append srcdest "build-aux/config.make.in") pairs #:target ".config.make"))

      (let ((make (and=> (file-name "make" deps) basename)))
        (display (string-append "
GNU Mes is configured for
   compiler: " compiler "
   cpu:      " mes-cpu "
   bits:     " mes-bits "
   libc:     " mes-libc "
   kernel:   " mes-kernel "
   system:   " mes-system "

Run:
  " (or make "./build.sh") "           to build mes
  " (or make "./build.sh") " help      for help on other targets\n"))))))
