【CMake 教程】常用函數與構建案例解析(三)

一、CMake 常用函數簡析

1. 條件判斷 if() / elseif() / else()

在 CMake 腳本中,條件判斷是控制邏輯的重要工具。if() 支持多種比較語句,包括數值、字符串、布爾值和變量存在性等。在條件滿足時執行特定邏輯代碼,下面是典型語法:

if(VARIABLE)# 當 VARIABLE 存在且為非空時執行
elseif(${VARIABLE} STREQUAL "value")# 當 VARIABLE 等于指定字符串 "value" 時執行
else()# 以上都不滿足時執行
endif()

1.1 基本語句表達

  • if(<constant>)

    如果常量是 1ONYESTRUEY 或非零數字(包括浮點數),則為 True 。如果常量是 0OFFNOFALSENIGNORENOTFOUND空字符串或以后綴 -NOTFOUND 結尾,則為 False 。

  • if(<variable>)

    如果給定的變量被定義為非 false 常量的值,則為 True 。否則為 False ,包括變量未定義的情況。

  • if(<string>)

    引用的字符串始終計算為 false,除了下面這些情況:

    • 字符串的值是 true 常量之一
    • 策略 CMP0054 未設置為 NEW ,并且字符串的值恰好是受 CMP0054 行為影響的變量名。(本章末尾將對 CMP0054 策略進行介紹)

那么有個問題:

set(IS_STRING "This is string")
if(IS_STRING)message(OK)
endif()

請問會輸出“OK”嗎?

1.2 邏輯運算符

在 CMake 中,邏輯運算符被廣泛應用于條件判斷,幫助我們根據變量狀態或構建條件調整邏輯流轉。主要運算符如下:

運算符含義示例
NOT邏輯非,取反NOT IS_EMPTY_STRING
AND邏輯與,所有條件為真IS_TRUE AND NOT IS_FALSE
OR邏輯或,任一條件為真IS_TRUE OR IS_FALSE

我們直接通過代碼來介紹:

option(IS_TRUE "This is true" ON)		# True
option(IS_FALSE "This is false" OFF)	# False
set(IS_EMPTY_STRING "")					# False
set(IS_STRING "This is a string")		# Trueif(NOT IS_EMPTY_STRING)	# Truemessage("IS_EMPTY_STRING is empty")
endif()if(IS_STRING)	# Truemessage("IS_STRING is: \"${IS_STRING}\"")
endif()if(IS_TRUE AND NOT IS_FALSE)	# Truemessage("IS_TRUE is true and IS_FALSE is false")
endif()if(IS_FALSE OR NOT IS_TRUE)	# Falsemessage("IS_FALSE is true or IS_TRUE is false")
elseif(IS_TRUE)	# Truemessage("IS_TRUE is true")
else()	# 這句到不了message("IS_FALSE is true")
endif()

1.3 存在檢查

下面列舉幾種比較常用的檢查:

1.3.1 if(DEFINED <name>|CACHE{<name>}|ENV{<name>})

如果定義了具有給定 <name>變量緩存變量環境變量,則為 True 。變量的值無關緊要

1.3.2 if(TARGET <target-name>)

用于判斷指定目標是否已通過 add_executable()add_library()add_custom_target() 定義,并且無論目標是在哪個目錄(包括子目錄或父目錄)中定義的都能檢測到。

1.3.3 if(<variable|string> IN_LIST <variable>)(3.3版本后才加入的)

如果給定元素包含在命名列表變量中,則為 True 。

1.4 文件操作

操作不少,這里只列出常用的(至少我在生產級別項目中常用的),想了解更多可以到官方教程看。

1.4.1 if(EXISTS <path-to-file-or-directory>)

如果指定的文件或目錄存在且可讀,則返回 True 。Linux 下,~/ 不會被解析為主目錄。

為了準確,盡可能使用絕對路徑,例如:if(EXISTS ${CMAKE_BINARY_DIR}/Debug/VarApp.exe)

1.4.2 if(IS_DIRECTORY <path>)

如果 path 是目錄,則為 True 。同樣,path 傳入絕對路徑,避免出問題。

1.4.3 if(IS_ABSOLUTE <path>)

如果給定路徑是絕對路徑,則為 True 。注意以下特殊情況:

  • 空的 path 計算結果為 False。
  • 在 Windows 主機上,任何以驅動器號和冒號(例如 C: )、正斜杠或反斜杠開頭的 path 都將計算為 True。這意味著像 C:no\base\dir 這樣的路徑將計算為 True,即使路徑的非驅動器部分是相對的。
  • 在非 Windows 主機上,任何以波浪號開頭的 path~ )都會被計算為 True。

1.5 比較

我直接把所有情況列出來吧,大家應該都能看懂:


if(<variable|string> MATCHES )

if(<variable|string> LESS <variable|string>)

if(<variable|string> GREATER <variable|string>)

if(<variable|string> EQUAL <variable|string>)

if(<variable|string> LESS_EQUAL <variable|string>)(在 3.7 版本中添加)

if(<variable|string> GREATER_EQUAL <variable|string>)(在 3.7 版本中添加)

if(<variable|string> STRLESS <variable|string>)

if(<variable|string> STRGREATER <variable|string>)

if(<variable|string> STREQUAL <variable|string>)

if(<variable|string> STRLESS_EQUAL <variable|string>)

if(<variable|string> STRGREATER_EQUAL <variable|string>)


要注意的是 if(<variable|string> MATCHES <regex>),大家在官方文檔這里看支持的正則。

1.6 版本比較

用的很少,真想用就看官方文檔吧。

3.24 版本中加入了 if(<variable|string> PATH_EQUAL <variable|string>),可以用來更準確地比較兩個地址。

我沒用過,不敢胡亂寫,大家可以看官方文檔。

1.4 模擬運用場景

1.4.1 操作系統判斷

判斷當前操作系統,根據平臺選擇不同的編譯選項。

if(CMAKE_BUILD_TYPE STREQUAL "Debug")if(CMAKE_SYSTEM_NAME STREQUAL "Windows")message("On Windows")elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")message("On Linux")else()message("Other system")endif()
endif()
1.4.2 模塊啟用控制

動態啟用或禁用某些功能模塊,通過變量開關來實現。

option(ENABLE_LOGS "Enable logging" ON)
if(ENABLE_LOGS)add_definitions(-DENABLE_LOGS)			# 添加全局的 log 輸出宏
elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")message(WARNING "Logging is disabled")	# Debug 模式不應該禁止log輸出
endif()
1.4.3 外部依賴(或者叫外部文件)檢查

檢查第三方庫文件是否存在。

set(LIB_PATH "/usr/lib/libz.so")
if(EXISTS ${LIB_PATH} AND NOT IS_DIRECTORY ${LIB_PATH})message(STATUS "Library found: ${LIB_PATH}")
else()message(FATAL_ERROR "Library not found")
endif()

message(FATAL_ERROR "Library not found") 這里將會使得 CMake 構建失敗,屬于自定義錯誤。

2. 循環 foreach()while()

2.1 foreach() 的三種用法

2.1.1 基于范圍:foreach(<loop_var> RANGE <stop>)
foreach(i RANGE 5)message(STATUS "Current value: ${i}")
endforeach()
2.1.2 基于范圍,指定起點和步長:foreach(<loop_var> RANGE <start> <stop> [<step>])

如果不指定 step,默認為1。

foreach(i RANGE 1 10 2)message(STATUS "Current step: ${i}")
endforeach()
2.1.3 基于列表或值的迭代:foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]])
set(FRUITS Apple Banana Cherry)
list(APPEND COLORS Blue Red Orange Pink)
foreach(fruit IN LISTS FRUITS)message(STATUS "Current fruit: ${fruit}")
endforeach()foreach(fruit IN LISTS FRUITS COLORS)message(STATUS "Current fruit: ${fruit}")
endforeach()

2.2 while() 的用法

其實有點廢話:

while(<condition>)<commands>
endwhile()

condition 部分與 if 是一樣的,對著用就好。

2.3 break()continue()

不介紹了,可以用在 foreach()while() 中。

要是看不懂怎么用,基本上就告別開發了。。。

3. 消息輸出 message()

我們也經常用到了,它最常用的方式就是輸出內容到命令行。

message([<mode>] "message text" ...)

接下來介紹一下各個 mode

  • FATAL_ERROR:CMake 錯誤,停止處理和生成。
  • SEND_ERROR:CMake 錯誤,繼續處理,但跳過生成。
  • WARNING:CMake 警告,繼續處理。
  • STATUS:項目用戶可能感興趣的主要信息。這些信息應該簡潔,不超過一行,但仍然具有信息量。

上面這幾個就是常用的,其他的我是沒用過,你們想用看官方文檔吧。

4. 其他常用函數

還有兩個比較重要的函數:file()string(),主要作用如下:

  • file():創建文件夾、讀取/寫入文件、遍歷目錄等。
  • string():處理字符串(替換、拼接和分割等)。

由于它們的作用比較泛且雜,我打算在后面用到再說。

現在完全介紹這兩個函數并不能很好的起到學習 CMake 的作用,因為脫離了實踐!

這里是 file() 的官方文檔

這里是 string() 的官方文檔

二、構建案例:跨平臺多線程項目

我們通過一個實例來熟悉 CMake 在跨平臺方面的使用。

項目結構:

ThreadSimple
├── CMakeLists.txt
├── main.c
├── thread_wrapper.c	# 線程封裝實現
└── thread_wrapper.h	# 線程封裝頭文件

thread_wrapper.h 內容:

#ifndef THREAD_WRAPPER_H
#define THREAD_WRAPPER_H// 跨平臺縣城宏定義
#ifdef _WIN32
#include <windows.h>typedef HANDLE thread_t;        // Windows 線程類型
typedef DWORD thread_return_t;  // Windows 線程函數返回類型
typedef LPVOID thread_param_t;  // Windows 線程參數類型
#else
#include <pthread.h>typedef pthread_t thread_t;     // POSIX 線程類型
typedef void *thread_return_t;  // POSIX 線程函數返回類型
typedef void *thread_param_t;   // POSIX 線程參數類型
#endif/// 通用線程函數指針
typedef thread_return_t (*thread_func_t)(thread_param_t arg);/// 創建線程
int create_thread(thread_t *thread, thread_func_t func, void *arg);/// 等待線程執行完成
void join_thread(thread_t thread);#endif  //! THREAD_WRAPPER_H

thread_wrapper.c 內容:

#include "thread_wrapper.h"int create_thread(thread_t *thread, thread_func_t func, void *arg)
{
#ifdef _WIN32*thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, (LPVOID)arg, 0, NULL);return *thread == NULL ? -1 : 0;
#elsereturn pthread_create(thread, NULL, func, arg);
#endif
}void join_thread(thread_t thread)
{
#ifdef _WIN32WaitForSingleObject(thread, INFINITE);CloseHandle(thread);
#elsepthread_join(thread, NULL);
#endif
}

main.c 內容:

#include <stdio.h>
#include "thread_wrapper.h"#define THREAD_COUNT 5  // 線程數量/// 線程函數
thread_return_t thread_function(thread_param_t arg)
{int thread_num = *((int *)arg);printf("Thread %d is running\n", thread_num);// 模擬線程任務for (int i = 0; i < 5; i++) {printf("Thread %d: %d\n", thread_num, i);}return 0;
}int main()
{thread_t threads[THREAD_COUNT];  // 線程句柄int thread_args[THREAD_COUNT];   // 線程參數// 創建線程for (int i = 0; i < THREAD_COUNT; i++) {thread_args[i] = i + 1;if (create_thread(&threads[i], thread_function, &thread_args[i]) != 0) {printf("Error creating thread %d\n", i + 1);return -1;}}// 等待線程完成for (int i = 0; i < THREAD_COUNT; i++) {join_thread(threads[i]);}printf("All threads finished.\n");return 0;
}

從源碼上看,我們需要在合適的平臺上添加 pthread 庫作為依賴。

直接上 CMakeLists.txt 代碼:

cmake_minimum_required(VERSION 3.21)project(ThreadSimple LANGUAGES C)    # 指定項目名稱和語言set(CMAKE_C_STANDARD 99)            # 指定C標準為C99add_executable(${PROJECT_NAME})target_sources(${PROJECT_NAME} PRIVATE main.c thread_wrapper.c)# 只有在非Windows平臺才鏈接pthread庫
if(NOT WIN32)target_link_libraries(${PROJECT_NAME} PRIVATE pthread)
endif()

WIN32 變量只有在 Windows 系統下才存在。

project() 中使用了 LANGUAGES C,也就意味著只用到了C語言,CMake 不會去查找以及使用C++編譯器

大家也發現,我并沒有在 add_executable() 函數內添加源文件,而是使用了 target_sources() 函數,target_sources() 的用法和第一章講的 target_link_libraries 非常相似,可以借鑒。

Linux 執行結果:

在這里插入圖片描述

Windows 執行結果:

在這里插入圖片描述


【CMake 教程】常用函數與構建案例解析(三) 結束,希望大家提提意見,歡迎指正!

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

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

相關文章

ASP.NET Core 8.0學習筆記(二十七)——數據遷移:Migrations深入與其他遷移命令

一、數據庫架構的管理 1.EF Core提供兩種方式來保持EF Core的模型與數據庫保持同步。 (1)以數據庫為準&#xff1a;反向工程&#xff08;Db First&#xff09;&#xff0c;適用于中大型工程 (2)以代碼為準&#xff1a;數據遷移&#xff08;Code First&#xff09;&#xff0c;…

Python 基本語法的詳細解釋

目錄 &#xff08;1&#xff09;注釋 &#xff08;2&#xff09;縮進 &#xff08;3&#xff09;變量和數據類型 變量定義 數據類型 &#xff08;4&#xff09;輸入和輸出 輸出&#xff1a;print() 函數 輸入&#xff1a;input() 函數 &#xff08;1&#xff09;注釋 注…

20-R 繪圖 - 餅圖

R 繪圖 - 餅圖 R 語言提供來大量的庫來實現繪圖功能。 餅圖&#xff0c;或稱餅狀圖&#xff0c;是一個劃分為幾個扇形的圓形統計圖表&#xff0c;用于描述量、頻率或百分比之間的相對關系。 R 語言使用 pie() 函數來實現餅圖&#xff0c;語法格式如下&#xff1a; pie(x, l…

Ubuntu 22.04 一鍵部署MinerU1.1.0

MinerU MinerU是一款將PDF轉化為機器可讀格式的工具&#xff08;如markdown、json&#xff09;&#xff0c;可以很方便地抽取為任意格式。 MinerU誕生于書生-浦語的預訓練過程中&#xff0c;我們將會集中精力解決科技文獻中的符號轉化問題&#xff0c;希望在大模型時代為科技發…

紫光同創開發板使用教程(二):sbit文件下載

sbit文件相當于zynq里面的bit文件&#xff0c;紫光的fpga工程編譯完成后會自動生成sbit文件&#xff0c;因工程編譯比較簡單&#xff0c;這里不在講解工程編譯&#xff0c;所以我這里直接下載sbit文件。 1.工程編譯完成后&#xff0c;可以看到Flow列表里面沒有報錯&#xff0c…

DeepSeek 部署全指南:常見問題解析與最新技術實踐

引言 隨著開源大模型DeepSeek的爆火&#xff0c;其部署需求激增&#xff0c;但用戶在實際操作中常面臨服務器壓力、本地部署性能瓶頸、API配置復雜等問題。本文結合2025年最新技術動態&#xff0c;系統梳理DeepSeek部署的核心問題與解決方案&#xff0c;并分享行業實踐案例&am…

Vue02

Vue02 綁定class樣式 字符串寫法&#xff0c;適用于&#xff1a;樣式的類名不確定&#xff0c;需要動態指定 數組寫法&#xff0c;適用于&#xff1a;要綁定的樣式個數不確定&#xff0c;名字也不確定 對象寫法&#xff0c;適用于&#xff1a;要綁定的樣式個數缺點&#xff…

超導量子計算機的最新進展:走向實用化的量子革命

超導量子計算機的最新進展:走向實用化的量子革命 大家好,我是 Echo_Wish,今天我們來聊聊科技圈最炙手可熱的話題之一——超導量子計算機。近年來,量子計算領域可謂是風起云涌,而超導量子計算機作為主流路線之一,已經在學術界和工業界取得了不少突破性進展。 那么,超導…

LangChain構建行業知識庫實踐:從架構設計到生產部署全指南

文章目錄 引言:行業知識庫的進化挑戰一、系統架構設計1.1 核心組件拓撲1.2 模塊化設計原則二、關鍵技術實現2.1 文檔預處理流水線2.2 混合檢索增強三、領域適配優化3.1 醫學知識圖譜融合3.2 檢索結果重排序算法四、生產環境部署4.1 性能優化方案4.2 安全防護體系五、評估與調優…

Node.js中如何修改全局變量的幾種方式

Node.js中如何修改全局變量。我需要先理解他們的需求。可能他們是在開發過程中遇到了需要跨模塊共享數據的情況&#xff0c;或者想要配置一些全局可訪問的設置。不過&#xff0c;使用全局變量可能存在一些問題&#xff0c;比如命名沖突、難以維護和測試困難&#xff0c;所以我得…

【Node.js】express框架

目錄 1初識express框架 2 初步使用 2.1 安裝 2.2 創建基本的Web服務器 2.3 監聽方法 2.3.1 監聽get請求 2.3.2 監聽post請求 2.4 響應客戶端 2.5 獲取url中的參數(get) 2.5.1 獲取查詢參數 2.5.2 獲取動態參數 2.6 托管靜態資源 2.6.1 掛載路徑前綴 2.6.2 托管多…

Vulhub靶機 Apache Druid(CVE-2021-25646)(滲透測試詳解)

一、開啟vulhub環境 docker-compose up -d 啟動 docker ps 查看開放的端口 1、漏洞范圍 在Druid0.20.0及更低版本中 二、訪問靶機IP 8888端口 1、點擊Load data進入新界面后&#xff0c;再點擊local disk按鈕。 2、進入新界面后&#xff0c;在標紅框的Base directory欄寫上…

【c語言】函數_作業詳解

前言&#xff1a; 對應鵬哥專升本c語言&#xff0c;51集 內容&#xff1a; 找出10個數值中的最大值&#xff0c; #include <stdio.h> //求10個整數中的最大值 int main() {//準備10個整數 //int arr[10] {1,2,3,4,13,6,7,8,9,-2};//用于循環10次int i 0;//也可以自…

計算機網絡之路由協議(自治系統)

一、自治系統&#xff08;AS&#xff09; 自治系統是由同一個技術管理機構管理、使用統一選路策略的一些路由器的集合。它是網絡的基本構成單位&#xff0c;每個自治系統是一個獨立運營并自主決定與誰交換流量的實體。自治系統內部運行內部網關協議&#xff08;IGP&#xff09…

【Unity】Unity clone 場景渲染的燈光貼圖異位問題

Unity clone 場景渲染的燈光貼圖異位問題 問題 需要將一個場景clone 一份保存到本地 當克隆完成后&#xff0c;副本場景的燈光貼圖異位了&#xff0c;與原場景存在較大的差別 問題原因 場景被clone 后&#xff0c;場景的燈光渲染數據不能共用&#xff0c;即Lightmapping.li…

Python----數據分析(Numpy一:numpy庫簡介,與list列表的區別,數組的創建,隨機數組的創建)

一、 Numpy庫簡介 1.1、概念 NumPy(Numerical Python)是一個開源的Python科學計算庫&#xff0c;旨在為Python提供 高性能的多維數組對象和一系列工具。NumPy數組是Python數據分析的基礎&#xff0c;許多 其他的數據處理庫&#xff08;如Pandas、SciPy&#xff09;都依賴于Num…

千峰React:函數組件使用(2)

前面寫了三千字沒保存&#xff0c;恨&#xff01; 批量渲染 function App() {const list [{id:0,text:aaaa},{id:1,text:bbbb},{id:2,text:cccc}]// for (let i 0; i < list.length; i) {// list[i] <li>{list[i]}</li>// }return (<div><…

Hot100 動態規劃

動態規劃 動規五部曲&#xff1a; 確定dp數組以及下標的含義確定遞推公式dp數組如何初始化確定遍歷順序舉例推導dp數組 70. 爬樓梯 - 力扣&#xff08;LeetCode&#xff09; 爬到第一層樓梯有一種方法&#xff0c;爬到二層樓梯有兩種方法。 那么第一層樓梯再跨兩步就到第三…

Rust語言基礎知識詳解【一】

1.在windows上安裝Rust Windows 上安裝 Rust 需要有 C 環境&#xff0c;以下為安裝的兩種方式&#xff1a; 1. x86_64-pc-windows-msvc&#xff08;官方推薦&#xff09; 先安裝 Microsoft C Build Tools&#xff0c;勾選安裝 C 環境即可。安裝時可自行修改緩存路徑與安裝路…

文章精讀篇——OMG-Seg

題目&#xff1a;OMG-Seg : Is One Model Good Enough For All Segmentation? 作者&#xff1a;Xiangtai Li1 ? Haobo Yuan1 Wei Li1 Henghui Ding1 Size Wu1 Wenwei Zhang1Yining Li2 Kai Chen2 Chen Change Loy1 代碼&#xff1a;OMG-Seg 會議&#xff1a;cvpr2024 邊讀…