.\" format with
.\" tbl % | xroff -ms | lpr
.\"
.\" revision date - change whenever this file is edited
.ds Rd 28 January 1991
.\" document revision number - change each time document is released
.ds Rn 1.05
.\"
.nr PO 1.2i	\" page offset 1.2 inches
.nr PD .7v	\" inter-paragraph distance
.\"
.EH 'Imake in X11R4'- % -''
.OH ''- % -'Imake in X11R4'
.OF 'Revision date:\0\0\*(Rd'\s+1\*-DRAFT SCRIBBLINGS\*-\s-1'Printed:\0\0\n(dy \*(MO 19\n(yr'
.EF 'Revision date:\0\0\*(Rd'\s+1\*-DRAFT SCRIBBLINGS\*-\s-1'Printed:\0\0\n(dy \*(MO 19\n(yr'
.\"
.\"
.\" I - italic font (taken from -ms and changed)
.de I
.nr PQ \\n(.f
.if t \&\\$3\\f2\\$1\\fP\&\\$2
.if n .if \\n(.$=1 \&\\$1
.if n .if \\n(.$>1 \&\\$1\c
.if n .if \\n(.$>1 \&\\$2
..
.\" start block.  LP gives a bit extra space. Can say .Ds .IP C, etc.
.de Ds
.if \\n(.$<1 .LP
.if \\n(.$>=1 \\$1
.if \\n(.$<2 .DS
.if \\n(.$>=2 .DS \\$2 \\$3 \\$4 \\$5
..
.\" end block.  If arg is given, it replaces the .LP (e.g., .De .IP).
.de De
.DE
.if \\n(.$<1 .LP
.if \\n(.$>=1 \\$1 \\$2 \\$3 \\$4 \\$5
..
.TL
Using Imake to Configure
.br
the X Window System
.br
Version 11, Release 4
.AU
Paul DuBois
dubois@primate.wisc.edu
.AI
Wisconsin Regional Primate Research Center
Document revision:\0\0\*(Rn
Revision date:\0\0\*(Rd
.AB
The X Window System\(dg
.FS \(dg
X Window System is a trademark of the Massachusetts Institute of Technology.
.FE
is a large software project that, despite its size,
is remarkable in its portability.
Much of this is due to the method employed to configure the X
distribution for building and installation: use of a small number of
tools and isolation of machine dependencies into a small number of
files that can be easily maintained and modified.
This document discusses one of those tools,
.I imake ,
and the design of the configuration files used in conjunction with it.
.AE
.NH
Introduction
.LP
This document describes how
.I imake
configuration files are set up in the X Window System.
It is assumed that you know something about what
.I imake
is supposed to be used for (in general),
and that you're reading this to find out something
about how that is accomplished in X (in particular).
The description applies to X Version 11, release 4 configuration files, which
are organized quite differently than those from previous releases.
.LP
Other documentation (from the X11R4 distribution) that you may find useful
includes:
.IP \(bu
Section 2 of ``X Window System, Version 11 Release 4 Release Notes,''
by Jim Fulton.
.IP \(bu
.I mit/doc/config/usenixws/paper.ms :
``Configuration Management in the X Window System,'' also by Jim Fulton.
.IP \(bu
.I mit/config/README :
``X Window System Imake Configuration Guide, Release 4.''
.IP \(bu
.I mit/config/imake.man :
manual page for
.I imake .
.IP \(bu
.I mit/util/makedepend/mkdepend.man :
manual page for
.I makedepend .
.IP \(bu
.I contrib/doc/imake/imake.tex :
``An
.I Imake
Tutorial,'' by Mark Moraes.
This was written between R3 and R4, but is worthwhile reading nonetheless.
.LP
You should also have access to the X configuration files (in
.I mit/config ).
These are not really documentation as such,
but it is expected that you have them at hand, for comparison
against the descriptions below.
.LP
.I imake
is generally conceded to have a pretty steep learning curve.
The
.I README
referred to above notes that
.I imake
``can be somewhat tricky to master,'' an observation attested to by
many who use it.
There seem to be three camps regarding
.I imake ;
those who think it's wonderful, those who think it's wretched, and those
who suspect it might be useful (else why would it be used to configure
a major publicly-available effort like X?) but are puzzled by it.
The present document was written to fill in some of the gaps in the
existing documentation, in order to try to swell the ranks of the first
group by depleting the membership of the third.
(Those who despise
.I imake \*-members
of the second group\*-have good reasons for doing so and the present
effort is not likely to sway them.
The author of
.I imake
is in this group.)
.LP
Where appropriate, the current X configuration files are compared to those
from previous releases to show how limitations of earlier configuration
architectures have been eased.
There are also occasional comments to indicate how
.I imake
and the X configuration files might be adapted for use in contexts
other than building X.
.LP
This document is independent of the efforts of the MIT X Consortium.
There are no doubt errors lurking within, both of understanding and of fact;
they are my own.
Please send corrections, criticisms and comments to the address above.
.LP
The overall organization followed here is:
.IP 1.
Introduction.
.IP 2.
X configuration tool description\*-the programs you use.
.IP 3.
X configuration file description\*-what each file is used for, how each fits
into the configuration process.
.IP 4.
How to build
.I imake .
.IP 5.
How to build X.
.IP 6.
Configuration file bugs.
.IP 7.
How to write your own
.I Imakefile .
You can read this section without reading the rest of the paper,
maybe.
.IP 8.
How to make mistakes writing an
.I Imakefile .
.IP Appendix
Listing of the symbols used in each configuration file, and
what they're used for.
.NH
The tools: imake, makedepend, xmkmf
.LP
.I imake
is a configuration tool\*-the main tool used to configure X.
It is not a replacement for the
.I make
program, but it does assume the availability of
.I make
as the tool used to direct project building and installation.
What
.I imake
provides is an alternative to writing
.I Makefiles
by hand.
The name means ``include-make''\*-\fIimake\fR
uses the C preprocessor
.I cpp ,
taking advantage of its include-file and macro-processing
facilities for the purpose of generating
.I Makefiles
suitable for
.I make .
.LP
For each directory in the X distribution,
.I imake
reads a bunch of
configuration files that go to a lot of trouble to compensate for and
adjust to the individual characteristics of your system.
These are combined with an
.I Imakefile
from the current directory and the whole mess is sent through
.I cpp
to build a
.I Makefile
there.
The same configuration files are used to build every
.I Makefile ;
they are kept together in
.I mit/config ,
isolating machine dependencies in one location to ease development
and maintenance.
In contrast, the
.I Imakefile
is directory-specific (it specifies the targets to be built in that directory)
and is machine-independent.
.LP
This means that when the distribution is built on a different machine,
only the configuration files need be changed.
The
.I Imakefiles
do not need to be.
If X configuration were specified directly using
.I Makefiles ,
this would not be true.
Because
the configuration information in
.I Makefiles
is not portable, each one would have to be edited individually\*-bad
enough for a single
.I Makefile ,
but an overwhelming prospect for a project the size of X.
.LP
The value of being able to localize machine-dependent configuration
information into one place should not be underestimated, particularly
as X becomes more sophisticated and the number of systems on which it
runs increases.
The number of
.I make
variables used to build X has increased from approximately 60
in R1 to 100 in R3 to 150 in R4.
.LP
Another tool,
.I makedepend ,
is used to generate header file dependencies for C source files in
each directory after the
.I Makefile
has been built.
Dependencies of targets upon object files can be statically listed in the
.I Imakefile ,
but not those for header files.
Different systems organize these differently so dependencies on them
must be generated dynamically.
.LP
While X is being built,
.I imake
itself is located with the configuration files in
.I mit/config .
.I makedepend
lives in
.I util/makedepend
if you use the compiled version (preferred),
or in
.I util/scripts
if you use the shell script version (slower).
.LP
When X is installed, copies of
.I imake ,
.I makedepend ,
and a related tool,
.I xmkmf ,
are placed in a public directory, and the configuration files are
copied to
.I /usr/lib/X11/config .
.I xmkmf
is used to bootstrap a
.I Makefile
from an
.I Imakefile
using the installed configuration files,\**
.FS
Normally a new
.I Makefile
is produced with ``make Makefile'', an operation that presumes you
already have a properly configured
.I Makefile
containing the rules necessary to run
.I imake .
Hence the bootstrap problem.
Obviously, if you know the proper incantation to utter, you can issue
the appropriate
.I imake
command manually, but
.I xmkmf
eliminates the need.
Besides, the only way you'd know which wand to wave is by having a
reasonable understanding of
.I imake
in the first place\*-in which case you wouldn't be reading this!
.FE
and can be used to build
programs from outside of the X source tree, such as you might write
yourself or get from comp.sources.x on Usenet.
.LP
The X configuration files make very
few assumptions about the capabilities of
.I make
itself.
Although several enhanced versions of
.I make
provide special features or extensions, any
.I Makefile
that relies on universal availability of those features
will fail on systems with less-capable versions of
.I make .
.I Makefiles
produced by
.I imake
in X do not use any of these constructs, so they should work with
even the lowest-common-denominator
.I make
program.
.LP
Since
.I imake
passes its input through
.I cpp ,
the configuration file writer can take advantage of several features
not present in plain-vanilla
.I make ,
such as parameterized macros (using #define), file inclusion (using
#include) and conditional testing (using #if, #ifdef, #ifndef).
This ameliorates much of the sobering effect of having to assume that a dumb
.I make
is the only one available.
.LP
Use of
.I cpp
can produce problems, too, of course:
.IP (1)
How to specify, in configuration files, comments that should end up in the
.I Makefile .
.I make
comments are introduced by a ``#'' character\*-unfortunately,
.I cpp
treats lines beginning with ``#'' as preprocessor directives.
A comment of the form
.Ds .IP
# if your system doesn't have ranlib, use /bin/true
.De .IP
is considered a symbol test by
.I cpp
(it begins with ``if'') and is gobbled up, while a comment like
.Ds .IP
# The next rule is a workaround for a broken compiler
.De .IP
is not understood by
.I cpp
and generates a syntax error.
To get around this, comments
should be preceded by ``/**/#'' instead of just ``#''.
.I cpp
will strip off the ``/**/'' (the empty C comment)
and won't treat the line as a directive (though it's
still liable to symbol substitution).
.IP
The exception to this ``comment-commenting'' convention is in the
.I Imakefile
itself, where
.I imake
automatically adds ``/**/'' onto lines beginning with ``#'' that are not
preprocessor directives.
The reason for this is to insulate the end user of
.I imake
(who simply writes the
.I Imakefile
and not the underlying configuration files) from the need to be aware
of, or abide by, the commenting convention.
I would hazard a guess that many people are not aware of this
exception, given the high incidence of
.I Imakefiles
containing ``/**/#''.
.IP
Comments that are not to be passed through to the
.I Makefile
can be written as normal C comments (text surrounded by ``/*'' and ``*/'')
and will be deleted by
.I cpp .
.IP (2)
Sometimes the values of
.I cpp
symbols are to be concatenated.
The symbol names cannot be concatenated in the configuration files,
because that results in a different symbol name, so they must be kept
apart somehow.
The empty C comment is useful here, too.
For instance, if Prefix, LibName and Suffix are defined as
.I lib ,
.I mylib
and
.I .a ,
respectively, then to obtain the concatentation value
.I libmylib.a ,
one writes Prefix/**/LibName/**/Suffix, not PrefixLibNameSuffix.
.IP (3)
The configuration files define several multiple-line macros that are
intended to produce multiple lines of output.
.I cpp
joins multi-line macros into a single line, so some post-processing is
necessary.
.NH
Configuration file architecture
.LP
To produce a
.I Makefile
from an
.I Imakefile ,
the following configuration files are used:
.Ds
.ta 1.5i
Imake.tmpl	master template
\fIplatform\fR.cf	platform-specific definitions (the filename varies)
site.def	site-specific definitions
Project.tmpl	X-specific default definitions
Imake.rules	\fIcpp\fR macros to generate \fImake\fR rules
.De
In general,
if a port exists for your system, the only file that you should need to modify
to build and install X is
.I site.def .
In some cases it may be necessary to modify the platform file.
.I Imake.tmpl ,
.I Project.tmpl
and
.I Imake.rules
should be left alone.\**
.FS
It should be emphasized that ``should be left alone'' applies
.I "only to building X itself" .
If you are adapting the configuration files for use with another
project, you will probably modify all of them somewhat.
You may even find yourself in the position of having modified them to such
an extent that they end up hacked to bits, incapable of configuring
anything, with you left possessing only the faint hope that something useful
will rise, Phoenix-like, from out of the remains.
Be assured\*-it won't.
But cheer up; start again and learn from your mistakes.
.FE
.LP
If you are porting X to a new platform, you will need to write your
own platform file, and modify the top part of
.I Imake.tmpl
slightly.
If you are doing a new port of X,
.I or
porting the X configuration files for use with a different project, you
are well-advised to study all of the configuration files thoroughly.
Ignorance is not bliss in such instances.
.LP
.I Imake.tmpl
contains an #include line for each of the other four configuration files and
for the
.I Imakefile
in the current directory.
It also contains in-line sections for global constant definitions,
header block selection, description of system characteristics, build
definitions, and extra
.I make
rules.
The template is
structured as follows to make everything fit together:
.LP
.TS
box tab (%) ;
l   l   s   l   l
l   _   _   _   l
l | l   l   s | l
l | l   l   s | l
l | l   _   l | l
l | l | l | l | l
l | l   _   l | l
l | l   _   l | l
l | l | l | l | l
l | l   _   l | l
l | l   l   l | l
l | l   l   l | l
l | l   _   l | l
l | l | l | l | l
l | l   _   l | l
l | l   _   l | l
l | l | l | l | l
l | l   _   l | l
l | l   _   l | l
l | l | l | l | l
l | l   _   l | l
l | l   l   s | l
l   _   _   _   l .
\h'.01i'%Imake.tmpl:%\h'.01i'%\h'.01i'
%
%\h'.01i'%global constants
%%header blocks
%%
%%#include <\fIplatform\fR.cf>
%%
%%
%%#include <site.def>
%%
%%system description and
%%build definitions
%%
%%#include <Project.tmpl>
%%
%%
%%#include <Imake.rules>
%%
%%
%%#include "./Imakefile"
%%
%%extra \fImake\fR rules
%
.TE
.LP
Before these sections of the template are described, there are some general
principles that should be kept in mind.
.LP
Much of the flexibility of the X configuration files is achieved
through the use of the following construct, which defines
.I symbol
only if it has not already been defined:
.Ds
#ifndef \fIsymbol\fR
#define \fIsymbol value\fR
#endif
.De
This construct allows any system-, build- or project-related symbol to
be given a default definition if none has been supplied earlier;
coupled with support for inclusion of platform- and site-specific
files prior to the section in which the default is defined,
the default may be overridden
by a definition occurring within those files.
For example, the default for the C compiler symbol occurs in the build
definitions section of
.I Imake.tmpl
and is defined thus:
.Ds
#ifndef CcCmd
#define CcCmd cc
#endif
.De
If the GNU C compiler is to be used instead, the default definition
can be overridden
simply by putting the following in
.I site.def :
.Ds
#ifndef CcCmd
#define CcCmd gcc
#endif
.De
Use of the #ifndef/#endif construct is pervasive throughout the X11R4
configuration files, to a much greater extent than in the X11R3 files.
This is especially true in the platform-specific files.
.LP
A fairly consistent pattern followed within the system/build section of
.I Imake.tmpl
and within
.I Project.tmpl
is that definitions for
.I cpp
symbols are listed first, followed by definitions for
.I make
variables.
I presume this is done because there is greater flexibility
available in defining
.I cpp
symbols, e.g., through #ifndef conditional testing.
Since
.I cpp
can't tell whether a
.I make
variable has previously been defined, the strategy adopted is to associate a
.I cpp
symbol with a given
.I make
variable thus:
define the symbol conditionally to allow the possibility of
overriding, then equate the
variable to whatever value the symbol finally ends up with.
For instance,
.I Imake.tmpl
contains:
.Ds
#ifndef CcCmd
#define CcCmd cc
#endif
.sp .3v
\&. . .
.sp .3v
CC = CcCmd
.De
If no earlier definition of CcCmd overrides the default
CC ends up as
``cc''.
On the other hand, if the default is overridden to use ``gcc''
instead (e.g., in
.I site.def ),
CC ends up as ``gcc''.
.LP
Sometimes ``mixed'' symbol definitions occur, in which
.I cpp
symbols are defined in terms of
.I make
variables.
There are at least two reasons to do this.
.IP (1)
To avoid order-of-definition problems.
Consider the two sets of definitions below.
.Ds .IP
.ta 3i
#ifndef a	#ifndef a
#define a b	#define a ${B}
#endif	#endif
A = a	A = a
.sp .3v
\&. . .	. . .
.sp .3v
#ifndef b	#ifndef b
#define b z	#define b z
#endif	#endif
B = b	B = b
.De .IP
What ends up in the
.I Makefile
is:
.Ds .IP
.ta 3i
A = b	A = ${B}
B = z	B = z
.De .IP
The example on the left is dependent on the order in which a and b are
defined.
As written above, a (and hence A) is defined in terms of
a symbol that hasn't been defined yet; both get the value of a
literal ``b''.
The example on the right works; b is defined as ``z'', B gets
the same value.
A becomes ``${B}'', which is not evaluated further until
.I make
is run.
At that time A evaluates properly to ``z'', independently of the order in
which a and b are defined in the configuration file.
.IP (2)
To allow for greater flexibility at installation time.
For example, UsrLibDir and USRLIBDIR are defined as:
.Ds .IP
#ifndef UsrLibDir
#define UsrLibDir $(DESTDIR)/usr/lib
#endif
.sp .3v
\&. . .
.sp .3v
USRLIBDIR = UsrLibDir
.De .IP
Symbols such as IncRoot, BinDir, AdmDir are defined similarly, even
though DestDir (to which DESTDIR is eventually equated) is defined
earlier (i.e., there is no order-of-definition problem here).
If a user wants the install to take place under a different root than
DestDir, the command ``make DESTDIR=/alternate/root install''
suffices, by overriding the definition of DESTDIR in the
.I Makefile .
If UsrLibDir, etc., were defined directly in terms of DestDir rather
than DESTDIR, this would not be possible.\**
.FS
Actually, it would be possible, but cumbersome: ``make install
BINDIR=/alt/bin/dir ADMDIR=/alt/adm/dir LIBDIR=/alt/lib/dir ...''
.FE
.LP
Each of the sections of
.I Imake.tmpl
is described below.
The descriptions only contain representative examples of the symbols
used in each configuration file.
For exhaustive (and exhausting?) lists, consult the appendix.
.NH 2
Global constant definitions
.LP
This section of
.I Imake.tmpl
defines two symbols (YES as 1 and NO as 0).
References to these symbols are legion
throughout the rest of the configuration
specification and their values should not be changed.
.LP
Note: symbols #define'd in the configuration files or the
.I Imakefile
have nothing to do with symbols #define'd in your programs; they
are entirely independent.\**
.FS
I once wondered about this, which seems pretty ridiculous now.
.FE
.NH 2
Header block selection
.LP
The header block section of
.I Imake.tmpl
determines the type of machine on which
.I imake
is being run.
This is done by looking for a
.I trigger \*-a
.I cpp
preprocessor symbol that uniquely and unambiguously indicates a given platform.
For instance,
``sun'' is defined only on Sun systems, ``apollo'' only on Apollo systems,
and
``ultrix'' is only on Ultrix systems.
Their header blocks look like this:
.Ds
.ta 3i
\fBSun:\fR	\fBUltrix:\fR
#ifdef sun	#ifdef ultrix
#define MacroIncludeFile <sun.cf>	#define MacroIncludeFile <ultrix.cf>
#define MacroFile sun.cf	#define MacroFile ultrix.cf
#undef sun	#ifdef vax
#define SunArchitecture	#undef vax
#endif /* sun */	#define VaxArchitecture
	#endif
\fBApollo:\fR	#ifdef mips
#ifdef apollo	#undef mips
#define MacroIncludeFile <apollo.cf>	#define MipsArchitecture
#define MacroFile apollo.cf	#endif
#undef apollo	#undef ultrix
#define ApolloArchitecture	#define UltrixArchitecture
#endif /* apollo */	#endif
.De
The trigger symbol might be predefined by
.I cpp
itself; if no such predefined symbol is available,\**
.FS
A goal ANSI C will help us reach?
.FE
.I imake
is built to explicitly pass a definition for one to
.I cpp
to cause the correct header block to be selected.
.LP
If no trigger is defined, a generic configuration
header block is selected instead.
Since that means the platform wasn't
properly determined, a warning is written into the
.I Makefile :
.Ds
# WARNING:  Imake.tmpl not configured; guessing at definitions!!!
# This might mean that BOOTSTRAPCFLAGS wasn't set when building imake.
.De
The resulting generically-configured
.I Makefile
will probably fail in one of a number of strange and wondrous ways when
used to try to build anything,
With any luck, the hapless user will examine the
.I Makefile
to figure out
what went wrong, see the warning, and realize that the platform wasn't
determined properly.
.LP
Failure to find a defined trigger symbol might occur because no
port exists for the machine in question, and thus no header block for
it exists.
(Each time X is ported, a header block for the new platform is added
to this section of
.I Imake.tmpl .)
Failure will also occur if
.I imake
was not built properly (i.e., it doesn't pass the
proper trigger symbol definition to
.I cpp ).
.LP
Assuming the proper header block is selected, several
things happen inside of it.
The name of the associated platform-specific
.I .cf
file is defined for later inclusion by the template; one or more architecture
indicator symbols are defined that can be used
by later configuration sections
to test for particular software or hardware platforms;
the trigger symbol is undefined so it won't trigger any other header
block.
.LP
A header block may define a single architecture indicator symbol that refers
both to the software and hardware,
or separate indicators may be defined for an operating system that
runs on multiple hardware types,
For example, ``ultrix'' indicates an Ultrix system, but it is no longer
true (as it once was) that a VAX can be assumed as the hardware
platform\*-RISC Ultrix systems run on MIPS chips now.
Thus UltrixArchitecture is defined as the software indicator symbol, and
VaxArchitecture or MipsArchitecture as the hardware indicator.
.LP
Software indicator symbols should be unique.
Hardware indicator symbols are not necessarily unique in themselves
and may be shared across different software
platforms, e.g., MipsArchitecture can be defined for Ultrix or SGI systems,
VaxArchitecture for Ultrix or BSD systems.
.LP
.I Imakefiles
should always test for indicator symbols rather than trigger symbols,
since the former are more reliable (the latter are undefined by the
header block, anyway).
.LP
The use of predefined or
.I imake -supplied
.I cpp
trigger symbols is fraught with peril, and one must construct the
header blocks carefully, for two reasons:
.IP (1)
Symbols may be ambiguous.
For instance, ``vax'' can be defined both under Ultrix and under BSD.
Thus, the Ultrix header block must come first.
It tests for ``ultrix'' and if that succeeds, defines UltrixArchitecture
and undefines ``vax'' so the BSD block will fail.
.IP (2)
Symbols may become ambiguous in unanticipated ways in the
future.
The ``mips'' symbol is an example.
In R3 it was used to unambiguously detect a true MIPS machine.
This can no longer be done given the presence of that symbol on other
systems that also run on MIPS chips now.
.LP
When porting X to other platforms, it is sometimes
difficult to know either what the trigger symbol should be, or what
indicator symbol(s) to use.
Consider MIPS machines running RISC/os.
``mips'' is insufficient as a trigger, because it is does not
unambiguously define MIPS systems.
The hardware indicator symbol is clear enough, MipsArchitecture, but
the software indicator is less so.
The name of the MIPS operating system suggests that
RiscosArchitecture might be a good choice.
Guess again.
Acorn Computers Ltd. has an OS with a similar name, ``RISC OS''.\**
.FS
My own preference is to use ``mipsriscos'' as the trigger symbol,
MipsArchitecture as the hardware indicator,
and MipsRiscosArchitecture
(which seems suitably unique, but has the disadvantage of being quite
long, and uneuphonious to boot) as the software indicator.
MIPS Computers now claims to consider ``RISCOS'' their trigger symbol.
.FE
.NH 2
platform.cf
.LP
After the header block section has determined which platform-specific
file to use, that file is then included by
.I Imake.tmpl :
.Ds
/**/################################################################
/**/# platform-specific configuration parameters - edit MacroFile to change
#include MacroIncludeFile
.De
where MacroIncludeFile has been defined properly by the header block.
.LP
The platform file contains definitions needed to make X build and
install correctly on a particular system.
Some common things found here are definitions for the operating
system name version numbers (major and minor), C compiler version
numbers, and workarounds for missing commands.
Overall, these files are really quite a hodgepodge of different things,
and it is difficult to describe them in any general way.
You should read through the
.I .cf
file for your system before you try to compile anything, to get an
idea what can be done with it.
(It doesn't hurt to read
.I all
the
.I .cf
files, as a matter of fact, especially if you are doing a new port.)
.LP
It is important that version numbers in the platform file
accurately reflect your system.
Some symbol values are contingent upon the OS or C
compiler version, to accommodate system changes, deficiencies, or
bugs, so you want to make sure you get the right definitions.
For example,
.I sun.cf
contains the following:
.Ds
#if OSMajorVersion <= 3
#define HasVoidSignalReturn NO
#else
#define HasVoidSignalReturn YES
#endif
.De
This reflects a change in the return type of the
.I signal()
system call (from
.I int*
to
.I void* )
on Sun systems beginning with SunOS 4.0.
Ultrix systems underwent a similar change between 3.0 and 3.1 but the
.I ultrix.cf
file doesn't yet reflect the change.
Perhaps in R5.
.LP
A common missing-command workaround occurs for those systems
that have no BSD-compatible
.I install
command, such as in
.I cray.cf :
.Ds
#define InstallCmd sh $(SCRIPTSRC)/install.sh
.De
It is important to set the value of the SystemV symbol to either YES or NO
in the platform file because
(1) the value of parameters defined in
.I site.def
may depend on it, and the default value is not set until
the system/build definition section (i.e., after
.I site.def );
(2)
the default values of many parameters in later parts of the template
depend on the value of SystemV.
For instance, there is usually no
.I ranlib
under System V, so the default is defined as a no-op:
.Ds
#ifndef RanlibCmd
#if SystemV
#define RanlibCmd /bin/true
#else
#define RanlibCmd ranlib
#endif
#endif
.De
It is perhaps safer to rely on predefined
.I cpp
symbols (should they exist) in the platform-specific files
than in the header block section of
.I Imake.tmpl .
Once you know the system type, the universe of such
symbols is more constrained and their meanings cannot clash with those
of symbols defined on other vendors' systems.
.LP
The platform files were significantly reorganized between R3 and R4.
In R3, each platform file was expected to contain a definition for
each of the build parameters (C compiler, loader, link command,
etc.).
Whenever a change was made that affected one platform file, such as
defining a new symbol, it usually affected them all.
Since the definitions were usually broadly similar across platforms,
this was more work than necessary.
The approach adopted in R4 is to supply best-guess values as the
default definitions for these parameters in the template, which
individual platform files may override as necessary.
This set of defaults defines a baseline configuration.
There are two advantages to this method.
The platform files need only specify where they
.I differ
from the baseline, by overriding the default definitions; and changes or
additions to the baseline usually require retrofitting of only a few
platform files.
.NH 2
site.def
.LP
This file contains site-specific definitions.
It is used to reflect local site-wide conventions.
Should you wish to override the defaults,
this is the place to indicate installation directories, whether to
build the server and example programs, any special versions of
programs to use during the build, etc.
.LP
The name
.I site.def
seems, to me at least, something of a misnomer.
For instance, one of the things that is likely to be set there
is HasLargeTmp, to indicate whether you have a large temp file
directory.
.I site.def
is the place for this definition (it depends on your particular
file system layout, so it doesn't go in the platform-specific file,
and it's not
.I cpp -guessable,
so it doesn't go in the system description section).
But a ``site'' can be a location at which multiple hosts
are administered, and which may be configured dissimilarly.
Some may have a large temp directory, others may not.
Similarly,
.I site.def
is the logical place to specify that you want to use the GNU C
compiler,
but you might not necessarily want to use it on all hosts at a site.
In some ways
.I host.def
might be a better name for this file.
.NH 2
System description and build parameter definitions
.LP
The platform- and site-specific files are followed by a section containing
a number of default definitions.
Generally, these describe system characteristics
(e.g., does it have
.I vfork() ?
does it have sockets?) or are related to management of the
build and installation\**
.FS
Viz.,
.I how
things should be installed, not
.I where ;
defaults for the latter are in
.I Project.tmpl .
.FE
processes (e.g., what is the name of the C compiler?
does the loader need any special flags?).
These are not X issues and so are segregated into their own section.
.LP
This section of
.I Imake.tmpl
should not be modified; definitions should be overridden
in platform- or site-specific files.
.NH 2
Project.tmpl
.LP
This
file contains definitions for parameters that are specific to X.
Examples: whether to build debuggable versions of libraries, the
screen resolution, what sorts of connections to accept (UNIX, TCP
or DECnet sockets), where programs or libraries should be installed.
.LP
A number of the
.I make
variables in
.I Project.tmpl
are directory locations.
Most of them are named in one of two ways, \fIXXX\fRDIR or \fIXXX\fRSRC.
Variables of the first form are defined by equating them to the
values
.I cpp
symbols having similar names (e.g., LIBDIR=LibDir).
The
.I cpp
symbols are defined in the usual default-provided-but-override-allowed
manner,
because they refer to where things are to be placed
at installation time, something which the installer might wish to
change.
.LP
Variables of the second form, \fIXXX\fRSRC, indicate the layout of various
source directories within the X distribution.
They are not supposed to be changed, so there is no provision for
overriding them; rather than being equated to
.I cpp
symbols, they are simply defined
directly (e.g., SERVERSRC=$(TOP)/server).
.LP
.I Project.tmpl
should not be modified; definitions should be overridden
in platform- or site-specific files.
.NH 2
Imake.rules
.LP
This file contains
.I cpp
macros used to generate
.I make
rules from concise descriptions of targets and dependencies.
Since all the
.I cpp
symbol substitution functionality is available, these macros can be
very powerful and are sometimes quite complicated.
.LP
.B Example:
A simple rule to compile a program might be (this isn't an actual X
rule):
.Ds
.ta .5i +4i
# define CompileProgram(program,objects,libraries)
program: objects
	cc \-o program objects libraries
.De
If invoked in an
.I Imakefile
as ``CompileProgram (xproga, main.o, \-lm)'', this macro would expand in the
.I Makefile
to:
.Ds
.ta .5i
xproga: main.o
	cc \-o xproga main.o \-lm
.De
The invocation
``CompileProgram (xprogb, main.o parse.o scan.o, \-lm \-ll \-ly)''
would expand to:
.Ds
.ta .5i
xproga: main.o parse.o scan.o
	cc \-o xproga main.o parse.o scan.o \-lm \-ll \-ly
.De
There is one problem with these examples, which is that the CompileProgram()
macro definition spans multiple lines, and
.I cpp
macros are normally single-line.
One can can write multiple-line macros by putting a ``\e'' at the end
of lines to be continued onto the next, but this doesn't work for
.I imake's
purpose since
.I cpp
will still collapse the expanded macro onto a single (possibly quite long)
line.
That makes
.I make
quite unhappy.
.LP
To get around this, special place markers ``@@\e'' are used on all
lines of a rule definition but the last.
The ``\e'' allows multiple-line definitions for
.I cpp
and the ``@@'' lets
.I imake
know how to postprocess
.I cpp
output in order to split expanded macros back up into multiple lines
suitable for
.I make .
This keeps both
.I cpp
and
.I make
happy.
.LP
So:
the
.I real
way to write the CompileProgram() macro is:
.Ds
.ta .5i +4i
# define CompileProgram(program,objects,libraries)	@@\e
program: objects	@@\e
	cc \-o program objects libraries
.De
The invocation ``CompileProgram (xproga, main.o, \-lm)'' comes out of
.I cpp
looking like this:
.Ds
xproga: main.o @@ cc \-o xproga main.o \-lm
.De
.I imake
sees the ``@@'' and knows it has to break the line back up to
produce the intended result.
.LP
In short,
the purpose of the strange syntax is to keep
.I cpp
from totally destroying the usefulness of the output for
.I make
purposes.
Thus, although
.I imake
relies heavily on
.I cpp ,
one of
.I imake 's
jobs is to undo some of the damage done by
.I cpp \*-an
uneasy alliance indeed.\**
.FS
This use of ``@@\e'' seems quite natural and obvious, now that I have worked
with it for a while.
I remember, though, that the first few times I looked at the X
.I imake
rules, especially the more complicated ones,
the formatting and syntax seemed so bizarre to me that
a switch flipped in my brain, which then refused to comprehend
anything at all.
Repeated exposure gradually inured me to the infelicities of the
syntax.
.FE
.LP
All the macro definitions in
.I Imake.rules
are embedded within the standard override construct:
.Ds
#ifndef \fIrulename\fR
#define \fIrulename\fR .\0.\0.
#endif
.De
Thus, even rules are subject to being overridden.
If a rule needs to be overridden for a particular platform,
the default definition will be placed in the
platform-specific
file (\fIsgi.cf\fR does this).
If a rule only needs redefining in a single directory, it may be
better to undefine it and redefine right it in that directory's
.I Imakefile
(see
.I mit/config/Imakefile
for an example).
.LP
In R3, rule definitions were not bracketed within #ifndef/#endif pairs.
This meant that although individual
.I Imakefiles
could undefine and redefine rules, platform files could not.
Any definition of a rule in a platform file would be unconditionally replaced
by the definition in
.I Imake.rules
(possibly accompanied by a ``symbol redefined'' message).
The R4 architecture is an improvement because it is not
constrained by this limitation.
.LP
.I Imake.rules
should not be modified; definitions should be overridden
in platform- or site-specific files.
.NH 2
\&./Imakefile
.LP
The last file included by the template is the
.I Imakefile
from the current directory.
As already mentioned,
.I imake
causes
.I make
comments in this file to be made
.I cpp -safe
by preceding them with ``/**/'' if necessary.
The
.I Imakefile
indicates targets to be built and installed, and their
dependencies, in terms of the
.I cpp
macros defined in
.I Imake.rules .
There may also be targets for generating dependencies, creating
installation directories, creating lint libraries or tag files, etc.
.LP
.I Imakefile -writing
is described in more detail in a later section.
.NH 2
Extra make rules
.LP
The last section of
.I Imake.tmpl
adds some common targets to the
.I Makefile ,
such as a ``Makefile'' target to regenerate the
.I Makefile
itself, and default ``tags'' and ``clean'' targets.
If the directory has subdirectories, rules to
recurse through them for the ``install'', ``install.man'', ``clean'',
``tags'', ``Makefiles'' and ``includes'' targets are generated.
These rules are added if the
.I cpp
symbol IHaveSubdirs is defined and the
.I make
variable SUBDIRS is equated to the list of subdirectories in the
.I Imakefile .
.NH
Building imake
.LP
Before you can build any part of X,
.I imake
itself must be built.
``make World'' in the top-level X source directory builds
.I imake
automatically (in
.I mit/config ).
The following discussion explains what happens during that process and
some of the things you may need to do in preparation for the ``World''
build.
You want to read this section if ``make World'' dies when you try it, or you
want to port X to another platform,
or you want to build
.I imake
or adapt the X configuration files for use with other projects.
You need to look at the following files:
.I Makefile.ini ;
.I imakemdep.h ;
.I Imake.tmpl .
.NH 2
Determine what you are trying to accomplish
.LP
When you build
.I imake
you want to
(1) get it to compile successfully, so you can use it;
(2) guarantee that it makes sure
.I cpp
knows the trigger symbol\*-whether
.I cpp
predefines it or not\*-so you don't end up with generically-configured
.I Makefiles .
.LP
Conspiring to keep you from your goal are three dilemmas.
.IP \(bu
.I imake
generates
.I Makefiles
for use in compiling programs;
.I imake
is compiled using a
.I Makefile .
.IP \(bu
.I imake
needs to know your system's trigger symbol in order to pass it along to
.I cpp
for proper
.I Makefile
generation.
But
.I imake
doesn't know the trigger itself if neither
.I cc
nor
.I cpp
predefine it.
.IP \(bu
Some systems require special C compiler flags to ensure proper
compilation of all but the most trivial programs.
One of the facilities provided by
.I imake -generated
.I Makefiles
is that these flags can be automatically specified.
Of course, that is small consolation when the program to be compiled is
.I imake \*-which
happens to be just such a non-trivial program itself.
.LP
The solution to the first dilemma is to use a minimal handwritten file
.I Makefile.ini
instead.\**
.FS
There might be a
.I Makefile
in
.I mit/config
already,
but it might not have been generated on your system, so it's not safe to
use.
It has a bug in it, anyway.
.FE
You might need to hack
.I Makefile.ini
file slightly for your system\*-but only slightly.
If you find yourself making large changes, that is a symptom you don't
understand what you're supposed to be doing.
Stop and think some more first.
.LP
The second and third dilemmas are solved by manually passing the trigger symbol
to the
.I imake
compilation and using a small bootstrap program that knows what
special flags are necessary to get
.I imake
to compile without error.
The commands in
.I Makefile.ini
that build
.I imake
look something like this:\**
.FS
The commands actually use CFLAGS, which includes BOOTSTRAPCFLAGS as part
of its definition; you get the idea.
.FE
.Ds
$(CC) \-o ccimake $(BOOTSTRAPCFLAGS) ccimake.c
$(CC) \-o imake $(BOOTSTRAPCFLAGS) imake.c `./ccimake`
.De
BOOTSTRAPCFLAGS is the
.I make
variable used to pass the trigger value for your system to
.I ccimake
and
.I imake
so they know the platform type.
It is typically ``BOOTSTRAPCFLAGS=\-D\fItrigger\fR''
if your
.I cpp
doesn't predefine the trigger, empty otherwise.\**
.FS
The BOOTSTRAPCFLAGS mechanism was not present in some earlier versions
of X11 (e.g., R1).
The number of systems to which X had been ported then was smaller and
all of them had
.I cpp 's
that predefined a unique symbol.
.FE
.LP
.I ccimake
is the bootstrap program.
It is\*-by design\*-so simple that it should compile
without any special treatment, and is used to figure out any extra
flags needed on your platform to get
.I imake
to compile.
BOOTSTRAPCFLAGS is supplied to the
.I ccimake
build so it can select the necessary flags by platform type.
.LP
.I imake
is built using the flags supplied by
.I ccimake ,
so that it compiles correctly, and with the
the trigger value supplied in BOOTSTRAPCFLAGS,
so that it learns and memorizes the trigger for
itself.
When
.I imake
passes the trigger to
.I cpp ,
that in turn allows
.I cpp
to properly determine the correct header block in
.I Imake.tmpl
when
.I Makefiles
are generated.
.NH 2
Lay the groundwork
.LP
First, you need to determine what the trigger symbol is for your system,
as well as the value of BOOTSTRAPCFLAGS.
For existing ports, you can find out the trigger symbol by
examining the header block section of
.I Imake.tmpl .
The value of BOOTSTRAPCFLAGS can be found in the platform
.I .cf
file for your system.
Look for a line that defines BootstrapCFlags; if
.I cpp
predefines the trigger, there will likely be no such line.
This means BOOTSTRAPCFLAGS is empty.
Otherwise the value will probably be ``\-D\fItrigger\fR'', e.g.,
``\-DmacII'', ``\-Datt'', ``\-Daix''.
.LP
A complication likely to arise in the future with regard to
predefined preprocessor symbols is that ANSI C takes a dim view of
all (or almost all) such.
BOOTSTRAPCFLAGS is likely
to be non-empty on more systems as implementations of ANSI C become
distributed more widely.
.LP
For new ports, you need to invent a trigger symbol
that uniquely identifies your system and define
BOOTSTRAPCFLAGS accordingly.
Suppose you have a Brand X system.
You can use ``brandx'' as the trigger and ``BOOTSTRAPCFLAGS=\-Dbrandx''.
You also must modify
.I imakemdep.h .
This header file is #include'd by three programs,
.I ccimake ,
.I imake
and
.I makedepend ,
and has one section for each.\**
.FS
.I Imake.tmpl
contains some comments near its beginning pertaining to new ports.
These indicate that
.I imake.c ,
and
.I main.c
in the
.I makedepend
source, should be modified.
These comments are correct for R3 but evidently were not updated for R4;
in both cases the modifications should be made to
.I imakemdep.h .
Similarly, the
.I README
file refers to
.I ccflags.c ,
the R3 equivalent of
.I ccimake.c .
.FE
.LP
The first section of
.I imakemdep.h
pertains to
.I ccimake ;
it consists of a bunch of #ifdef/#endif blocks that define
.I imake_ccflags
according to the trigger symbol.
.I imake_ccflags
is defined as the flags needed to compile
.I imake
on your platform.
For instance, if your Brand X system is System V-based, you need to
specify that:
.Ds
#ifdef brandx
#define imake_ccflags "\-DSYSV"
#endif
.De
.I ccimake
simply writes the value of
.I imake_ccflags
to its standard output.
Common flags in this definition are \-DSYSV or \-DUSG to indicate System
V or USG systems.
Other flags might also be necessary\*-see the ``hpux'' and ``umips''
blocks for some particularly unpleasant examples.
If no special flags are necessary to compile
.I imake
(e.g., under Ultrix or BSD),
there need not be any block for your system, and
.I ccimake
simply writes out a default definition of
.I imake_ccflags
(currently \-O).
.LP
You might have to fool around trying to compile
.I imake
by hand to determine the correct flags before you know how to define
.I imake_ccflags .
.LP
The second section of
.I imakemdep.h
is for
.I imake .
It too has a set of #ifdef/#endif blocks, this time to select a
trigger symbol definition based on the trigger symbol.
That sounds circular and it is.
These blocks add entries to
.I cpp_argv [\|],
which is an array of strings to be passed to
.I cpp
by
.I imake .
If your
.I cpp
predefines the correct trigger symbol automatically, you don't need to
do anything to this.
Otherwise, this is where you want your trigger symbol to be listed,
and there should be a block something like this:
.Ds
#ifdef brandx
        "\-Dbrandx",        /* for Brand X systems */
#endif
.De
This causes
.I imake
to pass ``\-Dbrandx'' to
.I cpp
to make it simulate predefinition of the trigger.
When
.I cpp
reads the configuration files the trigger will have been defined and
the correct header block in
.I Imake.tmpl
will be selected.
.LP
Following the sections for
.I ccimake
and
.I imake ,
.I imakemdep.h
contains a third section for
.I makedepend
(the compiled version; if you use the shell script version of
.I makedepend
in
.I mit/util/scripts ,
this section of
.I imakemdep.h
is irrelevant).
It looks for various system- and compiler-related definitions that may
be predefined by
.I cc
and/or
.I cpp .
.I makedepend
uses this information to be smart.
Consider the following C program fragment:
.Ds
#ifdef ultrix
#include "inca.h"
#else
#include "incb.h"
#endif /* ultrix */
.De
If ``ultrix'' is defined,
.I makedepend
knows to generate a dependency for ``inca.h'' and not
``incb.h'';
a dumb
.I makedepend
has to to assume dependencies on both.
.LP
It's easiest simply to leave this section of
.I imakemdep.h
alone.
.NH 2
Build the program
.LP
You're ready to begin (this should actually be simple if you've done
the above correctly).
The first thing to do is ensure the absence of any detritus that might
be laying around from previous builds:
.Ds
make \-f Makefile.ini clean
.De
Then build
.I ccimake
and
.I imake .
If your
.I cpp
predefines the trigger, you can build with:
.Ds
make \-f Makefile.ini
or
make \-f Makefile.ini BOOTSTRAPCFLAGS=
.De
Otherwise, specify the trigger explicitly.
E.g., for Brand X, use:
.Ds
make \-f Makefile.ini BOOTSTRAPCFLAGS=\-Dbrandx
.De .NH 2
Test your handiwork
.LP
If
.I imake
is supposed to pass a trigger symbol definition to
.I cpp ,
you should test whether it actually does or not by executing the
following command (\fB\-T\fI/dev/null\fR provides an empty input template, and
\fB\-s\fI/dev/null\fR throws away the output so it doesn't clobber the
.I Makefile
in your current directory):
.Ds
imake \-v \-T/dev/null \-s/dev/null
.De
.B \-v
causes
.I imake
to print out the
.I cpp
command that it executes.
If you don't see a \-D\fItrigger\fR
in that command,
.I imake
wasn't built properly.
.NH 2
Modifying other configuration files for a new platform
.LP
If you are developing a new port to another system,
you have to do more than be able to compile
.I imake ,
you also need to be able to use it
in conjunction with the configuration files.
First, you must modify the header block section of
.I Imake.tmpl
to add a block for your system.\**
.FS
This is the only section of
.I Imake.tmpl
that should ever need modifying.
Other changes would be specified in the platform-specific file for
your system.
.FE
It will look something like this:
.Ds
#ifdef brandx
#define MacroIncludeFile <brandx.cf>
#define MacroFile brandx.cf
#undef brandx
#define BrandxArchitecture
#endif /* brandx */
.De
Second, you must create a platform-specific file
.I brandx.cf .
If your bootstrap flags are non-empty, this should contain, at
minimum, ``#define BootstrapCFlags \-Dbrandx''.
Most certainly it will have other things in it, too.
You will discover just what as you go through the process
of porting the server and/or clients.
.NH
Building X Itself, or, When Is BOOTSTRAPCFLAGS Necessary?
.LP
The question addressed in this section is:
how can you get all the
.I Makefiles
built?
(This document is really about configuring X, not building it, and
configuring X amounts to building the
.I Makefiles .)
.LP
If you have gotten this far, you know you can get
.I imake
compiled and can proceed to build X itself.
.I "Before you do anything else,"
copy the top-level
.I Makefile
somewhere safe (outside of the source tree).
If something goes wrong and this file gets trashed,
you want to be able to recover it.
You're asking for trouble if you don't.
.LP
The ``do everything'' operation in building X is ``make World''.
It builds
.I imake ,
generates all the
.I Makefiles ,
removes extraneous object files, builds the header file tree,
generates dependencies, and compiles everything.
The interesting parts of this
from a configuration perspective are the first two steps.
.LP
The important questions are: (1) what is the value of BOOTSTRAPCFLAGS;
and (2) when
must BOOTSTRAPCFLAGS be specified?
You have already answered the first question by determining how to
build
.I imake ,
so only the second is considered below.
.LP
When you build
.I imake
``manually'' in
.I mit/config
using
.I Makefile.ini ,
you must specify BOOTSTRAPCFLAGS explicitly if it is non-empty.
What about when building from the top-level directory, e.g., ``make
World''?
It is a useful exercise to build
.I imake
by hand to test your understanding of the issues involved, but that
won't help you to do the ``World'' build\*-the first thing
``make World'' does, unfortunately, is throw
.I imake
away and rebuild it from scratch.
.LP
The X Release Notes (section 2) indicate that for
``make World'' you should
specify BOOTSTRAPCFLAGS if you find a definition for BootstrapCFlags in the platform
.I .cf
file (which means there is no unique predefined
.I cpp
symbol).
By implication, you do not need to specify BOOTSTRAPCFLAGS
if there is no value of BootstrapCFlags in your platform
.I .cf
file.
This means essentially that the empty value ``BOOTSTRAPCFLAGS='' is sufficient to
compile
.I imake
properly, which brings up a subtle point.
Each
.I Makefile
created in the X distribution contains a line that says
.Ds
BOOTSTRAPCFLAGS=\fIsomething\fR
.De
where
.I something
is the default value for any
.I make
operation, and may or may not be empty.
Thus, when you execute a command such as
.Ds
make World BOOTSTRAPCFLAGS=\-Dbrandx
.De
you are not just
.I providing
a value of BOOTSTRAPCFLAGS, you are also
.I overriding
the default value in the
.I Makefile .
.LP
The implication that BOOTSTRAPCFLAGS need not be specified if
BootstrapCFlags is
not defined involves a hidden assumption, i.e., that BOOTSTRAPCFLAGS
.I already
has the empty value in the top-level
.I Makefile .
If you obtain your X distribution from a system on which X has been built
and on which BOOTSTRAPCFLAGS is non-empty, that assumption is incorrect.
If you don't explicitly provide an empty value by saying
.Ds
make World BOOTSTRAPCFLAGS=
.De
to override the default in the
.I Makefile ,
a value which is incorrect for your system is used instead.
You can get some very strange results this way.
.LP
I submit that, in the general case, the deciding factor determining
whether to specify BOOTSTRAPCFLAGS is not so much whether
BootstrapCFlags is defined or not, but whether you've built your
.I Makefiles
yet.
If not, you need to specify BOOTSTRAPCFLAGS,
.I "even if it's empty."
Otherwise, you don't have to.
In practice, this means that when you are building X on a new system,
you should assume BOOTSTRAPCFLAGS in the
.I Makefile
is incorrect and explicitly override it on the command line.
Normally this will be on the first ``World'' build.
.LP
The Release Notes do not cover the general case.
Rather, they assume you are working from a virgin distribution (where
the default BOOTSTRAPCFLAGS is in fact empty).
.LP
Once the
.I Makefiles
have been built properly,
BOOTSTRAPCFLAGS will be defined correctly in them and
they will take care of propagating the right value
for you automatically\*-forever, even into another operation that
rebuilds
.I imake
and the
.I Makefiles .
You can test this for yourself on a machine that
has a non-empty BOOTSTRAPCFLAGS.
Do ``make World BOOTSTRAPCFLAGS=\-D\fItrigger\fR'', then ``make
World''.
You'll see during the ``make World'' that BOOTSTRAPCFLAGS is supplied for you.
.LP
.B "Note 1:"
It is not necessary to do a ``World'' build to build
.I imake
and the
.I Makefiles ;
there is a ``mastermakefiles'' target that will do so.
This is a far more modest undertaking than doing the ``World'' build
and you can check a few of the
.I Makefiles
afterward to see if the ``World'' build is likely to succeed or not:
If BOOTSTRAPCFLAGS is correct and the correct header block was selected,
you can
then go ahead and do ``make World'' without having to specify BOOTSTRAPCFLAGS.
.LP
.B "Note 2:"
The above discussion assumes that if BOOTSTRAPCFLAGS is not empty, BootstrapCFlags is
defined
correctly in the platform
.I .cf
file, because BOOTSTRAPCFLAGS normally gets its value from that symbol when
.I Makefiles
are generated.
.NH 2
Alternatives ways of specifying BOOTSTRAPCFLAGS
.LP
There are two alternatives to specifying BOOTSTRAPCFLAGS explicitly
on the
command line when you build
the
.I Makefiles ,
both sneaky and both deprecated.
They are mentioned here in order to point out why you shouldn't use
them.
.LP
First, you can edit
.I config/Makefile.ini
manually to set the value of BOOTSTRAPCFLAGS directly.
The reason this is not a good idea is that if you give your X
distribution to somebody else that doesn't have the same kind of
machine, you've given away an explicitly misconfigured
.I Makefile.ini .
The recipient might fail to appreciate the subtle irony of this
gesture.
.LP
Second, you can forget about BOOTSTRAPCFLAGS entirely.
That's correct\*-you can build
.I imake
with wild abandon and total lack of regard for whether it knows about
the correct trigger symbol or not.
``But on some systems,
.I imake
must pass the trigger definition to
.I cpp
explicitly,'' you say, ``and
.I imake
compiled in such a reckless and irresponsible manner may not do the job.''
Quite right.
However...
.I imake
also examines your environment, and the value of IMAKEINCLUDE, if
defined there, is passed to
.I cpp .
The value of IMAKEINCLUDE must begin with ``\-I'' (or
.I imake
will reject it), but you can be clever and set it
to ``\-I. \-Dbrandx'' and the
trigger symbol will be passed through to
.I cpp
and your
.I Makefiles
will build happily.
.LP
This is sneaky because it uses the environment to affect the
build process in a way that is not evident.
Worse yet, if you actually install an
.I imake
built this way, it won't work for anyone else who isn't privy to the
IMAKEINCLUDE convention.
.LP
(A third alternative is to edit
BOOTSTRAPCFLAGS to the proper value in the top-level
.I Makefile
before ``make World''.
This is functionally equivalent to specifying it on the command line.)
.NH
Imakeconoclasm\*-X configuration bugs
.LP
Given the size of the X project, there are astonishingly few outright
errors in the files involved in the configuration process.
The ones I know about are listed below.
.IP (1)
Assume the following scenario.
You do a ``make World''.
This builds
.I imake ,
generates all the
.I Makefiles ,
cleans all the directories and finally builds the X stuff.
.IP
Then you decide to do some work in an X directory that requires the
.I Makefile
to be rebuilt, so you execute
``make Makefile''.
What happens?
You might be surprised to observe that
.I imake
is rebuilt before your
.I Makefile
is regenerated.
This reason is that the rules for the ``Makefile'' target check
whether
.I imake
exists, and if not they build it first.
Since the ``World'' build does a clean after generating the
.I Makefiles ,
.I imake
was thrown away.
.IP
In fact,
.I imake
will be built the first time ``make Makefile'' is done after the
``World'' build even if the
cleaning had not been done.
The rules that check for
.I imake 's
existence look for
.I Makefile
in
.I mit/config/Makefile.ini
and build
.I imake
with that if it's there.
(It will be because the ``World'' build generates it.)
But the dependency for
.I imake
in
.I Makefile
is
.I imake.o
while the dependency in
.I Makefile.ini
(with which
.I imake
is initially built) is
.I imake.c .
This means that even if
.I imake
is present in
.I mit/config ,
it will be built again because
.I imake.o
isn't.
.IP
That isn't actually a bug, but it can be confusing.
Possibly even more confusing than the previous paragraph.
.IP
What
.I is
a bug is that
.I Makefile
in
.I mit/config
does not pass BOOTSTRAPCFLAGS to the
.I imake
compilation.
If your BOOTSTRAPCFLAGS is non-empty, you end up with a bad
.I imake.
The fix is as follows:
.Ds .IP
Change:
	DEFINES = $(SIGNAL_DEFINES)
To:
	DEFINES = $(SIGNAL_DEFINES) $(BOOTSTRAPCFLAGS)
.De .IP (2)
The IncRoot symbol is defined twice, once in
.I Imake.tmpl
and once in
.I Project.tmpl .
This is a minor nit, since it gets the same value in both files.
However, if you use a copy of the configuration files as a base for your own
projects, you will probably
modify these files and you should be aware that changing the definition in
.I Project.tmpl
has no effect if you leave both definitions in.
.IP (3)
.I makedepend
has some hardwired pathnames in the source code.
This may bite you if you use
.I gcc
or compile on a MIPS system under the BSD environment, or on other
systems that put include files somewhere other than
.I /usr/include .
.IP (4)
The default value of ExecableScripts in
.I Imake.tmpl
should be given a value based on SystemV, not SYSV.
.Ds .IP
Change:
	#ifndef ExecableScripts
	#ifdef SYSV
To:
	#ifndef ExecableScripts
	#ifdef SystemV
.De .IP
This is simply for consistency.
Probably the right thing always happens anyway.
.NH
Writing Imakefiles.
.LP
.B
This section is miserably incomplete.
.LP
This section assumes that
.I imake ,
.I makedepend
and
.I xmkmf
are installed in a public directory somewhere, which implies the
additional assumption that the
configuration files are installed where
.I xmkmf
can get at them.
Why make these assumptions?
.LP
Normally you work with both an
.I Imakefile
and a
.I Makefile
and after you make changes to the
.I Imakefile ,
you do ``make Makefile'' to regenerate the
.I Makefile .
You can use
.I Makefile
to rebuild itself this way because it has a
``Makefile'' target in it containing rules to do so.
.LP
That doesn't work when you're starting from scratch with just
.I Imakefile .
This is where
.I xmkmf
comes in useful.
Once you've written your
.I Imakefile ,
you just execute ``xmkmf'' and it will bootstrap a
.I Makefile
for you.
Thereafter you can use ``make Makefile'' when
.I Makefile
needs rebuilding.
.LP
However, in order to bootstrap
.I Makefile ,
you need
.I xmkmf .
.I xmkmf
assumes
.I imake
has been built and installed.
If an installed
.I imake
is used, an installed
.I makedepend
must also be used.\**
.FS
To see why, you need to look at the ImakeDependency() and
DependDependency() macros in
.I Imake.rules .
Notice that both macros depend on the value of UseInstalled, so if one
is installed, the other must be as well.
Also see the definitions of IMAKE and MAKEDEPEND in
.I Project.tmpl .
.FE
And if installed versions of
.I xmkmf ,
.I imake
and
.I makedepend
are used, they will expect the configuration files to have been
installed.
Hence the assumptions at the beginning of this section.
.LP
Stuff that should go in here.
.Ds
1) ComplexProgramTarget() all, program, install, install.man, depend,
	lint, clean (still need CleanTarget())
2) ComplexProgramTarget_{1,2,3}
3) SimpleProgramTarget() = CPT with only single source file
4) NormalProgramTarget()
.De
.LP
Here is the simplest
.I Imakefile :
.Ds
.ta 1i
	\(<- that's it right over there
.De
That was easy.
But what can you do with it?
First, execute ``xmkmf'', and you'll have the corresponding
.I Makefile .
Then you can try a few operations such as ``make
clean'', ``make'', ``make Makefile'' to see what happens.
(Note in particular that last target and observe that
you can get your first
.I Makefile
with ``touch Imakefile; xmkmf''.)
.LP
Since
.I make
actually executes correctly for all of those targets (or should,
anyway), you can see that a lot of structure is supplied for you by
the configuration files.
If you look at the
.I Makefile
generated from the null
.I Imakefile ,
you might be surprised by the complexity of it.
.LP
To test the syntactic correctness of your
.I Imakefile :
.Ds
xmkmf ; make Makefile
.De
This builds
.I Makefile
twice.
.I xmkmf
builds the
.I Makefile
from scratch and fails if the configuration is bad.
.I make
builds a new
.I Makefile
using the one built by
.I xmkmf ,
and fails if that one is not legal.
(say how that can happen, e.g., spelling errors cause it.)
This should not happen with a null
.I Imakefile ,
but it may very well happen when you're writing real ones.
.NH 2
Miscellaneous Observations
.IP \(bu
Don't forget ``make depend'' after ``make Makefile''.
.IP \(bu
If you intend to use
.I if
or
.I for
constructs in an
.I Imakefile ,
you are well advised to copy the way in which they
are used in
.I Imake.rules .
(See the beginning of the
.I Imake.rules
section of the Appendix for some notes on
.I if
constructs.)
.IP \(bu
If you need to pass special \-D's to the C compiler, set the
.I make
variable DEFINES.
If you need to pass special \-I's to the C compiler, set the
.I make
variable INCLUDES.
These will be passed to compilations automatically in CFLAGS
(see definitions of CFLAGS, ALLDEFINES and ALLINCLUDES in
.I Imake.tmpl ).
.IP \(bu
Some rules may not work as you expect after changing
.I Imakefile .
In particular,
problems can occur the first time you try ``make \fItarget\fR'' that
mysteriously disappear the second time.
As an instance of this,
note that the following command lines are not always equivalent:
.Ds
make Makefile Makefiles
make Makefile ; make Makefiles
.De
You normally want to build the ``Makefiles'' target when there are
subdirectories.
Suppose you add a new subdirectory.
This is done by adding it to the definition of
SUBDIRS in the current directory's
.I Imakefile .
You then need to rebuild the
.I Makefile
so that the definition of SUBDIRS is reset, and then rebuild
.I Makefile
in each of those subdirectories.
The first command line above builds the ``Makefiles'' target using the
(old) value of SUBDIRS from the (current)
.I Makefile ,
which is incorrect.
The second command line rebuilds the
.I Makefile ,
then runs a second
.I make
process to build ``Makefiles''.
The second process sees the (new) value of SUBDIRS in the (new)
.I Makefile ,
which includes the new subdirectory, and has the intended result.
.IP
What can be especially confusing is that if you executed ``make
Makefile Makefiles'' twice, it would not do what you expected the
first time, but it
.I would
the second time.
.NH
Courting disaster:  How to do the wrong thing
.LP
.I imake
is wonderful for portability when everything is configured
properly.
However, subtle syntax errors in the configuration files are often difficult
to track down when you begin to make any significant changes to them.
This section describes some of the misfortunes that may beset you
should you make so bold as to engage in such an endeavor.
.LP
For X, the source of problems might be in any of
.I Imakefile ,
.I Imake.rules ,
.I Imake.tmpl ,
the platform
.I .cf
file, or
.I site.def .
The most likely candidates are
.I site.def
and the platform file, though, since those are the only ones you're supposed
to edit.
(If you're writing an
.I Imakefile ,
add that to the list, too.)
Just remember that the effects of mistakes in the early files may
not be manifest until much later on.
Problems may appear to originate at locations far from the actual
cause.
.IP (1)
.I Makefile
trashing
.IP
A number of problems during ``make Makefile'' can result in a trashed
.I Makefile .
If you have
.I xmkmf
working, you can use that to regenerate the
.I Makefile
after fixing whatever the problem was.
Otherwise you must recover it somehow.
If you have a copy of
.I Makefile
stashed somewhere, you can use that.
If not:
the first thing that ``make Makefile'' does is to move the original
.I Makefile
to
.I Makefile.bak ;
if the new
.I  Makefile
isn't created properly, you can
usually recover it with ``cp Makefile.bak Makefile''.
Then you can fix
the problem and try again.
If
.I Makefile.bak
is trashed as well, you can
grab the
.I Makefile
from another directory at the same level and use that (this works because
they both contain the same ``make Makefile''
rule).
You can also use a
.I Makefile
from a directory at another level,
but you need to edit the line that sets TOP to reflect where the top
project directory is in relation to the current directory.
.IP
Moral: make sure
.I xmkmf
is working first!
.IP (2)
Boolean vs. existent/nonexistent symbols
.IP
Some symbols are used in boolean fashion and are defined as
YES or NO.
They are tested by ``#if \fIsymbol\fR'' or ``#if !\fIsymbol\fR''.
Others are turned on simply by being defined.
They are tested by ``#ifdef \fIsymbol\fR'' or ``#ifndef \fIsymbol\fR''.
Failure to distinquish the sense in which a symbol is used can lead to
problems.
It is necessary to define symbols properly
.I and
to test them properly.
.IP
.B Example:
YES/NO symbols must be defined properly.
SystemV is such a symbol, and the proper test is ``#if SystemV''.
If you use ``#define SystemV'' instead of ``#define SystemV YES'',
thinking that the former will turn it on, you'll
have problems; ``#if SystemV'' turns into just ``#if'' and generates:
.Ds .IP
cpp: /usr/tmp/tmp-imake.\fImmm\fR:line \fInnn\fR: syntax error
.De .IP
.B Example:
YES/NO symbols must be tested properly.
If you test such a symbol with #ifdef, the test will always succeed,
whether the value is YES or NO; the symbol is defined in
.I both
cases.
.IP
.B Example:
Existent/nonexistent symbols must be tested properly.
A symbol such as UseInstalled is simply defined (as nothing) or left undefined.
If you say ``#define UseInstalled NO'', thinking that will turn it
off, you will be surprised.
(The test ``#ifdef UseInstalled'' will succeed.)
.IP
.B Example:
Existent/nonexistent symbols must be properly.
Such a symbol may be defined as nothing.
If you test it, incorrectly, with ``#if symbol'', the test will fail.
Use #ifdef.
.IP (3)
Rule definition problems
.IP
These can occur after ``make Makefile'', if a rule in
.I Imake.rules
is
#define'd with a space between the rule name and the argument list:
.Ds .IP
#define rule(arglist)               right
#define rule (arglist)              wrong!
.De .IP
.I cpp
will define rulename as ``(arglist)''.
When this happens, you'll see
.Ds .IP
make:  line \fInnn\fR: syntax error
.De .IP
and you'll find `` (arglist)'' on line nnn of the
.I Makefile
because ``rule'' wasn't expanded properly (or at least not the way you
expect).
Fix it and try again.
.IP
Other similar errors can occur if rules are defined or used with spaces
anywhere between the end of the macro name to the closing parenthesis of
the argument list.
This is particularly true if a macro argument is used
to generate rule target names or file names.
For example, AliasedLibaryTarget() looks like this:
.Ds .IP
.ta .5i +4i
#define	AliasedLibraryTarget(libname,alias)	@@\e
AllTarget(lib/**/alias.a)	@@\e
		@@\e
lib/**/alias.a: lib/**/libname.a	@@\e
	$(RM) $@	@@\e
	$(LN) lib/**/libname.a $@
.De .IP
If invoked as ``AliasedLibraryTarget(xyz, libxyz)'', this will
expand to:
.Ds .IP
lib libxyz.a: libxyz.a
	$(RM) $@
	$(LN) libxyz.a $@
.De .IP
which won't do very well when
.I make
gets ahold of it.
There will appear to be two targets (not one) having a dependency on
.I libxyz.a \*-and
one of them is itself!
.IP (4)
Failure to supply
.I cpp
symbol default values
.IP
When a
.I make
variable is equated to a
.I cpp
symbol, the
.I cpp
symbol must be defined somewhere, even if it's just
defined as nothing.
Otherwise the make variable will be set to the literal
.I cpp
symbol name.
That is, if you have ``MAKEVAR=CppSymbol'', it must be preceded
somewhere by:
.Ds .IP
#ifndef CppSymbol
#define CppSymbol \fIwhatever\fR /* (``whatever'' might be empty) */
#endif
.De .IP
or MAKEVAR will end up with the literal value ``CppSymbol''.
.IP (5)
Macro expansion failure
.IP
Suppose you include the following line in your
.I Imakefile
so that a special version of your library will be compiled:
.Ds .IP
DebuggedAndProfiledLibraryObjectrule()
.De .IP
After you regenerate
.I Makefile ,
you try to use it and get the message:
.Ds .IP
Make: Must be a separator on rules line \fInnn\fR.  Stop.
.De .IP
This is symptomatic of a spelling error.
If you look closely at the
.I Imake.rules
file, you'll find that the rule name is actually spelled
``DebuggedAndProfiledLibraryObjectRule()''.
If you don't spell a rule name correctly, it won't be expanded.
Fortunately, these problems are easy to find, because at least the error
message tells you where to look.
Unfortunately, you can't say ``make Makefile'' after you fix
.I Imakefile ,
because the effect of the error is to make your
.I Makefile
unusable.
Use
.I xmkmf .
