วันนี้ได้ทำการเขียน 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 input.o: input.cc output.o: output.cc process.o: process.cc clean: distclean: again: |
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:
| C++ = g++ |
The command line options are also different for different compilers so it’s a good idea to have a macro for them:
| C++-Flags = -c -Wall |
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:
| RM = rm -f |
Even make may not always be called make (though I know of no system where this is the case):
| MAKE = make |
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) mirror.o: mirror.cc input.o: input.cc output.o: output.cc process.o: process.cc clean: distclean: again: |
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) .cc.o: clean: distclean: again: |
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) .cc.o: # remove object files and core (if any) # remove object files, core dump, and executable (if any) # remove object files, core dump, and executable (if any) and |
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
| .SUFFIXES: |
If you do that you have to list all suffixes that your Makefile does support. In the above case that would be
| .SUFFIXES: .o .cc |
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):
| info: echo $(USER) |
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) .cc.o: # remove object files and core (if any) # remove object files, core dump, and executable (if any) # remove object files, core dump, and executable (if any) and # echo username |
Last changed tuesday 6/2/1998

0 Responses
Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.