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:
--disable-static
to disable building of static libraries, which are useless since they cannot be loaded within TCL;
--prefix=<path>
to specify an installation path where to install the library when you type make install
. Remember that a subdir lib
will be added, i.e., if you use --prefix=/locale
then libraries will be installed in --prefix=/locale/lib/
./configure --help
to examine available arguments, switches and influential environment variables.
That's all... just type make
and build your dynamic module for ns!