3d0859096d13293430346b716174392a5acc0f74
[mes.git] / configure
1 #! /bin/sh
2 # -*- scheme -*-
3 unset LANG LC_ALL
4 echo -n "checking for guile..."
5 GUILE=$(type -p ${GUILE-guile} 2>/dev/null|tail -n 1|sed 's,^.* ,,')
6 export GUILE
7 if [ -x "$GUILE" ]; then
8     echo " $GUILE"
9 else
10     pm=$({ guix --help || dpkg --help; }|head -n 1|sed 's,.*Usage: \([^ ]*\).*,\1,g')
11 #-paredit:'})(
12     case "$pm" in dpkg) message="sudo apt-get install guile-2.0";; *) message="guix package -i guile";; esac
13     cat <<EOF
14 Missing dependencies, run
15
16     $pm
17 EOF
18     exit 1
19 fi
20 exec ${GUILE} --no-auto-compile -L $(pwd) -e '(@@ (configure) main)' -s "$0" ${1+"$@"}
21 !#
22
23 ;;; Mes --- Maxwell Equations of Software
24 ;;; Copyright © 2016,2017 Jan Nieuwenhuizen <janneke@gnu.org>
25 ;;;
26 ;;; configure: This file is part of Mes.
27 ;;;
28 ;;; Mes is free software; you can redistribute it and/or modify it
29 ;;; under the terms of the GNU General Public License as published by
30 ;;; the Free Software Foundation; either version 3 of the License, or (at
31 ;;; your option) any later version.
32 ;;;
33 ;;; Mes is distributed in the hope that it will be useful, but
34 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
35 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36 ;;; GNU General Public License for more details.
37 ;;;
38 ;;; You should have received a copy of the GNU General Public License
39 ;;; along with Mes.  If not, see <http://www.gnu.org/licenses/>.
40
41
42 (define-module (configure)
43   #:use-module (ice-9 and-let-star)
44   #:use-module (ice-9 curried-definitions)
45   #:use-module (ice-9 getopt-long)
46   #:use-module (ice-9 match)
47   #:use-module (ice-9 optargs)
48   #:use-module (ice-9 popen)
49   #:use-module (ice-9 rdelim))
50
51 (define *shell* "sh")
52 (define PACKAGE "mes")
53 (define VERSION "0.7")
54 (define PREFIX "/usr/local")
55 (define GUILE_EFFECTIVE_VERSION (effective-version))
56 (define GUILE (or (getenv "guile") "guile"))
57 (define SYSCONFDIR "$(PREFIX)/etc")
58
59 ;;; Utility
60 (define (logf port string . rest)
61   (apply format (cons* port string rest))
62   (force-output port)
63   #t)
64
65 (define (stderr string . rest)
66   (apply logf (cons* (current-error-port) string rest)))
67
68 (define (stdout string . rest)
69   (apply logf (cons* (current-output-port) string rest)))
70
71 (define *verbose?* #f)
72
73 (define (verbose string . rest)
74   (if *verbose?* (apply stderr (cons string rest))))
75
76 (define (gulp-pipe command)
77   (let* ((port (open-pipe* OPEN_READ *shell* "-c" command))
78          (output (read-string port))
79          (status (close-pipe port)))
80     (verbose "command[~a]: ~s => ~a\n" status command output)
81     (if (not (zero? status)) "" (string-trim-right output #\newline))))
82
83 (define* ((->string #:optional (infix "")) h . t)
84   (let ((o (if (pair? t) (cons h t) h)))
85     (match o
86       ((? char?) (make-string 1 o))
87       ((? number?) (number->string o))
88       ((? string?) o)
89       ((? symbol?) (symbol->string o))
90       ((h ... t) (string-join (map (->string) o) ((->string) infix)))
91       (_ ""))))
92
93 (define (tuple< a b)
94   (cond
95    ((and (null? a) (null? b)) #t)
96    ((null? a) (not (null? b)))
97    ((null? b) #f)
98    ((and (not (< (car a) (car b)))
99          (not (< (car b) (car a))))
100     (tuple< (cdr a) (cdr b)))
101    (else (< (car a) (car b)))))
102
103 (define (tuple<= a b)
104   (or (equal? a b) (tuple< a b)))
105
106 ;;; Configure
107 (define (version->string version)
108   ((->string '.) version))
109
110 (define (string->version string)
111   (and-let* ((version (string-tokenize string
112                                        (char-set-adjoin char-set:digit #\.)))
113              ((pair? version))
114              (version (sort version (lambda (a b) (> (string-length a) (string-length b)))))
115              (version (car version))
116              (version (string-tokenize version
117                                        (char-set-complement (char-set #\.)))))
118             (map string->number version)))
119
120 (define required '())
121 (define* (check-version name expected
122                         #:key
123                         (deb #f)
124                         (version-option '--version)
125                         (compare tuple<=)
126                         (command name))
127   (stderr "checking for ~a~a..." name
128           (if (null? expected) ""
129               (format #f " [~a]" (version->string expected))))
130   (let* ((output (gulp-pipe (format #f "~a ~a 2>&1" command version-option)))
131          (actual (string->version output))
132          (pass? (and actual (compare expected actual))))
133     (stderr "~a ~a\n" (if pass? (if (pair? actual) "" " yes")
134                           (if actual " no, found" "")) (version->string actual))
135     (or pass?
136         (if (not (pair? name)) (begin (set! required (cons (or deb name) required)) pass?)
137             (check-version (cdr name) expected deb version-option compare)))))
138
139 (define* (check-pkg-config package expected #:optional (deb #f))
140   (check-version (format #f "pkg-config --modversion ~a" package) expected deb))
141
142 (define (check-compile-header-c header)
143   (and (= 0 (system (format #f "echo '#include ~s' | gcc -E - > /dev/null 2>&1" header)))
144        'yes))
145
146 (define (check-compile-header-c++ header)
147   (and (= 0 (system (format #f "echo '#include ~s' | gcc --language=c++ --std=c++11 -E - > /dev/null 2>&1" header)))
148        'yes))
149
150 (define* (check-header-c header deb #:optional (check check-compile-header-c))
151   (stderr "checking for ~a..." header)
152   (let ((result (check header)))
153     (stderr " ~a\n" (if result result "no"))
154     (if (not result)
155         (set! required (cons deb required)))))
156
157 (define* (check-header-c++ header deb #:optional (check check-compile-header-c++))
158   (check-header-c header deb check))
159
160 (define guix?
161   (and (zero? (system "guix --version 1>/dev/null 2>/dev/null")) 1))
162 ;;;
163
164 (define CC (or (getenv "CC") "gcc"))
165 (define BUILD_TRIPLET (gulp-pipe (string-append CC " -dumpmachine 2>/dev/null")))
166 (define ARCH (car (string-split BUILD_TRIPLET #\-)))
167 (define CC32 (or (getenv "CC32")
168                  (if (member ARCH '("i686" "arm")) (string-append BUILD_TRIPLET "-" CC)
169                      "i686-unknown-linux-gnu-gcc")))
170
171 (define (parse-opts args)
172   (let* ((option-spec
173           '((build (value #t))
174             (host (value #t))
175             (help (single-char #\h))
176             (prefix (value #t))
177             (sysconfdir (value #t))
178             (verbose (single-char #\v))
179             (with-courage)
180             ;;ignore
181             (enable-fast-install)))
182          (options (getopt-long args option-spec))
183          (help? (option-ref options 'help #f))
184          (files (option-ref options '() '()))
185          (prefix (option-ref options '() PREFIX))
186          (usage? (and (not help?) #f)))
187     (if (pair? files)
188         (stderr "ignoring files: ~a\n" files))
189     (or (and (or help? usage?)
190              ((or (and usage? stderr) stdout) "\
191 Usage: ./configure [OPTION]...
192   -h, --help           display this help
193       --build=BUILD    configure for building on BUILD [guessed]
194       --host=HOST      cross-compile to build programs to run on HOST [BUILD]
195   --prefix=DIR         install in PREFIX [~a]
196   --sysconfdir=DIR     read-only single-machine data [PREFIX/etc]
197   -v, --verbose        be verbose
198   --with-courage       assert being courageous to configure for unsupported platform
199 " PREFIX)
200              (exit (or (and usage? 2) 0)))
201         options)))
202
203 (define (main args)
204   (let* ((options (parse-opts args))
205          (build-triplet (option-ref options 'build BUILD_TRIPLET))
206          (host-triplet (option-ref options 'host BUILD_TRIPLET))
207          (prefix (option-ref options 'prefix PREFIX))
208          (sysconfdir (option-ref options 'sysconfdir SYSCONFDIR))
209          (verbose? (option-ref options 'verbose #f))
210          (with-courage? (option-ref options 'with-courage #f)))
211     (set! *verbose?* verbose?)
212     (check-version 'bash '(4 0))
213     (when (and (not (member ARCH '("i686" "x86_64"))) (not with-courage?))
214           (stderr "platform not supported: ~a, try --with-courage\n" ARCH)
215           (exit 1))
216     (check-version CC '(4 8))
217     (check-header-c "stdio.h" "libc-dev")
218     (check-header-c "limits.h" "linux-headers")
219     (check-version CC32 '(4 8))
220     (check-version 'guile '(2 0))
221     (check-version 'make '(4 0))
222     (check-version 'perl '(5))
223     (check-version 'nyacc '(0 78 0) #:command (string-append GUILE " -c '(use-modules (nyacc lalr)) (display *nyacc-version*)'"))
224
225     (when (pair? required)
226       (stderr "\nMissing dependencies [~a], run\n\n" ((->string ", ") required))
227       (if guix?
228           (stderr "    guix environment -l guix.scm\n")
229           (stderr "    sudo apt-get install ~a\n" ((->string " ") required)))
230       (exit 1))
231     (with-output-to-file ".config.make"
232       (lambda ()
233         (stdout "build:=~a\n" build-triplet)
234         (stdout "host:=~a\n" host-triplet)
235         (stdout "srcdir:=.\n")
236         (stdout "ARCH:=~a\n" ARCH)
237         (stdout "CC:=~a\n" CC)
238         (stdout "CC32:=~a\n" CC32)
239         (stdout "GUILE:=~a\n" GUILE)
240         (stdout "GUILE_FOR_BUILD:=~a\n" GUILE)
241         (stdout "GUILE_EFFECTIVE_VERSION:=~a\n" GUILE_EFFECTIVE_VERSION)
242         (stdout "GUIX_P:=~a\n" (if guix? guix? ""))
243         (stdout "PACKAGE:=~a\n" PACKAGE)
244         (stdout "VERSION:=~a\n" VERSION)
245         (stdout "PREFIX:=~a\n" (gulp-pipe (string-append "echo " prefix)))
246         (stdout "SYSCONFDIR:=~a\n" sysconfdir)))
247     (stdout "\nRun:
248   make            to build mes
249   make help       for help on other targets\n")))