Makefiles
Michael Himsolt
Makefiles
This section describes how Graphlet's configuration system can be
used to add makefiles for modules. The source code
distribution must be installed to take advantage of these features.
This section requires basic knowledge of how make programs
work.
Overview of the configuration system
Graphlet uses GNU make for its configuration. This
version of make has some features which are not normally present in
make programs, such as extended macro processing and
if...{then...{}else} structures. We chose GNU make over
preprocessor based systems because the latter one would be harder to
debug.
One side effect of using GNU make is that we are using the name
GNUmakefile instead of Makefile or
makefile. This has the advantage that any other
make program will not even try to source the file and report
syntax errors, but will report that it could not find a make
file and exit. We feel this is a cleaner approach and
gives better error messages. Also, our coding standards require to
use the name GNUmakefile for makefiles.
All of Graphlet's configuration files are located in
lib/graphlet/config. The default installation procedure
copies the configuration system to
Graphlet Installation Directory/lib/graphlet/config.
The configuration system does not only determine the proper compiler
flags and procedures for installing Graphlet on a particular
system, but also provides a large set of predefined make variables and
targets.
To get access to the predefined variables and procedures, every
GNUmakefile must include the file
lib/graphlet/config/common. Since the information in
common relies on information about the site installation, it
can only be used with a source code distribution.
Anatomy of a GNUmakefile
An example for a GNUmakefile can be found in the algorithms
module. A GNUmakefile must at least contain the following:
- Define of the variable MODULE:
\begin{quote}
MODULE = insert the name of the module here
\end{quote}
MODULE is the name of the module in which the
GNUmakefile resides.
- Define the variable GRAPHLET_BASE_DIR:
\begin{quote}
GRAPHLET_BASE_DIR=insert the relative path to the
toplevel of Graphlet
\end{quote}
GRAPHLET_BASE_DIR must be the relative path to the
toplevel directory of Graphlet. For example, if your module is in
src/mymodule, then set
\begin{quote}
GRAPHLET_BASE_DIR=../..
\end{quote}
-
Define a standard target
all which is executed when
no arguments are given to make (which is usually the case):
\begin{quote}
all: lib\(MODULE).a \(SUBDIRS)
\end{quote}
This defines that the default actions are building the
object libraries, and recursively traversing the subdirectories.
For a module which has no default actions and no
subdirectories, use
.PHONY: all
all:
Insert the actions for target all here
The directive .PHONY: states that the following target(s)
have no dependencies.
- Include the common configuration definitions:
\begin{quote}
include\(Graphlet_BASE_DIR)/lib/Graphlet/config/common
\end{quote}
Standard Variables
- SUBDIRS
-
The variable
SUBDIRS holds a list of subdirectories.
Most targets will recursively descend into subdirectories.
- TCLFILES
-
TCLFILES is a list of Tcl files. This target
index constructs a tclIndex
file (for autoloading)
- MYCFILES
-
MYCFILES is the list of C/C++ files which
are not generated by a program (e.g. yacc or lex).
- CFILES
-
CFILES is the list of all C/C++ files,
including those which generated by a program (e.g. yacc or lex).
- HFILES
-
HFILES is the list of C/C++ Header files.
HFILES can be generated from MYCFILES as follows:
\begin{quote}
HFILES = \(CFILES:\
\end{quote}
You must assign values to both MYCFILES and
CFILES.
The convention that there is a header file for each C++ file is
essential for configuration system to work correctly.
Standard Targets
- it,
all -
it and all must the default targets
which are executed when no targets are given to make.
Both targets it and all are required.
- install.local
-
This target is used to perform installation operations which are
specific for the local directory. \TBD{}.
Modules
This section describes how to design Graphlet modules. A
module is a set of C++ and Graphscript files which
implement one or several algorithms. The core of Graphlet itself is
divided into three modules:
- The module
gt_base, which implements basic and Tcl
independend data structures.
- The module
gt_tcl, which implements Graphlet's Tcl
interface. Most of Graphscript is implemented here.
- The module
gt_algorithms implements various algorithms.
Each module has a name, which must be unique throughout the Graphlet
system. There are no restrictions on the name, but it is usually a
good idea to choose a descriptive name. Later, the header files of a
module name will be installed in a directory
name\footnote{As a subdirectory of the Graphlet installation
directory, e.g. /usr/local/include on UNIX systems.}, and
its library will be installed under the name
libname.a\footnote{On UNIX systems}.
Graphlet uses the prefix gt_ for its names to prevent name
clashes with other software packages.
To avoid cluttering the system with too many libraries, modules should
contain several algorithms, and should possibly further structured
into submodules.
Guidelines for adding C++ modules
Example
An example module can be found in the directory
src/gt_algorithms in the Graphlet distribution.
- The initialization file of the algorithms module:
\begin{quote}
src/gt_algorithms/algorithms.cpp
\end{quote}
- Header file of the initialization procedure of the algorithms module:
\begin{quote}
src/gt_algorithms/algorithms.h
\end{quote}
The Initialization File
A module mod must provide its initialization code in a file
named mod.cpp (respectively. mod.h).
The following headers are required for module initialization files:
-
#include
-
#include
-
#include
The Initialization Procedure
The initialization procedure for a module mod must be declared
as follows:
int GT_mod_init (Tcl_Interp* interp, GT_GraphScript* graphscript)
where interp is the current Tcl interpreter, and
graphscript is a pointer to the current Graphscript class.
The initialization procedure must return TCL_OK if the
initialization was successful, and TCL_ERROR otherwise.
The naming convention is necessary to use the standard
initialization macros with application_init.
Initialization procedures must never make any
assumptions on the sequence in which initialization procedures are
called. The only valid assumption is that Graphlet's
initializations are called before any algorithm modules are
initialized.
Guidelines for adding Graphscript modules
Placement
During development, Graphscript files are best kept in the
developers directory. This is best done with autoloading (see the Tcl
documentation for details). For inclusion in the source code
distribution, Graphscript files should be placed in the directory
lib/graphscript within Graphlet, or in subdirectories of
lib/graphscript.
Filenames in lib/graphscript
The filenames should reflect the name of the module:
- If there is only one Graphscript file, it should have the
same name as the module (with suffix
.tcl).
- If there are several files, organize them in a subdirectory
with the name of the module.
GNUmakefile modifications
To add files to the Graphscript library, edit the file
lib/graphscript/GNUmakefile
and add the files to the list of files in the variable
TCLFILES. It is also neccessary to re-create the Tcl index
after new files were added or changed. This can be done with the
following command:
\begin{quote}
gmake index
\end{quote}
where gmake is GNU make.
Subdirectories do not need to have their own GNUmakefile's.
Instead, add all files to lib/graphscript/GNUmakefile.
Remember to use relative paths.
Initialization
There are two ways to initialize a Graphscript module:
-
Tcl will execute any statements at the top level of a
Tcl file when it loads the file. Put initialization code
here.
-
(Preferred)
Implement an initialization procedure in Tcl (preferably called
GT_init_name for module name) and execute that
procedure in the initialization procedure of the C++ code.
Here is an example:
code = Tcl_Eval (interp, "GT_name_init");
if (code == TCL_ERROR) {
return code;
}
The Tcl initialization procedure should come at the end of the
C++ module initialization, after all Graphscript commands have
been installed.
How to determine what is installed
Before features are used which are implemented in an optional module,
you need to check whether the module is present. Generally,
Graphlet installations have the freedom to install or de-install
any module. Therefore, it is illegal to make any a priory
assumptions about installed modules. It is however possible to obtain
information on whether a specific feature is installed:
- C++
-
For each module name, Graphlet defines a preprocessor
symbol GT_MODULE_NAME (note the capital letters). This
can be used to write code which is only executed when a specific
module exists:
#ifdef GT_MODULE_NAME
... put code here that needs module name here ...
#endif
- Graphscript
-
Tcl goes even one step further than C++ and allows runtime testing
for installed commands with
info commands. Here is an
example how to use that:
if { [info commands cmd_name] != {} } {
... Put code here that needs command cmd_name ...
}
\begin{note}
It is generally a good idea to add menu entries only for those
commands which are actually installed.
\end{note}
Building Graphscript Interpreters
For a quick take, copy and modify Graphlet's graphscript.cpp.
After new Graphscript commands have been implemented, it is
necessary to build a new interpreter which contains these new
commands. Technically, this the same procedure as needed for a
standard Tcl interpreter, with Graphscript as an additional
module.
\begin{note}
Tcl/Tk now supports dynamic loading. We will change the
initialization procedure at some point in the future to change
support dynamic loading. Stay tuned. There should be no major
problems unless you use global variables, in which case there will.
\end{note}
The main procedure
Generally, a main procedure for a Graphscript interpreter should
have the following form:
int main (int argc, char **argv)
{
return GT_GraphScript::gt_main (argc, argv, application_init);
}
GT_GraphScript:gt_main is standard method which calls Tk's
Tk_Main procedure (see the Tk documentation for
details) and parses the command line in argc and
argv for GraphScript specific options.
application_init must be provided by the application
and initializes Graphscript and algorithm modules.
It is legal to use the procedure Tk_main
instead of GT_GraphScript:gt_main.
The procedure application_init
Each interpreter must provide a procedure application_init
which must have the following form:
//
// application_init (Tcl_interp* interp)
//
// interp is the current Tcl interpreter.
//
static int application_init (Tcl_Interp *interp)
{
//
// Create a GraphScript handler.
// For customization, replace the class GT_GraphScript
// by a derived class.
//
GT_GraphScript* graphscript = new GT_GraphScript (interp);
//
// Initialize Tcl, Tk and GraphScript.
//
// code holds the Tcl return code.
//
int code = graphscript->application_init (interp);
//
// Check for an error. This step is mandatory.
//
if (code == TCL_ERROR) {
return code;
}
//
// Initialize other algorithm modules
//
#ifdef GT_MODULE_ALGORITHMS
GT_ADD_MODULE(gt_algorithms)
#endif
#ifdef GT_MODULE_LSD
GT_ADD_MODULE(gt_lsd)
#endif GT_MODULE_LSD
#ifdef GT_MODULE_GRID_ALGORITHMS
GT_ADD_MODULE(gt_grid_algorithms)
#endif GT_MODULE_LSD
return code;
}
We recommend the above syntax for the initialization of other
modules, provided that these modules conform to the standards
described in the module documentation.
Graphlet's configuration system automatically defines a
macro GT_MODULE_NAME for each installed module name
(note the capital letters).
The macro GT_ADD_MODULE is provided by Graphlet and
performs all actions necessary to initialize a standard module. It
will exit the procedure and return TCL_ERROR if
the module initialization fails.
application_init must return a Tcl error code, that
is TCL_OK for a successful return or TCL_ERROR
otherwise.
Because Tcl is a C library, application_init must be
a procedure or a static member function.
Linking the program
\begin{WhoCanSkipThis}
This section is meant as a guideline for developers who do not use
the binary distribution. Developers who use the source code
distribution may use Graphlet's configuration system to create
makefiles.
\end{WhoCanSkipThis}
Link our code with the following libraries:
- Graphlet libraries
-
MODULES -lgt_tcl -lgt_base where MODULES is
the list of modules as used in application_init above.
- Tcl/Tk libraries
-
-ltk -ltcl
- GTL libraries (release 3.4)
-
-lP -lG -lL
- X11 and system libraries
-
This depends on your operating system, flavor of X11 and
local installation policy. Here are some recommendations:
- On solaris systems (2.4/2.5), we recommend
-L/usr/openwin/lib -lX11 -lsocket -lnsl -ldl -lm.
- On SunOS systems (4.1.*), we recommend
-L/usr/openwin/lib -lX11 -lm
- On Linux systems, we recommend
-L/usr/X11/lib -lX11 -ldl -lm
If you are linking with GTL 3.3.*, you need to add the library
-lWx.
The order in which libraries are linked is important.
We recommend to link libraries in the order indicated above.
\end{document}