Babel Tutorial 101

This tutorial will guide you through the process of writing the classic "Hello World!" example using the Babel tools.  In the process, you will learn how to configure and install the Babel library, write a SIDL (Scientific Interface Definition Language) interface description file, generate the library implementation in C++, and write a C main program to call the library.  You will also learn how to write a Makefile to compile and link the library and program.

This document is arranged as follows:

Please feel free to send any suggestions for improving this tutorial to components@llnl.gov.

Getting the Babel Distribution

You can obtain the Babel distribution through the Lawrence Livermore National Laboratory Component Technologies web site at http://www.llnl.gov/CASC/components.  Follow the links to download the Babel distribution.  We will place new Babel releases and news on this web site.  Bug reports and feature requests may be submitted through the bug reporting database at http://www-casc.llnl.gov/bugs or diirectly to the components team at components@llnl.gov.

Installing Babel

For the purposes of this tutorial, we will assume that you saved the Babel distribution tarball in your home directory and will install Babel into a subdirectory called babel/.  The following commands will unpack the distribution into a directory called babel-0.8.2, probe configuration information for your system, and make and install the tools in your babel/ subdirectory.
% gzcat babel-0.8.2.tar.gz | tar -xf -
% cd babel-0.8.2
% ./configure --prefix=$HOME/babel
% gmake install
The configure script will probe your system for compilers and other required tools.  Make sure your PATH environment variable includes Java, the C compiler, and the C++ compiler.  We currently recommend using the GNU compilers gcc and g++.  Although we are developing support for other compilers, the generation of C++ shared libraries is still somewhat platform-dependent.  Future releases will more fully support other platforms and compilers.  Currently, you must use GNU make to compile the Babel distribution, although we are modifying the build system to support non-GNU make programs.

If you have configuration troubles, please contact us at components@llnl.gov with a detailed description of the platform, environment, and the log file config.log from the configuration utility.

Assuming the "make install" completes successfully, the babel/ subdirectory of your home directory will contain the following:

Writing a SIDL File

Now that we have installed Babel, let's write the "Hello World!" application.  The following procedure may seem to be the most complicated way to write the world's simplest program, but, of course, the same process will also work for significantly more complicated applications.

We will write the "Hello World!" program in a directory called hello/ and place the client library in a subdirectory hello/lib/:

% mkdir hello
% cd hello
% mkdir lib
The first step is to write a SIDL file.  Recall that SIDL is an interface definition language (IDL) that describes the calling interface for a scientific library.  It is used by the Babel tools to generate glue code that hooks together different  programming languages.  A complete description of SIDL is beyond the scope of this introductory tutorial.  Other documentation covers the SIDL grammar, Fortran 77, and C++ bindings. In addition, a sample SIDL file for the hypre library has been provided.

For this particular application, we will write a SIDL file that contains a class World in a package Hello.  Method getMsg in class World returns a string containing the traditional computer greeting.  Using your favorite editor, create a file called hello.sidl in the hello/ directory:

package Hello version 1.0 {
  class World {
    string getMsg();
  }
}
The package statement provides a namespace for class World, which contains only one method, getMsg.  The version clause identifies this as version 1.0 of the Hello package.

Writing the Implementation

We will write the implementation in the lib/ subdirectory of hello/.  The first step is to run the babel shell script to generate the library implementation code for our SIDL file.  We will implement the library in C++.  The command to generate the Babel library code is:
% cd lib
% $HOME/babel/bin/babel -R$MYREP -sC++ ../hello.sidl
In this babel command, the "-sC++" flag indicates that we wish to generate server side C++ bindings.  The "-R" flag indicates that the repository for other symbols (such as the base SIDL symbols) is in the directory $MYREP.  The "--help" flag lists other babel command-line options.  You may also refer to the command line documentation.

This command will generate a large number of C and C++ header and source files.  The files that begin with "Hello" correspond to our sample program.  The files that begin with "SIDL" are part of the C++ interface to the SIDL run-time library.  The file names that contain "IOR" or "Skel" are part of the Babel glue code.  The file babel.make is a Makefile fragment that will simplify writing the Makefile necessary to compile the library.  You may ignore the babel.make file if you wish.  The only files that should be modified are those headers and sources that end with "Impl." Babel generates these implementation files as a starting point for developers.  These files will contain the implementation of the Hello library.

Every implementation file contains many pairs of comment "splicer" lines such as the following:

string
Hello::World_impl::getMsg()
throw ()
{
  // DO-NOT-DELETE splicer.begin(Hello.World.getMsg)
  // insert implementation here
  // DO-NOT-DELETE splicer.end(Hello.World.getMsg)
}
Any modifications between these splicer lines will be saved between invocations of the babel tool.  Any changes outside the splicer lines will be lost.  Thus, it is easy to add new methods to the hello.sidl file without the loss of your modifications when babel regenerates its code.

For our hello application, the implementation is trivial.  Add the following return statement between the splicer lines in the Hello_World_Impl.cc file:

string
Hello::World_impl::getMsg()
throw ()
{
  // DO-NOT-DELETE splicer.begin(Hello.World.getMsg)
  return string("Hello World!");
  // DO-NOT-DELETE splicer.end(Hello.World.getMsg)
}
To keep the Makefile simple, we will use the GNU gcc and g++ compilers.  The following Makefile will compile the library files and create a shared library named libhello.so:
.cc.o:
    g++ -fPIC -I$(HOME)/babel/include -c $<
.c.o:
    gcc -fPIC -I$(HOME)/babel/include -c $<

include babel.make
OBJS = ${IMPLSRCS:.cc=.o} ${IORSRCS:.c=.o} \
       ${SKELSRCS:.cc=.o} ${STUBSRCS:.cc=.o}

libhello.so: ${OBJS}
    g++ -shared -o $@ ${OBJS}

clean:
    ${RM} *.o libhello.so

You do not necessarily need to create a shared library for this example; you may generate a standard static library (e.g., libhello.a).  However, in general, you must generate a shared library if you will be calling your library from Python or Java.  To create the shared library archive libhello.so, simply execute make:
% gmake libhello.so

Writing the Client

We will write the client in the main hello/ subdirectory.  The main program will be written in C.  File hello.c is as follows:
#include <stdio.h>
#include "Hello_World.h"

int main(int argc, char** argv)
{
  Hello_World h = Hello_World__create();
  char* msg = Hello_World_getMsg(h);
  printf("%s\n", msg);
  Hello_World_deleteRef(h);
  free(msg);
}

This code creates the Hello_World object, calls the getMsg method, prints the ubiquitous saying, decrements the reference count for the object, and frees the message string. To generate the C glue code necessary to call the library, we run the babel tool again, this time specifying C as the target language:
% $HOME/babel/bin/babel -cC -R$MYREPO hello.sidl
The "-cC" flag tells the babel code generator to create only the C client  code, not the entire library implementation.  The library libhello.so already contains the necessary IOR, skeleton, and implementation object files. We compile the hello program using the following Makefile:
.c.o:
    gcc -I$(HOME)/babel/include -Ilib -c $<

include babel.make
OBJS = hello.o ${STUBSRCS:.c=.o}

hello: ${OBJS}
    gcc ${OBJS} -o $@ \
      -Rlib -Llib -lhello \
      -R$(HOME)/babel/lib -L$(HOME)/babel/lib -lsidl

clean:
    ${RM} *.o hello

Note that the "-R" flags in the above Makefile snippet tell the dynamic library loader where to find the hello and sidl shared libraries.  You could achieve the same behavior through environment variables such as LD_LIBRARY_PATH. Finally, we make the executable and run it:
% make hello
% ./hello
Hello World!
Congratulations!  You are now ready to develop a parallel scalable linear solver package.

Environment Variables

The following environment variables may affect the runtime behavior of a program using BABEL:
SIDL_DLL_PATH
The stub code for some of the languages loads the implementation of the class or interface at runtime using dlopen or something similar. When searching for an implementation, it will search all the shared libraries/dynamic link libraries found in the semicolon separated list of directories. This is particular necessary for Python clients. The semicolon is used because someday we may use URIs instead simple file directories.
LD_LIBRARY_PATH
Many Unix-like operating systems will search the colon separated list of directories in this environment variable to find shared libraries/dynamic link libraries.