genmake.scm

From: Shiro Kawai <shiro_at_lava.net>
Date: Tue, 05 Oct 1999 03:44:15 -1000

Inspired by Perl's extension mechanism, I modified stk-genmake
to be a module to ease distribution and installation of STk
extensions.

The basic ides is that the extension writer provides a small file
called Makefile.scm, which contains lines like these:

  (require "genmake")
  (generate-makefile :target-scm "extension.scm"
                     :target-so "extention")

To compile and install the extension, you will do

  stk Makefile.scm
  make
  make install

At least it helps me to install my own code on different
platforms. But I wonder if you think it's useful, or
if there is already mechanisms something like this.

;;;
;;; genmake.scm - generate Makefile
;;;
;;; Copyright (c) 1999 Shiro Kawai (shiro_at_acm.org)
;;; Permission to use, modify, and distribute of this code is granted
;;; under the same condition as STk.
;;; This code is provided `as is', without any warranty.
;;;
;;; $Id: genmake.scm,v 1.4 1999/10/05 13:30:11 shiro Exp $


;;; This module is a modification of stk-genmake script to make it
;;; more customizable and extensible. I borrowed the idea from Perl's
;;; ExtUtils::MakeMaker.
;;;
;;; Each extention is provided with a file called Makefile.scm, which
;;; looks like this:
;;;
;;; (require "genmake")
;;; (generate-makefile :target-scm "myextension.scm"
;;; :target-so '(("myextension" "myextension" "moresource"))
;;; :libs '("-lmylib")
;;; :lpath '("-L/usr/home/my/lib")
;;; )
;;;
;;; If you want to install new extension module, all you need to do is
;;;
;;; stk Makefile.scm
;;; make
;;; make install
;;;
;;; Valid keyword argument for generate-makefile. To generate meaningful
;;; Makefile, you need to specify at least one of :target-so or :target-scm.
;;; The default values of other parameters are taken from config.make file.
;;;
;;; :target-so SOFILE-SPEC
;;;
;;; Specifies the target compiled shared file(s).
;;; Syntax of SOFILE is as follows:
;;;
;;; SOFILE-SPEC : soname | (SOFILE-DEP ...)
;;; SOFILE-DEP : soname | (soname oname ...)
;;;
;;; wher `soname' is a string specifying the name of target file,
;;; and `oname' is a string specifying the object file to create
;;; the target .so file. You shouldn't add suffix to `soname'
;;; and `oname'. Here are some examples
;;;
;;; :target-so "foo"
;;;
;;; genmake assumes "foo.so" is created from "foo.o"
;;;
;;; :target-so '("foo" "bar")
;;;
;;; genmake assumes "foo.so" is created from "foo.o", and
;;; "bar.so" is created from "bar.o".
;;;
;;; :target-so '(("foo" "foo1" "foo2" "foo3")
;;; "bar")
;;;
;;; genmake assumes "foo.so" is created from "foo1.o",
;;; "foo2.o" and "foo3.o", while "bar.so" is created from
;;; "bar.o".
;;;
;;; If :target-so is specified multiple times, SOFILE-SPEC
;;; are accumulated.
;;;
;;; :target-scm SCMFILE
;;; :target-scm (SCMFILE ...)
;;;
;;; Specifies the scheme source(s) to be installed.
;;; If :target-scm is specified multiple times, SCMFILEs are
;;; accumulated. You need to specify suffix.
;;;
;;; :ipath "-Ipath"
;;; :ipath ("-Ipath" ...)
;;;
;;; Add "-Ipath" to IPATH variable which is used when .c file
;;; is compiled.
;;;
;;; :libs "-llib"
;;; :libs ("-llib" ...)
;;;
;;; Add "-llib" to LIBS variable which is used to create .so file.
;;;
;;; :lpath "-Lpath"
;;; :lpath ("-Lpath" ...)
;;;
;;; Add "-Llib" to LPATH variable which is used to create .so file.
;;;
;;; :prefix DIR
;;;
;;; Override prefix directory.
;;;

(define-module genmake
  (export generate-makefile)

;;; Default

;;; The Large portion of code is taken from stk-genmake by
;;; Eric Gallesio [eg_at_unice.fr]
;;; Copyright (c) 1998-1999 Erick Gallesio - I3S-CNRS/ESSI <eg_at_unice.fr>

(define version "0.1")
(define config.make-file ; Complete path name of the Config.make file
  (string-append (%library-location) "/" (machine-type) "/Config/config.make"))

(define (abort . l)
  (apply format (current-error-port) l)
  (newline (current-error-port))
  (exit 0))

(define (warn . l)
  (apply format (current-error-port)l)
  (newline (current-error-port)))

;;;
;;; Parse config.make
;;;

(define *parameters* '()) ;alist of parameters
(define *so-dependencies* '()) ;alist of dependency between .so & .o

(define rx-comment (string->regexp "^[ \t]*#"))
(define rx-continue (string->regexp "\\\\$"))
(define rx-define (string->regexp "^[ \t]*([_A-Za-z][_0-9A-Za-z]*)[ \t]*=[ \t]*(.*)"))

(define (parse-config-make)
  (if (not (file-exists? config.make-file))
      (abort "~A: File ~S does not exist (you probably need a \"make install.libs\""
             *program-name*))
  (with-input-from-file config.make-file
    (lambda ()
      (let loop ((line (read-line)) (pending '()))
        (cond
         ((eof-object? line)
          (if (not (null? pending))
              (warn "warning: unterminated line at the end of config.make" )))
         ((rx-comment line) (loop (read-line) pending))
         ((rx-continue line)
          => (lambda (match)
               (loop (read-line)
                     (cons (substring line 0 (caar match)) line))))
         (else
          (push-parameter (apply string-append
                                 (reverse (cons line pending))))
          (loop (read-line) '()))))))
  )

(define (push-parameter string)
  (cond ((rx-define string)
         => (lambda (match)
              (let ((var (substring string
                                    (car (cadr match)) (cadr (cadr match))))
                    (val (substring string
                                    (car (caddr match)) (cadr (caddr match)))))
                (set! *parameters* (cons (list var val) *parameters*)))))
        (else
         (warn "warning: unrecognized line in config.make: ~a" string))))

;;;
;;; Configure parameters
;;;

(define (set-parameter param value)
  (let ((a (assoc param *parameters*))
        (v (if (pair? value) value (list value))))
    (if a
        (set-cdr! a v)
        (set! *parameters* (cons (cons param v) *parameters*)))))

(define (add-parameter param value)
  (let ((a (assoc param *parameters*))
        (v (if (pair? value) value (list value))))
    (if a
        (set-cdr! a (append! (cdr a) v))
        (set! *parameters* (cons (cons param v) *parameters*)))))

(define (add-so-dependency sofile ofiles)
  (let ((a (assoc sofile *so-dependencies*)))
    (if a
        (set-cdr! a (append! (cdr a) ofiles))
        (set! *so-dependencies* (cons (cons sofile ofiles) *so-dependencies*)))))

(define (target-defined)
  (assoc "TARGET" *parameters*))

(define (target-scm-defined)
  (assoc "TARGET_SCM" *parameters*))

(define (target-so-defined)
  (assoc "TARGET_SO" *parameters*))

;; Mapping from keyword to parameter.

(define *parameter-list*
  ;; keyword add? parameter
  `((:prefix #f "prefix")
    (:stkdir #f "stkdir")
    (:libdir #f "libdir")
    (:execdir #f "execdir")
    (:confdir #f "confdir")
    (:ardir #f "ardir")
    (:incdir #f "incdir")
    (:mandir #f "mandir")
    (:bindir #f "bindir")
    (:dflgs #t "DFLGS")
    (:eobj #t "EOBJ")
    (:etkobj #t "ETKOBJ")

    (:ipath #t "IPATH") ;extra -Isomething
    (:lpath #t "LPATH") ;extra -Lsomething
    (:libs #t "LIBS") ;extra -lsomething

    (:target-scm #t "TARGET_SCM")
    ))

(define (process-parameters keylist)
  (let loop ((keylist keylist))
    (cond ((null? keylist) (adjust-target))
          ((or (null? (cdr keylist))
               (not (keyword? (car keylist))))
           (abort "generate-makefile: Bad keyword argument: ~s" (car keylist)))
          ((assq (car keylist) *parameter-list*)
           => (lambda (entry)
                ((if (cadr entry) add-parameter set-parameter)
                 (caddr entry)
                 (cadr keylist))
                (loop (cddr keylist))))
          ((eq? (car keylist) :target-so)
           (process-target-so (cadr keylist))
           (loop (cddr keylist)))
          (else
           (abort "generate-makefile: Unknown keyword: ~s" (car keylist))))))

(define (add-sh-suffix base)
  (string-append base ".$(SH_SUFFIX)"))

(define (process-target-so desc)
  (cond ((string? desc)
         (add-parameter "TARGET_SO" (add-sh-suffix desc))
         (add-so-dependency desc (list desc)))
        ((list? desc)
         (for-each (lambda (desc)
                     (cond ((string? desc)
                            (add-parameter "TARGET_SO"
                                           (add-sh-suffix desc))
                            (add-so-dependency desc (list desc)))
                           ((list? desc)
                            (add-parameter "TARGET_SO"
                                           (add-sh-suffix (car desc)))
                            (add-so-dependency (car desc) (cdr desc)))
                           (else
                            (abort "generate-makefile: bad spec in :target-so: ~s" desc))))
                   desc))
        (else (abort "generate-makefile: bad spec in :target-so : ~s" desc))
        ))

(define (adjust-target)
  (if (target-scm-defined)
      (add-parameter "TARGET" "$(TARGET_SCM)"))
  (if (target-so-defined)
      (add-parameter "TARGET" "$(TARGET_SO)")))

;;;
;;; Write Makefile
;;;

(define (write-parameters)
  (for-each (lambda (p)
              (display (car p))
              (let ((len (string-length (car p)))) ;make it look nice
                (if (< len 16)
                    (dotimes (n (- 15 len)) (display #\ ))))
              (display " = ")
              (let loop ((value (cdr p)))
                (cond ((null? value) (newline))
                      ((null? (cdr value)) (display (car value)) (newline))
                      (else (display (car value))
                            (display #\ )
                            (loop (cdr value))))))
            (reverse *parameters*)))

(define (generate-prelude)
  (format #t "# Makefile automatically generated by genmake version ~A. DO NOT EDIT\n"
          version)
  (write-parameters)
  ;; Generate the .SUFFIXES rules
  (format #t "\n")
  (format #t "CFLAGS= $(SH_CCFLAGS) $(STKCFLAGS) $(DFLGS) -DUSE_TK -DHAVE_UNISTD_H=1 -DHAVE_LIMITS_H=1 -DHAVE_SIGACTION=1 $(IPATH) -I$(incdir)\n")
  (format #t "LDFLAGS = $(SH_LDFLAGS) $(LPATH)\n\n")
  (format #t ".SUFFIXES: .$(SH_SUFFIX) .o .c\n\n")
  (format #t ".o.$(SH_SUFFIX):\n")
  (format #t "\t$(SH_LOADER) $(LDFLAGS) $*.$(SH_SUFFIX) $< $(LIBS)\n")
  (format #t "\tif test -f a.out ;then mv a.out $*.$(SH_SUFFIX); fi\n\n")

  (format #t "\nall: $(TARGET)\n\n"))

(define (generate-target)
  (for-each (lambda (dep)
              (format #t "~a.$(SH_SUFFIX):" (car dep))
              (for-each (lambda (ofile)
                          (format #t " ~a.o" ofile))
                        (cdr dep))
              (newline))
            *so-dependencies*)
  (newline))

(define (generate-postlude)
  (format #t "install: $(TARGET)\n")
  (when (target-scm-defined)
     (format #t "\tfor f in $(TARGET_SCM); do\\\n")
     (format #t "\t $(CP) -f $$f $(stkdir)/site-scheme/$$f;\\\n")
     (format #t "\t chmod 444 $(stkdir)/site-scheme/$$f;\\\n")
     (format #t "\tdone\n"))
  (when (target-so-defined)
     (format #t "\tfor f in $(TARGET_SO); do\\\n")
     (format #t "\t $(CP) -f $(ardir)/$$f;\\\n")
     (format #t "\t chmod 444 $(ardir)/$$f;\\\n")
     (format #t "\tdone\n"))
  (format #t "\n")
  (format #t "clean:\n")
  (format #t "\t_at_/bin/rm -f *.o *.$(SH_SUFFIX) core *~\n\n")
  (format #t "realclean: clean\n")
  (format #t "\t_at_/bin/rm -f Makefile\n\n")
  (format #t "#End of Makefile\n"))

;;;
;;; Main entry
;;;

(define (generate-makefile . args)
  (with-output-to-file "Makefile"
    (lambda ()
      (set! *parameters* '())
      (parse-config-make)
      (process-parameters args)
      (generate-prelude)
      (generate-target)
      (generate-postlude)
      (exit 0))))

); define-package genmake

(provide "genmake")
(import genmake) ;make exported symbol available to the current package
Received on Tue Oct 05 1999 - 15:41:27 CEST

This archive was generated by hypermail 2.3.0 : Mon Jul 21 2014 - 19:38:59 CEST