1. 程式人生 > >工程管理之makefile與自動建立makefile檔案過程

工程管理之makefile與自動建立makefile檔案過程

(風雪之隅 http://www.laruence.com/2009/11/18/1154.html)

Linux Makefile自動編譯和連結使用的環境

想知道到Linux Makefile系統的真相麼,想知道Linux Makefile系統中藏有的內在奧義麼,只有我來給大家全面講解介紹Linux Makefile系統作為Linux下的程式開發人員,大家一定都遇到過Linux Makefile,用make命令來編譯自己寫的程式確實是很方便。一般情況下,大家都是手工寫一個簡單Linux Makefile,如果要想寫出一個符合自由軟體慣例的Linux Makefile就不那麼容易了。

在本文中,將給大家介紹如何使用autoconf和automake兩個工具來幫助我們自動地生成符合自由軟體慣例的Linux Makefile,這樣就可以象常見的GNU程式一樣,只要使用“./configure”,“make”,“make instal”就可以把程式安裝到Linux系統中去了。這將特別適合想做開放原始碼軟體的程式開發人員,又或如果你只是自己寫些小的Toy程式,那麼這個文章對你也會有很大的幫助。

一、Linux Makefile介紹

Linux Makefile是用於自動編譯和連結的,一個工程有很多檔案組成,每一個檔案的改變都會導致工程的重新連結,但是不是所有的檔案都需要重新編譯,Linux Makefile中紀錄有檔案的資訊,在Linux Makefile時會決定在連結的時候需要重新編譯哪些檔案。

Linux Makefile的宗旨就是:讓編譯器知道要編譯一個檔案需要依賴其他的哪些檔案。當那些依賴檔案有了改變,編譯器會自動的發現最終的生成檔案已經過時,而重新編譯相應的模組。

Linux Makefile的基本結構不是很複雜,但當一個程式開發人員開始寫Linux Makefile時,經常會懷疑自己寫的是否符合慣例,而且自己寫的 Linux Makefile經常和自己的開發環境相關聯,當系統環境變數或路徑發生了變化後,Linux Makefile可能還要跟著修改。這樣就造成了手工書寫 Linux Makefile的諸多問題,automake恰好能很好地幫助我們解決這些問題。

使用automake,程式開發人員只需要寫一些簡單的含有預定義巨集的檔案,由autoconf根據一個巨集檔案生成configure,由automake根據另一個巨集檔案生成Linux Makefile.in,再使用configure依據Linux Makefile.in來生成一個符合慣例的Linux Makefile。下面我們將詳細介紹Linux Makefile的automake生成方法。

二、使用的環境

本文所提到的程式是基於Linux發行版本:Fedora Core release 1,它包含了我們要用到的autoconf,automake。

三、從helloworld入手

我們從大家最常使用的例子程式helloworld開始。下面的過程如果簡單地說來就是:新建三個檔案:

  1. helloworld.c  
  2. configure.in  
  3. Linux Makefile.am 

然後執行:

aclocal; autoconf; automake --add-missing; ./configure; make; ./helloworld

就可以看到Linux Makefile被產生出來,而且可以將helloworld.c編譯通過。很簡單吧,幾條命令就可以做出一個符合慣例的Linux Makefile,感覺如何呀。現在開始介紹詳細的過程:

1、建目錄

在你的工作目錄下建一個helloworld目錄,我們用它來存放helloworld程式及相關檔案,如在/home/my/build下:

$ mkdir helloword
   $ cd helloworld

2、 helloworld.c

然後用你自己最喜歡的編輯器寫一個hellowrold.c檔案,如命令:vi helloworld.c。使用下面的程式碼作為helloworld.c的內容。

  1. int main(int argc, char** argv)  
  2. {  
  3. printf("Hello, Linux World!/n");  
  4. return 0;  
  5. }  

完成後儲存退出。現在在helloworld目錄下就應該有一個你自己寫的helloworld.c了。

3、生成configure

我們使用autoscan命令來幫助我們根據目錄下的原始碼生成一個configure.in的模板檔案。

命令:

  1. $ autoscan  
  2. $ ls  
  3. configure.scan helloworld.c  

執行後在hellowrold目錄下會生成一個檔案:configure.scan,我們可以拿它作為configure.in的藍本。現在將configure.scan改名為configure.in,並且編輯它,按下面的內容修改,去掉無關的語句:

configure.in內容開始
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_INIT(helloworld.c)
AM_INIT_AUTOMAKE(helloworld, 1.0)
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_OUTPUT(Linux Makefile)
configure.in內容結束

然後執行命令aclocal和autoconf,分別會產生aclocal.m4及configure兩個檔案:

  1. $ aclocal   
  2. $ls   
  3. aclocal.m4 configure.in helloworld.c   
  4. $ autoconf   
  5. $ ls   
  6. aclocal.m4 autom4te.cache configure configure.in helloworld.c  


大家可以看到configure.in內容是一些巨集定義,這些巨集經autoconf處理後會變成檢查系統特性、環境變數、軟體必須的引數的shell指令碼。autoconf 是用來生成自動配置軟體原始碼指令碼(configure)的工具。

configure指令碼能獨立於autoconf執行,且在執行的過程中,不需要使用者的干預。要生成configure檔案,你必須告訴autoconf如何找到你所用的巨集。方式是使用aclocal程式來生成你的aclocal.m4。aclocal根據configure.in檔案的內容,自動生成aclocal.m4檔案。

aclocal是一個perl 指令碼程式,它的定義是:“aclocal - create aclocal.m4 by scanning configure.ac”。autoconf從configure.in這個列舉編譯軟體時所需要各種引數的模板檔案中建立configure。autoconf需要GNU m4巨集處理器來處理aclocal.m4,生成configure指令碼。

m4是一個巨集處理器。將輸入拷貝到輸出,同時將巨集展開。巨集可以是內嵌的,也可以是使用者定義的。除了可以展開巨集,m4還有一些內建的函式,用來引用檔案,執行命令,整數運算,文字操作,迴圈等。m4既可以作為編譯器的前端,也可以單獨作為一個巨集處理器。

4、新建Linux Makefile.am

新建Linux Makefile.am檔案,命令:$ vi Linux Makefile.am 內容如下:

  1. AUTOMAKE_OPTIONS=foreign
  2. bin_PROGRAMS=helloworld
  3. helloworldhelloworld_SOURCES=helloworld.c  

automake會根據你寫的Linux Makefile.am來自動生成Linux Makefile.in。Linux Makefile.am中定義的巨集和目標,會指導automake生成指定的程式碼。例如,巨集bin_PROGRAMS將導致編譯和連線的目標被生成。

5、執行automake

命令:

  1. $ automake --add-missing  
  2. configure.in: installing `./install-sh'  
  3. configure.in: installing `./mkinstalldirs'  
  4. configure.in: installing `./missing'  
  5. Linux Makefile.am: installing `./depcomp'  

automake會根據Linux Makefile.am檔案產生一些檔案,包含最重要的Linux Makefile.in。

6、執行configure生成Linux Makefile

  1. $ ./configure   
  2. checking for a BSD-compatible install... /usr/bin/install -c  
  3. checking whether build environment is sane... yes  
  4. checking for gawk... gawk  
  5. checking whether make sets $(MAKE)... yes  
  6. checking for gcc... gcc  
  7. checking for C compiler default output... a.out  
  8. checking whether the C compiler works... yes  
  9. checking whether we are cross compiling... no  
  10. checking for suffix of executables...   
  11. checking for suffix of object files... o  
  12. checking whether we are using the GNU C compiler... yes  
  13. checking whether gcc accepts -g... yes  
  14. checking for gcc option to accept ANSI C... none needed  
  15. checking for style of include used by make... GNU  
  16. checking dependency style of gcc... gcc3  
  17. configure: creating ./config.status  
  18. config.status: creating Linux Makefile  
  19. config.status: executing depfiles commands  
  20. $ ls -l Linux Makefile  
  21. -rw-rw-r-- 1 yutao yutao 15035 Oct 15 10:40 Linux Makefile  

你可以看到,此時Linux Makefile已經產生出來了。

7、使用Linux Makefile編譯程式碼

$ make if gcc -DPACKAGE_NAME="" -DPACKAGE_TARNAME="" -DPACKAGE_VERSION="" -DPACKAGE_STRING="" -DPACKAGE_BUGREPORT="" -DPACKAGE="helloworld" -DVERSION="1.0" 
-I. -I. -g -O2 -MT helloworld.o -MD -MP -MF ".deps/helloworld.Tpo" /-c -o helloworld.o `test -f 'helloworld.c' || echo './'`helloworld.c; /then mv -f ".deps/helloworld.Tpo" ".deps/helloworld.Po"; /else rm -f ".deps/helloworld.Tpo"; exit 1; /figcc -g -O2 -o helloworld helloworld.o  執行helloworld$ ./helloworld Hello, Linux World!

這樣helloworld就編譯出來了,你如果按上面的步驟來做的話,應該也會很容易地編譯出正確的helloworld檔案。你還可以試著使用一些其他的make命令,如make clean,make install,make dist,看看它們會給你什麼樣的效果。感覺如何?自己也能寫出這麼專業的Linux Makefile,老闆一定會對你刮目相看。

四、深入淺出

針對上面提到的各個命令,我們再做些詳細的介紹。

1、 autoscan

autoscan是用來掃描原始碼目錄生成configure.scan檔案的。autoscan可以用目錄名做為引數,但如果你不使用引數的話,那麼 autoscan將認為使用的是當前目錄。autoscan將掃描你所指定目錄中的原始檔,並建立configure.scan檔案。

2、 configure.scan

configure.scan包含了系統配置的基本選項,裡面都是一些巨集定義。我們需要將它改名為configure.in

3、 aclocal

aclocal是一個perl 指令碼程式。aclocal根據configure.in檔案的內容,自動生成aclocal.m4檔案。aclocal的定義是:“aclocal - create aclocal.m4 by scanning configure.ac”。

4、 autoconf

autoconf是用來產生configure檔案的。configure是一個指令碼,它能設定源程式來適應各種不同的作業系統平臺,並且根據不同的系統來產生合適的Linux Makefile,從而可以使你的原始碼能在不同的作業系統平臺上被編譯出來。

configure.in檔案的內容是一些巨集,這些巨集經過autoconf 處理後會變成檢查系統特性、環境變數、軟體必須的引數的shell指令碼。configure.in檔案中的巨集的順序並沒有規定,但是你必須在所有巨集的最前面和最後面分別加上AC_INIT巨集和AC_OUTPUT巨集。

在configure.ini中:#號表示註釋,這個巨集後面的內容將被忽略。AC_INIT(FILE) 這個巨集用來檢查原始碼所在的路徑。AM_INIT_AUTOMAKE(PACKAGE, VERSION) 這個巨集是必須的,它描述了我們將要生成的軟體包的名字及其版本號:PACKAGE是軟體包的名字,VERSION是版本號。

當你使用make dist命令時,它會給你生成一個類似helloworld-1.0.tar.gz的軟體發行包,其中就有對應的軟體包的名字和版本號。AC_PROG_CC這個巨集將檢查系統所用的C編譯器。 AC_OUTPUT(FILE)這個巨集是我們要輸出的Linux Makefile的名字。

我們在使用automake時,實際上還需要用到其他的一些巨集,但我們可以用aclocal 來幫我們自動產生。執行aclocal後我們會得到aclocal.m4檔案。產生了configure.in和aclocal.m4 兩個巨集檔案後,我們就可以使用autoconf來產生configure檔案了。

5、 Linux Makefile.am

Linux Makefile.am是用來生成Linux Makefile.in的,需要你手工書寫。Linux Makefile.am中定義了一些內容:AUTOMAKE_OPTIONS 這個是automake的選項。在執行automake時,它會檢查目錄下是否存在標準GNU軟體包中應具備的各種檔案,例如AUTHORS、ChangeLog、NEWS等檔案。我們將其設定成foreign時,automake會改用一般軟體包的標準來檢查。

bin_PROGRAMS這個是指定我們所要產生的可執行檔案的檔名。如果你要產生多個可執行檔案,那麼在各個名字間用空格隔開。 helloworld_SOURCES 這個是指定產生“helloworld”時所需要的原始碼。

如果它用到了多個原始檔,那麼請使用空格符號將它們隔開。比如需要 helloworld.h,helloworld.c那麼請寫成helloworld_SOURCES= helloworld.h helloworld.c。如果你在bin_PROGRAMS定義了多個可執行檔案,則對應每個可執行檔案都要定義相對的filename_SOURCES。

6、 automake

我們使用automake --add-missing來產生Linux Makefile.in。選項--add-missing的定義是“add missing standard files to package”,它會讓automake加入一個標準的軟體包所必須的一些檔案。我們用automake產生出來的Linux Makefile.in檔案是符合GNU Linux Makefile慣例的,接下來我們只要執行configure這個shell 指令碼就可以產生合適的 Linux Makefile 檔案了。

7、 Linux Makefile

在符合GNU Makefiel慣例的Linux Makefile中,包含了一些基本的預先定義的操作:make根據Linux Makefile編譯原始碼,連線,生成目標檔案,可執行檔案。make clean清除上次的make命令所產生的object檔案(字尾為“.o”的檔案)及可執行檔案。

make install將編譯成功的可執行檔案安裝到系統目錄中,一般為/usr/local/bin目錄。make dist產生髮布軟體包檔案(即distribution package)。這個命令將會將可執行檔案及相關檔案打包成一個tar.gz壓縮的檔案用來作為釋出軟體的軟體包。它會在當前目錄下生成一個名字類似“PACKAGE-VERSION.tar.gz”的檔案。

PACKAGE和VERSION,是我們在configure.in中定義的AM_INIT_AUTOMAKE(PACKAGE, VERSION)。make distcheck生成釋出軟體包並對其進行測試檢查,以確定釋出包的正確性。這個操作將自動把壓縮包檔案解開,然後執行configure命令。

並且執行make,來確認編譯不出現錯誤,最後提示你軟體包已經準備好,可以釋出了。helloworld-1.0.tar.gz is ready for distributionmake distclean 類似make clean,但同時也將configure生成的檔案全部刪除掉,包括Linux Makefile。

五、結束語

通過上面的介紹,你應該可以很容易地生成一個你自己的符合GNU慣例的Linux Makefile檔案及對應的專案檔案。如果你想寫出更復雜的且符合慣例的Linux Makefile,你可以參考一些開放程式碼的專案中的configure.in和Linux Makefile.am檔案,比如:嵌入式資料庫sqlite,單元測試cppunit。

【推薦】

例解 Linux 下 make 命令(http://blog.csdn.net/hazir/article/details/18408007)

Linux 下 make 命令是系統管理員和程式設計師用的最頻繁的命令之一。管理員用它通過命令列來編譯和安裝很多開源的工具,程式設計師用它來管理他們大型複雜的專案編譯問題。本文我們將用一些例項來討論 make 命令背後的工作機制。

Make 如何工作的

對於不知道背後機理的人來說,make 命令像命令列引數一樣接收目標。這些目標通常存放在以 “Makefile” 來命名的特殊檔案中,同時檔案也包含與目標相對應的操作。更多資訊,閱讀關於 Makefiles 如何工作的系列文章。

當 make 命令第一次執行時,它掃描 Makefile 找到目標以及其依賴。如果這些依賴自身也是目標,繼續為這些依賴掃描 Makefile 建立其依賴關係,然後編譯它們。一旦主依賴編譯之後,然後就編譯主目標(這是通過 make 命令傳入的)。

現在,假設你對某個原始檔進行了修改,你再次執行 make 命令,它將只編譯與該原始檔相關的目標檔案,因此,編譯完最終的可執行檔案節省了大量的時間。

Make 命令例項

下面是本文所使用的測試環境:

OS —— Ubunut 13.04
Shell —— Bash 4.2.45
Application —— GNU Make 3.81

下面是工程的內容:

$ ls 
anotherTest.c Makefile test.c test.h

下面是 Makefile 的內容:

all: test 

test: test.o anotherTest.o 
    gcc -Wall test.o anotherTest.o -o test

test.o: test.c 
    gcc -c -Wall test.c 

anotherTest.o: anotherTest.c 
    gcc -c -Wall anotherTest.c 

clean: 
    rm -rf *.o test

現在我們來看 Linux 下一些 make 命令應用的例項:

1. 一個簡單的例子

為了編譯整個工程,你可以簡單的使用 make 或者在 make 命令後帶上目標 all

$ make 
gcc -c -Wall test.c 
gcc -c -Wall anotherTest.c 
gcc -Wall test.o anotherTest.o -o test

你能看到 make 命令第一次建立的依賴以及實際的目標。

如果你再次檢視目錄內容,裡面多了一些 .o 檔案和執行檔案:

$ ls 
anotherTest.c anotherTest.o Makefile test test.c test.h test.o

現在,假設你對 test.c 檔案做了一些修改,重新使用 make 編譯工程:

$ make 
gcc -c -Wall test.c 
gcc -Wall test.o anotherTest.o -o test

你可以看到只有 test.o 重新編譯了,然而另一個 Test.o 沒有重新編譯。

現在清理所有的目標檔案和可執行檔案 test,你可以使用目標 clean:

$ make clean
rm -rf *.o test

$ ls
anotherTest.c Makefile test.c test.h

你可以看到所有的 .o 檔案和執行檔案 test 都被刪除了。

2. 通過 -B 選項讓所有目標總是重新建立

到目前為止,你可能注意到 make 命令不會編譯那些自從上次編譯之後就沒有更改的檔案,但是,如果你想覆蓋 make 這種預設的行為,你可以使用 -B 選項。

下面是個例子:

$ make
make: Nothing to be done for `all’.

$ make -B
gcc -c -Wall test.c
gcc -c -Wall anotherTest.c
gcc -Wall test.o anotherTest.o -o test

你可以看到儘管 make 命令不會編譯任何檔案,然而 make -B 會強制編譯所有的目標檔案以及最終的執行檔案。

3. 使用 -d 選項列印除錯資訊

如果你想知道 make 執行時實際做了什麼,使用 -d 選項。

這是一個例子:

$ make -d | more
GNU Make 3.81
Copyright (C) 2006 Free S