$(TOP) in the document refers to the top level directory
of the X Windows source tree.
On the Silicon Graphics System it is /usr/lib/X11
Imake is a tool for generating Makefiles for large software distributions. Imake uses the C preprocessor on macro- makefiles (called Imakefiles) to generate Makefiles. It uses a predefined template file for default values and macros. The imake program is part of, and used by the X Windows System software distribution. (The following description uses fragments from the X Windows distribution as examples --- you might want to look at the files in $(TOP)/config for more detailed information. The source code for imake is in $(TOP)/util/imake, and includes a sample.rules file as well as a manual page)
Why Imake
A Simple Example
Generating the Makefile for the first time
The Imake Template
Simple Imakefiles
Miscellaneouse
History
Gotchas
Imake Variables
Makefiles have some limitations for large software distributions, distributed over multiple directories, viz.
For example, for a simple program, xcalc, the Imakefile looks something like:
LOCAL_LIBRARIES = $(XLIB) SYS_LIBRARIES = -lm #ifdef MacIIArchitecture DEFINES = -DIEEE #endif /* MacIIArchitecture */ SRCS = xcalc.c sr.c OBJS = xcalc.o sr.o ComplexProgramTarget(xcalc)
![]()
This will generate a Makefile which will contain an all target, and an xcalc target, both of which will result in the program xcalc being compiled and linked. The Makefile will also contain an install target, which will install the binaries in specified destination directories, an install.man target, which will install the manual pages for the program, a clean target, which will remove all compiler generated files, editor backup files, etc., and a depend target which will invoke the makedepend program to generate Makefile depenencies.
It will also contain a Makefile target, so you only need to type make Makefile to regenerate the Makefile should the Imakefile change.
The real work lies in ComplexProgramTarget, which is defined in the template as a #define macro, which expands out to the make rules needed to generate program xcalc, from the variables SRCS and OBJS. Let's take a look at that particular rule.
/****************************************************** * This target is the general interface for building * * a single program * ******************************************************/ #define ComplexProgramTarget(program) PROGRAM = program AllTarget(program) program: $(OBJS) $(LOCAL_LIBRARIES) $(RM) $@ $(CC) -o $@ $(OBJS) $(LOCAL_LIBRARIES) $(LDFLAGS) $(SYSLAST_LIBRARIES) relink:: $(RM) $(PROGRAM) $(MAKE) $(MFLAGS) $(PROGRAM) InstallProgram(program,$(BINDIR)) InstallManPage(program,$(MANDIR)) DependTarget() clean:: $(RM) $(PROGRAM)
![]()
Ignoring AllTarget() for now, we see that program is defined to depend on the OBJS, and LOCAL_LIBRARIES, and to make it, the old version is removed (a precaution to save disk space, among other things), and then, program is linked from OBJS, LOCAL_LIBRARIES, and SYSLAST_LIBRARIES. (LDFLAGS are link flags)
An additional target relink:: is also generated, which can
be used if libraries change. InstallProgram generates the the
install:: target, which puts the program in BINDIR,
InstallManPage installs the manual page, DependTarget generates
the target for make depend which invokes a makedepend program to
follow all #includes in the source. These macros are listed
below --- they aren't very complex.
/************************ * Install a man page. * ************************/ #define InstallManPage(file,dest) InstallManPageLong(file,dest,file) /*********************** * Install a program * ***********************/ #define InstallProgram(program,dest) install:: program $(INSTALL) -c $(INSTALLFLAGS) program dest /******************************************** * This makes the depend target given OBJS. * ********************************************/ #define DependTarget() depend:: $(DEPEND) depend:: $(DEPEND) -s "# DO NOT DELETE" -- $(CFLAGS) -- $(SRCS) $(DEPEND): @echo "making $@"; cd $(DEPENDSRC); $(MAKE) #define AllTarget(depends) all:: depends
![]()
Note the use of make variables like $(INSTALL) to describe programs that are used - this makes it possible to define these in the templates, and thus enhances portability.
Also, note that the make targets use a conditional
dependency branch with double colons (::) rather than the more
common single colon (:). This allows multiple make targets.
Based on all this, the Imakefile expands to the following Makefile:
LOCAL_LIBRARIES = $(XLIB) SYS_LIBRARIES = -lm SRCS = xcalc.c sr.c OBJS = xcalc.o sr.o PROGRAM = xcalc all:: xcalc xcalc: $(OBJS) $(LOCAL_LIBRARIES) $(RM) $@ $(CC) -o $@ $(OBJS) $(LOCAL_LIBRARIES) $(LDFLAGS) $(SYSLAST_LIBRARIES) relink:: $(RM) $(PROGRAM) $(MAKE) $(MFLAGS) $(PROGRAM) install:: xcalc $(INSTALL) -c $(INSTALLFLAGS) xcalc $(BINDIR) install.man:: xcalc.man $(INSTALL) -c $(INSTMANFLAGS) xcalc.man $(MANDIR)/xcalc.n$(MANDIR)/xcalc.n depend:: $(DEPEND) depend:: $(DEPEND) -s "# DO NOT DELETE" -- $(CFLAGS) -- $(SRCS) $(DEPEND): @echo "making $@"; \ cd $(DEPENDSRC); $(MAKE) clean:: $(RM) $(PROGRAM)
![]()
We've omitted a few standard definitions for CFLAGS, CC,
DEPEND, INSTALL, RM, LDFLAGS etc. These are installation and
system dependent, and form part of the Imake template. They will
precede the above text.
After you make changes to the Imakefile, you may do make Makefile to regenerate the Makefile. You can use the Makefile to rebuild itself since it has a "Makefile" target in it containing rules to do so.
If you only have an Imakefile then you are at the mercy of imake. This is where xmkmf comes in useful: write your Imakefile, execute xmkmf and it will create a Makefile for you. Thereafter you can use make Makefile. xmkmf, imake and makedepend expect the configuration files to have been installed. To get your first Makefile do
touch Imakefile ; xmkmf
For a more portable setup, it is possible to split the template file into the following components:
the rules
the system-dependent macros, which vary depending on the
specific architecture/operating-system combination, things
like the compiler name, flags.
the site dependent variables --- where to install the
binaries, libraries etc.
For example the X distribution has an Imake.rules file,
which defines various rules like ComplexProgramTarget(), system
dependent files of the form machinetype.cf, eg. sun.cf,
ultrix.cf, ..., and a site dependent variables file called
site.def.
The first Imakefile is for a simple program that has only one source file xstring.c and a corrsponding manual page xstring.man It requires the libraries XAWLIB, XTOOLLIB, and XLIB (the X Athena widgets, the X Toolkit, and the C library interface to X).
LOCAL_LIBRARIES = $(XAWLIB) $(XTOOLLIB) $(XLIB) SimpleProgramTarget(xstring)
![]()
A slightly more complex Imakefile is the one for xcalc, the example described earlier.
LOCAL_LIBRARIES = $(XLIB) SYS_LIBRARIES = -lm #ifdef MacIIArchitecture DEFINES = -DIEEE #endif /* MacIIArchitecture */ SRCS = xcalc.c sr.c OBJS = xcalc.o sr.o ComplexProgramTarget(xcalc)
![]()
An Imakefile for a directory with multiple programs is a little more complicated. ComplexProgramTarget_1 is similar to ComplexProgramTarget but it uses OBJS1 to link the program instead of OBJS. The first argument is the program name, the second is the list of LOCAL_LIBRARIES, and the third is the list of SYSLAST_LIBRARIES. It is possible to have more complex programs defined (the X Imake rules allow for 3 such targets) called ComplexProgramTarget_1, ComplexProgramTarget_2, and ComplexProgramTarget_3 which link OBJS1, OBJS2 and OBJS3 respectively.
NormalProgramTarget - generate rules to compile and link
the indicated program; since it does not use any default object
files, it may be used for multiple programs in the same
Imakefile. SingleProgramTarget is the obsolete form without
deplibs.
NormalProgramTarget
(program,objects,deplibs,locallibs,syslibs)
SingleProgramTarget
(program,objects,locallibs,syslibs)
SRCS = popup.c xboxes.c xmenu.c xlist.c LOCAL_LIBS = XawClientLibs DEPLIBS = XawClientDepLibs PROGRAMS = popup xboxes xmenu xlist AllTarget($(PROGRAMS)) NormalProgramTarget(popup,popup.o,$(DEPLIBS),$(LOCAL_LIBS), /**/) NormalProgramTarget(xboxes,xboxes.o,$(DEPLIBS),$(LOCAL_LIBS), /**/) NormalProgramTarget(xmenu,xmenu.o,$(DEPLIBS),$(LOCAL_LIBS), /**/) NormalProgramTarget(xlist,xlist.o,$(DEPLIBS),$(LOCAL_LIBS), /**/) DependTarget()
![]()
SRCS = popup.c xboxes.c xmenu.c xlist.c LOCAL_LIBS = XawClientLibs DEPLIBS = XawClientDepLibs PROGRAMS = popup xboxes xmenu xlist AllTarget($(PROGRAMS)) SingleProgramTarget(popup,popup.o,$(LOCAL_LIBS), /**/) SingleProgramTarget(xboxes,xboxes.o,$(LOCAL_LIBS), /**/) SingleProgramTarget(xmenu,xmenu.o,$(LOCAL_LIBS), /**/) SingleProgramTarget(xlist,xlist.o,$(LOCAL_LIBS), /**/) DependTarget()
![]()
CFLAGS, LINTFLAGS and LDFLAGS are defined as follows:
CFLAGS = $(CDEBUGFLAGS) $(INCLUDES) $(STD_DEFINES) $(DEFINES) LINTFLAGS = $(LINTOPTS) $(INCLUDES) $(STD_DEFINES) $(DEFINES) -DLINT LDFLAGS = $(CDEBUGFLAGS) $(SYS_LIBRARIES) $(SYSAUX_LIBRARIES)
![]()
An Imakefile can redefine some of the right hand side variables - typically:
CDEBUGFLAGS can be -g or -O, or if you're using the GNU C
compiler, -g -O,
INCLUDES is usually -I${TOP} by default,
DEFINES is left for user defined -D flags. (STD_DEFINES is
defined by the system macros file and should be left alone)
SYS_LIBRARIES or SYSAUX_LIBRARIES can be used for -l options
to link in system libraries. (SYSLAST_LIBRARIES is defined by
the system macros, typically for portability or emulation).
An Imakefile for a library looks something like:
# # This library contains miscellaneous utility routines and is # not part of the Xlib standard. # STD_DEFINES = LibraryDefines CDEBUGFLAGS = LibraryCDebugFlags INCLUDES = -I. -I$(TOP) -I$(TOP)/X11 INSTALLFLAGS = $(INSTINCFLAGS) LINTLIBS = $(LINTXLIB) #ifdef OsNameDefines OS_NAME_DEFINES = OsNameDefines #endif HEADERS = \ Xmu.h SRCS = Atoms.c CrPixFBit.c CvtStdSel.c DefErrMsg.c \ DrRndRect.c FToCback.c Lookup.c Lower.c \ RdBitF.c StrToBS.c StrToBmap.c StrToCurs.c \ StrToJust.c StrToOrnt.c StrToWidg.c OBJS = Atoms.o CrPixFBit.o CvtStdSel.o DefErrMsg.o \ DrRndRect.o FToCback.o Lookup.o Lower.o \ RdBitF.o StrToBS.o StrToBmap.o StrToCurs.o \ StrToJust.o StrToOrnt.o StrToWidg.o all:: NormalLibraryTarget(Xmu,$(OBJS)) LintLibraryTarget(Xmu,$(SRCS)) InstallLibrary(Xmu,$(USRLIBDIR)) InstallLintLibrary(Xmu,$(LINTLIBDIR)) InstallMultiple($(HEADERS),$(INCDIR)) DependTarget() NormalLintTarget($(SRCS))
![]()
Sometimes, it is nice to be able to generate libraries compiled with debugging on, and libraries compiled with profiling on, so that these can be used by developers. Something like the following, added before the NormalLibraryTarget
#if DebugLibXmu && ProfileLibXmu DebuggedAndProfiledLibraryObjectRule() #else # if DebugLibXmu DebuggedLibraryObjectRule() # else # if ProfileLibXmu ProfiledLibraryObjectRule() # else NormalLibraryObjectRule() # endif # endif #endif
![]()
and something like the following added before the InstallMultiple would do the trick quite nicely.
#if ProfileLibXmu ProfiledLibraryTarget(Xmu,$(OBJS)) InstallLibrary(Xmu_p,$(USRLIBDIR)) #endif #if DebugLibXmu DebuggedLibraryTarget(Xmu,$(OBJS)) #endif
![]()
If we wanted a debugging library, we'd define the symbol
DebugLibXmu in the site.def file, and the DebuggedLibraryRule
would ensure it generated a library target for libname_d.a, while
if we defined the ProfileLibXmu symbol, we would get a profiled
library target for libname_p.a, in addition to the usual
libname.a. This sort of configurability is very hard to get from
a vanilla makefile.
The following Imakefile is for a directory that only contains subdirectories, and we want to make each of the subdirectories:
#define IHaveSubdirs #define PassCDebugFlags 'CDEBUGFLAGS=$(CDEBUGFLAGS)' SUBDIRS = X \ oldX \ Xt \ Xmu \ Xaw MakeSubdirs($(SUBDIRS)) DependSubdirs($(SUBDIRS)) MakeLintLibSubdirs($(SUBDIRS)) MakeLintSubdirs($(SUBDIRS),install.ln,install.ln)
![]()
This will have the Makefiles target defined as well as
Makefile and using the make Makefiles command will cause it to
regenerate the Makefiles in the subdirectories. It does this by
prepending a ``../'' in front of the TOP variable, which means
that TOP should be a relative path. (This is the only reason for
TOP to be a relative path - if there are no subdirectories, TOP
can easily be an absolute path).
Imake puts null comments of the form /**/ in front of normal make style comments i.e. the # to protect them from the C preprocessor. They survive unscathed in the resulting Makefile, while C style comments vanish. This provides a good way to distinguish betwen Imakefile comments and comments that you want to appear in the Makefile.
However, it does not protect comments in the files that are
included (site.def, Imake.rules, machine.macros) so if you want
to use # style comments in those files, you must protect them
yourself with in front of them.
Imake was originally written by Todd Brunhoff
(toddb@tekcrl.crl) for X Windows Version 11 Release 1. The
configurations for X Windows Version 11 Releases 2, 3 and 4 were
reworked considerably by Jim Fulton (jim@expo.lcs.mit.edu) to
isolate the rules, machine dependent information, and site
dependent information into separate files.
Don't forget ``make depend'' after ``make Makefile''. (Or
xmkmf -a).
If you need to pass special -D's to the C compiler, use the
make variable DEFINES.
If you need to pass special -I's to the C compiler, use the
make variable INCLUDES.
These will be passed to compilations automatically in CFLAGS (see definitions of CFLAGS, ALLDEFINES and ALLINCLUDES in Imake.tmpl ).
Some rules may not work as you expect after changing As an
instance of this, Paul DuBois in "Using Imake to Configure the
X Window System Version 11, Release 4", notes that the
following command lines are not always equivalent:"
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 Imakefile . You then
need to rebuild the Makefile so that the definition of
SUBDIRS is reset, and then rebuild Makefile in each of those
subdirectories. The first command line above builds the
``Makefiles'' target using the (old) value of SUBDIRS from
the (current) Makefile , which is incorrect. The second
command line rebuilds the Makefile , then runs a second make
process to build ``Makefiles''. The second process sees the
(new) value of SUBDIRS in the (new) Makefile , which
includes the new subdirectory, and has the intended result.
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 would the second time, since
SUBDIRS would be correct on the second try."
Click here for the meaning of the various Imake variables and where they get their default values from.
