Linux 入門五:Makefile—— 從手動編譯到工程自動化的蛻變

一、概述:Makefile—— 工程編譯的 “智能指揮官”

1. 為什么需要 Makefile?

  • 手動編譯的痛點:當工程包含數十個源文件時,每次修改都需重復輸入冗長的編譯命令(如gcc file1.c file2.c -o app),且無法自動識別哪些文件需要重新編譯。
  • Makefile 的核心價值:通過定義 “目標 - 依賴 - 命令” 規則,實現自動化編譯。只需執行make命令,即可根據文件修改時間智能判斷編譯順序,避免重復工作,大幅提升開發效率。
  • 本質:一個名為Makefile(或makefile)的文本文件,存儲編譯規則,由make命令解析執行。

2. 核心概念快速入門

  • 目標(Target):要生成的文件(如可執行文件app)或偽操作(如清理編譯產物的clean)。
  • 依賴(Prerequisites):生成目標所需的文件(如app依賴main.ofunc.o)。
  • 命令(Command):生成目標的具體操作,需以Tab 鍵開頭(Makefile 嚴格要求)。

二、簡單使用:從第一個 Makefile 起步

1. 創建與編輯 Makefile

# 創建文件
touch Makefile
# 用vim編輯(推薦用Visual Studio Code等IDE提升體驗)
vim Makefile

2. 編寫第一個編譯規則:編譯單文件程序

# 目標:生成可執行文件hello,依賴hello.c
hello: hello.c# 命令:用gcc編譯,-o指定輸出文件名,@禁止回顯命令本身@echo "正在編譯hello..."gcc hello.c -o hello# 偽目標:清理編譯產物,.PHONY避免與同名文件沖突
.PHONY: clean
clean:@echo "清理編譯產物..."rm -f hello  # -f強制刪除,即使文件不存在也不報錯

3. 執行 Makefile

# 編譯目標(首次執行會生成hello)
make
# 輸出:
# 正在編譯hello...
# (若命令前無@,會額外回顯"gcc hello.c -o hello")# 清理產物
make clean
# 輸出:清理編譯產物...(同時刪除hello文件)

4. 關鍵語法解析

  • 注釋#開頭的行,用于解釋規則(如# 偽目標:清理編譯產物)。
  • 自動推導:Makefile 默認知道.c文件可編譯為.o文件(如main.o依賴main.c,無需顯式書寫規則)。
  • 偽目標:用.PHONY聲明(如clean),確保即使存在同名文件,make clean也會執行命令。

三、變量:讓 Makefile 告別 “硬編碼”

1. 自定義變量:四種賦值方式對比

賦值符號特性示例適用場景
=遞歸展開(可引用后續定義的變量)CFLAGS = -Wall\nOBJECTS = $(SRCS:.c=.o)需要動態計算值的場景
:=立即展開(定義時直接計算)SRCS := $(wildcard *.c)避免遞歸引用導致的循環定義
+=追加值(在原有值后添加新內容)LIBS += -lm(追加數學庫)逐步構建復雜參數
?=惰性賦值(僅在未定義時生效)CC ?= gcc(默認用 gcc,可被命令行覆蓋)設置默認值

2. 自動變量:依賴文件的 “快捷引用”

在模式規則(如%.o: %.c)中,自動變量可簡化代碼:

變量含義示例(目標main.o依賴main.c
$@當前目標文件名命令中$@代表main.o
$<第一個依賴文件命令中$<代表main.c
$^所有依賴文件(去重)依賴a.c b.c時,$^代表a.c b.c
$?比目標新的依賴文件僅重新編譯修改過的文件

示例:多文件編譯(使用自動變量)

CC := gcc          # 立即賦值,指定編譯器
CFLAGS := -Wall -g  # 編譯選項:開啟警告和調試信息
TARGET := app       # 目標文件名
SRCS := main.c func.c  # 顯式列出源文件(或用wildcard函數自動收集)
OBJS := $(SRCS:.c=.o)  # 將.c替換為.o,生成目標文件列表$(TARGET): $(OBJS)$(CC) $(OBJS) -o $(TARGET)  # 鏈接所有.o文件%.o: %.c$(CC) $(CFLAGS) -c $< -o $@  # 編譯單個.c到.o,$<是源文件,$@是目標文件.PHONY: clean
clean:@rm -f $(OBJS) $(TARGET)

3. 預定義變量:Makefile 的 “內置工具”

Makefile 自帶常用工具變量,可直接使用:

  • CC:C 編譯器(默認cc,通常設為gcc)。
  • AR:歸檔工具(用于靜態庫,默認ar)。
  • RM:刪除命令(默認rm -f,自動添加-f強制刪除)。
  • CXX:C++ 編譯器(默認g++)。

示例:使用預定義變量

main.o: main.c$(CC) -c main.c -o main.o  # 等價于`gcc -c main.c -o main.o`(若CC=gcc)

四、函數:讓 Makefile 更 “聰明”

1. 文件搜索函數:wildcard

  • 功能:按模式匹配文件,返回匹配的文件列表(支持通配符*)。
  • 語法$(wildcard 模式),如$(wildcard src/*.c)獲取src/目錄下所有.c文件。
  • 示例:自動收集所有源文件
    SRCS := $(wildcard *.c)  # 收集當前目錄所有.c文件
    OBJS := $(SRCS:.c=.o)     # 轉換為.o文件列表app: $(OBJS)$(CC) $(OBJS) -o app
    

2. 字符串替換函數:patsubst

  • 功能:按指定模式替換字符串中的部分內容。
  • 語法$(patsubst 原模式, 新模式, 字符串),如$(patsubst %.c, %.o, a.c b.cpp)a.o b.o(需配合手動處理.cpp 文件)。
  • 示例:靈活處理混合格式源文件
    SRCS := a.c b.cpp c.c
    # 分別將.c和.cpp轉換為.o(需分步處理)
    C_OBJS := $(patsubst %.c, %.o, $(filter %.c, $(SRCS)))
    CPP_OBJS := $(patsubst %.cpp, %.o, $(filter %.cpp, $(SRCS)))
    OBJS := $(C_OBJS) $(CPP_OBJS)
    

五、選項:擴展 make 命令的能力

1.?-f:指定非默認Makefile

  • 場景:項目存在多個 Makefile(如Makefile.linuxMakefile.win),需顯式指定。
  • 用法
    make -f Makefile.linux  # 執行指定文件中的規則,而非默認的Makefile
    

2.?-C:切換目錄執行

  • 場景:工程分模塊存放(如src/lib/目錄各有獨立 Makefile)。
  • 用法
    # 總控Makefile,編譯所有模塊
    all:@make -C src  # 進入src目錄,執行該目錄下的Makefile@make -C lib  # 進入lib目錄,執行該目錄下的Makefile.PHONY: clean
    clean:@make -C src clean  # 清理src模塊@make -C lib clean  # 清理lib模塊
    

3. 其他實用選項

選項含義示例
-n干運行,僅打印命令不執行(調試用)make -n?查看編譯步驟是否正確
-s靜默模式,不回顯命令(僅顯示輸出)make -s?隱藏編譯命令,輸出更簡潔
-j N并行編譯,N 為線程數(加快多核 CPU 編譯速度)make -j 4?使用 4 個線程編譯

六、實戰模板:三種常用 Makefile 寫法

模板一:生成可執行文件(多文件編譯)

# 一、變量定義
CC := gcc              # C編譯器
CFLAGS := -Wall -g -Iinclude  # 編譯選項:警告+調試+頭文件路徑
SRCS := $(wildcard src/*.c)  # 自動收集src目錄下所有.c文件
OBJS := $(patsubst src/%.c, obj/%.o, $(SRCS))  # 生成obj/目錄下的.o文件# 二、目標規則
# 1. 最終目標:生成可執行文件app
app: $(OBJS)@echo "鏈接生成可執行文件..."$(CC) $(OBJS) -o app -Llib -lm  # -L指定庫路徑,-lm鏈接數學庫# 2. 模式規則:src/xxx.c → obj/xxx.o(自動創建obj目錄)
obj/%.o: src/%.c@mkdir -p obj  # 確保obj目錄存在$(CC) $(CFLAGS) -c $< -o $@# 三、偽目標
.PHONY: clean
clean:@echo "清理編譯產物..."@rm -f app $(OBJS)  # 刪除可執行文件和所有.o文件@rm -rf obj  # 刪除obj目錄

模板二:生成動態庫(.so 文件)

# 一、變量定義
SO_NAME := libmylib.so  # 動態庫名稱
CC := gcc
CFLAGS := -fPIC -Wall  # -fPIC生成位置無關代碼(動態庫必需)
SHLIB_FLAGS := -shared  # 生成動態庫的關鍵選項
SRCS := $(wildcard src/*.c)
OBJS := $(SRCS:.c=.o)# 二、目標規則
# 1. 生成動態庫
$(SO_NAME): $(OBJS)$(CC) $(SHLIB_FLAGS) $(OBJS) -o $(SO_NAME)# 2. 編譯.o文件(與可執行文件規則類似)
%.o: %.c$(CC) $(CFLAGS) -c $< -o $@# 三、偽目標
.PHONY: clean
clean:@rm -f $(OBJS) $(SO_NAME)

模板三:生成靜態庫(.a 文件)

# 一、變量定義
A_NAME := libmylib.a  # 靜態庫名稱
AR := ar rcs          # ar命令參數:r(添加)c(創建)s(生成索引)
CC := gcc
CFLAGS := -Wall
SRCS := $(wildcard src/*.c)
OBJS := $(SRCS:.c=.o)# 二、目標規則
# 1. 生成靜態庫(打包所有.o文件)
$(A_NAME): $(OBJS)$(AR) $(A_NAME) $(OBJS)  # 將.o文件打包成靜態庫# 2. 編譯.o文件
%.o: %.c$(CC) $(CFLAGS) -c $< -o $@# 三、偽目標
.PHONY: clean
clean:@rm -f $(OBJS) $(A_NAME)

七、常見易錯點與避坑指南

  1. 命令前必須用 Tab 鍵

    • 錯誤:命令行以空格開頭,導致 “missing separator (did you mean TAB instead of 8 spaces?)” 錯誤。
    • 正確:所有命令行必須以 Tab 鍵開頭(可在編輯器中設置 Tab 為 4 個空格,但最終需確保是 Tab 符)。
  2. 偽目標未聲明.PHONY

    • 后果:若當前目錄存在名為clean的文件,make clean會認為目標已存在,不執行清理命令。
    • 正確:始終為清理等偽目標添加.PHONY: clean聲明。
  3. 變量引用格式錯誤

    • 錯誤:直接寫變量名(如CC=gcc),應使用$(CC)引用變量。
    • 正確:所有變量引用需用$(變量名)${變量名}格式。
  4. 依賴關系遺漏

    • 后果:頭文件(.h)修改后,未將其加入依賴,導致.o文件未重新編譯。
    • 正確:在規則中顯式依賴頭文件(如main.o: main.c defs.h),或利用 Makefile 自動推導(需確保頭文件包含正確)。

八、作業:從模仿到獨立編寫

1. 任務一:解析經典 Makefile

  • 下載開源項目(如nginxredis)的 Makefile,分析以下內容:
    ① 如何定義編譯選項(CFLAGSCXXFLAGS)?
    ② 靜態庫 / 動態庫的生成規則有何不同?
    ③?clean目標如何處理多層目錄的編譯產物?

2. 任務二:編寫三個萬能模板(強化版)

  • 可執行文件模板:添加對 C++ 文件的支持(.cpp文件用g++編譯),使用wildcard遞歸搜索子目錄源文件(如src/**/*.c)。
  • 動態庫模板:添加版本號(如libmylib.so.1.0.0),使用ln -s創建軟鏈接(如libmylib.so → libmylib.so.1.0.0)。
  • 靜態庫模板:支持多架構編譯(如同時生成x86arm版本),通過變量ARCH切換編譯選項。

3. 任務三:實戰復雜工程

創建一個包含以下結構的項目:

project/
├─ Makefile       # 總控Makefile
├─ src/
│  ├─ main.c
│  ├─ func.c
│  └─ Makefile     # 模塊Makefile
├─ include/
│  └─ func.h
└─ lib/           # 編譯生成的庫文件存放目錄

要求總控 Makefile 使用-C選項調用src/目錄下的 Makefile,最終在lib/目錄生成可執行文件。

總結:Makefile 讓工程編譯 “化繁為簡”

通過掌握 Makefile 的核心規則、變量、函數和選項,你將實現從手動編譯到自動化編譯的跨越。記住以下關鍵點:

  • 規則是基礎:每個目標必須明確依賴和命令,利用自動推導簡化常規編譯步驟。
  • 變量提效率:自定義變量減少重復輸入,自動變量和預定義變量提升代碼簡潔性。
  • 函數增智能wildcardpatsubst自動處理文件列表,適應復雜工程結構。
  • 選項擴場景-f-C應對多 Makefile 和分模塊編譯,-j加速編譯過程。

現在,打開你的項目,用 Makefile 替代繁瑣的手動命令,讓編譯過程從此高效、智能!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/901092.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/901092.shtml
英文地址,請注明出處:http://en.pswp.cn/news/901092.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python-Django+vue二手電子設備交易平臺功能說明

?(^_-) 上千個精美定制模板,各類成品Java、Python、PHP、Android畢設項目,歡迎咨詢。 ?(^_-) 程序開發、技術解答、代碼講解、文檔,??文末獲取源碼+數據庫+文檔?? ??軟件下載 | 實戰案例 ??文章底部二維碼,可以聯系獲取軟件下載鏈接,及項目演示視頻。 本項目…

數據庫管理工具實戰:IDEA 與 DBeaver 連接 TDengine(二)

五、DBeaver 連接 TDengine 實戰 5.1 安裝 DBeaver 下載安裝包&#xff1a;訪問 DBeaver 官方網站&#xff08;https://dbeaver.io/download/ &#xff09;&#xff0c;根據你的操作系統選擇合適的安裝包。如果是 Windows 系統&#xff0c;下載.exe 格式的安裝文件&#xff1…

Spring Boot接口返回Long類型的數據時丟失精度的全局處理

1、問題 當實體類中的字段為Long類型時&#xff0c;通過Ajax請求返回給前段&#xff0c;在js中數據會丟失精度 直接通過postman請求或通過瀏覽器請求&#xff0c;看下響應則不會丟失精度 2、處理方式 1、使用JsonSerialize注解 JsonSerialize(using ToStringSerializer.…

英偉達Llama-3.1-Nemotron-Ultra-253B-v1語言模型論文快讀:FFN Fusion

FFN Fusion: Rethinking Sequential Computation in Large Language Models 代表模型&#xff1a;Llama-3.1-Nemotron-Ultra-253B-v1 1. 摘要 本文介紹了一種名為 FFN Fusion 的架構優化技術&#xff0c;旨在通過識別和利用自然并行化機會來減少大型語言模型&#xff08;LLM…

Django學習記錄-1

Django學習記錄-1 雖然網上教程都很多&#xff0c;但是感覺自己記錄一下才屬于自己&#xff0c;之后想找也方面一點&#xff0c;文采不佳看的不爽可繞道。 參考貼 從零開始的Django框架入門到實戰教程(內含實戰實例) - 01 創建項目與app、加入靜態文件、模板語法介紹&#xff…

Python爬蟲第7節-requests庫的高級用法

目錄 前言 一、文件上傳 二、Cookies 三、會話維持 四、SSL證書驗證 五、代理設置 六、超時設置 七、身份認證 八、Prepared Request 前言 上一節&#xff0c;我們認識了requests庫的基本用法&#xff0c;像發起GET、POST請求&#xff0c;以及了解Response對象是什么。…

Python 要致富先修路

今天準備在原有基礎上重新深入學習并記錄python學習進程。 # 整體思路 不廢話&#xff1a; 階段1&#xff1a;精選入門電子教程堅持學習&#xff1b; 階段2&#xff1a;跟著教程學習代碼思維&#xff0c;做好學習筆記并構建知識庫方便以后速查&#xff1b; 階段3&#xff…

微服務無感發布實踐:基于Nacos的客戶端緩存與故障轉移機制

微服務無感發布實踐&#xff1a;基于Nacos的客戶端緩存與故障轉移機制 背景與問題場景 在微服務架構中&#xff0c;服務的動態擴縮容、滾動升級是常態&#xff0c;而服務實例的上下線需通過注冊中心&#xff08;如Nacos&#xff09;實現服務發現的實時同步。但在實際生產環境…

2025年的Android NDK 快速開發入門

十年前寫過一篇介紹NDK開發的文章《Android實戰技巧之二十三&#xff1a;Android Studio的NDK開發》&#xff0c;今天看來已經發生了很多變化&#xff0c;NDK開發變得更加容易了。下面就寫一篇當下NDK開發快速入門。 **原生開發套件 (NDK) **是一套工具&#xff0c;使開發者能…

Shell 編程之條件語句

目錄 條件測試操作 文件測試 整數值比較 字符串比較 邏輯測試 if 條件語句 if語句的結構 1、單分支 if 語句 2、雙分支 if 語句 3、多分支 if 語句 if語句應用實例 1、單分支 if 語句應用 2、雙分支 if 語句應用 3、多分支 if 語句應用 case 分支語句 case語句的結構 case語…

【模板】縮點

洛谷p3387 思路: 算法:tarjan算法 根據題意,我們只要找到一個路徑,使得最終權重最大即可,首先,根據題目可知,如果一個點在一個環上,那么我們就將這整個環都選上,題目上允許我們能夠重復走,因此,我們可以將環縮成點,將環所稱點后,就可以轉換成樹,從沒有父節點的結點開始,我們向…

js觸發隱式類型轉換的場景

JavaScript 的隱式類型轉換&#xff08;Implicit Type Coercion&#xff09;會在某些操作或上下文中自動觸發&#xff0c;將值從一種類型轉換為另一種類型。以下是常見的觸發場景&#xff1a; 1. 使用 &#xff08;寬松相等&#xff09;比較時 會嘗試將兩邊的值轉換為相同類型后…

c++將jpg轉換為灰度圖

c將jpg轉換為灰度圖 step1:添加依賴 下載這兩個文件&#xff0c;放在cpp同一目錄下&#xff0c;編譯生成 https://github.com/nothings/stb/blob/master/stb_image_write.h https://github.com/nothings/stb/blob/master/stb_image.hstep2:C:\Users\wangrusheng\source\repos…

python——正則表達式

一、簡介 在 Python 中&#xff0c;正則表達式主要通過 re 模塊實現&#xff0c;用于字符串的匹配、查找、替換等操作。 二、Python的re模塊 使用前需要導入&#xff1a; import re 三、常用方法 方法描述re.match(pattern, string)從字符串開頭匹配&#xff0c;返回第一個匹…

Soybean Admin 配置vite兼容低版本瀏覽器、安卓電視瀏覽器(飛視瀏覽器)

環境 window10 pnpm 8.15.4 node 8.15.4 vite 5.1.4 soybean admin: 1.0.0 native-ui: 2.38.0 小米電視 MIUI TV版本&#xff1a;MiTV OS 2.7.1886(穩定版) 飛視瀏覽器&#xff1a;https://www.fenxm.com/1220.html在小米電視安裝飛視瀏覽器可以去小紅書查安裝教程&#xff1a…

系統與網絡安全------網絡通信原理(1)

資料整理于網絡資料、書本資料、AI&#xff0c;僅供個人學習參考。 文章目錄 網絡通信模型協議分層計算機網絡發展計算機網絡功能什么是協議為什么分層郵局實例 OSI模型OSI協議模型OSI七層模型OSI七層的功能簡介 TCP/IP模型OSI模型與TCP/IP模型TCP/IP協議族的組成各層PDU設備與…

如何使用通義靈碼完成PHP單元測試 - AI輔助開發教程

一、引言 在軟件開發過程中&#xff0c;測試是至關重要的一環。然而&#xff0c;在傳統開發中&#xff0c;測試常常被忽略或草草處理&#xff0c;很多時候并非開發人員故意為之&#xff0c;而是缺乏相應的測試思路和方法&#xff0c;不知道如何設計測試用例。隨著 AI 技術的飛…

批量清空圖片的相機參數、地理位置等敏感元數據

我們在使用相機或者手機拍攝照片的時候&#xff0c;照片中都會帶有一些敏感元數據信息&#xff0c;比如說相機的型號&#xff0c;參數&#xff0c;拍攝的時間地點等等。這些信息雖說不是那么引人注意&#xff0c;但是在某些時候他是非常隱私非常重要的。如果我們將這些信息泄露…

SQL優化算法解析 | PawSQL 如何將EXISTS子查詢“秒拆“為JOIN連接

在數據庫性能調優中,子查詢優化是提升查詢效率的關鍵點之一。今天,我們將分享一個使用 PawSQL 對EXISTS子查詢進行重寫優化的案例,展示如何通過合理的SQL重寫與索引設計,實現超過487516.45%的性能提升! 一、案例分析&#xff1a;EXISTS子查詢的性能困境 這個查詢的目的是找出…

大模型day1 - 什么是GPT

什么是GPT 全稱 Generative Pre-trained Transformer 是一種基于 Transformer 架構的大規模 預訓練 語言模型&#xff0c;由OpenAI研發&#xff0c;但GPT僅僅只是借鑒了Transformer 中 Decoder 的部分&#xff0c;并且做了升級 Transformer 架構 Transformer架構 是一種用于…