Using R — Packaging a C library in 15 minutes

This entry is part 12 of 14 in the series Using R

Yes, this post condenses 50+ hours of learning into a 15 minute tutorial.  Read ‘em and weep.  (That is, you read while I weep.)

OK.  For the last week I’ve been learning how to call C code as documented in previous posts:  Calling C code “Hello World!”, .Call(“hello”) and Calling C code with Rcpp.  Now comes that task that puts this hard won knowledge to use — providing an R interface to a library of C code as part of a package.  You are encouraged to have Writing R Extensions open in another window and use search whenever you see something you want more information about.  In an effort to keep this exercise to 15 minutes we will stick to the task at hand and not do a whole lot of explaining.  It is assumed that you already have the required compilers installed.

1) Download and test your C library

We’ll be working with the “libmseed” library that reads binary seismic data.  Download the .gz file from the IRIS DMC Softare Library and then compile and test it with the example code.  (Read the README files on your own time.  Right now we have work to do.)

[02:00 minutes]

Congratulations are in order.  (No, not for you!  For the smart folks who wrote libmseed!) ;-)

2) Set up your package structure

Yes, we’re going to do that now.  You could take incremental steps and practice writing an R callable version of example/msview.c and then try to compile it with R CMD SHLIB, then give up and write a Makefile that contains all of the R CMD SHLIB flags.  But I’m here to tell you that most of that will happen auto-magically if you set up a package first and let R worry about the details.

Here is the minimal package structure you need.  We’ll create a new directory named “mseed” and copy all of the “libmseed” code into it:

[02:40]

(Curiosity might lead a person to search through your already open copy of “Writing R Extensions” to figure out what “extdata” is used for.  In the interest of time, please do that later.)

3) Add Makevars to your mseed/src/ directory

You need to educate R that you are going to compile code that depends upon the libmseed.a library.  The easiest way to do this is by creating a file named mseed/src/Makevars::

[03:20]

4) Convert example C code to R callable routine.

This step assumes that you already know everything learned in the .Call(“hello”) post.  I took the code in msview.c and whittled it down as much as possible before converting it into an R callable function that spits out, once again, “Hello World!”.  Here’s what I ended up with.  Copy this code to mseed/src/test_hello_mseed.c

[04:30]

You may have noticed that we hardcoded the location of our data file.  Not really following best practices here but we’re just trying to do the minimum to get it all to work.  We just need to make sure we’re in the directory above mseed/ when we invoke this function.

5) Add wrapper code, NAMESPACE and DESCRIPTION

As described in  .Call(“hello”) we need to add a wrapper function for our C code.  Create mseed/R/mseedWrappers.R with the following content:

Now we need to educate R about the namespace associated with our package.  Create mseed/NAMESPACE with the following:

When R compiles everything, the test_hello_mseed() function written in C will be part of the mseed shared object library.

The last step needed for package compilation is the mseed/DESCRIPTION file:

[06:50]

6) Wave your wand and watch the magic!

Assuming you have everything in place and have copy-pasted well the following should work if you position yourself above the mseed/ directory.  (Always start with R CMD REMOVE mseed to get rid of any previous version.)

[08:20]

It’s a miracle!!!

Congratulations are in order.  Go have a coffee or a beer or whatever to kill the rest of your 15 minutes … unless …

Unless, that is, you want to tackle actually communicating data between R and this C library.  That will take a few more minutes

7) Start with a C code example that reads data from a buffer

Presumably, we’re packaging our library so that R can pass objects to C code and get objects back.  That means that our C code better know how to read data from a buffer.  We probably want to start out with a C code example that does this.  In the real world you will either have to write the buffer example yourself or ask a friend, preferably the author of the package, to help out.  I started with msviewmemory.c.  Better make sure it works:

[09:30]

So far so good.

8) Whittle this code down to an R callable routine

You can try and figure out what the code below does later.  We’re at risk of going over time so go back to the directory above mseed and just copy and paste the following code into mseed/src/test_hello_mseed_mem.c:

[11:10]

9) Add a wrapper, external data and a demo

Add this to mseed/R/mseedWrappers.R:

Of course, we’ll also have to add one more line specifying this new function to mseed/NAMESPACE:

Make a copy of test.mseed so that it is found in the normal place for package “external data”:

Now add the following demonstration script as mseed/demo/hello.R:

and a file named mseed/demo/00Index:

[12:55]

10) Racing to the finish line

[Smack that timer!] Done.

So, how is your time?  Did you keep up the pace and finish in under 15 minutes?  Or did you get distracted trying to understand what you were doing and go over time.

You should feel good about your accomplishment in either case if you got this far.  (I certainly did!)  Compiling a C library for inclusion with R is not for the faint of heart.  I hope that this baby-steps example makes it seem like it’s at least possible if perhaps not easy.

Best of luck extending R!

 

Series NavigationUsing R — Callling C code with RcppPopulation Databrowser
This entry was posted in R and tagged , , . Bookmark the permalink.

3 Responses to Using R — Packaging a C library in 15 minutes

  1. Pingback: Packaging a C library in 15 minutes | Things about R | Scoop.it

  2. siefert says:

    Thanks so much for the fine worked example. I especially like the “15 minute” approach.

    Trying to follow your example on my Mac, however, I ran into problems. I include the error message below. Any ideas on what needs to be modified?

    bash-3.2$ R CMD INSTALL mseed
    * installing to library ‘/Library/Frameworks/R.framework/Versions/2.15/Resources/library’
    * installing *source* package ‘mseed’ …
    ** libs
    *** arch – i386
    gcc -arch i386 -std=gnu99 -I/Library/Frameworks/R.framework/Resources/include -I/Library/Frameworks/R.framework/Resources/include/i386 -DNDEBUG -Ilibmseed -I/usr/local/include -fPIC -g -O2 -c test_hello_mseed.c -o test_hello_mseed.o
    gcc -arch i386 -std=gnu99 -dynamiclib -Wl,-headerpad_max_install_names -undefined dynamic_lookup -single_module -multiply_defined suppress -L/usr/local/lib -o mseed.so test_hello_mseed.o -Llibmseed -lmseed -F/Library/Frameworks/R.framework/.. -framework R -Wl,-framework -Wl,CoreFoundation
    ld: warning: in libmseed/libmseed.a, file was built for unsupported file format which is not the architecture being linked (i386)
    ld: can’t write output file: mseed.so
    collect2: ld returned 1 exit status
    make: *** [mseed.so] Error 1
    ERROR: compilation failed for package ‘mseed’
    * removing ‘/Library/Frameworks/R.framework/Versions/2.15/Resources/library/mseed’

    • Jonathan Callahan says:

      I don’t do much compilation on Macs and have no direct experience but I see one linker warning and one error:

      ld: warning: in libmseed/libmseed.a, file was built for unsupported file format which is not the architecture being linked (i386)
      ld: can’t write output file: mseed.so

      That last line makes it look like the linker is trying to create a file in a directory where it doesn’t have permission.

      To resolve the “architecture” warning you will need to read libmseed/INSTALL to figure out how to compile libmseed for a Mac. Did you do the compile and test of libmseed in step 1)?

Leave a Reply

Your email address will not be published. Required fields are marked *

* Copy This Password *

* Type Or Paste Password Here *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">