【CMake】自定義package并通過find_package找到

在一些場景下我們需要編寫一些庫,并希望其他程序可以找到這些庫并引用。
CMake采用package這個概念來解決這個問題。
關于CMake的find_package文章有很多,但這些文章的內容大多不直觀講了一堆講不到點子上,讓人看了一頭霧水。因此我想通過本文從實用角度出發介紹一下CMake的package概念。
CMake關于Package的官方文檔:https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html

CMake的Package概念

簡單理解Package是有Component組成的,而Component包含了庫和頭文件。如:

find_package(Qt5 5.1.0 REQUIRED Widgets Xml Sql)

上面代碼中Qt5是package,5.1.0是package的版本號,REQUIRED表示必須要找到,WidgetsXmlSql是Component名稱。因此上面代碼的意思是“必須找到版本為5.1.0的Qt5 Package的名為Widgets、Xml和Sql的Components”。

target_link_libraries(Foo PRIVATE Qt5::Widgets Qt5::Xml Qt5::Sql)

上面代碼的意思是在編譯目標Foo是鏈接Qt5WidgetsXmlSql這三個Component提供的庫。其實Qt5::WidgetsQt5::XmlQt5::Sql就是庫的別名,因此上面代碼也等價于

target_link_libraries(Foo PRIVATE qt5wdgets qtxml qt5sql)

最終參與編譯的是libqt5wdgets.alibqt5xml.alibqt5sql.a這三個庫。上面三個庫名是我瞎編的可能與實際不符。

關于Config模式和Module模式

很多文章都提到了find_package有兩種查詢package的模式,一種是Config模式,另一種是Module模式。這里就不再贅述了,因為對實際使用毫無益處。只需要知道如果庫也是用CMake構建的就是Config模式;如果庫不是CMake構建的就用Module模式。又因為在團隊內部通常構建工具都是統一的,而且現在CMake非常流行,因此本文自關心Config模式。

自定義package

經過以上的介紹,應該清楚所謂的Component只不過是庫的別名而已。因此我們自定義package,實際上就是編寫一套CMake文件,這些文件里定義了庫的頭文件路徑、庫文件路徑,并為庫文件起個別名即可。
以上工作并不需要我們親自編寫,因為CMake已經給我們提供了EXPORT工具,我們只需要使用EXPORT便可以完成上面的工作。
下面是我自己編寫的一個Demo。foo_lib是一個庫也就是自定義的package,foo_app是一個應用通過find_package查找foo_lib并引用。install是它倆的安裝路徑。
在這里插入圖片描述

foo_lib

在這里插入圖片描述
foo_lib中包含四個文件"foo_lib.h"、“foo_lib.cpp”、“CMakeLists.txt”、“FooLibConfig.cmake.in”

foo_lib.h

foo_lib.h作為庫的頭文件,這只提供一個方法foo_lib_func()聲明

#pragma
void foo_lib_func();

foo_lib.cpp

foo_lib.cpp作為庫的源代碼文件,實現foo_lib_func()

#include <stdio.h>
#include "foo_lib.h"void foo_lib_func()
{printf("My name is foo lib");
}

CMakeLists.txt

CMakeLists.txt 是CMake的構建文件,也是我們要講解的重點

cmake_minimum_required(VERSION 3.5)project(FooLib VERSION 1.0.0 LANGUAGES CXX)add_library(${PROJECT_NAME} SHARED ${CMAKE_CURRENT_SOURCE_DIR}/foo_lib.cpp)install(TARGETS ${PROJECT_NAME}EXPORT ${PROJECT_NAME}TargetsLIBRARY DESTINATION libINCLUDES DESTINATION include
)install(FILES foo_lib.h DESTINATION include
)install(EXPORT ${PROJECT_NAME}TargetsFILE ${PROJECT_NAME}Targets.cmakeDESTINATION "${CMAKE_INSTALL_PREFIX}/lib/cmake/${PROJECT_NAME}"NAMESPACE ${PROJECT_NAME}::
)include(CMakePackageConfigHelpers)
configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in""${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"INSTALL_DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/cmake/${PROJECT_NAME}"
)install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/cmake/${PROJECT_NAME}"
)
  • cmake_minimum_required 規定了cmake的版本
  • project 定義了項目屬性
  • add_library 表示以項目名編譯一個動態庫
  • 第一個 install
    第一個install 中最關鍵的是EXPORT。意思是當前的TARGETS要向外導出。導出的名稱為${PROJECT_NAME}Targets
  • 第二個 install
    第二個install用來安裝頭文件。雖然在第一個install中寫了INCLUDES DESTINATION include,但是并沒有真正安裝頭文件,原因很簡單因為CMake無法自動推斷出哪些頭文件需要安裝。
  • 第三個 install
    第三個 install的作用是導出Targets文件。第一個install的EXPORT只是表示要導出${PROJECT_NAME}Targets,但是導出到哪并沒有指明。因此需要第三個install指明導出文件為${PROJECT_NAME}Targets.cmake,導出的位置為"${CMAKE_INSTALL_PREFIX}/lib/cmake/${PROJECT_NAME}",導出的命名空間為${PROJECT_NAME}::

    上文說的庫頭文件路徑,庫文件路徑,庫文件別名 都會自動生成并保存在${PROJECT_NAME}Targets.cmake

  • include(CMakePackageConfigHelpers)
    為了引入configure_package_config_file方法
  • configure_package_config_file
    configure_package_config_file的所用是生成${PROJECT_NAME}Config.cmake。這個文件名是固定的,調用find_package(xxx)時,cmake就會找xxxConfig.cmake文件并引入。到這就很好理解了,我們只要在${PROJECT_NAME}Config.cmake中引用${PROJECT_NAME}Targets.cmake就可以把庫頭文件路徑、庫文件路徑、庫的別名等信息導入了。
  • 第四個install
    第四個個install的作用是將${PROJECT_NAME}Config.cmake按照到指定路徑,好讓find_package(xxx)能找到。

FooLibConfig.cmake.in

@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
# find_dependency(xxx)
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
  • @PACKAGE_INIT@ 是固定寫法,這部分內容會被configure_package_config_file替換。
  • include(CMakeFindDependencyMacro) 和 find_dependency(xxx)
    如果你的庫還引用了其他庫需要在這里追加,如
    include(CMakeFindDependencyMacro) 
    find_dependency(Qt5)
    find_dependency(Boost)
    
  • include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
    作用是引用${PROJECT_NAME}Targets.cmake

編譯,現在在foo_lib下執行以下指令

mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=../../install
make install

執行完成后在install目錄結構如下
在這里插入圖片描述
感興趣的話可以看看FooLibConfig.cmake和FooLibTargets.cmake的內容,就可以看到庫的路徑、名稱和別名了。

使用自定義Package

foo_app只有兩個文件main.cppCMakeLists.txt
在這里插入圖片描述

main.cpp

這個文件不必多說

#include "foo_lib.h"int main(int argc, char** argv)
{foo_lib_func();
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(FooApp VERSION 1.0.0)
find_package(FooLib REQUIRED)
add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE FooLib::FooLib)
install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)

這里關鍵的兩步是

  • find_package(FooLib REQUIRED) 查找FooLib庫
  • target_link_libraries(${PROJECT_NAME} PRIVATE FooLib::FooLib) 編譯時鏈接FooLib庫

編譯

在foo_app目錄下執行

mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=../../install
make install

執行完后install目錄結構如下
在這里插入圖片描述

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

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

相關文章

【MATLAB例程】AOA與TDOA混合定位例程,適用于二維環境、3個錨點的定位|附代碼下載鏈接

本 MATLAB 程序實現了基于 Angle of Arrival (AOA) 與 Time Difference of Arrival (TDOA) 的二維定位方法&#xff0c;通過自適應融合與最小二乘優化&#xff0c;實現對未知目標的高精度估計。本例中固定使用了 3 個基站&#xff08;錨點&#xff09;&#xff0c;算法框架支持…

磐維數據庫panweidb集中式集群配置VIP【添加、刪除和修改】

0 說明 panweidb集中式集群為了防止主備切換后應用連接無法切換到新主庫&#xff0c;需要配置vip&#xff0c;應用可以只通過該ip與數據庫連接&#xff0c;不用感知數據庫在哪個節點上。 panweidb中配置 VIP主要依賴 CM 組件的 VIP 仲裁功能&#xff0c;通過回調腳本在主備切換…

python的保險業務管理與數據分析系統

前端開發框架:vue.js 數據庫 mysql 版本不限 后端語言框架支持&#xff1a; 1 java(SSM/springboot)-idea/eclipse 2.NodejsVue.js -vscode 3.python(flask/django)–pycharm/vscode 4.php(thinkphp/laravel)-hbuilderx 數據庫工具&#xff1a;Navicat/SQLyog等都可以 保險行業…

R語言如何接入實時行情接口

目錄 1. 安裝必要的R包 2. 導入庫 3. 連接WebSocket 4. 處理連接成功后的操作 5. 處理接收到的消息 6. 處理連接關閉和錯誤 7. 發送心跳數據 8. 自動重連機制 9. 啟動連接和重連 總結 在數據分析和金融研究中&#xff0c;實時行情數據的獲取至關重要&#xff0c;但市…

Redis數據安全性分析

Redis高可用與數據安全機制深度解析前置知識&#xff1a;Redis基礎安裝與使用&#xff08;主從復制、哨兵集群、Cluster集群搭建&#xff09;一、Redis性能壓測工具 工具名稱&#xff1a;redis-benchmark核心作用&#xff1a;快速基準測試Redis性能使用場景&#xff1a;評估不同…

差分和前綴和

差分和前綴和的原理、用法和區別。前綴和&#xff08;Prefix Sum&#xff09;核心思想&#xff1a;預處理數組的前綴和&#xff0c;快速回答「區間和查詢」 適用場景&#xff1a;數組靜態&#xff08;更新少、查詢多&#xff09;&#xff0c;需要頻繁計算任意區間的和1. 定義與…

C++并發編程-12. 用內存順序實現內存模型

前情回顧 前文我們介紹了六種內存順序&#xff0c;以及三種內存模型&#xff0c;本文通過代碼示例講解六種內存順序使用方法&#xff0c;并實現相應的內存模型。全局一致性模型同步模型(獲取和釋放)松散模型memory_order_seq_cst memory_order_seq_cst代表全局一致性順序&#…

AI測試革命:從智能缺陷檢測到自愈式測試框架的工業實踐

AI測試革命&#xff1a;從智能缺陷檢測到自愈式測試框架的工業實踐 希望對大家有用&#xff01; 目錄AI測試革命&#xff1a;從智能缺陷檢測到自愈式測試框架的工業實踐希望對大家有用&#xff01;一、傳統測試之殤&#xff1a;工業質檢的切膚之痛二、智能缺陷檢測系統架構1. …

二、深度學習——損失函數

二、損失函數損失函數定義&#xff1a;損失函數是用來衡量模型參數的質量的函數&#xff0c;衡量方式是比較網絡輸出和真實輸出的差異別名&#xff1a;損失函數&#xff08;loss function&#xff09;&#xff0c;代價函數&#xff08;cost function&#xff09;&#xff0c;目…

面向數據報的套接字通道技術詳解

數據報通道基礎 通道特性與創建方式 java.nio.channels.DatagramChannel類實例代表數據報通道&#xff0c;默認處于阻塞模式。通過configureBlocking(false)方法可將其配置為非阻塞模式。創建數據報通道需調用其靜態open()方法&#xff0c;若用于IP組播則需指定組播組的地址類型…

147.在 Vue3 中使用 OpenLayers 地圖上 ECharts 模擬飛機循環飛行

&#x1f9e9; 效果預覽 &#x1f447; 飛機從多個城市起飛并向其他城市飛行&#xff0c;動畫流暢&#xff0c;地圖可縮放拖拽&#xff1a; &#x1f4e6; 一、項目技術棧 技術用途Vue 3現代前端框架OpenLayers地圖底圖渲染ECharts ol-echarts飛機飛行動畫渲染ol-echarts將 …

OCR與PDF解析的區別

我們日常所接觸的文檔中&#xff0c;經常能碰到多語言混合的文檔。比如論文試卷、財報研報、跨國票據都含有多種語言和文字。要將文檔中的內容識別并提取務必需要使用到OCR技術&#xff0c;而傳統的OCR工具在處理這類型文檔的時候有局限性。早期的 OCR 系統識別精度有限&#x…

Java 單例類詳解:從基礎到高級,掌握線程安全與高效設計

作為一名Java開發工程師&#xff0c;你一定對**單例模式&#xff08;Singleton Pattern&#xff09;**不陌生。它是23種經典設計模式中最簡單也是最常用的一種&#xff0c;用于確保一個類在整個應用程序中只有一個實例存在。單例廣泛應用于系統配置、數據庫連接池、日志管理器、…

面向對象設計

你列出的這些屬于 C 高級開發中面向對象設計與架構設計的核心知識&#xff0c;也是面試高級工程師崗位必問的內容。下面我按順序&#xff0c;深入講解每一項概念、原理、用途&#xff0c;并穿插 C 示例。? 1. 設計原則&#xff08;SOLID&#xff09;SOLID 是面向對象設計的五大…

IntelliJ IDEA讓我的開發效率翻倍:從新手到高效開發者的進階之路

IntelliJ IDEA讓我的開發效率翻倍&#xff1a;從新手到高效開發者的進階之路 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30c; 總有一行代碼&#xff0c;能點亮萬千星辰。 &#x1f50d; 在技術的宇宙中&#xff0c;我愿做永不停歇的探索者。 ? 用…

css sprites使用

CSS Sprites 是一種將多個小圖標或背景圖像合并到一個大圖中的技術。通過減少HTTP請求次數&#xff0c;可以顯著提高頁面加載速度。其核心原理是&#xff1a;通過設置元素的背景圖&#xff08;background-image&#xff09;為這個大圖&#xff0c;然后調整背景位置&#xff08;…

分布式爬蟲在電商平臺商品數據大規模采集中的技術應用

在電商平臺商品數據大規模采集場景中&#xff0c;分布式爬蟲憑借其高效、可擴展、抗風險的特性&#xff0c;成為突破單節點爬蟲性能瓶頸的核心技術方案。以下從技術架構、關鍵技術點、電商場景適配及挑戰應對四個維度&#xff0c;解析其具體應用&#xff1a;一、分布式爬蟲的核…

Linux的`if test`和`if [ ]中括號`的取反語法比較 筆記250709

Linux的if test和if 中括號的取反語法比較 筆記250709 Linux的 test命令&#xff08;或等價中括號寫法 [空格expression空格]&#xff09;的用法詳解. 筆記250709 四種取反語法: if ! test -e xxx ;then... 和 if test ! -e xxx ;then... 和 if ! [ -e xxx ] ;then... 和 if …

記錄使用ubuntu16.04編譯aosp(android8.1與10)遇到的問題

一、前言&#xff1a; 本來打算用wsl來編譯AOSP&#xff0c;但是折騰了好幾天&#xff0c;以失敗告終。后來使用vmware反而成功了。 本篇同樣會把wsl遇到的問題與嘗試記錄下來。 環境&#xff1a;vmware ubuntu16.04。 為什么會使用ubuntu16.04呢&#xff0c;因為在公司有一…

hiredis window之RFDMap

簡介 RFDMap用于將socket分配映射成連續的文件描述符&#xff0c;同時管理回收的文件描述符&#xff0c;因為ae構架中管理fd與對應事件處理器使用的是數據&#xff0c;fd作為數組下標 結構 #mermaid-svg-zQz2LTrKRi0LQTII {font-family:"trebuchet ms",verdana,arial…