簡體   English   中英

cmake 使用 find_package 傳播依賴項

[英]cmake propagate dependencies using find_package

舉一個簡單的例子:有兩個庫和一個可執行文件。 兩個庫都是SHARED (即 .so 文件)。 一種稱為libMain ,一種稱為libUtil libMain使用libUtil ,可執行文件也是如此。 可執行文件可能會單獨使用libUtil ,但通常它會調用libMain中的一個方法,該方法在其實現中確實使用了libUtil

所以在閱讀了一些關於 CMake 的教程和文檔之后,這個例子看起來相當簡單。 每個項目都有一個簡單的 CMakeLists.txt,而libUtil鏈接到libMain ,可執行鏈接到libMain (我省略了target_include_directories以節省一些行)所有項目都使用相同的 PREFIX_PATH。

庫實用程序

include(GNUInstallDirs)

project(libUtil)
set(CMAKE_SHARED_LIBRARY_PREFIX "")    
add_library(libUtil SHARED main.cpp)
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Config
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(EXPORT ${PROJECT_NAME}Config DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake)

主庫

include(GNUInstallDirs)

project(libMain)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
add_library(libUtil SHARED main.cpp)
find_package(libUtil REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC libUtil)
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Config
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(EXPORT ${PROJECT_NAME}Config DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake)

可執行文件

project(exe)
find_package(libMain REQUIRED)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
add_executable(exe main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE libMain)

但是,在構建可執行文件時,我最終遇到了鏈接器錯誤,告訴 exe無法執行 -llibUtil

我已經花了很多時間,在閱讀了幾篇文章后,我發現(或者至少我這么認為......)我需要提供一個自定義的 libMainConfig.cmake (我將生成的重命名為 libMainTargets.cmake)

include("${CMAKE_CURRENT_LIST_DIR}/libMainTargets.cmake")

find_package(libUtil REQUIRED)

鏈接器錯誤現在消失了,我可以運行我的項目。 我仍然不明白為什么我需要這樣做。 我知道處理已經構建的庫時存在差異。 然而,由於 CMake 總是使用基於目錄的項目分離,我如何以及為什么要在同一個 CMakeLists.txt 中構建多個項目? 到目前為止,我一直使用find_package (它本質上是 add_library IMPORTED,即搜索預構建庫,如果我沒記錯的話)並希望將其定義的依賴項傳播到“消費”項目。 但似乎來自導入目標的所有PUBLICINTERFACEPRIVATE對“消費”目標沒有影響?

請在解釋中詳細說明和/或指出我遺漏的一些參考資料,並指出一些最佳實踐,假設所有項目要么由我自己構建,要么是像 boost 或 JNI 這樣的系統庫。

謝謝!

CMake 有一個特定的函數來傳播find_package這樣的稱為find_dependency 你可以這樣使用它:

include(CMakeFindDependencyMacro)
find_dependency(libutil)

CMake 不會在其生成的文件中導出調用find_package 您必須使用find_packagefind_dependency手動執行此操作。 不同之處在於find_dependency將正確轉發REQUIREDQUIET

為什么 CMake 嘗試將-llibUtil發送到鏈接器?

target_link_libraries添加內容時,有兩種情況:

  1. 要鏈接的庫是目標。 在這種情況下,接口屬性會正確傳播,包括需要時的鏈接。
  2. 它只是一個字符串,沒有目標。 在這種情況下, CMake 假定它是一個系統庫並添加-l標志

由於鏈接到libutil是一個公共財產libmain ,所有消費者libmain也將鏈接到libutil

由於調用 find 包必須手動完成,因此libutil不是目標。 假設鏈接到系統庫,因此 CMake 會將exe到名為libutil的系統庫,該庫不存在。

調用find_package(libUtil REQUIRED)將確保exe通過鏈接目標而不是庫來消耗libutil的使用要求。

libUtil如何成為目標?

CMake 目標不(完全)綁定到目錄。 您可以擁有GLOBALIMPORTED目標,也可以按項目擁有多個目標。 想象一個由多個庫和可執行文件組成的項目。 一個類比是 CMake 項目是 Visual Studio 解決方案,而 CMake 目標是 Visual Studio 項目。

現在在導入的目標上。 它們旨在表示已由另一個項目編譯的目標,您安裝或導出了構建樹。 導入的目標讓你可以像普通目標一樣使用來自不同項目的東西,就好像你聲明了它一樣。 導入的目標比編譯器標志更精確:它們當然攜帶庫,但也攜帶如何鏈接、所需的編譯器標志、包含目錄和其他要求。

find_packagefind_dependency用於查找配置文件,這些文件包含導入的目標信息。 之后,只需鏈接到它就會設置包含目錄、正確的鏈接器標志等。

導出多個目標的項目的一個很好的例子是 SFML,它將聲音、圖形和窗口管理等不同模塊導出為不同的靜態/動態庫。

怎樣才能避免這種愚蠢的錯誤呢?

CMake 讓我們為目標使用命名空間。 使用命名空間語法時,不能是系統庫。 CMake 將輸出未找到目標的錯誤,而不是嘗試鏈接到不存在的庫。

install(
    EXPORT ${PROJECT_NAME}Config
    NAMESPACE libUtil
    DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake
)

然后將鏈接更改為:

target_link_libraries(${PROJECT_NAME} PUBLIC libUtil::libUtil)

命名空間名稱的約定是使用與包相同的名稱。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM