6. Imake ©


     $(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



6.1. Why Imake

Makefiles have some limitations for large software distributions, distributed over multiple directories, viz.




6.2. A simple example

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.


6.3. Generating the Makefile for the first time

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




6.4. The Imake template

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.


6.5. Simple Imakefiles

6.5.1. A single program, one source file

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)





6.5.2. A single program, multiple source files

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)





6.5.3. Multiple programs

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).



6.5.4. A library archive of object files

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.


6.5.5. Subdirectories

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).


6.6. Miscellaneous

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.


6.7. History

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.


6.8. Gotchas

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:"
make Makefile Makefiles
make Makefile ; make Makefiles

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."


6.9. Imake Variables

Click here for the meaning of the various Imake variables and where they get their default values from.