diff --git a/ld/testsuite/ChangeLog b/ld/testsuite/ChangeLog index cf52631385..4cef5fad03 100644 --- a/ld/testsuite/ChangeLog +++ b/ld/testsuite/ChangeLog @@ -1,3 +1,15 @@ +2001-06-13 Hans-Peter Nilsson + + * config/default.exp (AS, GASP, OBJDUMP, NM, NMFLAGS, OBJCOPY, + OBJCOPYFLAGS, READELF, READELFFLAGS, LD, LDFLAGS): Provide + default. + + * lib/ld-lib.exp (run_dump_test): Import from gas testsuite. Add + new options "ld", "source", "xfail", "target", "notarget" and + "error". Support the runtest_file_p "*.exp=testname" feature. + (slurp_options, regexp_diff, file_contents, verbose_eval): Import + from gas testsuite. + 2001-06-12 Martin Schwidefsky * testsuite/ld-undefined/undefined.exp: Correct setup_xfail rule. diff --git a/ld/testsuite/config/default.exp b/ld/testsuite/config/default.exp index 2cc82a3731..9363c39dbc 100644 --- a/ld/testsuite/config/default.exp +++ b/ld/testsuite/config/default.exp @@ -170,3 +170,55 @@ proc ld_exec { target output } { default_ld_exec $target $output } +# From gas-defs.exp, to support run_dump_test. +if ![info exists AS] then { + set AS $as +} + +if ![info exists GASP] then { + set GASP [findfile $base_dir/../gas/gasp-new $base_dir/../gas/gasp-new [transform gasp]] +} + +if ![info exists ASFLAGS] then { + set ASFLAGS "" +} + +if ![info exists OBJDUMP] then { + set OBJDUMP $objdump +} + +if ![info exists OBJDUMPFLAGS] then { + set OBJDUMPFLAGS {} +} + +if ![info exists NM] then { + set NM $nm +} + +if ![info exists NMFLAGS] then { + set NMFLAGS {} +} + +if ![info exists OBJCOPY] then { + set OBJCOPY $objcopy +} + +if ![info exists OBJCOPYFLAGS] then { + set OBJCOPYFLAGS {} +} + +if ![info exists READELF] then { + set READELF [findfile $base_dir/../binutils/readelf] +} + +if ![info exists READELFFLAGS] then { + set READELFFLAGS {} +} + +if ![info exists LD] then { + set LD [findfile $base_dir/ld-new ./ld-new [transform ld]] +} + +if ![info exists LDFLAGS] then { + set LDFLAGS {} +} diff --git a/ld/testsuite/lib/ld-lib.exp b/ld/testsuite/lib/ld-lib.exp index 4935215f2a..bd8d211a34 100644 --- a/ld/testsuite/lib/ld-lib.exp +++ b/ld/testsuite/lib/ld-lib.exp @@ -393,6 +393,497 @@ proc simple_diff { file_1 file_2 } { } } +# run_dump_test FILE +# Copied from gas testsuite, tweaked and further extended. +# +# Assemble a .s file, then run some utility on it and check the output. +# +# There should be an assembly language file named FILE.s in the test +# suite directory, and a pattern file called FILE.d. `run_dump_test' +# will assemble FILE.s, run some tool like `objdump', `objcopy', or +# `nm' on the .o file to produce textual output, and then analyze that +# with regexps. The FILE.d file specifies what program to run, and +# what to expect in its output. +# +# The FILE.d file begins with zero or more option lines, which specify +# flags to pass to the assembler, the program to run to dump the +# assembler's output, and the options it wants. The option lines have +# the syntax: +# +# # OPTION: VALUE +# +# OPTION is the name of some option, like "name" or "objdump", and +# VALUE is OPTION's value. The valid options are described below. +# Whitespace is ignored everywhere, except within VALUE. The option +# list ends with the first line that doesn't match the above syntax +# (hmm, not great for error detection). +# +# The interesting options are: +# +# name: TEST-NAME +# The name of this test, passed to DejaGNU's `pass' and `fail' +# commands. If omitted, this defaults to FILE, the root of the +# .s and .d files' names. +# +# as: FLAGS +# When assembling, pass FLAGS to the assembler. +# If assembling several files, you can pass different assembler +# options in the "source" directives. See below. +# +# ld: FLAGS +# Link assembled files using FLAGS, in the order of the "source" +# directives, when using multiple files. +# +# PROG: PROGRAM-NAME +# The name of the program to run to analyze the .o file produced +# by the assembler or the linker output. This can be omitted; +# run_dump_test will guess which program to run by seeing which of +# the flags options below is present. +# +# objdump: FLAGS +# nm: FLAGS +# objcopy: FLAGS +# Use the specified program to analyze the assembler or linker +# output file, and pass it FLAGS, in addition to the output name. +# +# source: SOURCE [FLAGS] +# Assemble the file SOURCE.s using the flags in the "as" directive +# and the (optional) FLAGS. If omitted, the source defaults to +# FILE.s. +# This is useful if several .d files want to share a .s file. +# More than one "source" directive can be given, which is useful +# when testing linking. +# +# xfail: TARGET +# The test is expected to fail on TARGET. This may occur more than +# once. +# +# target: TARGET +# Only run the test for TARGET. This may occur more than once; the +# target being tested must match at least one. +# +# notarget: TARGET +# Do not run the test for TARGET. This may occur more than once; +# the target being tested must not match any of them. +# +# error: REGEX +# An error with message matching REGEX must be emitted for the test +# to pass. The PROG, objdump, nm and objcopy options have no +# meaning and need not supplied if this is present. +# +# Each option may occur at most once unless otherwise mentioned. +# +# After the option lines come regexp lines. `run_dump_test' calls +# `regexp_diff' to compare the output of the dumping tool against the +# regexps in FILE.d. `regexp_diff' is defined later in this file; see +# further comments there. + +proc run_dump_test { name } { + global subdir srcdir + global OBJDUMP NM AS OBJCOPY READELF LD + global OBJDUMPFLAGS NMFLAGS ASFLAGS OBJCOPYFLAGS READELFFLAGS LDFLAGS + global host_triplet runtests + + if [string match "*/*" $name] { + set file $name + set name [file tail $name] + } else { + set file "$srcdir/$subdir/$name" + } + + if ![runtest_file_p $runtests $name] then { + return + } + + set opt_array [slurp_options "${file}.d"] + if { $opt_array == -1 } { + perror "error reading options from $file.d" + unresolved $subdir/$name + return + } + set dumpfile tmpdir/dump.out + set run_ld 0 + set opts(as) {} + set opts(ld) {} + set opts(xfail) {} + set opts(target) {} + set opts(notarget) {} + set opts(objdump) {} + set opts(nm) {} + set opts(objcopy) {} + set opts(readelf) {} + set opts(name) {} + set opts(PROG) {} + set opts(source) {} + set opts(error) {} + set asflags{${file}.s} {} + + foreach i $opt_array { + set opt_name [lindex $i 0] + set opt_val [lindex $i 1] + if ![info exists opts($opt_name)] { + perror "unknown option $opt_name in file $file.d" + unresolved $subdir/$name + return + } + + switch -- $opt_name { + xfail {} + target {} + notarget {} + source { + # Move any source-specific as-flags to a separate array to + # simplify processing. + if { [llength $opt_val] > 1 } { + set asflags([lindex $opt_val 0]) [lrange $opt_val 1 end] + set opt_val [lindex $opt_val 0] + } else { + set asflags($opt_val) {} + } + } + default { + if [string length $opts($opt_name)] { + perror "option $opt_name multiply set in $file.d" + unresolved $subdir/$name + return + } + + # A single "# ld:" with no options should do the right thing. + if { $opt_name == "ld" } { + set run_ld 1 + } + } + } + set opts($opt_name) [concat $opts($opt_name) $opt_val] + } + + # Decide early whether we should run the test for this target. + if { [llength $opts(target)] > 0 } { + set targmatch 0 + foreach targ $opts(target) { + if [istarget $targ] { + set targmatch 1 + break + } + } + if { $targmatch == 0 } { + return + } + } + foreach targ $opts(notarget) { + if [istarget $targ] { + return + } + } + + if {$opts(PROG) != ""} { + switch -- $opts(PROG) { + objdump + { set program objdump } + nm + { set program nm } + objcopy + { set program objcopy } + readelf + { set program readelf } + default + { perror "unrecognized program option $opts(PROG) in $file.d" + unresolved $subdir/$name + return } + } + } elseif { $opts(error) != "" } { + # It's meaningless to require an output-testing method when we + # expect an error. For simplicity, we fake an arbitrary method. + set program "nm" + } else { + # Guess which program to run, by seeing which option was specified. + set program "" + foreach p {objdump objcopy nm readelf} { + if {$opts($p) != ""} { + if {$program != ""} { + perror "ambiguous dump program in $file.d" + unresolved $subdir/$name + return + } else { + set program $p + } + } + } + if {$program == ""} { + perror "dump program unspecified in $file.d" + unresolved $subdir/$name + return + } + } + + set progopts1 $opts($program) + eval set progopts \$[string toupper $program]FLAGS + eval set binary \$[string toupper $program] + if { $opts(name) == "" } { + set testname "$subdir/$name" + } else { + set testname $opts(name) + } + + if { $opts(source) == "" } { + set sourcefiles [list ${file}.s] + } else { + set sourcefiles {} + foreach sf $opts(source) { + lappend sourcefiles "$srcdir/$subdir/$sf" + # Must have asflags indexed on source name. + set asflags($srcdir/$subdir/$sf) $asflags($sf) + } + } + + # Time to setup xfailures. + foreach targ $opts(xfail) { + setup_xfail $targ + } + + # Assemble each file. + set objfiles {} + for { set i 0 } { $i < [llength $sourcefiles] } { incr i } { + set sourcefile [lindex $sourcefiles $i] + + set objfile "tmpdir/dump$i.o" + lappend objfiles $objfile + set cmd "$AS $ASFLAGS $opts(as) $asflags($sourcefile) -o $objfile $sourcefile" + + send_log "$cmd\n" + set cmdret [catch "exec $cmd" comp_output] + set comp_output [prune_warnings $comp_output] + + # We accept errors at assembly stage too, unless we're supposed to + # link something. + if { $cmdret != 0 || ![string match "" $comp_output] } then { + send_log "$comp_output\n" + verbose "$comp_output" 3 + if { $opts(error) != "" && $run_ld == 0 } { + if [regexp $opts(error) $comp_output] { + pass $testname + return + } + } + fail $testname + return + } + } + + # Perhaps link the file(s). + if { $run_ld } { + set objfile "tmpdir/dump" + set cmd "$LD $LDFLAGS $opts(ld) -o $objfile $objfiles" + + send_log "$cmd\n" + set cmdret [catch "exec $cmd" comp_output] + set comp_output [prune_warnings $comp_output] + + if { $cmdret != 0 || ![string match "" $comp_output] } then { + verbose -log "failed with: <$comp_output>, expected: <$opts(error)>" + send_log "$comp_output\n" + verbose "$comp_output" 3 + if { $opts(error) != "" } { + if [regexp $opts(error) $comp_output] { + pass $testname + return + } + } + fail $testname + return + } + } else { + set objfile "tmpdir/dump0.o" + } + + # We must not have expected failure if we get here. + if { $opts(error) != "" } { + fail $testname + } + + if { [which $binary] == 0 } { + untested $testname + return + } + + if { $progopts1 == "" } { set $progopts1 "-r" } + verbose "running $binary $progopts $progopts1" 3 + + # Objcopy, unlike the other two, won't send its output to stdout, + # so we have to run it specially. + if { $program == "objcopy" } { + set cmd "$binary $progopts $progopts1 $objfile $dumpfile" + send_log "$cmd\n" + catch "exec $cmd" comp_output + set comp_output [prune_warnings $comp_output] + if ![string match "" $comp_output] then { + send_log "$comp_output\n" + fail $testname + return + } + } else { + set cmd "$binary $progopts $progopts1 $objfile > $dumpfile" + send_log "$cmd\n" + catch "exec $cmd" comp_output + set comp_output [prune_warnings $comp_output] + if ![string match "" $comp_output] then { + send_log "$comp_output\n" + fail $testname + return + } + } + + verbose_eval {[file_contents $dumpfile]} 3 + if { [regexp_diff $dumpfile "${file}.d"] } then { + fail $testname + verbose "output is [file_contents $dumpfile]" 2 + return + } + + pass $testname +} + +proc slurp_options { file } { + if [catch { set f [open $file r] } x] { + #perror "couldn't open `$file': $x" + perror "$x" + return -1 + } + set opt_array {} + # whitespace expression + set ws {[ ]*} + set nws {[^ ]*} + # whitespace is ignored anywhere except within the options list; + # option names are alphabetic only + set pat "^#${ws}(\[a-zA-Z\]*)$ws:${ws}(.*)$ws\$" + while { [gets $f line] != -1 } { + set line [string trim $line] + # Whitespace here is space-tab. + if [regexp $pat $line xxx opt_name opt_val] { + # match! + lappend opt_array [list $opt_name $opt_val] + } else { + break + } + } + close $f + return $opt_array +} + +# regexp_diff, copied from gas, based on simple_diff above. +# compares two files line-by-line +# file1 contains strings, file2 contains regexps and #-comments +# blank lines are ignored in either file +# returns non-zero if differences exist +# +proc regexp_diff { file_1 file_2 } { + + set eof -1 + set end_1 0 + set end_2 0 + set differences 0 + set diff_pass 0 + + if [file exists $file_1] then { + set file_a [open $file_1 r] + } else { + warning "$file_1 doesn't exist" + return 1 + } + + if [file exists $file_2] then { + set file_b [open $file_2 r] + } else { + fail "$file_2 doesn't exist" + close $file_a + return 1 + } + + verbose " Regexp-diff'ing: $file_1 $file_2" 2 + + while { 1 } { + set line_a "" + set line_b "" + while { [string length $line_a] == 0 } { + if { [gets $file_a line_a] == $eof } { + set end_1 1 + break + } + } + while { [string length $line_b] == 0 || [string match "#*" $line_b] } { + if [ string match "#pass" $line_b ] { + set end_2 1 + set diff_pass 1 + break + } elseif [ string match "#..." $line_b ] { + if { [gets $file_b line_b] == $eof } { + set end_2 1 + break + } + verbose "looking for \"^$line_b$\"" 3 + while { ![regexp "^$line_b$" "$line_a"] } { + verbose "skipping \"$line_a\"" 3 + if { [gets $file_a line_a] == $eof } { + set end_1 1 + break + } + } + break + } + if { [gets $file_b line_b] == $eof } { + set end_2 1 + break + } + } + + if { $diff_pass } { + break + } elseif { $end_1 && $end_2 } { + break + } elseif { $end_1 } { + send_log "extra regexps in $file_2 starting with \"^$line_b$\"\nEOF from $file_1\n" + verbose "extra regexps in $file_2 starting with \"^$line_b$\"\nEOF from $file_1" 3 + set differences 1 + break + } elseif { $end_2 } { + send_log "extra lines in $file_1 starting with \"^$line_a$\"\nEOF from $file_2\n" + verbose "extra lines in $file_1 starting with \"^$line_a$\"\nEOF from $file_2\n" 3 + set differences 1 + break + } else { + verbose "regexp \"^$line_b$\"\nline \"$line_a\"" 3 + if ![regexp "^$line_b$" "$line_a"] { + send_log "regexp_diff match failure\n" + send_log "regexp \"^$line_b$\"\nline \"$line_a\"\n" + set differences 1 + } + } + } + + if { $differences == 0 && !$diff_pass && [eof $file_a] != [eof $file_b] } { + send_log "$file_1 and $file_2 are different lengths\n" + verbose "$file_1 and $file_2 are different lengths" 3 + set differences 1 + } + + close $file_a + close $file_b + + return $differences +} + +proc file_contents { filename } { + set file [open $filename r] + set contents [read $file] + close $file + return $contents +} + +proc verbose_eval { expr { level 1 } } { + global verbose + if $verbose>$level then { eval verbose "$expr" $level } +} + # This definition is taken from an unreleased version of DejaGnu. Once # that version gets released, and has been out in the world for a few # months at least, it may be safe to delete this copy.