Dear R developers, I plan to upload a first version of my R package RnavGraph to the R CRAN server in a week or two. However I'm still struggling with an image resizing function written in C as a tcl extension. I did all my development in Ubuntu, and everything works fine in Ubuntu, however my attempts to compile this C function under Windows or OSX have all failed. I provide a minimal self contained example at the end of this post. I also wrapped this example in a minimal R package (less than 20 lines of code!) and it can be downloaded at http://www.waddell.ch/RnavGraph/TclHelloWorld.zip Can somebody help me to get this package to compile correctly under Linux, Windows and OSX? (i.e. writing a configure script with the correct compile commands). Thanks, Adrian Waddell ########################### C Code (save as hello.c): #include <tcl.h> static int Hello_Cmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_SetObjResult(interp, Tcl_NewStringObj("Hello, World!", -1)); return TCL_OK; } int DLLEXPORT Hello_Init(Tcl_Interp *interp) { Tcl_CreateObjCommand(interp, "hello", Hello_Cmd, NULL, NULL); return TCL_OK; } which can be compiled (under Ubuntu 10.04) with: gcc -shared -o hello.so -DUSE_TCL_STUBS -I/usr/include/tcl8.5/ hello.c -L/usr/lib/ -ltclstub8.5 -fPIC and used within an R session with library(tcltk) .Tcl('load ./hello[info sharedlibextension]') tcl('hello')
Compiling a Tcl extension for an R package
3 messages · Adrian Waddell, Simon Urbanek, Brian Ripley
Adrian,
On Feb 8, 2011, at 1:06 PM, Adrian Waddell wrote:
Dear R developers, I plan to upload a first version of my R package RnavGraph to the R CRAN server in a week or two. However I'm still struggling with an image resizing function written in C as a tcl extension. I did all my development in Ubuntu, and everything works fine in Ubuntu, however my attempts to compile this C function under Windows or OSX have all failed. I provide a minimal self contained example at the end of this post. I also wrapped this example in a minimal R package (less than 20 lines of code!) and it can be downloaded at http://www.waddell.ch/RnavGraph/TclHelloWorld.zip Can somebody help me to get this package to compile correctly under Linux, Windows and OSX? (i.e. writing a configure script with the correct compile commands).
You should really read the TEA (Tcl Extension Architecture) documentation for details. Technically, you cannot use R package compilation to build TEAs, because they use entirely separate process (you can use a Makefile/Makevar with a separate target, though). Also note that TEAs are intended to be installed in the Tcl location, so you may want to think twice about it as it is orthogonal to the R package process (usually packages require extensions) -- for example there is no guarantee that the compiler used to build Tcl is on the machine that builds R packages. TEA recommends the use of tcl.m4 and autoconf - but note that you'll need to separate it from the package's flags.
That said, if you are willing to take some risks and cut corners (normally not what I'd suggest), there are a few things you can consider.
On unix (which includes Mac OS X), you may get away with locating tclConfig.sh and using the appropriate flags from there. This is still best done using configure, otherwise you'll need to jump through hoops to get tclConfig.sh sourced before a call to a sub-make, worry about multi-arch etc.
Another alternative (more corner-cutting) is to use R's own Tcl/Tk configuration and pray that it will work. It did work for me on Mac, but be aware that it assumes that Tcl is compatible with the compiler used for R and that R's config is good enough to compile TEAs:
*** Makevars:
# you can't use $(DYLIB_EXT) even though that's what Tcl uses
# because R multi-arch installs won't copy it! So it must be SHLIB_EXT
TEALIB=helloTEA$(SHLIB_EXT)
all: $(SHLIB) $(TEALIB)
$(TEALIB): helloTEA.o
$(SHLIB_LINK) -o $(TEALIB) helloTEA.o $(TCLTK_LIBS)
helloTEA.o: hello.c
$(CC) -DTEA=1 -c hello.c -o $@ $(CPPFLAGS) $(CFLAGS) $(CPICFLAGS) $(TCLTK_CPPFLAGS)
**** hello.c [modified - see comments]
/* we have to make it conditional as R will also compile it for the package */
#if TEA
#include <tcl.h>
static int Hello_Cmd(ClientData cdata, Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]) {
Tcl_SetObjResult(interp, Tcl_NewStringObj("Hello, World!", -1));
return TCL_OK;
}
/* to avoid name clashes the TEA version is called helloTEA so the Init has to be adjusted accordingly */
int DLLEXPORT Hellotea_Init(Tcl_Interp *interp) {
Tcl_CreateObjCommand(interp, "hello", Hello_Cmd, NULL, NULL);
return TCL_OK;
}
#else
/* your R package C code goes here if you want */
#endif
*** test run on a Mac (created as package A)
library(A) library(tcltk)
Loading Tcl/Tk interface ... done
.Tcl(paste('load',system.file("libs",.Platform$r_arch,paste("helloTEA",.Platform$dynlib.ext,sep=''),package="A")))
<Tcl> dlsym(0x93b3e0, Hellotea_SafeInit): symbol not founddlsym(0x93b3e0, Hellotea_Unload): symbol not founddlsym(0x93b3e0, Hellotea_SafeUnload): symbol not found
.Tcl('hello')
<Tcl> Hello, World! Modulo a small bug in R (x64/Makeconf has wrong TCLTK_LIBS - it should point to bin64 instead of bin) and the fact that Tcl doesn't like paths with spaces (I suppose you can escape it somehow) it actually works on Windows as well. Cheers, Simon
Thanks,
Adrian Waddell
###########################
C Code (save as hello.c):
#include <tcl.h>
static int Hello_Cmd(ClientData cdata, Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]) {
Tcl_SetObjResult(interp, Tcl_NewStringObj("Hello, World!", -1));
return TCL_OK;
}
int DLLEXPORT Hello_Init(Tcl_Interp *interp) {
Tcl_CreateObjCommand(interp, "hello", Hello_Cmd, NULL, NULL);
return TCL_OK;
}
which can be compiled (under Ubuntu 10.04) with:
gcc -shared -o hello.so -DUSE_TCL_STUBS -I/usr/include/tcl8.5/ hello.c
-L/usr/lib/ -ltclstub8.5 -fPIC
and used within an R session with
library(tcltk)
.Tcl('load ./hello[info sharedlibextension]')
tcl('hello')
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
On Tue, 8 Feb 2011, Simon Urbanek wrote:
Adrian, On Feb 8, 2011, at 1:06 PM, Adrian Waddell wrote:
Dear R developers, I plan to upload a first version of my R package RnavGraph to the R CRAN server in a week or two. However I'm still struggling with an image resizing function written in C as a tcl extension. I did all my development in Ubuntu, and everything works fine in Ubuntu, however my attempts to compile this C function under Windows or OSX have all failed. I provide a minimal self contained example at the end of this post. I also wrapped this example in a minimal R package (less than 20 lines of code!) and it can be downloaded at http://www.waddell.ch/RnavGraph/TclHelloWorld.zip Can somebody help me to get this package to compile correctly under Linux, Windows and OSX? (i.e. writing a configure script with the correct compile commands).
You should really read the TEA (Tcl Extension Architecture) documentation for details. Technically, you cannot use R package compilation to build TEAs, because they use entirely separate process (you can use a Makefile/Makevar with a separate target, though). Also note that TEAs are intended to be installed in the Tcl location, so you may want to think twice about it as it is orthogonal to the R package process (usually packages require extensions) -- for example there is no guarantee that the compiler used to build Tcl is on the machine that builds R packages. TEA
And that is serious. Solaris and Windows are two platforms on which Tcl/Tk and R are often built with different compilers. A few of us were looking into this last month to see if there was an easy portable way to provide Tcl extensions such as Bwidget, Tktable and Img (the common ones used in CRAN packages). We concluded it probably was possible (as Tktable uses the tclConfig.sh route) but not very portable.
recommends the use of tcl.m4 and autoconf - but note that you'll
need to separate it from the package's flags.
That said, if you are willing to take some risks and cut corners
(normally not what I'd suggest), there are a few things you can
consider.
On unix (which includes Mac OS X), you may get away with locating
tclConfig.sh and using the appropriate flags from there. This is
still best done using configure, otherwise you'll need to jump
through hoops to get tclConfig.sh sourced before a call to a
sub-make, worry about multi-arch etc.
Another alternative (more corner-cutting) is to use R's own Tcl/Tk
configuration and pray that it will work. It did work for me on Mac,
but be aware that it assumes that Tcl is compatible with the
compiler used for R and that R's config is good enough to compile
TEAs:
*** Makevars:
# you can't use $(DYLIB_EXT) even though that's what Tcl uses
# because R multi-arch installs won't copy it! So it must be SHLIB_EXT
TEALIB=helloTEA$(SHLIB_EXT)
all: $(SHLIB) $(TEALIB)
$(TEALIB): helloTEA.o
$(SHLIB_LINK) -o $(TEALIB) helloTEA.o $(TCLTK_LIBS)
helloTEA.o: hello.c
$(CC) -DTEA=1 -c hello.c -o $@ $(CPPFLAGS) $(CFLAGS) $(CPICFLAGS) $(TCLTK_CPPFLAGS)
**** hello.c [modified - see comments]
/* we have to make it conditional as R will also compile it for the package */
#if TEA
#include <tcl.h>
static int Hello_Cmd(ClientData cdata, Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]) {
Tcl_SetObjResult(interp, Tcl_NewStringObj("Hello, World!", -1));
return TCL_OK;
}
/* to avoid name clashes the TEA version is called helloTEA so the Init has to be adjusted accordingly */
int DLLEXPORT Hellotea_Init(Tcl_Interp *interp) {
Tcl_CreateObjCommand(interp, "hello", Hello_Cmd, NULL, NULL);
return TCL_OK;
}
#else
/* your R package C code goes here if you want */
#endif
*** test run on a Mac (created as package A)
library(A) library(tcltk)
Loading Tcl/Tk interface ... done
.Tcl(paste('load',system.file("libs",.Platform$r_arch,paste("helloTEA",.Platform$dynlib.ext,sep=''),package="A")))
<Tcl> dlsym(0x93b3e0, Hellotea_SafeInit): symbol not founddlsym(0x93b3e0, Hellotea_Unload): symbol not founddlsym(0x93b3e0, Hellotea_SafeUnload): symbol not found
.Tcl('hello')
<Tcl> Hello, World! Modulo a small bug in R (x64/Makeconf has wrong TCLTK_LIBS - it should point to bin64 instead of bin) and the fact that Tcl doesn't like paths with spaces (I suppose you can escape it somehow) it actually works on Windows as well. Cheers, Simon
Thanks,
Adrian Waddell
###########################
C Code (save as hello.c):
#include <tcl.h>
static int Hello_Cmd(ClientData cdata, Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]) {
Tcl_SetObjResult(interp, Tcl_NewStringObj("Hello, World!", -1));
return TCL_OK;
}
int DLLEXPORT Hello_Init(Tcl_Interp *interp) {
Tcl_CreateObjCommand(interp, "hello", Hello_Cmd, NULL, NULL);
return TCL_OK;
}
which can be compiled (under Ubuntu 10.04) with:
gcc -shared -o hello.so -DUSE_TCL_STUBS -I/usr/include/tcl8.5/ hello.c
-L/usr/lib/ -ltclstub8.5 -fPIC
and used within an R session with
library(tcltk)
.Tcl('load ./hello[info sharedlibextension]')
tcl('hello')
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
Brian D. Ripley, ripley at stats.ox.ac.uk Professor of Applied Statistics, http://www.stats.ox.ac.uk/~ripley/ University of Oxford, Tel: +44 1865 272861 (self) 1 South Parks Road, +44 1865 272866 (PA) Oxford OX1 3TG, UK Fax: +44 1865 272595