原創博文,轉載請標明出處--周學偉http://www.cnblogs.com/zxouxuewei/編程
仔細研究咱們的以前Makefile發現,咱們還有改進的地方,就是此處:函數
target_bin : main.o debug.o ipc.o timer.o tools.o >---gcc -o target_bin main.o debug.o ipc.o timer.o tools.o
若是增長一個源文件xx.c的話,須要在兩處或多處增長xx.o文件。咱們可使用變量來解決這個問題。以前說過,Makefile的變量就像C語 言的宏同樣,使用時在其位置上直接展開。變量在聲明時賦予初值,在引用變量時須要給在變量名前加上「$」符號,但最好用小括號「()」或是大括號「{}」 把變量給包括起來。優化
默認目標target_bin也在多處出現了,該文件也可使用變量代替。spa
修改咱們的Makefile以下debug
SRC_OBJ = main.o debug.o ipc.o timer.o tools.o SRC_BIN = target_bin $(SRC_BIN) : $(SRC_OBJ) >---gcc -o $(SRC_BIN) $(SRC_OBJ) clean: >---rm $(SRC_OBJ) $(SRC_BIN)
這樣每次有新增的文件是隻須要在SRC_OBJ變量裏面增長一個文件便可。要修改最終目標的名字是能夠只修改變量SRC_BIN。調試
其實在以前還說過特殊變量:blog
$@,表示規則中的目標。ip
$<,表示規則中的第一個依賴文件。get
$?,表示規則中全部比目標新的條件,組成一個列表,以空格分隔。編譯器
$^,表示規則中的全部條件,組成一個列表,以空格分隔。
上一節咱們看到make -p有不少自定義的變量,好比CC。其中不少變量咱們能夠直接使用或修改其變量值或增長值。咱們的Makefile中可使用CC(默認值爲cc)、RM(默認值爲rm -f)。
因而可知咱們的Makefile還能夠進一步修改
SRC_OBJ = main.o debug.o ipc.o timer.o tools.o SRC_BIN = target_bin $(SRC_BIN) : $(SRC_OBJ) >---$(CC) -o $@ $^ clean: >---$(RM) $(SRC_OBJ) $(SRC_BIN)
這樣的Makefile編譯也是可用的。
可是這樣的Makefile仍是須要咱們手動添加文件,仍是不夠自動化,最好增刪文件都要修改Makefile。偉大的人類真是太懶了!!因而乎, 他們發明了一個函數wilcard(函數後面會講到),它能夠用來獲取指定目錄下的全部的.c文件列表。這樣的話咱們能夠自動獲取當前目錄下全部.c源文 件,而後經過其餘方法再獲得.o文件列表,這樣的話就不須要在每次增刪文件時去修改Makefile了。所謂其餘方法這裏給出兩種:
1. 使用patsubst函數。在$(patsubst %.c,%.o,$(dir) )中,patsubst把$(dir)中的變量符合後綴是.c的所有替換成.o。
2. 變量值的替換。 咱們能夠替換變量中的共有的部分,其格式是「$(var:a=b)」或「${var:a=b}」,其意思是,把變量「var」中全部以「a」字串「結尾」的「a」替換成「b」字串。
修改後的Makefile以下:
# SRC_OBJ = $(patsubst %.c, %.o, $(wildcard *.c)) SRC = $(wildcard *.c) SRC_OBJ = $(SRC:.c=.o) SRC_BIN = target_bin $(SRC_BIN) : $(SRC_OBJ) >---$(CC) -o $@ $^ clean: >---$(RM) $(SRC_OBJ) $(SRC_BIN)
其中# 後面的內容爲註釋。
這樣終於知足了那些懶人的想法了。可見在使用變量時,的確能夠是編譯變得更自動化。
其實變量的定義有三種運算符=、:=、?=、+=。
1. =運算符能夠讀取到後面定義的變量。好比:
VAR = $(VAR2) VAR2 = hello_make all: >---@echo =====$(VAR)=====
運行結果爲:
# =====hello_make===== #
可是這種定義可能會致使並不是咱們意願的事發生,並非很符合C語言的編程習慣。
2. :=運算符在遇到變量定義時當即展開
VAR := $(VAR2) VAR2 = hello_make all: >---@echo =====$(VAR)=====
運行結果爲:
# ========== #
3. ?=運算符在複製以前先作判斷變量是否已經存在。例如var1 ?= $(var2)的意思是:若是var1沒有定義過,那麼?=至關於=,若是var1先前已經定義了,則什麼也不作,不會給var從新賦值。
4. +=運算符是給變了追加值。若是變量尚未定義過就直接用+=賦值,那麼+=至關於=
如何使用這幾個運算符要看實際狀況,有時一個大的工程可能有許多Makefile組成,變量可能在多個Makefile中都在使用,這時可能使用+=比較好。使用:=有時可能比要好。
有時在編譯程序時,咱們須要編譯器給出警告,或加入調試信息,或告知編譯器優化可執行文件。編譯時C編譯器的選項CFLAGS使用的較多,默認沒有 提供值,咱們能夠給該變量賦值。有時咱們還須要使用連接器選項LFLAGS告訴連接器連接時須要的庫文件。可能咱們還須要給出包含頭文件的路徑,由於頭文 件極可能和源文件再也不同一目錄。因此,咱們今天的Makefile加上部分註釋又更新了:
# A commonMakefile for c programs, version 1.0 # Copyright (C)2014 shallnew \at 163 \dot com CFLAGS += -g -Wall-Werror -O2 CPPFLAGS += -I.-I./inc LDFLAGS +=-lpthread # SRC_OBJ =$(patsubst %.c, %.o, $(wildcard *.c)) SRC_FILES =$(wildcard *.c) SRC_OBJ =$(SRC_FILES:.c=.o) SRC_BIN =target_bin $(SRC_BIN) :$(SRC_OBJ) >---$(CC) -o $@$^ $(LDFLAGS) clean: >---$(RM)$(SRC_OBJ) $(SRC_BIN)
編譯:
# make cc -g -Wall-Werror -O2 -I. -I./inc -c -o debug.odebug.c cc -g -Wall-Werror -O2 -I. -I./inc -c -o ipc.oipc.c cc -g -Wall-Werror -O2 -I. -I./inc -c -o main.omain.c cc -g -Wall-Werror -O2 -I. -I./inc -c -o timer.otimer.c cc -g -Wall-Werror -O2 -I. -I./inc -c -o tools.otools.c cc -o target_bindebug.o ipc.o main.o timer.o tools.o -lpthread #
可見咱們的預編譯選項,編譯選項都用到了,以前咱們說過make的使用隱含規則自動推導:
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) –c
其中變量CFLAGS 和 CPPFLAGS均是咱們給出的,變量$(TARGET_ARCH)未給,因此在編譯輸出能夠看到-c前面有2個空,最先未給變量是有四個空。
目前給出的Makefile基本上能夠適用於那些源代碼所有在同一目錄下的簡單項目,而且基本上在增刪文件時不須要再去手動修改Makefile代 碼。在新的一個項目只須要把該Makefile拷貝到源代碼目錄下,再修改一下你須要編譯的可執行文件名稱以及你須要的編譯鏈接選項便可。
後面章節將會講到如何寫多目錄源代碼工程下的Makefile。
最後,今天的最終Makefile是這樣的:
# A commonMakefile for c programs, version 1.0 # Copyright (C)2014 shallnew \at 163 \dot com CFLAGS += -g -Wall-Werror -O2 CPPFLAGS += -I.-I./inc LDFLAGS +=-lpthread # SRC_OBJ =$(patsubst %.c, %.o, $(wildcard *.c)) SRC_FILES =$(wildcard *.c) SRC_OBJ =$(SRC_FILES:.c=.o) SRC_BIN =target_bin $(SRC_BIN) :$(SRC_OBJ) >---$(CC) -o $@$^ $(LDFLAGS) clean: >---$(RM)$(SRC_OBJ) $(SRC_BIN)