1. 程式人生 > >Linux 應用---make及makefile的編寫

Linux 應用---make及makefile的編寫

     Make 在我們做linux 開發中是必不可少的一部分,它在我們編寫大型專案工程檔案中起到非常大的作用。

     Make工程管理器也就是個“自動編譯管理器”,這裡的“自動”是指它能夠根據檔案時間戳自動發現更新過的檔案而減少編譯的工作量,同時,它通過讀入Makefile檔案的內容來執行大量的編譯工作。Make將只編譯改動的程式碼檔案,而不用完全編譯。

    而Makefile是Make讀入的唯一配置檔案,makefile關係到了整個工程的編譯規則。一個工程中的原始檔不計數,其按型別、功能、模組分別放在若干個目錄中,makefile定義了一系列的規則來指定,哪些檔案需要先編譯,哪些檔案需要後編譯,哪些檔案需要重新編譯,甚至於進行更復雜的功能操作,因為makefile就像一個Shell指令碼一樣,其中也可以執行作業系統的命令。

下面我們通過兩個個例項來學習makefile的編寫:

一、Makefile編寫的基本規則

    我在資料夾下有如下檔案

[email protected]:~/qiang/makefile/example1$ ls
buffer.h  defs.h  files.c  main.c  makefile  utils.c
[email protected]:~/qiang/makefile/example1$ 

這裡的程式碼就不展示了,我說一下檔案包含關係:files.c需要buffer.h及defs.h ,  main.c需要defs.h  ,utils.c需要defs.h。

我把makefile檔案內容展示出來:

OBJS = main.o files.o utils.o
CC = gcc
CFLAGS = -c

Test:$(OBJS)
	$(CC) -o Test $(OBJS)
main.o:main.c defs.h
	$(CC) $(CFLAGS) main.c
files.o:files.c buffer.h defs.h
	$(CC) $(CFLAGS) files.c
utils.o:utils.c  defs.h
	$(CC) $(CFLAGS) utils.c

.PHONY:clean
clean:
	rm -rf *.o Test
	@echo "Clean done!"

我們開始學習makefile編寫規範:Makefile的根本任務是根據規則生成目標檔案。

1、規則

一條規則包含三個:目標檔案,目標檔案依賴的檔案,更新(或生成)目標檔案的命令。

規則:               

<目標檔案>:<依賴檔案>

         <更新目標的命令>

注意:命令列前面必須是一個“TAB鍵”,否則會編譯錯誤!

Example      

hello.o: hello.c hello.h    
	gcc -c hello.c -o hello.o

目標hello.o 依賴於hello.c,hello.h. 生成hello.o的命令時是“gcc -c hello.c -o hello.o”

2、終極目標

makefile並不會更新所有規則中的目標,它只會更新終極目標以及終極目標依賴的目標。
預設情況下makefile的第一個目標是終極目標,而且大家約定俗成的總是將all作為第一個目標。環境變數MAKECMDGOALS記錄著終極目標。

Test:$(OBJS)
	$(CC) -o Test $(OBJS)

我們這裡就是終極目標

注意:終極目標必須放在第一個,其餘的可以隨便放!

3、多規則目標

Makefile中,一個檔案可以作為多個規則的目標,這種情形就是多規則目標。
多規則目標下,以這個檔案為目標的所有規則的依賴檔案將會被合併成此一個依賴檔案列表,但是命令不會合並,而且實際上,這多個規則中至多隻能有一個規則定義了更新命令。

all:hello.o            

all:hello.h            

等價於  all: hello.o hello.h

main.o:main.c defs.h

我們這裡就是多規則目標;

4、偽目標
一般情況下目標檔案是一個具體的檔案,但有時候我們只需要一個標籤,如目標clean。

宣告偽目標:   

.PHONY:  <偽目標>

偽目標只是一個標籤,這意味著偽目標的時間戳總是最新的,結果就是makefile每次都會去執行更新偽目標的命令。

.PHONY:clean
clean:
	rm -rf *.o Test
	@echo "Clean done!"

我們這裡clean就是個偽目標,我們可以看到偽目標是沒有依賴檔案的,只有用make來呼叫時,才會執行。

5、什麼時候更新目標

如果目標不存在或者依賴檔案中至少有一個檔案的時間戳比目標新,則執行目標更新命令。

我們這裡,如果main.c 或者files被修改時,都會更新目標。

6、建立和使用變數

1)定義變數

makefile的變數定義有四種方法:

1. 立即賦值 a:=b
2. 延遲賦值 a=b
3. 條件賦值 a?=b
4. 附加賦值 a+=b

它們之間的區別是:

第一種方式,會立即計算b的值,並賦值給a;
第二種方式,相當於C++和java的引用。如果後面b的值改變了,那麼a的值也會改變;
第三種方式,如果a沒有定義,則相當於a=b ,否則不執行任何操作;
第四種方式,將b的值新增到a原有的值後面,再賦值給a。

2)獲取變數值

$(var) //表示取變數var的值,記得當變數名多於一個字元時,使用”()”.

OBJS = main.o files.o utils.o
Test:$(OBJS)
	$(CC) -o Test $(OBJS)

這裡我們可以看到變數的建立與使用。

3)預定義變數

  CC  :C編譯器的名稱,預設值為cc 。CPP  C預編譯器的名稱,預設值為$(CC) -E。

CC = gcc
Test:$(OBJS)
	$(CC) -o Test $(OBJS)

這裡我們可以看到預定義變數CC的使用。

其他的預定義變數:

ARFLAGS  庫檔案維護程式的選項,無預設值。

ASFALGS  彙編程式的選項,無預設值。

CFALGS    C編譯器的選項,無預設值。
....

CFLAGS = -c
main.o:main.c defs.h
	$(CC) $(CFLAGS) main.c

這裡我們可以看到CFLAGS的使用。

4)自動變數

$* 不包含副檔名的目標檔名稱

$< 第一個依賴檔案的名稱

$@ 目標檔案的完整名稱

$^ 所有不重複的目標依賴檔案,以空格分開

...

    變數的使用有助於我們修改makefile,大大加快了我們的開發效率。

7、回顯問題

我們知道,makefile中的命令列都會被打印出來的,如果遇到我們不想列印的命令怎麼辦?

@echo "Clean done!"

在命令列前面加 @ ,就是不回顯的意思,這樣"echo "Clean done!" "就不會列印。

我們來看看編譯效果:

[email protected]:~/qiang/makefile/example1$ ls
buffer.h  defs.h  files.c  main.c  makefile  utils.c
[email protected]:~/qiang/makefile/example1$ make
gcc -c main.c
gcc -c files.c
gcc -c utils.c
gcc -o Test main.o files.o utils.o
[email protected]:~/qiang/makefile/example1$ ls
buffer.h  files.c  main.c  makefile  utils.c
defs.h    files.o  main.o  Test      utils.o
[email protected]:~/qiang/makefile/example1$ make clean
rm -rf *.o Test
Clean done!
[email protected]:~/qiang/makefile/example1$ ls
buffer.h  defs.h  files.c  main.c  makefile  utils.c
[email protected]:~/qiang/makefile/example1$ 


二、巢狀執行Makefile

     在一些大的工程中,我們會把我們不同模組或是不同功能的原始檔放在不同的目錄中,我們可以在每個目錄中都書寫一個該目錄的Makefile,這有利於讓我們的Makefile變得更加地簡潔,而不至於把所有的東西全部寫在一個Makefile中,這樣會很難維護我們的Makefile,這個技術對於我們模組編譯和分段編譯有著非常大的好處。

例如,我們有一個子目錄叫subdir,這個目錄下有個Makefile檔案,來指明瞭這個目錄下檔案的編譯規則。那麼我們總控的Makefile可以這樣書寫:

    subsystem:
            cd subdir && $(MAKE)

其等價於:

    subsystem:
            $(MAKE) -C subdir

定義$(MAKE)巨集變數的意思是,也許我們的make需要一些引數,所以定義成一個變數比較利於維護。這兩個例子的意思都是先進入“subdir”目錄,然後執行make命令。

我們把這個Makefile叫做“總控Makefile”,總控Makefile的變數可以傳遞到下級的Makefile中(如果你顯示的宣告),但是不會覆蓋下層的Makefile中所定義的變數,除非指定了“-e”引數。

如果你要傳遞變數到下級Makefile中,那麼你可以使用這樣的宣告:

    export ;

如果你不想讓某些變數傳遞到下級Makefile中,那麼你可以這樣宣告: 

    unexport ;

如:
    
    示例一:

        export variable = value

        其等價於:

        variable = value
        export variable

        其等價於:

        export variable := value

        其等價於:

        variable := value
        export variable

    示例二:

        export variable += value

        其等價於:

        variable += value
        export variable

如果你要傳遞所有的變數,那麼,只要一個export就行了。後面什麼也不用跟,表示傳遞所有的變數。

需要注意的是,有兩個變數,一個是SHELL,一個是MAKEFLAGS,這兩個變數不管你是否export,其總是要傳遞到下層Makefile中,特別是MAKEFILES變數,其中包含了make的引數資訊,如果我們執行“總控Makefile”時有make引數或是在上層Makefile中定義了這個變數,那麼MAKEFILES變數將會是這些引數,並會傳遞到下層Makefile中,這是一個系統級的環境變數。

但是make命令中的有幾個引數並不往下傳遞,它們是“-C”,“-f”,“-h”“-o”和“-W”(有關Makefile引數的細節將在後面說明),如果你不想往下層傳遞引數,那麼,你可以這樣來:

    subsystem:
            cd subdir && $(MAKE) MAKEFLAGS=

如果你定義了環境變數MAKEFLAGS,那麼你得確信其中的選項是大家都會用到的,如果其中有“-t”,“-n”,和“-q”引數,那麼將會有讓你意想不到的結果,或許會讓你異常地恐慌。

還有一個在“巢狀執行”中比較有用的引數,“-w”或是“--print-directory”會在make的過程中輸出一些資訊,讓你看到目前的工作目錄。比如,如果我們的下級make目錄是“/home/hchen/gnu/make”,如果我們使用“make -w”來執行,那麼當進入該目錄時,我們會看到:

    make: Entering directory `/home/hchen/gnu/make'.

而在完成下層make後離開目錄時,我們會看到:

    make: Leaving directory `/home/hchen/gnu/make'

當你使用“-C”引數來指定make下層Makefile時,“-w”會被自動開啟的。如果引數中有“-s”(“--slient”)或是“--no-print-directory”,那麼,“-w”總是失效的。

頭的特殊變數,我們會在後面介紹),make在執行命令包時,命令包中的每個命令會被依次獨立執行。

下面我們做一個實驗,學習巢狀執行Makefile編寫的過程:

1、建立頂層目錄

[email protected]:~/qiang/makefile$ mkdir makefileTest
[email protected]:~/qiang/makefile$ cd makefileTest/
[email protected]:~/qiang/makefile/makefileTest$ mkdir f1 f2 main obj include
[email protected]:~/qiang/makefile/makefileTest$ ls
f1  f2  include  main  obj
[email protected]untu:~/qiang/makefile/makefileTest$ 

在include資料夾中建立一個共用標頭檔案,在其中輸入#include <stdio.h>

[email protected]:~/qiang/makefile/makefileTest$ cd include/
[email protected]:~/qiang/makefile/makefileTest/include$ vi myinclude.h
[email protected]:~/qiang/makefile/makefileTest/include$ cat myinclude.h 
#include <stdio.h>
[email protected]:~/qiang/makefile/makefileTest/include$ 

2、建立頂層Makefile檔案

[email protected]:~/qiang/makefile/makefileTest$ vi Makefile

內容如下:

CC = gcc                                                                                                         
SUBDIRS = f1 \
	 f2 \
	 main \
	 obj  //上面三個'\'後面不能有空格
OBJS = f1.o f2.o main.o
BIN = myapp
OBJS_DIR = obj
BIN_DIR = bin
export CC OBJS BIN OBJS_DIR BIN_DIR    //匯出環境變數,傳遞到下級目錄

all:CHECK_DIR $(SUBDIRS)
CHECK_DIR:
	mkdir -p $(BIN_DIR)
$(SUBDIRS):ECHO
	make -C [email protected] //先進入到SUBDIRS下的目錄,再執行make

ECHO:
	@echo $(SUBDIRS)
	@echo begin compile

CLEAN:
	@$(RM) $(OBJS_DIR)/*.o
	@rm -rf $(BIN_DIR)

 3、進入在f1目錄下建立makefile

[email protected]:~/qiang/makefile/makefileTest$ cd f1
[email protected]:~/qiang/makefile/makefileTest/f1$ vi f1.c

內容如下:

#include "../include/myinclude.h"                                                                              

void print1()  
{  
	printf("Message f1.c\n");  
	return;  
} 
[email protected]:~/qiang/makefile/makefileTest/f1$ vi Makefile

內容如下:

../$(OBJS_DIR)/f1.o:f1.c                                                                                       
	$(CC) -c $^ -o [email protected]  

 4、進入f2目錄

[email protected]:~/qiang/makefile/makefileTest/f1$ cd ../f2
[email protected]:~/qiang/makefile/makefileTest/f2$ vi f2.c

內容如下:

#include "../include/myinclude.h"                                                                              

void print2()  
{  
	printf("Message f2.c\n");  
	return;  
} 
[email protected]:~/qiang/makefile/makefileTest/f2$ vi makefile

內容如下:

../$(OBJS_DIR)/f2.o:f2.c                                                                                       
	$(CC) -c $^ -o [email protected] 

 5、進入main目錄

<a target=_blank href="mailto:[email protected]:~/qiang/makefile/makefileTest/f2$"><span style="color:#000000;">[email protected]:~/qiang/makefile/makefileTest/f2$</span></a> cd ../main
[email protected]:~/qiang/makefile/makefileTest/main$ vi main.c

內容如下:

#include <stdio.h>                                                                                             

int main()  
{  
	print1();  
	print2();  

	return 0; 
}  
[email protected]:~/qiang/makefile/makefileTest/main$ vi Makefile

內容如下:

../$(OBJS_DIR)/main.o:main.c                                                                                   
	$(CC) -c $^ -o [email protected]  

6、進入obj目錄

[email protected]:~/qiang/makefile/makefileTest$ cd obj
[email protected]:~/qiang/makefile/makefileTest/obj$ ls
[email protected]:~/qiang/makefile/makefileTest/obj$ vi Makefile

內容如下:

../$(BIN_DIR)/$(BIN) : $(OBJS)
	$(CC) -o [email protected] $^

這樣我們總體架構就完成了,我們看一下樹狀圖:

我們執行一下:

[email protected]:~/qiang/makefile/makefileTest$ make
Makefile:3: *** commands commence before first target.  Stop.

makefile時常遇到這樣的問題,彙總網上的原因如下:
1. 上一行換行符號 \ 後面有空格
2. 本行前面的空白有非法字元
1)Makefile可能是以命令列開始:以[Tab]字元開始,但不是一個合法的命令列(例如,一個變數的賦值)。命令列必須和規則一一對應。
2)第二種原因可能是一行的第一個非空字元為分號,make會認為此處遺漏了規則的“target: prerequisite”部分。

這兒我們的原因是第一個

改正後編譯:

[email protected]:~/qiang/makefile/makefileTest$ make
mkdir -p bin
f1 f2 main obj
begin compile
make -C f1
make[1]: Entering directory `/home/fs/qiang/makefile/makefileTest/f1'
gcc -c f1.c -o ../obj/f1.o
make[1]: Leaving directory `/home/fs/qiang/makefile/makefileTest/f1'
make -C f2
make[1]: Entering directory `/home/fs/qiang/makefile/makefileTest/f2'
gcc -c f2.c -o ../obj/f2.o 
make[1]: Leaving directory `/home/fs/qiang/makefile/makefileTest/f2'
make -C main
make[1]: Entering directory `/home/fs/qiang/makefile/makefileTest/main'
gcc -c main.c -o ../obj/main.o  
make[1]: Leaving directory `/home/fs/qiang/makefile/makefileTest/main'
make -C obj
make[1]: Entering directory `/home/fs/qiang/makefile/makefileTest/obj'
gcc -o ../bin/myapp f1.o f2.o main.o
make[1]: Leaving directory `/home/fs/qiang/makefile/makefileTest/obj'

執行一下:

[email protected]:~/qiang/makefile/makefileTest$ cd bin/
[email protected]:~/qiang/makefile/makefileTest/bin$ ls
myapp
[email protected]:~/qiang/makefile/makefileTest/bin$ ./myapp
Message f1.c
Message f2.c
[email protected]:~/qiang/makefile/makefileTest/bin$ 

相關推薦

Linux 應用---makemakefile編寫

     Make 在我們做linux 開發中是必不可少的一部分,它在我們編寫大型專案工程檔案中起到非常大的作用。      Make工程管理器也就是個“自動編譯管理器”,這裡的“自動”是指它能夠根據檔案時間戳自動發現更新過的檔案而減少編譯的工作量,同時,它通過讀入Make

Linuxmakemakefile的使用方法

◊make是什麼?   make是一個命令工具,是一個解釋makefile中指令的命令工具。它可以簡化編譯過程裡面所下達的指令,當執行 make 時,make 會在當前的目錄下搜尋 Makefile (or makefile) 這個文字檔案,執行對應的操作。make

linuxmakemakefile

make是一個命令,Makefile是一個檔案(可大寫也可以小寫) Makefile裡面放的是依賴關係和依賴方法 新建一個makefile檔案,在Makefile裡寫: hello:hello.c gcc -o hello hello.c      

如何將多個C檔案連結在一起----Makefile編寫make指令

需使用GCC編譯器,關於MinGW的安裝指南:https://people.eng.unimelb.edu.au/ammoffat/teaching/20005/Install-MinGW.pdf   單個.c檔案且沒有使用自定義標頭檔案的編譯,在命令列:   &nbs

linux動態庫靜態庫的製作 和makefile 的簡單編寫

一.庫什麼是庫,簡單的可以說是可執行程式碼的二進位制形式,能夠被作業系統載入記憶體執行。作業系統的不同,二者的庫也是不相容的,如windows與linux.庫又分為靜態庫和動態庫,動態庫又稱為共享庫。linux下靜態庫(.a)檔案,動態庫(.so)檔案。主要存放函式庫的路徑有

Linux下C語言編譯基礎makefile編寫

這篇文章介紹在LINUX下進行C語言程式設計所需要的基礎知識。在這篇文章當中,我們將會學到以下內容: 源程式編譯 Makefile的編寫 程式庫的連結 程式的除錯 標頭檔案和系統求助 1.源程式的編譯 在Linux下面,如果要編譯一個C語言源程式,我們要使用GNU的gc

Linux 作業系統 C 語言程式設計入門之編譯除錯說明Makefile編寫

1.C語言編譯和除錯說明: sourefile: main.c 編譯:將原始檔編譯成目標檔案--->gcc -c main.c 將目標檔案編譯成可執行檔案--->gcc -o main m

linux應用之xampp集成環境的安裝配置(centos)

其他 配置文件 ln -s 提示 config 執行權 listen location x64 1.xampp集成環境的下載   在xampp的官網上選擇對應系統的版本進行下載,官網地址:https://www.apachefriends.org/zh_cn/index.h

【2017-07-01】Linux應用開發工程師面試問題記錄之二:關於結構體的大小內存對齊問題

偶數 而且 strong span net 但是 開發 f11 flag Tencent後臺服務器開發有一道題是計算一個結構體的sizeof的大小: struct strData { int m_Int; char m_Char; short m_Short; char

Linux系統安全應用

linux 賬號安全 nmap 弱口令檢測 楊書凡 作為一個開放源代碼的操作系統,Linux服務器以其安全、高效和穩定的顯著優勢而得以廣泛應用。下面主要從賬戶安全、系統引導、登錄控制的角度,優化Linux系統的安全性賬號安全控制 用戶賬號,是計算機使用者的身份憑證,每個訪問系

Linux——makefile編寫

另一個 arch 運用 大量 技術 公司 的人 16px sdn 以前對makefile的編寫,限於剛開始接觸,我都比較局限一些死板的格式,有時候就會顯得有些繁瑣。在進一步了解一些系統編譯和鏈接的知識後,對makefile編寫流程有了一些新的認識,所以來此梳理梳理,方便更靈

新手學習Linux——NFS應用場景環境搭建

log 創建 watermark 51cto 輸入 ado dir ins 共享目錄 以上為解釋神圖。 ———————————————————分割線———————————————————一、接下來我們的操作在服務端進行。1、首先準備兩臺配置好IP地址和yum源的虛擬機。 2

Linux下多資料夾編寫Makefile詳解

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Linux 核心分析應用

編輯推薦 本書分模組介紹了 Linux 作業系統的核心設計和實現,針對關鍵概念、演算法和資料結構做了重點的講解。同時,對諸多經典應用程式進行了剖析,如 Nginx、Memcached、Redis、LVS 等,講解如何利用作業系統提供的底層支援進行合理的應用設計和實現。 內容簡介 本書由架構師親

Linux應用程式開發筆記:make menuconfig環境搭建

1、目的 Linux應用程式開發採用與Linux核心一致的menuconfig圖形配置,方便功能元件裁剪。   2、準備工作 下載:Kconfiglib原始碼(https://github.com/ulfalizer/Kconfiglib)   3、環境搭

嵌入式Linux應用開發① | 嵌入式LInux介紹開發環境搭建

1.嵌入式Linux 嵌入式Linux是對執行在嵌入式裝置上的Linux的統稱,它們都是經過高度裁剪、具備特定功能的嵌入式Linux作業系統。 嵌入式裝置效能、資源有限,所以不能將標準的Linux應用到嵌入式和裝置上,所以對標準的Linux進行核心裁剪,針對某一個特定的功能進行完善,就可

Linux中的靜態庫、共享庫、標頭檔案makefile檔案舉例

2、在bill.c檔案中引入標頭檔案,寫該函式的實現; ***@ubuntu:~/codeC/1201B$ more bill.c #include "bill.h" int bill(int a[], int n) {     int i,max = a[i];     for( i = 0; i <

linux字元裝置驅動開發模板Makefile

#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> //

Linux---make小程式進度條

make 執行 Makefile 一個Makefile主要含有一系列的規則: 目標:依賴檔案 tab 命令 main.o: main.c gcc -c -g main.c -o main.o -Wall 輸出所有警告資訊 -O 在編

Linuxmake的工作原理和makefile檔案

1、make會在當前目錄下找名字叫“Makefile”或“makefile”的檔案。2、如果找到,它會找檔案中的第一個目標檔案(target),在上面的例子中,他會找到“edit”這個檔案,並把這個檔案作為最終的目標檔案。3、如果edit檔案不存在,或是edit所依賴的後面的 .o 檔案的檔案修改時間要比ed