CMake
是一個(gè)開(kāi)源的跨平臺(tái)工具,可以構(gòu)建、測(cè)試和打包軟件。
它具有如下特性:
- 自動(dòng)搜索可能需要的程序、庫(kù)和頭文件的能力;
- 獨(dú)立的構(gòu)建目錄(如
build
),可以安全清理; - 支持復(fù)雜的自定義命令(下載、生成各種文件);
- 自定義配置可選組件;
- 從簡(jiǎn)單的文本文件(
CMakeLists.txt
)自動(dòng)生成工作區(qū)和項(xiàng)目的能力; - 在主流平臺(tái)上自動(dòng)生成文件依賴項(xiàng)并支持并行構(gòu)建;
- 幾乎支持所有的
IDE
1. 安裝CMake
sudo apt install cmake -y
安裝完成后用查看版本指令cmake -version
檢查CMake
是否安裝成功。
出現(xiàn)以上提示代表安裝成功。
2. 第一個(gè)CMake例子
我們首先新建兩個(gè)文件,main.cpp
和 CMakeLists.txt
touch main.cpp CMakeLists.txt
main.cpp
中編寫如下代碼
#include <iostream>
using namespace std;
int main(){
cout << "第一個(gè)CMake程序" << endl;
return 0;
}
CMakeLists.txt
中編寫如下代碼
# CMake 最低版本號(hào)要求
cmake_minimum_required (VERSION 3.1)
# demo 代表項(xiàng)目名稱
project (demo)
# 添加一個(gè)可執(zhí)行程序,demo是可執(zhí)行程序名稱,main.cpp是源文件
add_executable(main main.cpp)
這里我們用到
add_executable
,其中第一個(gè)參數(shù)是最終生成的可執(zhí)行文件名以及在CMake
中定義的Target
名。我們可以在CMake
中繼續(xù)使用Target
的名字為Target
的編譯設(shè)置新的屬性和行為。命令中第一個(gè)參數(shù)后面的參數(shù)都是編譯目標(biāo)所使用到的源文件。
在準(zhǔn)備好后我們依次執(zhí)行下列指令:
# 第一步:配置,-S 指定源碼目錄,-B 指定構(gòu)建目錄
cmake -S . -B build
# 第二步:生成,--build 指定構(gòu)建目錄
cmake --build build
# 運(yùn)行
./build/main
- 第一步,我們輸入
cmake -S . -B build
- 第二步,我們輸入
cmake --build build
此時(shí)會(huì)產(chǎn)生一個(gè)可執(zhí)行文件 main
- 第三步,我們輸入
./build/main
運(yùn)行可執(zhí)行文件main
3. 同一個(gè)目錄下編譯多個(gè)文件
接下來(lái)我們嘗試在同一個(gè)目錄下編譯多個(gè)文件,我們首先在文件夾下新建如下
4
4
4 個(gè)文件,我們首先演示同時(shí)編譯帶有頭文件和 cpp
文件的案例。
touch main.cpp Account.cpp Account.h CMakeLists.txt
mian.cpp
的內(nèi)容如下:
# include "Account.h"
# include <iostream>
int main()
{
Account Q;
}
Account.cpp
的內(nèi)容如下:
#include "Account.h"
#include <iostream>
Account::Account(/* args */)
{
std::cout << "構(gòu)造函數(shù)Account::Account()" << std::endl;
}
Account::~Account()
{
std::cout << "析構(gòu)函數(shù)Account::~Account()" << std::endl;
}
Account.h
的內(nèi)容如下:
#ifndef Account_H
#define Account_H
class Account
{
private:
/* data */
public:
Account(/* args */);
~Account();
};
#endif // Account_H
CMakeLitsts.txt
的內(nèi)容如下:
#account_dir/CMakeLists.txt
# 最低版本要求
cmake_minimum_required(VERSION 3.10)
# 項(xiàng)目信息
project(Account)
# main 是可執(zhí)行文件名稱,后面是源文件
add_executable(main Account.cpp main.cpp Account.h)
接下來(lái)我們開(kāi)始編譯,依次輸入以下指令:
cmake -S . -B build
cmake --build build
編譯成功后文件結(jié)構(gòu)如下所示
這個(gè)案例是想說(shuō)明,如果在同一目錄下有多個(gè)源文件,那么只要在
add_executable
里把所有源文件都添加進(jìn)去就可以了,但是當(dāng)我們?cè)次募^(guò)多的時(shí)候就不太方便了,這時(shí)候我們用到了cmake
的另一個(gè)命令aux_source_directory(dir var)
第一個(gè)參數(shù)dir
是指定目錄,第二個(gè)參數(shù)var
是用于存放源文件列表的變量。
所以我們可以改寫一下 CMakeLists.txt
# 最低版本要求
cmake_minimum_required(VERSION 3.10)
# 項(xiàng)目信息
project(Account)
aux_source_directory(. SRC_LIST)
add_executable(main ${SRC_LIST})
這段代碼使用
aux_source_directory
把當(dāng)前目錄下的源文件存列表存放到變量SRC_LIST
里,然后在add_executable
里調(diào)用SRC_LIST
再次執(zhí)行 cmake
后最終的得到的結(jié)果是完全一樣的。
值得注意的是,aux_source_directory()
也存在弊端,它會(huì)把指定目錄下的所有源文件都加進(jìn)來(lái),可能會(huì)加入一些我們不需要的文件,此時(shí)我們可以使用 set
命令去新建變量來(lái)存放需要的源文件,如下:
cmake_minimum_required (VERSION 3.10)
project (Account)
set( SRC_LIST
./main.cpp
./Account.cpp
./Account.h)
add_executable(main ${SRC_LIST})
set
命令是用于定義變量的,set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
EXECUTABLE_OUT_PATH
和PROJECT_SOURCE_DIR
是CMake
自帶的預(yù)定義變量,其意義如下,
EXECUTABLE_OUTPUT_PATH
:目標(biāo)二進(jìn)制可執(zhí)行文件的存放位置PROJECT_SOURCE_DIR
:工程的根目錄
4. 不同目錄下編譯多個(gè)文件
接著上面的內(nèi)容,我們來(lái)演示不同目錄下編譯多個(gè)文件的案例,我們將上面的文件按照如下的結(jié)構(gòu)重構(gòu),內(nèi)容不需要改變。
一般來(lái)說(shuō), 源文件都放到 src
目錄下,把頭文件放入到 include
文件下,生成的對(duì)象文件放入到 build
目錄下,最終輸出的 elf
文件會(huì)放到 bin
目錄下
隨后在最外層目錄下新建 main.cpp
和 CMakeLists.txt
main.cpp
代碼如下:
#include <iostream>
#include "Account.h"
int main()
{
Account alice_account;
std::cout << "test Account 的main函數(shù)" << std::endl;
return 0;
}
CMakeLists.txt
代碼如下:
cmake_minimum_required (VERSION 3.10)
project (Account)
include_directories (include src)
aux_source_directory (include SRC_LIST)
aux_source_directory (src SRC_LIST1)
add_executable (main main.cpp ${SRC_LIST} ${SRC_LIST1})
這里出現(xiàn)了一個(gè)新的命令:
include_directories
。該命令是用來(lái)向工程添加多個(gè)指定頭文件的搜索路徑,路徑之間用空格分隔。
因?yàn)?main.cpp
里include
了Account.h
,如果沒(méi)有這個(gè)命令來(lái)指定頭文件所在位置,就會(huì)無(wú)法編譯。當(dāng)然,也可以在main.cpp
里使用include
來(lái)指定路徑,如下
#include "include/Account.h""
完成后開(kāi)始編譯:
cmake -S . -B build
cmake --build build
./build/main
5. 靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)
5.1 什么是庫(kù)文件?
庫(kù)文件(Library files)是預(yù)先編譯好的可重用代碼和資源的集合,它們被用于簡(jiǎn)化軟件開(kāi)發(fā)過(guò)程并提供常見(jiàn)功能的封裝。庫(kù)文件包含一組函數(shù)、類、數(shù)據(jù)結(jié)構(gòu)、變量、常量或其他可執(zhí)行代碼的實(shí)現(xiàn)。
庫(kù)文件的主要目的是為了促進(jìn)代碼的重用和模塊化開(kāi)發(fā),以提高開(kāi)發(fā)效率、減少代碼冗余,并使代碼更易于維護(hù)。通過(guò)使用庫(kù)文件,開(kāi)發(fā)人員可以在自己的應(yīng)用程序中調(diào)用庫(kù)中提供的函數(shù)、方法和類,從而實(shí)現(xiàn)特定功能而無(wú)需從頭開(kāi)始編寫代碼。
庫(kù)文件可以分為兩種主要類型:
- 靜態(tài)庫(kù)(Static Library):靜態(tài)庫(kù)的代碼在編譯時(shí)被復(fù)制到可執(zhí)行文件中,可執(zhí)行文件獨(dú)立于庫(kù)文件運(yùn)行。靜態(tài)庫(kù)提供了一種靜態(tài)鏈接的方式,使得可執(zhí)行文件可以在沒(méi)有外部依賴的情況下運(yùn)行。靜態(tài)庫(kù)通常以.lib (Dynamic-Link Libraries)(Windows)或.a(Unix/Linux)等文件擴(kuò)展名表示。
- 動(dòng)態(tài)庫(kù)(Dynamic Library):動(dòng)態(tài)庫(kù)的代碼在運(yùn)行時(shí)由操作系統(tǒng)動(dòng)態(tài)加載到內(nèi)存中,并與可執(zhí)行文件共享。動(dòng)態(tài)庫(kù)可以被多個(gè)應(yīng)用程序共享使用,減少了內(nèi)存占用和重復(fù)加載的開(kāi)銷。動(dòng)態(tài)庫(kù)通常以.dll(Windows)或.so (Shared Object)(Unix/Linux)等文件擴(kuò)展名表示。
庫(kù)文件可以提供各種功能,例如數(shù)學(xué)計(jì)算、文件操作、網(wǎng)絡(luò)通信、圖形界面、數(shù)據(jù)庫(kù)訪問(wèn)、加密解密等。通過(guò)使用庫(kù)文件,開(kāi)發(fā)人員可以利用已經(jīng)實(shí)現(xiàn)和測(cè)試過(guò)的功能,加速開(kāi)發(fā)過(guò)程,減少錯(cuò)誤和重復(fù)勞動(dòng)。
5.2 靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的區(qū)別?
-
鏈接方式:
- 靜態(tài)庫(kù):在編譯時(shí),靜態(tài)庫(kù)的代碼會(huì)被完整地復(fù)制到可執(zhí)行文件中。鏈接時(shí),編譯器將庫(kù)的代碼與應(yīng)用程序代碼合并成一個(gè)獨(dú)立的可執(zhí)行文件。這意味著可執(zhí)行文件在運(yùn)行時(shí)不依賴于外部的庫(kù)文件。
- 動(dòng)態(tài)庫(kù):在編譯時(shí),動(dòng)態(tài)庫(kù)的代碼不會(huì)被復(fù)制到可執(zhí)行文件中。相反,可執(zhí)行文件在運(yùn)行時(shí)加載動(dòng)態(tài)庫(kù),并在內(nèi)存中共享庫(kù)的代碼和數(shù)據(jù)。這意味著可執(zhí)行文件依賴于外部的庫(kù)文件,并且可以與多個(gè)應(yīng)用程序共享。
-
文件大小和內(nèi)存占用:
-
靜態(tài)庫(kù):靜態(tài)庫(kù)將代碼復(fù)制到可執(zhí)行文件中,因此可執(zhí)行文件的大小會(huì)增加。每個(gè)使用該靜態(tài)庫(kù)的可執(zhí)行文件都包含該庫(kù)的完整副本,因此占用的內(nèi)存空間也會(huì)較大。
-
動(dòng)態(tài)庫(kù):動(dòng)態(tài)庫(kù)的代碼不復(fù)制到可執(zhí)行文件中,因此可執(zhí)行文件的大小較小。多個(gè)應(yīng)用程序可以共享同一個(gè)動(dòng)態(tài)庫(kù)的實(shí)例,因此內(nèi)存占用可以被多個(gè)應(yīng)用程序共享,減少了重復(fù)加載的開(kāi)銷。
-
-
更新和維護(hù):
- 靜態(tài)庫(kù):靜態(tài)庫(kù)一旦被編譯到可執(zhí)行文件中,更新庫(kù)的代碼需要重新編譯可執(zhí)行文件。這意味著每個(gè)使用該靜態(tài)庫(kù)的應(yīng)用程序都需要重新構(gòu)建以包含最新版本的庫(kù)。
- 動(dòng)態(tài)庫(kù):動(dòng)態(tài)庫(kù)可以被獨(dú)立地更新,而不需要重新編譯可執(zhí)行文件。只需替換動(dòng)態(tài)庫(kù)文件即可,所有使用該庫(kù)的應(yīng)用程序都可以享受到更新的功能和修復(fù)的 bug。
-
可移植性:
- 靜態(tài)庫(kù):靜態(tài)庫(kù)是平臺(tái)相關(guān)的,需要為每個(gè)目標(biāo)平臺(tái)編譯不同的庫(kù)文件??蓤?zhí)行文件與特定平臺(tái)上的靜態(tài)庫(kù)文件緊密耦合,不易在不同平臺(tái)上移植。
- 動(dòng)態(tài)庫(kù):動(dòng)態(tài)庫(kù)是平臺(tái)獨(dú)立的,可以在多個(gè)平臺(tái)上共享使用。可執(zhí)行文件只需要加載適應(yīng)當(dāng)前平臺(tái)的動(dòng)態(tài)庫(kù)即可實(shí)現(xiàn)跨平臺(tái)的移植性。
5.3 生成庫(kù)文件
下面使用代碼來(lái)演示一下生成靜態(tài)庫(kù)的過(guò)程,依然新建三個(gè)文件,Account.cpp
, Account.h
,CMakeLists.txt
Account.cpp
內(nèi)容如下:
#include "Account.h"
#include <iostream>
Account::Account(/* args */)
{
std::cout << "構(gòu)造函數(shù)Account::Account()" << std::endl;
}
Account::~Account()
{
std::cout << "析構(gòu)函數(shù)Account::~Account()" << std::endl;
}
Account.h
內(nèi)容如下:
#ifndef Account_H
#define Account_H
class Account
{
private:
/* data */
public:
Account(/* args */);
~Account();
};
#endif // Account_H
CMakeLists.txt
內(nèi)容如下:
#account_dir/CMakeLists.txt
# 最低版本要求
cmake_minimum_required(VERSION 3.10)
# 項(xiàng)目信息
project(Account)
# 添加靜態(tài)庫(kù),Linux下會(huì)生成libAccount.a
# Account是庫(kù)名,STATIC表示靜態(tài)庫(kù),SHARED表示動(dòng)態(tài)庫(kù),后面是源文件
add_library(Account STATIC Account.cpp Account.h)
add_library
生成動(dòng)態(tài)庫(kù)或靜態(tài)庫(kù) (第 1 1 1 個(gè)參數(shù)指定庫(kù)的名字;第 2 2 2 個(gè)參數(shù)決定是動(dòng)態(tài)還是靜態(tài) (默認(rèn)靜態(tài));第 3 3 3 個(gè)參數(shù)指定生成庫(kù)的源文件)
準(zhǔn)備完成后我們使用如下指定編譯:
cmake -S . -B build
cmake --build build
成功編譯后我們就可以在 build
文件夾下看到我們以 .a
結(jié)尾的庫(kù)文件了
下面來(lái)演示更加復(fù)雜的例子,我們首先將上一步編譯好的部分文件移除,下圖高亮部分,也就是在build
目錄下只留下 libAccount.a
文件:
隨后在 test_account
文件夾下新建如下兩個(gè)文件:
mian.cpp
內(nèi)容如下:
#include <iostream>
#include "Account.h"
int main()
{
Account alice_account;
std::cout << "main函數(shù)被調(diào)用" << std::endl;
return 0;
}
CMakeLists.txt
內(nèi)容如下:
# test_account/CMakeLists.txt
# 最低版本要求
cmake_minimum_required(VERSION 3.10)
# 項(xiàng)目名稱
project(test_account)
# 添加執(zhí)行文件
add_executable(test_account main.cpp)
# 指定目標(biāo)包含的頭文件目錄
target_include_directories(test_account PUBLIC "../account_dir")
# 添加庫(kù)文件目錄,如果不添加,找不到庫(kù)文件
target_link_directories(test_account PUBLIC "../account_dir/build")
# 指定目標(biāo)鏈接的庫(kù)
target_link_libraries(test_account PRIVATE Account)
通過(guò)
target_include_directories
,我們給test_account
添加了頭文件引用路徑"../account_dir"
。上面的關(guān)鍵詞PUBLIC
,PRIVATE
用于說(shuō)明目標(biāo)屬性的作用范圍。
通過(guò)target_link_libraries
,將前面生成的靜態(tài)庫(kù)libAccount.a
鏈接給對(duì)象test_account
,但此時(shí)還沒(méi)指定庫(kù)文件的目錄,CMake
無(wú)法定位庫(kù)文件
再通過(guò)target_link_directories
,添加庫(kù)文件的目錄即可。
準(zhǔn)備完成后結(jié)構(gòu)如下:
進(jìn)入 test_account
目錄開(kāi)始編譯:
編譯后結(jié)構(gòu)如下所示:
6. CMake OpenCV 例子
安裝 OpenCV
庫(kù):
sudo apt install libopencv-dev
main.cpp
代碼如下:
#include <iostream>
#include <opencv2/opencv.hpp>
# 輸入一張圖像返回這張圖象縮放后的灰度圖
int main(int argc, char **argv)
{
if (argc != 2)
{
std::cout << "請(qǐng)輸入圖片路徑" << std::endl;
return -1;
}
else
{
std::cout << "圖片路徑為:" << argv[1] << std::endl;
cv::Mat image = cv::imread(argv[1]);
cv::Mat image_resized;
cv::resize(image, image_resized, cv::Size(400, 400));
cv::cvtColor(image_resized, image_resized, cv::COLOR_BGR2GRAY);
cv::imwrite("resized.png", image_resized);
std::cout << "圖片已處理完畢,并保存為resized.png" << std::endl;
}
return 0;
}
CMakeLists.txt
代碼如下
cmake_minimum_required(VERSION 3.10)
project(demo_opencv)
find_package(OpenCV REQUIRED)
if (OpenCV_FOUND)
message(STATUS "OpenCV library status:")
message(STATUS " libraries: ${OpenCV_LIBS}")
message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
else ()
message(FATAL_ERROR "Could not find OpenCV")
endif ()
add_executable(demo_opencv main.cpp)
target_include_directories(demo_opencv PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries(demo_opencv ${OpenCV_LIBS})
左側(cè)為原圖,右側(cè)為縮放后的圖
知識(shí)點(diǎn)整理較為混亂
TODO:后面應(yīng)該多做幾個(gè)真實(shí)案例文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-700224.html
參考文獻(xiàn)
Linux下CMake簡(jiǎn)明教程文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-700224.html
到了這里,關(guān)于Linux 系統(tǒng)下 CMake 示 例的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!