run-test.in 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. #!/bin/sh
  2. # -*- mode: scheme; coding: utf-8; -*-
  3. GUILE_AUTO_COMPILE=0
  4. export GUILE_AUTO_COMPILE
  5. main='(@ (run-test) build/run)'
  6. exec "${GUILE-@GUILE@}" -l "$0" \
  7. -c "(apply $main (cdr (command-line)))" "$@"
  8. !#
  9. ;;; GCC-StarPU
  10. ;;; Copyright (C) 2011, 2012 Institut National de Recherche en Informatique et Automatique
  11. ;;;
  12. ;;; GCC-StarPU is free software: you can redistribute it and/or modify
  13. ;;; it under the terms of the GNU General Public License as published by
  14. ;;; the Free Software Foundation, either version 3 of the License, or
  15. ;;; (at your option) any later version.
  16. ;;;
  17. ;;; GCC-StarPU is distributed in the hope that it will be useful,
  18. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. ;;; GNU General Public License for more details.
  21. ;;;
  22. ;;; You should have received a copy of the GNU General Public License
  23. ;;; along with GCC-StarPU. If not, see <http://www.gnu.org/licenses/>.
  24. ;;;
  25. ;;; Written by Ludovic Courtès <ludovic.courtes@inria.fr>.
  26. ;;;
  27. (define-module (run-test)
  28. #:use-module (ice-9 regex)
  29. #:use-module (ice-9 popen)
  30. #:use-module (ice-9 rdelim)
  31. #:use-module (ice-9 format)
  32. #:use-module (ice-9 match)
  33. #:use-module (srfi srfi-1)
  34. #:use-module (srfi srfi-9)
  35. #:use-module (srfi srfi-11)
  36. #:use-module (srfi srfi-13)
  37. #:use-module (srfi srfi-14)
  38. #:use-module (srfi srfi-26)
  39. #:export (build/run))
  40. ;;; Commentary:
  41. ;;;
  42. ;;; Test machinery similar to the DejaGNU-based test framework used in GCC.
  43. ;;; In a nutshell, this program compiles code with GCC and makes sure
  44. ;;; warnings and errors are as appear in source code comments.
  45. ;;;
  46. ;;; This module should work with both Guile 1.8 and Guile 2.0.
  47. ;;;
  48. ;;; Code:
  49. ;; Make sure the reader gets position information.
  50. (read-enable 'positions)
  51. (define %log-port
  52. ;; Output port for debugging messages.
  53. (current-output-port))
  54. (define (log fmt . args)
  55. "Write an informational message."
  56. (apply format %log-port (string-append fmt "\n") args))
  57. ;;;
  58. ;;; Compiling code.
  59. ;;;
  60. (define %srcdir "@srcdir@")
  61. (define %builddir "@builddir@")
  62. (define %gcc "@CC@")
  63. (define %cuda-cppflags
  64. (string-tokenize "@STARPU_CUDA_CPPFLAGS@"))
  65. (define %opencl-cppflags
  66. (string-tokenize "@STARPU_OPENCL_CPPFLAGS@"))
  67. (define %default-cflags
  68. `("-I" ,%srcdir
  69. "-I" ,(string-append %srcdir "/../../src") ; for <common/uthash.h>
  70. "-I" ,(string-append %srcdir "/../../include")
  71. "-I" ,(string-append %builddir "/../../include")
  72. "-I" ,(string-append %builddir "/../..")
  73. ,@%cuda-cppflags
  74. ,@%opencl-cppflags
  75. ;; Unfortunately `libtool --mode=execute' doesn't help here, so hard-code
  76. ;; the real file name.
  77. ,(string-append "-fplugin=" %builddir "/../src/.libs/starpu.so")
  78. ;; Use the non-installed headers.
  79. "-fplugin-arg-starpu-include-dir=@top_srcdir@/include"
  80. ;; Find OpenCL source files under $srcdir.
  81. ,(string-append "-fplugin-arg-starpu-opencl-include-dir=" %srcdir)
  82. "-g"
  83. "-fdump-tree-gimple" "-Wall"))
  84. (define %default-ldflags
  85. `(,(string-append "-L" %builddir "/../../src")))
  86. (define %libtool
  87. (string-append %builddir "/../../libtool"))
  88. (define (compile-starpu-code file cc cflags ldflags)
  89. "Compile and link FILE with CC, using CFLAGS and LDFLAGS. Return the
  90. compiler status and the list of lines printed on stdout/stderr."
  91. (let* ((compile? (member "-c" cflags))
  92. (ldflags (if compile?
  93. (remove (cut string-prefix? "-L" <>) ldflags)
  94. ldflags))
  95. (mode (if compile?
  96. "compile"
  97. "link"))
  98. (command (format #f "LC_ALL=C ~a --mode=~a ~a ~{~a ~} \"~a\" ~{~a ~} 2>&1"
  99. %libtool mode cc cflags file ldflags))
  100. (pipe (begin
  101. (log "running `~a'" command)
  102. (open-input-pipe command))))
  103. (let loop ((line (read-line pipe))
  104. (result '()))
  105. (if (eof-object? line)
  106. (values (close-pipe pipe) (reverse result))
  107. (loop (read-line pipe)
  108. (cons line result))))))
  109. (define (run-starpu-code executable)
  110. "Run EXECUTABLE using Libtool; return its exit status."
  111. (let* ((exe (if (string-index executable #\/)
  112. executable
  113. (string-append (getcwd) "/" executable)))
  114. (command (string-append %libtool " --mode=execute "
  115. exe)))
  116. (log "running `~a'" command)
  117. (system command)))
  118. ;;;
  119. ;;; GCC diagnostics.
  120. ;;;
  121. (define-record-type <location>
  122. (make-location file line column)
  123. location?
  124. (file location-file)
  125. (line location-line)
  126. (column location-column))
  127. (define (location=? loc1 loc2)
  128. "Return #t if LOC1 and LOC2 refer roughly to the same file and line
  129. number."
  130. (and (location-file loc1) (location-file loc2)
  131. (string=? (basename (location-file loc1))
  132. (basename (location-file loc2)))
  133. (= (location-line loc1) (location-line loc2))))
  134. (define-record-type <diagnostic>
  135. (make-diagnostic location kind message)
  136. diagnostic?
  137. (location diagnostic-location)
  138. (kind diagnostic-kind)
  139. (message diagnostic-message))
  140. (define %diagnostic-with-location-rx
  141. ;; "FILE:LINE:COL: KIND: MESSAGE..."
  142. (make-regexp "^(.+):([[:digit:]]+):([[:digit:]]+): ([^:]+): (.*)$"))
  143. (define (string->diagnostic str)
  144. "Parse STR and return the corresponding `diagnostic' object."
  145. (cond ((regexp-exec %diagnostic-with-location-rx str)
  146. =>
  147. (lambda (m)
  148. (let ((loc (make-location (match:substring m 1)
  149. (string->number (match:substring m 2))
  150. (string->number (match:substring m 3))))
  151. (kind (string->symbol (match:substring m 4))))
  152. (make-diagnostic loc kind (match:substring m 5)))))
  153. (else
  154. (make-diagnostic #f #f str))))
  155. ;;;
  156. ;;; Reading test directives.
  157. ;;;
  158. (define (read-test-directives port)
  159. "Read test directives from PORT. Return a list of location/directive
  160. pairs."
  161. (define (consume-whitespace p)
  162. (let loop ((chr (peek-char p)))
  163. (cond ((char-set-contains? char-set:whitespace chr)
  164. (read-char p) ;; consume CHR
  165. (loop (peek-char p)))
  166. (else chr))))
  167. (define (read-until-*/ p)
  168. (let loop ((chr (read-char p)))
  169. (cond ((eof-object? chr)
  170. (error "unterminated C comment"))
  171. ((eq? chr #\*)
  172. (let ((next (peek-char p)))
  173. (if (eq? next #\/)
  174. (read-char p) ;; consume CHR
  175. (loop (read-char p)))))
  176. (else
  177. (loop (read-char p))))))
  178. (let loop ((chr (read-char port))
  179. (directives '()))
  180. (cond ((eof-object? chr)
  181. (reverse directives))
  182. ((eq? chr #\/)
  183. (let ((chr (read-char port)))
  184. (if (eq? chr #\*)
  185. (let ((chr (consume-whitespace port)))
  186. (if (eq? chr #\()
  187. (let ((loc (make-location (port-filename port)
  188. (1+ (port-line port))
  189. (port-column port)))
  190. (sexp (read port)))
  191. (read-until-*/ port)
  192. (loop (peek-char port)
  193. (cons (cons loc sexp)
  194. directives)))
  195. (begin
  196. (read-until-*/ port)
  197. (loop (peek-char port) directives))))
  198. (loop chr directives))))
  199. (else
  200. (loop (read-char port) directives)))))
  201. (define (diagnostic-matches-directive? diagnostic directive location
  202. cflags ldflags)
  203. "Return #t if DIAGNOSTIC matches DIRECTIVE, which is at LOCATION."
  204. (define optimizing?
  205. (let ((opt (find (cut string-prefix? "-O" <>) cflags)))
  206. (match opt
  207. ((or #f "-O0") #f)
  208. (_ #t))))
  209. (let loop ((directive directive))
  210. (match directive
  211. (('if 'optimizing? directive)
  212. (or (not optimizing?)
  213. (loop directive)))
  214. (('unless 'optimizing? directive)
  215. (or optimizing?
  216. (loop directive)))
  217. ((kind message)
  218. (and (eq? kind (diagnostic-kind diagnostic))
  219. (location? (diagnostic-location diagnostic))
  220. (location=? (diagnostic-location diagnostic) location)
  221. (string-match message (diagnostic-message diagnostic)))))))
  222. ;;;
  223. ;;; Compiling and matching diagnostics against directives.
  224. ;;;
  225. (define (compile/match* file directives cc cflags ldflags)
  226. "Compile FILE and check whether GCC's diagnostics match DIRECTIVES. Return
  227. 3 values: the compiler's status code, the unmatched diagnostics, and the
  228. unsatisfied directives."
  229. (let-values (((status diagnostics)
  230. (compile-starpu-code file cc cflags ldflags)))
  231. (let loop ((diagnostics (map string->diagnostic diagnostics))
  232. (directives directives)
  233. (unsatisfied '()))
  234. (if (null? directives)
  235. (values status diagnostics unsatisfied)
  236. (let* ((dir (car directives))
  237. (diag (find (cute diagnostic-matches-directive?
  238. <> (cdr dir) (car dir)
  239. cflags ldflags)
  240. diagnostics)))
  241. (if diag
  242. (loop (delq diag diagnostics)
  243. (cdr directives)
  244. unsatisfied)
  245. (loop diagnostics
  246. (cdr directives)
  247. (cons dir unsatisfied))))))))
  248. (define (executable-file source)
  249. "Return the name of the executable file corresponding to SOURCE."
  250. (let* ((dot (string-rindex source #\.))
  251. (exe (if dot
  252. (substring source 0 dot)
  253. (string-append source ".exe")))
  254. )
  255. (if (string-prefix? %srcdir exe)
  256. (string-append %builddir (substring exe (string-length %srcdir)))
  257. exe
  258. )))
  259. (define (compile/match file cc cflags ldflags)
  260. "Read directives from FILE, and compiler/link/run it. Make sure directives
  261. are matched, and report any errors otherwise. Return #t on success and #f
  262. otherwise."
  263. (define directives
  264. (call-with-input-file file read-test-directives))
  265. (define exe
  266. (executable-file file))
  267. (define (c->o c-file)
  268. (string-append (substring c-file 0 (- (string-length c-file) 2))
  269. ".lo"))
  270. (log "~a directives found in `~a'" (length directives) file)
  271. (let*-values (((error-expected?)
  272. (find (lambda (l+d)
  273. (match l+d
  274. (((? location?) 'error _)
  275. #t)
  276. (_ #f)))
  277. directives))
  278. ((instructions)
  279. (or (any (lambda (l+d)
  280. (match l+d
  281. (((? location?) 'instructions x ...)
  282. x)
  283. (_ #f)))
  284. directives)
  285. '(run)))
  286. ((options)
  287. (match instructions
  288. ((_ options ...)
  289. options)
  290. (_ '())))
  291. ((dependencies)
  292. (or (assq-ref options 'dependencies)
  293. '())))
  294. (or (null? dependencies)
  295. (format (current-output-port) "~s has ~a dependencies: ~{~s ~}~%"
  296. file (length dependencies) dependencies))
  297. (and (every (cut compile/match <> cc cflags ldflags)
  298. (map (cut string-append %srcdir "/" <>) dependencies))
  299. (let*-values (((goal)
  300. (if error-expected?
  301. 'compile
  302. (car instructions)))
  303. ((cflags)
  304. `(,@cflags
  305. ,@(or (assq-ref options 'cflags) '())
  306. ,@(if (memq goal '(link run))
  307. `("-o" ,exe)
  308. '("-c"))))
  309. ((ldflags)
  310. `(,@(map c->o dependencies)
  311. ,@ldflags
  312. ,@(or (assq-ref options 'ldflags)
  313. '())))
  314. ((directives)
  315. (remove (lambda (l+d)
  316. (match l+d
  317. (((? location?) 'instructions _ ...)
  318. #t)
  319. (_ #f)))
  320. directives))
  321. ((status diagnostics unsatisfied)
  322. (compile/match* file directives cc cflags ldflags))
  323. ((unmatched)
  324. ;; Consider unmatched only diagnostics that have a
  325. ;; kind, to avoid taking into account messages like
  326. ;; "In file included from", "In function 'main'",
  327. ;; etc.
  328. (filter diagnostic-kind diagnostics)))
  329. (or (null? unmatched)
  330. (begin
  331. (format (current-error-port)
  332. "error: ~a unmatched GCC diagnostics:~%"
  333. (length unmatched))
  334. (for-each (lambda (d)
  335. (format (current-error-port)
  336. " ~a:~a:~a: ~a: ~a~%"
  337. (and=> (diagnostic-location d)
  338. location-file)
  339. (and=> (diagnostic-location d)
  340. location-line)
  341. (and=> (diagnostic-location d)
  342. location-column)
  343. (diagnostic-kind d)
  344. (diagnostic-message d)))
  345. unmatched)
  346. #f))
  347. (if (null? unsatisfied)
  348. (or (null? directives)
  349. (log "~a directives satisfied" (length directives)))
  350. (begin
  351. (format (current-error-port)
  352. "error: ~a unsatisfied directives:~%"
  353. (length unsatisfied))
  354. (for-each (lambda (l+d)
  355. (let ((loc (car l+d))
  356. (dir (cdr l+d)))
  357. (format (current-error-port)
  358. " ~a:~a:~a: ~a: ~s~%"
  359. (location-file loc)
  360. (location-line loc)
  361. (location-column loc)
  362. (car dir)
  363. (cadr dir))))
  364. unsatisfied)
  365. #f))
  366. (if error-expected?
  367. (if (= 0 status)
  368. (format (current-error-port)
  369. "error: compilation succeeded~%"))
  370. (if (= 0 status)
  371. (or (eq? goal 'compile)
  372. (file-exists? exe)
  373. (begin
  374. (format (current-error-port)
  375. "error: executable file `~a' not found~%" exe)
  376. #f))
  377. (format (current-error-port)
  378. "error: compilation failed (compiler exit code ~a)~%~{ ~a~%~}"
  379. status
  380. (map diagnostic-message diagnostics))))
  381. (and (null? unmatched)
  382. (null? unsatisfied)
  383. (if error-expected?
  384. (not (= 0 status))
  385. (and (= 0 status)
  386. (or (eq? goal 'compile) (file-exists? exe))
  387. (or (not (eq? goal 'run))
  388. (let ((status (run-starpu-code exe)))
  389. (or (= 0 status)
  390. (begin
  391. (format (current-error-port)
  392. "error: program `~a' failed \
  393. (exit code ~a)~%"
  394. exe status)
  395. #f)))))))))))
  396. ;;;
  397. ;;; Entry point.
  398. ;;;
  399. (define (build/run . file)
  400. (exit (every (lambda (file)
  401. ;; For each file, check that everything works both with and
  402. ;; without optimizations.
  403. (every (cut compile/match file %gcc <> %default-ldflags)
  404. `((,"-O0" ,@%default-cflags)
  405. (,"-O2" ,@%default-cflags))))
  406. file)))
  407. ;;; run-test.in ends here