makefile 完美教程
簡介
Makefile 是和 make 命令一起配合使用的,很多大型項目的編譯都是通過 Makefile 來組織的,。
我建立工程的方法有以下三點:
1.makefile:
優點:使用非常廣泛,通用性強,可跨平臺。
缺點:語法比較蛋疼。要寫出一個通用,便于管理,且兼容性強的makefile比較困難。
2.cmake:
優點:簡單易用,使用較廣泛,方便管理,可跨平臺。
缺點:自動生成的makefile太臃腫。
3.sh腳本:
優點:自由,高度定制。簡單易用,可操作性強。方便維護。(甚至還可以生成makefile)
缺點:sh建的工程太少了(估計沒人這么搞吧)
但我考慮到方便移植和管理其他人的工程還是選擇了第一種,以makefile建立管理工程。(其實我的內心是比較向往第三種sh腳本建工程的)。下面來介紹下makefile的規則以及語法
PS:我本意是想寫短一點的,只是想寫點常用的東西,方便大家查閱。精煉了這么久,可還有這么多內容(15000字)。所以,大家還是耐心的學習吧,想學好linux這是必不可少的一步。
規則
說明
<target1 > <target2>.... : <prerequisites1> <prerequisites2>...
[TAB] <command1>
[TAB] <command2>
...
target可以是一個object file(目標文件),也可以是一個執行文件,還可以是一個標簽(label)。對于標簽這種特性,在后續的“偽目標”章節中會有敘述。
<prerequisites>就是,要生成那個target所需要的文件或是目標。
<command>也就是make需要執行的命令。(任意的shell命令)
這是一個文件的依賴關系,也就是說,<target>這一個或多個的目標文件依賴于<prerequisites>中的文件,其生成規則定義在 <command>中。說白一點就是說,<prerequisites>中如果有一個以上的文件比<target>文件要新的話,<command>所定義的命令就會被執行。這就是makefile的規則。也就是makefile中最核心的內容。
經典示例
原始版本
objects = main.o kbd.o command.o display.o \insert.o search.o files.o utils.oedit : $(objects)cc -o edit $(objects) main.o : main.c defs.hcc -c main.c kbd.o : kbd.c defs.h command.hcc -c kbd.c command.o : command.c defs.h command.hcc -c command.c display.o : display.c defs.h buffer.hcc -c display.c insert.o : insert.c defs.h buffer.hcc -c insert.c search.o : search.c defs.h buffer.hcc -c search.c files.o : files.c defs.h buffer.h command.hcc -c files.c utils.o : utils.c defs.hcc -c utils.c clean :rm edit $(objects)自動推導版本
objects = main.o kbd.o command.o display.o \insert.o search.o files.o utils.occ = gccedit : $(objects)cc -o edit $(objects)main.o : defs.h kbd.o : defs.h command.h command.o : defs.h command.h display.o : defs.h buffer.h insert.o : defs.h buffer.h search.o : defs.h buffer.h files.o : defs.h buffer.h command.h utils.o : defs.h.PHONY : clean clean :rm edit $(objects)理解
1.內容展開
list_a = 1.a 2.a list_b = 1.b 2.b 3.b#list_a中的元素每次單個展開連接list_b中的所有元素 #結果如下 test:list_a %.a:list_b #展開:1.a:1.b 2.b 3.b #此時&@=1.a $<=1.b 2.b 3.b2.a:1.b 2.b 3.b #此時&@=2.a $<=1.b 2.b 3.b#內容還可以繼續展開 %.b:%.x #自動推導展開1.b:1.x2.b:2.x3.b:3.x2.include 相當于將內容復制展開,export可以共享全局變量。
3.用make遍歷子目錄makefile時相當于,用新進程開啟make。因此export不能共享全局變量。
注意
1.注意空格與TAB(有些編輯器會自動將TAB轉換成4個空格,難以發現)
2.注意 = 與 := 的區別
包括export 在內的所有變量都不能共享
如果使用cd &&make進行遍歷。那么最好轉換成絕對路徑
3.如果使用cd &&make進行遍歷。那么最好轉換成絕對路徑
語法
1.符號
1) 命令前綴
[不用前綴 ]輸出執行的命令以及命令執行的結果, 出錯的話停止執行
[前綴 @]只輸出命令執行的結果, 出錯的話停止執行
[前綴 -]命令執行有錯的話, 忽略錯誤, 繼續執行
2) 通配符
*表示任意一個或多個字符
? 表示任意一個字符
[...] [abcd] 表示a,b,c,d中任意一個字符, [^abcd]表示除a,b,c,d以外的字符, [0-9]表示 0~9中任意一個數字
3) 自動變量
在下文的 自動變量
4) 賦值
= 與 :=
相同點:都是給內容賦值;
區別:與Verilog的=和<=類似。
其中 = 和 := 的區別在于 :
= 只能使用前面定義好的變量,
= 可以使用后面定義的變量
賦值符號 [=]
= 最后再賦值
# Makefile內容 OBJS2 = $(OBJS1) programC.o OBJS1 = programA.o programB.oall:@echo $(OBJS2)# bash中執行 make, 可以看出雖然 OBJS1 是在 OBJS2 之后定義的, 但在 OBJS2中可以提前使用 $ make programA.o programB.o programC.o賦值符號 [:=]
:= 立即賦值
# Makefile內容 OBJS2 := $(OBJS1) programC.o OBJS1 := programA.o programB.oall:@echo $(OBJS2)# bash中執行 make, 可以看出 OBJS2 中的 $(OBJS1) 為空 $ make programC.o賦值符號 [+=]
+= 變量追加值
# Makefile內容 SRCS := programA.c programB.c programC.c SRCS += programD.call:@echo "SRCS: " $(SRCS)# bash中運行make $ make SRCS: programA.c programB.c programC.c programD.c5) 偽目標
.PHONY
.PHONY : clean clean :-rm edit $(objects)2. 隱含規則
1) 自動推倒命名:
編譯C時,*.o 的目標會自動推導為 *.c
2) 隱含變量
[RM] rm -f
[AR] ar
[CC] cc
[CXX] g++
[ARFLAGS] AR命令的參數
[CFLAGS] 語言編譯器的參數
[CXXFLAGS] C++語言編譯器的參數
3) 自動變量
[$@] 目標集合
[$%] 當目標是函數庫文件時, 表示其中的目標文件名
[$<] 第一個依賴目標. 如果依賴目標是多個, 逐個表示依賴目標
[$?] 比目標新的依賴目標的集合
[$^] 所有依賴目標的集合, 會去除重復的依賴目標
[$+] 所有依賴目標的集合, 不會去除重復的依賴目標
[$*] 這個是GNU make特有的, 其它的make不一定支持
3.定義
不限于makefile還有部分shell指令
1) 查看依賴關系
gcc -MM
gcc -MM kvm_main.c kvm_main.o: kvm_main.c iodev.h coalesced_mmio.hasync_pf.h 這句就可以加到 Makefile 中作為編譯 kvm_main.o 的依賴關系2) 定義命令包
define <command-name>
command
...
endef
3) 條件判斷
# Makefile 內容 all: ifeq ("aa", "bb")@echo "equal" else@echo "not equal" endif# bash 中執行 make $ make not equal4) 自定義函數
$(call <expression>,<parm1>,<parm2>,<parm3>...)
# Makefile 內容 hello = "hello " $(1) " world"all:@echo $(call hello,"我是參數1")# bash 中執行 make $ make hello 我是參數1 world5) 執行shell指令
$(shell <shell command>) 與 `<shell command>`
作用:執行一個shell命令, 并將shell命令的結果作為返回.
注意:` 是反引號(英文狀態下,鍵盤的ESC下面那個鍵)
4.函數
不限于makefile還有部分shell指令
1) 傳參(同一個進程)
export
export EX_VAR = value export EX_VAR := value export EX_VAR += value #= := += 與上面的描述基本相同注意:是同一個進程下的make才有用。當多級遍歷make時是無法全局的。
2) 字符串處理
(1) 字符串替換函數
$( subst<source>,<new>,<text>)
功能: 把字符串<text> 中的<source> 替換為<new>
返回: 替換過的字符串
(2) 字符串中每個元素替換函數
$( patsubst <source>,<new>,<text>)
功能: 把字符串 <text> 中的的每個元素 <source> 替換為<new>
返回: 替換過的字符串
(3) 去空格函數
$(strip <text>)
功能: 去掉 <string> 字符串中開頭和結尾的空字符
返回: 被去掉空格的字符串值
(4) 判斷字符串內是否存在特定字符串
$(findstring<text>,<elem>)
功能: 在字符串 <text> 中查找 <elem> 字符串是否存在
返回: 如果找到, 返回 <elem> 字符串, 否則返回空字符串
3) 文件元素相關
(1) 取文件函數
保留符合條件的元素
$(filter <elem1 elem2...>,<text>)
# Makefile 內容 all:@echo $(filter %.o %.a,program.c program.o program.a)# bash 中執行 make $ make program.o program.a program.c去掉符合條件的元素
$(filter-out <elem1 elem2...>,<text>)
# Makefile 內容 all:@echo $(filter-out %.o %.a,program.c program.o program.a)# bash 中執行 make $ make program.c獲取該目錄下所有文件
獲取該目錄下所有.x文件
$(wildcard *.x)
(2) 路徑函數
去掉路徑
$(notdir <elem elem...>)
功能: 去掉序列的路徑
返回: 文件名序列 <elem> 中的非目錄部分
取路徑
$(dir <elem1 elem2...>)
功能: 從文件名序列 <elem> 中取出目錄部分
返回: 文件名序列 <elem> 中的目錄部分
(3) 取前后綴
取后綴函數
$(suffix <elem1 elem2...>)
功能: 從文件名序列 <elem> 中取出各個文件名的后綴
返回: 文件名序列 <elem> 中各個文件名的后綴, 沒有后綴則返回空字符串
取前綴函數
$(basename <elem1 elem2...>)
功能: 從文件名序列 <elem> 中取出各個文件名的前綴
返回: 文件名序列 <elem> 中各個文件名的前綴, 沒有前綴則返回空字符串
(3) 增添前后綴
增添后綴函數
$(addsuffix <suffix>,<elem1 elem2...>)
功能: 把后綴 <suffix> 加到 <elem> 中的每個單詞后面
返回: 加過后綴的文件名序列
增添前綴函數
$(addprefix <prefix>, <elem1 elem2...>)
功能: 把前綴 <prefix> 加到 <elem> 中的每個單詞前面
返回: 加過前綴的文件名序列
我寫的管理大型項目的makefile
以下內容為基礎版本。可以混編C和C++。
但沒有加入平臺兼容性驗證等功能。
在ubuntu16.04下測試通過。
頂層 makefile
# ------------------------------------------- # FileName :makefile # Author :wind # Date :2018-1-16 # Description # # Top makefile. # # ------------------------------------------- # 設置當前路徑 DIR_ROOT:=.# 設置遞歸子目錄 DIR_LIST_SUB :=module appinclude $(DIR_ROOT)/make_conf.mk all:make_root clean:make_clean include $(DIR_ROOT)/make_fun.mk中間遍歷層 makefile
# ------------------------------------------- # FileName :xx/makefile # Author :wind # Date :2018-1-16 # Description # # Level1 makefile. # # ------------------------------------------- # 設置當前路徑 DIR_ROOT:=..# 添加遞歸子目錄 DIR_LIST_SUB :=\ app1\ app2\# 添加源文件 FILE_LIST_C +=\FILE_LIST_CPP +=\FILE_LIST_S +=\# 添加頭文件路徑 DIR_LIST_INCLUDE+=\include $(DIR_ROOT)/make_conf.mk all:make_show make_subdir clean:make_clean include $(DIR_ROOT)/make_fun.mkapp的makefile
# ------------------------------------------- # FileName :xx/xx/makefile # Author :wind # Date :2018-1-16 # Description # # Level2 makefile. # # ------------------------------------------- # 設置當前路徑 DIR_ROOT:=../..# 添加遞歸子目錄 DIR_LIST_SUB :=\# 添加源文件 FILE_LIST_C +=\ main.c\FILE_LIST_CPP +=\FILE_LIST_S +=\# 添加頭文件路徑 DIR_LIST_INCLUDE+=\ $(DIR_ROOT)/module/test/hal\ $(DIR_ROOT)/module/test/drive\include $(DIR_ROOT)/make_conf.mk all:make_show make_app clean:make_clean include $(DIR_ROOT)/make_fun.mkdrive的makefile
# ------------------------------------------- # FileName :xx/xx/makefile # Author :wind # Date :2018-1-16 # Description # # Level2 makefile. # # ------------------------------------------- # 設置當前路徑 DIR_ROOT:=../..# 添加遞歸子目錄 DIR_LIST_SUB :=\# 添加源文件 FILE_LIST_C +=\ drive/test.c\ hal/test_hal.c\FILE_LIST_CPP +=\ drive/cpp_test.cpp\FILE_LIST_S +=\# 添加頭文件路徑 DIR_LIST_INCLUDE+=\ hal\ drive\include $(DIR_ROOT)/make_conf.mk all:make_show make_lib_a clean:make_clean include $(DIR_ROOT)/make_fun.mk配置文件 make_conf.mk
# ------------------------------------------- # FileName :make_set.inc # Author :wind # Date :2018-1-16 # Description # # 工程相關設置。 # # -------------------------------------------# 設置常用指令 # ------------------------------------------- RM = rm -f MV = mv -f MKDIR = mkdir -p RMDIR = rm -rf # 顏色輸出 # ------------------------------------------- ECHO_END:=\033[0m" ECHO_GREEN:=echo "\033[32m ECHO_RED:=echo "\033[31m ECHO_YELLOW:=echo "\033[33m ECHO_BLUE:=echo "\033[34m ECHO_GREEN_YELLOW:=echo "\033[42;30m# 編譯缺省設置 # -------------------------------------------# 默認編譯 CXX:=g++#編譯選項 COMPILE_C ?= $(CXX) COMPILE_CXX ?= $(CXX) COMPILE_ASM ?= $(CXX) COMPILE_AR ?= ar# 設置優化等級 OPT ?=0# 設置警告開關 COMPILE_WARN ?= -Wall # 設置靜態編譯 COMPILE_STATIC ?= -s# 在環境基礎下添加設置 CFLAGS+= $(DIR_LIST_INCLUDE_I) $(COMPILE_WARN) -O$(OPT) $(COMPILE_STATIC) CXXFLAGS+= $(DIR_LIST_INCLUDE_I) $(COMPILE_WARN) -O$(OPT) $(COMPILE_STATIC) ASFLAGS+= -Wa,-adhlns=$(<:.S=.lst),-gstabs $(DIR_LIST_INCLUDE_I)# 編譯設置匯總 COMPILE_CFLAGS = $(CFLAGS) COMPILE_CXXFLAGS = $(CXXFLAGS) COMPILE_ASFLAGS = $(ASFLAGS)#鏈接選項 LDFLAGS+= -lstdc++#編譯C++ LDFLAGS+= -lpthread#使用了線程 LDFLAGS+= -fPIC#編譯為位置獨立的代碼 LDFLAGS+= -ldl#引用動態庫 LDFLAGS+= $(DIR_LIST_INCLUDE_I)#引用其他靜態庫 FILE_LIST_LIB_A+=\功能文件 make_fun.mk
# ------------------------------------------- # FileName :make_fun.mk # Author :wind # Date :2018-1-16 # Description # # 實際編譯過程。 #s # ------------------------------------------- # 路徑關系 # ------------------------------------------- DIR_ROOT_REAL=$(realpath $(DIR_ROOT)) NAME_MODULE := $(notdir $(CURDIR))#所在的文件夾名稱 DIR_OUTPUT:=$(DIR_ROOT)/output DIR_BIN:=$(DIR_OUTPUT)/bin DIR_OBJ:=$(DIR_OUTPUT)/obj DIR_LIB:=$(DIR_OUTPUT)/lib# 路徑處理成可用參數 # ------------------------------------------- DIR_LIST_INCLUDE_I:=$(addprefix -I,$(DIR_LIST_INCLUDE))#添加編譯選項-I DIR_LIST_SUB:=$(addprefix $(CURDIR)/,$(DIR_LIST_SUB))#轉換成絕對路徑 DIR_CURDIR:=$(subst $(DIR_ROOT_REAL),,$(CURDIR))#相對路徑 DIR_OBJ_OUT:=$(DIR_OBJ)$(DIR_CURDIR)#文件處理 FILE_LIST_OBJ:=$(FILE_LIST_C:%.c=%.o) FILE_LIST_OBJ+=$(FILE_LIST_CPP:%.cpp=%.o) FILE_LIST_OBJ+=$(FILE_LIST_S:%.s=%.o)FILE_LIST_OBJ:=$(addprefix $(DIR_OBJ_OUT)/,$(FILE_LIST_OBJ))#轉換成帶絕對路徑的中間文件 FILE_LIB_A:=$(DIR_LIB)/libobj.a FILE_LIST_LIB_APP:=$(DIR_BIN)/$(NAME_MODULE).bin# 函數庫 # ------------------------------------------- make_root:make_start make_subdir make_end# 工程開始 make_start:@$(ECHO_BLUE)\t-----------------------------$(ECHO_END)@$(ECHO_BLUE)\t-\t [編譯開始] \t -$(ECHO_END)@$(ECHO_BLUE)\t-----------------------------$(ECHO_END)@$(ECHO_BLUE)[COMPILE_C]$(COMPILE_C) [COMPILE_CFLAGS]$(COMPILE_CFLAGS)$(ECHO_END)@$(ECHO_BLUE)[COMPILE_CXX]$(COMPILE_CXX) [COMPILE_CXXFLAGS]$(COMPILE_CXXFLAGS)$(ECHO_END)@$(ECHO_BLUE)[COMPILE_ASM]$(COMPILE_ASM) [COMPILE_ASFLAGS]$(COMPILE_ASFLAGS)$(ECHO_END)@$(ECHO_BLUE)[COMPILE_AR]$(COMPILE_AR) [LDFLAGS]$(LDFLAGS)$(ECHO_END)@$(ECHO_BLUE)[OPT]$(OPT) $(ECHO_END)@$(ECHO_BLUE)[COMPILE_STATIC]$(COMPILE_STATIC) $(ECHO_END)@$(ECHO_BLUE)[FILE_LIST_LIB_A]$(FILE_LIST_LIB_A) $(ECHO_END)# 工程完成 make_end: @$(ECHO_BLUE)[編譯完成]$(ECHO_END) # 遞歸子目錄 make_subdir:@for list in $(DIR_LIST_SUB);\do\cd $$list && make all|| exit 1;\done# 生成可執行文件 make_app:make_lib_a make_bin# 生成靜態鏈接庫 make_lib_a:make_obj@$(MKDIR) `dirname $(FILE_LIB_A)`@$(ECHO_YELLOW)[$(COMPILE_AR)]-rcs $(FILE_LIB_A) $(FILE_LIST_OBJ)$(ECHO_END)$(COMPILE_AR) -rcs $(FILE_LIB_A) $(FILE_LIST_OBJ)# 鏈接 make_bin:$(FILE_LIST_LIB_APP)# 編譯 make_obj:$(FILE_LIST_OBJ)# 清除 make_clean:$(RMDIR) $(DIR_OUTPUT)# 顯示參數,方便調試 make_show:@$(ECHO_GREEN_YELLOW)[DIR_CURDIR] $(DIR_CURDIR) $(ECHO_END)@$(ECHO_GREEN)[FILE_LIST_C] $(FILE_LIST_C) $(ECHO_END)@$(ECHO_GREEN)[DIR_LIST_INCLUDE] $(subst $(DIR_ROOT)/,[DIR_ROOT]/,$(DIR_LIST_INCLUDE)) $(ECHO_END)# 文件操作過程 # ------------------------------------------- # 編譯過程成中間文件 $(DIR_OBJ_OUT)/%.o: %.c@$(MKDIR) `dirname $@`@$(ECHO_YELLOW)[$(COMPILE_C)]$< -o $@ $(ECHO_END)$(COMPILE_C) -c $(COMPILE_CFLAGS) $< -o $@$(DIR_OBJ_OUT)/%.o: %.cpp@$(MKDIR) `dirname $@`@$(ECHO_YELLOW)[$(COMPILE_CXX)]$< -o $@ $(ECHO_END)$(COMPILE_CXX) -c $(COMPILE_CXXFLAGS) $< -o $@$(DIR_OBJ_OUT)/%.o: %.s@$(MKDIR) `dirname $@`@$(ECHO_YELLOW)[$(COMPILE_C)]$< -o $@ $(ECHO_END)$(COMPILE_ASM) -c $(COMPILE_ASFLAGS) $< -o $@# 鏈接成二進制文件 $(FILE_LIST_LIB_APP): $(FILE_LIB_A)@$(MKDIR) `dirname $@`@$(ECHO_YELLOW)[$(COMPILE_C)]$< -o $@ $(ECHO_END)$(COMPILE_C) $(LDFLAGS) -L$(DIR_LIB) -lobj $< $(FILE_LIST_LIB_A) -o $@內容推薦
跟我一起寫Makefile
Makefile 使用總結
項目實用makefile
Makefile之大型工程項目子目錄Makefile的一種通用寫法
引用本地址
https://www.cnblogs.com/wittxie/p/9836097.html
轉載于:https://www.cnblogs.com/wittxie/p/9836097.html
總結
以上是生活随笔為你收集整理的makefile 完美教程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: table合并单元格宽度自适应
- 下一篇: Harbo1.5.2离线搭建