#!/bin/sh # aside from this initial boilerplate, this is actually -*- scheme -*- code main='(module-ref (resolve-module '\''(scripts autofrisk)) '\'main')' exec ${GUILE-guile} -l $0 -c "(apply $main (cdr (command-line)))" "$@" !# ;;; autofrisk --- Generate module checks for use with auto* tools ;; Copyright (C) 2002, 2006 Free Software Foundation, Inc. ;; ;; This program 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 2, or ;; (at your option) any later version. ;; ;; This program 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 this software; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301 USA ;;; Author: Thien-Thi Nguyen ;;; Commentary: ;; Usage: autofrisk [file] ;; ;; This program looks for the file modules.af in the current directory ;; and writes out modules.af.m4 containing autoconf definitions. ;; If given, look for FILE instead of modules.af and output to FILE.m4. ;; ;; After running autofrisk, you should add to configure.ac the lines: ;; AUTOFRISK_CHECKS ;; AUTOFRISK_SUMMARY ;; Then run "aclocal -I ." to update aclocal.m4, and finally autoconf. ;; ;; The modules.af file consists of a series of configuration forms (Scheme ;; lists), which have one of the following formats: ;; (files-glob PATTERN ...) ;; (non-critical-external MODULE ...) ;; (non-critical-internal MODULE ...) ;; (programs (MODULE PROG ...) ...) ;; (pww-varname VARNAME) ;; PATTERN is a string that may contain "*" and "?" characters to be ;; expanded into filenames. MODULE is a list of symbols naming a ;; module, such as `(srfi srfi-1)'. VARNAME is a shell-safe name to use ;; instead of "probably_wont_work", the default. This var is passed to ;; `AC_SUBST'. PROG is a string. ;; ;; Only the `files-glob' form is required. ;; ;; TODO: Write better commentary. ;; Make "please see README" configurable. ;;; Code: (define-module (scripts autofrisk) :autoload (ice-9 popen) (open-input-pipe) :use-module (srfi srfi-1) :use-module (srfi srfi-8) :use-module (srfi srfi-13) :use-module (srfi srfi-14) :use-module (scripts read-scheme-source) :use-module (scripts frisk) :export (autofrisk)) (define *recognized-keys* '(files-glob non-critical-external non-critical-internal programs pww-varname)) (define (canonical-configuration forms) (let ((chk (lambda (condition . x) (or condition (apply error "syntax error:" x))))) (chk (list? forms) "input not a list") (chk (every list? forms) "non-list element") (chk (every (lambda (form) (< 1 (length form))) forms) "list too short") (let ((un #f)) (chk (every (lambda (form) (let ((key (car form))) (and (symbol? key) (or (eq? 'quote key) (memq key *recognized-keys*) (begin (set! un key) #f))))) forms) "unrecognized key:" un)) (let ((bunched (map (lambda (key) (fold (lambda (form so-far) (or (and (eq? (car form) key) (cdr form) (append so-far (cdr form))) so-far)) (list key) forms)) *recognized-keys*))) (lambda (key) (assq-ref bunched key))))) (define (>>strong modules) (for-each (lambda (module) (format #t "GUILE_MODULE_REQUIRED~A\n" module)) modules)) (define (safe-name module) (let ((var (object->string module))) (string-map! (lambda (c) (if (char-set-contains? char-set:letter+digit c) c #\_)) var) var)) (define *pww* "probably_wont_work") (define (>>weak weak-edges) (for-each (lambda (edge) (let* ((up (edge-up edge)) (down (edge-down edge)) (var (format #f "have_guile_module~A" (safe-name up)))) (format #t "GUILE_MODULE_AVAILABLE(~A, ~A)\n" var up) (format #t "test \"$~A\" = no &&\n ~A=\"~A $~A\"~A" var *pww* down *pww* "\n\n"))) weak-edges)) (define (>>program module progs) (let ((vars (map (lambda (prog) (format #f "guile_module~Asupport_~A" (safe-name module) prog)) progs))) (for-each (lambda (var prog) (format #t "AC_PATH_PROG(~A, ~A)\n" var prog)) vars progs) (format #t "test \\\n") (for-each (lambda (var) (format #t " \"$~A\" = \"\" -o \\\n" var)) vars) (format #t "~A &&\n~A=\"~A $~A\"\n\n" (list-ref (list "war = peace" "freedom = slavery" "ignorance = strength") (random 3)) *pww* module *pww*))) (define (>>programs programs) (for-each (lambda (form) (>>program (car form) (cdr form))) programs)) (define (unglob pattern) (let ((p (open-input-pipe (format #f "echo '(' ~A ')'" pattern)))) (map symbol->string (read p)))) (define (>>checks forms) (let* ((cfg (canonical-configuration forms)) (files (apply append (map unglob (cfg 'files-glob)))) (ncx (cfg 'non-critical-external)) (nci (cfg 'non-critical-internal)) (prog (cfg 'non-critical)) (report ((make-frisker) files)) (external (report 'external))) (let ((pww-varname (cfg 'pww-varname))) (or (null? pww-varname) (set! *pww* (car pww-varname)))) (receive (weak strong) (partition (lambda (module) (or (member module ncx) (every (lambda (i) (member i nci)) (map edge-down (mod-down-ls module))))) external) (format #t "AC_DEFUN([AUTOFRISK_CHECKS],[\n\n") (>>strong strong) (format #t "\n~A=~S\n\n" *pww* "") (>>weak (fold (lambda (module so-far) (append so-far (mod-down-ls module))) (list) weak)) (>>programs (cfg 'programs)) (format #t "AC_SUBST(~A)\n])\n\n" *pww*)))) (define (>>summary) (format #t (symbol->string '#{ AC_DEFUN([AUTOFRISK_SUMMARY],[ if test ! "$~A" = "" ; then p=" ***" echo "$p" echo "$p NOTE:" echo "$p The following modules probably won't work:" echo "$p $~A" echo "$p They can be installed anyway, and will work if their" echo "$p dependencies are installed later. Please see README." echo "$p" fi ]) }#) *pww* *pww*)) (define (autofrisk . args) (let ((file (if (null? args) "modules.af" (car args)))) (or (file-exists? file) (error "could not find input file:" file)) (with-output-to-file (format #f "~A.m4" file) (lambda () (>>checks (read-scheme-source-silently file)) (>>summary))))) (define main autofrisk) ;; Local variables: ;; eval: (put 'receive 'scheme-indent-function 2) ;; End: ;;; autofrisk ends here