วันนี้ได้ทำการเขียน makefile ใหม่ให้มีประสิทธิ์ภาพมากขึ้น โดยการใช้ inference rules จึงไปค้นหาเวปมาได้เวปดีๆ คือ
http://www-cip.physik.uni-bonn.de/pool/infos/make/advanced.html
Getting started with Makefiles: Advanced features
1. What are macros and why should I use them?
Think of programs that shall be used at different locations: CERN, DESY, SLAC, University of Bonn, … The C/C++ programs are highly portable (at least after some effort) but the names of the programs used to compile them – as well as their arguments – may be very different. So it would be nice to once give the program name, the list of arguments, etc. and then only use symbolic names so that you can rapidly adjust to the local computing environment. Those symbolic names are called “macros”. Let’s recall Makefile5.
.IGNORE:
.SILENT:mirror: input.o mirror.o process.o output.o
g++ -o mirror input.o mirror.o process.o output.o
mirror.o: mirror.cc
g++ -c -Wall mirror.cc
input.o: input.cc
g++ -c -Wall input.cc
output.o: output.cc
g++ -c -Wall output.cc
process.o: process.cc
g++ -c -Wall process.cc
clean:
rm -f *.o core
distclean:
make clean
rm -f mirror
again:
make distclean
make mirror |
All object files are made using g++. On other machines that may be cxx, cpp, or something the like. So let’s define a macro for the C++ compiler:
The command line options are also different for different compilers so it’s a good idea to have a macro for them:
On some machines the command to remove files is rm (or rm -f), on others del, some use delete, some erase, and some even discard.So that’s another candidate for a macro:
Even make may not always be called make (though I know of no system where this is the case):
In our example the linker is also g++. In general that need not be the case. So we define the following two macros:
LINKER = g++
LINKER-FLAGS = -o |
The meaning of the following two macros is obvious:
OBJS = input.o mirror.o process.o output.o
TARGET = mirror |
After these changes the present state of the Makefile is Makefile6:
C++ = g++
C++-FLAGS = -c -Wall
LINKER = g++
LINKER-FLAGS = -o
RM = rm -f
MAKE = make
OBJS = input.o mirror.o process.o output.o
TARGET = mirror.IGNORE:
.SILENT:
$(TARGET): $(OBJS)
$(LINKER) $(LINKER-FLAGS) $(TARGET) $(OBJS)
mirror.o: mirror.cc
$(C++) $(C++-FLAGS) mirror.cc
input.o: input.cc
$(C++) $(C++-FLAGS) input.cc
output.o: output.cc
$(C++) $(C++-FLAGS) output.cc
process.o: process.cc
$(C++) $(C++-FLAGS) process.cc
clean:
$(RM) *.o core
distclean:
$(MAKE) clean
$(RM) $(TARGET)
again:
$(MAKE) distclean
$(MAKE) $(TARGET) |
2. What are inference rules and what are they good for?
If you have a close look at the rules to make mirror.o, input.o, output.o, and process.o you see that they are all the same:
<filename>.o: <filename>.cc
$(C++) $(C++-FLAGS) <filename>.cc |
To avoid all these lenthy entries in a Makefile there are inference rules: They describe the ‘standard procedure’ of making files of type b from files of type a. In this case the rule to make an object file from a given C++ file is
.cc.o:
$(C++) $(C++-FLAGS) $< |
or alternatively you could use
.cc.o:
$(C++) $(C++-FLAGS) $*.cc |
It’s easy to see what $< and $* mean but I’ll come back to that in just a moment. First I will change the rule for making the target ‘mirror’ so that it makes use of another strange-looking predefined macros:
$(TARGET): $(OBJS)
$(LINKER) $(LINKER-FLAGS) $@ $(OBJ) |
Now let’s have a look at the meaning of $*, $<, and $@:
$* ist the current target without an extension (the base file name) with path. For example, in
input.o: input.cc
$(C++) $(C++-FLAGS) $*.cc |
the value of $* is input. $* is commonly used only in inference rules and command lines.
$@ is the current target (including extension, if any). For example, in
mirror: $(OBJS)
$(LINKER) $(LINKER-FLAGS) $@ $(OBJS) |
the value of $@ is mirror.
$< is a dependent file out-of date with the target file. For example,
.cc.o:
$(C++) $(C++-FLAGS) $*.cc |
Notice that $<, in an inference rule such as .cc.o, is equivalent to $*.cc (as already mentioned).
Just to remind you: Your Makefile should now look like Makefile7:
C++ = g++
C++-FLAGS = -c -Wall
LINKER = g++
LINKER-FLAGS = -o
RM = rm -f
MAKE = make
OBJS = input.o mirror.o process.o output.o
TARGET = mirror.IGNORE:
.SILENT:
$(TARGET): $(OBJS)
$(LINKER) $(LINKER-FLAGS) $@ $(OBJS)
.cc.o:
$(C++) $(C++-FLAGS) $<
clean:
$(RM) *.o core
distclean:
$(MAKE) clean
$(RM) $(TARGET)
again:
$(MAKE) distclean
$(MAKE) $(TARGET) |
3. Continuation lines
If your project consists of a great number of files (or you need lots of options) you may wish to split lines. Another reason for splitting lines is increasing the readability of your Makefile (don’t underestimate that point!). In Makefile7 you could change the line reading
| OBJS = input.o mirror.o process.o output.o |
to read
OBJS = input.o \
mirror.o \
process.o \
output.o |
4. Comments
You can also add comments to your Makefile like the ones in Makefile8:
C++ = g++ # use GNU C++ compiler
C++-FLAGS = -c -Wall # warn all
LINKER = g++ # use GNU C++ as linker
LINKER-FLAGS = -o # flags for linker
RM = rm -f # how to remove files
MAKE = make # name of make utility
# you cannot add comments after the continuation character!
OBJS = input.o \
mirror.o \
process.o \
output.o
TARGET = mirror # name of executable .IGNORE: # ignore problems (as far as possible)
.SILENT: # don't echo commands executed
$(TARGET): $(OBJS)
$(LINKER) $(LINKER-FLAGS) $@ $(OBJS)
.cc.o:
$(C++) $(C++-FLAGS) $<
# remove object files and core (if any)
clean:
$(RM) *.o core
# remove object files, core dump, and executable (if any)
distclean:
$(MAKE) clean
$(RM) $(TARGET)
# remove object files, core dump, and executable (if any) and
# make them again.
again:
$(MAKE) distclean
$(MAKE) $(TARGET) |
5. The .SUFFIXES pseudo target
make comes with a couple of predefined rules how to make certain files. To get rid of them simply use
If you do that you have to list all suffixes that your Makefile does support. In the above case that would be
You can also use .SUFFIXES to add rules to the predefined ones (in precisely the same manner as in the example above).
6. Using environment variables
You can use environment variables in the same way as macros (besides that they are defined outside the Makefile):
Surely this is not a very useful example. Finding more useful ones is left to the reader. Now, your Makefile should look like Makefile9:
C++ = g++ # use GNU C++ compiler
C++-FLAGS = -c -Wall # warn all
LINKER = g++ # use GNU C++ as linker
LINKER-FLAGS = -o # flags for linker
RM = rm -f # how to remove files
MAKE = make # name of make utility
# you cannot add comments after the continuation character!
OBJS = input.o \
mirror.o \
process.o \
output.o
TARGET = mirror # name of executable.IGNORE: # ignore problems (as far as possible)
.SILENT: # don't echo commands executed
.SUFFIXES: # get rid of predefined rules
.SUFFIXES: .cc .o
$(TARGET): $(OBJS)
$(LINKER) $(LINKER-FLAGS) $@ $(OBJS)
.cc.o:
$(C++) $(C++-FLAGS) $<
# remove object files and core (if any)
clean:
$(RM) *.o core
# remove object files, core dump, and executable (if any)
distclean:
$(MAKE) clean
$(RM) $(TARGET)
# remove object files, core dump, and executable (if any) and
# make them again.
again:
$(MAKE) distclean
$(MAKE) $(TARGET)
# echo username
info:
echo User is: $(USER) |
Last changed tuesday 6/2/1998