.\" 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 %% %%system description and %%build definitions %% %%#include %% %% %%#include %% %% %%#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 #define MacroIncludeFile #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 #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 #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 .