Spaces:
Sleeping
Sleeping
#!/bin/sh | |
# aside from this initial boilerplate, this is actually -*- scheme -*- code | |
main='(module-ref (resolve-module '\''(scripts api-diff)) '\'main')' | |
exec ${GUILE-guile} -l $0 -c "(apply $main (cdr (command-line)))" "$@" | |
!# | |
;;; api-diff --- diff guile-api.alist files | |
;; 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 <ttn@gnu.org> | |
;;; Commentary: | |
;; Usage: api-diff [-d GROUPS] ALIST-FILE-A ALIST-FILE-B | |
;; | |
;; Read in the alists from files ALIST-FILE-A and ALIST-FILE-B | |
;; and display a (count) summary of the groups defined therein. | |
;; Optional arg "--details" (or "-d") specifies a comma-separated | |
;; list of groups, in which case api-diff displays instead the | |
;; elements added and deleted for each of the specified groups. | |
;; | |
;; For scheme programming, this module exports the proc: | |
;; (api-diff A-file B-file) | |
;; | |
;; Note that the convention is that the "older" alist/file is | |
;; specified first. | |
;; | |
;; TODO: Develop scheme interface. | |
;;; Code: | |
(define-module (scripts api-diff) | |
:use-module (ice-9 common-list) | |
:use-module (ice-9 format) | |
:use-module (ice-9 getopt-long) | |
:autoload (srfi srfi-13) (string-tokenize) | |
:export (api-diff)) | |
(define (read-alist-file file) | |
(with-input-from-file file | |
(lambda () (read)))) | |
(define put set-object-property!) | |
(define get object-property) | |
(define (read-api-alist-file file) | |
(let* ((alist (read-alist-file file)) | |
(meta (assq-ref alist 'meta)) | |
(interface (assq-ref alist 'interface))) | |
(put interface 'meta meta) | |
(put interface 'groups (let ((ht (make-hash-table 31))) | |
(for-each (lambda (group) | |
(hashq-set! ht group '())) | |
(assq-ref meta 'groups)) | |
ht)) | |
interface)) | |
(define (hang-by-the-roots interface) | |
(let ((ht (get interface 'groups))) | |
(for-each (lambda (x) | |
(for-each (lambda (group) | |
(hashq-set! ht group | |
(cons (car x) | |
(hashq-ref ht group)))) | |
(assq-ref x 'groups))) | |
interface)) | |
interface) | |
(define (diff? a b) | |
(let ((result (set-difference a b))) | |
(if (null? result) | |
#f ; CL weenies bite me | |
result))) | |
(define (diff+note! a b note-removals note-additions note-same) | |
(let ((same? #t)) | |
(cond ((diff? a b) => (lambda (x) (note-removals x) (set! same? #f)))) | |
(cond ((diff? b a) => (lambda (x) (note-additions x) (set! same? #f)))) | |
(and same? (note-same)))) | |
(define (group-diff i-old i-new . options) | |
(let* ((i-old (hang-by-the-roots i-old)) | |
(g-old (hash-fold acons '() (get i-old 'groups))) | |
(g-old-names (map car g-old)) | |
(i-new (hang-by-the-roots i-new)) | |
(g-new (hash-fold acons '() (get i-new 'groups))) | |
(g-new-names (map car g-new))) | |
(cond ((null? options) | |
(diff+note! g-old-names g-new-names | |
(lambda (removals) | |
(format #t "groups-removed: ~A\n" removals)) | |
(lambda (additions) | |
(format #t "groups-added: ~A\n" additions)) | |
(lambda () #t)) | |
(for-each (lambda (group) | |
(let* ((old (assq-ref g-old group)) | |
(new (assq-ref g-new group)) | |
(old-count (and old (length old))) | |
(new-count (and new (length new))) | |
(delta (and old new (- new-count old-count)))) | |
(format #t " ~5@A ~5@A : " | |
(or old-count "-") | |
(or new-count "-")) | |
(cond ((and old new) | |
(let ((add-count 0) (sub-count 0)) | |
(diff+note! | |
old new | |
(lambda (subs) | |
(set! sub-count (length subs))) | |
(lambda (adds) | |
(set! add-count (length adds))) | |
(lambda () #t)) | |
(format #t "~5@D ~5@D : ~5@D" | |
add-count (- sub-count) delta))) | |
(else | |
(format #t "~5@A ~5@A : ~5@A" "-" "-" "-"))) | |
(format #t " ~A\n" group))) | |
(sort (union g-old-names g-new-names) | |
(lambda (a b) | |
(string<? (symbol->string a) | |
(symbol->string b)))))) | |
((assq-ref options 'details) | |
=> (lambda (groups) | |
(for-each (lambda (group) | |
(let* ((old (or (assq-ref g-old group) '())) | |
(new (or (assq-ref g-new group) '())) | |
(>>! (lambda (label ls) | |
(format #t "~A ~A:\n" group label) | |
(for-each (lambda (x) | |
(format #t " ~A\n" x)) | |
ls)))) | |
(diff+note! old new | |
(lambda (removals) | |
(>>! 'removals removals)) | |
(lambda (additions) | |
(>>! 'additions additions)) | |
(lambda () | |
(format #t "~A: no changes\n" | |
group))))) | |
groups))) | |
(else | |
(error "api-diff: group-diff: bad options"))))) | |
(define (api-diff . args) | |
(let* ((p (getopt-long (cons 'api-diff args) | |
'((details (single-char #\d) | |
(value #t)) | |
;; Add options here. | |
))) | |
(rest (option-ref p '() '("/dev/null" "/dev/null"))) | |
(i-old (read-api-alist-file (car rest))) | |
(i-new (read-api-alist-file (cadr rest))) | |
(options '())) | |
(cond ((option-ref p 'details #f) | |
=> (lambda (groups) | |
(set! options (cons (cons 'details | |
(map string->symbol | |
(string-tokenize | |
groups | |
#\,))) | |
options))))) | |
(apply group-diff i-old i-new options))) | |
(define main api-diff) | |
;;; api-diff ends here | |