sourcing a tcl file in itcl code at the global level - tcl

I have a tcl file which has few config procs defined. My one app is coded in itcl which needs to call these config proc for each object. I am not able to source TCL file at the global level in itcl. it allows sourcing only within public methods but this is not what i am looking for. I want to source the tcl file at once only for all objects and then use it till the program runs.
Any suggestions please?
thanks

The source command always evaluates the file's contents at the current level; it really is effectively just read the file's contents into a string and then eval that (except for a little trickery with info script). That means that if you want to source a file in the global context, you should do one of these:
# Quotes around #0 just because of Stack Overflow's highlighting
uplevel "#0" {
source thefile.tcl
}
uplevel "#0" [list source $thefile]
namespace eval :: {
source thefile.tcl
}
namespace eval :: [list source $thefile]
The versions with list are doing code generation, and will work much better when the filename is in a variable (or generated by a command like file join); if everything is literal, you're better off using the braced versions.
The versions with uplevel #0 are different from the versions with namespace eval :: in that the former goes up the stack to the global level, whereas the latter just pushes a new stack frame that you can uplevel out of. Most of the time, with sane code, the differences are very slight; use the one you prefer as both are good code.

Related

Can we create module from shared object ie '*.so' files by renaming it to '$modulename-$version.tm'?

Want to create module from shared object file x.so to avoid load command. Shared object C source code x.c defines package p with version 1 in it.
I renamed x.so to p-1.tm and added its path in ::tcl::tm::path and
used package require p 1
Its showing error that it cannot read command. It is because the main file is x.so instead of x.tcl.
Modules are always sourced. Making them include a loadable library as well requires some trickery. Here's one way. Make a Tcl script like this:
apply {{scriptname realname} {
set script [open $scriptname]
chan configure $script -eofchar \x1a
chan read $script
chan configure $script -translation binary
chan seek $script 1 current; # Reset EOF state and skip past the EOF character
set f [file tempfile filename $realname.so]
chan copy $script $f
chan close $f
chan close $script
tailcall load $filename $realname; # Used to find the _Init function
}} [info script] YourRealLibraryName
Concatenate it with an ASCII EOF char (Ctrl+Z) and then concatenate your real shared library on the end after that, saving it all as the module file. When the script is sourced, it will copy the library off the end of itself into a temporary file and load it from that.
This depends on the fact that source always configures the channel it uses to read the script to use the EOF character to act as an end-of-file marker, even on systems that wouldn't normally use it. You can then read past that and extract any payload data you want from there on. Concatenating a loadable library on the end is trivial, but it needs to be extracted elsewhere to actually be fed into load. Internally, all loading of dynamic libraries in Tcl goes through load; it's the only command that knows how to do it. We're just preparing the way for it (and real files are required by default since we're delegating the actual loading to the operating system library calls that do the job; file tempfile is perfect for this job!). Finally, we wrap everything in an apply and tailcall load at the end so that we clean away all the filename manipulation machinery is cleaned away neatly.
More sophisticated approaches using virtual filesystems are possible, but take far more code to explain. They're still just doing this sort of thing, but with more trickery hidden out of sight.

Is there a Tcl package init procedure?

Is there a way to define an initialization procedure that's automatically called when a Tcl package is loaded?
In this case, I need to parse a configuration file and set a namespace variable.
I originally had the code in the namespace, outside of a proc, but pkg_mkIndex tried to execute the code when it sourced the file and tossed an error "while sourcing". The package file sources just fine from tclsh, and I'm not sure why it wouldn't do so within pkg_mkIndex.
I can comment out the init routine for pkg_mkIndex's sake, if that's the proper way to do this, but I wondered if there's a built-in way to have init procedures executed automatically, a la C's main().
but I wondered if there's a built-in way to have init procedures
executed automatically
It is common practise to provide an initialisation script as part of your package ifneeded script, e.g.:
package ifneeded mypkg 1.0.0 "source [list [file join $dir mypkg.tcl]]; source [list [file join $dir myinit.tcl]]"
Using pkg_mkIndex turns out not particularly helpful in anything non-trivial, as it attempts to (partially) evaluate the source files with all their dependencies. Better handicraft the pkgIndex.tcl script and separate the concerns (pkg definition, pkg initialisation; see above).

I need to run tcl script with options from another tcl script

I have a tcl script drakon_gen.tcl . I am running it, from another script run.tcl like this:
source "d:\\del 3\\drakon_editor1.22\\drakon_gen.tcl"
When I run run.tcl I have following output:
This utility generates code from a .drn file.
Usage: tclsh8.5 drakon_gen.tcl <options>
Options:
-in <filename> The input filename.
-out <dir> The output directory. Optional.
Now I need to add to run.tcl options that are in the output. I tried many ways but I receive errors. What is the right way to add options?
When you source a script into a tcl interpreter, you are evaluating the script file in the context of the current interpreter. If it was written to be a standalone program you may run into problems with conflicting variables and procedures in the global namespace. One way to avoid that is to investigate the use of slave interpreters (see the interp command) to provide a separate environment for the child script.
In your specific example it looks like you just need to provide some command line arguments. These are normally provided by the argv variable which holds a list of all the command line arguments. If you define this list before sourcing the script you can feed it the required command line. eg:
set original_argv $argv
set argv [list "--optionname" "value"]
source $additional_script_filename
set argv $original_argv

How to access user-supplied command-line options in Tcl?

in the command line i m giving input as
filename option
here option starting with hyphen.
how to pass command line arguments starts with -
The list of all the arguments passed to the process hosting your Tcl interpreter can be accessed using the global variable argv, which you can iterate over and inspect.
Special packages implementing ready-made solutions for "declarative" parsing of command-line arguments exist, with cmdline being one example. Also search the wiki for alternatives.
I think, you need the cmdline package from tcllib.
Documentation is here
http://tcllib.sourceforge.net/doc/cmdline.html
Joachim

Need some explanation about package in TCL

I am having a little problem understanding the following command:
package ifneeded HelloWorld 1.0 [list source [file join $dir helloworld.tcl]]
in the pkgIndex.tcl,
I understand that when the pkgIndex.tcl is sourced and for example, we package require HelloWorld 1.0 , the helloworld.tcl will be sourced. I dont understand the list command...
The package ifneeded command is used to register (or query) how to make a package actually become present in a Tcl interpreter. This is done by evaluating a script, which is the argument generated with list in your example. Let's deconstruct it.
package ifneeded HelloWorld 1.0 [list source [file join $dir helloworld.tcl]]
---------------- ========== --- =============================================
command name package ver how to make it present,
name result of [list ...]
So far, so good. Now, a little aside: the list command is not just used for making lists, but it also makes guaranteed-substitution-free commands. That is, its result is a scrip that consists of an invocation of the command with its arguments, exactly as they were when they went into the list command.
This means that we're producing a script that is source somefilename, where somefilename is the result of the file join. In other words, you're getting almost the same thing as:
package ifneeded HelloWorld 1.0 "source $dir/helloworld.tcl"
Except that there is no assumption that the filename separator is / (that's formally a feature of the OS, not of Tcl, and file join knows about the difference) and it is safe if $dir happens to contain a space or other metacharacters (rather more common than you might hope).
What is $dir? Well, it's a special feature of pkgIndex.tcl scripts that they are (normally) evaluated in a context that sets the dir variable to the absolute name of the directory that contains the pkgIndex.tcl script. (You mustn't make assumptions about the current directory at this point; that belongs to the user of the main Tcl program, not to the package author.) This makes it enormously easier to relocate a package, as you can place all its component files relative to the one script and just move the whole lot in one chunk.
The package ifneeded command expects the following inputs:
package ifneeded package version ?script?
You can see that in your case, the package is HelloWorld, and the version is 1.0. Finally, the script is [list source [file join $dir helloworld.tcl]]. The reason list is used is that the script parameter expects a list.
The package ifneeded command expects a script as its last argument. A script is expected (in a common sense) to be well-formed, that is, to be parsable by the Tcl parser.
In this case of a rather standard pkgIndex.tcl, the thing to ensure is: no matter what the "dir" variable contains at the time the code from that pkgIndex.tcl is processed, the script should be constructed in such a way, that later the Tcl parser sees in it the source command with exactly one argument — no matter if $dir expanded to contain whitespace or funky characters like { etc.
Enter the list command. Here, it's used to construct a list of two elements: the string "source" and a string containing a file name (to serve as the sole argument to that source command). Now, when that list is interpreted as a script (a string), Tcl ensures that string representation contains all the needed quoting to remove any ambiguity about whitespace etc.
This ensures when the parser later interprets our constructed script, the source command in it will receive exactly one argument.
You can read much more of better written information on using list to prevent quoting issues here.