blob: 1454dad44865539774dcbbcf038dcc8a518b40c7 [file]
# Copyright (C) 92, 93, 94, 95, 1996 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., 675 Mass Ave, Cambridge, MA 02139, USA.
# Please email any bugs, comments, and/or additions to this file to:
# bug-dejagnu@prep.ai.mit.edu
# This file was written by Rob Savoye. (rob@welcomehome.org)
# a hairy pattern to recognize text
set text "\[- A-Za-z0-9\.\;\"\_\:\'\`\(\)\!\#\=\+\?\&\*]"
#
# this is a collection of support procs for the target data
# structures. We use a named array, since Tcl has no real data
# structures. Here's the special index words for the array:
# Required fields are:
# name - the name of the target. (mostly for error messages) This
# should also be the string used for this target's array.
# It should also be the same as the linker script so we
# can find them dynamically.
# Optional fields are:
# ldflags - the flags required to produce a fully linked executable.
# config - the target canonical for this target. This is a regexp
# as passed to istarget or isnative.
# cflags - the flags required to produce an object file from a
# source file.
# connect - the connectmode for this target. This is for both IP and
# serial connections.
# target - the hostname of the target. This is for TCP/IP based connections,
# and is also used for version of tip that use /etc/remote.
# serial - the serial port. This is typically /dev/tty? or com?:.
# netport - the IP port.
# baud - the baud rate for a serial port connection.
# x10 - parameters for the x10 controller (used to reboot)
# fileid - the fileid or spawn id of of the connection.
# prompt - a regexp for matching the prompt.
# abbrev - abbreviation for tool init files.
# ioport - the port for I/O on dual port systems.
#
# there are three main arrays, indexed in with "target", "build", and "host".
# all other targets are indexed with a name usually based on the linker script
# like "idp", or "ex93x.ld".
#
#
# Set the elements of the target data structure
# The order of the values is name, ldflags, config, cflags, connect, target, serial,
# netport, baud, x10, fileid, prompt, abbrev, ioport.
# FIXME: I'm not entirely sure this proc is a good idea...
proc set_target_info { args } {
global target_info
set name [lindex $args 0]
# process the linker arguments
if { [llength $args] > 0 } {
set target_info($name,ldflags) [lindex $args 1]
} else {
set target_info($name,ldflags) ""
}
# process the config string
if { [llength $args] > 1 } {
set target_info($name,config) [lindex $args 2]
} else {
set target_info($name,config) ""
}
# process the compiler arguments
if { [llength $args] > 2 } {
set target_info($name,cflags) [lindex $args 3]
} else {
set target_info($name,cflags) ""
}
# process the connection mode
if { [llength $args] > 3 } {
set target_info($name,connect) [lindex $args 3]
} else {
set target_info($name,connect) ""
}
# process the target's hostname
if { [llength $args] > 4 } {
set target_info($name,target) [lindex $args 3]
} else {
set target_info($name,target) ""
}
# process the serial port
if { [llength $args] > 5 } {
set target_info($name,serial) [lindex $args 3]
} else {
set target_info($name,serial) ""
}
# process the netport
if { [llength $args] > 6 } {
set target_info($name,netport) [lindex $args 3]
} else {
set target_info($name,netport) ""
}
# process the baud
if { [llength $args] > 7 } {
set target_info($name,baud) [lindex $args 3]
} else {
set target_info($name,baud) ""
}
# process the x10 unit number.
if { [llength $args] > 8 } {
set target_info($name,x10) [lindex $args 3]
} else {
set target_info($name,x10) ""
}
# process the fileid
if { [llength $args] > 9 } {
set target_info($name,fileid) [lindex $args 3]
} else {
set target_info($name,fileid) ""
}
# process the prompt
if { [llength $args] > 10 } {
set target_info($name,prompt) [lindex $args 3]
} else {
set target_info($name,prompt) ""
}
# process the abbrev
if { [llength $args] > 10 } {
set target_info($name,connect) [lindex $args 3]
} else {
set target_info($name,connect) ""
}
# process the ioport
if { [llength $args] > 11 } {
set target_info($name,ioport) [lindex $args 3]
} else {
set target_info($name,ioport) ""
}
}
#
# Set the target connection.
#
proc push_target { name } {
pop_config target
push_config target $name
}
#
# Set the host connnection.
#
proc push_host { name } {
pop_config host
push_config host $name
}
#
# Set the config for the current host or target connection.
#
proc push_config { type name } {
global target_info
if [info exists target_info(${name},name)] {
set target_info($type,name) $name
}
if [info exists target_info(${name},ldflags)] {
set target_info($type,ldflags) $target_info(${name},ldflags)
}
if [info exists target_info(${name},config)] {
set target_info($type,config) $target_info(${name},config)
}
if [info exists target_info(${name},cflags)] {
set target_info($type,cflags) $target_info(${name},cflags)
}
if [info exists target_info(${name},connect)] {
set target_info($type,connect) $target_info(${name},connect)
}
if [info exists target_info(${name},target)] {
set target_info($type,target) $target_info(${name},target)
}
if [info exists target_info(${name},serial)] {
set target_info($type,serial) $target_info(${name},serial)
}
if [info exists target_info(${name},netport)] {
set target_info($type,netport) $target_info(${name},netport)
}
if [info exists target_info(${name},baud)] {
set target_info($type,baud) $target_info(${name},baud)
}
if [info exists target_info(${name},x10)] {
set target_info($type,x10) $target_info(${name},x10)
}
if [info exists target_info(${name},fileid)] {
set target_info($type,fileid) $target_info(${name},fileid)
}
if [info exists target_info(${name},prompt)] {
set target_info($type,prompt) $target_info(${name},prompt)
}
if [info exists target_info(${name},abbrev)] {
set target_info($type,abbrev) $target_info(${name},abbrev)
}
if [info exists target_info(${name},ioport)] {
set target_info($type,ioport) $target_info(${name},ioport)
}
}
#
# Set the current connection for target or host.
#
proc pop_config { type } {
global target_info
set target_info(${type},name) ""
set target_info(${type},ldflags) ""
set target_info(${type},config) ""
set target_info(${type},cflags) ""
set target_info(${type},connect) ""
set target_info(${type},target) ""
set target_info(${type},serial) ""
set target_info(${type},netport) ""
set target_info(${type},baud) ""
set target_info(${type},x10) ""
set target_info(${type},fileid) ""
set target_info(${type},prompt) ""
set target_info(${type},abbrev) ""
set target_info(${type},ioport) ""
}
#
# Unset the target connection.
#
proc pop_target { } {
pop_config target
}
#
# Unset the host connection.
#
proc pop_host { } {
pop_config host
}
#
# list all the configured targets.
# returns:
# "" if there are no targets.
# else it returns a list of unique names.
#
proc list_targets { } {
global target_info
if ![info exists target_info] {
return ""
}
set j ""
set targs ""
foreach i "[lsort [array names target_info]]" {
set i "[lindex [split $i ","] 0]"
if { $i == $j } {
continue
} else {
lappend targs "[lindex [split $i ","] 0]"
set j $i
}
}
return $targs
}
#
# Remove extraneous warnings we don't care about
#
proc prune_warnings { text } {
# remove the \r part of "\r\n" so we don't break all the patterns
# we want to match.
regsub -all -- "\r" $text "" text
# This is from sun4's. Do it for all machines for now.
# The "\\1" is to try to preserve a "\n" but only if necessary.
if [ishost "sparc-*-sunos"] {
regsub -all "(^|\n)(ld.so: warning:\[^\n\]*\n?)+" $text "\\1" text
}
# See Brendan for the raison d'etre of this one.
if [ishost "alpha*-*-*"] {
regsub -all "(^|\n)(/usr/(ucb|bin)/ld.*without exceptions was\[^\n\]+\n?)" $text "\\1" text
}
# Ignore these.
regsub -all "(^|\n)\[^\n\]*linker input file unused since linking not done" $text "" text
regsub -all "(^|\n)\[^\n\]*file path prefix \[^\n\]* never used" $text "" text
# It might be tempting to get carried away and delete blank lines, etc.
# Just delete *exactly* what we're ask to, and that's it.
return $text
}
#
# Invoke the compiler. This gets interesting cause the compiler may
# not be on the same machine we're running DejaGnu on.
#
proc compile { arg } {
global target_info
global comp_output
global CC
if [info exists target_info(target,cflags)] {
lappend options "$target_info(target,cflags)"
}
append options " $arg"
verbose "Invoking the compiler as $CC $options"
set comp_output [prune_warnings [execute_anywhere "$CC $options"]]
return ${comp_output}
}
#
# Invoke the archiver.
#
proc archive { arg } {
global target_info
global comp_output
global AR
if [info exists target_info(target,arflags)] {
lappend options "$target_info(target,arflags)"
}
append options "$arg"
verbose "Invoking the archiver as $AR $options"
set comp_output [prune_warnings [execute_anywhere "$AR $options"]]
return ${comp_output}
}
proc ranlib { arg } {
global target_info
global comp_output
global RANLIB
append options "$arg"
verbose "Invoking the archiver as $RANLIB $options"
set comp_output [prune_warnings [execute_anywhere "$RANLIB $options"]]
return ${comp_output}
}
#
# Link a few objects together. This gets interesting cause the
# objects may not be on the same machine we're running DejaGnu on.
#
proc link_objects { arg } {
global target_info
global comp_output
global LD
set options "$arg"
if [info exists target_info(target,ldlags)] {
lappend options "$target_info(target,ldlags)"
}
set comp_output [execute_anywhere "$LD $args"]
return [ prune_warnings $comp_output]
}
#
# Remotely execute something. This gets fun cause we can't expect an
# Unix machine on the other end. We'll use expect instead so we can
# connect using $connectmode. This is really designed for executing
# the tools to be tested, rather than the test cases.
#
proc execute_anywhere { cmdline } {
global exec_output
global target_info
if ![info exists target_info(current,prompt)] {
set prompt ""
} else {
set prompt $target_info(current,prompt)
}
# if we're running stuff that's hosted on the same machine
if ![is3way] {
verbose -log "Executing on local host: ${cmdline}" 2
set status [catch "exec ${cmdline}" exec_output]
if ![string match "" ${exec_output}] {
# FIXME: This should be done below, after `else'.
verbose -log -- "${exec_output}" 2
}
return ${exec_output}
} else {
verbose -log "Executing on remote host: ${cmdline}" 2
# open the connection
verbose "Connecting to remote host" 2
set shellid [remote_open "host"]
if { $shellid < 0 } {
perror "Can't open connection to remote host"
return REMOTERROR
}
# stty -echo
send -i $shellid "echo START ; $cmdline ; echo END\r\n"
expect {
-i $shellid "echo START \; $cmdline \; echo END" {
}
default {
warning "Never got command echo"
}
}
expect {
-i $shellid "START" {
exp_continue
}
-i $shellid "END" {
regsub -all "\]" $expect_out(buffer) "" exec_output
regsub "END" $exec_output "" exec_output
} default {
set exec_output $i
}
}
}
if [info exists exec_output] {
verbose "EXEC_OUTPUT = \"$exec_output\"" 2
}
# stty echo
# close the connection
remote_close $shellid
if [info exists exec_output] {
return $exec_output
} else {
return REMOTERROR
}
}
#
# Get something resembling a prompt We can't grab more
# than the last word cause we have no real idea how long
# the prompt is. We also get the full prompt, but it's
# kinda useless as it might contain command numbers or
# paths that change. If we can't return a prompt, return
# null. so at least other patterns won't break.
#
proc getprompt { shellid } {
global spawn_id
if { $shellid < 0 } {
perror "Invalid spawn id"
return ""
}
set tries 0
set text ""
while { $tries <=3 } {
verbose "Trying to get the remote host's prompt"
send -i $shellid "ACK\r\n"
expect {
-i $shellid -re "Kerberos rcmd failed.*$" {
perror "Need to kinit"
return ""
}
-i $shellid -re "$text*\[\r\n\]*" {
return [lindex [split $expect_out(buffer) "\r\n"] 5]
break
}
-i $shellid -re "Terminal type is.*tty.*\>" {
return [lindex [split $expect_out(buffer) "\r\n"] 5]
break
}
-i $shellid "" {
warning "No prompt"
}
-i $shellid timeout {
perror "Couldn't sync with the remote system"
}
-i $shellid eof {
perror "Got EOF instead of a prompt"
}
}
incr tries
}
# see if we maxed out on errors
if { $tries >= 3 } {
warning "Couldn't get the prompt"
return ""
}
}
#
#
#
proc make { args } {
perror "Unimplemented"
}