IRC Services Technical Reference Manual
10. Compilation
10-1. Compilation overview
10-2. The configure script
10-3. The compilation process
10-3-1. Core source files
10-3-2. Modules
10-3-3. Language files
10-3-4. The tools and data directories
10-4. Installation
10-5. Assumptions
Previous section: The database conversion tool |
Table of Contents |
Next section: Future work
10-1. Compilation overview
Services uses the common configure; make; make install method
for compilation. The configure script in the top directory tests
the system compiler and environment to ensure Services can be compiled and
to compensate for differences between systems, such as broken or missing
implementations of system library functions; once it has run, the source
files are compiled using the make tool. Once compilation is
complete, the command make install will install the executable
and data files to their final location on the system as specified by
parameters to the configure script (or using the defaults from
that script).
Back to top
10-2. The configure script
Before the source code can be compiled, certain features of the compiler
and environment need to be checked, and appropriate settings made; this is
performed by the configure script present in the top directory.
The script performs the following operations:
- Defines miscellaneous utility functions.
- Creates a temporary directory, for storing files used in
the tests. The directory name is defined to be "conf-tmp" at the top of
the script. If the directory already exists (because a previous
configure run was aborted, for example), that directory is
used.
- Initializes configuration and command-line option
variables.
- Parses command-line options. See
table 2-2 in section 2 of the main
manual for a list of all recognized options.
- Loads the results of any previous run from the cache
file, config.cache, unless the -ignore-cache option is
given. If the cache was created by an earlier version of the script,
appropriate adjustments are made to the cached results (usually clearing
the affected variable or variables so that the modified test is run
again). Note that the loading is done via a source command,
executing the commands in the cache file directly, which can lead to
security problems if the user is tricked into storing a malicious cache
file in the Services directory.
- Determines installation directories for executable files
(variable BINDEST) and data files (variable DATDEST),
either from the -prefix, -bindest, and -datdest
options, or from the defaults included in the script, as follows:
- If the -prefix option is given with a parameter
PATH, then the executable directory is set to
PATH/sbin and the data directory is set to
PATH/lib/PROGRAM, where PROGRAM is
the program name given to the -program option (or the default of
"ircservices" if the -program option is not given).
- If the -prefix option is not given and the -bindest
option is given, then the executable directory is set to the path given by
the -bindest option.
- If neither of the -prefix or -bindest options are
given, then the executable directory is set to the cached value of the
BINDEST variable if present; otherwise, the default of
/usr/local/sbin is used.
- If the -prefix option is not given and the -datdest
option is present, then the data directory is set to the path given by the
-datdest option.
- If neither of the -prefix or -datdest options are
given, but the -bindest option is given, then the data directory
is set to a path derived from the executable directory, by either replacing
a trailing /sbin or /bin with /lib/PROGRAM
or appending /lib if the executable directory does not end in
/sbin or /bin.
- If none of the -prefix, -bindest, or -datdest
options are given, then the data directory is set to the cached value of
the DATDEST variable if present; otherwise, the default of
/usr/local/lib/PROGRAM is used.
Note that the current Services architecture does not allow data files to be
stored in multiple separate locations.
- Opens the log file (configure.log).
Significant commands run by the script, and the results of all tests, are
logged to this file to aid diagnosis in case of problems. Each test is
given a distinctive name, written at the beginning of each line in the log;
these names are given in the descriptions below.
- (check_test_nt)
Checks whether the shell's built-in test command, if any, or the
system /bin/test or /usr/bin/test command supports the
-nt option for testing whether one file is newer than another.
Some shells, such as /bin/sh on at least some versions of the
Solaris operating system, do not support the -nt test. This test
is required for module compilation
(see section 10-3-2).
- (find_cc)
Determines the program to use to compile source files. This program is
assumed to be able to link object files into an executable as well. If the
-cc option or a cached result is available, they are used in that
order of preference, and subsequent compiler checks are bypassed.
Otherwise, the commands gcc, icc, and cc are
checked in that order, and the first one that is able to compile a short
test program is used as the C compiler. (However, if gcc is
chosen and the version cannot be confirmed to be at least 3.2, the script
aborts; likewise, if another compiler is chosen and it is unable to compile
ANSI C programs, the script aborts.) Finally, the chosen compiler is
tested to ensure that it understands variadic-argument macros and the
va_copy function, C99 features that Services makes use of.
- (find_ccopts)
Determines the command-line options to use with the C compiler for
compiling source code. If the -cflags option is given, the
specified option string is used; if -cflags is not given and the
cached result for the C compiler program name was chosen, the cached option
string is used; otherwise, a default option string is chosen by the
def_cc_flags function. In the latter case, the compiler is
checked for the presence of the GCC
stack-protector
extension [www.research.ibm.com]; at
least some versions of this extension have a bug which causes incorrect
code to be generated, and if the extension is present and this bug is
detected, -fno-stack-protector is added to the default option
string to disable this extension. A test is made with the default flags to
ensure that the compiler accepts them, and if the test fails, an empty
option string is used instead.
Regardless of the method used to find the flags, a final test is made to
ensure that the compilation with the selected flag set does not cause the
stack-protector bug to appear. (This bug relates to the GCC
__builtin_apply and __builtin_return facility for passing
a function call through to a different function, used in Services to handle
imported functions in the database/version4 module. There have
been other bugs in the implementation of this facility as well, so the test
is carefully written to avoid triggering them.)
- (check_gcc_builtin)
Checks for bugs in the implementation of the GCC __builtin_apply
and __builtin_return facility, if understood by the compiler. At
least three bugs are present in various versions of GCC, noted in the GCC
Bugzilla tracker as bugs
8028,
11151, and
20076
[gcc.gnu.org]. Bug 20076 is not relevant
to Services, but if one of the other bugs is detected, the script will
activate assembly-language workarounds for i386, Sparc, and PowerPC CPUs,
and abort on other processors since no workaround is available.
- (find_lflags)
Determines what options to use when linking executables. Uses the option
string given with the -lflags option, the previous cached result,
or an empty string, in that order of preference.
- (find_exe_suffix)
Checks what filename extension, if any, is appended to executable
filenames by the compiler/linker.
- (find_libs)
Determines what additional libraries need to be specified on the link
command line. Checks are made for -lm, -lsocket,
-lresolv, -lnsl, and -lcrypt; if relevant
functions from these libraries cannot be called without the corresponding
-l option, the option is added to the library option string. If
the -libs option is passed to the script, its parameter is
appended to the library option string after all library checks are done.
- (check_shared)
Checks whether dynamic (shared-object) modules can be used. If the
-use-static-modules option is given, dynamic modules are disabled;
if not, and if a cached result is available, that result is used.
Otherwise, a check is first made for the presence of the dlfcn.h
header file, using the test_include function (see the
check_stdint test below). If this file is present, the script
proceeds to check whether -ldl is needed to access the dynamic
loader functions and ensure that -rdynamic and -shared
can be used with the compiler. A check is then made that attempts to open
shared objects with unresolved symbols fail, and that shared objects with
no unresolved symbols can in fact be used properly. If these tests
succeed, a final check is made to determine whether symbol lookups require
an underscore prepended to the symbol name or not. If any test fails,
dynamic modules are disabled.
- (check_ranlib)
Checks whether the ranlib program exists on the system. If a
cached result is not available, a dummy file is created, and the
ar program is used to add it to a dummy archive (if ar is
not available, the script aborts). ranlib is then run on this
archive, and the success or failure of the command is recorded.
- (check_stdint)
Checks for the presence of the stdint.h header. This is done by
calling the test_include function, which first checks whether the
given file exists in /usr/include, then, if the file is not found, checks
whether a test program that includes the file with #include can be
compiled. The variable HAVE_header_name, where
header_name is the header filename converted to upper case
with non-alphanumeric characters replaced by underscores, is then set to 1
or 0 if the test succeeded or failed, respectively. The return value of
the function itself is the success or failure of the test.
- (check_strings)
Checks for the presence of the strings.h header.
- (check_sysselect)
Checks for the presence of the sys/select.h header.
- (check_sysproto)
Checks for the presence of the sys/proto.h header.
- (check_int8)
Determines what type can be used for 8-bit integers. The char
type, and the int8_t type if the stdint.h header is
present, are checked first, giving preference to int8_t if
available; if neither is an 8-bit type, a byte type is checked for
next. If that type does not exist or is not 8 bits wide, the script
aborts. The selected type is used to declare the int8 and
uint8 types. (As described in section
11-1, Services development was started before stdint.h and
the sized integer types were standardized, hence the use of nonstandard
type names.)
- (check_int16)
Determines what type can be used for 16-bit integers, selecting among
int16_t (if present), int, and short in that
order. The selected type is used to declare the int16 and
uint16 types.
- (check_int32)
Determines what type can be used for 32-bit integers, selecting among
int32_t (if present), int, and long in that
order. The selected type is used to declare the int32 and
uint32 types.
- (check_int_size)
Finds the size of the int type. If the int type is less
than 16 bits wide (a violation of the C standard), the script aborts.
- (check_long_size)
Finds the size of the long type. If the long type is less
than 32 bits wide (a violation of the C standard), the script aborts.
- (check_ptr_size)
Finds the size of pointer types. If pointers are smaller than integers,
the script aborts (see section 10-5 regarding this
assumption).
- (check_time_t)
Finds the size of the time_t type. If the time_t type is
less than 32 bits wide, the script aborts.
- (check_gid_t)
Checks for the presence of the gid_t type and finds its size.
- (check_socklen_t)
Checks for the presence of the socklen_t type.
- (check_aix_intNN)
Checks whether system headers define the int16 and int32
types, which clash with type names used in Services. The
sys/systypes.h header in at least some versions of the AIX
operating system seems to define these.
- (check_strerror)
Checks for how system error numbers can be converted to descriptive
strings. The strerror() function is used preferentially if
present; if not, a compatibility function in compat.c implements
strerror(), using either the system's sys_errlist[] array
if present, or a built-in message list if not. However, if the
-use-local-funcs option was given, this test is skipped and the
compatibility function (assuming no sys_errlist[]) is used.
- (check_compat)
Checks for compatibility functions that need to be enabled. At this stage,
the -use-local-funcs option is checked and, if present,
compatibility functions for all functions below through strsignal()
are enabled. If not, and cached results are available for all functions,
those results are used. If not, testing proceeds to each individual
function test.
- (check_hstrerror)
Checks for the presence of the check_hstrerror() function. This
is done by calling the test_function function, which attempts to
compile a test program which calls the function (the contents of the test
program are specified by the called). If the compilation succeeds and the
test program returns a successful exit code (0), the function is assumed to
exist. As with test_include, the variable
HAVE_function_name (where function_name is
the function name in upper-case) and return value are set according to the
test result.
- (check_snprintf)
Checks for the presence and behavior of the snprintf() function
(if found, the vsnprintf() function is assumed to also exist).
Services ignores the possibility of string truncation—which is
probably a security issue in some cases; see also
section 11-1—and expects snprintf()
to return the number of characters actually written into the buffer. If
snprintf() is available but uses a different return value scheme,
a compatibility function translates the return value to what Services
expects. If snprintf() is not available at all, the version
included in vsnprintf.c is used.
- (check_strtok)
Checks for the presence and behavior of the strtok() function.
Some system library implementations of strtok() have been found to
contain bugs, which are checked for by this test.
- (check_stricmp)
Checks for the presence of the stricmp() function. This is
equivalent to the POSIX strcasecmp() function, but has a more
concise and clearer name (I always have to stop and remember that the
"case" in strcasecmp means that case is ignored rather
than that it is compared). If stricmp() is not available but
strcasecmp() is available, the former is made an alias for the
latter via #define.
- (check_strdup)
Checks for the presence of the strdup() function.
- (check_strspn)
Checks for the presence of the strspn() and strcspn()
functions (the variable set is HAVE_STRSPN).
- (check_strsignal)
Checks for the presence of the strsignal() function.
- (check_gettimeofday)
Checks for the presence of the gettimeofday() function.
- (check_setgrent)
Checks for the presence of the setgrent() function. However, if
the gid_t type does not exist, the test is not executed and the
function is assumed to not exist.
- (check_setregid)
Checks for the presence of the setregid() function. However, if
the gid_t type does not exist, the test is not executed and the
function is assumed to not exist.
- (check_umask)
Checks for the presence of the umask() function.
- (check_fork)
Checks for the presence of the fork() function.
- (check_gethostbyname)
Checks for the presence of the gethostbyname() function.
- (check_getsetrlimit)
Checks for the presence of the getrlimit() and setrlimit()
functions.
- (check_crypt)
Checks for the presence of the crypt() function.
- (check_install)
Checks whether a working install program exists on the system. If
not, the install-script script included in the top source
directory is used instead.
- (check_install-d)
Checks whether install -d can be used to create directories. If
not, mkdir is tried as a workaround, falling back to the
install-script script if necessary.
- (check_copy_recurse)
Determines what command should be used to copy entire directories. The
default command is cp -dpr in Linux and Cygwin environments, and
cp -pr in other environments. If this command does not work, the
tar command is used as a substitute if possible (see the
cp-recursive script in the top source directory). If tar
cannot be used either, the script aborts.
- Creates the file config.h, containing the
results of configuration relevant to the source code (such as compilation
options and required compatibility functions) as #define
preprocessor macros. If the config.h file already exists and the
content of the new config.h to be written is unchanged from that
in the existing file, the existing file is left untouched, so as not to
cause an unneeded recompile of all source files if configure is
re-run with identical parameters.
- Creates the file Makefile.inc, containing the
results of configuration relevant to compilation commands (such as the
compiler command and options and installation directories) as make
variables. As above, if Makefile.inc already exists and its
content has not changed, the old file is left alone.
- Creates the cache file config.cache, saving the
results of configuration so that a subsequent run of the configure
script will execute faster. The file is written as a sequence of shell
commands, so that it can simply be sourced at runtime rather than parsing
each line individually.
Note that a deliberate decision was made to not use the GNU
autoconf/automake/libtool suite of tools, as
they are overly complex for the range of systems Services is expected to be
used on. (I have seen far too many programs where running the
autoconf-generated configure script takes longer than compiling
the program itself.)
Back to top
10-3. The compilation process
Overall compilation is controlled by the Makefile in the top
source directory. When run with no target specified, the default target
all is used, which first checks that the configure script
has been run (aborting with a notice to the user if not), then proceeds to
actual compilation with the myall target. This target compiles
the main program (ircservices or ircservices.exe), then
generates the language files and creates the convert-db and
ircservices-chk tools.
Additional targets available are: install, which installs
program and data files to the appropriate directories (see
section 10-4); clean, which removes most
generated files, such as object and executable files; and
spotless, or distclean in the GNU style, which removes
all generated files (including config.cache). All of these
targets are called recursively for the modules, lang, and
tools directories.
The main Makefile also includes two rules, for services.h and
language.h, which cause those files to be touched (the
file timestamp updated) whenever any sub-header file changes. This is
done because it is considered easier than ensuring that every dependency
list stays up to date with all sub-header files.
Back to top
10-3-1. Core source files
Compilation of the main executable starts with compilation of the core
source files, stored in the top source directory. The object files to be
created are listed in alphabetical order in the $(OBJS) variable.
The options used for compilation are -DSTATIC_MODULES if using
static modules, followed by the options selected by the configure
script (in the $(BASE_CFLAGS) variable), followed by the options
in the $(MORE_CFLAGS) variable (defined at the top of the
Makefile, intended for users to add or change extra options on the fly).
After compiling all of the core source files, compilation proceeds to
the modules, as described below; finally, a version.c file is
generated by the version.sh script, containing the program version
number and a build number (extracted from the previous contents of
version.c) which is incremented by one each build, and this file
is compiled and linked with the rest of the main source files, as well as
the modules when compiling modules statically, to produce the main
executable.
Note that one core header file, langstrs.h, is copied from the
language file subdirectory, where it is first generated if necessary; see
section 10-3-3 below for details.
Back to top
10-3-2. Modules
Compilation of modules is handled by modules/Makefile and its
auxiliary file, modules/Makerules. The top directory's Makefile
calls one of two targets in modules/Makefile to compile all
available modules: all-dynamic if dynamic linking is in use,
all-static for static linking. Both of these targets perform the
same basic function: search for all subdirectories of the modules
directory that contain Makefiles, and call the all-dynamic or
all-static target in each subdirectory's Makefile.
Since the subdirectory search is performed at compilation time, it is
possible to add new modules to Services by simply copying the directory
containing the module source code and Makefile into the modules
directory. This is in fact the recommended method for installing
third-party modules, and the module compilation system was designed with
this aim in mind.
Of the two main targets in modules/Makefile, the
all-dynamic target is the simpler of the two. It loops through
all subdirectories, calling the all-dynamic target in each, then
updates a dummy .stamp file if the subdirectory's .stamp
file was updated. This file is used by the main Makefile to determine
whether version.c should be regenerated.
For static modules, the process is slightly more complicated, since a
list of all modules and exported symbols must be provided to the core
module manager. In addition to calling each subdirectory's
all-static target, the .modext-*, .modsyms-*,
and .modlist-* files in each subdirectory are concatenated to
form module and symbol lists; these are then compiled into an additional
object file, modlist.o, and an archive (modules.a) is
created containing this file and all module object files. This archive is
then linked into the final executable.
The compilation of individual modules is handled by the
modules/Makerules file, which is included by each subdirectory's
Makefile. This file is designed so that individual module Makefiles need
only set up some variables with lists of files to be compiled and include
the Makerules file. However, due in part to limitations of the
GNU make program, the actual implementation is quite complex, and
as a side effect it takes make considerable time to process the
module subdirectories even when no files need to be recompiled.
The limitation in make which I found no easy way around during
development is that there is (or was; see the
relevant note in section 11-1 for a method that works with newer
versions of GNU make) no way to specify a rule in which the name
of a variable specifying a dependency varies with the target name. For
example, suppose a subdirectory contains two modules, module1 and
module2. If each module consists of only one source file and has
no other dependencies, the rule is simple (assuming dynamic modules, and
assuming the presence of a %.o: %.c compilation rule):
%.so: %.o
$(CC_SHARED) $^ -o $@
However, suppose that module1 depends on file aux1.o
and module2 depends on files aux2.o and aux3.o.
If these are specified in $(OBJECTS-module1.so) and
$(OBJECTS-module2.so), one might then be tempted to write:
%.so: %.o $(OBJECTS-%.so)
$(CC_SHARED) $^ -o $@
Unfortunately, this does not substitute module1 or
module2 in the $(OBJECTS-%.so) reference, but uses the
value of the variable literally named OBJECTS-%.so. Nor does it
work to use $@ instead of %.so in the variable name:
%.so: OBJECTS = $(OBJECTS-$@)
%.so: %.o $(OBJECTS)
$(CC_SHARED) $^ -o $@
Here, $(OBJECTS) is only defined for commands inside the
pattern rule, so it cannot be used in the dependency list. Likewise,
$@ is only defined for the command list, so specifying
$(OBJECTS-$@) in the dependency list also does not work.
The upshot of all this is that in order to have a dependency list that
varies with the target, it is necessary to call make recursively.
The bulk of the Makerules file is dedicated to handling this
recursive calling and setting variables appropriately for each target.
The Makefile for a module directory typically consists only of variable
definitions followed by the line "include ../Makerules". Chief
among the variables required is the $(MODULES) variable, which
lists the modules contained in the directory; each module is specified with
a .so extension (static modules are compiled to .a
archives, but the filename translation is handled transparently by the
Makerules file), and is assumed to be compiled from a source file
of the same name with a .c extension. If any additional object
files are required by the module, they are specified in a variable named
$(OBJECTS-module.so), where module is
replaced by the module name. Header file dependencies can be specified
through the $(INCLUDE) variable for dependencies common to all
object files, and $(INCLUDE-object.o) for a particular
object file. If any object requires a special rule for compilation, that
rule can be written in the Makefile, but rules are not needed for the
ordinary case of compiling a .c source file to a .o
object file.
When the subdirectory's all-dynamic or all-static rule
is first invoked, the script first checks, via pattern rules, that no
modules have names ending in _static (which would conflict with
the static module compilation procedure, as described below) or beginning
with a period (which would conflict with the temporary files created during
compilation). Then, for each module, make is called recursively
with the $(TARGET) variable set to the target module name (with
no filename extension), $(OBJECTS) variable set to the contents of
the particular module's object list ($(OBJECTS-module.so)),
and $(REALLY_COMPILE), the recursion level variable, set to 1.
When called with $(REALLY_COMPILE) equal to 1,
Makerules instead defines a rule for the target file,
$(TARGET).so or $(TARGET).a For dynamic modules, this
simply involves compiling the relevant files and linking them into a
shared object; for static modules, however, the objects will eventually be
archived into a single modules.a archive, so no .a file
for the individual module is actually needed. What the rule does instead
is to record each object file's name in the .$(DIRNAME).lst file,
which is then used by the all-static rule to link all object files
in the directory into a single object $(DIRNAME).o for storing in
the modules.a archive. (A dummy $(TARGET).a file is also
created so that make can perform its file timestamp checks.) In
addition, since the static module manager requires a list of names and
pointers for all variables and functions exported by each module, the
static module compilation rule searches through all source files—each
object file is assumed to correspond to a single source file with the same
name and a .c extension, and no header files are checked—for
EXPORT_VAR(), EXPORT_ARRAY(), and EXPORT_FUNC()
macro invocations. The export information given to these macros, along
with declarations for the implicitly exported variables
_this_module_ptr and module_version, array
module_config[], and functions init_module() and
exit_module(), are written to two temporary files:
.modext-module.h, containing extern declarations,
and .modsyms-module.c, containing the actual symbol
entries. A line with the module name and symbol array pointer is also
written to .modlist-module.c for later inclusion in the
overall module list.
For each individual object file, Makerules first (at recursion
level 1) generates a rule for compiling the object file that calls
make recursively, with $(TARGET) now set to the object
file's base filename (with the .o extension stripped),
$(INCLUDES2) set to the particular object file's dependency list
from $(INCLUDES-object.o), -DMODULE and
-DMODULE_ID=module-id appended to $(CFLAGS)
(module-id is a C-style identifier derived from the
directory and module name, used to make common module identifiers unique),
and $(REALLY_COMPILE) set to 2. In addition, for the main file
of a module (the source file with the same name as the module),
-DMODULE_MAIN_FILE is also appended to $(CFLAGS).
In this second recursion level, the actual source file compilation is
performed. The object.o rule does not perform the actual
compilation, but depends on a .compiled-object.o dummy file
whose rule performs the compilation and on a dummy FRC (force)
rule; this hack prevents make from outputting "nothing to do"
messages for every unchanged object file. The compilation command itself
is similar to that used for the core source code, but the command is
prefixed by a cd to the top directory, so that the relative path
to the source file is saved in the object file's debug information. This
allows debuggers to easily find the proper source file, even if multiple
module subdirectories have identically-named source files.
For static modules, a slight change is made for the module's main source
file: rather than compiling the source file to module.o,
the object file is given the filename module_static.o, and
the five implicitly exported variables/functions (init_module(),
etc.) are renamed via -D options to names containing the
module-id so that they do not cause symbol conflicts with
other modules at link time.
Back to top
10-3-3. Language files
The language files, stored in the lang directory, are
precompiled into binary format to speed the startup of Services, as
described in section 2-8-4. This precompilation
is performed by the langcomp program, compiled from
langcomp.c. When called with the all target, the
Makefile first compiles langcomp, then runs it on each language
source file to generate the corresponding precompiled binary file. In the
special case of the Japanese language files (ja_*.l), which
correspond to the various encodings common on Japanese computer systems,
the EUC file (ja_euc.l) is treated as canonical, and when it
changes, the jconv.pl script is automatically run to regenerate
the ja_sjis.l file before compiling it to binary format. (There
was also a JIS-encoded file, ja_jis.l, in the past, but this was
dropped because of extra % characters in the text causing printf()
functions to break.)
The list of standard language strings is taken from the English language
file, en_us.l; a simple grep is used to extract the
string names to the index file, and this file is then used to
generate langstrs.h, which contains the string names both as
preprocessor (#define) constants and as a string array, available
if LANGSTR_ARRAY is defined. The core source file
language.c uses this array for looking up string names when
loading external language files at runtime.
Back to top
10-3-4. The tools and data directories
The tools directory contains two additional programs:
convert-db, the database conversion tool discussed in
section 9, and ircservices-chk, a simple
script designed to run from a periodic execution utility such as
cron to ensure that Services is restarted if it should stop for
any reason (such as a crash).
In addition to the main source file convert-db and the
convert-*.c source files that handle particular database types,
convert-db makes use of three source files from the main source
code: compat.c, containing compatibility functions;
modules/database/fileutil.c, containing routines to read and write
data in binary database files; and modules/misc/xml-export.c,
containing routines to generate an XML file from loaded data. These three
files are compiled using special rules, which include the
-DCONVERT_DB compiler option to trigger special handling in the
source files for the convert-db tool.
The "compilation" of ircservices-chk consists of simply
replacing the @PROGRAM@, @BINDEST@ and @DATDEST@
fields in the template file ircservices-chk.in with the actual
file/pathnames, writing the output to the file ircservices-chk
(more precisely, $(PROGRAM)-chk, where $(PROGRAM) is the
value of the -program option given to the configure
script), and marking that file executable with chmod.
Likewise, the two sample configuration files
(example-ircservices.conf and example-modules.conf) in
the data subdirectory are generated from template files, replacing
occurrences of @PROGRAM@ with the actual program name to give
appropriate defaults for various file names.
Back to top
10-4. Installation
The install target, which like all defers actual work
to the myinstall target, first creates the target directories,
$(BINDEST) and $(DATDEST), if they do not exist; copies
the main executable file to the $(BINDEST) directory; and calls
the install target in the modules, lang,
tools, and data directories. However, when static
modules are being used, the module install is skipped (since the module
objects are linked directly into the executable, there is nothing to
install).
The module installation consists of calling the install target
in each module subdirectory; this target, declared in the
modules/Makerules file, creates a subdirectory of the same name
inside the modules directory under $(DATDEST), then
copies all module shared-object files into that directory.
The language installation first creates a language directory
under $(DATDEST); the precompiled language files are then copied
into that directory.
The tool installation copies the ircservices-chk script to
$(BINDEST), and the convert-db program to
$(DATDEST). The latter is not copied to the executable file
directory to avoid the possibility that the executable file name conflicts
with another program installed on the system. (A better solution might be
to rename the program to something like ircservices-convert.)
The data file installation copies the example configuration files,
example-ircservices.conf and example-modules.conf, to
$(DATDEST); example-ircservices.conf is renamed at this
time to example-$(PROGRAM).conf. If the helpfiles
directory does not exist under $(DATDEST), it is copied from the
helpfiles subdirectory of the data directory.
Since the install target depends on the all (or more
precisely, the myall) target, it is also possible to perform
compilation and installation in one step by simply executing make
install.
If the variable INSTALL_PREFIX is set, its value is prepended
to all pathnames used for installation; for example, the
ircservices executable file is installed to
$(INSTALL_PREFIX)$(BINDEST)/ircservices. This allows Services to
be installed to an alternate root directory, such as when preparing a
chroot'd environment or a distribution image. This variable is not set by
any of the Makefiles, but can be set on the make command line.
(Note that there is no slash after $(INSTALL_PREFIX); inserting
one would have the side effect of prefixing $(BINDEST) and
$(DATDEST) with a slash when no prefix was given, which could
potentially have undesired side effects.)
Back to top
10-5. Assumptions
Finally, it is worth noting a few assumptions made by the source code.
These are all believed to hold on any system Services is likely to be used
on, and some are double-checked by the configure script.
- Pointer values are at least as large as int
values. The module system uses void * as type
placeholders for module callbacks, as described in
section 4-5-3, and some database management
routines, such as in the OperServ news and autokill modules (see
section 7-2-2-1, for example) store integer
values in pointer fields. While these are admittedly very crocky ways of
doing things, they do require that pointers be at least large enough to
hold values that may be stored in them. The configure script
checks that pointers are at least as large as ints, and aborts if
not.
- Character values are exactly 8 bits. More
precisely, a char variable is exactly one byte in size (whatever
size a byte may be). This is not checked by the configure script,
but it is mandated by the C standard and should not be a problem.
- Unprototyped routines can accept up to five arguments
regardless of type. Or to be more accurate, all of the first five
arguments to a routine are stored in same-size locations regardless of
type. This requirement is a side effect of the module callback calling
method mentioned above, and holds on at least x86 (where everything except
long long is 32 bits) and x86-64 (where the first five arguments
are all passed in registers). Note that this is not checked by the
configure script.
- Pointer aliasing (type-punning) is allowed. The
C standard disallows "aliasing" of pointers of different types (C99 6.5 (7));
in other words, if you have a pointer variable struct foostruct
*foo, you may not assign the value of foo to struct
barstruct *bar and then modify the contents of *bar. This
sounds like a good idea on the surface, but it turns out to be quite
inconvenient in practice; in particular, it prevents the use of "derived
structures", where the first member of one structure is a second structure
(similar to a derived class in object-oriented languages). It would in
theory be possible, if unduly verbose, to rewrite the code to obey the
aliasing rules; but for convenience, Services simply assumes that this sort
of aliasing is permitted. The configure script does not check for
this directly, since any effect would be compiler-dependent and difficult
(if not impossible) to detect reliably, but the script does enable the
-fno-strict-aliasing option when compiling with GCC, which
disables optimizations that rely on this aliasing rule.
- The NULL pointer is bitwise zero for all
pointer types. For the sake of efficiency (and concise source code),
arrays or structures containing pointers are often cleared by using
calloc() to allocate precleared memory or memset() to
fill the memory region with the byte value 0, and it is assumed that these
operation are equivalent to individually assigning the NULL value
to each pointer. The C standard does not require that NULL be
stored in memory as the value zero, only that it compare equal to zero, but
every system I have used so far uses bitwise zero for NULL, so I
have not made an effort to write "correct" code. Note that the
configure script does not check for this.
Back to top
Previous section: The database conversion tool |
Table of Contents |
Next section: Future work