一文入門Makefile

今天我們來玩玩Makefile。

這邊是借鑒的陳皓老師的《跟我一起寫 Makefile》

pdf下載鏈接如下。

鏈接:https://pan.baidu.com/s/1woRq2nEkgzLv1o5uE0FZHg?pwd=mhrh?
提取碼:mhrh

我們之前已經算是入門了gcc,那我們的下一站就是Makefile,上一篇我們也發現了,當我們的main.c包含了自己寫的頭文件,那么編譯命令會變得很長,如果包含的頭文件很多,那么光是敲編譯命令都夠花我們半條命了。

所以也有下面這個說法。

會不會寫 makefile ,從一個側面說明了一個人是否具備完成大型工程的能力。

那么Makefile到底是做什么的呢?簡單來說,Makefile通過定義一系列編譯規則,指導make工具如何根據源文件的修改情況來執行增量編譯,從而極大地提高了開發效率,Makefile寫出來也就是一個腳本文件。

使用Makefile不僅使得編譯過程自動化,還帶來了諸多好處:

  • 自動化處理復雜的依賴關系:Makefile能夠自動處理和解析項目中各文件之間的復雜依賴關系,確保只有必要的部分被重新編譯。
  • 支持跨平臺構建:通過編寫與平臺無關的Makefile,可以在不同操作系統間實現一致的構建過程。
  • 提高開發效率:開發者只需關注源代碼的編寫,編譯過程的大部分操作都由Makefile自動完成,減少了手動操作的錯誤和時間消耗。

那么廢話不多說,我們直接開始先寫一個Makefile吧。

先別管這里的內容是什么意思,這內容我們寫到Makefile里,Makefile就是這個腳本文件的名字,沒有后綴,當然了也可以叫makefile和GNUmakefile(最好不要叫這個,因為只有GNU make才能識別)。

我們寫好之后要執行Makefile,就直接執行一個make命令。

然后就可以看到執行了一堆命令,這些命令是我們寫在Makefile里面的。

并且生成了下面一堆中間文件。

那其實大部分情況我們是不需要中間文件的,那么我們是不是也可以把刪除中間文件的命令寫進Makefile里呢?那當然是可以了,因為Makefile本質上就是腳本文件,自然是可以寫的。

然后我們執行

make clean

可以看到執行了我們寫在Makefile里面的最后的那個語句。

接下來我們開始揭秘Makefile。

我們先看第一行test:test.o,這實際上是一個規則。這段的含義是我們需要生成test(也就是目標文件),為了生成test首先需要擁有test.o(也就是依賴文件),通過執行下面首行縮進一個tab的那個命令來生成。

如果當前目錄下沒有依賴文件,那么就會在Makefile里找有沒有目標文件是依賴文件的規則。

那么前四個規則大家應該就能看懂了。

但是第五個clean它不是簡單的規則,它是一個特殊的目標,一般用來刪除編譯過程中產生的中間文件,并且我們看到它是沒有依賴的。

執行的時候就是輸入命令 make clean。

因為clean涉及到刪除,而在Linux中刪除就是真刪除了,不像Window里還能從回收站里找到,因此編寫clean的時候我們需要格外小心,但是保不齊我們會失誤,那怎么辦呢?

我們可以在make clean后面加個-n選項來模擬刪除,然后再次檢查是否出錯,此時還不是真的刪除,因此還有改正的機會。

如上可以看得出來當我們執行make clean命令加上-n選項之后,它會展示出執行的刪除命令,但是并沒有真正執行,這時我們就可以對make clean 進行檢查了。

除了直接在Makefile里寫clean,更穩健的寫法是下面這樣的。

?其中.PHONY表示后面的clean是一個偽目標,并不是正常的目標。并且在 rm 前面加上了一個 - ,這表示當我們刪除某些不存在的文件的時候忽略報錯,接著往后執行。

當然還有個潛規則就是clean放在Makefile的最后面。

接著再回到我們的第一版最簡單的Makefile,它是由多個規則構成的,每個規則里有目標文件,依賴文件和執行命令。在Makefile里,它默認會把第一個規則中的目標文件當成是最終目標,也就是說它不一點會執行每個規則,而是只要能夠生成最終目標就行。

要指定最終目標也是可以的,在Makefile開頭加上ALL:最終目標即可。像下面這樣,實際上不一定非得叫ALL,原理就是Makefile會以第一個規則為最終目標,那么我放在第一行的那就是最終目標。

上面就是Makefile最基礎最基礎的內容了。

那接下來我們就可以更進一步了。

既然Makefile是腳本文件,那么它有變量也是正常的對叭。

一般寫在文件開頭,格式就是? 變量名 = 變量值 ,當我們使用時是這樣的 $(?變量名 ),這邊需要提的是Makefile中的變量比較接近于宏定義,也就是直接替換的,沒有類型。

光講可能比較抽象,我們直接看下面的例子。

上面例子把目標文件直接賦值給了變量targetFile,我們在規則中可以使用$(targetFile)的方式取出test的這個值去使用,當然了上面只是簡單地做個示范,我們還可以拿變量去裝一些更復雜的東西,比如說一堆的文件(沒錯,一個變量能裝的可不止一個文件,用空格隔開可以裝多個)

那我們要給變量賦值賦上一堆文件,除了一個個手敲上去,還有別的更省力的方式那就是通配符。

比如說我們需要給一個變量賦值當前目錄下所有的 .c 文件,那么我們用通配符就可以非常方便的賦值。

大家想的是下面這樣的對叭。

cfile = *.c

但是實際上這樣是不行的,這樣在Makefile中,cfile這個變量的值就是 *.c ,而不是我們想要的所有 .c 文件,這時候就需要用上函數了,既然Makefile是腳本文件,那么有個函數也是很正常的對叭。

正確的寫法是下面這樣的。

cfile = $(wildcard *.c)

這個函數就是 $(wildcard? ),在括號里,wildcard后寫的通配符就會被展開,就會是我們想要的效果了。

除了上面我們自己去寫變量,Makefile里有一些自動化變量我們可以直接去使用的,我們挑幾個常用的來說。

$@ : 本條規則中的目標

$< : 本條規則中第一個依賴,如果第一個依賴代表多個文件(后面會說怎么實現),那么$<就表示為將這多個文件一個個取出來。

$^ :本條規則中的所有依賴,用空格分隔。

$* : 可以簡單理解成目標文件的名字但是不含后綴名。

$??:比目標更新的依賴,也就是最后修改時間比目標更晚的依賴。

用了上面的自動化變量,那么我們第一版的Makefile可以寫成下面這樣了。

現在我們改一改我們的test.c,像下面這樣。包含了自己寫的頭文件,用了里面的方法。

對應的在Makefile里也需要修改。我們添加了對于mytool1的編譯。

是可以正常make的。

然后我們發現,后面兩個規則中的命令是一樣的,那么實際上我們是可以對他們進行一個合并操作的,也就是說在一個規則里,可以有多個目標。可以像下面這樣改寫,但是由于gcc -o選項只能輸出一個文件,因此在命令里不能使用 $@,但是還好-c選項可以不指定輸出文件,默認就是輸入文件的后綴改成 .o當輸出文件了。

也是可以正常make的。

這個例子主要是想說在Makefile中,在一個規則里可以有多個目標。然后同一個目標也可以有多個規則(這邊不演示了)。

然后實際上我們還有種更簡單的寫法,那就是模式規則。

這邊直接貼出怎么寫的。

看不懂什么意思沒關系,我們make一下,make之后會顯示出它執行的命令,然后我們再分析分析。

看的出來Makefile直接幫我們把我們需要的依賴都執行了。

這是因為上面的%可以看成是任意的東西(類似于通配符中的*),當我們的目標的依賴不存在的時候,Makefile就會在規則中尋找是否目標的依賴和哪個規則的目標一致,找到就會執行這個規則中的命令。就比如說我們上面的Makefile,需要尋找目標為test.o以及目標為mytool1.o的依賴,最終都找到了 %.o:%.c 這個規則,因為%代表任意東西,那么自然是可以匹配上的,這就是模式規則強大的地方,我們可以少寫很多東西了。

不過上面這種寫法有一個可以改進的點,那就是將$^改成$<,我們來看看二者的區別。

我們上面的例子簡單,所以沒什么差別,但是當我們的依賴變多之后,依賴嵌套依賴之后,我們就得用$<了。

使用模式規則也有個弊端,那就是模式規則會匹配所有符合條件的,因為%可以匹配任意東西,因此只要后綴符合,都會被我們的模式規則匹配到,如果我只有一部分的文件需要用模式規則中的命令怎么辦呢?

這就要用到我們的靜態模式規則了。

在模式規則的基礎上,在前面加上需要匹配到的目標即可。

可能有小伙伴會說,這樣不就又繞回去了嘛,還是要把目標的名字都寫出來。那其實我們可以再靜態模式規則的后面再寫個模式規則,而只在靜態模式規則中寫上需要特殊處理的目標即可,剩下的可以都丟給后面的模式規則去兜底。

一般情況下我們不會將工具文件和我們的主文件放在同一級目錄下,因此我新建個文件夾my_tools,然后將工具文件放進去,接著我們就毫不意外地發現Makefile找不到庫文件了。

我們可以在Makefile中指定頭文件目錄,指定的方式也很簡單,就像下面這樣。

VPATH = 路徑1:路徑2……

可以指定多個路徑,用冒號:隔開即可。

指定完之后仍然報錯,但這就是鏈接階段報的錯誤了。

我們需要在規則的命令中添加頭文件路徑。

最終是下面這樣的。

可以正常運行,在Makefile執行規則命令的時候也幫我們把頭文件路徑加上去了。

VPATH是一個特殊變量,指明了這個特殊變量,當Makefile找不到依賴文件,也沒有目標是依賴的規則,那么Makefile就會到VPATH指明的路徑去尋找。

除了VPATH還有vpath,也就是小寫的,但是vpath不是變量,而是關鍵字,使用可以參考下面這樣。

看的出來vpath一次只能添加一條路徑,但是和VPATH相比的話,vpath可以更細致地指定路徑,可以給不同后綴的文件指定不同的路徑。

至此我們就算是入門Makefile成功了,相信各位小伙伴已經能夠給自己以前寫的小項目編寫(相對)高效的Makefile了。

如果想進一步了解Makefile的話,最好的方法是去看官方文檔,當然了,也可以看看陳皓老師寫的《跟我一起寫 Makefile》,下載鏈接在本文的開頭。

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

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

相關文章

http和https請求總結

http請求是不安全的請求的端口是80&#xff0c;https請求是安全的請求的端口是443 但是請求安全也不是絕對的。 要想先了解https就的先說幾個概念 1、證書 2、加密算法 openssl TLS/SSL 3、協議x509協議 http傳輸數據都是明文&#xff0c;在數據傳輸的過程會經過很長的鏈路…

C#面: 能夠將非靜態的方法覆寫成靜態方法嗎?

在C#中&#xff0c;不能將非靜態方法覆寫成靜態方法。這是因為靜態方法是屬于類的&#xff0c;而非靜態方法是屬于類的實例的。覆寫&#xff08;重寫&#xff09;是指在派生類中重新實現基類中的虛方法或抽象方法&#xff0c;以改變其行為。而靜態方法是無法被派生類所繼承的&a…

嵌入式學習(Day 51:ARM指令/匯編與c語言函數相互調用)

1.Supervisor模式與SVC模式 Supervisor模式是ARM處理器的一個特權工作模式&#xff0c;允許執行特權指令和訪問特權資源。SVC模式&#xff08;Supervisor Call&#xff09;是與Supervisor模式相關的一個功能或指令&#xff0c;用于從用戶模式切換到Supervisor模式&#xff0c;…

1、Redis系列-Redis高性能原理詳解

Redis高性能原理詳解 Redis是一款高性能的內存數據庫&#xff0c;廣泛應用于需要快速讀寫訪問的數據密集型應用中。它的高性能得益于多方面的設計和優化。以下是Redis高性能實現的詳細解釋&#xff1a; 1. 單線程架構 Redis采用單線程架構來處理客戶端請求&#xff0c;這與傳…

服務器流量收發測試-續篇

文章目錄 一、概述二、普通java工程1&#xff0c;pom文件2&#xff0c; 定時任務3&#xff0c;打包4&#xff0c;jar運行 三、打包docker鏡像1&#xff0c;鏡像打包配置docker環境&#xff1a;2&#xff0c;連接遠程鏡像倉庫 四、部署運行1. 容器運行2. 單容器多次運行jar3. 容…

大模型應用研發基礎環境配置(Miniconda、Python、Jupyter Lab、Ollama等)

老牛同學之前使用的MacBook Pro電腦配置有點舊&#xff08;2015 年生產&#xff09;&#xff0c;跑大模型感覺有點吃力&#xff0c;操作起來有點卡頓&#xff0c;因此不得已撿起了塵封了快兩年的MateBook Pro電腦&#xff08;老牛同學其實不太喜歡用 Windows 電腦做研發工作&am…

04_記錄鎖

記錄鎖&#xff08;Record Lock&#xff09; 文章目錄 記錄鎖&#xff08;Record Lock&#xff09;簡介原理加鎖流程鎖類型使用場景示例與其他鎖的對比結論 簡介 MySQL 中的記錄鎖&#xff08;Record Lock&#xff09;是行級鎖的一種&#xff0c;用于鎖定數據庫表中的特定行。…

從零開始做題:老照片中的密碼

老照片中的密碼 1.題目 1.1 給出圖片如下 1.2 給出如下提示 這張老照片中的人使用的是莫爾斯電報機&#xff0c;莫爾斯電報機分為莫爾斯人工電報機和莫爾斯自動電報機&#xff08;簡稱莫爾斯快機&#xff09;。莫爾斯人工電報機是一種最簡單的電報機&#xff0c;由三個部分組…

SelfReg-UNet:解決UNet語義損失,增強特征一致性與減少冗余的優化模型

SelfReg-UNet&#xff1a;解決UNet語義損失&#xff0c;增強特征一致性與減少冗余的優化模型 提出背景拆解類比&#xff1a;整理書架語義一致性正則化內部特征蒸餾為什么 UNet 會有語義損失&#xff1f; 提出背景 論文&#xff1a;https://arxiv.org/pdf/2406.14896 代碼&…

c++內存管理_復習

new與placement new new&#xff1a; 先調用operator new(大小)&#xff0c;而operator new()會調用malloc嘗試分配內存&#xff0c;失敗則調用_callnewh()來釋放內存&#xff0c;直至分配成功 可以設置分配失敗的處理函數&#xff1a;將寫好的處理函數作為參數傳入set_new_han…

Vue3 使用 Vue Router 時,params 傳參失效

前言&#xff1a; 在寫項目的時候&#xff0c;使用了 vue-router 的 params 進行傳參&#xff0c;但是在詳情頁面中一直獲取不到參數。原因&#xff1a;Vue Router 在2022-8-22的那次更新后&#xff0c;使用這種方式在新頁面上無法獲取&#xff01; 正文&#xff1a; 在列表頁進…

deeplabcut

import pandas as pd import h5py import pickle import json import os # 讀取 CSV 文件 csv_file_path /mnt/data/CollectedData_dlc.csv csv_data pd.read_csv(csv_file_path) # 讀取 H5 文件 h5_file_path /mnt/data/CollectedData_dlc.h5 with h5py.File(h5_file_pat…

LeetCode題練習與總結:只出現一次的數字Ⅱ--137

一、題目描述 給你一個整數數組 nums &#xff0c;除某個元素僅出現 一次 外&#xff0c;其余每個元素都恰出現 三次 。請你找出并返回那個只出現了一次的元素。 你必須設計并實現線性時間復雜度的算法且使用常數級空間來解決此問題。 示例 1&#xff1a; 輸入&#xff1a;n…

K8S日常運維手冊

Kubernetes&#xff08;簡稱 K8S&#xff09;是一種廣泛使用的容器編排平臺&#xff0c;能夠自動化部署、擴展和管理容器化應用。對于運維人員來說&#xff0c;掌握 Kubernetes 的日常運維技能是確保系統穩定運行的關鍵。本文將介紹一些 Kubernetes 日常運維的基本操作與技巧&a…

虛擬機裝入kali linux

VMware 首先需要先安裝VMware Workstation Pro可以根據這篇文章來下載VMware 下載kali linux Installer Images VS Virtual Machines Installer Images&#xff08;安裝鏡像&#xff09;Virtual Machines&#xff08;虛擬機&#xff09; 直接訪問硬件&#xff0c;定制內核…

Matlab|【防騙帖】考慮時空相關性的風電功率預測誤差建模與分析

目錄 1 主要內容 2 部分程序 3 下載鏈接 1 主要內容 這個程序《考慮時空相關性的風電功率預測誤差建模與分析》畫的圖片非常漂亮&#xff0c;和原文獻基本一致&#xff0c;但是實際上內容并未實現出來&#xff0c;主要就是利用現有的風電預測的數據和結果做了相關的圖&#…

【數據結構】(C語言):鏈表

鏈表&#xff1a; 基本單位是節點。節點至少兩部分&#xff1a;數據&#xff0c;下一個數據的地址。頭指針head&#xff0c;始終指向鏈表的第一個節點。若沒有節點&#xff0c;則headNULL。鏈表在內存中是非連續的。不能使用索引&#xff08;下標&#xff09;查找元素。只能從…

解決:Xshell通過SSH協議連接Ubuntu服務器報“服務器發送了一個意外的數據包,received:3,expected:20”

下圖所示&#xff1a; 日志也基本看不出來問題在哪&#xff0c;只是說斷開了連接大概是驗證失敗。有幸在某論壇評論區找到了原因&#xff0c;是因為我的xshell版本太低了而服務器的ssh版本太高&#xff0c;高版本的ssh默認屏蔽了一部分不太安全的算法導致建立連接的時候驗證失敗…

C++ 14新特性個人總結

variable templates 變量模板。這個特性允許模板被用于定義變量&#xff0c;就像之前模板可以用于定義函數或類型一樣。變量模板為模板編程帶來了新的靈活性&#xff0c;特別是在定義泛化的常量和元編程時非常有用。 變量模板的基本語法 變量模板的聲明遵循以下基本語法&am…

解決Vue+Vite打包后Leaflet的marker圖標不顯示的問題

前言 用Leaflet寫關于WebGIS的開發&#xff0c;用Vite或者webpack打包&#xff0c;打包后會找不到圖標&#xff0c;如下所示。 直言的說&#xff0c;筆者去網上搜了搜&#xff0c;其實收到一個比較好是答案。網址如下。 &#xff08;完美解決~&#xff09;關于VueLeaflet添加…