target.exp   [plain text]


# 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"
}