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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

cmake的使用-目标类型详解

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

Target Types

本章概括

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

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

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

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

可執行程序

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

第四章-構建簡單對象

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

本章新增

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

字段說明

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

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

test1將git導入到當前工程中

set(GIT_EXECUTABLE "/usr/bin/git") add_executable(AAAAA IMPORTED) # 設置 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}")

輸出結果:

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

說明:IMPORTED定義的可執行對象稱為可執行導入對象

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

ALIAS和名字一樣,就是為目標在CMake工程中建立個別名,不過這個別名是只讀的。創建別名并不是真的為別名構建一個和別名一樣的對象,而只是創建一個指向真實對象的別名,別名同樣不能被安裝和導出,并且別名的別名是不被允許的。

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

庫文件

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

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

第四章中沒有進行擴展的命令:

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

CMake3.12版本之前:

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

CMake3.12以及之后的版本:

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

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

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

許多開發者可能會發現,幾乎沒有CMake工程中使用目標庫,因為相比于目標庫,靜態庫是一個更加方便,更優的選擇。

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

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

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

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

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

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

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

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 不對該可執行程序進行編譯 set(executeProcess myExe) # 使用變量 add_executable(${executeProcess} main.c echo_demo)

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

具體形式如下:

add_library(targetName INTERFACE [IMPORTED [GLOBAL]])

所有需要傳遞的信息都可以通過target_…()開頭使用INTERFACE關鍵字的命令轉發。當然你也可以使用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目標鏈接了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)

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

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

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

具體的關系總結如下表:

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

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

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

add_library(aliasName ALIAS otherTarget)

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

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

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

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)

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

# 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來查找一個已安裝的包,可以使用下面的方式,添加該實現。

# 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會總是認為具有雙冒號::的名字是別名或者導入目標。除了這兩種情況,任何目標以其他方式使用::將會被當成錯誤處理。

突然來了這一句不知道是不是為了湊字數:在使用target_link_library()時,如果沒法獲取目標的名稱,CMake會拋出錯誤。同樣當庫名沒有提供的時候,CMake會默認為其為系統的庫,將會在構建的過程中出現錯誤如下:

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 不對該可執行程序進行編譯 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)

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

導入目標的提升

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

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

# 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 )

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

建議實踐

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

例如:

# 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 )

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。