This page is for Hints and Issues involved in getting MkTclApp to work properly. So, far I have been building under windows, and will look to start porting to linux soon. All these comments are about my windows experiences. -- E.T.
Recently, a friend discovered there is a Yahoo discussion group for this at http://groups.yahoo.com/group/mktclapp/
Note: much of the same issues are involved in linux. We still have to get most of our packages loaded manually, since mktclapp does not handle tcl "glob" which is used to search for all the packages to load.
Update: My tcl wrapper which is now updated for use on linux
getting some extensions going on my windows system with visual c++ I needed to add this code: (I don't know what happens on *nix yet). On windows, the mktclapp generated code that intercepts all source commands does not seem to set the name of the script as the real source would. This makes [info script] return {} which later is passed to a file directory command that returns . (dot). So, auto_path would end up with ./directory instead of c:/tcl/lib/iwidgets4.0.0/directory.
# this is needed to get bwidgets started correctly # for blt, the blt_init does this for us, but bwidgets # has no C code we can call, so we need to do this ourselves # Note, it must agree with what mktclapp thinks we have setup set dir {C:/TCL/lib/bwidget1.4.0} source {C:/TCL/lib/bwidget1.4.0/pkgIndex.tcl} # there is an error in the [info script] call, which returns a bad # value (this is used to compute the auto_path for iwidgets) # so we do it ourselves source {c:/tcl/lib/iwidgets4.0.0/iwidgets.tcl} package require Iwidgets global auto_path lappend auto_path C:/TCL/lib/iwidgets4.0.0/generic \ C:/TCL/lib/iwidgets4.0.0/scripts
to package up something for windows, you need to include the .dlls and put them in the same directory with your executable. But, if you do this when seting standalone mode to "no", you might not be able to test your program.
So, when testing don't put the .dlls (tcl83.dll, etc.) in the same directory with the executable or that will overide where some code thinks the rest of the tcl library code lives. I.E. Some scripts start a search based on what it thinks is the location of the tcl dll. If you are testing, then it will need to retrieve these files from disk, and will not find them.
To really test for standalone mode, I rename, or move the tcl directory temporarily. Otherwise you can get fooled and some things will still get read off the disk (like blt files for postscript).
But watch out. It is real easy to forget to rename it back when you want to resume working. One gotcha that bit me was that if you leave the directory renamed (ex: c:/tcl/... to c:/tclx/...) and then do a save out of xmktclapp.tcl, (i.e. it was still running before I did the rename - else it won't launch) then it will silently NOT find the library files. You will have a truncated .mta config file and your build might not reflect this (i.e. won't give any errors, but is not built correctly). Then when you run you will get some errors. Always check your .mta file to see that it does have all the files you would expect. I chased this for an hour once.
Best to install tcl in c:/tcl/... instead of c:/program files/tcl/...
this is because xmktclapp.tcl has a bug in it's other library section. It will not handle directory names with spaces.
In the below fragment in proc ReadState, the values of var and value get computed incorrectly, (the 2 commented out statements, replaced with the 2 below it to fix the problem)
proc ReadState {fn {quiet 0}} { ... some code ... foreach line [split $text \n] { if {![regexp {^## } $line]} continue if {[lindex $line 0]!="##"} continue #set var [lindex $line 1] #set value [lindex $line 2] set var [lrange $line 1 end-1] set value [lindex $line end] } ... }
Another problem is that sometimes this directory gets represented as progrm~1 or some other ugly thing and so might not match up with the long file name when mktclapp is doing a lookup.
If you get an error that simply quits with no indications (still on windows systems) sometimes you can figure it out by setting a breakpoint on the line following initerr label. Often Et_Bgerror cannot function properly (esp if you haven't got the init stuff to work yet). The interp->result is a string with the error message. Often I saw messages like: cannot find a suitable init.tcl etc.
initerr: Et_EvalF(interp,"Et_Bgerror \"%q\"", interp->result); return TCL_ERROR;
I now have blt, bwidgets, iwidgets, itk, and itcl working with mktclapp. I needed to use the version that supports data files (3.8 or greater I believe)
path.../bltCanvEps.pro path.../bltGrahp.pro path.../lang/en.rc
ex:
## Data:C:/Tcl/lib/blt2.4/bltCanvEps.pro 1 ## Data:C:/Tcl/lib/blt2.4/bltGraph.pro 1 ## Data:C:/Tcl/lib/bwidget1.4.0/lang/en.rc 1
Also libraries:
## OtherLib:C:/Tcl/lib/blt2.4 1 ## OtherLib:C:/Tcl/lib/bwidget1.4.0 1 ## OtherLib:C:/Tcl/lib/itcl3.2 1 ## OtherLib:C:/Tcl/lib/itk3.2 1 ## OtherLib:C:/Tcl/lib/iwidgets4.0.0 1 ## OtherLib:C:/Tcl/lib/iwidgets4.0.0/scripts 1
This was the reference to the version that handles long files (greater than 64k bytes). It may be possible to include a long file (w/o this patch) as a data file, since that generates static integers instead of concatonated strings.
Yes, look at https://www.tcl3d.org/poApps/html/unsupMktclapp.html . It's a patch of mktclapp 3.9. Not the newest version, but it works perfectly for me.
The main reason I get large files is that I also use visual tcl (vtcl) to build my programs. This generates large files if you include lots of widgets (it includes some vtcl library code in line). When I did this, I also had to include a wrapper around this code so I could do my workarounds before invoking the vtcl code. EX: myapp.tcl is generated by vtcl, so:
wrapper.tcl: ...do wrapper stuff ... source myapp.tcl
This was needed to get things done before all the package requires that could otherwise fail (see my first note above). Then I just told xmktclapp that wrapper.tcl was the startup script.
This is what I usually start with now. It works as a windows app (not a console app - so no m.s. dos console window shows up). I have some code there to do a complete tcl trace. Needed this to find some of the errors.
note: in prior versions of blt and/or mktclapp, the include of tk.h was not required - at least it would work (not sure why, unless blt.h or myapp.h no longer generates an include itself). If you get errors in blt.h, like this
c:\tcl\include\blt.h(57) : error C2143: syntax error : missing ')' before '*' c:\tcl\include\blt.h(57) : error C2081: 'Drawable' : name in formal parameter list illegal c:\tcl\include\blt.h(57) : error C2143: syntax error : missing '{' before '*' c:\tcl\include\blt.h(57) : error C2059: syntax error : ')'
You need to add the tk.h include as shown below:
#include <tk.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "myapp.h" #include <blt.h> int Et_Init(int argc, char **argv); // -------------------- C main if console task ------------------- int main(int argc, char **argv) { return Et_Init(argc,argv)!=TCL_OK; return 0; } // ------------------ utility - updates screen -------------------- int do_tkupdate(int z) { // optional, handy routine while (Tk_DoOneEvent(TK_ALL_EVENTS | TK_DONT_WAIT)) { z++; } return z; // ignore, I was just curious about this } // ------------------------- // debugging trace routines // ------------------------- void xxxlog(char *what,char *hex,char *name) { static FILE *io; static once = 1; if (once) { io = fopen("xxxlog.txt","w"); once = 0; } else { io = fopen("xxxlog.txt","a"); } if (io) { fprintf(io,"%-15s %8x %s\n",what,hex,name); fclose(io); } } void xxxlog2(char *what,int dec,char *name) { static FILE *io; static once = 1; if (once) { io = fopen("xxxlog.txt","w"); once = 0; } else { io = fopen("xxxlog.txt","a"); } if (io) { fprintf(io,"%-15s %8d %s\n",what,dec,name); fclose(io); } } // -------------------------- // A debugging trace callback // -------------------------- void proc( ClientData clientData, Tcl_Interp *interp, int level, char *command, Tcl_CmdProc *cmdProc, ClientData cmdClientData, int argc, char *argv[]) { char buf[400],*cc; int i,j; static FILE *io; static once = 1; if (once) { io = fopen("xxxlog.txt","w"); once = 0; } strcpy(buf,"???"); for(i = 0 ; i < 199 ; i++ ) { if (command[i] == '\0') { buf[i] = 0; break; } else if (command[i] == '\n') { buf[i] = ' '; } else { buf[i] = command[i]; } } buf[90] = 0; if (io) { fprintf(io,"%3d) %s\n",level,buf); for(j = 0 ; j < argc ; j++ ) { cc = argv[j]; strcpy(buf,"-none-"); for(i = 0 ; i < 65 ; i++ ) { if (cc[i] == '\0') { buf[i] = 0; break; } else if (cc[i] == '\n') { buf[i] = ' '; } else { buf[i] = cc[i]; } } buf[60] = 0; fprintf(io," %2d %s\n",j,buf); } fflush(io); } } // -------------------------- -------------------------- -------------------------- // -------------------------- init blt and maybe a trace ------------------------- // -------------------------- -------------------------- -------------------------- #ifdef WIN32 Blt_Init(); int Itcl_Init(); int Itk_Init(); #endif static Tcl_Interp *theinterp = {0}; int Et_AppInit(Tcl_Interp *interp){ int n; n = Blt_Init(interp); if (Itcl_Init(interp) == TCL_ERROR) { return TCL_ERROR; } if (Itk_Init(interp) == TCL_ERROR) { return TCL_ERROR; } // to setup a trace, uncomment out these #if 0 xxxlog("appinit",(char *)interp,"interp"); theinterp = interp; Tcl_CreateTrace(interp, 30, proc, (ClientData) 0); #endif return TCL_OK; } #ifdef WIN32 #include <windows.h> int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow // show state ) { char *cmdline; char *argv[2]; cmdline = GetCommandLine(); argv[0] = cmdline; Et_Init(1,argv); } #endif
This is my tcl wrapper code that lets me use blt, itcl, itk, and tkhtml as well as a package called browser from https://wiki.tcl-lang.org/2993
Note that this wrapper assumes your real tcl code is in myapp.tcl and sources it at the bottom.
################################################ #### Additional initialization for packages ################################################ ################################################ #### So we get errors reported - needs more work ################################################ proc Et_Bgerror err { catch {puts "Error in bgerror:\n$err"} if {$tcl_platform(platform) == "windows"} { catch { toplevel .baderr message .baderr.mess -text $err pack .baderr.mess -fill both -expand 1 } } else { } catch {_Et_Bgerror $err} } ################################################################ proc zputs {a} { catch {puts $a} } ################################################################ global tcl_platform # zputs "here we are at the top $tcl_platform(platform)" ###################################### set lib [file dirname [info library]] ###################################### # on windows, we link against the dll's, on linux we don't need them all # # windows: blt24.dll, itcl32.dll, itk32.dll, tcl83.dll, tk83.dll, libtkhtml.dll # # linux - libtkhtml2.0.so # # linux makefile: # ./mktclapp -f appinit.mta >myappx.c # gcc -o myapp myapp.c tcl_c_code.c -I./ -ltk -ltcl -L/usr/X11R6/lib -lX11 -lm -ldl (backslash) # /usr/local/ActiveTcl/lib/libBLT24.a /usr/local/ActiveTcl/lib/libitk3.2.a (backslash) # /usr/local/ActiveTcl/lib/libitcl3.2.a if {$tcl_platform(platform) == "windows"} { ###### need to source this for tkhtml and do the load ourselves # set dir to where the tkhtml2.0 pkgIndex file lives, then source it set dir [file join $lib tkhtml2.0] source [file join $dir pkgIndex.tcl] # find the location of the tkhtml dll, and load it (assumes in same dir as our executable) # note when we are testing, we don't have the dll in the same directory as our # program, so this will fail, but as long as we have tcl/tk installed, it will find it # through the file system. When we are strict mode and not installed, we must have # this dll in our own directory with our executable, just like the other dlls. catch { set dll [file join [file dirname [info nameofexecutable]] libtkhtml[info sharedlibextension]] load $dll TkHtml } #now set it up for use (probably not needed here) #package require Tkhtml } else { # note name is different on linux, but like windows, to be independent, we need this dll in our # directory where the executable resides set dll [file join [file dirname [info nameofexecutable]] libtkhtml2.0[info sharedlibextension]] package ifneeded Tkhtml 2.0 [list load $dll] #zputs "ifneeded set to: [package ifneeded Tkhtml 2.0 ] " } #zputs "here we are 2" ###### need to source http, and browser # set dir to where the http2.4 pkgIndex file lives, then source it set dir [file join [info library] http2.4] source [file join $dir pkgIndex.tcl] # set dir to where the browser pkgIndex file lives, then source it set dir [file join $lib browser] source [file join $dir pkgIndex.tcl] # and finally source browser itself source [file join $dir browser.tcl] # now set it up for use package require browser if {$tcl_platform(platform) == "windows"} { } else { # need message handler too for errors - this seems to crash linux ??? #package ifneeded msgcat 1.1.1 [list source /usr/local/ActiveTcl/lib/tcl8.3/msgcat1.1/msgcat.tcl] } #zputs "here we are 3" # this is needed to get bwidgets started correctly # for blt, the blt_init does this for us, but bwidgets # has no C code we can call, so we need to do this ourselves # Note, it must agree with what mktclapp thinks we have setup set dir [file join $lib bwidget1.4.0] source [file join $dir pkgIndex.tcl] #zputs "here we are 4" # there is an error in the [info script] call, which returns a bad # value (this is used to compute the auto_path for iwidgets) # so we do it ourselves if {$tcl_platform(platform) == "windows"} { set dir $lib set file [file join $dir iwidgets4.0.0 iwidgets.tcl] source $file package require Iwidgets global auto_path lappend auto_path [file join $dir iwidgets4.0.0 generic] [file join $dir iwidgets4.0.0 scripts] global blt_library set blt_library [file join $lib blt2.4] package require BLT namespace import blt::* } else { #zputs "here we are 5" #zputs " source this =[file join $lib iwidgets4.0.0 iwidgets.tcl]=" source [file join $lib iwidgets4.0.0 iwidgets.tcl] package require Iwidgets global auto_path lappend auto_path [file join $lib iwidgets4.0.0 generic] [file join $lib iwidgets4.0.0 scripts] #zputs stderr "---auto path \n\n $auto_path \n\n" #update #after 5000 } if {0} { # some test code we might want to use instead of myapp.tcl #tabset .ts -relief sunken -borderwidth 2 #pack .ts #graph .g #pack .g #update #after 2000 #::iwidgets::calendar .cal #pack .cal } #catch {zputs "About to source myapp.tcl \nscript= /[info script]/ \npnames =/[package names]/"} #we do the above from this file so we can get in before #myapp runs (if vtcl, we want to be ahead of it's package requires) source myapp.tcl
My updated list of "other libraries"
## OtherLib:C:/Tcl/lib/blt2.4 1 ## OtherLib:C:/Tcl/lib/browser 1 ## OtherLib:C:/Tcl/lib/bwidget1.4.0 1 ## OtherLib:C:/Tcl/lib/img1.2 1 ## OtherLib:C:/Tcl/lib/itcl3.2 1 ## OtherLib:C:/Tcl/lib/itk3.2 1 ## OtherLib:C:/Tcl/lib/iwidgets4.0.0 1 ## OtherLib:C:/Tcl/lib/iwidgets4.0.0/scripts 1 ## OtherLib:C:/Tcl/lib/tcl8.3/http2.4 1 ## OtherLib:C:/Tcl/lib/tkhtml2.0 1
My linux setup - tried to use msgcat - didn't work right
## OtherLib:/usr/local/ActiveTcl/lib/Img1.2 1 ## OtherLib:/usr/local/ActiveTcl/lib/blt2.4 1 ## OtherLib:/usr/local/ActiveTcl/lib/browser 1 ## OtherLib:/usr/local/ActiveTcl/lib/bwidget1.4.0 1 ## OtherLib:/usr/local/ActiveTcl/lib/itcl3.2 1 ## OtherLib:/usr/local/ActiveTcl/lib/itk3.2 1 ## OtherLib:/usr/local/ActiveTcl/lib/iwidgets4.0.0 1 ## OtherLib:/usr/local/ActiveTcl/lib/iwidgets4.0.0/scripts 1 ## OtherLib:/usr/local/ActiveTcl/lib/tcl8.3/http2.4 1 ## OtherLib:/usr/local/ActiveTcl/lib/tkhtml2.0 1 -i "/usr/local/ActiveTcl/lib/blt2.4/bltCanvEps.pro" -i "/usr/local/ActiveTcl/lib/blt2.4/bltGraph.pro" -i "/usr/local/ActiveTcl/lib/bwidget1.4.0/lang/en.rc"
CL records that mktclapp 3.12 does not work cleanly with 8.4, and that a specific symptom, "error reading package index file ..." is characteristic. I'm trying to get the correction incorporated in 3.13.