core: eval_apply profile.
[mes.git] / configure
1 #! /bin/sh
2 # -*- scheme -*-
3 unset LANG LC_ALL
4 guile=$(command -v ${GUILE-guile})
5 guix=$(command -v ${GUIX-guix})
6 if [ -n "$guix" ] ; then
7     install="guix environment -l .guix.scm"
8 else
9     install="sudo apt-get install guile-2.2-dev"
10 fi
11 if [ -z "$guile" ]; then
12     cat <<EOF
13
14 Missing dependencies: ${GUILE-guile}, please install Guile 2.2 or later; run
15     $install
16 EOF
17 exit 1
18 fi
19 GUILE=$guile
20 export GUILE
21 exec ${guile} -L . --no-auto-compile -e '(configure)' -s "$0" ${1+"$@"}
22 !#
23
24 ;;; GNU Mes --- Maxwell Equations of Software
25 ;;; Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
26 ;;;
27 ;;; configure: This file is part of GNU Mes.
28 ;;;
29 ;;; GNU Mes is free software; you can redistribute it and/or modify it
30 ;;; under the terms of the GNU General Public License as published by
31 ;;; the Free Software Foundation; either version 3 of the License, or (at
32 ;;; your option) any later version.
33 ;;;
34 ;;; GNU Mes is distributed in the hope that it will be useful, but
35 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
36 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37 ;;; GNU General Public License for more details.
38 ;;;
39 ;;; You should have received a copy of the GNU General Public License
40 ;;; along with GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
41
42 (define-module (configure)
43   #:use-module (srfi srfi-1)
44   #:use-module (srfi srfi-9)
45   #:use-module (srfi srfi-9 gnu)
46   #:use-module (srfi srfi-26)
47   #:use-module (ice-9 and-let-star)
48   #:use-module (ice-9 curried-definitions)
49   #:use-module (ice-9 getopt-long)
50   #:use-module (ice-9 match)
51   #:use-module (ice-9 optargs)
52   #:use-module (ice-9 popen)
53   #:use-module (ice-9 rdelim)
54   #:use-module (ice-9 regex)
55   #:export (main))
56
57 (define* (PATH-search-path name #:key (default name) warn?)
58   (or (search-path (string-split (getenv "PATH") #\:) name)
59       (and (and warn? (format (current-error-port) "warning: not found: ~a\n" name))
60            default)))
61
62 (define *shell* "sh")
63 (define PACKAGE "mes")
64 (define VERSION "0.18")
65
66 ;;; Utility
67 (define (logf port string . rest)
68   (apply format (cons* port string rest))
69   (force-output port)
70   #t)
71
72 (define (stderr string . rest)
73   (apply logf (cons* (current-error-port) string rest)))
74
75 (define (stdout string . rest)
76   (apply logf (cons* (current-output-port) string rest)))
77
78 (define %verbose? #f)
79
80 (define (verbose string . rest)
81   (if %verbose? (apply stderr (cons string rest))))
82
83 (define (gulp-pipe command)
84   (let* ((port (open-pipe* OPEN_READ *shell* "-c" command))
85          (output (read-string port))
86          (status (close-pipe port)))
87     (verbose "command[~a]: ~s => ~a\n" status command output)
88     (if (not (zero? status)) "" (string-trim-right output #\newline))))
89
90 (define* ((->string #:optional (infix "")) h . t)
91   (let ((o (if (pair? t) (cons h t) h)))
92     (match o
93       ((? char?) (make-string 1 o))
94       ((? number?) (number->string o))
95       ((? string?) o)
96       ((? symbol?) (symbol->string o))
97       ((h ... t) (string-join (map (->string) o) ((->string) infix)))
98       (_ ""))))
99
100 (define (tuple< a b)
101   (cond
102    ((and (null? a) (null? b)) #t)
103    ((null? a) (not (null? b)))
104    ((null? b) #f)
105    ((and (not (< (car a) (car b)))
106          (not (< (car b) (car a))))
107     (tuple< (cdr a) (cdr b)))
108    (else (< (car a) (car b)))))
109
110 (define (tuple<= a b)
111   (or (equal? a b) (tuple< a b)))
112
113 (define (conjoin . predicates)
114   (lambda (. arguments)
115     (every (cut apply <> arguments) predicates)))
116
117 (define (char->char from to char)
118   (if (eq? char from) to char))
119
120 (define (string-replace-char string from to)
121   (string-map (cut char->char from to <>) string))
122
123 ;;; Configure
124
125 (define-immutable-record-type <dependency>
126   (make-depedency name version-expected optional? version-option commands file-name)
127   dependency?
128   (name dependency-name)
129   (version-expected dependency-version-expected)
130   (version-option dependency-version-option)
131   (optional? dependency-optional?)
132   (commands dependency-commands)
133   (file-name dependency-file-name)
134   (version-found dependency-version-found))
135
136 (define* (make-dep name #:optional (version '(0))
137                    #:key optional? (version-option "--version") (commands (list name)) file-name)
138   (let* ((env-var (getenv (name->shell-name name)))
139          (commands (if env-var (cons env-var commands) commands)))
140    (make-depedency name version optional? version-option commands file-name)))
141
142 (define (find-dep name deps)
143   (find (compose (cut equal? <> name) dependency-name) deps))
144
145 (define (file-name name deps)
146   (and=> (find-dep name deps) dependency-file-name))
147
148 (define (variable-name dependency)
149   (and=>
150    (dependency-name dependency)
151    name->shell-name))
152
153 (define (name->shell-name name)
154   (string-upcase (string-replace-char name #\- #\_)))
155
156 (define (version->string version)
157   ((->string '.) version))
158
159 (define (string->version string)
160   (and-let* ((version (string-tokenize string
161                                        (char-set-adjoin char-set:digit #\.)))
162              ((pair? version))
163              (version (sort version (lambda (a b) (> (string-length a) (string-length b)))))
164              (version (car version))
165              (version (string-tokenize version
166                                        (char-set-complement (char-set #\.)))))
167             (map string->number version)))
168
169 (define (check-program-version dependency)
170   (let ((name (dependency-name dependency))
171         (expected (dependency-version-expected dependency))
172         (version-option (dependency-version-option dependency))
173         (commands (dependency-commands dependency)))
174     (let loop ((commands commands))
175       (if (null? commands) dependency
176           (let ((command (car commands)))
177             (stdout "checking for ~a~a... " command
178                     (if (null? expected) ""
179                         (format #f " [~a]" (version->string expected))))
180             (let* ((output (gulp-pipe (format #f "~a ~a 2>&1" command version-option)))
181                    (actual (string->version output))
182                    (pass? (and actual (tuple< expected actual)))
183                    (dependency (set-field dependency (dependency-version-found) actual)))
184               (stdout "~a ~a\n" (if pass? (if (pair? actual) "" " yes")
185                                     (if actual " no, found" "no")) (version->string actual))
186               (if pass? (let ((file-name (or (PATH-search-path command)
187                                              (dependency-file-name dependency))))
188                           (set-field dependency (dependency-file-name) file-name))
189                   (loop (cdr commands)))))))))
190
191 (define (check-file dependency)
192   (stdout "checking for ~a... " (dependency-name dependency))
193   (let ((file-name (and (file-exists? (dependency-file-name dependency))
194                         (dependency-file-name dependency))))
195     (stdout "~a\n" (or file-name ""))
196     (set-field dependency (dependency-file-name) file-name)))
197
198 (define* (check-header-c dependency  #:optional (check check-compile-header-c))
199   (let ((name (dependency-name dependency)))
200     (stderr "checking for ~a..." name)
201     (let ((result (check name)))
202       (stderr " ~a\n" (if result "yes" "no"))
203       (if result (set-field dependency (dependency-file-name) name)
204           dependency-file-name))))
205
206 (define (check-compile-header-c header)
207   (zero? (system (format #f "echo '#include ~s' | gcc -E - > /dev/null 2>&1" header))))
208
209 (define (parse-opts args)
210   (let* ((option-spec
211           '((build (value #t))
212             (host (value #t))
213             (help (single-char #\h))
214             (prefix (value #t))
215             (bindir (value #t))
216             (datadir (value #t))
217             (docdir (value #t))
218             (libdir (value #t))
219             (srcdir (value #t))
220             (sysconfdir (value #t))
221             (verbose (single-char #\v))
222             (with-cheating)
223             (with-courage)
224             (infodir (value #t))
225             (mandir (value #t))
226             (disable-silent-rules)
227             (enable-silent-rules)
228
229             (enable-fast-install)       ; Ignored for Guix
230             (includedir (value #t))     ; Ignored for Debian
231             (mandir (value #t))         ; Ignored for Debian
232             (localstatedir (value #t))  ; Ignored for Debian
233             (libdir (value #t))         ; Ignored for Debian
234             (libexecdir (value #t))     ; Ignored for Debian
235             (runstatedir (value #t))    ; Ignored for Debian
236             (disable-maintainer-mode)   ; Ignored for Debian
237             (disable-dependency-tracking) ; Ignored for Debian
238             )))
239
240     (getopt-long args option-spec)))
241
242 (define* (print-help #:optional (port (current-output-port)))
243   (format port "\
244 `configure' configures ~a ~a to adapt to many kinds of systems.
245
246 Usage: ./configure [OPTION]... [VAR=VALUE]
247
248 To assign environment variables (e.g., CC, CFLAGS...), specify them as
249 VAR=VALUE.  See below for descriptions of some of the useful variables.
250
251 Defaults for the options are specified in brackets.
252
253 Options:
254   -h, --help           display this help
255       --build=BUILD    configure for building on BUILD [guessed]
256       --disable-silent-rules
257                        verbose build output [BUILD_DEBUG=1]
258       --host=HOST      cross-compile to build programs to run on HOST [BUILD]
259   -v, --verbose        be verbose
260   --with-courage       assert being courageous to configure for unsupported platform
261   --with-cheating      cheat using Guile instead of Mes
262
263 Installation directories:
264   --prefix=DIR         install in prefix DIR [~a]
265   --infodir=DIR        info documentation [PREFIX/share/info]
266   --mandir=DIR         man pages [PREFIX/share/man]
267
268 Ignored for Guix:
269   --enable-fast-install
270
271 Ignored for Debian:
272   --disable-dependency-tracking
273   --disable-maintainer-mode
274   --includedir=DIR
275   --libdir=DIR
276   --libexecdir=DIR
277   --localstatedir=DIR
278   --runstatedir=DIR
279
280 Some influential environment variables:
281   CC                C compiler command
282   CFLAGS            C compiler flags
283   CC32              x86 C compiler command
284   CC64_CFLAGS       x86_64 C compiler flags
285   CC64              x86_64 C compiler command
286   CC32_CFLAGS       x86 C compiler flags
287   GUILE             guile command
288   GUILE_TOOLS       guile-tools command
289   MES_CFLAGS        MesCC flags
290   MES_SEED          location of mes-seed
291   MESCC_TOOLS_SEED  location of mescc-tools-seed
292   TCC               tcc C compiler command
293   TINYCC_PREFIX     location of tinycc [for tests/test2]
294   TINYCC_SEED       location of tinycc-seed
295 " PACKAGE VERSION (getenv "prefix")))
296
297 (define (main args)
298   (let* ((options (parse-opts args))
299          (build-type (option-ref options 'build %host-type))
300
301          (arch (car (string-split build-type #\-)))
302          (host-type (option-ref options 'host %host-type))(prefix "/usr/local")
303
304          (prefix "/usr/local")
305          (prefix (option-ref options 'prefix prefix))
306          (infodir (option-ref options 'infodir "${prefix}/share/info"))
307          (mandir (option-ref options 'infodir "${prefix}/share/man"))
308          (sysconfdir (option-ref options 'sysconfdir "${prefix}/etc"))
309
310          (bindir (option-ref options 'bindir "${prefix}/bin"))
311          (datadir (option-ref options 'datadir "${prefix}/share"))
312          (docdir (option-ref options 'docdir "${datadir}/doc/mes-${VERSION}"))
313          (libdir (option-ref options 'libdir "${prefix}/lib"))
314          (moduledir "${datadir}/mes/module")
315          (moduledir/ (gulp-pipe (string-append "echo " prefix "/share/mes/module/")))
316          (guile-effective-version (effective-version))
317          (guile-site-dir (if (equal? prefix ".") (canonicalize-path ".")
318                              (string-append "${prefix}/share/guile/site/" guile-effective-version)))
319          (guile-site-ccache-dir (if (equal? prefix ".") (canonicalize-path ".")
320                                     (string-append "${prefix}/lib/guile/" guile-effective-version "/site-ccache")))
321
322          (srcdir (dirname (car (command-line))))
323          (srcdest (if (equal? srcdir ".") ""
324                       (string-append srcdir "/")))
325          (abs-top-srcdir (canonicalize-path srcdir))
326          (abs-top-builddir (canonicalize-path (getcwd)))
327          (top-builddir (if (equal? srcdir ".") "."
328                            abs-top-builddir))
329
330          (with-cheating? (option-ref options 'with-cheating #f))
331          (with-courage? (option-ref options 'with-courage #f))
332          (disable-silent-rules? (option-ref options 'disable-silent-rules #f))
333          (enable-silent-rules? (option-ref options 'enable-silent-rules #f))
334          (vars (filter (cut string-index <> #\=) (option-ref options '() '())))
335          (help? (option-ref options 'help #f)))
336     (when help?
337       (print-help)
338       (exit 0))
339     (set! %verbose? (option-ref options 'verbose #f))
340     (when %verbose?
341       (stderr "configure args=~s\n" args))
342     (for-each (lambda (v) (apply setenv (string-split v #\=))) vars)
343     (let* ((mes-seed (or (getenv "MES_SEED")
344                          (string-append srcdest "../mes-seed")))
345            (tinycc-prefix (or (getenv "TINYCC_PREFIX")
346                               (string-append srcdest "../tinycc-prefix")))
347            (tinycc-seed (or (getenv "TINYCC_SEED")
348                             (string-append srcdest "../tinycc-seed")))
349            (mescc-tools-seed (or (getenv "MESCC_TOOLS_SEED")
350                                  (string-append srcdest "../mescc-tools-seed")))
351            (deps (fold (lambda (program results)
352                          (cons (check-program-version program) results))
353                        '()
354                        (list (make-dep "guile" '(2 0) #:commands '("guile-2.2" "guile-2.0" "guile-2" "guile"))
355                              (make-dep "guix" '(0 13) #:optional? #t)
356                              (make-dep "bash" '(2 0) #:optional? #t)
357                              (make-dep "guile-tools" '(2 0))
358                              (make-dep "mes-seed" '(0 18) #:optional? #t
359                                        #:commands (list (string-append mes-seed "/refresh.sh"))
360                                        #:file-name mes-seed)
361                              (make-dep "tinycc-seed" '(0 18) #:optional? #t
362                                        #:commands (list (string-append tinycc-seed "/refresh.sh"))
363                                        #:file-name tinycc-seed)
364                              (make-dep "cc" '(2 95) #:commands '("gcc"))
365                              (make-dep "make" '(4))
366                              (make-dep "cc32" '(2 95)
367                                        #:optional? #t
368                                        #:commands '("i686-unknown-linux-gnu-gcc"))
369                              (make-dep "cc64" '(2 95)
370                                        #:optional? #t
371                                        #:commands '("gcc"))
372                              (make-dep "M1" '(0 3))
373                              (make-dep "blood-elf" '(0 1))
374                              (make-dep "hex2" '(0 3))
375                              (make-dep "tcc" '(0 9 26) #:optional? #t #:version-option "-v")
376                              (make-dep "makeinfo" '(5) #:optional? #t)
377                              (make-dep "dot" '(2) #:version-option "-V" #:optional? #t)
378                              (make-dep "help2man" '(1 47) #:optional? #t)
379                              (make-dep "perl" '(5) #:optional? #t)
380                              (make-dep "git" '(2) #:optional? #t))))
381            (deps (cons (check-program-version (make-dep "nyacc" '(0 86 0) #:commands (list (string-append (file-name "guile" deps) " -c '(use-modules (nyacc lalr)) (display *nyacc-version*)'")) #:file-name #t))
382                        deps))
383            (deps (if (file-name "cc" deps)
384                      (cons* (check-header-c (make-dep "stdio.h"))
385                             (check-header-c (make-dep "limits.h"))
386                             deps)
387                      deps))
388            (deps (cons (check-file (make-dep "mescc-tools-seed" '(0) #:optional? #t
389                                              #:file-name mescc-tools-seed))
390                        deps))
391            (deps (cons (check-file (make-dep "tinycc-prefix" '(0) #:optional? #t
392                                              #:file-name tinycc-prefix))
393                        deps))
394            (missing (filter (conjoin (negate dependency-file-name)
395                                      (negate dependency-optional?)) deps)))
396
397       (define* (substitute file-name pairs
398                            #:key (target (if (string-suffix? ".in" file-name)
399                                              (string-drop-right file-name 3) file-name)))
400         (system* "mkdir" "-p" (dirname target))
401         (with-output-to-file target
402           (lambda _
403             (display
404              (fold (lambda (o result)
405                      (regexp-substitute/global #f (car o) result 'pre (cdr o) 'post))
406                    (with-input-from-file file-name read-string) pairs)))))
407
408       (when (and (not (member arch '("i686" "x86_64"))) (not with-courage?))
409         (stderr "platform not supported: ~a, try --with-courage\n" arch)
410         (exit 1))
411       (when (pair? missing)
412         (stderr "\nMissing dependencies: ~a\n" (string-join (map dependency-name missing)))
413         (exit 1))
414       (let ((git (find-dep "git" deps)))
415         (when (and git
416                    (not (file-exists? ".git")))
417           ;; Debian wants to run `make clean' from a tarball
418           (and (zero? (system* "git" "init"))
419                (zero? (system* "git" "add" "."))
420                (zero? (system* "git" "commit" "--allow-empty" "-m" "Import mes")))))
421       (with-output-to-file ".config.make"
422         (lambda _
423           (stdout "PACKAGE:=~a\n" PACKAGE)
424           (stdout "VERSION:=~a\n" VERSION)
425
426           (stdout "arch:=~a\n" arch)
427           (stdout "build:=~a\n" build-type)
428           (stdout "host:=~a\n" host-type)
429
430           (stdout "top_builddir:=~a\n" top-builddir)
431           (stdout "abs_top_builddir:=~a\n" abs-top-builddir)
432           (stdout "abs_top_srcdir:=~a\n" abs-top-srcdir)
433
434           (stdout "srcdest:=~a\n" srcdest)
435           (stdout "srcdir:=~a\n" srcdir)
436
437           (stdout "prefix:=~a\n" (gulp-pipe (string-append "echo " prefix)))
438           (stdout "datadir:=~a\n" datadir)
439           (stdout "docdir:=~a\n" docdir)
440
441           (stdout "bindir:=~a\n" bindir)
442           (stdout "guile_site_ccache_dir:=~a\n" guile-site-ccache-dir)
443           (stdout "guile_site_dir:=~a\n" guile-site-dir)
444           (stdout "infodir:=~a\n" infodir)
445           (stdout "libdir:=~a\n" libdir)
446           (stdout "mandir:=~a\n" mandir)
447           (stdout "moduledir:=~a\n" moduledir)
448           (stdout "sysconfdir:=~a\n" sysconfdir)
449
450           (for-each (lambda (o)
451                       (stdout "~a:=~a\n" (variable-name o) (or (dependency-file-name o) "")))
452                     deps)
453           (stdout "GUILE_EFFECTIVE_VERSION:=~a\n" (effective-version))
454
455           (when disable-silent-rules?
456             (stdout "V:=1\n"))
457
458           (when with-cheating?
459             (stdout "MES:=guile\n"))
460
461           (for-each (lambda (o)
462                       (stdout "~a:=~a\n" o (or (getenv o) "")))
463                     '(
464                       "CFLAGS"
465                       "CC32_CFLAGS"
466                       "CC64_CFLAGS"
467                       "HEX2FLAGS"
468                       "M1FLAGS"
469                       "MES_CFLAGS"
470                       ))))
471
472       (let ((pairs `(("@srcdest@" . ,srcdest)
473                      ("@srcdir@" . ,srcdir)
474                      ("@abs_top_srcdir@" . ,abs-top-srcdir)
475                      ("@abs_top_builddir@" . ,abs-top-builddir)
476                      ("@top_builddir@" . ,top-builddir)
477                      ("@BASH@" . ,(file-name "bash" deps))
478                      ("@GUILE@" . ,(file-name "guile" deps))
479                      ("@MES@" . ,(file-name "guile" deps))
480                      ("@prefix@" . ,prefix)
481                      ("@guile_site_dir@" . ,guile-site-dir)
482                      ("@guile_site_ccache_dir@" . ,guile-site-ccache-dir)
483                      ("@VERSION@" . ,VERSION)
484                      ("@arch@" . ,arch)
485                      ("mes/module/" . ,(string-append moduledir/)))))
486         (for-each (lambda (o)
487                     (let* ((src (string-append srcdest o))
488                            (target (string-drop-right o 3))
489                            (target (if (not (string-prefix? "build-aux/" target)) target
490                                        (string-drop target (string-length "build-aux/")))))
491                       (substitute src pairs #:target target)))
492                   '(
493                     "build-aux/GNUmakefile.in"
494                     "build-aux/build.sh.in"
495                     "build-aux/check.sh.in"
496                     "build-aux/install.sh.in"
497                     "build-aux/pre-inst-env.in"
498                     "build-aux/uninstall.sh.in"
499                     "mes/module/mes/boot-0.scm.in"
500                     "scripts/mescc.in"
501                     )))
502       (chmod "build.sh" #o755)
503       (chmod "check.sh" #o755)
504       (chmod "install.sh" #o755)
505       (chmod "pre-inst-env" #o755)
506       (chmod "uninstall.sh" #o755)
507       (chmod "scripts/mescc" #o755)
508       (let ((make (and=> (file-name "make" deps) basename)))
509         (format (current-output-port)
510                 "\nRun:
511   ~a            to build mes
512   ~a help       for help on other targets\n"
513                 (or make "./build.sh")
514                 (or make "./build.sh"))))))