[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

(scripts make-module-catalog)

From: Thien-Thi Nguyen
Subject: (scripts make-module-catalog)
Date: Fri, 19 Sep 2003 15:32:28 +0200

first, see SLIB and grok slibcat.  then, this will make more sense.
e.g., here is a frag from guile-1.4.x top-level

  : [...snip...]
  ls -d $(DESTDIR)$(pkglibdir)/[0-9]* $(DESTDIR)$(pkgdatadir)/[0-9]* \
    | sed 's/^/-x /g' > TMP1
  $(DESTDIR)$(bindir)/guile -c '(for-each write-line %load-path)' \
    | sed '\,'$(prefix)',!d' > TMP2
  $(DESTDIR)$(bindir)/guile-tools make-module-catalog `cat TMP1 TMP2`
  rm -f TMP1 TMP2

(tabs replaced with two spaces.)  TMP1 holds excluded directories,
formatted as: "-x DIR".  these are the version-specific subdirs for
previously installed versions only -- other exclusions are done
automatically by make-module-catalog.  TMP2 holds all dirs in
`%load-path' that are under $(prefix).

see also (scripts slurp), recently posted.


exec ${GUILE-guile} -e '(scripts make-module-catalog)' -s $0 "$@" # -*- scheme 
;;; make-module-catalog --- Create a .module-catalog file

;;      Copyright (C) 2003 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
;; 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., 59 Temple Place, Suite 330,
;; Boston, MA 02111-1307 USA
;; As a special exception, the Free Software Foundation gives permission
;; for additional uses of the text contained in its release of GUILE.
;; The exception is that, if you link the GUILE library with other files
;; to produce an executable, this does not by itself cause the
;; resulting executable to be covered by the GNU General Public License.
;; Your use of that executable is in no way restricted on account of
;; linking the GUILE library code into it.
;; This exception does not however invalidate any other reasons why
;; the executable file might be covered by the GNU General Public License.
;; This exception applies only to the code released by the
;; Free Software Foundation under the name GUILE.  If you copy
;; code from other Free Software Foundation releases into a copy of
;; GUILE, as the General Public License permits, the exception does
;; not apply to the code that you add in this way.  To avoid misleading
;; anyone as to the status of such modified files, you must delete
;; this exception notice from them.
;; If you write modifications of your own for GUILE, it is your choice
;; whether to permit this exception to apply to your modifications.
;; If you do not wish that, delete this exception notice.

;;; Author: Thien-Thi Nguyen <address@hidden>

;;; Commentary:

;; Usage: make-module-catalog [OPTIONS] [DIR ...]
;; Create a module catalog (named ".module-catalog") in each directory DIR.
;; A module catalog is an alist with module names (list of symbols) for the
;; key and CATALOG-INFO, described below, for the value.  OPTIONS are zero
;; or more of the following (defaults in square braces):
;;  -v, --verbose      -- describe each file scanned
;;  -x, --exclude NAME -- do not scan NAME, which may be either a filename
;;                        or a directory name (also excludes children);
;;                        this option may be given multiple times
;;  -o, --output STEM  -- write to DIR/STEM [".module-catalog"]
;;  -b, --bufsize NUM  -- use NUM bytes for the scan buffer [256]
;; Note that STEM may not include a directory component.  Also, the minimum
;; buffer size is 128; a specified NUM less than that is silently adjusted.
;; Each catalog entry has the form:
;; MODULE-NAME is a list of symbols, such as: (scripts slurp).  KW1... are
;; zero or more symbols that indicate special-handling required (if any) for
;; the module.  Here is a list of symbols generated and their reason:
;;   scm_init_module -- module is a dynamically loadable library that
;;                      follows the "scm_init_MODULE_NAME_module"
;;                      convention outlined in the Guile manual
;; FILENAME is a string, the absolute path to the file that provides the
;; interface to the module.  (This file may also provide the definitions, or
;; implementation, of the module, although that is not required.)
;; There are two special catalog entries whose keys are not module names:
;;   **exclude**  -- list of subdirectories excluded from this catalog
;;   **version**  -- version of guile which produced this catalog
;; The asterisks (*) are part of the symbol name.
;; Acknowledgements: The idea and style of the module catalog are directly
;;                   inspired by SLIB.  Thanks again, Aubrey Jaffer!
;; TODO: Factor bounded-space grep into its own module.
;;       Design/export Scheme module interface.

;;; Code:

(define-module (scripts make-module-catalog)
  :autoload (scripts PROGRAM) (HVQC-MAIN)
  :autoload (ice-9 ftw) (nftw)
  :autoload (scripts slurp) (slurp-file!)
  :autoload (ice-9 regex) (match:start match:end)
  :autoload (ice-9 rdelim) (read-line)
  :autoload (ice-9 popen) (open-input-pipe))

(define (make-scanner bufsize)
  (set! bufsize (max bufsize 128))
  (let ((buf (make-string bufsize))
        (rx (make-regexp "^\\(define-module *(\\([^()]*\\))" regexp/newline))
        (backtrack 64))                 ; suits rx
    ;; rv
    (lambda (dir filename statinfo flag)
      (cond ((not (eq? 'regular flag))
             (list #f flag))
               ;; slurp more than necessary to support the comparisons below
               (slurp-file! buf filename 0 8 0)
               (and (char=? #\177 (string-ref buf 0))
                    (string=? "ELF" (make-shared-substring buf 1 4))))
             (let* ((infer (format #f "nm ~A | sed ~A~A~A~A"
                                   "'s/_/ /'"))
                    (p (open-input-pipe infer))
                    (line (read-line p)))
               (close-pipe p)
               (cond ((eof-object? line) (list #f 'unrecognized 'ELF))
                     (else (list (with-input-from-string line read)
            ((string=? "!<arch>" (make-shared-substring buf 0 7))
             (list #f 'ar-archive))
            ((and (char=? #\# (string-ref buf 0))
                  (not (char=? #\! (string-ref buf 1))))
             (list #f 'unixoid-text-config))
            ((let ((p (open-input-file filename))
                   (fsize (stat:size statinfo)))
               (let loop ((start 0))
                 (and start
                      (let* ((left (let ((diff (- (+ start bufsize) fsize)))
                                     (and (< 0 diff)
                                          (- fsize start))))
                             (fill (or left bufsize))
                             ;; We end-justify so that `regexp-exec' never
                             ;; sees the previous `buf' contents, saving us a
                             ;; defensive (and expensive) `string-fill!'.
                             (fpos (- bufsize fill)))
                        (slurp-file! buf p start fill fpos)
                        (or (regexp-exec rx buf fpos)
                            (loop (if left
                                      (+ start (- bufsize backtrack)))))))))
             => (lambda (m)
                  (list (with-input-from-string
                             buf (match:start m 1) (match:end m 1))
            (else (list #f 'unrecognized))))))

(define (make-module-catalog options dir)
  (or (file-exists? dir) (error "no such file:" dir))
  (or (file-is-directory? dir) (error "not a directory:" dir))
  (let* ((fn (or (assq-ref options 'output) ".module-catalog"))
         (out (format #f "~A/~A" dir fn))
         (verbose (assq 'verbose options))
         (exclude (delete dir (or (assq-ref options 'exclude) '())))
         (cur (and (file-exists? out)
                   (let* ((out-mtime (stat:mtime (stat out)))
                          (p (open-input-file out))
                          (ls (read p))
                          (ht (make-hash-table 31)))
                     (close-port p)
                     (or (list? ls)
                         (error "bad file format: ~A" out))
                     (for-each (lambda (prev)
                                 (let* ((file (cdr (last-pair prev)))
                                        (mt (and file (string? file)
                                                 (file-exists? file)
                                                 (stat:mtime (stat file)))))
                                   (and mt (< mt out-mtime)
                                        (hash-set! ht file prev))))
         (cat '())
         (scan (make-scanner (cond ((assq-ref options 'bufsize)
                                    => (lambda (s)
                                         (if (string? s)
                                             (string->number s)
                                   (else 256))))
         (mask #f)
         (orphans '())
         (new 0) (ignored 0) (carried 0))
    (and (assq-ref options 'from-shell)
         (format #t "~A ~A\n" (if (file-exists? out)
    (nftw dir
          (lambda (filename statinfo flag base level)
            (let* ((xk? (let ((rv (and mask (> level mask)))) ; excluded kid
                          (and mask (not rv) (set! mask #f))
                   (res (cond (xk? (list #f 'child-of-excluded level))
                              ((and cur (hash-ref cur filename #f))
                               => (lambda (prev)
                                    (set! carried (1+ carried))
                                    (set! cat (cons prev cat))
                                    (list #f 'previously-scanned)))
                              ((and (not mask) (member filename exclude))
                               (and (eq? 'directory flag) (set! mask level))
                               (set! orphans (cons filename orphans))
                               (list #f 'excluded level))
                               (scan dir filename statinfo flag)))))
              (cond ((car res)
                     (set! new (1+ new))
                     (set! cat (cons `(,@res . ,filename) cat)))
                    (else (set! ignored (1+ ignored))))
              (and verbose (format #t " ~A => ~A\n"
                                   filename (or (car res)
                                                (format #f "ignored ~A"
                                                        (cdr res))))))
            #t)                         ; keep going
    (let ((p (open-output-file out)))
      (format p ";;; ~A\n" out)
      (format p ";;; generated ~A UTC -- do not edit!\n\n"
              (strftime "%Y-%m-%d %H:%M:%S" (gmtime (current-time))))
      (format p "(\n")
      (for-each (lambda (x)
                  (cond ((equal? (car x) '(guile)))
                        ((equal? (car x) '(guile-user)))
                        (else (format p " ~S\n" x))))
      (format p " ~S\n" (cons '**exclude** orphans))
      (format p " ~S\n" (cons '**version** (version)))
      (format p ")\n\n")
      (format p ";;; ~A ends here\n" out))
    (set! ignored (- ignored carried))
    (and (assq-ref options 'from-shell)
         (format #t " => Done ~A.\n"
                 `(,new new ,carried carried-over ,ignored ignored)))
    (list new carried ignored)))

(define (make-module-catalog/qop qop)
  (let* ((options '())
         (chk! (lambda (key)
                 (qop key (lambda (val)
                            (set! options (acons key val options)))))))
    (for-each chk! '(verbose output exclude bufsize))
    (qop 'output (lambda (name)
                   (and (string-index name #\/)
                        (error "cannot include directory:" name))))
    (cond ((assq 'exclude options)
           => (lambda (cell)
                (set-cdr! cell (append (qop '())
                                       (let ((cc (cdr cell)))
                                         (if (list? cc)
                                             (list cc)))))))
          (else (set! options (acons 'exclude (qop '()) options))))
    (set! options (acons 'from-shell #t options))
    (let ((do-it! (lambda (dir)
                    (make-module-catalog options dir))))
      (for-each do-it! (qop '())))))

(define (main args)
   args make-module-catalog/qop
   '(usage . commentary)
   '(package . "Guile")
   '(option-spec (verbose (single-char #\v))
                 (output  (single-char #\o) (value #t))
                 (exclude (single-char #\x) (value #t) (merge-multiple? #t))
                 (bufsize (single-char #\b) (value #t)))))

;;; make-module-catalog ends here

reply via email to

[Prev in Thread] Current Thread [Next in Thread]