cmake概要

ceph自從Kraken版本之後,將構建方式從手寫makefile,轉到了cmake。在學習過程當中,發現cmake,涉及到的東西比較多。在此作一個總結,也方便本身從此查閱。數據庫

一. 什麼是cmake

cmake能夠用簡單的語句來描述全部平臺的安裝(編譯過程)。他可以輸出各類各樣的makefile,能測試編譯器所支持的C++特性,相似UNIX下的automake。函數

二. CMake 使用方法

CMake的全部的語句都寫在一個叫:CMakeLists.txt的文件中。當CMakeLists.txt文件肯定後,應用cmake命令生成相應的makefile。學習

其基本操做流程爲:測試

$> cmake [options] <path-to-source> 
$> make

第一條命令用於根據CMakeLists.txt生成Makefile文件;
第二條命令用於執行Makefile文件,編譯程序,生成可執行文件。ui

三. 一個CMakelists.txt的例子

1 #project name
2 PROJECT(test_math)
3 #head file path
4 INCLUDE_DIRECTORIES(
5 include
6 )
7 #source directory
8 AUX_SOURCE_DIRECTORY(src DIR_SRCS)
9 #set environment variable
10 SET(TEST_MATH
11 ${DIR_SRCS}
12 )
13 #set extern libraries
14 SET(LIBRARIES
15 libm.so
16 )
17 #add executable file
18 ADD_EXECUTABLE(../bin/bin ${TEST_MATH})
19 #add link library
20 TARGET_LINK_LIBRARIES(../bin/bin ${LIBRARIES})
21

或者另外一個:命令行

1 #project name  
2 PROJECT(test_math)
3 
4 add_definitions("-Wall -lpthread -g")
5 
6 #head file path  
7 INCLUDE_DIRECTORIES(
8 include
9 )
10 
11 #source directory  
12 AUX_SOURCE_DIRECTORY(src DIR_SRCS)
13 
14 #set environment variable  
15 SET(TEST_MATH
16 ${DIR_SRCS}
17 )
18 
19 #set extern libraries  
20 SET(LIBRARIES
21 libm.so
22 )
23 
24 # set output binary path  
25 SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
26 
27 SET(FS_BUILD_BINARY_PREFIX "Yfs")
28 
29 #add executable file  
30 ADD_EXECUTABLE(${FS_BUILD_BINARY_PREFIX}sqrt ${TEST_MATH})
31 
32 #add link library  
33 TARGET_LINK_LIBRARIES(${FS_BUILD_BINARY_PREFIX}sqrt ${LIBRARIES})

這是一個測試數學函數的程序的CMakeLists.txt,"#"後面爲註釋的內容,CMake的命令所有爲大寫
第2行指定生成的工程名爲test_math
第4行指定頭文件目錄爲include
第8行指定源文件目錄爲src,並將其賦值給環境變量DIR_SRCS
第10行設定環境變量TEST_MATH的值爲環境變量DIR_SRCS的值,此處用於顯示如何用環境變量對環境變量進行賦值。
第14行將數學函數庫賦值給環境變量LIBRARIES,固然,能夠不用這個環境變量,而在後面直接使用該庫名
第18行用於指定生成文件,將環境變量TEST_MATH目錄下的全部文件編譯生成../bin目錄下的可執行文件bin
第20行指定../bin/bin執行時的連接庫爲環境變量LIBRARIES的值-libm.sodebug

src/main.c:code

#include<stdio.h>
#include"../include/a.h"
int main()
{
    double b=25.0;
    double a=0.0;
    a=get_sqrt(b);
 
    printf("a is %lf, b is %lf\n",a,b);
    return 0;
}

/src/a.cci

#include"../include/a.h"
double get_sqrt(double var1)
{
    return sqrt(var1);
}

/include/a.h開發

#ifndef  A_FILE_HEADER_INC
#define A_FILE_HEADER_INC
#include<math.h>
double get_sqrt(double var1);
#endif

將CMakeLists.txt放在當前目錄下,執行CMakeLists.txt

$> cmake .
$> make

便可生成可執行文件,在目錄/bin下的bin文件,好了運行看其效果是否和所想同樣。

注:
aux_source_directory: 查找在某個路徑下的全部源文件。
aux_source_directory(<dir> <variable>)
蒐集全部在指定路徑下的源文件的文件名,將輸出結果列表儲存在指定的變量中。該命令主要用在那些使用顯式模板實例化的工程上。模板實例化文件能夠存儲在Templates子目錄下,而後可使用這條命令自動收集起來;這樣能夠避免手工羅列全部的實例。

PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR的區別 通常來講,都是這樣用

cmake ./

這樣PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR是等價的。也就是當前源碼的目錄。
若是執行cmake的時候,並不在源碼的路徑的話,好比:

cmake ../src

這樣的好處是cmake生成的文件和編譯出來的東西,就不放在源碼路徑下了,保證了源碼路徑的乾淨整潔。 好比能夠在src的同級目錄下創建build目錄。而後在build目錄下執行:

cmake ../src

這樣編譯出來的東西和cmake生成的東西,都放到了build目錄下了。而且:

PROJECT_BINARY_DIR=全路徑/build
PROJECT_SOURCE_DIR=全路徑/src

四. CMakeLists.txt的語法

  1. 註釋

  2. 變量:使用set命令顯式定義及賦值,在非if語句中,使用${}引用,if中直接使用變量名引用;後續的set命令會清理變量原來的值;
  3. command (args ...) #命令不分大小寫,參數使用空格分隔,使用雙引號引發參數中空格
  4. set(var a;b;c) <=> set(var a b c) #定義變量var並賦值爲a;b;c這樣一個string list
  5. add_executable(${var}) <=> add_executable(a b c) #變量使用${xxx}引用
  6. 條件語句:
if(var) #var 非empty 0 N No OFF FALSE... #非運算使用NOT
      …
    else()/elseif() … endif(var)
  1. 循環語句
Set(VAR a b c)  
Foreach(f ${VAR})  
…  
Endforeach(f)
  1. 循環語句
WHILE() … ENDWHILE()
  1. 內部變量
    CMAKE_C_COMPILER:指定C編譯器
    CMAKE_CXX_COMPILER:指定C++編譯器
    CMAKE_C_FLAGS:編譯C文件時的選項,如-g;也能夠經過add_definitions添加編譯選項
    EXECUTABLE_OUTPUT_PATH:可執行文件的存放路徑
    LIBRARY_OUTPUT_PATH:庫文件路徑
    CMAKE_BUILD_TYPE:build 類型(Debug, Release, ...),CMAKE_BUILD_TYPE=Debug
    BUILD_SHARED_LIBS:Switch between shared and static libraries

  2. 內置變量的使用:
    在CMakeLists.txt中指定,使用set
    cmake命令中使用,如cmake -DBUILD_SHARED_LIBS=OFF

  3. 命令 project (HELLO) #指定項目名稱
    使用${HELLO_SOURCE_DIR}表示項目根目錄

include_directories:指定頭文件的搜索路徑,至關於指定gcc的-I參數
include_directories (${HELLO_SOURCE_DIR}/Hello) #增長Hello爲include目錄

link_directories:動態連接庫或靜態連接庫的搜索路徑,至關於gcc的-L參數
link_directories (${HELLO_BINARY_DIR}/Hello) #增長Hello爲link目錄

add_subdirectory:包含子目錄
add_subdirectory (Hello)

add_executable:編譯可執行程序,指定編譯,好像也能夠添加.o文件
add_executable (helloDemo demo.cxx demo_b.cxx) #將cxx編譯成可執行文件

add_definitions:添加編譯參數
add_definitions(-DDEBUG)將在gcc命令行添加DEBUG宏定義;
add_definitions( 「-Wall -ansi –pedantic –g」)

target_link_libraries:添加連接庫,相同於指定-l參數
target_link_libraries(demo Hello) #將可執行文件與Hello鏈接成最終文件demo

add_library:
add_library(Hello hello.cxx) #將hello.cxx編譯成靜態庫如libHello.a

add_custom_target: 增長一個沒有輸出的目標,使得它老是被構建。
enables testing and creates Make check command

add_custom_target(tests
    COMMENT "Building tests")
    enable_testing()
    set(CMAKE_CTEST_COMMAND ctest)
    add_custom_target(check
    COMMAND ${CMAKE_CTEST_COMMAND}
    DEPENDS tests)

set_target_properties( ... ): lots of properties... OUTPUT_NAME, VERSION, ....
爲一個目標設置屬性。該命令的語法是列出全部你想要變動的文件,而後提供你想要設置的值。你可以使用任何你想要的屬性/值對,而且在隨後的代碼中調用GET_TARGET_PROPERTY命令取出屬性的值。

link_libraries( lib1 lib2 ...):
All targets link with the same set of libs

add_dependencies: 怎樣添加編譯依賴項 用於確保編譯目標項目前依賴項必須先構建好

執行CMAKE時打印消息
message([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)

五. 處理多源文件目錄的方法

CMake 處理源代碼分佈在不一樣目錄中的狀況也十分簡單。現假設咱們的源代碼分佈狀況以下,其中 src 目錄下的文件要編譯成一個連接庫。

./step2
       |
      +---main.cpp
       |
      +---src
             |
            + -- Test1.h
             |
            + -- Test1.cpp
  1. 項目主目錄中的 CMakeLists.txt
    在目錄 step2 中建立文件 CMakeLists.txt 。文件內容以下:
1 PROJECT(main)
    2 CMAKE_MINIMUM_REQUIRED(VERSION 2.6) 
    3 ADD_SUBDIRECTORY( src )
    4 AUX_SOURCE_DIRECTORY(. DIR_SRCS)
    5 ADD_EXECUTABLE(main ${DIR_SRCS}  )
    6 TARGET_LINK_LIBRARIES( main Test )

第三行,使用命令 ADD_SUBDIRECTORY 指明本項目包含一個子目錄 src 。
第六行,使用命令 TARGET_LINK_LIBRARIES 指明可執行文件 main 須要鏈接一個名爲Test的連接庫 。

  1. 子目錄中的 CMakeLists.txt
    在子目錄 src 中建立 CmakeLists.txt。文件內容以下:
1 AUX_SOURCE_DIRECTORY(. DIR_TEST1_SRCS)
2 ADD_LIBRARY ( Test ${DIR_TEST1_SRCS})

在執行 cmake 的過程當中,首先解析目錄 step2 中的 CMakeLists.txt ,當程序執行命令 ADD_SUBDIRECTORY( src ) 時進入目錄 src 對其中的 CMakeLists.txt 進行解析。

六. 在工程中查找並使用其餘程序庫的方法

在開發軟件的時候咱們會用到一些函數庫,這些函數庫在不一樣的系統中安裝的位置可能不一樣,編譯的時候須要首先找到這些軟件包的頭文件以及連接庫所在的目錄以便生成編譯選項。例如一個須要使用博克利數據庫項目,須要頭文件db_cxx.h 和連接庫 libdb_cxx.so,如今該項目中有一個源代碼文件 main.cpp ,放在項目的根目錄中。

  1. 程序庫說明文件 在項目的根目錄中建立目錄 cmake/modules/,在 cmake/modules/ 下建立文件 Findlibdb_cxx.cmake ,內容以下:
01 MESSAGE(STATUS "Using bundled Findlibdb.cmake...")
02 
03 FIND_PATH(
04   LIBDB_CXX_INCLUDE_DIR
05   db_cxx.h 
06   /usr/include/ 
07   /usr/local/include/ 
08 )
09 
10 FIND_LIBRARY(
11   LIBDB_CXX_LIBRARIES NAMES  db_cxx
12   PATHS /usr/lib/ /usr/local/lib/
13 )
  1. 文件 Findlibdb_cxx.cmake 的命名要符合規範: FindlibNAME.cmake, 其中NAME 是函數庫的名稱。Findlibdb_cxx.cmake 的語法與 CMakeLists.txt 相同。

  2. 這裏使用了三個命令: MESSAGE , FIND_PATH 和 FIND_LIBRARY 。
    MESSAGE: 會將參數的內容輸出到終端。
    FIND_PATH: 指明頭文件查找的路徑,原型以下:
    find_path(<VAR> name1 [path1 path2 ...])
    該命令在參數 path* 指示的目錄中查找文件 name1 並將查找到的路徑保存在變量 VAR 中。
    第3-8行的意思是在 /usr/include/ 和 /usr/local/include/ 中查找文件db_cxx.h ,並將 db_cxx.h 所在的路徑保存在 LIBDB_CXX_INCLUDE_DIR 中。
    命令 FIND_LIBRARY 同 FIND_PATH 相似,用於查找連接庫並將結果保存在變量中。
    第10-13行的意思是在目錄 /usr/lib/ 和 /usr/local/lib/ 中尋找名稱爲 db_cxx 的連接庫,並將結果保存在 LIBDB_CXX_LIBRARIES 。

  3. 在項目的根目錄中建立 CmakeList.txt :

01 PROJECT(main)
02 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
03 SET(CMAKE_SOURCE_DIR .)
04 SET(CMAKE_MODULE_PATH ${CMAKE_ROOT}/Modules${CMAKE_SOURCE_DIR}/cmake/modules) 
05 AUX_SOURCE_DIRECTORY(. DIR_SRCS)
06 ADD_EXECUTABLE(main ${DIR_SRCS})
07 
08 FIND_PACKAGE( libdb_cxx REQUIRED)
09 MARK_AS_ADVANCED(
10 LIBDB_CXX_INCLUDE_DIR
11 LIBDB_CXX_LIBRARIES
12 )
13 IF (LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)
14 MESSAGE(STATUS "Found libdb libraries")
15    INCLUDE_DIRECTORIES(${LIBDB_CXX_INCLUDE_DIR})
16     MESSAGE( ${LIBDB_CXX_LIBRARIES} )
17     TARGET_LINK_LIBRARIES(main ${LIBDB_CXX_LIBRARIES}
18 )
19 ENDIF (LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)

第4行: 表示到目錄 ./cmake/modules 中查找 Findlibdb_cxx.cmake
8-19 行: 表示查找連接庫和頭文件的過程。
第8行: 使用命令 FIND_PACKAGE 進行查找,這條命令執行後 CMake 會到變量 CMAKE_MODULE_PATH 指示的目錄中查找文件 Findlibdb_cxx.cmake 並執行。
13-19行:條件判斷語句,表示若是 LIBDB_CXX_INCLUDE_DIR 和 LIBDB_CXX_LIBRARIES 都已經被賦值,則設置編譯時到 LIBDB_CXX_INCLUDE_DIR 尋找頭文件而且設置可執行文件 main 須要與連接庫 LIBDB_CXX_LIBRARIES 進行鏈接。
5) 執行 cmake

七. 使用 cmake 生成 debug 版和 release 版的程序

CMake 中有一個變量 CMAKE_BUILD_TYPE ,能夠的取值是 Debug/Release/RelWithDebInfo 和 MinSizeRel。當這個變量值爲 Debug 的時候,CMake 會使用變量 CMAKE_CXX_FLAGS_DEBUG 和 CMAKE_C_FLAGS_DEBUG 中的字符串做爲編譯選項生成 Makefile,當這個變量值爲 Release 的時候,工程會使用變量 CMAKE_CXX_FLAGS_RELEASE 和 CMAKE_C_FLAGS_RELEASE 選項生成 Makefile。
現假設項目中只有一個文件 main.cpp ,下面是一個能夠選擇生成 debug 版和 release 版的程序的 CMakeList.txt :

1 PROJECT(main)
2 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
3 SET(CMAKE_SOURCE_DIR .)
4 
5 SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
6 SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
7 
8 AUX_SOURCE_DIRECTORY(. DIR_SRCS)
9 ADD_EXECUTABLE(main ${DIR_SRCS})

第 5 和 6 行設置了兩個變量 CMAKE_CXX_FLAGS_DEBUG 和 CMAKE_CXX_FLAGS_RELEASE, 這兩個變量是分別用於 debug 和 release 的編譯選項。

相關文章
相關標籤/搜索