Makefile教程(一):由淺入深


本文參考文章:
Makefile由淺入深–教程、乾貨
Makefile文件是什麼?

1. Makefile文件是什麼?

Makefile 可以簡單的認爲是一個工程文件的編譯規則,描述了整個工程的編譯和鏈接等規則。其中包含了那些文件需要編譯,那些文件不需要編譯,那些文件需要先編譯,那些文件需要後編譯,那些文件需要重建等等。編譯整個工程需要涉及到的,我們在 Makefile 中都可以進行描述。換句話說,有了 Makefile 可以使得我們的項目工程的編譯變得自動化,不用每次都手動輸入一堆源文件和參數。

2. 一個簡單的Makefile demo

在這裏插入圖片描述
需要說明一下,文檔目錄結構是用戶目錄HOME下有src incl bin lib。

src:源碼

incl:頭文件

bin:執行碼

lib:靜態/動態庫

這是大家最常見的Linux編程目錄結構,以下代碼編譯都是依據這個結構。

Makefile代碼:

hello:hello.c
	gcc -I${HOME}/incl -c hello.c
	gcc -o hello hello.o
	rm -f hello.o
	mv hello ${HOME}/bin

沒接觸過Makefile的同學肯定能看出,這段代碼不就是把編譯、鏈接、刪除、移動寫成shell腳本執行嗎?沒錯的,把第一行去掉,其他代碼粘貼到shell腳本里同樣可以編譯成功,一點問題都沒有。

3. Makefile結構說明

Makefile裏主要包含了五個東西:變量定義、顯式規則、隱晦規則、文件指示和註釋。

1、變量的定義。在Makefile中我們要定義一系列的變量,變量一般都是字符串,這個有點像C語言中的宏,當Makefile被執行時,其中的變量都會被擴展到相應的引用位置上。

2、顯式規則。顯式規則說明了,如何生成一個或多的的目標文件。這是由Makefile的書寫者明顯指出,要生成的文件,文件的依賴文件,生成的命令。 剛纔寫的疑似shell腳本的Makefile全部都是顯示規則。

3、隱晦規則。由於我們的make有自動推導的功能,所以隱晦的規則可以讓我們比較粗糙地簡略地書寫Makefile,這是由make所支持的。

4、文件指示。其包括了三個部分,一個是在一個Makefile中引用另一個Makefile,就像C語言中的include一樣。

5、註釋。Makefile中只有行註釋,和UNIX的Shell腳本一樣,其註釋是用“#”字符,這個就像C/C++中的“//”一樣。如果你要在你的Makefile中使用“#”字符。

4. 複雜一些的Makefile

根據上面的結構說明,我們對Makefile一層一層的改寫,首先是隱晦規則,告訴大家其中一種用法:

.SUFFIXES: .cpp .c
.cpp.o:
	g++ ${INCL} -c $<

.c.o:
	gcc ${INCL} -c $<

這個隱晦規則其實就是告訴大家,後綴爲cpp的文件怎麼編譯成.o,後綴爲c的文件怎麼編譯成.o。
d
到目前爲止的Makefile已經有模有樣了,Makefile代碼如下。

#隱含規則
INCL=-I${HOME}/incl

.SUFFIXES: .cpp .c
.cpp.o:
	g++ ${INCL} -c $<

.c.o:
	gcc ${INCL} -c $<

#C++編譯
hellocpp:hellocpp.o
	echo "開始編譯"
	g++ -o hellocpp hellocpp.o
	rm -f hellocpp.o
	mv hellocpp ${HOME}/bin
	echo "編譯結束"

#C編譯
hello:hello.o
	echo "開始編譯"
	gcc -o hello hello.o
	rm -f hello.o
	mv hello ${HOME}/bin
	echo "編譯結束"

細心的同學會發現剛纔有個“$<”,如果看的有些蒙圈,那一定要了解預定義變量,下面這些是常用的預定義變量。

$*   不包含擴展名的目標文件名稱。

$+   所有的依賴文件,以空格分開,並以出現的先後爲序,可能包含重複的依賴文件。

$<   第一個依賴文件的名稱。

$?   所有的依賴文件,以空格分開,這些依賴文件的修改日期比目標的創建日期晚。

$@   目標的完整名稱。

$^   所有的依賴文件,以空格分開,不包含重複的依賴文件。

$% 如果目標是歸檔成員,則該變量表示目標的歸檔成員名稱。

Makefile的最終展現
在這裏插入圖片描述
實際編譯結果
在這裏插入圖片描述
看着挺亂的吧,稍微改一改

#最後形成的Makefile
INCL=-I${HOME}/incl
BIN=$(HOME)/bin
OBJ1=hellocpp.o
OBJ2=hello.o

.SUFFIXES: .cpp .c
.cpp.o:
	g++ ${INCL} -c $<

.c.o:
	gcc ${INCL} -c $<

all: hellocpp hello

#C++編譯
hellocpp:${OBJ1}
	@echo "============開始編譯============"
	g++ -o $@ $?
	@rm -f ${OBJ1}
	@mv $@ ${BIN}
	@echo "============編譯結束============"
	@echo ""

#C編譯
hello:${OBJ2}
	@echo "============開始編譯============"
	gcc -o $@ $?
	@rm -f ${OBJ2}
	@mv $@ ${BIN}
	@echo "============編譯結束============"
	@echo ""

命令前加@,表示當前命令不顯示,最後編譯結果是這樣。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章