實際上就是很複雜的程式語言
IDE使用變多使得makefile使用變少;
對於Linux來說了解makefile依然很必要
make根據指定命令進行構建
建構規則檔案:GNUmakefile, makefile, Makefile; (GNUmakefile是GNU專用, 不建議使用)
是固定的:
target...:prerequisites...
[Tab健] commands
由一系列規則構成
規則的目的: 建構目標的先決條件是什麼以及如何建構目標
如果未指定目標, 預設執行第一個目標
若prerequisites中有一個以上的檔案比target檔案要新, 執行commands所定義的命令
通常爲編譯期的檔名, 以指定要建構的物件, 也可以是執行檔案, 還可以是標籤(操作名稱, 僞目標)
可以爲單一目標, 也可以爲空格分隔的多個目標
每個目標都定義了一組處理規則, 和其相關規則構成規則鏈
爲生成該目標所需的先決檔案或目標(前置條件)
一般爲空格分隔的檔名, 指定目標是否重建的判斷標準, 即只要有一個先決檔案不存在或有過更新, 就重建目標
若目標先決條件本身需要重建, 則匹配該先決條件的目標, 執行其對應的命令由一行或多行shell命令組成, 命令前有tab建
(頂頭必須是tab
不能是空格)
指示如何構建目標, 一般爲生成目標檔案
每行命令都在單獨的進程中執行, 彼此間沒有繼承關係, 不能簡單傳遞數據;
解決方法:用分號將多條命令書寫在單行(此時可用"\"折行), 或者爲該條規則新增指示
「.ONESHELL:」
刪除編譯後的二進制目標檔案, 例如:
clean:
rm -f *.o
執行命令時须指定僞目標: $ make clean
若當前目錄下有clean檔案, 則此規則不會被執行;此時可用".PHONY:clean"明確指示clean爲僞目標; make將跳過檔案檢查, 執行其對應的命令;
執行清除任務的僞目標一般放置在指令碼的最後
all: 所有目標的目標, 一般爲編譯所有的目標, 對同時編譯多個程式極爲有用
clean: 刪除由make建立的檔案
install: 安裝已編譯好的程式, 主要任務是完成目標執行檔案的拷貝
print: 列出改變過的原始檔
tar: 打包備份原始檔
dist: 建立壓縮檔案, 一般將tar檔案壓縮成Z檔案或gz檔案
TAGS: 更新所有的目標, 以備完整地重編譯使用
check和test: 一般用來測試makefile的流程
prog : main.o library.o
cc -o prog main.o library.o
main.o:main.c library.h
cc -c main.c
library.o : library.c library.h
cc -c library.c
.PHONY : clean
clean:
rm main.o library.o
很多時候和shell的程式設計規範相似;
行解析: 命令按行解析
命令列的行首字元爲Tab
, 其他行的行首字元不得爲Tab
, 但可以使用多個空格縮排;
換行: 命令太長時, 行尾用""換行 \
"#"是註釋 #
關閉回顯: 行首字元後和命令前加"@" @
未關閉回顯時, make會首先回顯(列印)命令, 然後執行該命令
通常僅在註釋和純顯示的echo命令前使用;
include filename: 包含其他檔案
像C/C++;
行首加"-":忽略檔案包含的錯誤
正則匹配;
基本變數定義:
var_name = value
$(變數名):參照變數(無多餘空格);
shell變數用"$$", 如:
@echo $$HOME
變數在使用時展開, 形式上類似於宏替換(當作字串處理);
變數的使用場合: 幾乎所有場合: 目標, 先決條件, 新變數;
$(CC):當前使用的編譯器;
$(MAKE):當前使用的make工具
符號 | 含義 |
---|---|
$@ | 當前目標; |
$< | 當前目標的首個先決條件; |
$? | 比目標更新的所有先覺條件; |
$^ | 所有先決條件; |
$(@F), $(@D) | $@的目錄名和檔名; |
$(<D), $(<F) | $< 的目錄名和檔名 |
不太好理解和記憶
var_name =
value : 在執行時擴充套件, 允許遞回, 可以使用後續程式碼中出現的值
var_name :=
value : 在定義時擴充套件, 不允許遞回, 使用右側的現值, 不能使用後續程式碼中出現的值;
var_name ?=
value : 只有在該變數爲空時才設定值, 否則維持原值
var_name +=
value : 將值追加到變數的尾部; 若變數未定義, 則"+=
「自動解釋爲」=
";
若變數已定義, 則"+=
"繼承上次的操作符, 並追加新值(如之前是:=
賦值, 追加時也用:=
)
不經常使用
define var_name
@echo "One"
@echo "Two"
endef
define和endef行首字元不能爲Tab, 對齊時可使用空格(它倆本身不是命令)
參照: $(var_name)
多行變數主要用於定義命令包, 使用多行變數要小心, 展開時可能導致指令碼錯誤;
tatget ...:var_name = value
:定義目標變數
基本模式:
target ...: target-pattern:prerequisites ...
[tab鍵]commands
目的: 用於處理模式相同的多目標, 簡化指令碼程式碼
objs = main.o library.o
$(objs):%.o:%.c
$(CC) -c $(CFLAGS) $< -o $@
# 翻譯出來就是:
main.o:main.c
$(CC) -c $(CFLAGS) main.c -o main.o
library.o:library.c
$(CC) -c $(CFLAGS) library.c -o library.o
conditional-directive
text-if-true
else
text-if-false
endif
ifeq(arg1, arg2), ifeq ‘arg1’‘arg2’, ifeq"arg1"「arg2」
ifneq …
ifdef var_name
ifndef var_name
rulefor:
for filename in `echo $(objs)`;\
do\
rm -f $$filename;\
done
回圈爲shell回圈, 爲保證多行命令在同一進程下執行, 必須合成單行命令, 所以需要新增分行標識
可以使用反引號執行命令, 所獲得的結果集合可以作爲回圈的處理集合
filename本身是shell變數, 需使用"$$"參照
$(function arg1,arg2,...)
: 函數呼叫, 函數名爲function,
後跟逗號分隔的參數列表, 函數參數前後不能有多餘的空格
$(subst from,to,text)
: make的字串替代函數,將text中的from字串替換爲to,
返回替換後的字串
comma:=,
# 定義空值
empty:=
# 定義空格
space:=$(empty) $(empty)
foo:=a b c
# 將"a b c" 替換爲 "a,b,c"
bar:=$(subst $(space),$(comma),$(foo))
####################################################
# Generic makefile - 萬能Makefile
# for compiling and linking C++ projects on Linux
# Author: George Foot Modified:Jackie Lee
####################################################
### Customising
#
# Adjust the following if necessary; EXECUTABLE is the target
# executable's filename, and LIBS is a list of libraries to link in
# (e.g. alleg, stdcx, iostr, etc). You can override these on make's
# command line of course, if you prefer to do it that way.
#
#
EXECUTABLE := main # 可執行檔名
LIBDIR:= # 靜態庫目錄
LIBS := # 靜態庫檔名
INCLUDES:=. # 標頭檔案目錄
SRCDIR:= # 除了當前目錄外,其他的原始碼檔案目錄
#
# # Now alter any implicit rules' variables if you like, e.g.:
CC:=g++
CFLAGS := -g -Wall -O3
CPPFLAGS := $(CFLAGS)
CPPFLAGS += $(addprefix -I,$(INCLUDES))
CPPFLAGS += -MMD
#
# # The next bit checks to see whether rm is in your djgpp bin
# # directory; if not it uses del instead, but this can cause (harmless)
# # `File not found' error messages. If you are not using DOS at all,
# # set the variable to something which will unquestioningly remove
# # files.
#
RM-F := rm -f
# # You shouldn't need to change anything below this point.
#
SRCS := $(wildcard *.cpp) $(wildcard $(addsuffix /*.cpp, $(SRCDIR)))
OBJS := $(patsubst %.cpp,%.o,$(SRCS))
DEPS := $(patsubst %.o,%.d,$(OBJS))
MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS))
MISSING_DEPS_SOURCES := $(wildcard $(patsubst %.d,%.cpp,$(MISSING_DEPS)))
.PHONY : all deps objs clean veryclean rebuild info
all: $(EXECUTABLE)
deps : $(DEPS)
objs : $(OBJS)
clean :
@$(RM-F) *.o
@$(RM-F) *.d
veryclean: clean
@$(RM-F) $(EXECUTABLE)
rebuild: veryclean all
ifneq ($(MISSING_DEPS),)
$(MISSING_DEPS) :
@$(RM-F) $(patsubst %.d,%.o,$@)
-include $(DEPS)
$(EXECUTABLE) : $(OBJS)
$(CC) -o $(EXECUTABLE) $(OBJS) $(addprefix -L,$(LIBDIR)) $(addprefix -l,$(LIBS))
info:
@echo $(SRCS)
@echo $(OBJS)
@echo $(DEPS)
@echo $(MISSING_DEPS)
@echo $(MISSING_DEPS_SOURCES)