首先強烈推薦對CMake不熟的同學先看這本書《Cmake實踐》

CMake說起來是個好東西,可是真正用的時候並不那麼容易,很容易出現各種各樣的錯誤。這不,最近就被find_package這個命令折騰得死去活來。只好花了一天半時間,看上面那本書,再查資料,總算解決了昨天遇到的問題。

問題描述

已經成功編譯了深度學習框架Caffe,例程也可以順利執行。

但是當我在自己的代碼中調用編譯好的Caffe庫時,卻出現了編譯錯誤。此前,我已經在CMakeLists.txt中添加了下面幾句話:

include_directories(/home/wjg/projects/caffe/build/install/include)
add_executable(useSSD ssd_detect.cpp)
target_link_libraries(useSSD /home/wjg/projects/caffe/build/install/lib/libcaffe.so)

執行make后,鏈接出錯,找不到libboost_system.so文件。

這一錯誤倒是給我提了個醒,我本以為自己的代碼中沒用到boost,就不必添加boost庫路徑了,誰知道libcaffe.so中用到的庫也需要手動添加進去。

這時候我才意識到動態鏈接庫和靜態鏈接庫的區別。前者在程序運行時動態加載,而後者是在編譯時就和程序結合到一起了。於是動態鏈接庫即使編譯完成,也和其它動態庫是分離的,因此每次用都要把所有涉及的動態庫全部添加進來。在我的例子中,不僅僅需要添加boost,還有atlas、protobuf等等一大堆動態鏈接庫需要添加。這個時候,一條條添加就顯得太過麻煩,可以藉助find_package命令一次性添加所有與Caffe相關的動態鏈接庫。

find_package用法

使用如下方式查找Caffe庫:

find_package(Caffe REQUIRED)

如果找到Caffe庫,就可以在接下來的語句中使用Caffe_INCLUDE_DIRSCaffe_LIBRARIES這兩個變量,比如

find_package(Caffe REQUIRED)

if (NOT Caffe_FOUND)
    message(FATAL_ERROR "Caffe Not Found!")
endif (NOT Caffe_FOUND)

include_directories(${Caffe_INCLUDE_DIRS})

add_executable(useSSD ssd_detect.cpp)
target_link_libraries(useSSD ${Caffe_LIBRARIES})

問題是,很多情況下都會找不着,或者找到了錯誤的位置。要想用對find_package,就需要了解它的工作原理。

find_package原理

首先明確一點,cmake本身不提供任何搜索庫的便捷方法,所有搜索庫並給變量賦值的操作必須由cmake代碼完成,比如下面將要提到的FindXXX.cmake和XXXConfig.cmake。只不過,庫的作者通常會提供這兩個文件,以方便使用者調用。

find_package採用兩種模式搜索庫:

  • Module模式:搜索CMAKE_MODULE_PATH指定路徑下的FindXXX.cmake文件,執行該文件從而找到XXX庫。其中,具體查找庫並給XXX_INCLUDE_DIRSXXX_LIBRARIES兩個變量賦值的操作由FindXXX.cmake模塊完成。
  • Config模式:搜索XXX_DIR指定路徑下的XXXConfig.cmake文件,執行該文件從而找到XXX庫。其中具體查找庫並給XXX_INCLUDE_DIRSXXX_LIBRARIES兩個變量賦值的操作由XXXConfig.cmake模塊完成。

兩種模式看起來似乎差不多,不過cmake默認採取Module模式,如果Module模式未找到庫,才會採取Config模式。如果XXX_DIR路徑下找不到XXXConfig.cmake文件,則會找/usr/local/lib/cmake/XXX/中的XXXConfig.cmake文件。總之,Config模式是一個備選策略。通常,庫安裝時會拷貝一份XXXConfig.cmake到系統目錄中,因此在沒有顯式指定搜索路徑時也可以順利找到。

在我遇到的問題中,由於Caffe安裝時沒有安裝到系統目錄,因此無法自動找到CaffeConfig.cmake,我在CMakeLists.txt最前面添加了一句話之後就可以了。

set(Caffe_DIR /home/wjg/projects/caffe/build)   #添加CaffeConfig.cmake的搜索路徑

find_package(Caffe REQUIRED)

if (NOT Caffe_FOUND)
    message(FATAL_ERROR "Caffe Not Found!")
endif (NOT Caffe_FOUND)

include_directories(${Caffe_INCLUDE_DIRS})

add_executable(useSSD ssd_detect.cpp)
target_link_libraries(useSSD ${Caffe_LIBRARIES})

其實關於find_package還有許多知識點,可惜我也沒能全部掌握。XXXConfig.cmake的默認搜索路徑也不止一個,它們有詳細的優先級順序。對於庫的開發者來說,如何生成FindXXX.cmake或XXXConfig.cmake文件更是一個複雜工程,需要了解更多的知識,希望以後有機會再深入了解。

參考資料

CMake 2.8.10 Documentation

分享