https://www.toutiao.com/i6599850731023368707/
編譯自: https://opensource.com/article/18/8/what-how-makefile
作者: Sachin Patil
譯者: Zafiry
用這個方便的工具來更有效的運(yùn)行和編譯你的程序。
當(dāng)你需要在一些源文件改變后運(yùn)行或更新一個任務(wù)時,通常會用到 make 工具。make 工具需要讀取一個 Makefile(或 makefile)文件,在該文件中定義了一系列需要執(zhí)行的任務(wù)。你可以使用 make 來將源代碼編譯為可執(zhí)行程序。大部分開源項目會使用 make 來實現(xiàn)最終的二進(jìn)制文件的編譯,然后使用 make install 命令來執(zhí)行安裝。
本文將通過一些基礎(chǔ)和進(jìn)階的示例來展示 make 和 Makefile 的使用方法。在開始前,請確保你的系統(tǒng)中安裝了 make。
依然從打印 “Hello World” 開始。首先創(chuàng)建一個名字為 myproject 的目錄,目錄下新建 Makefile 文件,文件內(nèi)容為:
say_hello:
echo "Hello World"
在 myproject 目錄下執(zhí)行 make,會有如下輸出:
$ make
echo "Hello World"
Hello World
在上面的例子中,“say_hello” 類似于其他編程語言中的函數(shù)名。這被稱之為 目標(biāo)(target)。在該目標(biāo)之后的是預(yù)置條件或依賴。為了簡單起見,我們在這個示例中沒有定義預(yù)置條件。echo ‘Hello World' 命令被稱為 步驟(recipe)。這些步驟基于預(yù)置條件來實現(xiàn)目標(biāo)。目標(biāo)、預(yù)置條件和步驟共同構(gòu)成一個規(guī)則。
總結(jié)一下,一個典型的規(guī)則的語法為:
目標(biāo): 預(yù)置條件
<TAB> 步驟
作為示例,目標(biāo)可以是一個基于預(yù)置條件(源代碼)的二進(jìn)制文件。另一方面,預(yù)置條件也可以是依賴其他預(yù)置條件的目標(biāo)。
final_target: sub_target final_target.c
Recipe_to_create_final_target
sub_target: sub_target.c
Recipe_to_create_sub_target
目標(biāo)并不要求是一個文件,也可以只是步驟的名字,就如我們的例子中一樣。我們稱之為“偽目標(biāo)”。
再回到上面的示例中,當(dāng) make 被執(zhí)行時,整條指令 echo "Hello World" 都被顯示出來,之后才是真正的執(zhí)行結(jié)果。如果不希望指令本身被打印處理,需要在 echo 前添加 @。
say_hello:
@echo "Hello World"
重新運(yùn)行 make,將會只有如下輸出:
$ make
Hello World
接下來在 Makefile 中添加如下偽目標(biāo):generate 和 clean:
say_hello:
@echo "Hello World"
generate:
@echo "Creating empty text files..."
touch file-{1..10}.txt
clean:
@echo "Cleaning up..."
rm *.txt
隨后當(dāng)我們運(yùn)行 make 時,只有 say_hello 這個目標(biāo)被執(zhí)行。這是因為Makefile 中的第一個目標(biāo)為默認(rèn)目標(biāo)。通常情況下會調(diào)用默認(rèn)目標(biāo),這就是你在大多數(shù)項目中看到 all 作為第一個目標(biāo)而出現(xiàn)。all 負(fù)責(zé)來調(diào)用它他的目標(biāo)。我們可以通過 .DEFAULT_GOAL 這個特殊的偽目標(biāo)來覆蓋掉默認(rèn)的行為。
在 Makefile 文件開頭增加 .DEFAULT_GOAL:
.DEFAULT_GOAL := generate
make 會將 generate 作為默認(rèn)目標(biāo):
$ make
Creating empty text files...
touch file-{1..10}.txt
顧名思義,.DEFAULT_GOAL 偽目標(biāo)僅能定義一個目標(biāo)。這就是為什么很多 Makefile 會包括 all 這個目標(biāo),這樣可以調(diào)用多個目標(biāo)。
下面刪除掉 .DEFAULT_GOAL,增加 all 目標(biāo):
all: say_hello generate
say_hello:
@echo "Hello World"
generate:
@echo "Creating empty text files..."
touch file-{1..10}.txt
clean:
@echo "Cleaning up..."
rm *.txt
運(yùn)行之前,我們再增加一些特殊的偽目標(biāo)。.PHONY 用來定義這些不是文件的目標(biāo)。make 會默認(rèn)調(diào)用這些偽目標(biāo)下的步驟,而不去檢查文件名是否存在或最后修改日期。完整的 Makefile 如下:
.PHONY: all say_hello generate clean
all: say_hello generate
say_hello:
@echo "Hello World"
generate:
@echo "Creating empty text files..."
touch file-{1..10}.txt
clean:
@echo "Cleaning up..."
rm *.txt
make 命令會調(diào)用 say_hello 和 generate:
$ make
Hello World
Creating empty text files...
touch file-{1..10}.txt
clean 不應(yīng)該被放入 all 中,或者被放入第一個目標(biāo)中。clean 應(yīng)當(dāng)在需要清理時手動調(diào)用,調(diào)用方法為 make clean。
$ make clean
Cleaning up...
rm *.txt
現(xiàn)在你應(yīng)該已經(jīng)對 Makefile 有了基礎(chǔ)的了解,接下來我們看一些進(jìn)階的示例。
在之前的實例中,大部分目標(biāo)和預(yù)置條件是已經(jīng)固定了的,但在實際項目中,它們通常用變量和模式來代替。
定義變量最簡單的方式是使用 = 操作符。例如,將命令 gcc 賦值給變量 CC:
CC = gcc
這被稱為遞歸擴(kuò)展變量,用于如下所示的規(guī)則中:
hello: hello.c
${CC} hello.c -o hello
你可能已經(jīng)想到了,這些步驟將會在傳遞給終端時展開為:
gcc hello.c -o hello
${CC} 和 $(CC) 都能對 gcc 進(jìn)行引用。但如果一個變量嘗試將它本身賦值給自己,將會造成死循環(huán)。讓我們驗證一下:
CC = gcc
CC = ${CC}
all:
@echo ${CC}
此時運(yùn)行 make 會導(dǎo)致:
$ make
Makefile:8: *** Recursive variable 'CC' references itself (eventually). Stop.
為了避免這種情況發(fā)生,可以使用 := 操作符(這被稱為簡單擴(kuò)展變量)。以下代碼不會造成上述問題:
CC := gcc
CC := ${CC}
all:
@echo ${CC}
下面的 Makefile 使用了變量、模式和函數(shù)來實現(xiàn)所有 C 代碼的編譯。我們來逐行分析下:
# Usage:
# make # compile all binary
# make clean # remove ALL binaries and objects
.PHONY = all clean
CC = gcc # compiler to use
LINKERFLAG = -lm
SRCS := $(wildcard *.c)
BINS := $(SRCS:%.c=%)
all: ${BINS}
%: %.o
@echo "Checking.."
${CC} ${LINKERFLAG} $< -o $@
%.o: %.c
@echo "Creating object.."
${CC} -c $<
clean:
@echo "Cleaning up..."
rm -rvf *.o ${BINS}
%: %.o
@echo "Checking.."
${CC} ${LINKERFLAG} $< -o $@
foo: foo.o
@echo "Checking.."
gcc -lm foo.o -o foo
%.o: %.c
@echo "Creating object.."
${CC} -c $<
foo.o: foo.c
@echo "Creating object.."
gcc -c foo.c
下面是重寫后的 Makefile,該文件應(yīng)該被放置在一個有 foo.c 文件的目錄下:
# Usage:
# make # compile all binary
# make clean # remove ALL binaries and objects
.PHONY = all clean
CC = gcc # compiler to use
LINKERFLAG = -lm
SRCS := foo.c
BINS := foo
all: foo
foo: foo.o
@echo "Checking.."
gcc -lm foo.o -o foo
foo.o: foo.c
@echo "Creating object.."
gcc -c foo.c
clean:
@echo "Cleaning up..."
rm -rvf foo.o foo
關(guān)于 Makefile 的更多信息, GNU Make 手冊 提供了更完整的說明和實例。
via: https://opensource.com/article/18/8/what-how-makefile
作者: Sachin Patil 選題: lujun9972 譯者: Zafiry 校對: wxy
本文由 LCTT 原創(chuàng)編譯, Linux中國 榮譽(yù)推出