next up previous
Next: Defining new Packet Types, Up: Developer's manual Previous: Developer's manual

Building a NS module Autotools

Strictly speaking, you are not required to use autotools to develop a dynamic module for ns; other tools such as scons and cmake can probabily be successfully used as well, and of course you can always write your own Makefile or use gcc from the command line.

However, I personally think using autotools to build a ns module is convenient for you for the following reasons:

The major drawback of using autotools is that the learning curve is somehow steep. I will try to make everything easier by explaining step-by-step how to setup autotools support for building a ns dynamic library.

Let's assume we want to create a new ns2 module named foo, which is made up of two source code files foo.h and foo.cc, and a file with some tcl initializations which is called foo-init.tcl. Suppose we use the directory /locale/foo for building the module.

First of all, create the following subdirectories:

baldo@pcsignet08:/locale/foo$ mkdir src
baldo@pcsignet08:/locale/foo$ mkdir m4
baldo@pcsignet08:/locale/foo$ ls -la
total 16
drwxr-xr-x   4 baldo dottor 4096 Jun 11 17:54 .
drwxrwxr-t  10 root  signet 4096 Jun 11 17:53 ..
drwxr-xr-x   2 baldo dottor 4096 Jun 11 17:54 m4
drwxr-xr-x   2 baldo dottor 4096 Jun 11 17:53 src

Dowload the nsallinone.m4 file and put it into the m4 subdir.

Create a file /local/foo/autogen.sh with the following content:

#!/bin/sh

aclocal -I m4 --force && libtoolize --force && automake --foreign --add-missing && autoconf

Make the above file executable:

baldo@pcsignet08:/locale/foo$ chmod a+x autogen.sh

Please note the -I m4 argument which is passed to aclocal is necessary so that the m4 macros in nsallinone.m4 can be found when autoconf is run.

Create a file /local/foo/configure.ac with the following content:

AC_INIT(foo,1.0)
AM_INIT_AUTOMAKE
AC_PROG_CXX
AC_PROG_MAKE_SET

AC_DISABLE_STATIC
AC_LIBTOOL_WIN32_DLL
AC_PROG_LIBTOOL

AC_PATH_NS_ALLINONE

AC_DEFINE(CPP_NAMESPACE,std)

AC_CONFIG_FILES([
		Makefile
		src/Makefile
		m4/Makefile
		])
		
AC_OUTPUT

Note the call to the macro AC_PATH_NS_ALLINONE which is defined in nsallinone.m4

Create a file /local/foo/Makefile.am with the following content:

SUBDIRS = src m4
EXTRA_DIST = autogen.sh 
ACLOCAL_AMFLAGS = -I m4
DISTCHECK_CONFIGURE_FLAGS = @NS_ALLINONE_DISTCHECK_CONFIGURE_FLAGS@

It is good practice to include all the custom .m4 files you used in the distribution (should you ever run make dist and distribute the resulting package). For this purpose, create a file named /local/foo/m4/Makefile.am with the following content:

EXTRA_DIST = nsallinone.m4

Next, put your source code files (i.e., foo.h and foo.cc) together with the tcl initialization file (foo-init.tcl) into /local/foo/src.

Then create a file /local/foo/src/Makefile.am with the following content:

lib_LTLIBRARIES = libfoo.la

libfoo_la_SOURCES = foo.cc foo.h initlib.cc
libfoo_la_CPPFLAGS = @NS_CPPFLAGS@ 
libfoo_la_LDFLAGS =  @NS_LDFLAGS@
libfoo_la_LIBADD =   @NS_LIBADD@ 


nodist_libfoo_la_SOURCES = embeddedtcl.cc
BUILT_SOURCES = embeddedtcl.cc
CLEANFILES = embeddedtcl.cc

TCL_FILES =  foo-init.tcl

embeddedtcl.cc: Makefile $(TCL_FILES)
		cat $(TCL_FILES) | @TCL2CPP@ FooTclCode > embeddedtcl.cc

EXTRA_DIST = $(TCL_FILES)

Let's briefly explain the above. We want to create a libtool library libfoo.la, which e.g. on linux will result in a libfoo.so shared object. We declare the source files which are needed to build this library. Furthermore, we declare the C preprocessor flags (CFLAGS) and linker flags (LDFLAGS) used to build libfoo, and we include the list of libraries libfoo has to be linked with (LIBADD).

The variables @NS_CPPFLAGS@, @NS_LDFLAGS@ and @NS_LIBADD@ are defined in nsallinone.m4; their value will be determined when we run ./configure --with-ns-allinone=<path> and will depend on the location of the ns-allinone installation which is being used.

We note in particular that the @NS_CPPFLAGS@ variable will be expanded in a number of -I<path> directives which will refer to the several locations of the C header files in the NS source, so that these header files can be included by the preprocessor. In other words, thanks to @NS_CPPFLAGS@ we can use things like #include<packet.h> and #include<mac-802_11.h> in our code and it will compile without complaining3.

Note that, in addition to the source code files foo.h and foo.cc, some other source files are needed. We will explain how to write initlib.cc in a moment. As for embeddedtcl.cc, there is no need to create it by hand, since it will be generated automatically; in fact, embeddedtcl.cc will just contain the TCL code in foo-init.tcl converted to a C string by means of the tcl2c++ program, whose absolute path is contained in the @TCL2CPP@ variable (which is again defined in nsallinone.m4 and whose value will be determined when we run ./configure).

The purpose of this setup is to allow the TCL coded provided in foo-init.tcl to be executed by the TCL interpreter as the library libfoo.so is loaded within a TCL script. How does it work? That's where initlib.cc comes into play. Create /locale/foo/src/initlib.cc and add the following code to it:

#include<tclcl.h>

extern EmbeddedTcl FooTclCode;

extern "C" int Foo_Init() {
	FooTclCode.load();
	return 0;
}

The trick is that when the TCL interpreter is instructed to load a shared library, it looks for an initialization function within that library. This plain C function4 is required to have a name of the type Modulename_Init, where Modulename is the filename of the shared library which will contain the new module5. In our case, the name of the library is libfoo.so, so the name of the initialization function is Foo_Init(). The name of the EmbeddedTcl variable is determined by the first argument passed to tcl2c++ which we defined in /local/foo/src/Makefile.am.

It is to be noted that on CYGWIN libraries are named differently - typically, libfoo.so becomes cygfoo-0.dll, as we already discussed when dealing with the installation of the patch for CYGWIN. For this reason, if you care about CYGWIN compatibility, you should also append something like this in your initlib.cc file

extern "C" int Cygfoo_Init() {
     return Foo_Init();
}
so that a proper initialization function can be found and the right code executed on CYGWIN too.

We can finally initialize autotools support by switching to the root directory of our module and running autogen.sh:

baldo@pcsignet08:/locale/foo$ ./autogen.sh

If everything is setup correctly, this needs to be done only once. Now you can follow the standard unix build practice of running ./configure and make. The only difference is that you must pass a --with-ns-allinone=<path> argument to ./configure, so that it can set all the variables we have mentioned before according to the actual path of your ns-allinone installation.

Note that the implementation of the --with-ns-allinone switch which we provide in our nsallinone macro also performs several checks to make sure that the provided path is correct, and that (almost ;-) ) everything which is needed can be found. As an example, have a look at the following output generated by ./configure:

baldo@pcsignet08:/locale/foo$ ./configure --with-ns-allinone=/locale/ns/ns-allinone-2.31
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking for g++... g++

<a lot of output snipped here>

checking for ns-allinone installation... ok
checking if ns-allinone installation has been patched for dynamic libraries... yes
checking for tcl2c++... /locale/ns/ns-allinone-2.31/tclcl-1.19/tcl2c++
configure: creating ./config.status
config.status: creating Makefile
config.status: creating src/Makefile
config.status: executing depfiles commands

Other parameters which may be conveniently passed to ./configure are:

You can also run ./configure --help to examine available arguments, switches and influential environment variables.

That's all... just type make and build your dynamic module for ns!


next up previous
Next: Defining new Packet Types, Up: Developer's manual Previous: Developer's manual
nsmiracle-users mailing list