source: project/release/4/salmonella/trunk/salmonella @ 15873

Last change on this file since 15873 was 15873, checked in by Ivan Raikov, 11 years ago

yet more fixes to salmonella install prefix semantics

File size: 15.7 KB
Line 
1#!/bin/sh
2#| -*- scheme -*-
3exec csi -s $0 "$@"
4|#
5
6(use setup-download utils posix (srfi 1 13))
7
8(define major-version     4)
9(define chicken-install   (make-parameter "chicken-install"))
10(define lib-dir           (make-pathname '("lib" "chicken") (->string major-version)))
11(define repo-test-dir     (make-parameter (make-pathname (current-directory) "salmonella-repo")))
12(define repo-test-lib-dir (make-parameter (make-pathname (repo-test-dir) lib-dir)))
13(define chicken-eggs-dir  (make-parameter (make-pathname `(,(current-directory) "chicken-eggs" "release")
14                                                         (->string major-version))))
15(define chicken-install-args (make-parameter (lambda () (string-append " -prefix " (repo-test-dir) " -t local "
16                                                                       " -l " (chicken-eggs-dir)))))
17
18
19(define env-vars      (make-parameter
20                       (string-append
21                        "CHICKEN_INSTALL_PREFIX=" (repo-test-dir)
22                        " "
23                        "CHICKEN_INCLUDE_PATH=" (repo-test-dir) "share/chicken"
24                        " "
25                        "CHICKEN_REPOSITORY=" (repo-test-lib-dir)
26                        )))
27                       
28(define skip-eggs     (make-parameter '()))
29(define verbose       (make-parameter #f))
30(define logfile       (make-parameter "salmonella.log"))
31(define system-deps   (make-parameter #f))
32(define pkg-manager   (make-parameter #f))
33(define resume        (make-parameter #f))
34(define dont-ask      (make-parameter #f))
35(define progress-file (make-parameter "salmonella.progress"))
36(define html-output   (make-parameter "salmonella-report"))
37(define css-file      (make-parameter #f))
38(define ->html        (make-parameter #f))
39
40(define (show . msg)
41  (when (verbose)
42    (print "  " (string-concatenate (map ->string msg)))))
43
44(define egg-information (make-parameter (lambda () #f)))
45
46(define eggs (make-parameter (lambda () #f)))
47
48(define (system:system . command)
49  ;; I'm afraid this thing won't work on windows...
50  (let* ((p (open-input-pipe (sprintf "~A 2>&1" (string-concatenate command))))
51         (output (read-all p)))
52    (values (close-input-pipe p) output)))
53
54(define (system:output . command)
55  (let-values (((status output) (system:system (string-concatenate command))))
56    output))
57
58(define (system:status . command)
59  (let-values (((status output) (system:system (string-concatenate command))))
60    status))
61
62(define usage #<#EOF
63Usage: salmonella -h | -help | --help | -?
64       salmonella <conf-file>
65       salmonella <options>
66
67<options> are Scheme expressions. <conf-file> is a file which may
68contain the same Scheme expressions as <options>'s. The following
69expressions are recognized:
70
71             == Expression ==                   == Default ==
72 (chicken-install \"<path to chicken-install>\")    chicken-install
73 (chicken-install-args \"<args>\"                 a procedure which returns chicken-install options
74 (env-vars \"<environment variables settings>\" CHICKEN_INCLUDE_PATH=(repo-test-lib-dir)
75                                                CHICKEN_INSTALL_PREFIX=(repo-test-dir)
76 (repo-test-dir \"<path to repo-test-dir>\")    `pwd`/salmonella-repo
77 (chicken-eggs-dir \"<path to SVN dir>\")       `pwd`/chicken-eggs/release/4
78 (skip-eggs (list \"egg1\" \"egg2\" ...))         '()
79 (verbose <bool>)                               ##f
80 (logfile \"filename\")                         salmonella.log
81 (egg-information (lambda () ...))              a procedure which returns egg information for all eggs found in (chicken-eggs-dir)
82 (eggs (lambda () ...))                         a procedure which returns the names of all eggs found in (chicken-eggs-dir)
83 (system-deps \"filename\")                     ##f
84 (pkg-manager \"system pkg manager & args\")    ##f
85 (resume <bool>)                                ##f
86 (dont-ask <bool>)                              ##f
87 (progress-file \"filename\")                   salmonella.progress
88 (dont-ask <bool>)                              ##f
89 (progress-file \"filename\")                   salmonella.progress
90 (html-output \"dirname\")                      salmonella-report.  If ##f, no HTML output
91 (css-file \"filename\")                        ##f
92 (->html \"filename\")                          ##f
93Example:
94
95 salmonella '(chicken-install \"/usr/local/chicken/bin/chicken-install\") (verbose ##t)'
96EOF
97)
98
99(define (delete-path . paths)
100  ;; mostly stolen from chicken-setup.scm
101  (define *windows*
102    (and (eq? (software-type) 'windows)
103         (build-platform) ) )
104
105  (define *windows-shell* (memq *windows* '(msvc mingw32)))
106  (let ((cmd (if *windows-shell* "del /Q /S" "rm -fr")))
107    (for-each (lambda (path) (system* "~a ~a" cmd path)) paths)))
108
109(define (clean-repo-test-dir)
110  (delete-path (make-pathname (repo-test-dir) "*")))
111
112(define (delete-eggs-source #!optional confirm)
113  (when confirm
114    (display (string-append "salmonella will delete all *.egg and *.egg-dir files and directories under "
115                            (current-directory) ".  Proceed? [y/n]: "))
116    (flush-output)
117    (unless (string=? (read-line) "y")
118      (print "Aborting.")
119      (exit 0)))
120  (delete-path (make-pathname (current-directory) "*.egg")
121               (make-pathname (current-directory) "*.egg-dir")))
122
123(define (install-egg egg)
124  (system:system (sprintf "~a ~a ~a ~a" (env-vars) (chicken-install) ((chicken-install-args)) egg)))
125
126(define (init-repo)
127  (system:system (sprintf "~a ~a ~a" (chicken-install) "-init"  (repo-test-lib-dir))))
128
129(define (report egg status msg #!key (action 'egg-install))
130  (with-output-to-file (logfile)
131    (lambda () (pp (list egg action status msg))) append:))
132
133(define (set-installed egg)
134  (with-output-to-file (progress-file) (cut print egg) append:))
135
136(define (current-time) (seconds->string (current-seconds)))
137
138(define (report-env)
139  (when (logfile)
140    (with-output-to-file (logfile)
141      (lambda ()
142        (pp #<#EOF
143salmonella -- a tool for testing Chicken eggs (http://chicken.wiki.br/eggref/#(->string major-version)/salmonella)
144#(string-trim-both (current-time))
145#(system:output (chicken-install) " -version")
146Options:
147  chicken-install: #(chicken-install)
148  repo-test-dir: #(repo-test-dir)
149  chicken-install-args: #((chicken-install-args))
150  env-vars: #(env-vars)
151  skip-eggs:  #(skip-eggs)
152  verbose: #(verbose)
153  logfile: #(logfile)
154  pkg-manager: #(pkg-manager)
155  system-deps: #(system-deps)
156  resume: #(resume)
157  dont-ask: #(dont-ask)
158  progress-file: #(progress-file)
159  html-output: #(html-output)
160  css-file: #(css-file)
161  ->html: #(->html)
162EOF
163)))))
164
165(define (install-system-deps egg dep-table)
166  (let ((deps (assq (string->symbol egg) dep-table)))
167    (when deps
168      (for-each
169       (lambda (dep)
170         (show "Installing system dependency " dep)
171         (let-values (((status output) (system:system (pkg-manager) egg)))
172           (report egg status output action: 'osdep-install)))
173       (map ->string (cdr deps))))))
174
175(define (create-repo-test-dir)
176  (if (not (file-exists? (repo-test-lib-dir)))
177      (create-directory (repo-test-lib-dir) #t)))
178
179(define egg-dependencies
180  (let ((eggs/deps '()))
181    (lambda arg
182      (when (null? eggs/deps)
183        (set! eggs/deps (map (lambda (egg-data)
184                               (cons (car egg-data) (drop egg-data 3)))
185                             ((egg-information)))))
186      (if (null? arg)
187          eggs/deps
188          (let ((egg (car arg)))
189            (alist-ref (if (string? egg)
190                           (string->symbol egg)
191                           egg)
192                       eggs/deps))))))
193
194(define (main)
195  (let ((args (command-line-arguments)))
196   
197    ;; Check if user is asking for help
198    (unless (null? args)
199      (when (member (car args) '("-h" "-help" "--help" "-?"))
200        (print usage)
201        (exit 0)))
202   
203    ;; Eval command line options
204    (if (and (not (null? args)) (file-exists? (car args)))
205        (load (car args))
206        (for-each eval (with-input-from-string (string-intersperse args) read-file)))
207
208    (unless ((eggs))
209      (eggs (lambda ()
210              (show "Fetching eggs list...")
211              (map (compose ->string car) ((egg-information))))))
212
213    (unless ((egg-information))
214      (egg-information
215       (let ((egg-data #f))
216         (lambda () (if (not egg-data)
217                        (begin
218                          (set! egg-data (gather-egg-information (chicken-eggs-dir)))
219                          egg-data)
220                        egg-data)))))
221
222    (when (->html)
223      (logfile (->html))
224      (report->html)
225      (exit 0))
226         
227    (if (resume)
228        (if (file-exists? (progress-file))
229            (skip-eggs (append (skip-eggs) (read-lines (progress-file))))
230            (begin
231              (print "Could not find progress-file " (progress-file))
232              (exit 1)))
233        (when (file-exists? (progress-file))
234          (delete-file (progress-file))))
235   
236    (clean-repo-test-dir)
237    (create-repo-test-dir)
238    (init-repo)
239   
240    (report-env)
241
242    (delete-eggs-source (not (dont-ask)))
243   
244    (let ((deps (and (pkg-manager) (system-deps) (read-file (system-deps))))
245          (successful '())
246          (fail '()))
247      (for-each (lambda (egg)
248                  (unless (member egg (skip-eggs))
249                    (install-system-deps egg deps)
250                    (show "Deleting " egg " egg source (if it exists)...")
251                    (delete-eggs-source)
252                    (display (string-append "Installing " egg))
253                    (flush-output)
254                    (let-values (((status output) (install-egg egg)))
255                      (print (string-pad
256                              (cond ((zero? status)
257                                     (set! successful (cons egg successful))
258                                     "[ok]")
259                                    (else (set! fail (cons egg fail))
260                                          "[error]"))
261                              (- 50 (string-length egg))))
262                      (when (logfile) (report egg status output)))
263                    (set-installed egg)
264                    (show "Cleaning up " (repo-test-dir) "...")
265                    (clean-repo-test-dir)
266                    (create-repo-test-dir)
267                    ))
268                ((eggs))))))
269
270(define css-data
271"body { font-size: 10pt; }
272#egg-even { background-color: #CCC; }
273#egg-fail { font-weight: bold; color: red; }
274#egg-ok { font-weight: bold; color: blue; }
275#egg-odd { background-color: #FFFFCC; }")
276 
277;;; A simple web-scheme replacement
278
279(define s+ string-append)
280
281(define (make-tag tagname)
282  (lambda (data #!key (id #f))
283    (conc "<" tagname (if id (conc " id=\"" id "\"") "") ">"
284          data
285          "</" tagname ">")))
286
287(define (a url text)
288  (s+ "<a 'href='" url "'>" text "</a>"))
289
290(define p (make-tag 'p))
291(define div (make-tag 'div))
292(define pre (make-tag 'pre))
293(define td (make-tag 'td))
294(define tr (make-tag 'tr))
295(define table (make-tag 'table))
296(define b (make-tag 'b))
297(define html (make-tag 'html))
298(define body (make-tag 'body))
299(define head (make-tag 'head))
300(define title (make-tag 'title))
301(define meta (make-tag 'meta))
302(define style (make-tag 'style))
303(define h1 (make-tag 'h1))
304(define h2 (make-tag 'h2))
305(define h3 (make-tag 'h3))
306
307(define (ws:page contents #!key (css #f) (page-title ""))
308  (html
309   (s+ (head (s+ (title page-title)
310                 (if css
311                     (s+ "<link rel=stylesheet href='" css "' type=text/css>")
312                     "")))
313       (body contents))))
314
315(define (tag-attribs->string attribs)
316  (string-intersperse (map (lambda (attrib)
317                             (conc (car attrib) "=\"" (cdr attrib) "\""))
318                           attribs)
319                      " "))
320
321(define alternate-odd/even
322  (let ((current #f))
323    (lambda ()
324      (set! current (not current))
325      (if current
326          'egg-even
327          'egg-odd))))
328   
329(define (ws:make-table alist)
330  (table (string-intersperse
331          (map (lambda (line)
332                 (tr (string-intersperse (map td line) "") id: (alternate-odd/even)))
333               alist) "")))
334
335(define (report->html)
336
337  (define (link-egg-doc egg)
338    (a (conc "http://chicken.wiki.br/eggref/" major-version "/" egg ".html")
339       "egg page"))
340
341  (define (link-egg-deps egg)
342    (a (conc "http://chicken.wiki.br/dep-graphs/" egg ".png")
343       "dependencies"))
344
345  (define (egg-fail? egg eggs)
346    (let ((egg-data (alist-ref (->string egg) eggs equal?)))
347      ;; some eggs are not explicitly tested, so have no status
348      (and egg-data (not (zero? (cadr egg-data))))))
349         
350  ;; if the optional argument FAIL is not #f, it indicates that all the EGGS failed
351  (define (tabularize-eggs eggs css #!optional fail)
352    (ws:make-table
353     (append
354      (list (map b (let ((h '("Egg" "Date" "Doc" "Dependencies")))
355                     (if fail
356                         (append h (list "Broken dependencies"))
357                         h))))
358      (map (lambda (egg-data)
359             (let ((egg (car egg-data))
360                   (status (caddr egg-data))
361                   (output (cadddr egg-data)))
362               ;; write the per-egg report
363               (with-output-to-file (make-pathname (html-output) egg ".html")
364                 (lambda ()
365                   (print (ws:page (string-append (h1 egg) (pre output))
366                                   css: css
367                                   page-title: egg))))
368               ;; generate the eggs table
369               (let ((version-str (lambda (egg-data)
370                                    (or (and egg-data (let ((x (alist-ref 'version egg-data)))
371                                                        (and x (car x)))) ""))))
372               (map (lambda (info) (a (make-pathname '() egg ".html") info))
373                    (let ((data
374                           (list egg
375                                 (let ((egg-data (alist-ref (string->symbol egg) ((egg-information)))))
376                                  (version-str egg-data))
377                                 (link-egg-doc egg)
378                                 (link-egg-deps egg))))
379                      (if fail
380                          (append data (list (string-intersperse
381                                              (map link-egg-doc
382                                                   (filter (lambda (egg)
383                                                             (egg-fail? egg eggs))
384                                                           (egg-dependencies egg)))
385                                              ",")))
386                          data))))))
387           (sort eggs (lambda (s1 s2) (string< (car s1) (car s2))))))))
388
389  (show "Generating HTML report")
390  (when (file-exists? (html-output))
391    (delete-path (html-output)))
392  (create-directory (html-output))
393  (let* ((data (with-input-from-file (logfile) read-file))
394         (env-report (car data))
395         (eggs-report (cdr data))
396         (css (or (css-file)
397                  (begin
398                    (with-output-to-file (make-pathname (html-output) "salmonella.css")
399                      (cut print css-data))
400                    "salmonella.css"))))
401    (with-output-to-file (make-pathname (html-output) "index.html")
402      (lambda ()
403        (print
404         (ws:page
405          (string-append
406           (h1 "Salmonella report")
407           (h2 "Summary")
408           (let* ((success (filter (lambda (l) (zero? (caddr l))) eggs-report))
409                  (fail (filter (lambda (l) (not (zero? (caddr l)))) eggs-report))
410                  (success-count (length success))
411                  (fail-count (length fail))
412                  (total (+ (length eggs-report) (length (skip-eggs)))))
413             (string-append
414              (ws:make-table
415               `(("Failed"   ,(div (number->string fail-count) id: "egg-fail"))
416                 ("Succeed"  ,(div (number->string success-count) id: "egg-ok"))
417                 ("Skipped"  ,(length (skip-eggs)))
418                 (,(b "Total")    ,(b (number->string total)))))
419              (h2 "Eggs")
420              (h3 "Failed") (tabularize-eggs fail css 'fail)
421              (h3 "Succeed") (tabularize-eggs success css)
422              (if (null? (skip-eggs))
423                  ""
424                  (string-append
425                   (h3 "Skipped")
426                   (ws:make-table (append `((,(b "Egg"))) (map list (skip-eggs))))))))
427           (h2 "Environment")
428           (pre env-report)
429           (p (conc "Finished at: " (current-time))))
430          css: css
431          page-title: "Salmonella report"))))))
432
433(main)
434(when (html-output) (report->html))
Note: See TracBrowser for help on using the repository browser.