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