Linux C++ 開發4 - 入門makefile一篇文章就夠了

来源:https://www.cnblogs.com/luoweifu/p/18366803
-Advertisement-
Play Games

1. make 和 Makefile 1.1. 什麼是make? 1.2. 什麼是Makefile? 1.3. make 與 Makefile的關係 2. Makefile的語法 2.1. 基本語法 2.2. 變數 2.3. 偽目標 2.4. 模式規則 2.5. 自動變數 2.6. 條件判斷 3. ...


Linux C++ 開發 系列的前面2篇文章,我們介紹了通過g++來編譯C++代碼。這對於HelloWorld程式或者簡單的Demo程式來說沒有問題,但對於包含多個.cpp和多個.h文件的複雜項目來說,直接用g++命令來編譯的話,將會使編譯的指令非常冗長且難於維護。這個時候我們可以考慮用makefile來構建我們的程式。

1. make 和 Makefile

1.1. 什麼是make?

make是一個自動化構建工具,廣泛應用於C/C++項目中,但也可以用於其他編程語言。它的主要功能是根據Makefile中的規則自動執行一系列命令,從而生成目標文件。make通過比較目標文件和依賴文件的時間戳來決定是否需要重新構建某個目標,從而避免了不必要的編譯,提高了構建效率。

我們安裝GCC後,應該預設就已經安裝了make,沒有沒有安裝,Ubuntu下可通過如下命令來安裝:

sudo apt update
sudo apt install make

安裝完成後,你可以通過以下命令來驗證make是否安裝成功:

make --version

1.2. 什麼是Makefile?

Makefile 是一個文本文件,定義了構建項目的規則和指令。通常定義了多條包含 目標(target)、依賴(dependency)和命令(command) 的規則。

1.3. make 與 Makefile的關係

  • Makefile 你可以理解為是自動構建的腳本,裡面通過 目標(target)、依賴(dependency)和命令(command) 定義了規則,告訴make工具要如何一步步構建我們的最終目標。
  • make 是一個命令工具,是一個解釋並執行Makefile中指令的命令工具,按照Makefile制定的規則,構建最終的目標產物。

file

2. Makefile的語法

2.1. 基本語法

Makefile的基本語法如下:

目標: 依賴
    命令
  • 目標: 通常是需要生成的文件名,也可以是某個操作(如clean)。
  • 依賴: 生成目標文件所依賴的其他文件或其他目標。
  • 命令: 生成目標所需執行的shell命令,必須以Tab鍵開頭。

註意: 命令前面必須是tab鍵,表示命令的開始。不能用4個空格或者兩個空格。

2.2. 變數

在Makefile中,可以使用變數來簡化規則的編寫。變數定義如下:

變數名 = 值

使用變數時,需要在變數名前加上$符號,並用括弧括起來:

$(變數名)

2.3. 偽目標

偽目標是一種特殊的目標,它不代表具體的文件,通常用於執行某些操作。偽目標需要使用.PHONY聲明:

.PHONY: clean
clean:
    rm -f hello hello.o

2.4. 模式規則

模式規則允許定義通用的規則,適用於多個目標。例如:

%.o: %.c
    $(CC) $(CFLAGS) -c $<

這條規則表示所有.o文件都依賴於對應的.c文件,並且使用相同的編譯命令。

2.5. 自動變數

make提供了一些自動變數,用於簡化命令的編寫:

  • $@:表示目標文件名。
  • $<:表示第一個依賴文件名。
  • $^:表示所有依賴文件名。

2.6. 條件判斷

Makefile支持條件判斷,可以根據不同的條件執行不同的命令:

ifeq ($(DEBUG),1)
    CFLAGS += -g
else
    CFLAGS += -O2
endif

3. 示例演示

3.1. 編譯HelloWorld程式

我們用Makefile來編譯《Linux C++ 開發2 - 編寫、編譯、執行第一個程式》中的Hello world程式。

Makefile:

# 編譯 demo01.cpp
demo01.out: demo01.cpp
 g++ ./demo01.cpp -o demo01.out

# 申明clean為偽目標
.PHONY: clean
# 定義 clean 命令
clean:
 rm -f demo01.out

編譯和運行:

# 編譯
make
# 運行
./demo01.out
Hello, world!

3.2. 編譯多文件項目

3.2.1. 項目概述

C++之迭代器》一文中有一個例子是這樣的:

一個公司有多個部門,每個部門有多個人組成,這些人中有開發人員,有測試人員,和與項目相關的其它人員,其結構如下圖片。

file

現在要遍歷這個公司的所有開發人員,遍歷這個公司的所有測試人員。

在這篇文章中,我們用迭代器模式實現了這個需求,類的結構圖是這樣的:

file

詳細代碼參見: https://gitee.com/spencer_luo/iterator

現在我們就以這個項目為例,看看這個項目的makefile需要怎麼寫?

3.2.2. 需求分析

代碼結構如下,有三個.cpp,兩個.h,兩個.hpp

依賴關係如下:

Iterator(client) --> Enumerator --> Company --> Department --> Person

./iterator
├── Company.cpp
├── Company.h
├── Department.hpp
├── Enumerator.hpp
├── Iterator.cpp
├── Person.cpp
├── Person.h
└── README.md

3.2.3. Makefile V1.0

# 構建的最終目標 Iterator(可執行文件)
Iterator:Iterator.o Company.o Person.o
 g++ -o Iterator Iterator.o Company.o Person.o
# 構建目標 Iterator.o
Iterator.o:Iterator.cpp
 g++ -c Iterator.cpp
# 構建目標 Company.o
Company.o:Company.cpp
 g++ -c Company.cpp
# 構建目標 Person.o
Person.o:Person.cpp
 g++ -c Person.cpp

# 申明clean為偽目標
.PHONY: clean
clean:
 rm -f *.o Iterator

執行make進行編譯:

make      
g++    -c -o Iterator.o Iterator.cpp
g++    -c -o Company.o Company.cpp
g++    -c -o Person.o Person.cpp
g++ -o Iterator Iterator.o Company.o Person.o

上面的Makefile大家可能會有一些疑問,這裡對可能存在的疑問做一些解答。

3.2.3.1. 問題一:為什麼沒有頭文件的依賴?

問題描述:

如:編譯Person.o時,Person.cpp是包含了Person.h的,為什麼這條規則不寫成:

Person.o:Person.cpp Person.h
 g++ -c Person.cpp

問題解答:

這種寫法也是沒有問題的,對於makefile而言,沒有語法錯誤。但是沒有這個必要,《Linux C++ 開發3 - 你寫的Hello world經過哪些過程才被電腦理解和執行?》一文中我們講了在程式預處理階段,預處理器會將所有通過#include包含的頭文件替換成真正的內容,所以我們編譯的時候只需要對.cpp進行編譯即可。

3.2.3.2. 問題二:為什麼沒有對.hpp的規則定義?

問題描述:

為什麼Department.hppEnumerator.hpp不需要編譯。

問題解答:

正常,我們創建C++代碼文件的時候,一般會創建兩個文件:

  • 一個是頭文件(如:abc.h),用來進行類、函數、常量等的聲明。
  • 一個是源文件(如:abc.cpp),用來進行類、函數的定義。

但這樣每次要創建兩個文件,而且要在兩個文件上分別進行聲明和定義,挺麻煩的。於是為了偷懶,對於一些簡單的,沒有交叉引用的類,我們通常會把聲明和定義都放在一個文件中,這個文件通常以.hpp作為尾碼(如:abc.hpp)。

.hpp 本質上還是一個頭文件,GCC在編譯的時候,會把它當做頭文件來處理。所以我們在Makefile中可以不用寫對.hpp的編譯規則。

3.2.4. Makefile V2.0

GNU的make很強大,它可以自動推導文件以及文件依賴關係後面的命令,於是我們就沒必要在每一個.o文件後都寫上編譯的命令和規則,因為我們的make會自動識別,並自己推導命令。

於是我們的Makefile可以簡化為:

# 構建的最終目標 Iterator(可執行文件)
Iterator:Iterator.o Company.o Person.o
 g++ -o Iterator Iterator.o Company.o Person.o

# 申明clean為偽目標
.PHONY: clean
clean:
 rm -f *.o Iterator

執行make命令,我們會看到它會自動先去編譯.o, 然後再鏈接生成最終的二進位文件,編譯的過程和V1.0是一樣的。

make      
g++    -c -o Iterator.o Iterator.cpp
g++    -c -o Company.o Company.cpp
g++    -c -o Person.o Person.cpp
g++ -o Iterator Iterator.o Company.o Person.o

大家好,我是陌塵。

IT從業10年+, 北漂過也深漂過,目前暫定居於杭州,未來不知還會飄向何方。

搞了8年C++,也乾過2年前端;用Python寫過書,也玩過一點PHP,未來還會折騰更多東西,不死不休。

感謝大家的關註,期待與你一起成長。



【SunLogging】 掃碼二維碼,關註微信公眾號,閱讀更多精彩內容
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Python Lambda 用法大全 一、Lambda表達式基礎 Lambda 的組成分為三部分 Lambda表達式是Python中的一種匿名函數,它可以在一行代碼中定義函數並立即調用它。與普通函數不同的是,Lambda函數通常不需要使用def關鍵字去定義,而是在需要時直接使用lambda關鍵字來創 ...
  • 在PHP 7.4中安裝xdebug,出現"configure: error: rtnetlink.h is required, please make sure it is available by installing the correct package"的問題。 在使用sudo yum in ...
  • 寫在前面 今天繼續講JavaIO流的知識! Java 文件與流操作 File 類 File 類用於表示文件和文件夾的抽象。 構造方法 public File(String pathname): 使用路徑名創建 File 對象。 public File(String parent, String ch ...
  • 類中使用shared_ptr()的問題 當我們先定義一個指針,然後再用這個指針構造兩個智能指針 int main() { int* pt = new int(); std::shared_ptr<int> p1(pt); std::shared_ptr<int> p2(pt); std::cout ...
  • 先序: 學習編程語言要先學個輪廓,剛開始只用學核心的部分,一些細節、不常用的內容先放著,現用現查即可;把常用的東西弄熟練了在慢慢補充。 1、 安裝 Python 解釋器 為什麼需要安裝 Python Python 語言本身是由解釋器執行的,因此你需要在你的電腦上安裝 Python 解釋器。這個解釋 ...
  • 先序: 學習編程語言要先學個輪廓,剛開始只用學核心的部分,一些細節、不常用的內容先放著,現用現查即可;把常用的東西弄熟練了在慢慢補充。 1. Java 概述 Java 是一種面向對象的編程語言,由 Sun Microsystems(現在的 Oracle)在 1995 年推出。Java 程式可以在任何 ...
  • 正文 今天還是把周末的所有事情一起寫。每到周末都沒什麼寫日記的動力。 周五下午出去吃飯,吃一半,斯視頻突然打過來。我當時有點懵,不知道他要乾什麼,但還是接了。第一個畫面就是他擺弄自行車。他跟我說,自行車踏板和鏈條有問題,踩下去的時候轉一半就動不了了。 經過我們二人一通瞎猜,發現是後面的鏈條掉了,但是 ...
  • 1. Rust 簡介 Rust 的歷史 起源:Rust 語言最初由 Mozilla 研究員 Graydon Hoare 於 2006 年開始設計,並於 2009 年首次公開。 開發:Rust 是 Mozilla 實驗室的一個項目,目的是創建一種能夠保證記憶體安全同時又不犧牲性能的系統編程語言。 發佈: ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...