mescc: Remove dead code. Thanks Mark Weaver.
[mes.git] / configure
index 2584d723d4cbd7a44fb31127bc617b918cc79414..81f602b4d943596eaf0b511c9b094fe2693f42a0 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,56 +1,32 @@
 #! /bin/sh
-# -*- scheme -*-
-unset LANG LC_ALL
-echo -n "checking for guile..."
-GUILE=$(command -v ${GUILE-guile})
-GUIX=$(command -v ${GUIX-guix})
-export GUILE GUIX
-if [ -x "$GUILE" ]; then
-    echo " $GUILE"
-elif [ -x "$GUIX" ]; then
-    cat <<EOF
-not found
-Missing dependencies, run
-
-    guix environment -l guix.scm
-EOF
-    exit 1
-else
-cat <<EOF
-not found
-Missing dependencies, run
-
-    sudo apt-get install guile-2.2-dev
-EOF
-    exit 1
-fi
-exec ${GUILE-guile} -L . --no-auto-compile -e '(configure)' -s "$0" ${1+"$@"}
+# -*-scheme-*-
+MES_ARENA=100000000 exec ${SCHEME-guile} -L . --no-auto-compile -e '(configure)' -s "$0" ${1+"$@"}
 !#
 
-;;; Mes --- Maxwell Equations of Software
-;;; Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
+;;; GNU Mes --- Maxwell Equations of Software
+;;; Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 ;;;
-;;; configure: This file is part of Mes.
+;;; configure: This file is part of GNU Mes.
 ;;;
-;;; Mes is free software; you can redistribute it and/or modify it
+;;; 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.
 ;;;
-;;; Mes is distributed in the hope that it will be useful, but
+;;; 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 Mes.  If not, see <http://www.gnu.org/licenses/>.
+;;; 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 and-let-star)
-  #:use-module (ice-9 curried-definitions)
   #:use-module (ice-9 getopt-long)
-  #:use-module (ice-9 match)
   #:use-module (ice-9 optargs)
   #:use-module (ice-9 popen)
   #:use-module (ice-9 rdelim)
@@ -58,14 +34,40 @@ exec ${GUILE-guile} -L . --no-auto-compile -e '(configure)' -s "$0" ${1+"$@"}
 
 (define *shell* "sh")
 (define PACKAGE "mes")
-(define VERSION "0.16")
-(define GUILE (or (getenv "guile") "guile"))
-(define GUILE_EFFECTIVE_VERSION (effective-version))
+(define PACKAGE-NAME "GNU Mes")
+(define PACKAGE-BUGREPORT "bug-mes@gnu.org")
+(define VERSION "0.19")
+
+(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 prefix "/usr/local")
-(define infodir "${prefix}/share/info")
-(define mandir "${prefix}/share/man")
-(define sysconfdir "${prefix}/etc")
+(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)
@@ -79,27 +81,24 @@ exec ${GUILE-guile} -L . --no-auto-compile -e '(configure)' -s "$0" ${1+"$@"}
 (define (stdout string . rest)
   (apply logf (cons* (current-output-port) string rest)))
 
-(define *verbose?* #f)
+(define %verbose? #f)
 
 (define (verbose string . rest)
-  (if *verbose?* (apply stderr (cons string rest))))
+  (if %verbose? (apply stderr (cons string rest))))
 
-(define (gulp-pipe command)
-  (let* ((port (open-pipe* OPEN_READ *shell* "-c" command))
+(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)))
-    (verbose "command[~a]: ~s => ~a\n" status command output)
-    (if (not (zero? status)) "" (string-trim-right output #\newline))))
-
-(define* ((->string #:optional (infix "")) h . t)
-  (let ((o (if (pair? t) (cons h t) h)))
-    (match o
-      ((? char?) (make-string 1 o))
-      ((? number?) (number->string o))
-      ((? string?) o)
-      ((? symbol?) (symbol->string o))
-      ((h ... t) (string-join (map (->string) o) ((->string) infix)))
-      (_ ""))))
+         (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
@@ -114,93 +113,195 @@ exec ${GUILE-guile} -L . --no-auto-compile -e '(configure)' -s "$0" ${1+"$@"}
 (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 (version->string version)
-  ((->string '.) version))
 
-(define (string->version string)
-  (and-let* ((version (string-tokenize string
-                                       (char-set-adjoin char-set:digit #\.)))
-             ((pair? version))
-             (version (sort version (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-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* (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)))
+(define (version->string version)
+  (and version (string-join (map ->string version) ".")))
 
-(define optional '())
-(define required '())
-(define* (check-version name expected
-                        #:key
-                        optional?
-                        (deb #f)
-                        (version-option '--version)
-                        (compare tuple<=)
-                        (command name))
-  (stderr "checking for ~a~a..." (basename name)
-          (if (null? expected) ""
-              (format #f " [~a]" (version->string expected))))
-  (let* ((output (gulp-pipe (format #f "~a ~a 2>&1" command version-option)))
-         (actual (string->version output))
-         (pass? (and actual (compare expected actual)))
-         ;(pass? (PATH-search-path command))
-         )
-    (stderr "~a ~a\n" (if pass? (if (pair? actual) "" " yes")
-                          (if actual " no, found" "")) (version->string actual))
-    (or pass?
-        (if (not (pair? name)) (begin (if optional? (set! optional (cons (or deb name) optional))
-                                          (set! required (cons (or deb name) required)))
-                                      pass?)
-            (check-version (cdr name) expected deb version-option compare)))))
-
-(define* (check-pkg-config package expected #:optional (deb #f))
-  (check-version (format #f "pkg-config --modversion ~a" package) expected deb))
-
-(define (check-compile-header-c header)
-  (and (= 0 (system (format #f "echo '#include ~s' | gcc -E - > /dev/null 2>&1" header)))
-       'yes))
-
-(define (check-compile-header-c++ header)
-  (and (= 0 (system (format #f "echo '#include ~s' | gcc --language=c++ --std=c++11 -E - > /dev/null 2>&1" header)))
-       'yes))
-
-(define* (check-header-c header deb #:optional (check check-compile-header-c))
-  (stderr "checking for ~a..." header)
-  (let ((result (check header)))
-    (stderr " ~a\n" (if result result "no"))
-    (if (not result)
-        (set! required (cons deb required)))))
-
-(define* (check-header-c++ header deb #:optional (check check-compile-header-c++))
-  (check-header-c header deb check))
-
-(define guix?
-  (and (zero? (system "guix --version 1>/dev/null 2>/dev/null")) 1))
-;;;
+(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))
-            (help (single-char #\h))
+
             (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
-            (includedir (value #t))     ; Ignored for Debian
             (mandir (value #t))         ; Ignored for Debian
             (localstatedir (value #t))  ; Ignored for Debian
-            (libdir (value #t))         ; Ignored for Debian
             (libexecdir (value #t))     ; Ignored for Debian
             (runstatedir (value #t))    ; Ignored for Debian
             (disable-maintainer-mode)   ; Ignored for Debian
@@ -223,163 +324,354 @@ 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 [BUILD_DEBUG=1]
+                       verbose build output [V=1]
       --host=HOST      cross-compile to build programs to run on HOST [BUILD]
   -v, --verbose        be verbose
-  --with-courage       assert being courageous to configure for unsupported platform
+  --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
-  --includedir=DIR
-  --libdir=DIR
   --libexecdir=DIR
   --localstatedir=DIR
-  --mandir=DIR
   --runstatedir=DIR
 
 Some influential environment variables:
   CC                C compiler command
   CFLAGS            C compiler flags
-  CC32              x86 C compiler command
-  CC32_CFLAGS       x86 C compiler flags
   GUILE             guile command
-  GUILE_TOOLS       guile-tools command
-  MES_CFLAGS        MesCC flags
+  GUILD             guild command
+  MES_FOR_BUILD     build system MES [can be mes or guile]
   MES_SEED          location of mes-seed
-  MESCC_TOOLS_SEED  location of mescc-tools-seed
-  TCC               tcc C compiler command
-  TINYCC_SEED       location of tinycc-seed
 " PACKAGE VERSION (getenv "prefix")))
 
 (define (main args)
-  (let* ((CC (or (getenv "CC") "gcc"))
-         (BUILD_TRIPLET %host-type)
-         (ARCH (car (string-split BUILD_TRIPLET #\-)))
-         (options (parse-opts args))
-         (build-triplet (option-ref options 'build BUILD_TRIPLET))
-         (host-triplet (option-ref options 'host BUILD_TRIPLET))
+  (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))
-         (infodir (option-ref options 'infodir infodir))
-         (sysconfdir (option-ref options 'sysconfdir sysconfdir))
-         (verbose? (option-ref options 'verbose #f))
+         (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))
-         (make? #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)))
+         (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 ((CC32 (or (getenv "CC32")
-                    (if (member ARCH '("i686" "arm")) (string-append BUILD_TRIPLET "-" CC)
-                        "i686-unknown-linux-gnu-gcc")))
-          (HELP2MAN (or (getenv "HELP2MAN") "help2man"))
-          (TCC (or (getenv "TCC") "tcc"))
-          (MAKEINFO (or (getenv "MAKEINFO") "makeinfo"))
-          (GUILE_TOOLS (or (getenv "GUILE_TOOLS") "guile-tools"))
-          (BLOOD_ELF (or (getenv "BLOOD_ELF") "blood-elf"))
-          (HEX2 (or (getenv "HEX2") "hex2"))
-          (M1 (or (getenv "M1") "M1"))
-          (CFLAGS (getenv "CFLAGS"))
-          (CC32_CFLAGS (getenv "CC32_CFLAGS"))
-          (HEX2FLAGS (getenv "HEX2FLAGS"))
-          (M1FLAGS (getenv "M1FLAGS"))
-          (MES_CFLAGS (getenv "MES_CFLAGS"))
-          (MES_SEED (or (getenv "MES_SEED") "../mes-seed"))
-          (MESCC_TOOLS_SEED (or (getenv "MESCC_TOOLS_SEED") "../mescc-tools-seed"))
-          (TINYCC_SEED (or (getenv "TINYCC_SEED") "../tinycc-seed")))
-      (when help?
-        (print-help)
-        (exit 0))
-      (set! *verbose?* verbose?)
-      (check-version "guile" '(2 0))
-      (check-version "guile-tools" '(2 0))
-      (check-version "mes-seed" '(0 16) #:optional? #t #:command (string-append MES_SEED "/refresh.sh"))
-      (check-version "tinycc-seed" '(0 16) #:optional? #t #:command (string-append TINYCC_SEED "/refresh.sh"))
-      (check-version BLOOD_ELF '(0 1))
-      (check-version HEX2 '(0 3))
-      (check-version M1 '(0 3))
-      (check-version "nyacc" '(0 80 41) #:command (string-append GUILE " -c '(use-modules (nyacc lalr)) (display *nyacc-version*)'"))
-
-      (check-version "bash" '(4 0))
-      (when (and (check-version "git" '(2 0) #:optional? #t)
-                 (not (file-exists? ".git")))
-        ;; Debian wants to run `make clean' from a tarball
-        (and (zero? (system* "git" "init"))
-             (zero? (system* "git" "add" "."))
-             (zero? (system* "git" "commit" "-m" "Import mes"))))
-      (when (and (not (member ARCH '("i686" "x86_64"))) (not with-courage?))
-        (stderr "platform not supported: ~a, try --with-courage\n" ARCH)
+    (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 3))
+                             (make-dep "M1" #:version '(0 3))
+                             (make-dep "blood-elf" #:version '(0 1))
+                             (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 18) #: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))
-      (if (not (check-version CC '(4 8) #:optional? #t))
-          (set! CC #f))
-      (when CC
-        (check-header-c "stdio.h" "libc-dev")
-        (check-header-c "limits.h" "linux-headers"))
-      (if (not (check-version CC32 '(4 8) #:optional? #t))
-          (set! CC32 #f))
-      (if (not (check-version TCC '(0 9 26) #:optional? #t #:version-option "-v"))
-          (set! TCC #f))
-      (set! make? (check-version "make" '(4 0) #:optional? #t))
-      (check-version "perl" '(5))
-      (if (not (check-version "makeinfo" '(6) #:optional? #t))
-          (set! MAKEINFO #f))
-      (if (not (check-version "help2man" '(1 47) #:optional? #t))
-          (set! HELP2MAN #f))
-
-      (when (pair? required)
-        (stderr "\nMissing dependencies [~a], run\n\n" ((->string ", ") required))
-        (if guix?
-            (stderr "    guix environment -l guix.scm\n")
-            (stderr "    sudo apt-get install ~a\n" ((->string " ") required)))
+      (when (pair? missing)
+        (stderr "\nMissing dependencies: ~a\n" (string-join (map dependency-name missing)))
         (exit 1))
-      (with-output-to-file ".config.make"
-        (lambda ()
-          (stdout "build:=~a\n" build-triplet)
-          (stdout "host:=~a\n" host-triplet)
-          (stdout "srcdir:=.\n")
-          (stdout "prefix:=~a\n" (gulp-pipe (string-append "echo " prefix)))
-          (stdout "infodir:=~a\n" infodir)
-          (stdout "mandir:=~a\n" mandir)
-          (stdout "sysconfdir:=~a\n" sysconfdir)
-
-          (stdout "ARCH:=~a\n" ARCH)
-          (stdout "CC:=~a\n" (or CC ""))
-          (stdout "CC32:=~a\n" (or CC32 ""))
-          (stdout "HELP2MAN:=~a\n" (or HELP2MAN ""))
-          (stdout "TCC:=~a\n" (or TCC ""))
-          (stdout "BLOOD_ELF:=~a\n" (or BLOOD_ELF ""))
-          (stdout "MES_SEED:=~a\n" (or MES_SEED ""))
-          (stdout "MESCC_TOOLS_SEED:=~a\n" (or MESCC_TOOLS_SEED ""))
-          (stdout "TINYCC_SEED:=~a\n" (or TINYCC_SEED ""))
-          (stdout "HEX2:=~a\n" (or HEX2 ""))
-          (stdout "M1:=~a\n" (or M1 ""))
-          (stdout "GUILE:=~a\n" GUILE)
-          (stdout "GUILE_TOOLS:=~a\n" GUILE_TOOLS)
-          (stdout "GUILE_FOR_BUILD:=~a\n" GUILE)
-          (stdout "GUILE_EFFECTIVE_VERSION:=~a\n" GUILE_EFFECTIVE_VERSION)
-          (stdout "GUIX_P:=~a\n" (if guix? guix? ""))
-          (stdout "HEX2:=~a\n" (or HEX2 ""))
-          (stdout "PACKAGE:=~a\n" PACKAGE)
-          (stdout "VERSION:=~a\n" VERSION)
-          (when disable-silent-rules?
-            (stdout "BUILD_DEBUG:=1\n"))
-          (when CFLAGS (stdout "CFLAGS:=~a\n" CFLAGS))
-          (when CC32_CFLAGS (stdout "CC32_CFLAGS:=~a\n" CC32_CFLAGS))
-          (when HEX2FLAGS (stdout "HEX2FLAGS:=~a\n" HEX2FLAGS))
-          (when M1FLAGS (stdout "M1FLAGS:=~a\n" M1FLAGS))
-          (when MES_CFLAGS (stdout "MES_CFLAGS:=~a\n" MES_CFLAGS))))
-      (format (current-output-port)
-              "\nRun:
-  ~a            to build mes
-  ~a help       for help on other targets\n"
-              (if make? "make" "./build.sh")
-              (if make? "make" "./build.sh")))))
+      (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"))))))