Target Junior Makefiles
Andrew W. Fitzgibbon and William A. Hoffman
Introduction
The Target Jr makefiles provide
- Objects placed in a separate directory for each combination of architecture,
build flags, and compiler.
- O/S and compiler dependencies factored out using configure
- Automatic dependency checking.
- Convenience targets: clean, FORCE, echovar-VAR etc.
- TargetJr specifics such as documentation generation.
Key limitations are that there is expected to be only one major target
(library or program) in a directory, although there is provision for a set
of single-file programs (see MINI_SO_SOURCES and MINI_SO_TESTS).
Structure of a makefile
The general strategy employed is that a makefile declares the main target
it's going to make, say a library:
LIBRARY = MultiView
Then the things that the target needs:
USES = Numerics Basics COOL
And the sources and objects that make up that library:
SOURCES = file1.cc file2.cc file3.c
Finally we include the generic rule-set into the makefile:
include ${SYS_IUPACKAGES}/config/top.mk
Now we have a makefile which makes a library called "MultiView" (plus
whatever other prefices and suffixes are required for particular
architectures and builds:
libMultiView.a for a static unix build,
MULTIVIEW.DLL for the
Gates of Hades), from three source
files, and supplied the appropriate include directories etc. to use the TargetJr
packages Numerics, Basics and COOL.
Now the key point of the system emerges: this is a declarative
language, as is ``make'' itself. All desired actions are chosen by setting
variables which the generic rules interpret.
Customization
All makefile strategies are nice and minimal when you use them in their
default mode. What normally causes hypertension is trying to modify them
in order to make a teeny weeny alteration to their default behaviour.
We will explore customization in two ways. The first is using these
makefiles to make a completely different program; the second is to make
small modifications to the defaults.
- New program
- You want a sophisticated makefile, you don't want target.
- Small modifications
- You want to change compilation flags for one file.
This is easy, just set FLAGS_filename.cc to contain
the extra flags. For example
FLAGS_slow-code.cc = -O3
- Many programs in the same directory
-
If the programs are all one-file ones, then just use
MINI_PROG_SOURCES = prog1.cc prog2.cc
If you want multiple-file programs, then list all the sources in
SOURCES as usual and set MINI_PROG_EXES to the
list of program names. To define the object files that make each program,
set OBJECTS_progname. For example:
# Make program "a" from (a.cc file-in-both.cc)
# Make program "b" from (b1.cc b2.cc file-in-both.cc)
SOURCES = a.cc b1.cc b2.cc file-in-both.cc
MINI_PROG_EXES = a b
OBJECTS_a = $(OBJDIR)/a.o $(OBJDIR)/file-in-both.o
OBJECTS_b = $(OBJDIR)/b1.o $(OBJDIR)/b2.o $(OBJDIR)/file-in-both.o
- Linking 3rd party libraries
- You want to add your own or other people's libraries. Add the include
directory to
INCDIRS and the library directory to LIBDIRS.
Design issues
- Must work on "both" operating systems. Therefore all concepts must be
expressed generically, therefore INCDIRS rather than -I flags, LIBDIRS
rather than -L
- Most makefile strategies assume that source files can be inferred from
object files, but this doesn't work in many cases - the most common being
that there is a "file.c" in the same directory as a "file.cc".
In the Target makefiles, you generally supply the sources that will do
something, rather than the objects.
- This makefile should be included after all variables have been set,
i.e. generally at the end of the makefile. This is to make it somewhat
easier to debug, by using := instead of = for the variables.
Any extra makefile targets should generally be put after this is included,
so that they have access to computed variables such as LDPATH etc.
Include file structure
What actually gets included when you include "top.mk"?
top.mk # 2 lines, defines $(configdir)
generic.mk
identify-os.mk # No user-servicable parts inside, just sets $(OS)
config-$(OS)-$(COMPILER).mk # ./configured from config.mk.in
$(OS)-$(COMPILER).mk # Things that can't be ./configured about this OS
site.mk # *SITE* preferences.
user.mk # Even more local preferences
build-$(BUILD).mk # Sets code-generation flags for different build types.
Annotated list of variables
- SOURCES
- List of source files to compile to objects.
- OBJECTS
- List of additional objects whose compilation is handled in the
makefile. If you find you're using this variable to do something
generic, such as compile e.g. a YACC or pascal file, please consider
adding the rules to this rules.mk. Mail targetjr@robots.ox.ac.uk if
you wonder how generic your rule will be.
- USES
- A list of package names in specific-to-general order. These are used
to append IUPACKAGEHOME packages to INCDIRS and LIBDIRS.
"Specific-to-general" means that "higher-level" packages such as Viewing or
Segmentation come before more generic packages such as Numerics, which
comes before even more generic things such as COOL.
- USE_*
- A collection of boolean variables which request that certain
non-Target packages be included. These are placed in their correct
relative positions in the include and link lines by rules.mk, which is a
boon. It generally takes about 5 years of experience to be able to get
the order of these things right. Some examples as of this writing:
- USE_INTERVIEWS
- USE_FRESCO
- USE_STDC++
- USE_TCL
- USE_MSQL
- USE_OPENGL
- USE_XGL
- USE_IUE
- USE_X11
- USE_SOCKETS
One special case is USE_RENDERING as it enables 3D rendering for
Target, choosing the appropriate 3D library depending on options set at
"configure" time (e.g. the OPENGLRENDER variable). It sets a IU3DLIBS
variable which should be put in ones IULIBS variable just after
GenericView3D.
Compiler flags:
- CCFLAGS
- Special code-generation flags.
- INCDIRS
- Complete include-file search path, without
-I flags.
Can be set before including "top.mk", which will then append to it based on
the USES and USE_* variables. Further appends after the include will
happen at the end, but that's normally too late to be of any use for
anything other than fixing broken compilers.
- DEFINES
- List of -D, -U flags for C preprocessor.
Linker flags
These are needed only if you are making a PROGRAM (well actually you need
them on
windows).
- LIBDIRS
- Complete linker directory path, no -L flags.
- LDLIBS
- Complete list of libraries to be loaded, its default definition is in
terms of three sub-lists:
- LOCAL_LIBS
- Can be set in toplevel makefile to add to the library list.
- IULIBS
- Portions of TargetJr selected, which probably includes $(IU3DLIBS).
- STDLIBS
- Based on values of variables USE_X11, USE_SOCKETS etc.
In general it's probably a good idea to redefine LDLIBS in your own
makefile, inserting $(IULIBS) somewhere in the middle, with $(STDLIBS) at
the end:
LDLIBS = -lmystuff $(IULIBS) -lotherstuff $(STDLIBS)
This makes all the target libraries behave as a single