[Arjen Markus] (15 august 2022) During this month's monthly meet-up we discussed, among other things, the possibilities of DLLs (or shared objects) that contain the Tcl and Tk libraries as well as all the configuration files. Such "KitDLLs" are part of the [KitCreator] build system by [Roy Keene]. There are more solutions, like [tclkit], [BAWT], with similar goals and their own pros and cons. But I decided to have a closer look at [KitCreator], as I thought it might be used to build Fortran programs with Tcl/Tk as embedded resource. The fact that all configuration files are part of the KitDLL library makes distribution very easy. So, I downloaded the source code at [http://kitcreator.rkeene.org/fossil/index] and started building under MinGW-w64/MSYS2, as that is the build environment on Windows that is close to native Windows and still support the autotools build tools. Unfortunately, this failed. The crucial error occurred in building "tclvfs". I have not found the apparent quoting error yet in the generated code: ======none checking for a BSD-compatible install... /usr/bin/install -c checking whether make sets $(MAKE)... ./configure: eval: line 2963: unexpected EOF while looking for matching `"' ./configure: eval: line 2964: syntax error: unexpected end of file + for tryopt in "${tryopts[@]}" __fail__ + '[' __fail__ = __fail__ ']' + return 1 + die 'configure failed' + local msg + msg='configure failed' + echo 'configure failed' configure failed + exit 1 ====== As is usual with such code in a `configure` file, it is rather convoluted. Therefore I tried instead a prebuilt KitDLL - the http://www.rkeene.org/devel/kitcreator/kitbuild/nightly/%|%Tclkit for Tcl version 8.6.12 for Win64 on amd64%|%. The file is called `libtclkit-8.6.12-win64-amd64-kitdll-xcompile` - this name does not end with `.dll` or `.lib` as a typical compiler on Windows would expect. I renamed the file to `libtclkit-8.6.12-win64-amd64.dll` and I used the following tiny source file for `tclsh`: ======c #include int Tcl_AppInit(Tcl_Interp *interp) { return(Tcl_Init(interp)); } int main(int argc, char **argv) { Tcl_Main(argc, argv, Tcl_AppInit); return(1); } ====== The relevant include file `tcl.h` comes from a suitable Tcl installation or source tree. There was one small problem still: on Windows a DLL should be accompanied by a so-called import library. The GNU gcc compiler, however, circumvents this problem. Or you could use a utility to create one from the DLL. ---- '''NOTE:''' You better use, instead, the "software development kit", `sdk`, that is associated with the KitDLL. It contains the required libraries and include files in a ready form. It does not contain the above sample code for `tclsh`. ---- Anyway, I used the distribution of the GNU compilers from [http://www.equation.com] as they can be used in an ordinary DOS box (command window). The build step was easy and I got a `tclsh.exe`. However, the runtime name of the DLL is `libtcl8612.dll`, so I had to rename the DLL to that and it worked! (As noted above: the SDK contains just about all the files you need with the right names.) Simple session: ======tcl > package require Tk > pack [canvas .c] > exit ====== I also tried to use a Fortran program to call `Tcl_Main()` and get an easy way to embed Tcl and Tk in such a program. That did not work immediately, most likely because of the `argv` argument. By using a C main program and passing the arguments `argc` and `argv` to a Fortran routine, I did get things to work - albeit in a rather primitive way: The C main program calls a Fortran routine: ======c #include void ftcl_main( int argc, char **argv ); int main(int argc, char **argv) { ftcl_main(&argc, argv); return(1); } ====== The Fortran code looks like this (mimicking the interfaces to the two Tcl functions): ======none module ftcl use iso_c_binding implicit none interface integer function Tcl_Init( interp ) bind(C, name = "Tcl_Init") import :: c_ptr type(c_ptr), value :: interp end function Tcl_Init end interface interface integer function Tcl_Main( argc, argv, inifunc ) bind(C, name = "Tcl_Main") import :: c_ptr, c_funptr integer, value :: argc type(c_ptr), value :: argv type(c_funptr), value :: inifunc end function Tcl_Main end interface contains integer function ftcl_appinit( interp ) bind(C, name = "ftcl_appinit") type(c_ptr), value :: interp write(*,*) 'In ftcl_appinit' ftcl_appinit = Tcl_Init( interp ) end function ftcl_appinit integer function ftcl_main( argc, argv ) bind(C, name = 'ftcl_main') integer, value :: argc type(c_ptr), value :: argv write(*,*) 'In ftcl_main' ftcl_main = Tcl_Main( argc, argv, c_funloc(ftcl_appinit) ) end function ftcl_main end module ftcl ====== The ideal is to use a Fortran main program, but the `argv` argument presented some problems. It took me a bit of experimentation and soul searching, but this Fortran program gives me the `tclsh` I was looking for: ====== module ftcl use iso_c_binding implicit none interface integer function Tcl_Init( interp ) bind(C, name = "Tcl_Init") import :: c_ptr type(c_ptr), value :: interp end function Tcl_Init end interface interface integer function Tcl_Main( argc, argv, inifunc ) bind(C, name = "Tcl_Main") import :: c_ptr, c_funptr integer, value :: argc type(c_ptr), value :: argv type(c_funptr), value :: inifunc end function Tcl_Main end interface contains integer function ftcl_appinit( interp ) bind(C, name = "ftcl_appinit") type(c_ptr), value :: interp ftcl_appinit = Tcl_Init( interp ) end function ftcl_appinit integer function ftcl_main( inifunc ) interface integer function inifunc( interp) bind(C) import :: c_ptr type(c_ptr), value :: interp end function inifunc end interface integer :: i, length, argc type(c_ptr), dimension(:), allocatable, target :: argv character(len=256), dimension(:), allocatable, target :: arg argc = command_argument_count() allocate( argv(argc+1), arg(argc+1) ) do i = 0,argc call get_command_argument( i, arg(i+1) ) length = len_trim(arg(i+1)) arg(i+1)(length+1:) = c_null_char argv(i+1) = c_loc(arg(i+1)) enddo ftcl_main = Tcl_Main( argc+1, c_loc(argv), c_funloc(inifunc) ) end function ftcl_main end module ftcl program ftclsh use iso_c_binding use ftcl implicit none type(c_ptr), pointer :: argv integer :: rc rc = ftcl_main( ftcl_appinit ) end program ftclsh ====== Most of the above code is dedicated to mimicking the proper interfaces and can be put into a library. The `iso_c_binding` features hide all the platform dependencies: the program works with both `gfortran` and `Intel Fortran oneAPI`. <>Example|Tclkit