来自:http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/
A Simple Makefile Tutorial
Makefiles are a simple way to organize code compilation. This tutorialdoes not even scratch the surface of what is possible usingmake, but is intended as a starters guide so that you canquickly and easily create your own makefiles for small to medium-sizedprojects.
A Simple Example
Let's start off with the following three files, hellomake.c,hellofunc.c, and hellomake.h, which would represent a typical mainprogram, some functional code in a separate file, and an include file,respectively.
hellomake.c | hellofunc.c | hellomake.h |
---|---|---|
#include <hellomake.h> int main() { // call a function in another file myPrintHelloMake(); return(0); } | #include <stdio.h> #include <hellomake.h> void myPrintHelloMake(void) { printf("Hello makefiles!\n"); return; } | /* example include file */ void myPrintHelloMake(void); |
Normally, you would compile this collection of code by executing the following command:
gcc -o hellomake hellomake.c hellofunc.c -I.
This compiles the two .c files and names the executable hellomake. The-I. is included so that gcc will look in the currentdirectory (.) for the include file hellomake.h. Without a makefile,the typical approach to the test/modify/debug cycle is to use the uparrow in a terminal to go back to your last compile command so youdon't have to type it each time, especially once you've added a fewmore .c files to the mix.
Unfortunately, this approach to compilation has two downfalls. First,if you lose the compile command or switch computers you have to retypeit from scratch, which is inefficient at best. Second, if you are onlymaking changes to one .c file, recompiling all of them every time isalso time-consuming and inefficient. So, it's time to see what we cando with a makefile.
The simplest makefile you could create would look something like:
Makefile 1hellomake: hellomake.c hellofunc.c gcc -o hellomake hellomake.c hellofunc.c -I.
If you put this rule into a file called Makefile ormakefile and then type make on the command line itwill execute the compile command as you have written it in themakefile. Note that make with no arguments executes the firstrule in the file. Furthermore, by putting the list of files on whichthe command depends on the first line after the :, make knowsthat the rule hellomake needs to be executed if any of thosefiles change. Immediately, you have solved problem #1 and can avoidusing the up arrow repeatedly, looking for your last compilecommand. However, the system is still not being efficient in terms ofcompiling only the latest changes.
One very important thing to note is that there is a tab before the gcccommand in the makefile. There must be a tab at the beginning of anycommand, and make will not be happy if it's not there.
In order to be a bit more efficient, let's try the following:
Makefile 2CC=gcc CFLAGS=-I. hellomake: hellomake.o hellofunc.o $(CC) -o hellomake hellomake.o hellofunc.o -I.
So now we've defined some constants CC andCFLAGS. It turns out these are special constants thatcommunicate to make how we want to compile the fileshellomake.c and hellofunc.c. In particular, the macro CC isthe C compiler to use, and CFLAGS is the list of flags topass to the compilation command. By putting the objectfiles--hellomake.o and hellofunc.o--in the dependency list and in therule, make knows it must first compile the .c versionsindividually, and then build the executable hellomake.
Using this form of makefile is sufficient for most small scaleprojects. However, there is one thing missing: dependency on theinclude files. If you were to make a change to hellomake.h, forexample, make would not recompile the .c files, even thoughthey needed to be. In order to fix this, we need to tell makethat all .c files depend on certain .h files. We can do this bywriting a simple rule and adding it to the makefile.
Makefile 3CC=gcc CFLAGS=-I. DEPS = hellomake.h %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) hellomake: hellomake.o hellofunc.o gcc -o hellomake hellomake.o hellofunc.o -I.
This addition first creates the macro DEPS, which is the set of .hfiles on which the .c files depend. Then we define a rule that appliesto all files ending in the .o suffix. The rule says that the .o filedepends upon the .c version of the file and the .h files included inthe DEPS macro. The rule then says that to generate the .o file,make needs to compile the .c file using the compiler definedin the CC macro. The -c flag says to generate the object file, the-o $@ says to put the output of the compilation in the filenamed on the left side of the :, the $< is the first item inthe dependencies list, and the CFLAGS macro is defined as above.
As a final simplification, let's use the special macros $@and $^, which are the left and right sides of the :,respectively, to make the overall compilation rule more general. Inthe example below, all of the include files should be listed as partof the macro DEPS, and all of the object files should be listed aspart of the macro OBJ.
Makefile 4CC=gcc CFLAGS=-I. DEPS = hellomake.h OBJ = hellomake.o hellofunc.o %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) hellomake: $(OBJ) gcc -o $@ $^ $(CFLAGS)
So what if we want to start putting our .h files in an includedirectory, our source code in a src directory, and some locallibraries in a lib directory? Also, can we somehow hide thoseannoying .o files that hang around all over the place? The answer, ofcourse, is yes. The following makefile defines paths to the includeand lib directories, and places the object files in an objsubdirectory within the src directory. It also has a macro definedfor any libraries you want to include, such as the math library-lm. This makefile should be located in the srcdirectory. Note that it also includes a rule for cleaning up yoursource and object directories if you type make clean. The.PHONY rule keeps make from doing something with a file namedclean.
Makefile 5IDIR =../include CC=gcc CFLAGS=-I$(IDIR) ODIR=obj LDIR =../lib LIBS=-lm _DEPS = hellomake.h DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS)) _OBJ = hellomake.o hellofunc.o OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ)) $(ODIR)/%.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) hellomake: $(OBJ) gcc -o $@ $^ $(CFLAGS) $(LIBS) .PHONY: clean clean: rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~
So now you have a perfectly good makefile that you can modify tomanage small and medium-sized software projects. You can add multiplerules to a makefile; you can even create rules that call otherrules. For more information on makefiles and the makefunction, check out the GNU Make Manual, which will tell you more than you ever wanted to know (really).