| # runtest.exp -- Test framework driver |
| # Copyright (C) 1992-2019, 2020, 2022, 2023, 2024 |
| # Free Software Foundation, Inc. |
| # |
| # This file is part of DejaGnu. |
| # |
| # DejaGnu 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. |
| # |
| # DejaGnu 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 DejaGnu. If not, see <http://www.gnu.org/licenses/>. |
| |
| # This file was written by Rob Savoye <rob@welcomehome.org>. |
| |
| set frame_version 1.6.4-git |
| if {![info exists argv0]} { |
| send_error "Must use a version of Expect greater than 5.0\n" |
| exit 1 |
| } |
| |
| # trap some signals so we know whats happening. These definitions are only |
| # temporary until we read in the library stuff |
| # |
| trap { send_user "\ninterrupted by user\n"; exit 130 } SIGINT |
| trap { send_user "\nquit\n"; exit 131 } SIGQUIT |
| trap { send_user "\nterminated\n"; exit 143 } SIGTERM |
| |
| # |
| # Initialize a few global variables used by all tests. |
| # `reset_vars' resets several of these, we define them here to document their |
| # existence. In fact, it would be nice if all globals used by some interface |
| # of dejagnu proper were documented here. |
| # |
| # Keep these all lowercase. Interface variables used by the various |
| # testsuites (eg: the gcc testsuite) should be in all capitals |
| # (eg: TORTURE_OPTIONS). |
| # |
| set mail_logs 0 ;# flag for mailing of summary and diff logs |
| set psum_file "latest" ;# file name of previous summary to diff against |
| |
| set exit_status 0 ;# exit code returned by this program |
| |
| set xfail_flag 0 ;# indicates that a failure is expected |
| set xfail_prms 0 ;# GNATS prms id number for this expected failure |
| set kfail_flag 0 ;# indicates that it is a known failure |
| set kfail_prms 0 ;# bug id for the description of the known failure |
| set sum_file "" ;# name of the file that contains the summary log |
| set base_dir "" ;# the current working directory |
| set xml_file "" ;# handle on the XML file if requested |
| set xml 0 ;# flag for requesting xml |
| set logname "" ;# the users login name |
| set prms_id 0 ;# GNATS prms id number |
| set bug_id 0 ;# optional bug id number |
| set dir "" ;# temp variable for directory names |
| set srcdir "." ;# source directory containing the test suite |
| set ignoretests "" ;# list of tests to not execute |
| set objdir "." ;# directory where test case binaries live |
| set reboot 0 |
| set multipass "" ;# list of passes and var settings |
| set errno ""; ;# |
| set exit_error 1 ;# Toggle for whether to set the exit status |
| ;# on Tcl bugs in test case drivers. |
| # |
| # These describe the host and target environments. |
| # |
| set build_triplet "" ;# type of architecture to run tests on |
| set build_os "" ;# type of os the tests are running on |
| set build_vendor "" ;# vendor name of the OS or workstation the test are running on |
| set build_cpu "" ;# type of the cpu tests are running on |
| set host_triplet "" ;# type of architecture to run tests on, sometimes remotely |
| set host_os "" ;# type of os the tests are running on |
| set host_vendor "" ;# vendor name of the OS or workstation the test are running on |
| set host_cpu "" ;# type of the cpu tests are running on |
| set target_triplet "" ;# type of architecture to run tests on, final remote |
| set target_os "" ;# type of os the tests are running on |
| set target_vendor "" ;# vendor name of the OS or workstation the test are running on |
| set target_cpu "" ;# type of the cpu tests are running on |
| set target_alias "" ;# standard abbreviation of target |
| set compiler_flags "" ;# the flags used by the compiler |
| |
| # |
| # These set configuration file names and are local to this file. |
| # |
| set local_init_file site.exp ;# testsuite-local init file name |
| set global_init_file site.exp ;# global init file name |
| |
| # |
| # These are used to locate parts of the testsuite. |
| # |
| set testsuitedir "testsuite" ;# top-level testsuite source directory |
| set testbuilddir "testsuite" ;# top-level testsuite object directory |
| |
| # |
| # Collected errors |
| # |
| namespace eval ::dejagnu::error { |
| # list of { file message errorCode errorInfo } lists |
| variable list [list] |
| } |
| |
| # Various ccache versions provide incorrect debug info such as ignoring |
| # different current directory, breaking GDB testsuite. |
| set env(CCACHE_DISABLE) 1 |
| unset -nocomplain env(CCACHE_NODISABLE) |
| |
| # |
| # some convenience abbreviations |
| # |
| set hex "0x\[0-9A-Fa-f\]+" |
| set decimal "\[0-9\]+" |
| |
| # |
| # set the base dir (current working directory) |
| # |
| set base_dir [pwd] |
| |
| # |
| # These are set here instead of the init module so they can be overridden |
| # by command line options. |
| # |
| set all_flag 0 |
| set binpath "" |
| set debug 0 |
| set options "" |
| set outdir "." |
| set reboot 1 |
| set tracelevel 0 |
| set verbose 0 |
| set log_dialog 0 |
| |
| # |
| # verbose [-n] [-log] [--] message [level] |
| # |
| # Print MESSAGE if the verbose level is >= LEVEL. |
| # The default value of LEVEL is 1. |
| # "-n" says to not print a trailing newline. |
| # "-log" says to add the text to the log file even if it won't be printed. |
| # Note that the apparent behaviour of `send_user' dictates that if the message |
| # is printed it is also added to the log file. |
| # Use "--" if MESSAGE begins with "-". |
| # |
| # This is defined here rather than in framework.exp so we can use it |
| # while still loading in the support files. |
| # |
| proc verbose { args } { |
| global verbose |
| set newline 1 |
| set logfile 0 |
| |
| set i 0 |
| if { [string index [lindex $args 0] 0] eq "-" } { |
| for { set i 0 } { $i < [llength $args] } { incr i } { |
| if { [lindex $args $i] eq "--" } { |
| incr i |
| break |
| } elseif { [lindex $args $i] eq "-n" } { |
| set newline 0 |
| } elseif { [lindex $args $i] eq "-log" } { |
| set logfile 1 |
| } elseif { [lindex $args $i] eq "-x" } { |
| set xml 1 |
| } elseif { [string index [lindex $args $i] 0] eq "-" } { |
| clone_output "ERROR: verbose: illegal argument: [lindex $args $i]" |
| return |
| } else { |
| break |
| } |
| } |
| if { [llength $args] == $i } { |
| clone_output "ERROR: verbose: nothing to print" |
| return |
| } |
| } |
| |
| set level 1 |
| if { [llength $args] > $i + 1 } { |
| set level [lindex $args [expr { $i + 1 }]] |
| } |
| set message [lindex $args $i] |
| |
| if { $verbose >= $level } { |
| # We assume send_user also sends the text to the log file (which |
| # appears to be the case though the docs aren't clear on this). |
| if { $newline } { |
| send_user -- "$message\n" |
| } else { |
| send_user -- $message |
| } |
| } elseif { $logfile } { |
| if { $newline } { |
| send_log -- "$message\n" |
| } else { |
| send_log -- $message |
| } |
| } |
| } |
| |
| # |
| # Transform a tool name to get the installed name. |
| # target_triplet is the canonical target name. target_alias is the |
| # target name used when configure was run. |
| # |
| proc transform { name } { |
| global target_triplet |
| global target_alias |
| global host_triplet |
| global board |
| |
| if { $target_triplet eq $host_triplet } { |
| return $name |
| } |
| if { $target_triplet eq "native" } { |
| return $name |
| } |
| if {[board_info host exists no_transform_name]} { |
| return $name |
| } |
| if { $target_triplet eq "" } { |
| return $name |
| } else { |
| if {[info exists board]} { |
| if {[board_info $board exists target_install]} { |
| set target_install [board_info $board target_install] |
| } |
| } |
| if {[target_info exists target_install]} { |
| set target_install [target_info target_install] |
| } |
| if {$target_alias ne ""} { |
| set tmp $target_alias-$name |
| } elseif {[info exists target_install]} { |
| if { [lsearch -exact $target_install $target_alias] >= 0 } { |
| set tmp $target_alias-$name |
| } else { |
| set tmp "[lindex $target_install 0]-$name" |
| } |
| } |
| # There appears to be a possibility for tmp to be unset at this |
| # point, which will cause a Tcl error, but this can only occur if |
| # the init files invoke transform prior to defining target_alias, |
| # since the target_alias will be defaulted to the value of |
| # target_triplet before tests are run. If target_triplet is also |
| # empty, this point will not be reached; see test above. |
| verbose "Transforming $name to $tmp" |
| return $tmp |
| } |
| } |
| |
| # |
| # findfile arg0 [arg1] [arg2] |
| # |
| # Find a file and see if it exists. If you only care about the false |
| # condition, then you'll need to pass a null "" for arg1. |
| # arg0 is the filename to look for. If the only arg, |
| # then that's what gets returned. If this is the |
| # only arg, then if it exists, arg0 gets returned. |
| # if it doesn't exist, return only the prog name. |
| # arg1 is optional, and it's what gets returned if |
| # the file exists. |
| # arg2 is optional, and it's what gets returned if |
| # the file doesn't exist. |
| # |
| proc findfile { args } { |
| # look for the file |
| verbose "Seeing if [lindex $args 0] exists." 2 |
| if {[file exists [lindex $args 0]]} { |
| if { [llength $args] > 1 } { |
| verbose "Found file, returning [lindex $args 1]" |
| return [lindex $args 1] |
| } else { |
| verbose "Found file, returning [lindex $args 0]" |
| return [lindex $args 0] |
| } |
| } else { |
| if { [llength $args] > 2 } { |
| verbose "Didn't find file [lindex $args 0], returning [lindex $args 2]" |
| return [lindex $args 2] |
| } else { |
| verbose "Didn't find file, returning [file tail [lindex $args 0]]" |
| return [transform [file tail [lindex $args 0]]] |
| } |
| } |
| } |
| |
| # |
| # load_file [-1] [--] file1 [ file2 ... ] |
| # |
| # Utility to source a file. All are sourced in order unless the flag "-1" |
| # is given in which case we stop after finding the first one. |
| # The result is 1 if a file was found, 0 if not. |
| # If a tcl error occurs while sourcing a file, we print an error message |
| # and exit. |
| # |
| proc load_file { args } { |
| set i 0 |
| set only_one 0 |
| if { [lindex $args $i] eq "-1" } { |
| set only_one 1 |
| incr i |
| } |
| if { [lindex $args $i] eq "--" } { |
| incr i |
| } |
| |
| set found 0 |
| foreach file [lrange $args $i end] { |
| verbose "Looking for $file" 2 |
| # In Tcl, "file exists" fails if the filename looks like |
| # ~/FILE and the environment variable HOME does not exist. |
| if {! [catch {file exists $file} result] && $result} { |
| set found 1 |
| verbose "Found $file" |
| if { [catch "uplevel #0 source $file"] == 1 } { |
| send_error "ERROR: tcl error sourcing $file.\n" |
| global errorInfo |
| if {[info exists errorInfo]} { |
| send_error "$errorInfo\n" |
| } |
| exit 1 |
| } |
| if { $only_one } { |
| break |
| } |
| } |
| } |
| return $found |
| } |
| |
| # |
| # search_and_load_file -- search DIRLIST looking for FILELIST. |
| # TYPE is used when displaying error and progress messages. |
| # |
| proc search_and_load_file { type filelist dirlist } { |
| set found 0 |
| |
| foreach dir $dirlist { |
| foreach initfile $filelist { |
| set filename [file join $dir $initfile] |
| verbose "Looking for $type $filename" 2 |
| if {[file exists $filename]} { |
| set found 1 |
| set error "" |
| if { $type ne "library file" } { |
| send_user "Using $filename as $type.\n" |
| } else { |
| verbose "Loading $filename" |
| } |
| if {[catch "uplevel #0 source $filename" error] == 1} { |
| global errorInfo |
| send_error "ERROR: tcl error sourcing $type $filename.\n$error\n" |
| if {[info exists errorInfo]} { |
| send_error "$errorInfo\n" |
| } |
| exit 1 |
| } |
| break |
| } |
| } |
| if { $found } { |
| break |
| } |
| } |
| return $found |
| } |
| |
| # |
| # Give a usage statement. |
| # |
| proc usage { } { |
| global tool |
| |
| send_user "USAGE: runtest \[options...\]\n" |
| send_user "\t--all, -a\t\tPrint all test output to screen\n" |
| send_user "\t--build \[triplet\]\tThe canonical triplet of the build machine\n" |
| send_user "\t--debug\t\t\tSet expect debugging ON\n" |
| send_user "\t--directory name\tRun only the tests in directory 'name'\n" |
| send_user "\t--global_init \[name\]\tThe file to load for global configuration\n" |
| send_user "\t--help\t\t\tPrint help text\n" |
| send_user "\t--host \[triplet\]\tThe canonical triplet of the host machine\n" |
| send_user "\t--host_board \[name\]\tThe host board to use\n" |
| send_user "\t--ignore \[name(s)\]\tThe names of specific tests to ignore\n" |
| send_user "\t--local_init \[name\]\tThe file to load for local configuration\n" |
| send_user "\t--log_dialog\t\t\Emit Expect output on stdout\n" |
| send_user "\t--mail \[name(s)\]\tWhom to mail the results to\n" |
| send_user "\t--objdir \[name\]\t\tThe test suite binary directory\n" |
| send_user "\t--outdir \[name\]\t\tThe directory to put logs in\n" |
| send_user "\t--reboot\t\tReboot the target (if supported)\n" |
| send_user "\t--srcdir \[name\]\t\tThe test suite source code directory\n" |
| send_user "\t--status\t\tSet the exit status to fail on Tcl errors\n" |
| send_user "\t--strace \[number\]\tTurn on Expect tracing\n" |
| send_user "\t--target \[triplet\]\tThe canonical triplet of the target board\n" |
| send_user "\t--target_board \[name(s)\] The list of target boards to run tests on\n" |
| send_user "\t--tool \[name(s)\]\tRun tests on these tools\n" |
| send_user "\t--tool_exec \[name\]\tThe path to the tool executable to test\n" |
| send_user "\t--tool_opts \[options\]\tA list of additional options to pass to the tool\n" |
| send_user "\t--verbose, -v\t\tProduce verbose output\n" |
| send_user "\t--version, -V\t\tPrint all relevant version numbers\n" |
| send_user "\t--xml, -x\t\tWrite out an XML results file\n" |
| send_user "\t--D\[0-1\]\t\tTcl debugger\n" |
| send_user "\tscript.exp\[=arg(s)\]\tRun these tests only\n" |
| if { [info exists tool] } { |
| if { [info procs ${tool}_option_help] ne "" } { |
| ${tool}_option_help |
| } |
| } |
| } |
| |
| # |
| # Parse the arguments the first time looking for these. We will ultimately |
| # parse them twice. Things are complicated because: |
| # - we want to parse --verbose early on |
| # - we don't want config files to override command line arguments |
| # (eg: $base_dir/$local_init_file vs --host/--target) |
| # - we need some command line arguments before we can process some config files |
| # (eg: --objdir before $objdir/$local_init_file, --host/--target before $DEJAGNU) |
| # The use of `arg_host_triplet' and `arg_target_triplet' lets us avoid parsing |
| # the arguments three times. |
| # |
| |
| namespace eval ::dejagnu::command_line { |
| variable cmd_var_list [list] |
| |
| proc save_cmd_var {name} { |
| variable cmd_var_list |
| |
| upvar 1 $name target_var |
| lappend cmd_var_list $name $target_var |
| } |
| |
| proc restore_cmd_vars {} { |
| variable cmd_var_list |
| |
| foreach {name value} $cmd_var_list { |
| uplevel 1 [list set $name $value] |
| } |
| verbose "Variables set by command line arguments restored." 4 |
| } |
| |
| proc dump_cmd_vars {} { |
| variable cmd_var_list |
| |
| verbose "Variables set by command line arguments:" 4 |
| foreach {name value} $cmd_var_list { |
| verbose " $name -> $value" 4 |
| } |
| } |
| } |
| |
| set arg_host_triplet "" |
| set arg_target_triplet "" |
| set arg_build_triplet "" |
| set argc [ llength $argv ] |
| for { set i 0 } { $i < $argc } { incr i } { |
| set option [lindex $argv $i] |
| |
| # make all options have two hyphens |
| switch -glob -- $option { |
| "--*" { |
| } |
| "-*" { |
| set option "-$option" |
| } |
| } |
| |
| # split out the argument for options that take them |
| switch -glob -- $option { |
| "--*=*" { |
| regexp {^[^=]*=(.*)$} $option nil optarg |
| } |
| "--bu*" - |
| "--g*" - |
| "--ho*" - |
| "--ig*" - |
| "--loc*" - |
| "--m*" - |
| "--ob*" - |
| "--ou*" - |
| "--sr*" - |
| "--str*" - |
| "--ta*" - |
| "--di*" - |
| "--to*" { |
| incr i |
| set optarg [lindex $argv $i] |
| } |
| } |
| |
| switch -glob -- $option { |
| "--V*" - |
| "--vers*" { # (--version) version numbers |
| send_user "DejaGnu version\t$frame_version\n" |
| send_user "Expect version\t[exp_version]\n" |
| send_user "Tcl version\t[ info tclversion ]\n" |
| exit 0 |
| } |
| |
| "--bu*" { # (--build) the build host configuration |
| set arg_build_triplet $optarg |
| ::dejagnu::command_line::save_cmd_var arg_build_triplet |
| continue |
| } |
| |
| "--g*" { # (--global_init) the global init file name |
| set global_init_file $optarg |
| ::dejagnu::command_line::save_cmd_var global_init_file |
| continue |
| } |
| |
| "--host_bo*" { |
| set host_board $optarg |
| ::dejagnu::command_line::save_cmd_var host_board |
| continue |
| } |
| |
| "--ho*" { # (--host) the host configuration |
| set arg_host_triplet $optarg |
| ::dejagnu::command_line::save_cmd_var arg_host_triplet |
| continue |
| } |
| |
| "--loc*" { # (--local_init) the local init file name |
| set local_init_file $optarg |
| ::dejagnu::command_line::save_cmd_var local_init_file |
| continue |
| } |
| |
| "--ob*" { # (--objdir) where the test case object code lives |
| set objdir $optarg |
| ::dejagnu::command_line::save_cmd_var objdir |
| continue |
| } |
| |
| "--sr*" { # (--srcdir) where the testsuite source code lives |
| set srcdir $optarg |
| ::dejagnu::command_line::save_cmd_var srcdir |
| continue |
| } |
| |
| "--target_bo*" { |
| set target_list $optarg |
| ::dejagnu::command_line::save_cmd_var target_list |
| continue |
| } |
| |
| "--ta*" { # (--target) the target configuration |
| set arg_target_triplet $optarg |
| ::dejagnu::command_line::save_cmd_var arg_target_triplet |
| continue |
| } |
| |
| "--tool_opt*" { |
| set TOOL_OPTIONS $optarg |
| ::dejagnu::command_line::save_cmd_var TOOL_OPTIONS |
| continue |
| } |
| |
| "--tool_exec*" { |
| set TOOL_EXECUTABLE $optarg |
| ::dejagnu::command_line::save_cmd_var TOOL_EXECUTABLE |
| continue |
| } |
| |
| "--to*" { # (--tool) specify tool name |
| set tool $optarg |
| set comm_line_tool $optarg |
| ::dejagnu::command_line::save_cmd_var tool |
| ::dejagnu::command_line::save_cmd_var comm_line_tool |
| continue |
| } |
| |
| "--di*" { |
| set cmdline_dir_to_run $optarg |
| ::dejagnu::command_line::save_cmd_var cmdline_dir_to_run |
| continue |
| } |
| |
| "--v" - |
| "--verb*" { # (--verbose) verbose output |
| incr verbose |
| continue |
| } |
| |
| "[A-Z0-9_-.]*=*" { # process makefile style args like CC=gcc, etc... |
| if {[regexp "^(\[A-Z0-9_-\]+)=(.*)$" $option junk var val]} { |
| set $var $val |
| verbose "$var is now $val" |
| append makevars "set $var $val;" ;# FIXME: Used anywhere? |
| unset junk var val |
| } else { |
| send_error "Illegal variable specification:\n" |
| send_error "$option\n" |
| } |
| continue |
| } |
| |
| } |
| } |
| verbose "Verbose level is $verbose" |
| |
| verbose [concat "Initial working directory is" [pwd]] |
| |
| ::dejagnu::command_line::dump_cmd_vars |
| |
| # |
| # get the users login name |
| # |
| if { [info exists env(USER)] } { |
| set logname $env(USER) |
| } elseif { [info exists env(LOGNAME)] } { |
| set logname $env(LOGNAME) |
| } elseif { [catch {exec whoami} logname] == 0 } { |
| # we now have it |
| } elseif { [catch {exec who am i} logname] == 0 } { |
| # systems using "who am i" apparently return some associated garbage |
| set logname [lindex [split $logname " !"] 1] |
| } else { |
| # if we get here, logname contains an error message; erase it |
| set logname "" |
| } |
| |
| # on the GNU system, "who am i" can successfully return an empty string |
| if { $logname eq "" } { |
| send_user "ERROR: couldn't get the user's login name\n" |
| set logname "Unknown" |
| } |
| |
| verbose "Login name is $logname" |
| |
| # |
| # lookfor_file -- try to find a file by searching up multiple directory levels |
| # |
| proc lookfor_file { dir name } { |
| foreach x [list . .. ../.. ../../.. ../../../..] { |
| verbose $dir/$x/$name 2 |
| if {[file exists [file join $dir $name]]} { |
| return [file join $dir $name] |
| } |
| set dir [remote_file build dirname $dir] |
| } |
| return "" |
| } |
| |
| # |
| # load_lib -- load a library by sourcing it |
| # |
| # If there a multiple files with the same name, stop after the first one found. |
| # The order is first look in the install dir, then in a parallel dir in the |
| # source tree (up one or two levels), then in the current dir. |
| # |
| proc load_lib { file } { |
| global verbose execpath tool |
| global libdir libdirs srcdir testsuitedir base_dir |
| global loaded_libs |
| |
| if {[info exists loaded_libs($file)]} { |
| return |
| } |
| |
| set loaded_libs($file) "" |
| set search_dirs [list ../lib $libdir $libdir/lib] |
| lappend search_dirs [file dirname [file dirname $srcdir]]/dejagnu/lib |
| lappend search_dirs $testsuitedir/lib |
| lappend search_dirs $execpath/lib "." |
| lappend search_dirs [file dirname [file dirname [file dirname $srcdir]]]/dejagnu/lib |
| if {[info exists libdirs]} { |
| lappend search_dirs $libdirs |
| } |
| if { [search_and_load_file "library file" $file $search_dirs ] == 0 } { |
| send_error "ERROR: Couldn't find library file $file.\n" |
| exit 1 |
| } |
| } |
| |
| # |
| # Begin sourcing the config files. |
| # All are sourced in order. |
| # |
| # Search order: |
| # (local) $base_dir/$local_init_file -> $objdir/$local_init_file -> |
| # (global) installed($global_init_file) -> $DEJAGNU -> $HOME/.dejagnurc |
| # |
| # For the normal case, we expect $base_dir/$local_init_file to set |
| # host_triplet and target_triplet. |
| # |
| |
| load_file [file join $base_dir $local_init_file] |
| |
| # Ensure that command line parameters override testsuite init files. |
| ::dejagnu::command_line::restore_cmd_vars |
| |
| # |
| # If objdir didn't get set in $base_dir/$local_init_file, set it to |
| # $base_dir. Make sure we source $objdir/$local_init_file in case |
| # $base_dir/$local_init_file doesn't exist and objdir was given on the |
| # command line. |
| # |
| |
| if { $objdir eq "." || $objdir eq $srcdir } { |
| set objdir $base_dir |
| } else { |
| load_file [file join $objdir $local_init_file] |
| } |
| |
| # Ensure that command line parameters override testsuite init files. |
| ::dejagnu::command_line::restore_cmd_vars |
| |
| # |
| # Find the testsuite. |
| # |
| |
| # The DejaGnu manual has always stated that a testsuite must be in a |
| # testsuite/ subdirectory. |
| |
| verbose "Finding testsuite ..." 3 |
| verbose "\$base_dir -> $base_dir" 3 |
| verbose "\$srcdir -> $srcdir" 3 |
| verbose "\$objdir -> $objdir" 3 |
| verbose [concat "file tail \$srcdir -> " [file tail $srcdir]] 3 |
| verbose [concat "file join \$srcdir testsuite -> " \ |
| [file join $srcdir testsuite]] 3 |
| verbose [concat "file isdirectory [file join \$srcdir testsuite] -> " \ |
| [file isdirectory [file join $srcdir testsuite]]] 3 |
| verbose [concat "file tail \$base_dir -> " [file tail $base_dir]] 3 |
| |
| if { [file tail $srcdir] eq "testsuite" } { |
| # Subdirectory case -- $srcdir includes testsuite/ |
| set testsuitedir $srcdir |
| set testbuilddir $objdir |
| } elseif { [file tail $srcdir] ne "testsuite" |
| && [file isdirectory [file join $srcdir testsuite]] } { |
| # Top-level case -- testsuite in $srcdir/testsuite/ |
| set testsuitedir [file join $srcdir testsuite] |
| set testbuilddir [file join $objdir testsuite] |
| } elseif { $srcdir eq "." && [file tail $base_dir] eq "testsuite" } { |
| # Development scaffold case -- testsuite in ".", but "." is "testsuite" |
| set testsuitedir $base_dir |
| set testbuilddir $base_dir |
| } else { |
| if { $testsuitedir eq "testsuite" && $testbuilddir eq "testsuite" } { |
| # Broken legacy case -- testsuite not actually in testsuite/ |
| # Produce a warning, but continue. |
| send_error "WARNING: testsuite is not in a testsuite/ directory.\n" |
| set testsuitedir $srcdir |
| set testbuilddir $objdir |
| } else { |
| # Custom case -- all variables are assumed to have been set correctly |
| } |
| } |
| |
| verbose "Finding testsuite ... done" 3 |
| |
| # Well, this just demonstrates the real problem... |
| if {![info exists tool_root_dir]} { |
| set tool_root_dir [file dirname $objdir] |
| if {[file exists [file join $tool_root_dir testsuite]]} { |
| set tool_root_dir [file dirname $tool_root_dir] |
| } |
| } |
| |
| verbose "Using test sources in $srcdir" |
| verbose "Using test binaries in $objdir" |
| verbose "Testsuite root is $testsuitedir" |
| verbose "Tool root directory is $tool_root_dir" |
| |
| set execpath [file dirname $argv0] |
| |
| # The runtest.exp file is installed directly in libdir. |
| # Conveniently, the source tree layout is the same as the installed libdir. |
| set libdir [file dirname $argv0] |
| if {[info exists env(DEJAGNULIBS)]} { |
| set libdir $env(DEJAGNULIBS) |
| } |
| # list of extra search directories used by load_lib to look for libs |
| set libdirs {} |
| |
| verbose "Using $libdir to find libraries" |
| |
| # |
| # If the host or target was given on the command line, override the above |
| # config files. We allow $DEJAGNU to massage them though in case it would |
| # ever want to do such a thing. |
| # |
| if { $arg_host_triplet ne "" } { |
| set host_triplet $arg_host_triplet |
| } |
| if { $arg_build_triplet ne "" } { |
| set build_triplet $arg_build_triplet |
| } |
| |
| # If we only specify --host, then that must be the build machine too, |
| # and we're stuck using the old functionality of a simple cross test. |
| if {[expr { $build_triplet eq "" && $host_triplet ne "" } ]} { |
| set build_triplet $host_triplet |
| } |
| # If we only specify --build, then we'll use that as the host too. |
| if {[expr { $build_triplet ne "" && $host_triplet eq "" } ]} { |
| set host_triplet $build_triplet |
| } |
| unset arg_host_triplet arg_build_triplet |
| |
| # |
| # If the build machine type hasn't been specified by now, use config.guess. |
| # |
| |
| if {[expr {$build_triplet eq "" && $host_triplet eq ""}]} { |
| # find config.guess |
| foreach dir [list $libdir $libdir/libexec $libdir/.. $execpath $srcdir $srcdir/.. $srcdir/../..] { |
| verbose "Looking for $dir/config.guess" 2 |
| if {[file exists [file join $dir config.guess]]} { |
| set config_guess [file join $dir config.guess] |
| verbose "Found [file join $dir config.guess]" |
| break |
| } |
| } |
| |
| # get the canonical triplet |
| if {![info exists config_guess]} { |
| send_error "ERROR: Couldn't find config.guess program.\n" |
| exit 1 |
| } |
| if { [info exists ::env(CONFIG_SHELL)] } { |
| if { [catch {exec $::env(CONFIG_SHELL) $config_guess} build_triplet] } { |
| if { [lindex $::errorCode 0] eq "CHILDSTATUS" } { |
| send_error "ERROR: Running config.guess with\ |
| CONFIG_SHELL=$::env(CONFIG_SHELL)\ |
| exited on code\ |
| [lindex $::errorCode 2].\n" |
| } else { |
| send_error "ERROR: Running config.guess with\ |
| CONFIG_SHELL=$::env(CONFIG_SHELL)\ |
| produced error:\n" |
| send_error " $::errorCode\n" |
| } |
| } |
| } elseif { [info exists ::env(SHELL)] } { |
| if { [catch {exec $::env(SHELL) $config_guess} build_triplet] } { |
| if { [lindex $::errorCode 0] eq "CHILDSTATUS" } { |
| send_error "ERROR: Running config.guess with\ |
| SHELL=$::env(SHELL)\ |
| exited on code\ |
| [lindex $::errorCode 2].\n" |
| } else { |
| send_error "ERROR: Running config.guess with\ |
| SHELL=$::env(SHELL)\ |
| produced error:\n" |
| send_error " $::errorCode\n" |
| } |
| } |
| } else { |
| if { [catch {exec $config_guess} build_triplet] } { |
| if { [lindex $::errorCode 0] eq "CHILDSTATUS" } { |
| send_error "ERROR: Running config.guess exited on code\ |
| [lindex $::errorCode 2].\n" |
| } else { |
| send_error "ERROR: Running config.guess produced error:\n" |
| send_error " $::errorCode\n" |
| } |
| } |
| } |
| if { ![regexp -- {^[[:alnum:]_.]+(-[[:alnum:]_.]+)+$} $build_triplet] } { |
| send_error "ERROR: Running config.guess produced bogus build triplet:\n" |
| send_error " $build_triplet\n" |
| send_error " (Perhaps you need to set CONFIG_SHELL or\ |
| SHELL in your environment\n" |
| send_error " to the absolute file name of a POSIX shell?)\n" |
| exit 1 |
| } |
| verbose "Assuming build host is $build_triplet" |
| if { $host_triplet eq "" } { |
| set host_triplet $build_triplet |
| } |
| } |
| |
| # |
| # Figure out the target. If the target hasn't been specified, then we have to |
| # assume we are native. |
| # |
| if { $arg_target_triplet ne "" } { |
| set target_triplet $arg_target_triplet |
| } elseif { $target_triplet eq "" } { |
| set target_triplet $build_triplet |
| verbose "Assuming native target is $target_triplet" 2 |
| } |
| unset arg_target_triplet |
| # |
| # Default target_alias to target_triplet. |
| # |
| if {$target_alias eq ""} { |
| set target_alias $target_triplet |
| } |
| |
| proc get_local_hostname { } { |
| if {[catch "info hostname" hb]} { |
| set hb "" |
| } else { |
| regsub "\\..*$" $hb "" hb |
| } |
| verbose "hostname=$hb" 3 |
| return $hb |
| } |
| |
| # |
| # We put these here so that they can be overridden later by site.exp or |
| # friends. |
| # |
| # Set up the target as machine NAME. We also load base-config.exp as a |
| # default configuration. The config files are sourced with the global |
| # variable $board set to the name of the current target being defined. |
| # |
| proc setup_target_hook { whole_name name } { |
| global board |
| global host_board |
| |
| if {[info exists host_board]} { |
| set hb $host_board |
| } else { |
| set hb [get_local_hostname] |
| } |
| |
| set board $whole_name |
| |
| global board_type |
| set board_type "target" |
| |
| load_config base-config.exp |
| if {![load_board_description $name $whole_name $hb]} { |
| if { $name ne "unix" } { |
| perror "couldn't load description file for $name" |
| exit 1 |
| } else { |
| load_generic_config "unix" |
| } |
| } |
| |
| if {[board_info $board exists generic_name]} { |
| load_tool_target_config [board_info $board generic_name] |
| } |
| |
| unset board |
| unset board_type |
| |
| push_target $whole_name |
| |
| if { [info procs ${whole_name}_init] ne "" } { |
| ${whole_name}_init $whole_name |
| } |
| |
| if { ![isnative] && ![isremote target] } { |
| global env build_triplet target_triplet |
| if { (![info exists env(DEJAGNU)]) && ($build_triplet ne $target_triplet) } { |
| warning "Assuming target board is the local machine (which is probably wrong).\nYou may need to set your DEJAGNU environment variable." |
| } |
| } |
| } |
| |
| # |
| # Clean things up afterwards. |
| # |
| proc cleanup_target_hook { name } { |
| global tool |
| # Clean up the target board. |
| if { [info procs ${name}_exit] ne "" } { |
| ${name}_exit |
| } |
| # We also call the tool exit routine here. |
| if {[info exists tool]} { |
| if { [info procs ${tool}_exit] ne "" } { |
| ${tool}_exit |
| } |
| } |
| remote_close target |
| pop_target |
| } |
| |
| proc setup_host_hook { name } { |
| global board |
| global board_info |
| global board_type |
| |
| set board $name |
| set board_type "host" |
| |
| load_board_description $name |
| unset board |
| unset board_type |
| push_host $name |
| if { [info procs ${name}_init] ne "" } { |
| ${name}_init $name |
| } |
| } |
| |
| proc setup_build_hook { name } { |
| global board |
| global board_info |
| global board_type |
| |
| set board $name |
| set board_type "build" |
| |
| load_board_description $name |
| unset board |
| unset board_type |
| push_build $name |
| if { [info procs ${name}_init] ne "" } { |
| ${name}_init $name |
| } |
| } |
| |
| # |
| # Find and load the global config file if it exists. |
| # The global config file is used to set the connect mode and other |
| # parameters specific to each particular target. |
| # These files assume the host and target have been set. |
| # |
| |
| if { [load_file -- [file join $libdir $global_init_file]] == 0 } { |
| # If $DEJAGNU isn't set either then there isn't any global config file. |
| # Warn the user as there really should be one. |
| if { ! [info exists env(DEJAGNU)] } { |
| send_error "WARNING: Couldn't find the global config file.\n" |
| } |
| } |
| |
| if {[info exists env(DEJAGNU)]} { |
| if { [load_file -- $env(DEJAGNU)] == 0 } { |
| # It may seem odd to only issue a warning if there isn't a global |
| # config file, but issue an error if $DEJAGNU is erroneously defined. |
| # Since $DEJAGNU is set there is *supposed* to be a global config file, |
| # so the current behaviour seems reasonable. |
| send_error "ERROR: global config file $env(DEJAGNU) not found.\n" |
| exit 1 |
| } |
| if {![info exists boards_dir]} { |
| set boards_dir "[file dirname $env(DEJAGNU)]/boards" |
| } |
| } |
| |
| # Load user .dejagnurc file last as the ultimate override. |
| load_file ~/.dejagnurc |
| |
| if {![info exists boards_dir]} { |
| set boards_dir "" |
| } |
| |
| # |
| # parse out the config parts of the triplet name |
| # |
| |
| # build values |
| if { $build_cpu eq "" } { |
| regsub -- "-.*-.*" $build_triplet "" build_cpu |
| } |
| if { $build_vendor eq "" } { |
| regsub -- "^\[a-z0-9\]*-" $build_triplet "" build_vendor |
| regsub -- "-.*" $build_vendor "" build_vendor |
| } |
| if { $build_os eq "" } { |
| regsub -- ".*-.*-" $build_triplet "" build_os |
| } |
| |
| # host values |
| if { $host_cpu eq "" } { |
| regsub -- "-.*-.*" $host_triplet "" host_cpu |
| } |
| if { $host_vendor eq "" } { |
| regsub -- "^\[a-z0-9\]*-" $host_triplet "" host_vendor |
| regsub -- "-.*" $host_vendor "" host_vendor |
| } |
| if { $host_os eq "" } { |
| regsub -- ".*-.*-" $host_triplet "" host_os |
| } |
| |
| # target values |
| if { $target_cpu eq "" } { |
| regsub -- "-.*-.*" $target_triplet "" target_cpu |
| } |
| if { $target_vendor eq "" } { |
| regsub -- "^\[a-z0-9\]*-" $target_triplet "" target_vendor |
| regsub -- "-.*" $target_vendor "" target_vendor |
| } |
| if { $target_os eq "" } { |
| regsub -- ".*-.*-" $target_triplet "" target_os |
| } |
| |
| # |
| # Load the primary tool initialization file. |
| # |
| |
| proc load_tool_init { file } { |
| global srcdir testsuitedir |
| global loaded_libs |
| |
| if {[info exists loaded_libs(tool/$file)]} { |
| return |
| } |
| |
| set loaded_libs(tool/$file) "" |
| |
| lappend searchpath [file join $testsuitedir lib tool] |
| lappend searchpath [file join $testsuitedir lib] |
| # for legacy testsuites that might have files in lib/ instead of |
| # testsuite/lib/ in the package source tree; deprecated |
| lappend searchpath [file join $srcdir lib] |
| |
| if { ![search_and_load_file "tool init file" [list $file] $searchpath] } { |
| warning "Couldn't find tool init file" |
| } |
| } |
| |
| # |
| # load the testing framework libraries |
| # |
| load_lib utils.exp |
| load_lib framework.exp |
| load_lib debugger.exp |
| load_lib remote.exp |
| load_lib target.exp |
| load_lib targetdb.exp |
| load_lib libgloss.exp |
| |
| # Initialize the test counters and reset them to 0. |
| init_testcounts |
| reset_vars |
| |
| # |
| # Parse the command line arguments. |
| # |
| |
| # Load the tool initialization file. Allow the --tool option to override |
| # what's set in the site.exp file. |
| if {[info exists comm_line_tool]} { |
| set tool $comm_line_tool |
| } |
| |
| if {[info exists tool]} { |
| load_tool_init ${tool}.exp |
| } |
| |
| set argc [ llength $argv ] |
| for { set i 0 } { $i < $argc } { incr i } { |
| set option [ lindex $argv $i ] |
| |
| # make all options have two hyphens |
| switch -glob -- $option { |
| "--*" { |
| } |
| "-*" { |
| set option "-$option" |
| } |
| } |
| |
| # split out the argument for options that take them |
| switch -glob -- $option { |
| "--*=*" { |
| regexp {^[^=]*=(.*)$} $option nil optarg |
| } |
| "--bu*" - |
| "--g*" - |
| "--ho*" - |
| "--ig*" - |
| "--loc*" - |
| "--m*" - |
| "--ob*" - |
| "--ou*" - |
| "--sr*" - |
| "--str*" - |
| "--ta*" - |
| "--di*" - |
| "--to*" { |
| incr i |
| set optarg [lindex $argv $i] |
| } |
| } |
| |
| switch -glob -- $option { |
| "--v*" { # (--verbose) verbose output |
| # Already parsed. |
| continue |
| } |
| |
| "--g*" { # (--global_init) the global init file name |
| # Already parsed (and no longer useful). The file has been loaded. |
| continue |
| } |
| |
| "--loc*" { # (--local_init) the local init file name |
| # Already parsed (and no longer useful). The file has been loaded. |
| continue |
| } |
| |
| "--bu*" { # (--build) the build host configuration |
| # Already parsed (and don't set again). Let $DEJAGNU rename it. |
| continue |
| } |
| |
| "--ho*" { # (--host) the host configuration |
| # Already parsed (and don't set again). Let $DEJAGNU rename it. |
| continue |
| } |
| |
| "--target_bo*" { |
| # Set it again, father knows best. |
| set target_list $optarg |
| continue |
| } |
| |
| "--ta*" { # (--target) the target configuration |
| # Already parsed (and don't set again). Let $DEJAGNU rename it. |
| continue |
| } |
| |
| "--a*" { # (--all) print all test output to screen |
| set all_flag 1 |
| verbose "Print all test output to screen" |
| continue |
| } |
| |
| "--di*" { |
| # Already parsed (and don't set again). Let $DEJAGNU rename it. |
| continue |
| } |
| |
| |
| "--de*" { # (--debug) expect internal debugging |
| if {[file exists ./dbg.log]} { |
| catch [file delete -force -- dbg.log] |
| } |
| if { $verbose > 2 } { |
| exp_internal -f dbg.log 1 |
| } else { |
| exp_internal -f dbg.log 0 |
| } |
| verbose "Expect Debugging is ON" |
| continue |
| } |
| |
| "--D[01]" { # (-Debug) turn on Tcl debugger |
| # The runtest shell script handles this option, but it |
| # still appears in the options in the Tcl code. |
| verbose "Tcl debugger is ON" |
| continue |
| } |
| |
| "--m*" { # (--mail) mail the output |
| set mailing_list $optarg |
| set mail_logs 1 |
| verbose "Mail results to $mailing_list" |
| continue |
| } |
| |
| "--r*" { # (--reboot) reboot the target |
| set reboot 1 |
| verbose "Will reboot the target (if supported)" |
| continue |
| } |
| |
| "--ob*" { # (--objdir) where the test case object code lives |
| # Already parsed, but parse again to make sure command line |
| # options override any config file. |
| set objdir $optarg |
| verbose "Using test binaries in $objdir" |
| continue |
| } |
| |
| "--ou*" { # (--outdir) where to put the output files |
| set outdir $optarg |
| verbose "Test output put in $outdir" |
| continue |
| } |
| |
| "--log_dialog*" { |
| incr log_dialog |
| continue |
| } |
| |
| "*.exp" { # specify test names to run |
| set all_runtests($option) "" |
| verbose "Running only tests $option" |
| continue |
| } |
| |
| "*.exp=*" { # specify test names to run |
| set tmp [split $option "="] |
| set all_runtests([lindex $tmp 0]) [lindex $tmp 1] |
| verbose "Running only tests $option" |
| unset tmp |
| continue |
| } |
| |
| "--ig*" { # (--ignore) specify test names to exclude |
| set ignoretests $optarg |
| verbose "Ignoring test $ignoretests" |
| continue |
| } |
| |
| "--sr*" { # (--srcdir) where the testsuite source code lives |
| # Already parsed, but parse again to make sure command line |
| # options override any config file. |
| |
| set srcdir $optarg |
| continue |
| } |
| |
| "--str*" { # (--strace) expect trace level |
| set tracelevel $optarg |
| strace $tracelevel |
| verbose "Source Trace level is now $tracelevel" |
| continue |
| } |
| |
| "--sta*" { # (--status) exit status flag |
| # preserved for compatability, do nothing |
| continue |
| } |
| |
| "--tool_opt*" { |
| continue |
| } |
| |
| "--tool_exec*" { |
| set TOOL_EXECUTABLE $optarg |
| continue |
| } |
| |
| "--to*" { # (--tool) specify tool name |
| set tool $optarg |
| verbose "Testing $tool" |
| continue |
| } |
| |
| "--x*" { |
| set xml 1 |
| verbose "XML logging turned on" |
| continue |
| } |
| |
| "--he*" { # (--help) help text |
| usage |
| exit 0 |
| } |
| |
| "[A-Z0-9_-.]*=*" { # skip makefile style args like CC=gcc, etc... (processed in first pass) |
| continue |
| } |
| |
| default { |
| if {[info exists tool]} { |
| if { [info procs ${tool}_option_proc] ne "" } { |
| if {[${tool}_option_proc $option]} { |
| continue |
| } |
| } |
| } |
| send_error "\nIllegal Argument \"$option\"\n" |
| send_error "try \"runtest --help\" for option list\n" |
| exit 1 |
| } |
| } |
| } |
| |
| # |
| # check for a few crucial variables |
| # |
| if {![info exists tool]} { |
| send_error "WARNING: No tool specified\n" |
| set tool "" |
| } |
| |
| # |
| # initialize a few Tcl variables to something other than their default |
| # |
| if { $verbose > 2 || $log_dialog } { |
| log_user 1 |
| } else { |
| log_user 0 |
| } |
| |
| set timeout 10 |
| |
| |
| |
| # |
| # open log files |
| # |
| open_logs |
| |
| # print the config info |
| clone_output "Test run by $logname on [timestamp -format %c]" |
| if {[is3way]} { |
| clone_output "Target is $target_triplet" |
| clone_output "Host is $host_triplet" |
| clone_output "Build is $build_triplet" |
| } else { |
| if {[isnative]} { |
| clone_output "Native configuration is $target_triplet" |
| } else { |
| clone_output "Target is $target_triplet" |
| clone_output "Host is $host_triplet" |
| } |
| } |
| |
| clone_output "\n\t\t=== $tool tests ===\n" |
| |
| # |
| # Look for the generic board configuration file. It searches in several |
| # places: $libdir/config, $libdir/../config, and $boards_dir. |
| # |
| |
| proc load_generic_config { name } { |
| global libdir |
| global board |
| global board_info |
| global boards_dir |
| global board_type |
| |
| if {[info exists board]} { |
| if {![info exists board_info($board,generic_name)]} { |
| set board_info($board,generic_name) $name |
| } |
| } |
| |
| if {[info exists board_type]} { |
| set type "for $board_type" |
| } else { |
| set type "" |
| } |
| |
| set dirlist [concat $libdir/config [file dirname $libdir]/config $boards_dir] |
| set result [search_and_load_file "generic interface file $type" $name.exp $dirlist] |
| |
| return $result |
| } |
| |
| # |
| # Load the tool-specific target description. |
| # |
| proc load_config { args } { |
| global testsuitedir |
| |
| set found 0 |
| |
| return [search_and_load_file "tool-and-target-specific interface file" $args [list $testsuitedir/config $testsuitedir/../config $testsuitedir/../../config $testsuitedir/../../../config]] |
| } |
| |
| # |
| # Find the files that set up the configuration for the target. There |
| # are assumed to be two of them; one defines a basic set of |
| # functionality for the target that can be used by all tool |
| # testsuites, and the other defines any necessary tool-specific |
| # functionality. These files are loaded via load_config. |
| # |
| # These used to all be named $target_abbrev-$tool.exp, but as the |
| # $tool variable goes away, it's now just $target_abbrev.exp. First |
| # we look for a file named with both the abbrev and the tool names. |
| # Then we look for one named with just the abbrev name. Finally, we |
| # look for a file called default, which is the default actions, as |
| # some tools could be purely host based. Unknown is mostly for error |
| # trapping. |
| # |
| |
| proc load_tool_target_config { name } { |
| global target_os libdir testsuitedir |
| |
| set found [load_config $name.exp $target_os.exp "default.exp" "unknown.exp"] |
| |
| if { $found == 0 } { |
| send_error "WARNING: Couldn't find tool config file for $name, using default.\n" |
| # If we can't load the tool init file, this must be a simple natively hosted |
| # test suite, so we use the default procs for Unix. |
| if { [search_and_load_file "library file" default.exp [list $libdir $libdir/config [file dirname [file dirname $testsuitedir]]/dejagnu/config $testsuitedir/config . [file dirname [file dirname [file dirname $testsuitedir]]]/dejagnu/config]] == 0 } { |
| send_error "ERROR: Couldn't find default tool init file.\n" |
| exit 1 |
| } |
| } |
| } |
| |
| # |
| # Find the file that describes the machine specified by board_name. |
| # |
| |
| proc load_board_description { board_name args } { |
| global libdir |
| global board |
| global board_info |
| global boards_dir |
| global board_type |
| |
| set dejagnu "" |
| |
| if { [llength $args] > 0 } { |
| set whole_name [lindex $args 0] |
| } else { |
| set whole_name $board_name |
| } |
| |
| set board_info($whole_name,name) $whole_name |
| if {![info exists board]} { |
| set board $whole_name |
| set board_set 1 |
| } else { |
| set board_set 0 |
| } |
| |
| set dirlist {} |
| if { [llength $args] > 1 } { |
| set suffix [lindex $args 1] |
| if { $suffix ne "" } { |
| foreach x $boards_dir { |
| lappend dirlist $x/$suffix |
| } |
| lappend dirlist $libdir/baseboards/$suffix |
| } |
| } |
| set dirlist [concat $dirlist $boards_dir] |
| lappend dirlist $libdir/baseboards |
| verbose "dirlist is $dirlist" |
| if {[info exists board_type]} { |
| set type "for $board_type" |
| } else { |
| set type "" |
| } |
| if {![info exists board_info($whole_name,isremote)]} { |
| set board_info($whole_name,isremote) 1 |
| if {[info exists board_type]} { |
| if { $board_type eq "build" } { |
| set board_info($whole_name,isremote) 0 |
| } |
| } |
| if { $board_name eq [get_local_hostname] } { |
| set board_info($whole_name,isremote) 0 |
| } |
| } |
| search_and_load_file "standard board description file $type" standard.exp $dirlist |
| set found [search_and_load_file "board description file $type" $board_name.exp $dirlist] |
| if { $board_set != 0 } { |
| unset board |
| } |
| |
| return $found |
| } |
| |
| # |
| # Find the base-level file that describes the machine specified by args. We |
| # only look in one directory, $libdir/baseboards. |
| # |
| |
| proc load_base_board_description { board_name } { |
| global libdir |
| global board |
| global board_info |
| global board_type |
| |
| set board_set 0 |
| set board_info($board_name,name) $board_name |
| if {![info exists board]} { |
| set board $board_name |
| set board_set 1 |
| } |
| if {[info exists board_type]} { |
| set type "for $board_type" |
| } else { |
| set type "" |
| } |
| if {![info exists board_info($board_name,isremote)]} { |
| set board_info($board_name,isremote) 1 |
| if {[info exists board_type]} { |
| if { $board_type eq "build" } { |
| set board_info($board_name,isremote) 0 |
| } |
| } |
| } |
| |
| if { $board_name eq [get_local_hostname] } { |
| set board_info($board_name,isremote) 0 |
| } |
| set found [search_and_load_file "board description file $type" $board_name.exp [list $libdir/baseboards]] |
| if { $board_set != 0 } { |
| unset board |
| } |
| |
| return $found |
| } |
| |
| # |
| # Source the testcase in TEST_FILE_NAME. |
| # |
| |
| proc runtest { test_file_name } { |
| global prms_id |
| global bug_id |
| global test_result |
| global errcnt warncnt |
| global errorCode |
| global errorInfo |
| global tool |
| global testdir |
| |
| clone_output "Running $test_file_name ..." |
| set prms_id 0 |
| set bug_id 0 |
| set errcnt 0 |
| set warncnt 0 |
| set test_result "" |
| |
| # set testdir so testsuite file -test has a starting point |
| set testdir [file dirname $test_file_name] |
| |
| if {[file exists $test_file_name]} { |
| set timestart [timestamp] |
| |
| if {[info exists tool]} { |
| if { [info procs ${tool}_init] ne "" } { |
| ${tool}_init $test_file_name |
| } |
| } |
| |
| if { [catch "uplevel #0 source $test_file_name" msg] == 1 } { |
| # If we have a Tcl error, propagate the exit status so |
| # that 'make' (if it invokes runtest) notices the error. |
| global exit_status exit_error |
| # exit error is set by the --status command line option |
| if { $exit_status == 0 } { |
| set exit_status 2 |
| } |
| set new_error [list $test_file_name $msg] |
| # We can't call `perror' here, it resets `errorInfo' |
| # before we want to look at it. Also remember that perror |
| # increments `errcnt'. If we do call perror we'd have to |
| # reset errcnt afterwards. |
| clone_output "ERROR: tcl error sourcing $test_file_name." |
| if {[info exists errorCode]} { |
| clone_output "ERROR: tcl error code $errorCode" |
| lappend new_error $errorCode |
| } else { |
| lappend new_error [list] |
| } |
| if {[info exists errorInfo]} { |
| clone_output "ERROR: $errorInfo" |
| lappend new_error $errorInfo |
| unset errorInfo |
| } else { |
| lappend new_error [list] |
| } |
| lappend ::dejagnu::error::list $new_error |
| unresolved "testcase '$test_file_name' aborted due to Tcl error" |
| } |
| |
| if {[info exists tool]} { |
| if { [info procs ${tool}_finish] ne "" } { |
| ${tool}_finish |
| } |
| } |
| set timeend [timestamp] |
| set timediff [expr {$timeend - $timestart}] |
| verbose -log "testcase $test_file_name completed in $timediff seconds" 4 |
| } else { |
| # This should never happen, but maybe if the file got removed |
| # between the `find' above and here. |
| perror "$test_file_name does not exist." 0 |
| } |
| } |
| |
| # Trap some signals so we know what's happening. These replace the previous |
| # ones because we've now loaded the library stuff. |
| # |
| if {![exp_debug]} { |
| foreach sig {{SIGINT {interrupted by user} 130} \ |
| {SIGQUIT {interrupted by user} 131} \ |
| {SIGTERM {terminated} 143}} { |
| set signal [lindex $sig 0] |
| set str [lindex $sig 1] |
| set code [lindex $sig 2] |
| trap "send_error \"got a \[trap -name\] signal, $str \\n\"; set exit_status $code; log_and_exit;" $signal |
| verbose "setting trap for $signal to $str" 1 |
| } |
| unset signal str sig |
| } |
| |
| # |
| # Given a list of targets, process any iterative lists. |
| # |
| proc process_target_variants { target_list } { |
| set result {} |
| foreach x $target_list { |
| if {[regexp "\\(" $x]} { |
| regsub {^.*\(([^()]*)\)$} $x {\1} variant_list |
| regsub {\([^(]*$} $x "" x |
| set list [process_target_variants $x] |
| set result {} |
| foreach x $list { |
| set result [concat $result [iterate_target_variants $x [split $variant_list ","]]] |
| } |
| } elseif {[regexp "\{" $x]} { |
| regsub "^.*\{(\[^\{\}\]*)\}$" $x {\1} variant_list |
| regsub "\{\[^\{\]*$" $x "" x |
| set list [process_target_variants $x] |
| foreach x $list { |
| foreach i [split $variant_list ","] { |
| set name $x |
| if { $i ne "" } { |
| append name "/" $i |
| } |
| lappend result $name |
| } |
| } |
| } else { |
| lappend result $x |
| } |
| } |
| return $result |
| } |
| |
| proc iterate_target_variants { target variants } { |
| return [iterate_target_variants_two $target $target $variants] |
| } |
| |
| # |
| # Given a list of variants, produce the list of all possible combinations. |
| # |
| proc iterate_target_variants_two { orig_target target variants } { |
| |
| if { [llength $variants] == 0 } { |
| return [list $target] |
| } else { |
| if { [llength $variants] > 1 } { |
| set result [iterate_target_variants_two $orig_target $target [lrange $variants 1 end]] |
| } else { |
| if { $target ne $orig_target } { |
| set result [list $target] |
| } else { |
| set result {} |
| } |
| } |
| if { [lindex $variants 0] ne "" } { |
| append target "/" [lindex $variants 0] |
| return [concat $result [iterate_target_variants_two $orig_target $target [lrange $variants 1 end]]] |
| } else { |
| return [concat $result $target] |
| } |
| } |
| } |
| |
| setup_build_hook [get_local_hostname] |
| |
| if {[info exists host_board]} { |
| setup_host_hook $host_board |
| } else { |
| set hb [get_local_hostname] |
| if { $hb ne "" } { |
| setup_host_hook $hb |
| } |
| } |
| |
| # |
| # main test execution loop |
| # |
| |
| if {[info exists errorInfo]} { |
| unset errorInfo |
| } |
| |
| |
| # make sure we have only single path delimiters |
| regsub -all {([^/])//*} $srcdir {\1/} srcdir |
| regsub -all {([^/])//*} $objdir {\1/} objdir |
| regsub -all {([^/])//*} $testsuitedir {\1/} testsuitedir |
| regsub -all {([^/])//*} $testbuilddir {\1/} testbuilddir |
| |
| if {![info exists target_list]} { |
| # Make sure there is at least one target machine. It's probably a Unix box, |
| # but that's just a guess. |
| set target_list { "unix" } |
| } else { |
| verbose "target list is $target_list" |
| } |
| |
| # |
| # Iterate through the list of targets. |
| # |
| global current_target |
| |
| set target_list [process_target_variants $target_list] |
| |
| set target_count [llength $target_list] |
| |
| clone_output "Schedule of variations:" |
| foreach current_target $target_list { |
| clone_output " $current_target" |
| } |
| clone_output "" |
| |
| |
| foreach current_target $target_list { |
| verbose "target is $current_target" |
| set current_target_name $current_target |
| set tlist [split $current_target /] |
| set current_target [lindex $tlist 0] |
| set board_variant_list [lrange $tlist 1 end] |
| |
| # Set the counts for this target to 0. |
| reset_vars |
| clone_output "Running target $current_target_name" |
| |
| setup_target_hook $current_target_name $current_target |
| |
| # If multiple passes requested, set them up. Otherwise prepare just one. |
| # The format of `MULTIPASS' is a list of elements containing |
| # "{ name var1=value1 ... }" where `name' is a generic name for the pass and |
| # currently has no other meaning. |
| |
| global env |
| |
| if { [info exists MULTIPASS] } { |
| set multipass $MULTIPASS |
| } |
| if { $multipass eq "" } { |
| set multipass { "" } |
| } |
| |
| # If PASS is specified, we want to run only the tests specified. |
| # Its value should be a number or a list of numbers that specify |
| # the passes that we want to run. |
| if {[info exists PASS]} { |
| set pass $PASS |
| } else { |
| set pass "" |
| } |
| |
| if {$pass ne ""} { |
| set passes [list] |
| foreach p $pass { |
| foreach multipass_elem $multipass { |
| set multipass_name [lindex $multipass_elem 0] |
| if {$p == $multipass_name} { |
| lappend passes $multipass_elem |
| break |
| } |
| } |
| } |
| set multipass $passes |
| } |
| |
| foreach pass $multipass { |
| |
| # multipass_name is set for `record_test' to use (see framework.exp). |
| if { [lindex $pass 0] ne "" } { |
| set multipass_name [lindex $pass 0] |
| clone_output "Running pass `$multipass_name' ..." |
| } else { |
| set multipass_name "" |
| } |
| set restore "" |
| foreach varval [lrange $pass 1 end] { |
| set tmp [string first "=" $varval] |
| set var [string range $varval 0 [expr {$tmp - 1}]] |
| # Save previous value. |
| if {[info exists $var]} { |
| lappend restore "$var [list [eval concat \$$var]]" |
| } else { |
| lappend restore $var |
| } |
| # Handle "CFLAGS=$CFLAGS foo". |
| eval set $var \[string range \"$varval\" [expr {$tmp + 1}] end\] |
| verbose "$var is now [eval concat \$$var]" |
| unset tmp var |
| } |
| |
| # look for the top level testsuites. if $tool doesn't |
| # exist and there are no subdirectories in $testsuitedir, then |
| # we print a warning and default to srcdir. |
| set test_top_dirs [lsort [getdirs -all $testsuitedir $tool*]] |
| if { $test_top_dirs eq "" } { |
| send_error "WARNING: could not find testsuite; trying $srcdir.\n" |
| set test_top_dirs [list $srcdir] |
| } else { |
| # JYG: |
| # DejaGNU's notion of test tree and test files is very |
| # general: |
| # given $testsuitedir and $tool, any subdirectory (at any |
| # level deep) with the "$tool" prefix starts a test tree |
| # given a test tree, any *.exp file underneath (at any |
| # level deep) is a test file. |
| # |
| # For test tree layouts with $tool prefix on |
| # both a parent and a child directory, we need to eliminate |
| # the child directory entry from test_top_dirs list. |
| # e.g. gdb.hp/gdb.base-hp/ would result in two entries |
| # in the list: gdb.hp, gdb.hp/gdb.base-hp. |
| # If the latter not eliminated, test files under |
| # gdb.hp/gdb.base-hp would be run twice (since test files |
| # are gathered from all sub-directories underneath a |
| # directory). |
| # |
| # Since $tool may be g++, etc. which could confuse |
| # regexp, we cannot do the simpler test: |
| # ... |
| # if [regexp "$testsuitedir/.*$tool.*/.*$tool.*" $dir] |
| # ... |
| # instead, we rely on the fact that test_top_dirs is |
| # a sorted list of entries, and any entry that contains |
| # the previous valid test top dir entry in its own pathname |
| # must be excluded. |
| |
| set temp_top_dirs [list] |
| set prev_dir "" |
| foreach dir $test_top_dirs { |
| if { $prev_dir eq "" |
| || [string first $prev_dir/ $dir] == -1 } { |
| # the first top dir entry, or an entry that |
| # does not share the previous entry's entire |
| # pathname, record it as a valid top dir entry. |
| # |
| lappend temp_top_dirs $dir |
| set prev_dir $dir |
| } |
| } |
| set test_top_dirs $temp_top_dirs |
| } |
| verbose "Top level testsuite dirs are $test_top_dirs" 2 |
| set testlist "" |
| if {[array exists all_runtests]} { |
| foreach x [array names all_runtests] { |
| verbose "trying to glob $testsuitedir/$x" 2 |
| set s [glob -nocomplain $testsuitedir/$x] |
| if { $s ne "" } { |
| set testlist [concat $testlist $s] |
| } |
| } |
| } |
| # |
| # If we have a list of tests, run all of them. |
| # |
| if { $testlist ne "" } { |
| foreach test_name $testlist { |
| if { $ignoretests ne "" } { |
| if { 0 <= [lsearch $ignoretests [file tail $test_name]]} { |
| continue |
| } |
| } |
| |
| # set subdir to the tail of the dirname after $srcdir, |
| # for the driver files that want it. XXX this is silly. |
| # drivers should get a single var, not $srcdir/$subdir |
| set subdir [relative_filename $srcdir \ |
| [file dirname $test_name]] |
| |
| # XXX not the right thing to do. |
| set runtests [list [file tail $test_name] ""] |
| |
| runtest $test_name |
| } |
| } else { |
| # |
| # Go digging for tests. |
| # |
| foreach dir $test_top_dirs { |
| if { $dir ne $testsuitedir } { |
| # Ignore this directory if is a directory to be |
| # ignored. |
| if {[info exists ignoredirs] && $ignoredirs ne ""} { |
| set found 0 |
| foreach directory $ignoredirs { |
| if {[string match *$directory* $dir]} { |
| set found 1 |
| break |
| } |
| } |
| if { $found } { |
| continue |
| } |
| } |
| |
| # Run the test if dir_to_run was specified as a |
| # value (for example in MULTIPASS) and the test |
| # directory matches that directory. |
| if {[info exists dir_to_run] && $dir_to_run ne ""} { |
| # JYG: dir_to_run might be a space delimited list |
| # of directories. Look for match on each item. |
| set found 0 |
| foreach directory $dir_to_run { |
| if {[string match *$directory* $dir]} { |
| set found 1 |
| break |
| } |
| } |
| if {!$found} { |
| continue |
| } |
| } |
| |
| # Run the test if cmdline_dir_to_run was specified |
| # by the user using --directory and the test |
| # directory matches that directory |
| if {[info exists cmdline_dir_to_run] \ |
| && $cmdline_dir_to_run ne ""} { |
| # JYG: cmdline_dir_to_run might be a space delimited |
| # list of directories. Look for match on each item. |
| set found 0 |
| foreach directory $cmdline_dir_to_run { |
| # Look for a directory that ends with the |
| # provided --directory name. |
| if {[string match $directory $dir] |
| || [string match "*/$directory" $dir]} { |
| set found 1 |
| break |
| } |
| } |
| if {!$found} { |
| continue |
| } |
| } |
| |
| foreach test_name [lsort [find $dir *.exp]] { |
| if { $test_name eq "" } { |
| continue |
| } |
| # Ignore this one if asked to. |
| if { $ignoretests ne "" } { |
| if { 0 <= [lsearch $ignoretests [file tail $test_name]]} { |
| continue |
| } |
| } |
| |
| # Get the path after the $srcdir so we know |
| # the subdir we're in. |
| set subdir [relative_filename $srcdir \ |
| [file dirname $test_name]] |
| # Check to see if the range of tests is limited, |
| # set `runtests' to a list of two elements: the script name |
| # and any arguments ("" if none). |
| if {[array exists all_runtests]} { |
| verbose "searching for $test_name in [array names all_runtests]" 2 |
| if { 0 > [lsearch [array names all_runtests] [file tail $test_name]]} { |
| if { 0 > [lsearch [array names all_runtests] $test_name] } { |
| continue |
| } |
| } |
| set runtests [list [file tail $test_name] $all_runtests([file tail $test_name])] |
| } else { |
| set runtests [list [file tail $test_name] ""] |
| } |
| runtest $test_name |
| } |
| } |
| } |
| } |
| |
| # Restore the variables set by this pass. |
| foreach varval $restore { |
| if { [llength $varval] > 1 } { |
| verbose "Restoring [lindex $varval 0] to [lindex $varval 1]" 4 |
| set [lindex $varval 0] [lindex $varval 1] |
| } else { |
| verbose "Restoring [lindex $varval 0] to `unset'" 4 |
| unset -- [lindex $varval 0] |
| } |
| } |
| } |
| cleanup_target_hook $current_target |
| if { $target_count > 1 } { |
| log_summary |
| } |
| } |
| |
| log_and_exit |