A Simple Makefile Tutorial
A Simple Makefile Tutorial: http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/
Makefiles are a simple way to organize code compilation. This tutorial does not even scratch the surface of what is possible using make, but is intended as a starters guide so that you can quickly and easily create your own makefiles for small to medium-sized projects.
Makefile是一種以簡單的方式來組織代碼進行編譯的文件。本教程並不對make使用深刻研究,而是旨在爲初學者指南,讓您能夠快速,輕鬆地爲小、中等規模的項目建立本身的makefile文件。
A Simple Example
一個簡單的示例
Let's start off with the following three files, hellomake.c, hellofunc.c, and hellomake.h, which would represent a typical main program, some functional code in a separate file, and an include file, respectively.
讓咱們由如下三個文件開始:hellomake.c、hellofunc.c、hellomake.h,分別是:一個典型的主程序、一個單獨的文件包含一些功能程序、一個頭文件。
+--------------------------------------+---------------------------------+------------------------------+
| hellomake.c | hellofunc.c | hellomake.h |
+--------------------------------------+---------------------------------+------------------------------+
| #include <hellomake.h> | #include <stdio.h> | /* |
| | #include <hellomake.h> | example include file |
| int main() { | void myPrintHelloMake(void) { | */ |
| // call a function in another file | | |
| myPrintHelloMake(); | printf("Hello makefiles!\n"); | void myPrintHelloMake(void); |
| | | |
| return(0); | return; | |
| } | } | |
+--------------------------------------+---------------------------------+------------------------------+
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 current directory (.) for the include file hellomake.h. Without a makefile, the typical approach to the test/modify/debug cycle is to use the up arrow in a terminal to go back to your last compile command so you don't have to type it each time, especially once you've added a few more .c files to the mix.
編譯兩個.c文件並輸出可執行文件名稱爲hellomake 。-I 表示gcc會查看在當前目錄(.)的頭文件hellomake.h 。若是沒有makefile文件,在典型的軟件編寫流程中的測試、修改、調試周期中,使用向上箭頭在終端中找到你最後的使用編譯命令(最近成功編譯的那條命令),尤爲是當你組合了比較多的.c文件時,這樣你就沒必要每次都輸入它,反正你確定不肯意每次都輸入一串很長的編譯命令,並且每次都是一樣的編譯命令。
Unfortunately, this approach to compilation has two downfalls. First, if you lose the compile command or switch computers you have to retype it from scratch, which is inefficient at best. Second, if you are only making changes to one .c file, recompiling all of them every time is also time-consuming and inefficient. So, it's time to see what we can do with a makefile.
不幸的是,這種方法彙編有兩個弱點。首先,若是你丟失、忘記了編譯命令或者換了臺電腦,你必須從頭開始,這充其量是低效率的從新輸入。其次,若是你只更改一個.c文件,每次從新編譯全部的文件也費時,效率低下。那麼,是時候看看,當咱們有了makefile文件以後,咱們能作什麼。
The simplest makefile you could create would look something like:
您能夠建立的最簡單的Makefile應該是這個樣子:
Makefile 1
1 hellomake: hellomake.c hellofunc.c
2 gcc -o hellomake hellomake.c hellofunc.c -I.
If you put this rule into a file called Makefile or makefile and then type make on the command line it will execute the compile command as you have written it in the makefile. Note that make with no arguments executes the first rule in the file. Furthermore, by putting the list of files on which the command depends on the first line after the :, make knows that the rule hellomake needs to be executed if any of those files change. Immediately, you have solved problem #1 and can avoid using the up arrow repeatedly, looking for your last compile command. However, the system is still not being efficient in terms of compiling only the latest changes.
若是你把這個規則寫到一個名爲Makefile的文件或Makefile的文件裏,而後輸入make命令行會執行您在makefile文件寫的編譯命令。須要注意的是,不帶參數默認執行文件中的第一條規則。此外,將命令依賴的文件列表放在第一行冒號(:)後面,若是任何文件發生改變,make知道hellomake規則須要從新執行。這樣,你已經解決了第一個問題,避免反覆使用向上箭頭,尋找你的最後編譯命令。可是,該系統仍沒有被有效地知足只編譯的最新變化的.c文件這個需求。
One very important thing to note is that there is a tab before the gcc command in the makefile. There must be a tab at the beginning of any command, and make will not be happy if it's not there.
須要注意的一個很是重要的事情是,在makefile gcc的命令前一個tab鍵。必須在任何命令的開頭一個tab鍵,若是它不存在,make將會很不爽。 :)
In order to be a bit more efficient, let's try the following:
爲了更有效一點,讓咱們嘗試如下操做:
Makefile 2
1 CC=gcc
2 CFLAGS=-I.
3
4 hellomake: hellomake.o hellofunc.o
5 $(CC) -o hellomake hellomake.o hellofunc.o -I.
So now we've defined some constants CC and CFLAGS. It turns out these are special constants that communicate to make how we want to compile the files hellomake.c and hellofunc.c. In particular, the macro CC is the C compiler to use, and CFLAGS is the list of flags to pass to the compilation command. By putting the object files--hellomake.o and hellofunc.o--in the dependency list and in the rule, make knows it must first compile the .c versions individually, and then build the executable hellomake.
因此,如今咱們已經定義了一些常量CC和CFLAGS。這些特殊的常量用於告知make,咱們想要如何編譯hellomake.c、hellofunc.c。特別是,宏CC是給C編譯器使用,而CFLAGS是標誌列表,傳遞給編譯器的參數。經過將目標文件hellomake.o、hellofunc.o放在規則的依賴列表中,使make知道它必須首先編譯.C單獨版本,而後生成可執行的hellomake 。
Using this form of makefile is sufficient for most small scale projects. However, there is one thing missing: dependency on the include files. If you were to make a change to hellomake.h, for example, make would not recompile the .c files, even though they needed to be. In order to fix this, we need to tell make that all .c files depend on certain .h files. We can do this by writing a simple rule and adding it to the makefile.
使用這種形式的makefile足以知足大多數小規模項目。然而,有一件事會發生:依賴發生的改變內容在.h文件中。若是僅僅對hellomake.h進行修改,即便這樣須要從新編譯.c文件,make也不會從新編譯.c文件。爲了解決這個問題,咱們須要告訴make,全部.c文件取決於某些.h文件。咱們能夠經過編寫一個簡單的規則,並將其添加到生成文件中作到這一點。
Makefile 3
1 CC=gcc
2 CFLAGS=-I.
3 DEPS = hellomake.h
4
5 %.o: %.c $(DEPS)
6 $(CC) -c -o $@ $< $(CFLAGS)
7
8 hellomake: hellomake.o hellofunc.o
9 gcc -o hellomake hellomake.o hellofunc.o -I.
This addition first creates the macro DEPS, which is the set of .h files on which the .c files depend. Then we define a rule that applies to all files ending in the .o suffix. The rule says that the .o file depends upon the .c version of the file and the .h files included in the DEPS macro. The rule then says that to generate the .o file, make needs to compile the .c file using the compiler defined in the CC macro. The -c flag says to generate the object file, the -o $@ says to put the output of the compilation in the file named on the left side of the :, the $< is the first item in the dependencies list, and the CFLAGS macro is defined as above.
這除了首先建立宏DEPS,而且將.h文件設置成爲.c文件的依賴。而後,咱們定義適用於全部.o後綴結尾的文件的規則。規則說,.o文件將取決於文件的.C版本和DEPS宏定義的.h文件。而後,規則說,生成的.o文件,使用CC宏定義的編譯器來編譯.c文件。-c標誌說,生成目標文件,-o $@說把編譯輸出到冒號(:)左邊的文件名中,$ <在依賴列表中的第一項,和CFLAGS宏被定義如前面所述。
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. In the example below, all of the include files should be listed as part of the macro DEPS, and all of the object files should be listed as part of the macro OBJ.
做爲最終簡化版本,讓咱們用特殊的宏$@和$^,他們分別表明冒號(:)左側和右側,讓使整個編制規則更具備通用性。在下面的例子中,全部的.h文件應該被列爲宏DEPS的一部分,而且全部的目標文件的應列爲宏OBJ的一部分。
Makefile 4
1 CC=gcc
2 CFLAGS=-I.
3 DEPS = hellomake.h
4 OBJ = hellomake.o hellofunc.o
5
6 %.o: %.c $(DEPS)
7 $(CC) -c -o $@ $< $(CFLAGS)
8
9 hellomake: $(OBJ)
10 gcc -o $@ $^ $(CFLAGS)
So what if we want to start putting our .h files in an include directory, our source code in a src directory, and some local libraries in a lib directory? Also, can we somehow hide those annoying .o files that hang around all over the place? The answer, of course, is yes. The following makefile defines paths to the include and lib directories, and places the object files in an obj subdirectory within the src directory. It also has a macro defined for any libraries you want to include, such as the math library -lm. This makefile should be located in the src directory. Note that it also includes a rule for cleaning up your source and object directories if you type make clean. The .PHONY rule keeps make from doing something with a file named clean.
那麼,若是咱們要把咱們的.h文件放在include目錄,在src目錄下存放源代碼,並在lib目錄下存放了一些本地庫,該怎麼辦?此外,咱們能夠以某種方式隱藏那些煩人的.o文件?固然,答案是確定的。下面的makefile定義include和lib目錄路徑,並將OBJ做爲src目錄的子目錄,將目標文件存放於OBJ目錄。固然也定義了一個宏用於包含你想要的任何庫,如數學庫-lm。這個makefile應位於src目錄。請注意,這還包括一條規則,若是你輸入make clean會清理你的源代碼和目標目錄的規則。.PHONY規則防止make將clean識別爲一個文件而不是一條規則。
Makefile 5
1 IDIR =../include
2 CC=gcc
3 CFLAGS=-I$(IDIR)
4
5 ODIR=obj
6 LDIR =../lib
7
8 LIBS=-lm
9
10 _DEPS = hellomake.h
11 # 在$(patsubst %.c,%.o,$(dir) )中,patsubst把$(dir)中的變量符合後綴是.c的所有替換成.o,
12 # 在$(patsubst %,$(IDIR)/%,$(_DEPS))中,patsubst把$(_DEPS)目標前面加入一個路徑前綴
13 DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
14
15 _OBJ = hellomake.o hellofunc.o
16 # 同上
17 OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
18
19
20 $(ODIR)/%.o: %.c $(DEPS)
21 $(CC) -c -o $@ $< $(CFLAGS)
22
23 hellomake: $(OBJ)
24 gcc -o $@ $^ $(CFLAGS) $(LIBS)
25
26 .PHONY: clean
27 clean:
28 rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~
So now you have a perfectly good makefile that you can modify to manage small and medium-sized software projects. You can add multiple rules to a makefile; you can even create rules that call other rules. For more information on makefiles and the make function, check out the GNU Make Manual, which will tell you more than you ever wanted to know (really).
因此,如今你有一個完美的makefile文件,您能夠修改管理小型、中型的軟件項目。您能夠添加多個規則到makefile文件;你甚至能夠建立規則來調用其餘規則。有關makefile文件的更多信息,以及功能,請查閱GNU Make使用手冊,它會告訴你,比你曾經想知道(真的)的更多 。