123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441 |
- #!/bin/sh
- # -*- mode: scheme; coding: utf-8; -*-
- GUILE_AUTO_COMPILE=0
- export GUILE_AUTO_COMPILE
- main='(@ (run-test) build/run)'
- exec "${GUILE-@GUILE@}" -l "$0" \
- -c "(apply $main (cdr (command-line)))" "$@"
- !#
- ;;; GCC-StarPU
- ;;; Copyright (C) 2011, 2012 Institut National de Recherche en Informatique et Automatique
- ;;;
- ;;; GCC-StarPU 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 3 of the License, or
- ;;; (at your option) any later version.
- ;;;
- ;;; GCC-StarPU 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 GCC-StarPU. If not, see <http://www.gnu.org/licenses/>.
- ;;;
- ;;; Written by Ludovic Courtès <ludovic.courtes@inria.fr>.
- ;;;
- (define-module (run-test)
- #:use-module (ice-9 regex)
- #:use-module (ice-9 popen)
- #:use-module (ice-9 rdelim)
- #:use-module (ice-9 format)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-9)
- #:use-module (srfi srfi-11)
- #:use-module (srfi srfi-13)
- #:use-module (srfi srfi-14)
- #:use-module (srfi srfi-26)
- #:export (build/run))
- ;;; Commentary:
- ;;;
- ;;; Test machinery similar to the DejaGNU-based test framework used in GCC.
- ;;; In a nutshell, this program compiles code with GCC and makes sure
- ;;; warnings and errors are as appear in source code comments.
- ;;;
- ;;; This module should work with both Guile 1.8 and Guile 2.0.
- ;;;
- ;;; Code:
- ;; Make sure the reader gets position information.
- (read-enable 'positions)
- (define %log-port
- ;; Output port for debugging messages.
- (current-output-port))
- (define (log fmt . args)
- "Write an informational message."
- (apply format %log-port (string-append fmt "\n") args))
- ;;;
- ;;; Compiling code.
- ;;;
- (define %srcdir "@srcdir@")
- (define %builddir "@builddir@")
- (define %gcc "@CC@")
- (define %cuda-cppflags
- (string-tokenize "@STARPU_CUDA_CPPFLAGS@"))
- (define %default-cflags
- `("-I" ,%srcdir
- "-I" ,(string-append %srcdir "/../../src") ; for <common/uthash.h>
- "-I" ,(string-append %srcdir "/../../include")
- "-I" ,(string-append %builddir "/../../include")
- "-I" ,(string-append %builddir "/../..")
- ,@%cuda-cppflags
- ;; Unfortunately `libtool --mode=execute' doesn't help here, so hard-code
- ;; the real file name.
- ,(string-append "-fplugin=" %builddir "/../src/.libs/starpu.so")
- ;; Use the non-installed headers.
- ,(string-append "-fplugin-arg-starpu-include-dir=@top_srcdir@/include")
- "-g"
- "-fdump-tree-gimple" "-Wall"))
- (define %default-ldflags
- `(,(string-append "-L" %builddir "/../../src")))
- (define %libtool
- (string-append %builddir "/../../libtool"))
- (define (compile-starpu-code file cc cflags ldflags)
- "Compile and link FILE with CC, using CFLAGS and LDFLAGS. Return the
- compiler status and the list of lines printed on stdout/stderr."
- (let* ((compile? (member "-c" cflags))
- (ldflags (if compile?
- (remove (cut string-prefix? "-L" <>) ldflags)
- ldflags))
- (mode (if compile?
- "compile"
- "link"))
- (command (format #f "LC_ALL=C ~a --mode=~a ~a ~{~a ~} \"~a\" ~{~a ~} 2>&1"
- %libtool mode cc cflags file ldflags))
- (pipe (begin
- (log "running `~a'" command)
- (open-input-pipe command))))
- (let loop ((line (read-line pipe))
- (result '()))
- (if (eof-object? line)
- (values (close-pipe pipe) (reverse result))
- (loop (read-line pipe)
- (cons line result))))))
- (define (run-starpu-code executable)
- "Run EXECUTABLE using Libtool; return its exit status."
- (let* ((exe (if (string-index executable #\/)
- executable
- (string-append (getcwd) "/" executable)))
- (command (string-append %libtool " --mode=execute "
- exe)))
- (log "running `~a'" command)
- (system command)))
- ;;;
- ;;; GCC diagnostics.
- ;;;
- (define-record-type <location>
- (make-location file line column)
- location?
- (file location-file)
- (line location-line)
- (column location-column))
- (define (location=? loc1 loc2)
- "Return #t if LOC1 and LOC2 refer roughly to the same file and line
- number."
- (and (location-file loc1) (location-file loc2)
- (string=? (basename (location-file loc1))
- (basename (location-file loc2)))
- (= (location-line loc1) (location-line loc2))))
- (define-record-type <diagnostic>
- (make-diagnostic location kind message)
- diagnostic?
- (location diagnostic-location)
- (kind diagnostic-kind)
- (message diagnostic-message))
- (define %diagnostic-with-location-rx
- ;; "FILE:LINE:COL: KIND: MESSAGE..."
- (make-regexp "^(.+):([[:digit:]]+):([[:digit:]]+): ([^:]+): (.*)$"))
- (define (string->diagnostic str)
- "Parse STR and return the corresponding `diagnostic' object."
- (cond ((regexp-exec %diagnostic-with-location-rx str)
- =>
- (lambda (m)
- (let ((loc (make-location (match:substring m 1)
- (string->number (match:substring m 2))
- (string->number (match:substring m 3))))
- (kind (string->symbol (match:substring m 4))))
- (make-diagnostic loc kind (match:substring m 5)))))
- (else
- (make-diagnostic #f #f str))))
- ;;;
- ;;; Reading test directives.
- ;;;
- (define (read-test-directives port)
- "Read test directives from PORT. Return a list of location/directive
- pairs."
- (define (consume-whitespace p)
- (let loop ((chr (peek-char p)))
- (cond ((char-set-contains? char-set:whitespace chr)
- (read-char p) ;; consume CHR
- (loop (peek-char p)))
- (else chr))))
- (define (read-until-*/ p)
- (let loop ((chr (read-char p)))
- (cond ((eof-object? chr)
- (error "unterminated C comment"))
- ((eq? chr #\*)
- (let ((next (peek-char p)))
- (if (eq? next #\/)
- (read-char p) ;; consume CHR
- (loop (read-char p)))))
- (else
- (loop (read-char p))))))
- (let loop ((chr (read-char port))
- (directives '()))
- (cond ((eof-object? chr)
- (reverse directives))
- ((eq? chr #\/)
- (let ((chr (read-char port)))
- (if (eq? chr #\*)
- (let ((chr (consume-whitespace port)))
- (if (eq? chr #\()
- (let ((loc (make-location (port-filename port)
- (1+ (port-line port))
- (port-column port)))
- (sexp (read port)))
- (read-until-*/ port)
- (loop (peek-char port)
- (cons (cons loc sexp)
- directives)))
- (begin
- (read-until-*/ port)
- (loop (peek-char port) directives))))
- (loop chr directives))))
- (else
- (loop (read-char port) directives)))))
- (define (diagnostic-matches-directive? diagnostic directive location)
- "Return #t if DIAGNOSTIC matches DIRECTIVE, which is at LOCATION."
- (and (location? (diagnostic-location diagnostic))
- (location=? (diagnostic-location diagnostic) location)
- (match directive
- ((kind message)
- (and (eq? kind (diagnostic-kind diagnostic))
- (string-match message (diagnostic-message diagnostic)))))))
- ;;;
- ;;; Compiling and matching diagnostics against directives.
- ;;;
- (define (compile/match* file directives cc cflags ldflags)
- "Compile FILE and check whether GCC's diagnostics match DIRECTIVES. Return
- 3 values: the compiler's status code, the unmatched diagnostics, and the
- unsatisfied directives."
- (let-values (((status diagnostics)
- (compile-starpu-code file cc cflags ldflags)))
- (let loop ((diagnostics (map string->diagnostic diagnostics))
- (directives directives)
- (unsatisfied '()))
- (if (null? directives)
- (values status diagnostics unsatisfied)
- (let* ((dir (car directives))
- (diag (find (cute diagnostic-matches-directive?
- <> (cdr dir) (car dir))
- diagnostics)))
- (if diag
- (loop (delq diag diagnostics)
- (cdr directives)
- unsatisfied)
- (loop diagnostics
- (cdr directives)
- (cons dir unsatisfied))))))))
- (define (executable-file source)
- "Return the name of the executable file corresponding to SOURCE."
- (let* ((dot (string-rindex source #\.))
- (exe (if dot
- (substring source 0 dot)
- (string-append source ".exe")))
- )
- (if (string-prefix? %srcdir exe)
- (string-append %builddir (substring exe (string-length %srcdir)))
- exe
- )))
- (define (compile/match file cc cflags ldflags)
- "Read directives from FILE, and compiler/link/run it. Make sure directives
- are matched, and report any errors otherwise. Return #t on success and #f
- otherwise."
- (define directives
- (call-with-input-file file read-test-directives))
- (define exe
- (executable-file file))
- (define (c->o c-file)
- (string-append (substring c-file 0 (- (string-length c-file) 2))
- ".lo"))
- (log "~a directives found in `~a'" (length directives) file)
- (let*-values (((error-expected?)
- (find (lambda (l+d)
- (match l+d
- (((? location?) 'error _)
- #t)
- (_ #f)))
- directives))
- ((instructions)
- (or (any (lambda (l+d)
- (match l+d
- (((? location?) 'instructions x ...)
- x)
- (_ #f)))
- directives)
- '(run)))
- ((options)
- (match instructions
- ((_ options ...)
- options)
- (_ '())))
- ((dependencies)
- (or (assq-ref options 'dependencies)
- '())))
- (or (null? dependencies)
- (format (current-output-port) "~s has ~a dependencies: ~{~s ~}~%"
- file (length dependencies) dependencies))
- (and (every (cut compile/match <> cc cflags ldflags)
- (map (cut string-append %srcdir "/" <>) dependencies))
- (let*-values (((goal)
- (if error-expected?
- 'compile
- (car instructions)))
- ((cflags)
- `(,@cflags
- ,@(or (assq-ref options 'cflags) '())
- ,@(if (memq goal '(link run))
- `("-o" ,exe)
- '("-c"))))
- ((ldflags)
- `(,@(map c->o dependencies)
- ,@ldflags
- ,@(or (assq-ref options 'ldflags)
- '())))
- ((directives)
- (remove (lambda (l+d)
- (match l+d
- (((? location?) 'instructions _ ...)
- #t)
- (_ #f)))
- directives))
- ((status diagnostics unsatisfied)
- (compile/match* file directives cc cflags ldflags))
- ((unmatched)
- ;; Consider unmatched only diagnostics that have a
- ;; kind, to avoid taking into account messages like
- ;; "In file included from", "In function 'main'",
- ;; etc.
- (filter diagnostic-kind diagnostics)))
- (or (null? unmatched)
- (begin
- (format (current-error-port)
- "error: ~a unmatched GCC diagnostics:~%"
- (length unmatched))
- (for-each (lambda (d)
- (format (current-error-port)
- " ~a:~a:~a: ~a: ~a~%"
- (and=> (diagnostic-location d)
- location-file)
- (and=> (diagnostic-location d)
- location-line)
- (and=> (diagnostic-location d)
- location-column)
- (diagnostic-kind d)
- (diagnostic-message d)))
- unmatched)
- #f))
- (if (null? unsatisfied)
- (or (null? directives)
- (log "~a directives satisfied" (length directives)))
- (begin
- (format (current-error-port)
- "error: ~a unsatisfied directives:~%"
- (length unsatisfied))
- (for-each (lambda (l+d)
- (let ((loc (car l+d))
- (dir (cdr l+d)))
- (format (current-error-port)
- " ~a:~a:~a: ~a: ~s~%"
- (location-file loc)
- (location-line loc)
- (location-column loc)
- (car dir)
- (cadr dir))))
- unsatisfied)
- #f))
- (if error-expected?
- (if (= 0 status)
- (format (current-error-port)
- "error: compilation succeeded~%"))
- (if (= 0 status)
- (or (eq? goal 'compile)
- (file-exists? exe)
- (begin
- (format (current-error-port)
- "error: executable file `~a' not found~%" exe)
- #f))
- (format (current-error-port)
- "error: compilation failed (compiler exit code ~a)~%~{ ~a~%~}"
- status
- (map diagnostic-message diagnostics))))
- (and (null? unmatched)
- (null? unsatisfied)
- (if error-expected?
- (not (= 0 status))
- (and (= 0 status)
- (or (eq? goal 'compile) (file-exists? exe))
- (or (not (eq? goal 'run))
- (let ((status (run-starpu-code exe)))
- (or (= 0 status)
- (begin
- (format (current-error-port)
- "error: program `~a' failed \
- (exit code ~a)~%"
- exe status)
- #f)))))))))))
- ;;;
- ;;; Entry point.
- ;;;
- (define (build/run . file)
- (exit (every (lambda (file)
- ;; For each file, check that everything works both with and
- ;; without optimizations.
- (every (cut compile/match file %gcc <> %default-ldflags)
- `((,"-O0" ,@%default-cflags)
- (,"-O2" ,@%default-cflags))))
- file)))
- ;;; run-test.in ends here
|