MAKEFILE的學習
Makefile/cmake/configure
重點學習Cmake
首先是簡單的MakeFile入門
1.1 簡單Makefile
范例1.1
all:
@echo "Hello all"
test:
@echo "Hello test"
運行結果如下

范例1.2
test:
@echo "hello test"
all:
@echo "hello all"
運行結果如下

范例1.3
all:test
@echo "hello all"
test:
@echoi "hello test"
運行結果如下:

我們通過上面3個簡單的例子主要就是要解決4個比較重點的地方
- 目標、依賴、命令
- all有什么意義
- all和test的順序
- 空格符號的影響
1.2 Makefile三要素

1.3 MakeFile工作

1.4.1 編譯程序

1.4.2 編譯程序-偽對象.PHONY

1.5.1 變量

- CC保存編譯器名
- RM用于指示刪除文件的命令
- EXE存放可執行文件名
- OBJS防止所有的目標文件名
1.5.2 自動變量

- \(@**用于表示一個規則中的目標。當我們的一個規則中有多個目標時,\)@所指是其中任何造成命令被運行的目標**
- $^則表示的是規則中的所有先擇條件
- $<表示的是規則中的第一個先決條件
1.5.3 自動變量-編譯

- wildcard是通配符函數,通過它可以得到我們所需的文件形式:$
- patsubst函數是用來進行字符串替換的,其形式為:$
1.5.4 依賴第三方庫

makefile中的函數
我們通過上面的1.5.3的展示的已經知道了一些函數的作用了,現在我們在介紹幾個函數的使用
addprefix 函數
appprefix函數就是用來給字符串中的每一個子串前加上一個前綴,其形式是:$(addprefix prefix, names...)
.PHONY: all
without_dir = foo.c bar.c main.o
with_dir := $( addprefix objs/, $(without_dir))
all:
@echo "你好"
@echo $(with_dir)
filter函數
filter函數用于從一個字符串中,根據模式得到滿足模式的字符串,其形式是:$(filter pattern...,text)
.PHONY: all
sources = foo.c bar.c baz.s ugh.h
sources:=$(filter %.c %.s,$(sources))
all:
@echo $(sources)
filter-out函數
filter-out 函數用于從一個字符串中根據模式濾除一部分字符串,其形式是:$(filter-out pattern....,text)
.PHONY: all
objects= main1.o foo.o main2.o bar.o
result = $(filter-out main%.o,$(objects))
all:
@echo $(result)
patsubst 函數
patsubst 函數是用來進行字符串替換的,其形式是:$(patsubst pattern,replacement,text)
.PHONY: all
mixed = foo.c bar.c main.o
objects:=$(patsubst %.c,%.o,$(mixed))
all:
@echo $(objects)
strip函數
strip函數用于去除變量中的多余的空格,其形式是:$(strip string)
.PHONY:all
original = foo.c bar.c
stripped := $(strip $(original))
all:
@echo "original=$(original)"
@echo "stripped=$(stripped)"
wildcard函數
wildcard是通配符函數,通過它可以得到我們所需的文件,這個函數如果我們在windows或是linux命令行中"*",其形式為:$(wildcard pattern)
.PHONY:all
SRCS = $(wildcard *.c)
all:
@echo $(SRCS)
makefile提高部分
我們做上面已經接觸并介紹了一些簡單的概念有關于MAKEfile的,下面我們就稍微弄一些比較難得MAKEFILE的例子,來進一步的完成我們的學習。
我們現在有一些需求:
- 講所有的目標文件放入源程序所在目錄的objs子目錄中。
- 將所有最終生成的可執行程序放入源程序所在目錄的exes子目錄中,
- 將引入用戶頭文件來模擬復雜項目的情形
創建一個文件夾
首先我們寫一下如何創建一個文件夾
.PHONY: all
MKDIR = mkdir
DIRS = objs exes
all:$(DIRS)
$(DIRS):
$(MKDIR) $@
但是這樣我們就會遇到這樣的一個問題,我們在第一個make時,由于OBJS和exes目錄都不存在,所以all目標將它們視作一個先決條件或者說是依賴目標,接著makefile中的第二條規則就被派上了用場。構建目錄時,第二條規則中的命令被執行,即真正的創建了objs和exes目錄,當我們第二次進行make時,此時,make仍以objs和exes為目標,但從目錄構建規則中發現,這兩個目標并沒有依賴關系,而且能從當前目錄中找到 objs 和 exes 目錄,即認為 objs 和 exes 目標都是最新的,所以不用再運行目錄構建規則中的命令來創建目錄。
接下來我們也得為我們的 Makefile 創建一個 clean 目標,專門用來刪除所生成的目標文件和可執行文件。加 clean 規則還是相當的直觀的,如圖 2.8 所示,其中我們又增加了兩個變量,一個是RM,另一個則是 RMFLAGS,這與 simple 項目中所使用的方法是一樣的
.PHONY: all
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
DIRS = objs exes
all:$(DIRS)
$(DIRS):
$(MKDIR) $@
clean:
$(RM) $(RMFLAGS) $(DIRS)
增加頭文件
我們增加進去我們需要編譯的部分。
foo.h
#ifndef __FOO_H
#define __FOO_H
void foo();
#endif
foo.c
#include<stdio.h>
#include"foo.h"
void foo()
{
printf("This is foo()!\n");
}
main.c
#include"foo.h"
int main()
{
foo();
return 0;
}
我們再來改造一下我們的Makefile
.PHONY: all
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
CC = gcc
EXE = complicated
DIRS = objs exes
SRCS = $(wildcard *.c)
OBJS =$(SRCS:.c=.o)
all:$(DIRS) $(EXE)
$(DIRS):
$(MKDIR) $@
$(EXE):$(OBJS)
$(CC) -o $@ $^
%.o:%.c
$(CC) -o $@ -c $^
clean:
$(RM) $(RMFLAGS) $(DIRS) $(EXE) $(OBJS)
我們運行結束以后,發現所有的目標文件以及可執行文件都放在當前目錄下,而并沒有如我們所希望那樣放在objs和exes目錄中去。
將文件放入目錄
為了將目標文件或者可執行程序分別放入所創建的objs和exes目錄中,我們就用我們之前學到過的函數---addprefix,現在我們修改一下我們的makefile文件,以便于目標文件都放入objs目錄當中,更改后的Makefile。
.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
CC = gcc
DIR_OBJS = objs
DIR_EXES = exes
DIRS = $(DIR_OBJS) $(DIR_EXES)
EXE = complicated
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
all: $(DIRS) $(EXE)
$(DIRS):
$(MKDIR) $@
$(EXE): $(OBJS)
$(CC) -o $@ $^
$(DIR_OBJS)/%.o: %.c
$(CC) -o $@ -c $^
clean:
$(RM) $(RMFLAGS) $(DIRS) $(EXE) $(OBJS)


我們已經修改了我們的MAKEfile文件,已經得到了我們想要的結果,但是生成的EXES文件還沒有在我們的EXES文件夾中,我們還需要對它進行一些細微的修改,完成我們的目的,修改如下:
.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
CC = gcc
DIR_OBJS = objs
DIR_EXES = exes
DIRS = $(DIR_OBJS) $(DIR_EXES)
EXE = complicated
EXE := $(addprefix $(DIR_EXES)/, $(EXE))
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
all: $(DIRS) $(EXE)
$(DIRS):
$(MKDIR) $@
$(EXE): $(OBJS)
$(CC) -o $@ $^
$(DIR_OBJS)/%.o: %.c
$(CC) -o $@ -c $^
clean:
$(RM) $(RMFLAGS) $(DIRS) $(EXE) $(OBJS)
更復雜的依賴關系
到了這里,好像我們的MAKEFILE已經達到了我們想要需求,但是這是真的嗎?我們看看現在還有什么樣的問題存在著。假設我們已經對項目進行了一次編譯,接著我們再去修改一下foo.h這個頭文件,增加了一個int型的參數,而不對foo.c進行相應的更改。這樣由于函數聲明和函數定義不相同,所以理論上編譯時應該報錯。
#ifndef __FOO_H
#define __FOO_H
void foo(int value);
#endif

為什么會出現這樣的情況那?那我們先make clean,然后再make會有什么結果那?的確是出錯了!那我們怎么樣才能在沒有進行make clean 之前,make就能發現需要對項目的部分(或者全部)進行重新構建那?如果我們這樣的makefile運用到現實項目中,那對于開發效率還是有影響的,因為每一次make之前都得進行clean,太費時!
我們來分析以后為什么現在的MakeFile會出現這一問題那?我們現有的Makefile所表達的依賴關系樹及與規則的映射關系圖。

從依賴圖中,我們可以發現,其中并沒有出現對foo.h的依賴關系,這就是為什么我們改動頭文件時,make無法發現的原因!
最為直接的改動是我們在構建目標文件的規則中,增加對于foo.h的依賴。改動后的makefile如下:
.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
CC = gcc
DIR_OBJS = objs
DIR_EXES = exes
DIRS = $(DIR_OBJS) $(DIR_EXES)
EXE = complicated
EXE := $(addprefix $(DIR_EXES)/, $(EXE))
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
all: $(DIRS) $(EXE)
$(DIRS):
$(MKDIR) $@
$(EXE): $(OBJS)
$(CC) -o $@ $^
$(DIR_OBJS)/%.o: %.c foo.h
$(CC) -o $@ -c $<
clean:
$(RM) $(RMFLAGS) $(DIRS) $(EXE) $(OBJS)
改動的部分并不是很多,需要指出的是,在這個makefile中我們使用了自動變量$<,在這個變量與$^的區別是,其只則表示所有的先決條件中的第一個,而$^則表示全部先決條件。我們要用$<是因為,我們不希望將foo.h也作為一個文件讓GCC去編譯,這樣的話會報錯。
這個方法暫時解決了我們遇到的問題,我們需要想一想這種解決方法的可操作性。當項目復雜時,如果我們要將每一個頭文件都寫入到makefile相對應的規則中,這將會是一個噩夢!看來我們還的找到另一種更好的方法。
有什么工具能幫助我們列出一個源程序所包含的頭文件那就好了,這樣的話,我們或許可以在make時,動態的生成文件的依賴關系。還真是有這么樣一個工具!就是我們的編譯器--gcc。我們可以采用-M選項和-MM選項列出foo.c對其他文件的依賴關系的結果,從結果你可以看出它們會列出foo.c中直接或是間接包含的頭文件。-MM 選項與-M 選項的區別是,-MM選項并不列出對于系統頭文件的依賴關系,比如 stdio.h 就屬于系統頭文件。其道理是,絕大多數情況我們并不會改變系統的頭文件,而只會對自己項目的頭文件進行更改。
執行

對于采用gcc的-MM的選項所生成的結果,我們從圖中就知道,我們會遇到的問題了,我們生成的目標文件是放在objs目錄當中的,因此,我們希望依賴關系中也包含這一目錄信息,否則,在我們的makefile中,根本沒有辦法做到生成的目標放到objs目錄中,這在前面的makefile中我們是在生成的文件前加前綴的方法。在使用新的方法是,我們仍然需要實現同樣的功能。這時,我們采用的就是sed工具了,這是linux中非常常用的一個字符串處理工具。示例如下:
gcc -MM foo.c | sed 's,\(.*\)\.o[ :]*,objs/\1.o: ,g'

Gcc還有一個非常有用的選項是-E,這個命令告訴Gcc只做預處理,而不進行程序編譯,在生成依賴關系是,其實我們并不需要GCC去編譯,只要進行預處理就行了。這可以避免在生成依賴關系時出現沒有必要的warning,以及提高依賴關系的生成效率。
接下來,我們要如何整合到我們的makefile中那?顯然,自動生成的依賴信息,不可能直接出現在我們的makefile中,因為我們不能動態的改變makefile的內容,那采用什么方法那?
- 我們為每一個源文件通過采用GCC和sed生成一個依賴關系文件,這些文件我們采用.dep后綴結尾。
- 從模塊化的角度來說,我們不希望.dep文件與.哦文件或者時可執行文件混放在一個目錄中。為此,創建一個新的deps目錄用于存放依賴文件更為合理。
MakeFile
.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
CC = gcc
DIR_OBJS = objs
DIR_EXES = exes
DIR_DEPS = deps
DIRS = $(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS)
EXE = complicated
EXE := $(addprefix $(DIR_EXES)/, $(EXE))
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
DEPS = $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))
all: $(DIRS) $(DEPS) $(EXE)
$(DIRS):
$(MKDIR) $@
$(EXE): $(OBJS)
$(CC) -o $@ $^
$(DIR_OBJS)/%.o: %.c
$(CC) -o $@ -c $^
$(DIR_DEPS)/%.dep: %.c
@echo "Making $@..."
@set -e; \
$(RM) $(RMFLAGS) $@.tmp;\
$(CC) -E -MM $^ > $@.tmp;\
sed 's,\(.*\)\.o[ :]*,objs/\1.o: ,g' < $@.tmp > $@ ;\
$(RM) $(RMFLAGS) $@.tmp
clean:
$(RM) $(RMFLAGS) $(DIRS) $(EXE) $(OBJS)
這里我們又有一個知識點需要注意,對于規則中的每一個命令,make都是在一個新的shell上運行它的,如果希望多個命令在同一個Shell中運行,則需要用';'將這些命令連起來。當命令很長時,為了方便閱讀,我們需要將一行命令分為多行,這需要用''。為了理解,我們可以做一個實驗,現在假設我們需要創建一個test目錄,然后,在這個test目錄再創建一個subtest目錄,你可能會寫出下面這樣的Makefile.
.PHONY:all
all:
@makedir test
@cd test
@makedir subtest
我們會得到這樣的目錄結構:

下面我們再來修改一下我們的makefile:
.PHONY: all
all:
@mkdir test ; \
cd test ; \
mkdir subtest
這樣就實現了我們的要求.
包含文件
我們現在已經產生了我們需要的依賴(dep)文件,那如何為我們的makefile所用呢?這需要用到Makefile中的include關鍵字,它如同C/C++的#include預處理指令。現在要做的就是在Makefile中加入對所有依賴文件的包含功能,
Makefile
.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
CC = gcc
DIR_OBJS = objs
DIR_EXES = exes
DIR_DEPS = deps
DIRS = $(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS)
EXE = complicated
EXE := $(addprefix $(DIR_EXES)/, $(EXE))
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
DEPS = $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))
all: $(DIRS) $(DEPS) $(EXE)
include $(DEPS)
$(DIRS):
$(MKDIR) $@
$(EXE): $(OBJS)
$(CC) -o $@ $^
$(DIR_OBJS)/%.o: %.c
$(CC) -o $@ -c $^
$(DIR_DEPS)/%.dep: %.c
@echo "Making $@..."
@set -e; \
$(RM) $(RMFLAGS) $@.tmp;\
$(CC) -E -MM $^ > $@.tmp;\
sed 's,\(.*\)\.o[ :]*,objs/\1.o: ,g' < $@.tmp > $@ ;\
$(RM) $(RMFLAGS) $@.tmp
clean:
$(RM) $(RMFLAGS) $(DIRS) $(EXE) $(OBJS)
我們現在運行一下看看結果怎么樣

發現了報錯了,找不到main.dep這個文件,我們怎么理解這個錯誤呢?我們的make對于INCLUde的處理是先于all目標構建的,這樣的話,由于依賴文件時在構建all目標時才創建的,所以在include的時候,是找不到依賴文件的。我們說第一次make的時候的確是沒有依賴文件,所以include出錯也是正常的,那么能不能讓make忽略這一錯誤呢?可以的,在Makefile中,如果在include前加上一個'-'號,當make處理這一包含指示時,如果文件不存在就會忽略這一錯誤。除此之外,需要對于Makefile中include有更加深入的理解。當make看到include指令時,會先找一下有沒有這個文件,如果有則讀入。接著,make還會看一看對于包含進來的文件,在Makefile中是否存在規則來更新它。如果存在,則運行規則去更新需被包含進來的文件,當更新完了以后再將其包含進來。在我們的MAKEfile中,的確存在用于創建(或更新)依賴的文件的規則,為什么make沒有幫助我們去創建依賴文件那?因為make想創建依賴文件時,deps目錄還沒有創建,所以無法成功的構建依賴文件。
有了這些信息以后,我們需要對Makefile的依賴關系進行調整,即將deps目錄的創建放在構建依賴文件之前。其改動就是在依賴文件的創建規則當中增加對deps目錄的信賴,且將其當作是第一個先決條件。采用同樣的方法,我們將所有的目錄創建都放到相應的規則中去。
.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
CC = gcc
DIR_OBJS = objs
DIR_EXES = exes
DIR_DEPS = deps
DIRS = $(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS)
EXE = complicated
EXE := $(addprefix $(DIR_EXES)/, $(EXE))
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
DEPS = $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))
all: $(EXE)
-include $(DEPS)
$(DIRS):
$(MKDIR) $@
$(EXE): $(DIR_EXES) $(OBJS)
$(CC) -o $@ $(filter %.o,$^)
$(DIR_OBJS)/%.o: $(DIR_OBJS) %.c
$(CC) -o $@ -c $(filter %.c,$^)
$(DIR_DEPS)/%.dep:$(DIR_DEPS) %.c
@echo "Making $@..."
@set -e; \
$(RM) $(RMFLAGS) $@.tmp;\
$(CC) -E -MM $(filter %.c,$^) > $@.tmp;\
sed 's,\(.*\)\.o[ :]*,objs/\1.o: ,g' < $@.tmp > $@ ;\
$(RM) $(RMFLAGS) $@.tmp
clean:
$(RM) $(RMFLAGS) $(DIRS)
再復雜一點的依賴關系
現在,我們再對源程序文件進行一定的修改,如圖 2.36 所示。其中的改動包括:
- 增加 define.h 文件并在其中定義一個 HELLO 宏。
- 在 foo.h 中包含 define.h 文件。
- 在 foo.c 中增加對 HELLO 宏的使用。
增加了這些改動以后,進行 make 操作
define.h
#ifndef __DEFINE_H
#define __DEFINE_H
#define HELLO “Hello”
#endif
foo.h
#ifndef __FOO_H
#define __FOO_H
#include “define.h”
void foo ();
#endif
foo.c
#include <stdio.h>
#include “foo.h”
void foo ()
{
printf (“%s, this is foo ()!\n”, HELLO);
}
main.c
#include “foo.h”
int main ()
{
foo ();
return 0;
}
然后我們編譯成功了以后,我們在進行一個操作
現在對 other.h 進行更改
other.h
#ifndef __OTHER_H
#define __OTHER_H
#define HELLO "Hi"
#endif
我們再進行make編譯,發現并沒有什么變化。
問題出來了,程序并沒有因為我們更改了 other.h 而重新編譯,問題出在哪呢?從 foo.dep 和main.dep 的內容來看,其中并沒有指出 foo.o 和 main.o 依賴于 other.h 文件,所以當我們進行 make時,make 程序沒有發現 foo.o 和 main.o 需要重新編譯。那如何解決呢?我們說,當我們進行 make時,如果此時 make 能發現 foo.dep 和 main.dep 需要重新生成的話,此時會發現 foo.o 和 main.o都依賴 other.h 文件,那自然就會發現 foo.o 和 main.o 也需要重新編譯。
也就是說我們也需要對依賴文件采用 foo.o 和 main.o 相類似的依賴規則,為此,我們希望在Makefile 中存在如圖 2.42 所示的依賴關系。如果存在這樣的依賴關系,當我們對 define.h 進行更改以增加對 other.h 文件的包含時,通過這個依賴關系 make 就能發現需要重新生成新的依賴文件,一旦重新生成依賴文件,other.h 也就自然會成為 foo.o 和 main.o 的一個先決條件。如果這樣的話,就不會出現前面所看到的依賴關系并不重新構建的問題了。
makefile
.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
CC = gcc
DIR_OBJS = objs
DIR_EXES = exes
DIR_DEPS = deps
DIRS = $(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS)
EXE = complicated
EXE := $(addprefix $(DIR_EXES)/, $(EXE))
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
DEPS = $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))
all: $(EXE)
-include $(DEPS)
$(DIRS):
$(MKDIR) $@
$(EXE): $(DIR_EXES) $(OBJS)
$(CC) -o $@ $(filter %.o, $^)
$(DIR_OBJS)/%.o: $(DIR_OBJS) %.c
$(CC) -o $@ -c $(filter %.c, $^)
$(DIR_DEPS)/%.dep: $(DIR_DEPS) %.c
@echo "Making $@ ..."
@set -e; \
$(RM) $(RMFLAGS) $@.tmp ; \
$(CC) -E -MM $(filter %.c, $^) > $@.tmp ; \
sed 's,\(.*\)\.o[ :]*,objs/\1.o $@: ,g' < $@.tmp > $@ ; \
$(RM) $(RMFLAGS) $@.tmp
clean:
$(RM) $(RMFLAGS) $(DIRS)
這個 Makefile 中你可以看出,我們只需在相應的規則命令中增加一個$@就行了,因為這個表示的是目標,即在創建 deps/foo.dep 時,其代表的就是 deps/foo.dep。
條件編譯
當 make 看到條件語法時將立即對其進行分析,這包括 ifdef、ifeq、ifndef 和 ifneq 四種語句形式。這也說明自動變量在這些語句塊中不能使用,因為自動變量的值是在命令處理階段才被賦值的。如果非得用條件語法,那得使用 Shell 所提供的條件語法而不是 Makefile 的。
Makefile 中的條件語法有三種形式。其中的 conditional-directive 可以是 ifdef、ifeq、ifndef 和 ifneq 中的任意一個。
conditional-directive
text-if-true
endif
或
conditional-directive
text-if-true
else
text-if-false
endif
或
conditional-directive
text-if-one-is-true
else conditional-directive
text-if-true
else
text-if-false
endif
makefile
.PHONY: all
sharp = square
desk = square
table = circle
ifeq ($(sharp), $(desk))
result1 = "desk == sharp"
endif
ifneq "$(table)" 'square'
result2 = "table != square"
endif
all:
@echo $(result1)
@echo $(result2)
makefile
.PHONY: all
foo = defined
ifdef foo
result1 = "foo is defined"
endif
ifndef bar
result2 = "bar is not defined"
endif
all:
@echo $(result1)
@echo $(result2)
當進行第二次 make clean 時,make 還會先構建依賴文件,接著再刪除,這是因為我們進行 make clean 也需要包含依賴文件的緣故。顯然,其中構建依賴文件的動作有點多余,因為后面馬上又被刪除了。為了去除在 make clean 時不必要的依賴文件構建,我們可以用條件語法來解決這一問題。
.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
CC = gcc
DIR_OBJS = objs
DIR_EXES = exes
DIR_DEPS = deps
DIRS = $(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS)
EXE = complicated
EXE := $(addprefix $(DIR_EXES)/, $(EXE))
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
DEPS = $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))
all: $(EXE)
ifneq ($(MAKECMDGOALS), clean)
-include $(DEPS)
endif
$(DIRS):
$(MKDIR) $@
$(EXE): $(DIR_EXES) $(OBJS)
$(CC) -o $@ $(filter %.o, $^)
$(DIR_OBJS)/%.o: $(DIR_OBJS) %.c
$(CC) -o $@ -c $(filter %.c, $^)
$(DIR_DEPS)/%.dep: $(DIR_DEPS) %.c
@echo "Making $@ ..."
@set -e; \
$(RM) $(RMFLAGS) $@.tmp ; \
$(CC) -E -MM $(filter %.c, $^) > $@.tmp ; \
sed 's,\(.*\)\.o[ :]*,objs/\1.o $@: ,g' < $@.tmp > $@ ; \
$(RM) $(RMFLAGS) $@.tmp
clean:
$(RM) $(RMFLAGS) $(DIRS)
推薦一個零聲學院免費教程,個人覺得老師講得不錯,
分享給大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,
TCP/IP,協程,DPDK等技術內容,點擊立即學習:
服務器
音視頻
dpdk
Linux內核

浙公網安備 33010602011771號