首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Makefile 完整學習指南:從入門到精通

     編輯:關於C語言

前言:為什麼要學習 Makefile?

許多 Windows 程式設計師可能對 Makefile 感到陌生,因為 Visual Studio 等 IDE 已經幫我們處理了編譯細節。但若想成為專業的開發者,理解 Makefile 是必經之路。這就像網頁開發者雖然有所謂「所見即所得」的編輯器,但仍需精通 HTML 標籤含義。在 Unix/Linux 環境下,無法編寫 Makefile 幾乎無法進行大型專案開發,它直接關係到整個工程的編譯規則與效率。

Makefile 的核心優勢在於「自動化編譯」:定義好規則後,只需一條 make 指令,即可根據檔案修改時間智能判斷哪些部分需重新編譯,極大提升開發效率。本教程基於 GNU Make 3.80(RedHat Linux 8.0 環境),以 C/C++ 為例,帶你徹底掌握這項核心技能。

第 0 章:基礎概念——編譯與鏈接

0.1 程式碼如何變成可執行檔?

無論 C、C++ 或 Pascal,流程皆相同:

  1. 編譯:原始碼(.c/.cpp) → 目標檔(Windows 下 .obj,Unix 下 .o)。編譯器檢查語法正確性與函數/變數宣告,生成中間碼。
  2. 鏈接:將多個目標檔合併為執行檔。鏈接器解析函數與全域變數的實際位置,若找不到定義則報錯(如 VC 的 Link 2001 錯誤)。

為管理大量目標檔,引入靜態庫:Windows 為 .lib,Unix 為 .a,本質是目標檔的封裝包。

第 1 章:Makefile 入門實戰

1.1 核心規則語法

target ... : prerequisites ...
    command
    ...
  • target:目標檔(可執行檔、目標檔或標籤)。
  • prerequisites:生成 target 所需的檔案或目標。
  • command:任意 Shell 指令,必須以 Tab 開頭。

規則含義:若 prerequisites 中任一檔案比 target 新,或 target 不存在,則執行 command。

1.2 經典範例:8 個 C 檔、3 個 H 檔的專案

需求:

  1. 首次編譯:全部編譯並鏈接。
  2. 修改部分 .c:僅重編修改檔並鏈接。
  3. 修改 .h:重編引用該頭檔的 .c 並鏈接。

Makefile 範例:

edit : main.o kbd.o command.o display.o insert.o search.o files.o utils.o
    cc -o edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o

main.o : main.c defs.h
    cc -c main.c
kbd.o : kbd.c defs.h command.h
    cc -c kbd.c
...(其餘 .o 規則類似)

clean :
    rm edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o

執行 make 生成 edit,執行 make clean 清理中間檔。

1.3 make 的工作流程

  1. 尋找 Makefile/makefile/GNUmakefile。
  2. 以第一個 target(此處 edit)為最終目標。
  3. 遞迴檢查依賴:若 edit 不存在或 .o 檔較新,執行鏈接命令。
  4. 對每個 .o 檔同理檢查 .c/.h 依賴,執行編譯命令。
  5. 若依賴鏈中斷(檔案缺失),make 直接報錯退出。

此機制確保「只重編必要檔案」,clean 等偽目標不在主依賴鏈上,需顯式呼叫 make clean。

1.4 變數:消除重複,提升維護性

原始範例中 .o 清單重複三次(目標、鏈接命令、clean),新增檔案極易遺漏。引入變數:

objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o

edit : $(objects)
    cc -o edit $(objects)

clean :
    rm edit $(objects)

現在只需修改 objects 一處即可。變數本質是字串替換,類似 C 宏。

1.5 隱含規則:讓 make 自動推導

GNU Make 內建推導:看到 .o 目標,自動尋找同名 .c 並套用 cc -c。Makefile 簡化為:

objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o

edit : $(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)

只需宣告「哪個 .o 依賴哪些 .h」,編譯命令交給 make 自動處理。

1.6 進階風格:靜態模式規則

利用模式 % 批量定義依賴:

objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o

edit : $(objects)
    cc -o edit $(objects)

$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h

.PHONY : clean
clean :
    rm edit $(objects)

雖簡潔,但依賴關係較難閱讀,大型專案建議採用顯式規則。

1.7 清理規則最佳實踐

.PHONY : clean
clean :
    -rm edit $(objects)
  • .PHONY:宣告 clean 為偽目標,避免同名檔案干擾。
  • - 前綴:忽略 rm 錯誤(如檔案不存在),繼續執行。
  • 慣例:clean 放在檔案末尾,防止成為預設目標。

第 2 章:Makefile 全景解析

2.1 五大構成要素

  1. 顯式規則:明確指定目標、依賴、命令。
  2. 隱含規則:make 自動推導的規則。
  3. 變數定義:字串替換,執行時展開。
  4. 檔案指示:include 引入其他 Makefile、條件編譯、多行命令定義。
  5. 註解:# 開頭,同 Shell 腳本。

⚠️ 命令必須以 Tab 開頭。

2.2 檔案命名與搜尋順序

預設搜尋:GNUmakefile → makefile → Makefile。建議用 Makefile(大寫 M 顯眼)。指定其他檔名:make -f Make.Linux。

2.3 引入其他 Makefile

include foo.make *.mk $(bar)

相當於 C 的 #include。搜尋路徑:當前目錄 → -I 指定目錄 → /usr/local/include 等系統目錄。-include 忽略讀取錯誤。

2.4 環境變數 MAKEFILES

若設定,make 會嘗試引入其中列出的 Makefile,但其目標不生效。不建議使用,易造成全域污染。

2.5 make 兩階段執行機制

  1. 讀取階段:讀所有 Makefile、include 檔、初始化變數、推導隱含規則、建立依賴鏈。
  2. 執行階段:根據依賴決定重建目標、執行命令。

變數展開採「延遲策略」:僅在依賴規則被使用時才展開。

第 3 章:規則撰寫進階

3.1 規則範例

foo.o : foo.c defs.h
    # foo 模組
    cc -c -g foo.c

說明:foo.o 依賴 foo.c 與 defs.h,若後者較新或 foo.o 不存在,執行編譯命令。

3.2 語法細節

targets : prerequisites
    command
    ...
# 或單行寫法
targets : prerequisites ; command
    command ...
  • targets:可多個,支援萬用字元。
  • prerequisites:依賴檔案/目標。
  • command:非同行時必須以 Tab 開頭;過長可用 \ 換行。

3.3 萬用字元應用

支援 *、?、[...],同 Unix Shell。~ 代表家目錄。
範例:

clean:
    rm -f *.o

print: *.c
    lpr -p $?
    touch print

objects := $(wildcard *.o)  # 讓變數展開為實際 .o 清單

3.4 檔案搜尋路徑:VPATH 與 vpath

  • VPATH:指定多個目錄,: 分隔,make 依序搜尋。
    VPATH = src:../headers
  • vpath(關鍵字,非變數):更靈活,可針對特定模式指定目錄。
    vpath %.h ../headers:所有 .h 檔去 ../headers 找。
    可多條疊加,優先序遞增。

3.5 偽目標

非真實檔案,僅作標籤(如 clean、all)。需配合 .PHONY 宣告:

.PHONY: clean
clean:
    rm *.o temp

進階用法:all 依賴多個執行檔,一次建置多目標;偽目標可串聯(cleanall: cleanobj cleandiff)。

3.6 多目標規則

多個目標共用依賴與命令,搭配自動變數 $@(目標集合):

bigoutput littleoutput : text.g
    generate text.g -$(subst output,,$@) > $@

等價於分別定義兩條規則,$@ 依序取值 bigoutput、littleoutput。

3.7 靜態模式規則

語法:targets: target-pattern: prereq-patterns
範例:

objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
    $(CC) -c $(CFLAGS) $< -o $@

展開為 foo.o: foo.c、bar.o: bar.c。配合 filter 可分組處理不同副檔名。

3.8 自動生成依賴關係

大型專案手寫 .h 依賴極難維護。利用編譯器 -M/-MM 選項自動產生:

gcc -MM main.c
# 輸出:main.o: main.c defs.h

GNU 建議:為每個 .c 產生對應 .d 檔存放依賴,再 include 進主 Makefile。模式規則:

%.d: %.c
    @set -e; rm -f $@; \
    $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
    sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$

主 Makefile:

sources = foo.c bar.c
include $(sources:.c=.d)

實現依賴關係隨原始碼自動更新。

第 4 章:命令撰寫細節

4.1 顯示控制

  • @ 前綴:不顯示命令本身,僅輸出結果(常用於 echo 提示)。
  • make -n / --just-print:僅顯示命令不執行,適合除錯。
  • make -s / --silent:全域靜默模式。

4.2 執行語境

每條命令獨立在新 Shell(預設 /bin/sh)執行。若需上一條命令影響下一條(如 cd),必須同行用分號隔開:

exec:
    cd /home/hchen; pwd

4.3 錯誤處理

  • 命令回傳非 0 → make 停止該規則,可能終止所有規則。
  • - 前綴:忽略該命令錯誤(如 -rm -f *.o)。
  • make -i / --ignore-errors:全域忽略錯誤。
  • .IGNORE 目標:該規則內所有命令忽略錯誤。
  • make -k / --keep-going:規則出錯則跳過,繼續其他規則。

4.4 巢狀執行 make

大型專案常將子目錄獨立 Makefile,總控 Makefile 呼叫:

subsystem:
    cd subdir && $(MAKE)
# 等價於
subsystem:
    $(MAKE) -C subdir
  • 用 $(MAKE) 而非直接 make,利於傳遞參數。
  • export variable 傳遞變數給子 make;unexport 阻擋。
  • MAKEFLAGS 自動傳遞(含參數),-C -f -h -o -W 除外。
  • -w / --print-directory:顯示進出目錄資訊,配合 -C 自動開啟。

4.5 命令包

重複命令序列可定義為「命令包」:

define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef

foo.c : foo.y
    $(run-yacc)

使用時如同變數 $(run-yacc),$^、$@ 等自動變數依規則語境解析。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved