[082]破局Cmake中的PRIVATE,PUBLIC拭抬,INTERFACE

前言

最近看了很多項(xiàng)目的代碼部默,代碼是用cmake編譯的,由于各種庫(kù)之間鏈接關(guān)系錯(cuò)綜復(fù)雜造虎,加上PRIVATE傅蹂,PUBLIC,INTERFACE屬性值算凿,我在添加代碼的時(shí)候總會(huì)遇到稀奇古怪的編譯的問題份蝴,網(wǎng)上看了很多文章,寫的都不是很靠譜澎媒,正好看到一個(gè)b站視頻講的不錯(cuò)搞乏,解決了我很多疑惑,我又有了新的疑惑戒努,折騰了一晚上終于把這個(gè)搞明白了请敦,分享給大家。

一储玫、原理

從 modern cmake(>=3.0) 開始侍筛,使用的范式從 director-oriented 轉(zhuǎn)換到了 target-oriented。 這其中最重要的有三個(gè)概念:

    target
    target相應(yīng)的properties
    可見性

所謂target就是編譯的目標(biāo)撒穷,一般就三種:

    靜態(tài)庫(kù): 使用add_library()
    動(dòng)態(tài)庫(kù): 使用add_library() 指定SHARED關(guān)鍵字
    可執(zhí)行文件: 使用add_executable

所謂properties就是target的屬性匣椰,最常見的有以下五種:

    編譯標(biāo)志:使用target_complie_option
    預(yù)處理宏標(biāo)志:使用 target_compile_definitions
    頭文件目錄:使用 target_include_directories
    鏈接庫(kù):使用 target_link_libraries
    鏈接標(biāo)志:使用 target_link_options

所謂可見性就是上述這些屬性在不同target之間的傳遞性。有三種:

    PRIVATE
    PUBLIC
    INTERFACE

    缺省值為PUBLIC

二端礼、可見性的傳遞(非常重要)

每一個(gè)Target對(duì)于自身設(shè)置的不同屬性處理

    對(duì)于private的property禽笑,不會(huì)傳遞入录,只會(huì)自己用。
    對(duì)于public的property佳镜,會(huì)傳遞僚稿,也自己用。
    對(duì)于interface的property蟀伸,會(huì)傳遞蚀同,但不會(huì)自己用

    public和interface的屬性是可傳遞屬性

可見性的傳遞是依靠target_link_libraries,傳遞的規(guī)則如下:

假設(shè)如下鏈接關(guān)系
target_link_libraries(B XXX A)// XXX為private啊掏,public蠢络,interface

    如果XXX為private,A的可傳遞屬性變成B的private property
    如果XXX為public迟蜜,A的可傳遞屬性變成B的public property
    如果XXX為interface刹孔,A的可傳遞屬性變成B的interface property

三、實(shí)戰(zhàn)1

3.1 最簡(jiǎn)單的demo


interface_a.h

#ifndef CPP_INTERFACE_A_H
#define CPP_INTERFACE_A_H
 
int addA(int a, int b);
 
#endif //CPP_INTERFACE_A_H

interface_b.h

#ifndef CPP_INTERFACE_B_H
#define CPP_INTERFACE_B_H
 
int addB(int a, int b);
#endif //CPP_INTERFACE_B_H

interface_a.cpp

#include <stdio.h>
#include "interface_a.h"
 
int addA(int a, int b) {
    printf("addA\n");
    return a + b;
}

interface_b.cpp

#include <stdio.h>
#include "interface_b.h"
#include "interface_a.h"
 
int addB(int a, int b)
{
    printf("addB\n");
    return addA(a, b);
}

main.cpp

#include "interface_b.h"
#include <stdio.h>
 
int main()
{
    printf("main\n");
    addB(1, 2);
    return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.22)
project(CPP)
 
set(CMAKE_CXX_STANDARD 17)
 
add_library(A libA/interface_a.c)
target_include_directories(A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/includeA)
 
add_library(B SHARED libB/interface_b.c)
target_link_libraries(B A)
target_include_directories(B PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/includeB)
 
add_executable(CPP main.c)
target_link_libraries(CPP B)

用圖來表示代碼就如下小泉,CPP調(diào)用B中addB芦疏,B中的addB調(diào)用addA



最后運(yùn)行的結(jié)果

main
addB
addA

這例子簡(jiǎn)單吧冕杠,我們進(jìn)一步來解讀一下CMakeLists.txt微姊,紅色為傳遞過來的屬性



查看對(duì)應(yīng)的cmake的編譯中間文件,可以進(jìn)一步驗(yàn)證我們的判斷分预,正好和對(duì)應(yīng)的屬性對(duì)應(yīng)兢交。


3.2 main中能否調(diào)用addA

可以看到CPP擁有target_include_directories(CPP PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/includeA和target_link_libraries(CPP A)的屬性
理論上來說肯定main.cpp可以調(diào)用addA

修改main.cpp

#include "interface_b.h"
#include "interface_a.h"
#include <stdio.h>
 
int main()
{
    printf("main\n");
    addA(1, 2);
    addB(1, 2);
    return 0;
}

成功運(yùn)行

main
addA
addB
addA

3.3 將PUBLIC改成PRIVATE

如果我們對(duì)CMakeLists.txt做如下修改,請(qǐng)問上面main.c還能不能正常運(yùn)行

cmake_minimum_required(VERSION 3.22)
project(CPP)
 
set(CMAKE_CXX_STANDARD 17)
 
add_library(A libA/interface_a.c)
target_include_directories(A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/includeA)
 
add_library(B SHARED libB/interface_b.c)
target_link_libraries(B PRIVATE A)//改動(dòng)的地方
target_include_directories(B PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/includeB)
 
add_executable(CPP main.c)
target_link_libraries(CPP B)

解讀一下CmakeLists.txt笼痹,紅色為傳遞過來的屬性


和3.2中最大的差異就是CPP中includeA沒了配喳,那main.c肯定找不到#include "interface_a.h",所以會(huì)編譯報(bào)錯(cuò)找不到頭文件interface_a.h
運(yùn)行結(jié)果果然和預(yù)料的一樣凳干。

/home/kobe/submits/CPP/main.c:2:10: fatal error: interface_a.h: No such file or directory
    2 | #include "interface_a.h"
      |          ^~~~~~~~~~~~~~~

3.4 手動(dòng)添加includeA

繼續(xù)修改 CMakeLists.txt

cmake_minimum_required(VERSION 3.22)
project(CPP)
 
set(CMAKE_CXX_STANDARD 17)
 
add_library(A libA/interface_a.c)
target_include_directories(A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/includeA)
 
add_library(B SHARED libB/interface_b.c)
target_link_libraries(B PRIVATE A)
target_include_directories(B PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/includeB)
 
add_executable(CPP main.c)
target_link_libraries(CPP B)
target_include_directories(CPP PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/includeA)//修改的代碼

解讀一下CmakeLists.txt晴裹,紅色為傳遞過來的屬性,紫色是CPP額外加的屬性


看到C自身屬性添加了includeA救赐,那頭文件也有了涧团,鏈接的時(shí)候,CPP鏈接B经磅,B鏈接A泌绣,最后可以鏈接到一起,CPP應(yīng)該可以使用addA了
運(yùn)行結(jié)果果然可以

main
addA
addB
addA

四.實(shí)戰(zhàn)2

4.1 Interface的作用

修改文件interface_b.cpp预厌,移除B對(duì)A的addA的使用

#include <stdio.h>
#include "interface_b.h"
 
int addB(int a, int b)
{
    printf("addB\n");
    return a + b;
}

修改文件cmakelists.txt

cmake_minimum_required(VERSION 3.22)
project(CPP)
 
set(CMAKE_CXX_STANDARD 17)
 
add_library(A libA/interface_a.c)
target_include_directories(A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/includeA)
 
add_library(B SHARED libB/interface_b.c)
target_link_libraries(B INTERFACE A)
target_include_directories(B PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/includeB)
 
add_executable(CPP main.c)
target_link_libraries(CPP B)

解讀一下CmakeLists.txt阿迈,紅色為傳遞過來的屬性


因?yàn)镃PP使用到A的接口和B的接口,B沒有使用A的接口轧叽,所以按照上面的屬性苗沧,A刊棕,B,CPP三個(gè)都可以正常編譯運(yùn)行

main
addA
addB

4.2 add_library(C INTERFACE) -- 比較特殊的用法

修改文件cmakelists.txt

cmake_minimum_required(VERSION 3.22)
project(CPP)
 
set(CMAKE_CXX_STANDARD 17)
 
add_library(A libA/interface_a.c)
target_include_directories(A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/includeA)
 
add_library(C INTERFACE)
target_include_directories(C INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/includeB)
 
add_library(B SHARED libB/interface_b.c)
target_link_libraries(B PUBLIC C INTERFACE A)
 
add_executable(CPP main.c)
target_link_libraries(CPP B)

這是種特殊的用法待逞,就是創(chuàng)建一個(gè)虛擬的target C鞠绰,add_library(C INTERFACE)不會(huì)編譯出任何庫(kù)和可執(zhí)行文件,而且C的所有屬性必須設(shè)置為INTERFACE

解讀一下CmakeLists.txt飒焦,紅色為傳遞過來的屬性



最后也可以完美的運(yùn)行蜈膨!

這里C就是一個(gè)header-only的庫(kù),他的所有屬性都是Interface的牺荠,不會(huì)編譯出任何庫(kù)翁巍,唯一作用就是將屬性傳遞給link它的目標(biāo)。

五休雌、總結(jié)

按照1.原理和2.可見性的傳遞灶壶,對(duì)應(yīng)每一個(gè)項(xiàng)目,用這樣子的表格列出來每一個(gè)target對(duì)應(yīng)的屬性杈曲,也就可以了解到每一個(gè)target編譯依賴的頭文件以及庫(kù)文件驰凛。記住以Target的視角來看待每一個(gè)屬性,關(guān)注兩個(gè)Target之間的link的屬性担扑,以及兩個(gè)Target之間的屬性傳遞恰响。

六、參考文獻(xiàn)

https://chunleili.github.io/cmake/understanding-INTERFACE

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末涌献,一起剝皮案震驚了整個(gè)濱河市胚宦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌燕垃,老刑警劉巖枢劝,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異卜壕,居然都是意外死亡您旁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門轴捎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鹤盒,“玉大人,你說我怎么就攤上這事轮蜕∽虻浚” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵跃洛,是天一觀的道長(zhǎng)率触。 經(jīng)常有香客問我,道長(zhǎng)汇竭,這世上最難降的妖魔是什么葱蝗? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任穴张,我火速辦了婚禮,結(jié)果婚禮上两曼,老公的妹妹穿的比我還像新娘皂甘。我一直安慰自己,他們只是感情好悼凑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布偿枕。 她就那樣靜靜地躺著,像睡著了一般户辫。 火紅的嫁衣襯著肌膚如雪渐夸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天渔欢,我揣著相機(jī)與錄音墓塌,去河邊找鬼。 笑死奥额,一個(gè)胖子當(dāng)著我的面吹牛苫幢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播垫挨,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼韩肝,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了棒拂?” 一聲冷哼從身側(cè)響起伞梯,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帚屉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體漾峡,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡攻旦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了生逸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牢屋。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖槽袄,靈堂內(nèi)的尸體忽然破棺而出烙无,到底是詐尸還是另有隱情,我是刑警寧澤遍尺,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布截酷,位于F島的核電站,受9級(jí)特大地震影響乾戏,放射性物質(zhì)發(fā)生泄漏迂苛。R本人自食惡果不足惜三热,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望三幻。 院中可真熱鬧就漾,春花似錦、人聲如沸念搬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)朗徊。三九已至夷野,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荣倾,已是汗流浹背悯搔。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留舌仍,地道東北人妒貌。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像铸豁,于是被迫代替她去往敵國(guó)和親灌曙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容