April 23 2007
Archived Technology R XDS

Building the R math library

Cross-compiling for the Windows platform from Linux

Reading time: about 4 minutes

The R project is a computing environment for statistical calculation. R includes a math library that can be built as a standalone version for use by other projects, like my own experimental design space explorer. The library is easy to build from the R sources on UNIX-like systems, but a pain to build directly on Windows. This article describes an alternative: using a Linux box to cross-compile the Windows version.

Download R Math Library 2.2.1 binaries for Linux and Win32

Setup

Your build environment will include the cross-compiler tools and two copies of the R source (one for each OS). To help keep things straight, these instructions make use of the following environment variables:

VariableTarget directory
$TOOLSwhere the cross-compiler is unpacked
$RDIRLwhere the R source for the Linux build is unpacked
$RDIRWwhere the R source for the Windows build is unpacked
$RLIBwhere the finished library files should go
$EDyour preferred editor for this task

I defined these as follows:

TOOLS=~/winr/cross-tools
RDIRL=~/winr/LinuxR/R-2.2.1
RDIRW=~/winr/WinR/R-2.2.1
RLIB=~/winr/output
ED=emacs 

Get the source

Get a copy of the R source and unpack two copies, one for each OS.

cd $RDIRL
wget http://cran.stat.sfu.ca/src/base/R-2/R-2.2.1.tar.gz ..
tar zxf ../R-2.2.1.tar.gz
cd $RDIRW
tar zxf ../R-2.2.1.tar.gz 

Install cross-compilers

I used the following precompiled binary and runtime, but you may need to find an equivalent:

cd $TOOLS
wget http://www.stats.ox.ac.uk/pub/Rtools/mingw-cross5.tar.bz2
wget http://umn.dl.sourceforge.net/sourceforge/mingw/mingw-runtime-3.10.tar.gz
tar jxf mingw-cross5.tar.bz2
mkdir -p i586-mingw32
cd i586-mingw32
tar zxf ../mingw-runtime-3.10.tar.gz

Put the cross-compilers on the path

export PATH=$PATH:$TOOLS/bin 

(I exported the new path because I was using a VM just for building the library. You don’t have to.)

Build the Linux version

You may need to run the configure script multiple times in order to identify missing packages. On my system I had to install a Fortran compiler and makeinfo (part of GNU texinfo).

cd $RDIRL
./configure --with-readline=no --with-x=no
make

I needed the options above to get it working. The --with-x=no is presumably becuase I didn’t have the X headers installed.

With some persistence you should make it through the configure process and then be able to run make. Speaking of which, you might want to go make some tea because this can take a while. Once it finishes, you can verify the Linux build with make check. That will give you a chance to drink that tea you made.

Building the libraries

Build the standalone Linux library (optional)

If you also need a Linux build of the math library, you can make that now:

cd $RDIRL/src/nmath/standalone
make

Test it:

make test
./test

And copy the result to your output directory:

mkdir -p $RLIB/linux-x86
cp *.so *.a $RLIB/linux-x86

Configure the Windows build

You’ll need to adjust the make configuration to perform a cross-compile:

cd $RDIRW/src/gnuwin32
cp MkRules MkRules.original
$ED MkRules 

Look for the following (line numbers refer to version 2.2.1):

LineChangePurpose
7BUILD=CROSSEnable cross-compile
59BINPREF=i586-mingw32-Prefix used for gcc tools to get cross-compiling versions
61HEADER=$TOOLS/i586-mingw32/includePoint to header files for cross-compiler
64R_EXE=$RDIRL/bin/RPoint to the Linux version you just built

At this point, after installing any needed library source files, you could build the full version of R build by running a make if you wanted.

Configure a Windows library build

cd $RDIRW/src/include
make -f Makefile.win config.h Rconfig.h Rmath.h
cd $RDIRW/src/nmath/standalone 

The gcc tools and Visual C++ have different ideas about how functions should be named in a .dll. To make a VC-compatible library, we will need to adjust the configuration to produce a .def file:

cp Makefile.win Makefile.win.original
$ED Makefile.win

Look for the line DLLFLAGS=-s and change it to DLLFLAGS=-s -W1,--output-def,Rmath.def,--out-implib,libRmath.a.

Build the standalone Windows library

make -f Makefile.win 

Assuming that goes off without a hitch, you can gather the files up someplace convenient:

mkdir -p $RLIB/win32
cp *.dll *.a $RLIB/win32
mkdir -p $RLIB/include
cp $RDIRW/include/Rmath.h $RLIB/include

Adjust the .def file

That .def file you made earlier is used to make a .lib that you can link to with Visual C++. It contains the “external” names of the exported functions. The compiler generated a library with identical internal names, but Visual C++ will expect the internal names to be mangled to start with an underscore (an assumption which violates Microsoft’s own specs for .def files). This leads to a maddening dynamic linking error: Windows will complain that function X does not exist, even though it does, because Windows is actually looking for _X, which doesn’t. The solution is to explicitly define the internal names to be identical with the external ones. An easy way to do this is:

sed 's/^ +\([a-z|A-Z|0-9|_]+\)/ \1=\1/' Rmath.def >$RLIB/win32/Rmath.def 

Finishing up

Configure the Rmath.h header for standalone use

cd $RLIB
$ED include/Rmath.h

Look for /* #undef MATHLIB_STANDALONE */ and replace it with #define MATHLIB_STANDALONE. It was line 36 in my version.

Pack the library files up and transfer to a Windows environment

For example, with:

zip rmath-lib-2.2.1.zip */*

Generate a static link library

Once in Windows, unpack your library files. If using Visual C++, you need to build a .lib stub library for static linking. That’s why we went to all the trouble with the .def file earlier:

lib /machine:i386 /def:Rmath.def

Add this library to the linker options for Visual C++ and you are good to go.

Normalize library names (optional)

Sometimes it is convenient or necessary that your libraries use the same base file name for all platforms, for example, if you want to link to it from Java. To do that, before you do the previous step rename your Rmath.dll to libRmath.dll, Rmath.def to libRmath.def, and edit libRmath.def to declare the LIBRARY name as libRmath. (Be sure to use the new file name when running the lib command!)

Using the libraries

Linking to the libraries

That’s it. From Visual C++ you can link to the .dll by statically linking to the .lib library. From a GNU compiler, you can dynamically link to the .so library or statically link to the .a library.