# Copyright (C) 2001 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 of the License, 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 program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Please email any bugs, comments, and/or additions to this file to: # libstdc++@gcc.gnu.org # # This file is contributed by Gabriel Dos Reis ## This file contains support routines for dg.exp based testsuite ## framework. ## The global associative array lib_env contains the totality ## of options necessary to run testcases; the meanings of which are ## as follows: ## lib_env(CXX) The compiler used to run testcases. ## lib_env(CXXFLAGS) Special flags passed to the compiler. ## lib_env(LIBGLOSSFLAGS) Flags for finding libgloss-using xcompilers. ## lib_env(INCLUDES) Includes options to pass to the compiler. ## lib_env(LIBS) Libraries to link, and dditional library flags. ## lib_env(LIBTOOL) Path to the `libtool' script. ## lib_env(SRC_DIR) Where V3 master source lives. ## lib_env(BUILD_DIR) Where V3 is built. ## lib_env(static) Flags to pass to the linker to build a ## statically linked executable. ## lib_env(shared) Flags to pass to the linker to build a ## dynamically linked executable. ## lib_env(testcase_options) Options specified by current testcase. ## These are specified through the @xxx@-keywords. load_lib dg.exp load_lib libgloss.exp ## Initialization routine. proc libstdc++-dg-init { args } { global lib_env global srcdir global outdir global dg-do-what-default # By default, we assume we want to run program images. set dg-do-what-default run # Get the source and the build directories. set src-dir [lookfor_file $srcdir libstdc++-v3] set build-dir [lookfor_file $outdir libstdc++-v3] # Set proper environment variables for the framework. libstdc++-setup-flags ${src-dir} ${build-dir} # mkcheck.in used to output this information. set output [remote_exec host $lib_env(CXX) -v] # XXX don't try clever formatting hacks at the moment # if { [lindex $output 0] == 0 } { # set output [lindex $output 1] # regexp "gcc version.*$" $output version # regsub "\n+" $version "" version # clone_output "Compiler: $version" # clone_output "Compiler flags: $lib_env(CXXFLAGS)" # } else { # perror "Cannot determine compiler version: [lindex $output 1]" # } } ## dg.exp callback. Called from dg-test to run PROGRAM. ## ## This is the heart of the framework. For the time being, it is ## pretty much baroque, but it will improve as time goes. proc libstdc++-load { prog } { global lib_env set opts $lib_env(testcase_options) set results [remote_load target $prog] if { [lindex $results 0] == "pass" && [info exists opts(diff)] } { # FIXME: We should first test for any mentioned @output@ file here # before taking any other action. set firsts [glob -nocomplain [lindex $opts(diff) 0]] set seconds [glob -nocomplain [lindex $opts(diff) 1]] foreach f $firsts s $seconds { if { [diff $f $s] == 0 } { # FIXME: Well we should report a message. But for the time # being, just pretend there is nothing much to say. # Yes, that is silly, I know. But we need, first, to # to have a working framework. break } } } return $results } ## Nothing particular to do. proc libstdc++-exit { } { } ## Output the version of the libs tested. proc libstdc++-version { } { global lib_env set version "undeterminated" # This file contains the library configuration, built at configure time. set config-file $lib_env(BUILD_DIR)/include/bits/c++config.h set version_pattern "__GLIBCPP__\[ \t\]\+\[0-9\]\+" if [file exists ${config-file}] { set version [grep ${config-file} $version_pattern] regexp "\[0-9\]\+" $version version } clone_output "$lib_env(SRC_DIR) version $version" return 0 } ## Main loop. Loop over TEST-DIRECTORIES and run each testcase ## found therein. proc libstdc++-runtest { testdirs } { global runtests global srcdir global outdir set top-tests-dir [pwd] foreach d $testdirs { set testfiles [glob -nocomplain $d/*.C $d/*.cc] if { [llength $testfiles] == 0 } { continue } # Make the appropriate test-dirs with related .libs/ subdir # to keep libtool happy. set td "$outdir/[dg-trim-dirname $srcdir $d]" maybe-make-directory $td maybe-make-directory $td/.libs cd $td; foreach testfile $testfiles { # If we're not supposed to test this file, just skip it. if ![runtest_file_p $runtests $testfile] { continue } # verbose "Testing [dg-trim-dirname $srcdir $testfile]" libstdc++-do-test $testfile static libstdc++-do-test $testfile shared } cd ${top-tests-dir} } } ## dg.exp callback. Main test-running routine. Called from ## dg-test. ## ## TESTCASE is the file-name of the program to test; ## COMPILE_TYPE is the kind of compilation to apply to TESTCASE; ## current compilation kinds are: preprocess, compile, ## assemble, link, run. proc libstdc++-dg-test { testfile compile_type additional-options } { global srcdir; global outdir global lib_env global which_library # Prepare for compilation output set comp_output "" # By default, we want to use libtool to compile and run tests. set lt $lib_env(LIBTOOL) set lt_args "--tag=CXX" libstdc++-process-options $testfile set output_file [file rootname [file tail $testfile]] set output_file "./$output_file" switch $compile_type { "preprocess" { set lt $lib_env(CXX) set lt_args "-E $lib_env(INCLUDES) $testfile -o $output_file.ii" } "compile" { set lt $lib_env(CXX) set lt_args "-S $lib_env(INCLUDES) $testfile -o $output_file.s" } "assemble" { append lt_args " --mode=compile $lib_env(FLAGS) $testfile" } "run" - "link" { # If we're asked to run a testcase, then just do a `link'. # Later, the framework will load the program image through # libstdc++-load callback. if { $which_library == "static" } { append output_file ".st-exe" } else { append output_file ".sh-exe" } append lt_args " --mode=link $lib_env(FLAGS) \ $lib_env(LIBGLOSSFLAGS) $lib_env($which_library) \ $testfile -o $output_file $lib_env(LIBS)" } default { perror "$compile_type: option not recognized" } } set results [remote_exec host $lt "$lt_args ${additional-options}"] if { [lindex $results 0] != 0 } { set comp_output [lindex $results 1]; } return [list $comp_output $output_file] } ## Get options necessary to properly run testcases. ## SRC-DIR is the library top source directory e.g. something like ## /codesourcery/egcs/libstdc++ ## BUILD-DIR is top build directory e.g. something like ## /tmp/egcs/i686-pc-linux-gnu/libstdc++ proc libstdc++-setup-flags {src-dir build-dir} { global lib_env set tmp [remote_exec host ${build-dir}/tests_flags "--built-library ${build-dir} ${src-dir}"] set status [lindex $tmp 0] set output [lindex $tmp 1] if { $status == 0 } { set flags [split $output :] set lib_env(BUILD_DIR) [lindex $flags 0] set lib_env(SRC_DIR) [lindex $flags 1] set lib_env(CXX) [lindex $flags 3] set lib_env(CXXFLAGS) [lindex $flags 4] set lib_env(INCLUDES) [lindex $flags 5] set lib_env(LIBS) [lindex $flags 6] set lib_env(LIBGLOSSFLAGS) [libgloss_link_flags] # This is really really fragile. We should find a better away to # tell the framework which flags to use for static/shared libraries. set lib_env(static) "-static" set lib_env(shared) "" set lib_env(LIBTOOL) "$lib_env(BUILD_DIR)/libtool" set lib_env(FLAGS) "$lib_env(CXX) \ $lib_env(INCLUDES) $lib_env(CXXFLAGS)" } else { perror "$output" } } proc maybe-make-directory {dir} { if {![file isdirectory $dir]} { file mkdir $dir } } proc libstdc++-do-test { testfile lib } { global which_library; set which_library $lib ## Is it planed to handle -keep-output throught @xxx@-option dg-test -keep-output $testfile "" "" } ## Process @xxx@ options. proc libstdc++-process-options { testfile } { global lib_env array set opts { diff {} output {} require {} } set percent [file rootname [file tail $testfile]] set option-pattern "@.*@.*" set results [grep $testfile ${option-pattern}] if ![string match "" $results] { foreach o $results { regexp "@(.*)@(.*)" $o o key value regsub -all "%" $value "$percent" value # Not yet supported: keep-output, output, link-against switch $key { "diff" - "keep-output" - "link-against" - "output" - "require" { } default { perror "libstdc++: Invalid option-specification `$o'" } } set opts($key) $value unset key value } } set lib_env(testcase_options) [array get opts] # copy any required data files. if ![string match "" $opts(require)] { set src [file dirname $testfile] set dst [pwd] foreach f $opts(require) { foreach t [glob -nocomplain "$src/$f"] { file copy -force $t $dst } } } } ### ### The following is an abominable hack, non-commendable software practice. ### This is supposed to be a very-very short term solution. ### Please, do not add any piece of code without my approval. ### -- Gaby ### # dg-test -- runs a new style DejaGnu test # # Syntax: dg-test [-keep-output] prog tool_flags default_extra_tool_flags # # PROG is the full path name of the file to pass to the tool (eg: compiler). # TOOL_FLAGS is a set of options to always pass. # DEFAULT_EXTRA_TOOL_FLAGS are additional options if the testcase has none. #proc dg-test { prog tool_flags default_extra_tool_flags } { proc dg-test { args } { global dg-do-what-default dg-interpreter-batch-mode dg-linenum-format global errorCode errorInfo global tool global srcdir ;# eg: /calvin/dje/devo/gcc/./testsuite/ global host_triplet target_triplet global which_library set keep 0 set i 0 if { [string index [lindex $args 0] 0] == "-" } { for { set i 0 } { $i < [llength $args] } { incr i } { if { [lindex $args $i] == "--" } { incr i break } elseif { [lindex $args $i] == "-keep-output" } { set keep 1 } elseif { [string index [lindex $args $i] 0] == "-" } { clone_output "ERROR: dg-test: illegal argument: [lindex $args $i]" return } else { break } } } if { $i + 3 != [llength $args] } { clone_output "ERROR: dg-test: missing arguments in call" return } set prog [lindex $args $i] set tool_flags [lindex $args [expr $i + 1]] set default_extra_tool_flags [lindex $args [expr $i + 2]] set text "\[- A-Za-z0-9\.\;\"\_\:\'\`\(\)\!\#\=\+\?\&\*]*" set name [dg-trim-dirname $srcdir $prog] # If we couldn't rip $srcdir out of `prog' then just do the best we can. # The point is to reduce the unnecessary noise in the logs. Don't strip # out too much because different testcases with the same name can confuse # `test-tool'. if [string match "/*" $name] { set name "[file tail [file dirname $prog]]/[file tail $prog]" } # Process any embedded dg options in the testcase. # Use "" for the second element of dg-do-what so we can tell if it's been # explicitly set to "S". set dg-do-what [list ${dg-do-what-default} "" P] set dg-excess-errors-flag 0 set dg-messages "" set dg-extra-tool-flags $default_extra_tool_flags set dg-final-code "" # `dg-output-text' is a list of two elements: pass/fail and text. # Leave second element off for now (indicates "don't perform test") set dg-output-text "P" # Define our own "special function" `unknown' so we catch spelling errors. # But first rename the existing one so we can restore it afterwards. catch {rename dg-save-unknown ""} rename unknown dg-save-unknown proc unknown { args } { return -code error "unknown dg option: $args" } set tmp [dg-get-options $prog] foreach op $tmp { verbose "Processing option: $op" 3 set status [catch "$op" errmsg] if { $status != 0 } { if { 0 && [info exists errorInfo] } { # This also prints a backtrace which will just confuse # testcase writers, so it's disabled. perror "$name: $errorInfo\n" } else { perror "$name: $errmsg for \"$op\"\n" } # ??? The call to unresolved here is necessary to clear `errcnt'. # What is needed is a proc like perror that doesn't set errcnt. # It should also set exit_status to 1. unresolved "$name: $errmsg for \"$op\"" return } } # Restore normal error handling. rename unknown "" rename dg-save-unknown unknown # Record the final set of flags, to tag log entries with. set label "$which_library $tool_flags ${dg-extra-tool-flags}" # If we're not supposed to try this test on this target, we're done. if { [lindex ${dg-do-what} 1] == "N" } { unsupported "$name" verbose "$name not supported on this target, skipping it" 3 return } # Run the tool and analyze the results. # The result of ${tool}-dg-test is in a bit of flux. # Currently it is the name of the output file (or "" if none). # If we need more than this it will grow into a list of things. # No intention is made (at this point) to preserve upward compatibility # (though at some point we'll have to). set results [${tool}-dg-test $prog [lindex ${dg-do-what} 0] "$tool_flags ${dg-extra-tool-flags}"]; set comp_output [lindex $results 0]; set output_file [lindex $results 1]; #send_user "\nold_dejagnu.exp: comp_output1 = :$comp_output:\n\n" #send_user "\nold_dejagnu.exp: message = :$message:\n\n" #send_user "\nold_dejagnu.exp: message length = [llength $message]\n\n" foreach i ${dg-messages} { verbose "Scanning for message: $i" 4 # Remove all error messages for the line [lindex $i 0] # in the source file. If we find any, success! set line [lindex $i 0] set pattern [lindex $i 2] set comment [lindex $i 3] #send_user "Before:\n$comp_output\n" if [regsub -all "(^|\n)(\[^\n\]+$line\[^\n\]*($pattern)\[^\n\]*\n?)+" $comp_output "\n" comp_output] { set comp_output [string trimleft $comp_output] set ok pass set uhoh fail } else { set ok fail set uhoh pass } #send_user "After:\n$comp_output\n" # $line will either be a formatted line number or a number all by # itself. Delete the formatting. scan $line ${dg-linenum-format} line switch [lindex $i 1] { "ERROR" { $ok "$name $comment (test for errors, line $line), $label" } "XERROR" { x$ok "$name $comment (test for errors, line $line), $label" } "WARNING" { $ok "$name $comment (test for warnings, line $line), $label" } "XWARNING" { x$ok "$name $comment (test for warnings, line $line), $label" } "BOGUS" { $uhoh "$name $comment (test for bogus messages, line $line), $label" } "XBOGUS" { x$uhoh "$name $comment (test for bogus messages, line $line), $label" } "BUILD" { $uhoh "$name $comment (test for build failure, line $line), $label" } "XBUILD" { x$uhoh "$name $comment (test for build failure, line $line), $label" } "EXEC" { } "XEXEC" { } } #send_user "\nold_dejagnu.exp: comp_output2= :$comp_output:\n\n" } #send_user "\nold_dejagnu.exp: comp_output3 = :$comp_output:\n\n" # Remove messages from the tool that we can ignore. #send_user "comp_output: $comp_output\n" set comp_output [prune_warnings $comp_output] if { [info proc ${tool}-dg-prune] != "" } { set comp_output [${tool}-dg-prune $target_triplet $comp_output] switch -glob $comp_output { "::untested::*" { regsub "::untested::" $comp_output "" message untested "$name: $message" return } "::unresolved::*" { regsub "::unresolved::" $comp_output "" message unresolved "$name: $message" return } "::unsupported::*" { regsub "::unsupported::" $comp_output "" message unsupported "$name: $message" return } } } # See if someone forgot to delete the extra lines. regsub -all "\n+" $comp_output "\n" comp_output regsub "^\n+" $comp_output "" comp_output #send_user "comp_output: $comp_output\n" # Don't do this if we're testing an interpreter. # FIXME: why? if { ${dg-interpreter-batch-mode} == 0 } { # Catch excess errors (new bugs or incomplete testcases). if ${dg-excess-errors-flag} { setup_xfail "*-*-*" } if ![string match "" $comp_output] { fail "$name (test for excess errors), $label" send_log "Excess errors:\n$comp_output\n" } else { pass "$name (test for excess errors), $label" } } # Run the executable image if asked to do so. # FIXME: This is the only place where we assume a standard meaning to # the `keyword' argument of dg-do. This could be cleaned up. if { [lindex ${dg-do-what} 0] == "run" } { if ![file exists $output_file] { warning "$name compilation failed to produce executable" } else { set status -1 set result [libstdc++-load $output_file] set status [lindex $result 0]; set output [lindex $result 1]; #send_user "After exec, status: $status\n" if { [lindex ${dg-do-what} 2] == "F" } { setup_xfail "*-*-*" } if { "$status" == "pass" } { pass "$name (execution test), $label" verbose "Exec succeeded." 3 if { [llength ${dg-output-text}] > 1 } { #send_user "${dg-output-text}\n" if { [lindex ${dg-output-text} 0] == "F" } { setup_xfail "*-*-*" } set texttmp [lindex ${dg-output-text} 1] if { ![regexp $texttmp ${output}] } { fail "$name output pattern test, is ${output}, should match $texttmp" verbose "Failed test for output pattern $texttmp" 3 } else { pass "$name output pattern test, $texttmp" verbose "Passed test for output pattern $texttmp" 3 } unset texttmp } } elseif { "$status" == "fail" } { # It would be nice to get some info out of errorCode. if [info exists errorCode] { verbose "Exec failed, errorCode: $errorCode" 3 } else { verbose "Exec failed, errorCode not defined!" 3 } fail "$name (execution test), $label" } else { $status "$name (execution test), $label" } } } # Are there any further tests to perform? # Note that if the program has special run-time requirements, running # of the program can be delayed until here. Ditto for other situations. # It would be a bit cumbersome though. if ![string match ${dg-final-code} ""] { regsub -all "\\\\(\[{}\])" ${dg-final-code} "\\1" dg-final-code # Note that the use of `args' here makes this a varargs proc. proc dg-final-proc { args } ${dg-final-code} verbose "Running dg-final tests." 3 verbose "dg-final-proc:\n[info body dg-final-proc]" 4 if [catch "dg-final-proc $prog" errmsg] { perror "$name: error executing dg-final: $errmsg" # ??? The call to unresolved here is necessary to clear `errcnt'. # What is needed is a proc like perror that doesn't set errcnt. # It should also set exit_status to 1. unresolved "$name: error executing dg-final: $errmsg" } } # Do some final clean up. # When testing an interpreter, we don't compile something and leave an # output file. if { ! ${keep} && ${dg-interpreter-batch-mode} == 0 } { catch "exec rm -f $output_file" } }