cmake的一些小經驗

初用CMake或者對其瞭解不太深的人,可能常常會被路徑包含、庫搜索路徑、連接路徑、RPath這些問題所絆倒,由於這些東西在手工執行gcc或者編寫makefile的時候是很垂手可得的任務。python

其實我當初也有很多疑惑,不過經過較長時間的實踐和閱讀manual,總算有了個相對很清晰的認識。app

  • 如何使用其manual

cmake的幫助組織的仍是頗有規律的,瞭解了其規律,找本身想要的東西就會很簡單,因此我的以爲這一點多是最重要的。其help系統大概是這麼幾類:less

  • command

這個是實用過程當中最長用到的,至關於通常腳步語言中的基本語法,包括定義變量,foreach,string,if,builtin command都在這裏。編輯器

能夠用以下這些命令獲取幫助:ui

 
cmake --help-commands 
 

這個命令將給出全部cmake內置的命令的詳細幫助,通常不知道本身要找什麼或者想隨機翻翻得時候,能夠用這個。this

我通常更經常使用的方法是將其重定向到less裏邊,而後在編輯器裏邊搜索關鍵字。spa

 

另外也能夠用以下的辦法層層縮小搜索範圍:調試

cmake --help-command-list
 

cmake --help-command-list | grep findcomponent

skyscribe@skyscribe:~/program/ltesim/bld$ cmake --help-command-list | grep find
find_file
find_library
find_package
find_path
find_programorm

 
 

cmake --help-command find_library

cmake version 2.6-patch 4
------------------------------------------------------------------------------
SingleItem

  find_library
       Find a library.

          find_library(<VAR> name1 [path1 path2 ...])

       This is the short-hand signature for the command that is sufficient in
       many cases.  It is the same as find_library(<VAR> name1 [PATHS path1
       path2 ...])

          find_library(
                    <VAR>
                    name | NAMES name1 [name2 ...]
                    [HINTS path1 [path2 ... ENV var]]
                    [PATHS path1 [path2 ... ENV var]]
                    [PATH_SUFFIXES suffix1 [suffix2 ...]]
                    [DOC "cache documentation string"]
                    [NO_DEFAULT_PATH]
                    [NO_CMAKE_ENVIRONMENT_PATH]
                    [NO_CMAKE_PATH]
                    [NO_SYSTEM_ENVIRONMENT_PATH]
                    [NO_CMAKE_SYSTEM_PATH]
                    [CMAKE_FIND_ROOT_PATH_BOTH |
                     ONLY_CMAKE_FIND_ROOT_PATH |
                     NO_CMAKE_FIND_ROOT_PATH]
                   )

  • variable

和command的幫助比較相似,只不過這裏能夠查找cmake本身定義了那些變量你能夠直接使用,譬如OSName,是不是Windows,Unix等。

我最經常使用的一個例子:

 
cmake --help-variable-list | grep CMAKE | grep HOST 
CMAKE_HOST_APPLE
CMAKE_HOST_SYSTEM
CMAKE_HOST_SYSTEM_NAME
CMAKE_HOST_SYSTEM_PROCESSOR
CMAKE_HOST_SYSTEM_VERSION
CMAKE_HOST_UNIX
CMAKE_HOST_WIN32
 

這裏查找全部CMake本身定義的builtin變量;通常和系統平臺相關。

若是但願將全部生成的可執行文件、庫放在同一的目錄下,能夠如此作:

這裏的target_dir是一個實現設置好的絕對路徑。(CMake裏邊絕對路徑比相對路徑更少出問題,若是可能儘可能用絕對路徑)

 
# Targets directory 
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${target_dir}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${target_dir}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${target_dir}/bin)
 
  • property

Property通常不多須要直接改動,除非你想修改一些默認的行爲,譬如修改生成的動態庫文件的soname等。

譬如須要在同一個目錄下既生成動態庫,也生成靜態庫,那麼默認的狀況下,cmake根據你提供的target名字自動生成相似的libtarget.so, libtarget.a,可是同一個project只能同時有一個,由於target必須惟一。

這時候,就能夠經過修改taget對應的文件名,從而達到既生成動態庫也產生靜態庫的目的。

譬如:

 
cmake --help-property-list | grep NAME 
GENERATOR_FILE_NAME
IMPORTED_SONAME
IMPORTED_SONAME_<CONFIG>
INSTALL_NAME_DIR
OUTPUT_NAME
VS_SCC_PROJECTNAME
skyscribe@skyscribe:~$ cmake --help-property OUTPUT_NAME
cmake version 2.6-patch 4
------------------------------------------------------------------------------
SingleItem
 
  OUTPUT_NAME
       Sets the real name of a target when it is built.
 
       Sets the real name of a target when it is built and can be used to
       help create two targets of the same name even though CMake requires
       unique logical target names.  There is also a <CONFIG>_OUTPUT_NAME
       that can set the output name on a per-configuration basis.
 
  • module

用於查找經常使用的模塊,譬如boost,bzip2, python等。經過簡單的include命令包含預約義的模塊,就能夠獲得一些模塊執行後定義好的變量,很是方便。

譬如經常使用的boost庫,能夠經過以下方式:

 
# Find boost 1.40 
INCLUDE(FindBoost)
find_package(Boost 1.40.0 COMPONENTS thread unit_test_framework)
if(NOT Boost_FOUND)
    message(STATUS "BOOST not found, test will not succeed!")
endif()
 
通常開頭部分的解釋都至關有用,可知足80%需求:
cmake --help-module FindBoost | head -40
cmake version 2.6-patch 4
------------------------------------------------------------------------------
SingleItem
 
  FindBoost
       Try to find Boost include dirs and libraries
 
       Usage of this module as follows:
 
       == Using Header-Only libraries from within Boost: ==
 
          find_package( Boost 1.36.0 )
          if(Boost_FOUND)
             include_directories(${Boost_INCLUDE_DIRS})
             add_executable(foo foo.cc)
          endif()
 
       
 
       
 
       == Using actual libraries from within Boost: ==
 
          set(Boost_USE_STATIC_LIBS   ON)
          set(Boost_USE_MULTITHREADED ON)
          find_package( Boost 1.36.0 COMPONENTS date_time filesystem system ... )
 
       
 
          if(Boost_FOUND)
             include_directories(${Boost_INCLUDE_DIRS})
             add_executable(foo foo.cc)
             target_link_libraries(foo ${Boost_LIBRARIES})
          endif()
 
       
 
       
 
       The components list needs to contain actual names of boost libraries
 
  • 如何根據其生成的中間文件查看一些關鍵信息

CMake相比較於autotools的一個優點就在於其生成的中間文件組織的頗有序,而且清晰易懂,不像autotools會生整天書同樣的龐然大物(10000+的不鮮見)。

通常CMake對應的Makefile都是有層級結構的,而且會根據你的CMakeLists.txt間的相對結構在binary directory裏邊生成相應的目錄結構。

譬如對於某一個target,通常binary tree下能夠找到一個文件夾:  CMakeFiles/<targentName>.dir/,好比:

 
skyscribe@skyscribe:~/program/ltesim/bld/dev/simcluster/CMakeFiles/SIMCLUSTER.dir$ ls -l 
 
total 84 
 
-rw-r--r-- 1 skyscribe skyscribe 52533 2009-12-12 12:20 build.make 
 
-rw-r--r-- 1 skyscribe skyscribe 1190 2009-12-12 12:20 cmake_clean.cmake 
 
-rw-r--r-- 1 skyscribe skyscribe 4519 2009-12-12 12:20 DependInfo.cmake 
 
-rw-r--r-- 1 skyscribe skyscribe 94 2009-12-12 12:20 depend.make 
 
-rw-r--r-- 1 skyscribe skyscribe 573 2009-12-12 12:20 flags.make 
 
-rw-r--r-- 1 skyscribe skyscribe 1310 2009-12-12 12:20 link.txt 
 
-rw-r--r-- 1 skyscribe skyscribe 406 2009-12-12 12:20 progress.make 
 
drwxr-xr-x 2 skyscribe skyscribe 4096 2009-12-12 12:20 src 
這裏,每個文件都是個很短小的文本文件,內容至關清晰明瞭。build.make通常包含中間生成文件的依賴規則,DependInfo.cmake通常包含源代碼文件自身的依賴規則。
比較重要的是flags.make和link.txt,前者通常包含了相似於GCC的-I的相關信息,如搜索路徑,宏定義等;後者則包含了最終生成target時候的linkage信息,庫搜索路徑等。
這些信息在出現問題的時候是個很好的輔助調試手段。
 
 
  • 文件查找、路徑相關
    • include

通常經常使用的是:

 
include_directories()用於添加頭文件的包含搜索路徑 
cmake --help-command include_directories
cmake version 2.6-patch 4
------------------------------------------------------------------------------
SingleItem
 
  include_directories
       Add include directories to the build.
 
         include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
 
       Add the given directories to those searched by the compiler for
       include files.  By default the directories are appended onto the
       current list of directories.  This default behavior can be changed by
       setting CMAKE_include_directories_BEFORE to ON.  By using BEFORE or
       AFTER you can select between appending and prepending, independent
       from the default.  If the SYSTEM option is given the compiler will be
       told that the directories are meant as system include directories on
       some platforms.
 
link_directories()用於添加查找庫文件的搜索路徑
 
cmake --help-command link_directories
cmake version 2.6-patch 4
------------------------------------------------------------------------------
SingleItem
 
  link_directories
       Specify directories in which the linker will look for libraries.
 
         link_directories(directory1 directory2 ...)
 
       Specify the paths in which the linker should search for libraries.
       The command will apply only to targets created after it is called.
       For historical reasons, relative paths given to this command are
       passed to the linker unchanged (unlike many CMake commands which
       interpret them relative to the current source directory).
 
  • library search

通常外部庫的link方式能夠經過兩種方法來作,一種是顯示添加路徑,採用link_directories(), 一種是經過find_library()去查找對應的庫的絕對路徑。

後一種方法是更好的,由於它能夠減小很多潛在的衝突。

        通常find_library會根據一些默認規則來搜索文件,若是找到,將會set傳入的第一個變量參數、不然,對應的參數不被定義,而且有一個xxx-NOTFOUND被定義;能夠經過這種方式來調試庫搜索是否成功。

        對於庫文件的名字而言,動態庫搜索的時候會自動搜索libxxx.so (xxx.dll),靜態庫則是libxxx.a(xxx.lib),對於動態庫和靜態庫混用的狀況,可能會出現一些混亂,須要格外當心;通常儘可能作匹配鏈接。

  • rpath

所謂的rpath是和動態庫的加載運行相關的。我通常採用以下的方式取代默認添加的rpath:

 
# RPATH and library search setting 
SET(CMAKE_SKIP_BUILD_RPATH  FALSE)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) 
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/nesim/lib")
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 
相關文章
相關標籤/搜索