makeFile基本介紹, 語法, 範例,通用makefile

2020-08-14 23:07:50

makefile(結尾有萬用makefile)

實際上就是很複雜的程式語言

IDE使用變多使得makefile使用變少;

對於Linux來說了解makefile依然很必要

make 命令: 負責C/C++程式編譯與鏈接

make根據指定命令進行構建

建構規則檔案:GNUmakefile, makefile, Makefile; (GNUmakefile是GNU專用, 不建議使用)

makefile檔案格式

是固定的:

target...:prerequisites...

[Tab健] commands

makefile檔案規則

由一系列規則構成

規則的目的: 建構目標的先決條件是什麼以及如何建構目標

如果未指定目標, 預設執行第一個目標

若prerequisites中有一個以上的檔案比target檔案要新, 執行commands所定義的命令

target: 目標

通常爲編譯期的檔名, 以指定要建構的物件, 也可以是執行檔案, 還可以是標籤(操作名稱, 僞目標)

可以爲單一目標, 也可以爲空格分隔的多個目標

每個目標都定義了一組處理規則, 和其相關規則構成規則鏈

preprequisites: 先決條件

爲生成該目標所需的先決檔案或目標(前置條件)

一般爲空格分隔的檔名, 指定目標是否重建的判斷標準, 即只要有一個先決檔案不存在或有過更新, 就重建目標

若目標先決條件本身需要重建, 則匹配該先決條件的目標, 執行其對應的命令

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: 更新所有的目標, 以備完整地重編譯使用

checktest: 一般用來測試makefile的流程

範例: 主程式檔案"main.c", 使用library庫

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

makefile語法

很多時候和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

目的: 用於處理模式相同的多目標, 簡化指令碼程式碼

範例:每個目標檔案都以.o結尾, 先決檔案都.c結尾

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

回圈:可以使用shell回圈

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)