Updated 2013-01-02 01:16:09 by RLE

Use TEA, but perhaps also see "Hello World as a C extension".

aricb I hope someone will fill in a few more details here. Some questions I have are:

What are the popular compilers used for Tcl extensions on OS X? What Mac-specific compiler arguments should an extension writer be familiar with?

The MacOS X page mentions that Tk can be compiled to have a native Aqua look and feel, or it can be compiled to use X-Windows. Does an extension writer need to worry about this distinction, and if so, what does one do differently in each case?

DAS - for simple extensions, I would strongly recommend looking at critcl, building extensions with critcl -pkg is much easier than using TEA or a handwritten Makefile.

generally the compiler to use on MacOS X is gcc, which comes with the system. There are commercial compilers available as well if you have special requirements.

If you use recent TEA, as an extension builder you do not need to know whether you are building against TkAqua or TkX11, TEA will extract this info from the tkConfig.sh file. However, if you use X11 calls in your code, the resulting binary will be tied to the Tk variant you built against and will not load or crash if loaded in the other variant of Tk (best to check tk windowingsystem in pkgIndex.tcl before loading). This is because the extension built against TkAqua will use emulated X11 calls whereas the other one will use real X11.

DAS goes on to say:

for TEA based extensions, the following configure invocation should do the trick:
         ./configure \
         --prefix=/usr/local --libdir=/Library/Tcl \
         --with-tcl=/Library/Frameworks/Tcl.framework \
         --with-tclinclude=/Library/Frameworks/Tcl.framework/Headers \
         --with-tk=/Library/Frameworks/Tk.framework \
         --with-tkinclude=/Library/Frameworks/Tk.framework/Headers \
         --enable-threads

You should probably link with stub libraries like on other unix platforms:
          gcc -dynamiclib -o yourdylib.dylib yourcommands.o \
     -L /Library/Frameworks/Tcl.framework/ -ltclstub8.4 \
     -L /Library/Frameworks/Tk.framework -ltkstub8.4

You can build tcl & tk with X11 yourself in the standard unix manner and install into e.g. /usr/local and not bother with the framework based TkAqua.

You'll need X11 & X11SDK from Apple:
        http://www.apple.com/macosx/features/x11/download/

[Dscho] This did not work too well for me: linking as described above would result in many unresolved symbols. However, replacing "-L /Libaray/Frameworks/Tcl.framework/ -ltclstub8.4" with "-framework Tcl" (which is more OSXish, and shorter) helped! So, it is:
          gcc -dynamiclib -o yourlib.dylib yourcommands.o \
     -framework Tcl -framework Tk

MB A minimal example using gcc alone:
        /*

  • Mac OS X build (-I may be skipped):
         *

  • gcc -Wall -g -DUSE_TCL_STUBS \
  • -I/Library/Frameworks/Tcl.framework/Headers/ -c minimal.c
  • gcc -dynamiclib -o minimal.dylib minimal.o \
  • -L/Library/Frameworks/Tcl.framework/ -ltclstub8.4
         *

  • Unix:
         *

  • gcc -Wall -g -DUSE_TCL_STUBS -fpic -c minimal.c
  • gcc -shared -o minimal.so minimal.o -ltclstub8.4
         *
         */

        #if TARGET_API_MAC_CARBON
        #   include <Tcl/tcl.h>
        #else
        #   include "tcl.h"
        #endif

        static int MinimalObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);

        DLLEXPORT int Minimal_Init(Tcl_Interp *interp) {
            if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
                return TCL_ERROR;
            }
            Tcl_CreateObjCommand(interp, "minimal", MinimalObjCmd, (ClientData) NULL, NULL);
            return Tcl_PkgProvide(interp, "minimal", "0.1");
        }

        DLLEXPORT int Minimal_SafeInit(Tcl_Interp *interp) {
            return Minimal_Init(interp);
        }

        static int MinimalObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
            Tcl_SetObjResult(interp, Tcl_NewStringObj("minimal seems ok", -1));
            return TCL_OK;
        }