source: trunk/lang/tcl/scripts/exec.tcl @ 1097

Last change on this file since 1097 was 1018, checked in by gah, 16 years ago

Massive changes: New directory/file layout

File size: 4.2 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: exec - simple way to exec with streaming output
3#
4#  This utility makes it easy to exec another tool and get streaming
5#  output from stdout and stderr.  Any stderr messages are specially
6#  encoded with the =RAPPTURE-ERROR=> tag, so they show up in red
7#  in the execution output.
8# ======================================================================
9#  AUTHOR:  Michael McLennan, Purdue University
10#  Copyright (c) 2004-2005  Purdue Research Foundation
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15package require BLT
16
17namespace eval Rappture { # forward declaration }
18
19# ----------------------------------------------------------------------
20# USAGE: exec <arg> <arg>...
21#
22# This utility acts like the standard Tcl "exec" command, but it also
23# streams stdout and stderr messages while the command is executing,
24# instead of waiting till the end.  Any error messages are prefixed
25# with the special =RAPPTURE-ERROR=> tag, so they are recognized as
26# errors within Rappture.
27# ----------------------------------------------------------------------
28proc Rappture::exec {args} {
29    variable execout
30    variable execctl
31
32    set execout(output) ""
33    set execout(channel) ""
34    set execout(extra) ""
35    set execctl ""
36
37    set status [catch {eval blt::bgexec ::Rappture::execctl \
38        -keepnewline yes \
39        -killsignal SIGTERM \
40        -onoutput {{::Rappture::_exec_out stdout}} \
41        -onerror {{::Rappture::_exec_out stderr}} \
42        $args} result]
43
44    # add any extra stuff pending from the last stdout/stderr change
45    append execout(output) $execout(extra)
46
47    if {$status != 0} {
48        if {[regexp {^KILLED} $execctl] && [llength $execctl] == 4} {
49            set reason [lindex $execctl end]
50            set result "job killed: $reason"
51        }
52        error $result
53    }
54    return $execout(output)
55}
56
57# ----------------------------------------------------------------------
58# USAGE: _exec_out <channel> <message>
59#
60# Called automatically whenever output comes in from the Rappture::exec
61# utility.  Streams the output to stdout and adds it to the "execout"
62# variable, so it can be returned as one large string at the end of
63# the exec.
64# ----------------------------------------------------------------------
65proc Rappture::_exec_out {channel message} {
66    variable execout
67
68    #
69    # If this message is being written to the stderr channel, then
70    # add the =RAPPTURE-ERROR=> prefix to each line.
71    #
72    if {$channel == "stderr"} {
73        set newmesg ""
74        foreach line [split $message \n] {
75            append newmesg "=RAPPTURE-ERROR=>$line\n"
76        }
77        set message $newmesg
78    }
79
80    #
81    # If this message is coming in on the same channel as the
82    # last, then fine, add it on.  But if it's coming in on a
83    # different channel, we must make sure that we're at a good
84    # breakpoint.  If there's not a line break at the end of the
85    # current output, then add the extra stuff onto a buffer
86    # that we will merge in later once we get to a good point.
87    #
88    if {$execout(channel) == ""} {
89        set execout(channel) $channel
90    }
91
92    set ready [expr {[string length $execout(output)] == 0
93        || [string index $execout(output) end] == "\n"}]
94
95    if {$channel != $execout(channel)} {
96        if {$ready} {
97            # changing channels...
98            if {[string length $execout(extra)] > 0} {
99                # any extra stuff on this new channel? put it out now
100                puts -nonewline $execout(extra)
101                append execout(output) $execout(extra)
102                set execout(extra) ""
103            }
104            puts -nonewline $message
105            append execout(output) $message
106            set execout(channel) $channel
107        } else {
108            # not ready to change channels -- keep this for later
109            append execout(extra) $message
110        }
111    } else {
112        # no need to change channels -- keep printing
113        puts -nonewline $message
114        append execout(output) $message
115    }
116}
Note: See TracBrowser for help on using the repository browser.