TCL equivalent to Python's `if __name__ == "__main__"`
Asked Answered
B

2

5

In one executable TCL script I'm defining a variable that I'd like to import in another executable TCL script. In Python one can make a combined library and executable by using the following idiom at the bottom of one's script:

# Library

if __name__ == "__main__":
    # Executable that depends on library
    pass

Is there something equivalent for TCL? There is for Perl.

Edit: The answer given by Donal Fellows is entirely sufficient, but for completion the comments below are gathered into one final answer here:

if {[info exists ::argv0] && $::argv0 eq [file normalize [info script]]} {
    # Do the things for if the script is run as a program...
}
Bournemouth answered 17/4, 2018 at 8:54 Comment(0)
A
8

The equivalent for Tcl is to compare the ::argv0 global variable to the result of the info script command.

if {$::argv0 eq [info script]} {
    # Do the things for if the script is run as a program...
}

The ::argv0 global (technically a feature of the standard tclsh and wish shells, or anything else that calls Tcl_Main or Tk_Main at the C level) has the name of the main script, or is the empty string if there is no main script. The info script command returns the name of the file currently being evaluated, whether that's by source or because of the main shell is running it as a script. They'll be the same thing when the current script is the main script.


As mrcalvin notes in the comments below, if your library script is sometimes used in contexts where argv0 is not set (custom shells, child interpreters, embedded interpreters, some application servers, etc.) then you should add a bit more of a check first:

if {[info exists ::argv0] && $::argv0 eq [info script]} {
    # Do the things for if the script is run as a program...
}
Apocalyptic answered 17/4, 2018 at 9:44 Comment(3)
You might want to generalize to obtain if {[info exists ::argv0] && $::argv0 eq [info script]}, just to make your script does not throw up in environments that don't run Tcl_Main (e.g., NaviServer, some embedded Tcl).Polka
Thank you for the answer! I stumbled across wiki.tcl.tk/40097 which tries to [file normalize [info script]/...] in various complicated ways; is this essential?Bournemouth
It is good practise, yes, and helps avoid confusion in corner cases, when you use relative paths etc. The file dirname and file normalize mambo jumbo seems complicated only at first glance.Polka
S
1

I recently wanted this functionality to set up some unit tests for my HDL build scripts suite. This is what i ended up with for Vivado:

proc is_main_script {} {                                               ;# +1 frame
    set frame [info frame [expr [info frame] -3]]
    if {![dict exists $frame file]} {
        set command [file normalize [lindex [dict get $frame cmd] 1]]
        set script  [file normalize [info script]] 
        if {$script eq $command} {
            return 1
        } else {
            return 0
        }
    } else {
        return 0
    }
}

if {is_main_script} {                                                  ;# +1 frame 
    puts "do your thing"
}

As I consider this for test/demo i consider the main use case to be something in the line with if {is_main_script} {puts "do something"} "un nested" at the end of the file.

If a need to make it more general a dynamic handle for the frame reference -3 could probably be developed. All though this has covered all my use cases so far.

frame -3 is used as proc and if creates extra frames and to evaluate this we want to check the call before.

dict exists is used to check if file exists within the frame. This would indicate the call was from a higher hierarchical level script and would there for not be the "main_script"

The solution if {[info exists ::argv0] && $::argv0 eq [info script]} works great if run as vivado -source TCLSCRIPT.tcl but the solution above covers source TCLSCRIPT.tcl in gui or tcl mode (this is something i often se my self doing when debugging a automation tcl).

I guess this is a niche case. But since I couldn't find any other solution for this problem I wanted to leave this here.

Smalley answered 30/4, 2019 at 17:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.