日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

cmake的使用-目标类型详解

發(fā)布時間:2025/3/15 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 cmake的使用-目标类型详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Target Types

本章概括

CMake支持的類型有很多種,可以是可執(zhí)行文件或者庫文件,也可以在不構(gòu)建的情況下作為其它實體的引用。

使用這種引用作用:可以在不將對象構(gòu)建成自己的二進(jìn)制文件的情況下,為這些被引用的文件屬性和依賴項。你也可以將這些被引用的文件稱為一種新型,這種庫不是傳統(tǒng)意義上的靜態(tài)庫或者動態(tài)庫,而是一種對象文件的集合。

很多事物都可以抽象為一個對象來隱藏復(fù)雜的平臺的差異、文件在系統(tǒng)中的位置、文件名等,本章會介紹所有對象類型,并介紹他們的用途。

除了上述提到的靜態(tài)庫、動態(tài)庫以及引用庫,還有另外一種對象,這種對象實用的程序或者用戶自定義對象。有了這些對象,用戶就可以執(zhí)行任意的命令和自定義生成規(guī)則,允許項目實現(xiàn)幾乎任何類型需要的行為(深入講解在第17章中)。

可執(zhí)行程序

add_executable()命令,不僅有第四章中給出的構(gòu)建簡單對象的形式,還有另外兩種形式,如下:

第四章-構(gòu)建簡單對象

add_executable(targetName [WIN32] [MACOSX_BUNDLE][EXCLUDE_FROM_ALL]source1 [source2 ...] )

本章新增

add_executable(targetName IMPORTED [GLOBAL]) add_executable(aliasName ALIAS targetName)

字段說明

IMPORTED選項,可以通過已有的可執(zhí)行程序創(chuàng)建一個CMake對象而不是在項目中構(gòu)建。雖然這種對象只是可執(zhí)行程序在項目中的表現(xiàn)方式,但是項目的其他部分可像對待項目中自己構(gòu)建的對象一樣對待這個對象。這樣使用的好處就是,當(dāng)該對象可以用在CMake的上下文中時,CMake會自動將對象名替換為其在磁盤上的位置。例如可以執(zhí)行測試命令或者用戶自定義命令,這些將在后面的章節(jié)講到。和普通對象不同的是該對象不能進(jìn)行安裝。

當(dāng)使用IMPORTED選項定義一個可執(zhí)行導(dǎo)入對象時,在正式使用之前需要對對象的一些屬性進(jìn)行設(shè)置。大多數(shù)與導(dǎo)入對象相關(guān)的屬性都已IMPORTED開頭,對于可執(zhí)行目標(biāo)其中兩個需要特別注意的就是IMPORTED_LOCATION和IMPORTED_LOCATION_<CONFIG>。當(dāng)需要可執(zhí)行導(dǎo)入對象的位置時,CMake會首先檢查給對象的特定配置屬性(詳情看屬性章節(jié)),只有特定配置沒有設(shè)置的時候才會查找更加普遍的IMPORTED_LOCATION屬性。通常來說位置不需要特定的配置,所以只設(shè)置IMPORTED_LOCATION屬性是非常常見的。

test1將git導(dǎo)入到當(dāng)前工程中

set(GIT_EXECUTABLE "/usr/bin/git") add_executable(AAAAA IMPORTED) # 設(shè)置 IMPORTED_LOCATION 屬性 set_property(TARGET AAAAA PROPERTY IMPORTED_LOCATION "${GIT_EXECUTABLE}") #獲取屬性并打印出來 get_target_property(git_location AAAAA IMPORTED_LOCATION) get_target_property(git_imported AAAAA IMPORTED) message(">>> git location: ${git_location}, ${git_imported}")

輸出結(jié)果:

>>> git location: /usr/bin/git, TRUE

說明:IMPORTED定義的可執(zhí)行對象稱為可執(zhí)行導(dǎo)入對象

GLOBAL關(guān)鍵字可選,當(dāng)沒有指定GLOBAL時,導(dǎo)入對象只是在文件創(chuàng)建目錄以及子目錄可見,當(dāng)指定GLOBAL關(guān)鍵字的時候,導(dǎo)入對象在整個目錄可見。與你想象中不一樣,實際使用中通常將導(dǎo)入目標(biāo)定義成全局的,其中的緣由以及一些減少可目標(biāo)可見度的實例將在第三小節(jié)中進(jìn)行說明。

ALIAS和名字一樣,就是為目標(biāo)在CMake工程中建立個別名,不過這個別名是只讀的。創(chuàng)建別名并不是真的為別名構(gòu)建一個和別名一樣的對象,而只是創(chuàng)建一個指向真實對象的別名,別名同樣不能被安裝和導(dǎo)出,并且別名的別名是不被允許的。

說明:CMake3.11之前,導(dǎo)入對象不能有別名,但是在3.11之后將這一限制放寬了一些,但是只有全局的導(dǎo)入對象能有別名

庫文件

add_library()同樣也有多種形式。第四章中介紹的構(gòu)建簡單對象中使用add_library()是我們常見的形式,該命令同樣支持定義目標(biāo)庫,目標(biāo)庫僅僅是目標(biāo)文件的集合,并不是真正意義上的構(gòu)建出一個靜態(tài)或者動態(tài)庫,擴(kuò)展之后的命令如下:

add_library(targetName [STATIC | SHARED | MODULE | OBJECT][EXCLUDE_FROM_ALL]source1 [source2 ...] )

第四章中沒有進(jìn)行擴(kuò)展的命令:

add_library(targetName [STATIC | SHARED | MODULE][EXCLUDE_FROM_ALL]source1 [source2 ...] )

CMake3.12版本之前:

  • 目標(biāo)庫不能像其他庫一樣被鏈接(例如:不能使用target_link_libraries())
  • 要求使用生成器表達(dá)式作為其他可執(zhí)行或者庫對象的源碼部分
  • 因為不能被鏈接,所以不能透傳依賴關(guān)系給鏈接它的對象
  • 不方便,頭文件路徑,編譯定義等信息都必須手動管理

CMake3.12以及之后的版本:

CMake3.12引入了一些特性,這些特性使目標(biāo)庫的表現(xiàn)更加像其他普通庫,但是有一些需要注意的地方。

  • 3.12之后可以使用target_link_libraries()
  • 因為添加的是目標(biāo)文件而不是真實的對象,傳遞當(dāng)前庫性質(zhì)是更加嚴(yán)格的,目的是為了防止目標(biāo)文件被多次添加到其他對象上

造成上述原因是因為,目標(biāo)庫是作為源碼直接添加到連接對象上的,而不是通過連接對象傳遞給其他對象。然而實際使用中目標(biāo)庫文件通常會向其他對象庫一樣通過連接的方式進(jìn)行傳播,因此,因此需要更加嚴(yán)格的限制。

許多開發(fā)者可能會發(fā)現(xiàn),幾乎沒有CMake工程中使用目標(biāo)庫,因為相比于目標(biāo)庫,靜態(tài)庫是一個更加方便,更優(yōu)的選擇。

就像可執(zhí)行文件一樣,庫文件同樣可以定義為導(dǎo)入的對象,他們被大量的用于打包過程或者Find module的執(zhí)行過程,除了這些作用在其他地方使用導(dǎo)入對象庫是有限制的。導(dǎo)入的對象庫不是定一個庫在項目中構(gòu)建出來,而是引用一個已經(jīng)在外部已經(jīng)存在的庫。

add_library(targetName (STATIC | SHARED | MODULE | OBJECT | UNKNOWN)IMPORTED [GLOBAL] )

庫的類型要緊隨targetName之后,如果庫的被引用類型是已知的,那么這個庫應(yīng)該按照這種類型聲明,這樣講允許CMake對待導(dǎo)入對象庫(IMPORED 聲明的)像普通工的庫一樣。

OBJECT類型只有CMake3.9之后的版本才能設(shè)置,之前的版本不支持這個類型,如果庫的類型未知,應(yīng)該設(shè)置成UNKNOWN類型,這樣CMake就會使用庫的全路徑,而不是將來連接的時候檢查復(fù)雜的鏈接器指令。

除了OBJECT libraries的上述類型,IMPORTED聲明的庫 在系統(tǒng)中的位置信息需要使用IMPORTED_LOCATION和/或IMPORTED_LOCATION_<CONFIG>進(jìn)行屬性設(shè)置(例如:在windows系統(tǒng)上兩個屬性應(yīng)該設(shè)置IMPORTED_LOCATION 與DLL的關(guān)聯(lián),IMPORTED_IMPLIB與導(dǎo)入庫關(guān)聯(lián)通常有.lib的擴(kuò)展名),而對于OBJECT libraries更重要的是使用IMPORTED_OBJECTS為導(dǎo)入目標(biāo)庫(imported target)其代表的一系列目標(biāo)文件設(shè)置屬性

導(dǎo)入庫也支持許多其他的目標(biāo)屬性,這些屬性大多數(shù)可以不管或者使用CMake的默認(rèn)配置。需要編寫配置包的開發(fā)者,應(yīng)該通過CMake參考手冊去了解IMPORTED_開頭的目標(biāo)屬性。大多數(shù)CMake工程依賴CMake生成這些配置包,因此、需要手動去編寫配置包的情況應(yīng)該相當(dāng)罕見。

默認(rèn)情況下,導(dǎo)入庫通常被定義為本地目標(biāo),這就意味者其僅在當(dāng)前目錄和子目錄中可見。給出GLOBAL關(guān)鍵字時可以使導(dǎo)入庫全局可見。一個庫可能創(chuàng)建的時候不是全局的,但是隨著使用的需要,后期需要變成全局可見的,這些問題將在第三小節(jié)的提升導(dǎo)入對象小節(jié)進(jìn)行詳細(xì)說明。

test2-工程外部庫的使用

# test 2 markdown message("================================test2========================") set(echo_demo_location "/work/libecho_demo.a") add_library(echo_demo STATIC IMPORTED) set_target_properties(echo_demo PROPERTIESIMPORTED_LOCATION "${echo_demo_location}"#INTERFACE_LINK_LIBRARIES collector ) get_target_property(collector_data echo_demo IMPORTED_LOCATION) get_target_property(collector_IMPORT_data echo_demo IMPORTED) message(">>> : ${collector_data}, ${collector_IMPORT_data}")#------------------------------------------------------- # EXCLUDE_FROM_ALL 不對該可執(zhí)行程序進(jìn)行編譯 set(executeProcess myExe) # 使用變量 add_executable(${executeProcess} main.c echo_demo)

另外一種形式的add_library()命令允許定義接口庫。接口庫通常不是一個真實存在的庫,而是用于收集需求以及依賴關(guān)系傳遞給任何連接他們的東西。通常見到的地方是為頭文件建立一個庫,這個庫不能真實生成一個庫,但是可以將頭文件路徑,編譯配置等信息轉(zhuǎn)發(fā)給任何使用這些頭文件的東西。

具體形式如下:

add_library(targetName INTERFACE [IMPORTED [GLOBAL]])

所有需要傳遞的信息都可以通過target_…()開頭使用INTERFACE關(guān)鍵字的命令轉(zhuǎn)發(fā)。當(dāng)然你也可以使用set_property() or set_target_properties()但是使用target_…()更加安全。

add_library(myHeaderOnlyToolkit INTERFACE) target_include_directories(myHeaderOnlyToolkitINTERFACE /some/path/include ) target_compile_definitions(myHeaderOnlyToolkitINTERFACE COOL_FEATURE=1$<$<COMPILE_FEATURES:cxx_std_11>:HAVE_CXX11> ) add_executable(myApp ...) target_link_libraries(myApp PRIVATE myHeaderOnlyToolkit)

上述的示例中myApp目標(biāo)鏈接了myHeaderOnlyToolkit那么通過INTERFACE定義的頭文件路徑/some/path/include以及COOL_FEATURE=1將傳遞給myApp.如果支持cxx_std_11 HAVE_CXX11也將傳遞過去

demo – test4

#This should be the first line of the CMakeLists.txt cmake_minimum_required(VERSION 3.16)# Poor practice, but very common set(projectName MyProject) project(${projectName} VERSION 4.7.2 LANGUAGES C) #--------------------------------------------------------------------------------- # test 4 markdown message("================================test4========================")add_library(echo_demo echo_demo.c)add_library(myHeaderOnlyToolkit INTERFACE) target_include_directories(myHeaderOnlyToolkitINTERFACE ./inc ) target_compile_definitions(myHeaderOnlyToolkitINTERFACE COOL_FEATURE=1 )add_executable(myApp main.c) target_link_libraries(myApp PRIVATE myHeaderOnlyToolkit) target_link_libraries(echo_demo PRIVATE myHeaderOnlyToolkit) target_link_libraries(myApp PRIVATE echo_demo)#================================================

另外一種接口庫的使用方式就是,通過接口庫來方便的連接一系列庫,如:

# Regular library targets add_library(algo_fast ...) add_library(algo_accurate ...) add_library(algo_beta ...) # Convenience interface library add_library(algo_all INTERFACE) target_link_libraries(algo_all INTERFACE algo_fast algo_accurate # 只有ENABLE_ALGO_BETA為true的時候才會鏈接algo_beta $<$<BOOL:${ENABLE_ALGO_BETA}>:algo_beta> ) # Other targets link to the interface library # instead of each of the real libraries add_executable(myApp ...) target_link_libraries(myApp PRIVATE algo_all)

這種將不同的庫抽象出來再實際中是非常有用的,經(jīng)過抽象可以實現(xiàn)不同平臺使用同樣的庫名,實際使用中只需要按照某些平臺相關(guān)變量,將不同平臺上庫名不同但是實際作用相同的庫抽象成同樣的庫名。實現(xiàn)屏蔽平臺差異

接口庫的原理很好理解,但是添加上IMPORTED之后的導(dǎo)入接口庫會引起一些混淆。當(dāng)INTERFACE庫需要在工程外部使用的時候,常需要使用這種組合之后的導(dǎo)入接口庫。當(dāng)接口庫被另外一個庫使用時仍然是起到接口庫的作用的,添加IMPORTED關(guān)鍵字是為了表明接口庫來至其他地方,這樣做的效果就是將庫的可見性限制在當(dāng)前目錄范圍內(nèi),而不是全局可見。有一個例外就是,當(dāng)導(dǎo)入接口庫聲明GLOBAL的時候,幾乎與接口庫的作用一致,可見性也會變成全局的。導(dǎo)入接口庫不需要設(shè)置(實際上是禁止)設(shè)置IMPORTED_LOCATION屬性

在CMake3.11之前的版本,target_…()命令不能在導(dǎo)入庫上設(shè)置INTERFACE_....屬性,但是可以使用set_property() or set_target_properties()設(shè)置。在3.11去除這個限制之后,INTERFACE IMPORTED聲明和庫的使用就和INTERFACE IMPORTED聲明的庫的使用非常接近了。

具體的關(guān)系總結(jié)如下表:

KeywordsVisibilityImported LOacationSet Interface PropertiesInstallable
INTERFACEGlobalProhibitedAny methodYes
IMPORTEDLocalRequiredRestrictedNO
IMPORTED GLOBALGlobalRequiredRestrictedNO
INTERFACE IMPORTEDLocalProhibitedRestrictedNO
INTERFACE IMPORTED GLOBALGlobalProhibitedRestrictedNO

接口和導(dǎo)入庫結(jié)合過于復(fù)雜和混亂,對于大多數(shù)開發(fā)者來說,上述表格中很多都是不需要關(guān)注的,只有純INTERFACE聲明的接口庫開發(fā)中會自己創(chuàng)建,剩下的工具自動完成的,不需要開發(fā)者實際參與。除了INTERFACE剩余的組合聲明將在25章中進(jìn)一步說明。

add_library()的最后一種使用方式是為庫定義別名:

add_library(aliasName ALIAS otherTarget)

庫別名和可執(zhí)行程序別名是一樣的,也是一種只讀方式的別名。庫別名同樣不能安裝,不能再次定義別名,也就是不能定義庫別名的別名。

說明:3.11之前庫別名不能聲明IMPORTED,3.11之后放開了這個限制

庫別名的使用和CMake3.0引入的一項新特性相關(guān),對于將要安裝的庫,一種常見模式是為庫創(chuàng)建一個別名,別名以工程名開頭作為自己的域名,所有該工程中的庫的別名都共享這個域名:

test5

# Any sort of real library (SHARED, STATIC, MODULE # or possibly OBJECT) add_library(myRealThings SHARED src1.cpp ...) add_library(otherThings STATIC srcA.cpp ...) # Aliases to the above with special names add_library(BagOfBeans::myRealThings ALIAS myRealThings) add_library(BagOfBeans::otherThings ALIAS otherThings)

在當(dāng)前項目中鏈接原庫或者鏈接域名庫效果是一樣的,使用別名的動機(jī)來至于—項目的安裝和一些來至installed/packaged的配置文件中創(chuàng)建的導(dǎo)入目標(biāo)(imported targets),這些配置文件將導(dǎo)入庫定義成帶有域名的方式,工程也需要使用域名的方式引用這些庫,例如:

# Pull in imported targets from an installed package. # See details in Chapter 23: Finding Things find_package(BagOfBeans REQUIRED)# Define an executable that links to the imported# library from the installed packageadd_executable(eatLunch main.cpp ...)target_link_libraries(eatLunch PRIVATEBagOfBeans::myRealThings )

如果將整個BagOfBeans工程直接合并到自己的項目中,而不是通過find_package來查找一個已安裝的包,可以使用下面的方式,添加該實現(xiàn)。

# Add BagOfBeans directly to this project, making # all of its targets directly available add_subdirectory(BagOfBeans)# Same definition of linking relationship still worksadd_executable(eatLunch main.cpp ...)target_link_libraries(eatLunch PRIVATEBagOfBeans::myRealThings )

另外一個重要的點是,CMake會總是認(rèn)為具有雙冒號::的名字是別名或者導(dǎo)入目標(biāo)。除了這兩種情況,任何目標(biāo)以其他方式使用::將會被當(dāng)成錯誤處理。

突然來了這一句不知道是不是為了湊字?jǐn)?shù):在使用target_link_library()時,如果沒法獲取目標(biāo)的名稱,CMake會拋出錯誤。同樣當(dāng)庫名沒有提供的時候,CMake會默認(rèn)為其為系統(tǒng)的庫,將會在構(gòu)建的過程中出現(xiàn)錯誤如下:

test6

#This should be the first line of the CMakeLists.txtcmake_minimum_required(VERSION 3.16) # Poor practice, but very common set(projectName MyProject) project(${projectName} VERSION 4.7.2 LANGUAGES C) #---------------------------------------------------------------------------------- message("================================test6========================")----------------------------------------- add_library(echo_demo echo_demo.c) # EXCLUDE_FROM_ALL 不對該可執(zhí)行程序進(jìn)行編譯 set(executeProcess myExe) # 使用變量 add_executable(${executeProcess} main.c echo_demo) target_link_libraries(${executeProcess}# PRIVATE echo_demoPRIVATE name_test )#================================================

書中給出能拋出異常的庫:

add_executable(main main.cpp) add_library(bar STATIC ...) add_library(foo::bar ALIAS bar) # Typo in name being linked to, CMake will assume a # library called "bart" will be provided by the # system at link time and won't issue an error. target_link_libraries(main PRIVATE bart) # Typo in name being linked to, CMake flags an error # at generation time because a namespaced name must # be a CMake target. # 因為域名不是工程的名字,所以同樣也會報錯 target_link_libraries(main PRIVATE foo::bart)

當(dāng)域名存在時,連接帶有域名的庫更加的可靠和健壯,工程中鼓勵使用域名,特別是為將要安裝和打包的目標(biāo)定義帶有域名的別名。這種域名別名自己的工程也能使用,并不僅僅限于用在在子工程或者其他預(yù)先編譯好的工程中

導(dǎo)入目標(biāo)的提升

導(dǎo)入目標(biāo)沒有聲明GLOBAL時,只能在當(dāng)前目錄或者子目錄中可見。這種行為源于他們的主要用途,這種行為也是查找模塊的特性,任何通過查找模塊獲取內(nèi)容通常希望本地可見,所以導(dǎo)入目標(biāo)通常不應(yīng)該添加GLOBAL變成全局可見性,這樣允許項目不同的模塊使用相同的包和模塊的同時使用不同的配置,并且保證相互之間互不干擾。

然而,有些時候需要導(dǎo)入目標(biāo)的可見性是全局的,例如需要在這個項目中使用特定的包和指定的版本的時候,需要在創(chuàng)建導(dǎo)入包的時候添加GLOBAL關(guān)鍵字實現(xiàn)這一點,但是創(chuàng)建的過程在項目中可能是不可控的。為了解決這個問題CMake3.11版本引入了一個提升的導(dǎo)入目標(biāo)控制屬性字段IMPORTED_GLOBAL,將其設(shè)置為ture即可實現(xiàn)導(dǎo)入目標(biāo)的全局可見性,需要注意的是這個屬性是單向的,因此一旦提升為全局可見,就不可能在改為局部可見

# Imported library created with local visibility. # This could be in an external file brought in # by an include() call rather than in the same # file as the lines further below. add_library(builtElsewhere STATIC IMPORTED) set_target_properties(builtElsewhere PROPERTIESIMPORTED_LOCATION /path/to/libSomething.a ) # Promote the imported target to global visibility set_target_properties(builtElsewhere PROPERTIESIMPORTED_GLOBAL TRUE )

重要的一點是,導(dǎo)入目標(biāo)只能在與定義的地方相同的域中進(jìn)行提升,不能提升父域中或者子域中定義的導(dǎo)入目標(biāo)。include()命令不創(chuàng)建文件域同時也不調(diào)用find_package()命令,所以通過include()命令引入的導(dǎo)入目標(biāo)是允許被提升的,這也是目標(biāo)提升的主要實現(xiàn)方式。一旦一個目標(biāo)被提升為全局可見,它同時也擁有了創(chuàng)建別名的能力。

建議實踐

  • CMake3.0版本對目標(biāo)之間依賴和要求之間的管理進(jìn)行了重大改變,解決了以前靠變量式的粗暴管理方式,是目標(biāo)的管理更加精細(xì)化
  • 一旦項目使用項目外部的包,就需要使用導(dǎo)入目標(biāo),開發(fā)人員應(yīng)該熟練使用
  • 一些久的項目使用變量的方式管理導(dǎo)入目標(biāo),應(yīng)該盡量替換為新的導(dǎo)入目標(biāo)管理方式
  • 盡量使用靜態(tài)庫而不是對象庫,對象庫有實際用途,但是靈活性不如靜態(tài)庫
  • 目標(biāo)的命令不要過于簡單,并且盡量為目標(biāo)添加域名
  • 當(dāng)對庫進(jìn)行重構(gòu)時,可能外部有依賴于這個老庫的文件,這時使用別名的方式提供老庫的連接方式

例如:

# Old library previously defined like this: add_library(deepCompute SHARED ...) # Now the library has been split in two, so define # an interface library with the old name to effectively # forward on the link dependency to the new libraries add_library(computeAlgoA SHARED ...) add_library(computeAlgoB SHARED ...) add_library(deepCompute INTERFACE) target_link_libraries(deepCompute INTERFACEcomputeAlgoAcomputeAlgoB )

總結(jié)

以上是生活随笔為你收集整理的cmake的使用-目标类型详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。