Eg. get_abc, get_xyz command which generate collection, set_pqr receives collection are custom commands and following is tcl file
#Tcl file start
get_abc
get_xyz
set_pqr -object [get_abc]
#Tcl file end
Now the requirement is we need to skip the set_pqr command and this tcl file is big and read only, we can't change it.
Now we added this handling in set_pqr command callback to skip processing, but still get_abc command in same line get processed which is anyway to be discarded and not needed when the collection goes to set_pqr. Also we can't skip get_abc from software because its valid and can be used at other places.
Does tcl provides capability to skip the full line of set_pqr
The only ways to make Tcl do what you want are to not use standard source to load the command. Let's write our own version instead:
proc skippingSource {filename} {
set f [open $filename]
set code [read $f]
close $f
# Comment out the offending lines; this is a cheap hack BTW
set code [regsub -all -line {^set_pqr } $code "#set_pqr "]
# Override [info script] until this procedure returns
info script $filename
uplevel 1 $code
}
Now we just need to use skippingSource instead of source. We can of course call it directly, the easiest method, or we can substitute in for source:
# Keep the original in case we want to put it back
rename source originalSource
rename skippingSource source
You redefine set_pqr to a proc that does nothing:
# Back up original set_pqr
rename set_pqr set_pqr_original
# Make new proc that does nothing
proc set_pqr {args} {}
Now you can source the file and each seq_pqr command will do nothing (and takes an arbitrary number of arguments).
When you need the original command back again:
rename set_pqr_original set_pqr
This whole thing could be wrapped up in one proc too:
proc source_with_skip {filename "skip_commands {}"} {
foreach command $skip_commands {
rename $command ${command}_original
}
source $filename
foreach command $skip_commands {
rename ${command}_original $command
}
}
% source my_file.tcl set_pqr
Note that the above may not be sufficient if the procs are in different namespaces than the current namespace.
Related
I am trying to run a tcl script through .bat file. I want to read some cmd arguments in the tcl script. Below is my code:
Command to run:
D:\Cadence\Sigrity2021.1\tools\bin\PowerSI.exe -tcl abcd.tcl %new_var%.spd %new_file_name%
Below is how I am trying to read the variable in the tcl file:
sigrity::open document [lindex $argv 0] {!}
It open up the Cadence Sigrity, but I see the below error:
How do I read cmd argument in tcl?
If you have no other way to do it that you can find (and it sounds like that might be the case) then you can fake it by writing a helper file with content like this, filling in the real arguments in the appropriate places:
# Name of script to call
set ::argv0 "abcd.tcl"
# Arguments to pass
set ::argv {}
lappend ::argv "%new_var%.spd"
lappend ::argv "%new_file_name%"
# Number of arguments (rarely used)
set ::argc [llength $::argv]
# Do the call
source $::argv0
Then you can pass that file to PowerSI and it will set things up and chain to the real file. It's messy, but practical.
If you're writing this from Tcl, use the list command to do the quoting of the strings (instead of putting them in double quotes) as it will do exactly the right thing for you. If you're writing the file from another language, you'll want to make sure you put backslashes in before \, ", $ and [ characters. The fiddlyness of doing that depends on your language.
As an easy example I just want to loop thorugh opening/closing files and use a variable to create another variable. In PERL this is pretty easy but I cnat figure it out in TCL
set gsrs ""
lappend gsrs "sir"
lappend gsrs "dir"
foreach gsr $gsrs {
set file "sdrv/icc/instance_toggle_overwrite.$gsr.txt"
puts "*** I : Generating $file"
set tempGSR gsr
puts "$$tempGSR" # would like output to be value of $gsr
set $gsr [open $file "w"] # normally you would not use a variable here for filename setting
close $$gsr
}
Double-dereferencing is usually not recommended, as it leads to complex code that is quite hard to maintain. However, if you insist on doing it then use set with one argument to do it:
puts [set $tempGSR]
Usually, thinking about using this sort of thing is a sign that either upvar (possibly upvar 0) or an array should be used instead.
When the input file is corrupt, the input file is held open by my code and I am unable to delete it. To delete it, I might close the command line which ran my code so the file is closed automatically then I am able to delete.
But what is the command to close a file opened via file readable?
I'm not sure if this is the best way to do it, but I think you could use something like this:
proc close_all_files {} {
foreach channel [file channels "file*"] {
close $channel
}
}
Then call close_all_files when you need to close files that have been previously opened.
Warning: this will close absolutely all files opened within the script. There are not a lot of options if you don't know the file identifier that was created by file readable, unless you modify that proc to add the file identifier to an accessible list outside the proc, the easiest example being to use a global list:
proc file'readable name {
global filesIds
set rc [catch {open $name} fp]
if {[string match "file*" $fp]} {
lappend filesIds $fp
}
if {$rc==0} {close $fp}
expr {$rc==0}
}
And then if you know the file order for which you used file readable, you can pick which one you need to close.
I want to find a way to return the name of a library of a certain path in a VHDL Design in Modelsim.
Given a VHDL Design with a path like "/mega_tb/D0". This is compiled in a library that is NOT 'work', say "libnwork".
I can of course take a look in my 'do' file to get the correct lib name. Or I can search in ModelSim's Library tab. But I want to have or create a modelsim command which I can later use in a Tcl script, to get the correct library name.
One of the easiest ways to find something in a Tcl script file – which is all a Modelsim “do” file is — is to evaluate it. Tcl's very good at that. Of course, you don't want to have the commands do all the conventional things. Instead, we'll evaluate in a context where we can make everything do nothing except for the command that produces the information we want:
# Set up our evaluation context, 'worker'
interp create worker -safe
interp eval worker {proc unknown args {}}; # Our do-nothing handler
interp alias worker theInterestingCommand {} ourHandler
proc ourHandler args {
puts "We were called with: $args"
}
# Parse the file!
set f [open /the/file.tcl]
interp eval worker [read $f]
# Clean up
close $f
interp delete worker
Now you just have to make theInterestingCommand have the right name and extract the interesting information from the arguments. Which should be relatively easy…
Te only way I've found is to use the command
write report -tcl
This prints a long list where I have search for the lib names with regexps.
Something like
set data [ write report -tcl]
foreach_regexp { _ type lib entity} $data{
if {$type == "Entity" && $entity == [entity_of_path /mega_tb/D0] } {
....
}
}
Where I of course had to define my "foreach_regexp" procedure and my "entity_of_path" procedure. I then can use something like regsub to extract the library name.
I am still looking for a better and easier way.
I have two tcl scripts. I want to run the second script when the first finished. How can I do it?
Depends on what do you really mean.
One way is to write a third ("master") script which would do
source /the/path/to/the/first.tcl
source /the/path/to/the/second.tcl
Another way is to just add the second call to source from the above example to the bottom of the first script.
Amendment to the first approach: if the scripts to be executed are located in the same directory as the master script, an idiomatic way to source them is
set where [file dirname [info script]]
source [file join $where first.tcl]
source [file join $where second.tcl]
This way sourcing will work no matter what the current process's directory is and where the project directory is located.
While this is generally a correct answer, because the question was not precisely formulated there are tons of ways to achieve the goal of running Tcl code from within Tcl.
I want to get into this in detail because understanding the execution of code is one major point in understanding Tcl itself.
There is source
The source command should not be confound with executing scripts in a classical way, what I think the thread starter has asked.
The source command is like the "include" command in c/perl/php.
Languages like java or python on the other hand only have "import" mechanisms.
The difference is that those languages create a internal database of available packages, who are linked to the corresponding source/binary/bytecode files. By writing a import statement, linked source or bytecode or binary files are loaded. This allows more in-depth dependency management without writing additional code.
In Tcl this can be achieved with namespaces and the package require command.
Example:
Suppose you have this source.tcl:
proc foo {bar} {puts "baz"}
set BAM "BOO"
Now, you have your "master" script like you call it. I call it "main". It has the content:
set BAM {my important data}
source source.tcl
#also the function foo can now be used because the source reads the whole script
foo {wuz}
set BAM
#will output "BOO"
The exec command
If you can live with additional overhead of starting a whole new interpreter instance you could also do:
set BAM {my important data}
exec tclsh source.tcl
#The variable BAM will not be modified. You can not use the function foo.
The eval command
The command eval can evaluate a string or a list (in Tcl everything is a string) like it would be programmed code.
You would have to load the complete source file to a string. And then use eval, to evaluate the code within a separate scope, to not overwrite stuff in your main source file.
set fp [open "somefile" r]
set code_string [read $fp]
close $fp
eval $code_string
You just need to use source to run the 2nd script.
source "/tmp/whatever.tcl"
Simplest possible working example I could find:
thufir#dur:~/NetBeansProjects/spawnTelnet/telnet$
thufir#dur:~/NetBeansProjects/spawnTelnet/telnet$ tclsh main.tcl
hello world
7
thufir#dur:~/NetBeansProjects/spawnTelnet/telnet$
thufir#dur:~/NetBeansProjects/spawnTelnet/telnet$ cat main.tcl
lappend auto_path /home/thufir/NetBeansProjects/spawnTelnet/telnet/api
package require weather 1.0
tutstack::hello
set A 3
set B 4
puts [tutstack::sum $A $B]
#puts [tutstack::hello "fred"]
thufir#dur:~/NetBeansProjects/spawnTelnet/telnet$
thufir#dur:~/NetBeansProjects/spawnTelnet/telnet$ cat api/weather.tcl
package provide weather 1.0
package require Tcl 8.5
namespace eval ::tutstack {
}
proc ::tutstack::hello {} {
puts "hello world"
}
proc ::tutstack::sum {arg1 arg2} {
set x [expr {$arg1 + $arg2}];
return $x
}
proc ::tutstack::helloWorld {arg1} {
return "hello plus arg"
}
thufir#dur:~/NetBeansProjects/spawnTelnet/telnet$
thufir#dur:~/NetBeansProjects/spawnTelnet/telnet$ cat api/pkgIndex.tcl
# Tcl package index file, version 1.1
# This file is generated by the "pkg_mkIndex" command
# and sourced either when an application starts up or
# by a "package unknown" script. It invokes the
# "package ifneeded" command to set up package-related
# information so that packages will be loaded automatically
# in response to "package require" commands. When this
# script is sourced, the variable $dir must contain the
# full path name of this file's directory.
package ifneeded weather 1.0 [list source [file join $dir weather.tcl]]
thufir#dur:~/NetBeansProjects/spawnTelnet/telnet$