CMake: find_package and custom find cmake (findXXX.cmake)

CMake: find_package and custom find cmake (findXXX.cmake)

我個人覺得這主題寫最好的是這篇,我會建議大家可以看這篇,不要看我寫的XD
https://gitlab.kitware.com/cmake/community/wikis/doc/tutorials/How-To-Find-Libraries

find_package

find_package是CMake中用來找第三方package的command。這command功能是去找到所需第三方package的資訊(版本、include path、library path等),並將資訊存入定好的variable中。find_package會曲找尋CMakeLists.txt所在資料夾下的FindXXX.cmake,或是cmake本身定義好的FindXXX.cmake,通常系統路徑是/usr/share/cmake/Modules下(Arch Linux是放在/usr/share/cmake-版本號/Modules下)。基本上有名的package都已經被定義好了。若是想要知道有哪些可用可以自行去路徑下找,或是用:

cmake  --help-module-lists

查詢。
在CMake中使用find_package通常會使用類似下列參數。

find_package(OpenCV 3.4.1 REQUIRED)

 find_package(package_name version [REQUIRED] [QUIET] ),Version非必要,主要看package敘述。REQUIRED和QUIET主要用於相依性,若package為必要用REQUIRED,若沒找到直接報錯。選擇性用QUIET,通常會用類似下列方式達成選擇性。若沒找到package不會報任何錯誤。

find_package(OpenMP)  
if (OPENMP_FOUND)  
    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")  
    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")  
endif()

透過find_package 定義的-FOUND判斷是否存在。
find_package通常會定義下列四種變數:

當有這些參數後,即可透過一般CMake include或link方式使用,例如:

include_directories( ${OpenCV_INCLUDE_DIRS} )  
target_link_libraries (main ${PCL_LIBRARIES} ${OpenCV_LIBS})

等方式使用。

 custom find cmake (findXXX.cmake) find modules

若是CMake沒提供modules就要自己寫find modules啦。

依照前面的敘述,可以知道Find modules最重要的就是取得那四種變數的值。下面以我寫的FindRealsense2.cmake為例。

find_package(PkgConfig)
find_package(PkgConfig)
if(${CMAKE_VERSION} VERSION_LESS 2.8.2)
    pkg_check_modules(PC_REALSENSE2 librealsense2-dev)
else()
    pkg_check_modules(PC_REALSENSE2 QUIET librealsense2-dev)
endif()
 
set(REALSENSE2_DEFINITIONS ${PC_REALSENSE2_CFLAGS_OTHER})
 
#add a hint so that it can find it without the pkg-config
find_path(REALSENSE2_INCLUDE_DIR rs.hpp
          HINTS
          ${PC_REALSENSE2_INCLUDEDIR}
          ${PC_REALSENSE2_INCLUDE_DIRS}
          PATHS
            "${PROGRAM_FILES}/librealsense2/Include"
            /usr/include
            /usr/include/librealsense2
            /user/include
            /user/include/librealsense2
          PATH_SUFFIXES librealsense2
)
 
if(${CMAKE_CL_64})
    set(REALSENSE2_PATH_SUFFIXES lib64)
else()
    set(REALSENSE2_PATH_SUFFIXES lib)
endif()
 
#add a hint so that it can find it without the pkg-config
find_library(REALSENSE2_LIBRARY
             NAMES librealsense2.so
             HINTS
             ${PC_REALSENSE2_LIBDIR}
             ${PC_REALSENSE2_LIBRARY_DIRS}
             PATHS
               "${PROGRAM_FILES}/librealsense2"
               /usr/lib
               /usr/lib/x86_64-linux-gnu
               /user/lib
)
 
set(REALSENSE2_INCLUDE_DIRS ${REALSENSE2_INCLUDE_DIR})
set(REALSENSE2_LIBRARIES ${REALSENSE2_LIBRARY})
 
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(librealsense2 DEFAULT_MSG
    REALSENSE2_LIBRARY REALSENSE2_INCLUDE_DIR)
 
mark_as_advanced(REALSENSE2_LIBRARY REALSENSE2_INCLUDE_DIR)

其中最值得關注的是find_path和find_library這兩個command。顧名思義,find_path是拿來找include path的,而find_library則是拿來找shared library location的。find_path主要格式如下(詳情見find_path):

find_path(
<VAR>
name | NAMES include_folder_or_file_name
[HINTS path1 path2 …]
[PATHS path1 path2 …]
[PATH_SUFFIXS suffix1 suffix2]
)

其中<VAR>即為XXX-INCLUDE_DIR,name可以是header name,比較特殊的是HINTS和PATHS,HINTS與PATHS最大差別是,HINTS會依照系統現有的變數去推測路徑,PATHS則是直接寫死路徑。PATH_SUFFIXS是路徑後綴,例如說rs.hpp實際上在Ubuntu 16.04的路徑是/usr/include/x86_64-linux-gnu/librealsense2/rs.hpp,而前面我們寫的路徑只有/usr/include/x86_64-linux-gnu,這時她會自動加上librealsense2這後綴到路徑中。PATH_SUFFIXS可以用來處理不同Linux distribution路徑放置的習慣問題(有些會像這個例子一樣把librealsense2的東西放入librealsense2中),若有多個cmake 會都try try看。
另一個類似的command是find_library(詳細在這find_library):

find_library(
<VAR>
name | NAMES libraries_name
[HINTS path1 path2 …]
[PATHS path1 path2 …]
[PATH_SUFFIXS suffix1 suffix2]
)

其中<VAR>即為XXX-LIBRARIES,其他欄位都和find_path一樣。而XXX-FOUND,則當find_path或是find_library都找不到東西實設為NOT-FOUND。

這個範例裡還有兩個好用東西值得一提,一是pkg_check_modules,這其實是pkg-config的再包裝,以前找opencv的library的時候很常用:

$ pkg-config opencv –libs
-lopencv_stitching -lopencv_superres -lopencv_videostab -lopencv_aruco …….

cmake中的pkg-config功能也差不多,她會幫你找到所需的library放入指定的var中。如下:

 pkg_check_modules(PC_REALSENSE2 librealsense2-dev)

 這裡是用pkg_check_modules先依照package manager裡有的資訊尋找路徑,產生variable放入HINTS中。

另一個好用的東西是mark_as_advanced,這command會將該變數設為最高級別,當使用ccmake時不需要開啟advanced mode即可看到的變數。

CMake跟automake之類的比起來算是蠻方便的。

comments powered by Disqus