make學習三:書寫規則

系列文章目錄

Make學習一:make初探

Make學習二:makefile組成要素

文章目錄

  • 系列文章目錄
  • 前言
  • 默認目標
  • 規則語法
  • order-only prerequisites
  • 文件名中的通配符
  • 偽目標 Phony Targets
  • 沒有 Prerequisites 和 recipe
  • 內建特殊目標名
  • 一個目標多條規則或多個目標共享 prerequisites
  • 自動生成依賴
  • 總結


前言

前文 Make學習二:makefile組成要素 中提到,Makefile 組成中的重要成員就是顯示規則。本文重點在編寫規則上。

顯示規則的格式如下:

target ... : prerequisites ...recipe...

引用前面 Make學習一:make初探 中的話,簡單來說,一條規則就像一道菜。如果說 target 是我們的西紅柿炒雞蛋,那 prerequisites 就是西紅柿炒雞蛋的依賴,或者說輸入,簡單點就是西紅柿和雞蛋。而 recipe 就是西紅柿炒雞蛋的菜譜,也就是要做成這道菜,需要什么步驟,每一步要怎么做。 實際上,recipe 確實是菜譜的意思,prerequisites 意為先決條件。

默認目標

每一條規則在 Makefile 中的 書寫順序一般不影響功能,但會決定默認目標(default goal),即如果你運行 make 沒有指定目標,默認會構建的那個目標。

默認目標的規則是:取第一個 Makefile 里第一條規則的第一個 target。

因此,通常會把整個程序編譯的規則寫在 Makefile 最前面,這個規則的目標經常叫做 all。類似,make 的約定。

如下示例,直接運行 make 輸出 Hello, world!:

all: hellohello:@echo "Hello, world!"

規則語法

一條規則的基本寫法是:

target ... : prerequisites ...recipe...

targets 一般是文件名,可以用空格分隔多個目標。也可以用 通配符(wildcards)。
配方(recipe)行必須以 Tab 開頭(這一點非常重要!)。 也可以通過設置 .RECIPEPREFIX 變量來改成別的符號

一條規則可以有:

  • 多個依賴(prerequisites);
  • 多個目標(targets);
  • 也可以沒有依賴(比如 .PHONY 規則);
  • 或者沒有 recipe(表示目標是偽目標或不需要命令)。

多個依賴示例:

edit: main.o kbd.o command.o display.ogcc -o edit main.o kbd.o command.o display.o

多個目標示例:

clean temp:rm -f *.o temp

這里 clean 和 temp 是 兩個目標,共享同一個配方。

長行可以用 \ 續行,但 recipe 不需要續行,必須每行一個 Tab。

規則的作用:如果目標不存在或目標的修改時間早于任何一個依賴,則按照 recipe 去更新它們

order-only prerequisites

有時你會想要某個依賴在目標之前被構建,但不希望因為這個依賴的更新導致目標被重建。這就需要用 order-only prerequisite(僅順序依賴)。

簡單一句話概括:該依賴必須存在,但同時它的更新不能影響到 target 的更新。那么你就需要 order-only prerequisite

寫法:target : normal-prerequisites | order-only-prerequisites 使用管道符把 order-only-prerequisites 放在右邊即可。

例如:我們想把當前目錄下的 foo.c 和 bar.c 文件構建出來的 foo.o 和 bar.o 放在 obj 目錄中。但這個目錄可能一開始不存在。我們需要:先保證 obj/ 目錄存在;但不要因為 obj/ 目錄變化而導致目標重建。則 Makefile 的內容如下所示:

OBJDIR := obj
OBJS := $(OBJDIR)/foo.o $(OBJDIR)/bar.oall: $(OBJS)# 創建 obj/ 目錄(order-only)
$(OBJDIR)/%.o : %.c | $(OBJDIR)@echo "Compiling $< to $@"@cp $< $@# 僅順序依賴:保證 obj/ 目錄存在,但不影響重建判斷
$(OBJDIR):@echo "Creating directory $@"@mkdir -p $@

第一次執行 make,輸出如下:

Creating directory obj
Compiling foo.c to obj/foo.o
Compiling bar.c to obj/bar.o

即便我們在 obj 目錄下通過命令touch obj/temp創建文件,執行 make 依舊不會有任何更新:make: 對“all”無需做任何事。

文件名中的通配符

在 Makefile 里,一個文件名可以使用通配符(wildcard characters)來匹配多個文件。支持的通配符和 Linux shell 里的一樣:* 匹配 任意長度的任意字符,? 匹配任意單個字符,[abc] 匹配括號中任意一個字符。~(波浪號)在文件名開頭(單獨出現或后跟 /)代表當前用戶根目錄。

一條規則分 target,prerequisite 和 recipe。target 和 prerequisite 中的 通配符由 make 來展開。而 recipe 中的通配符會把命令傳遞給 shell 由 shell 來處理。

例如:以下 Makefile 執行 make 之后會打印出所有更新之后的 .c 文件內容。$? = 依賴中比目標 print 新的文件。

print: *.c@echo $?

但變量中的通配符不會自動展開,需要使用函數 $(wildcard)。 例如:objects = *.o 中變量賦值并不會展開,objects 只是 “*.o” 字符串。

此時,當我們通過以下 Makefile 來執行 make 命令:

objects = *.ofoo : $(objects)cc -o foo $(CFLAGS) $(objects)

若當前目錄下沒有任何 .o 文件,則 *.o 就不會展開,則會出錯:make: *** 沒有規則可制作目標“*.c”,由“*.o” 需求。 停止。

為了解決這個問題,則需要在變量定義時則進行通配符展開為文件名列表。使用 wildcard 函數,格式:$(wildcard pattern...)

objects := $(wildcard *.o)
foo : $(objects)  cc -o foo $(CFLAGS) $(objects)

若當前目錄下沒有任何 .o 文件,則 *.o 仍舊會展開,只是變量 objects 為空字符串。報錯:cc -o foo cc: fatal error: no input files

偽目標 Phony Targets

所謂 偽目標(phony target),指的是根本就不是某個實際文件名的目標,而是一個用于明確執行某個配方(recipe)的名字。

使用偽目標的兩個主要原因是:避免與同名文件沖突 和 提高性能。例如,如下 Makefile 內容:

clean:rm *.o temp

上面例子中,clean 不是實際會被創建的文件。因此,每次運行 make clean 時,都會執行 rm 命令。

如果當前目錄里真有一個名字叫 clean 的文件,make clean 就會誤以為 clean 已是最新,不執行刪除操作

使用 .PHONY: clean 后,clean 的配方 總是會執行,不會被任何文件阻止。

沒有 Prerequisites 和 recipe

如果一個規則 既沒有依賴(prerequisite)也沒有配方(recipe),并且它的目標文件不存在,那么每次 make 運行時都會認為這個目標已經“更新”。這意味著:所有依賴于這個目標的規則,每次都會執行它們自己的配方。

這個機制常用于強制重新構建(不論文件是否真正變化)。例如以下示例:

clean: FORCErm $(objects)FORCE:

內建特殊目標名

有一些目標名稱如果出現在 Makefile 中,會具有特殊的含義。這些名字通常以 . 開頭。

.PHONY(偽目標聲明),.PHONY 后面列出的所有目標都被認為是偽目標(phony targets)。

.SUFFIXES 目標后面列出的是用于后綴規則 suffix rules的后綴列表。可以用 .SUFFIXES: 清除所有后綴規則(這樣就不會自動匹配如 .c → .o 這些規則了)。

.DEFAULT(沒有匹配規則時的最后手段),如果一個目標沒有任何匹配的規則(無顯式也無隱式規則),就會使用 .DEFAULT 提供的配方。

.DEFAULT:@echo "No rule for $@"

等等等等

一個目標多條規則或多個目標共享 prerequisites

例如:kbd.o command.o files.o: command.h 這條 Makefile 內容中,多個目標同時依賴同一個頭文件。此時可以用 $@ 來區分當前到底是哪個目標。

一個文件可以同時是多條規則的目標(target)。來自不同規則的所有依賴(prerequisites)會合并成一個總的依賴列表。只要目標比任何一個依賴舊,就會執行配方(recipe)。

例如以下 Makefile 的內容:

objects = foo.o bar.ofoo.o : defs.hbar.o : defs.h test.h$(objects) : config.h

foo.o 的依賴會合并成一個列表:config.h + defs.h

自動生成依賴

在程序的 Makefile 中,你通常需要寫很多這種規則:main.o: defs.hmain.c 里用了 #include "defs.h",那么你就得寫 main.o: defs.h

你寫這個規則是為了讓 make 知道:如果 defs.h 變了,就得重新編譯 main.o。如果你的程序很大,#include 很多,每次增加或刪掉頭文件都得手動改 Makefile,非常麻煩,容易出錯。

為了解決這個問題,現代 C 編譯器可以自動幫你生成這些依賴規則。它們會掃描你的源碼里的 #include 語句,通常使用 -M 選項來實現。例如:cc -M main.c 它會輸出:main.o : main.c defs.h 所以你不再需要自己寫這些規則,編譯器會幫你搞定。

總結

完結撒花!!

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

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

相關文章

網絡安全技能大賽B模塊賽題解析Server12環境

已知靶機存在?站系統&#xff0c;使?Nmap?具掃描靶機端?&#xff0c;并將?站服務的端?號作為Flag &#xff08;形式&#xff1a;Flag字符串&#xff09;值提交 使用nmap掃描目標靶機網站服務的端口號為8089 Falg&#xff1a;8089 訪問?站/admin/pinglun.asp??&#…

1、Linux操作系統下,ubuntu22.04版本切換中英文界面

切換中英文界面的方法很多&#xff0c;我也是按照一個能用的方法弄過來并且記錄&#xff0c; 1.如果剛開始使用Ubuntu環境&#xff0c;桌面的語言環境為英文&#xff0c;需要安裝中文簡體的字體包 打開桌面終端&#xff0c;輸入 sudo apt install language-pack-zh-hans lan…

SmolVLM2: The Smollest Video Model Ever(六)

繼續微調 微調視頻的代碼如下&#xff1a; # 此Python文件用于對SmolVLM2進行視頻字幕任務的微調 # 導入所需的庫 import os os.environ["CUDA_VISIBLE_DEVICES"] "1" import torch from peft import LoraConfig, prepare_model_for_kbit_training, get…

Spring Boot安裝指南

&#x1f516; Spring Boot安裝指南 &#x1f331; Spring Boot支持兩種使用方式&#xff1a; 1?? 可作為常規Java開發工具使用 2?? 可作為命令行工具安裝 ?? 安裝前提&#xff1a; &#x1f4cc; 系統需安裝 Java SDK 17 或更高版本 &#x1f50d; 建議先運行檢查命令…

數據結構(七)---鏈式棧

#### 鏈式棧實現 ##### linkstack.h #ifndef _LINKSTACK_H #define _LINKSTACK_H // 引入相關的庫文件 #include <stdio.h> #include <stdlib.h> #include <string.h> // 定義元素類型的別名 typedef int DATA; //定義鏈式棧節點 typedef struct node { …

【Spring Boot】Maven中引入 springboot 相關依賴的方式

文章目錄 Maven中引入 springboot 相關依賴的方式1. 不使用版本管理&#xff08;不推薦&#xff09;2、使用版本管理&#xff08;推薦&#xff09;2.1 繼承 spring-boot-starter-parent2.2 使用 spring-boot-dependencies 自定義父工程2.3引入 spring-framework-bom Maven中引…

DataStreamAPI實踐原理——快速上手

引入 通過編程模型&#xff0c;我們知道Flink的編程模型提供了多層級的抽象&#xff0c;越上層的API&#xff0c;其描述性和可閱讀性越強&#xff0c;越下層API&#xff0c;其靈活度高、表達力越強&#xff0c;多數時候上層API能做到的事情&#xff0c;下層API也能做到&#x…

WPF 圖片文本按鈕 自定義按鈕

效果 上面圖片,下面文本 樣式 <!-- 圖片文本按鈕樣式 --> <Style x:Key="ImageTextButtonStyle" TargetType="Button"><Setter Property="Background" Value="Transparent"/><Setter Property="BorderTh…

驅動開發硬核特訓 · Day 22(上篇): 電源管理體系完整梳理:I2C、Regulator、PMIC與Power-Domain框架

&#x1f4d8; 一、電源子系統總覽 在現代Linux內核中&#xff0c;電源管理不僅是系統穩定性的保障&#xff0c;也是實現高效能與低功耗運行的核心機制。 系統中涉及電源管理的關鍵子系統包括&#xff1a; I2C子系統&#xff1a;硬件通信基礎Regulator子系統&#xff1a;電源…

設計模式全解析:23種經典設計模式及其應用

創建型模式 1. 單例模式&#xff08;Singleton Pattern&#xff09; 核心思想&#xff1a;確保一個類只有一個實例&#xff0c;并提供一個全局訪問點。適用場景&#xff1a;需要共享資源的場景&#xff0c;如配置管理、日志記錄等。 public class Singleton {// 靜態變量保存…

力扣熱題100題解(c++)—矩陣

73.矩陣置零 給定一個 m x n 的矩陣&#xff0c;如果一個元素為 0 &#xff0c;則將其所在行和列的所有元素都設為 0 。請使用 原地 算法。 int m matrix.size(); // 行數int n matrix[0].size(); // 列數bool firstRowZero false; // 標記第一行是否包含 0bool f…

本地部署DeepSeek-R1(Dify升級最新版本、新增插件功能、過濾推理思考過程)

下載最新版本Dify Dify1.0版本之前不支持插件功能&#xff0c;先升級DIfy 下載最新版本&#xff0c;目前1.0.1 Git地址&#xff1a;https://github.com/langgenius/dify/releases/tag/1.0.1 我這里下載到老版本同一個目錄并解壓 拷貝老數據 需先停用老版本Dify PS D:\D…

PostSwigger Web 安全學習:CSRF漏洞3

CSRF 漏洞學習網站&#xff1a;What is CSRF (Cross-site request forgery)? Tutorial & Examples | Web Security Academy CSRF Token 基本原理 CSRF Token 是服務端生成的唯一、隨機且不可預測的字符串&#xff0c;用于驗證客戶端合法校驗。 作用&#xff1a;防止攻擊…

用 Nodemon 解決 npm run serve 頻繁重啟服務

Nodemon 是一個基于 Node.js 構建的開發工具&#xff0c;專為幫助開發者自動監控項目文件的更改而設計。每當文件發生變更時&#xff0c;Nodemon 會自動重啟 Node.js 服務器&#xff0c;無需手動停止并重啟。這對于提升開發速度、減少人工操作非常有幫助&#xff0c;尤其適用于…

django admin 中更新表數據 之后再將數據返回管理界面

在Django中&#xff0c;更新數據庫中的數據并將其重新顯示在Django Admin界面上通常涉及到幾個步驟。這里我將詳細說明如何在Django Admin中更新表數據&#xff0c;并確保更新后的數據能夠立即在管理界面上顯示。 定義模型 首先&#xff0c;確保你的模型&#xff08;Model&…

真.從“零”搞 VSCode+STM32CubeMx+C <1>構建

目錄 前言 準備工作 創建STM32CubeMx項目 VSCode導入項目&配置 構建錯誤調試 后記 前言 去年10月開始接觸單片機&#xff0c;一直在用樹莓派的Pico&#xff0c;之前一直用Micropython&#xff0c;玩的不亦樂乎&#xff0c;試錯階段優勢明顯&#xff0c;很快就能鼓搗一…

C語言學習之結構體

在C語言中&#xff0c;我們已經學了好幾種類型的數據。比如整型int、char、short等&#xff0c;浮點型double、float等。但是這些都是基本數據類型&#xff0c;而這些數據類型應用在實際編程里顯然是不夠用的。比如我們沒有辦法用一旦數據類型來定義一個”人“的屬性。因此這里…

架構-計算機系統基礎

計算機系統基礎 一、計算機系統組成 &#xff08;一&#xff09;計算機系統層次結構 硬件組成 主機&#xff1a;包含CPU&#xff08;運算器控制器&#xff09;、主存儲器&#xff08;內存&#xff09;。外設&#xff1a;輸入設備、輸出設備、輔助存儲器&#xff08;外存&…

【計算機網絡性能優化】從基礎理論到實戰調優

目錄 前言技術背景與價值當前技術痛點解決方案概述目標讀者說明 一、技術原理剖析核心概念圖解核心作用講解關鍵技術模塊說明技術選型對比 二、實戰演示環境配置要求核心代碼實現案例1&#xff1a;iPerf3帶寬測試案例2&#xff1a;TCP窗口優化案例3&#xff1a;QoS流量整形 運行…

Python 自動化辦公:Excel 數據處理的“秘密武器”

引言 在日常的 IT 辦公場景里&#xff0c;Excel 是數據處理與分析的 “常勝將軍”。無論是財務人員整理賬目、銷售團隊統計業績&#xff0c;還是運營人員分析用戶數據&#xff0c;Excel 都發揮著關鍵作用。但面對海量數據&#xff0c;手動操作 Excel 不僅效率低下&#xff0c;還…