1. 程式人生 > >使用cmake自動構建工程

使用cmake自動構建工程

轉自http://www.cnblogs.com/chengxuyuancc/p/5347646.html。真的很好,謝謝作者辛苦整理。若侵權告知即刪。

 

公司引擎是用cmake根據目標平臺來構建工程的,剛接觸的時候深深體會到cmake的方便:如果目標平臺是windows,它可以幫你自動構建出vs工程;如果是安卓,自動構建出eclipse工程,如果是IOS,自動構建出xcode工程。想想以前用vs建工程的時候,如果要引入第三方庫,必須要手動配置第三方庫路徑,如果引入的庫少,那還沒什麼,如果多的話就悲劇了,配個環境都要半天。再想想以前在linux平臺下手動寫Makefile的時候,如果工程比較小,模組少還好辦,如果工程大,模組多,各種寫依賴關係都要讓你抓狂。有了cmake這個工具,我們完全可以靠它來幫我們構建vs工程,寫Makefile檔案。既然cmake構建工程這麼方便,當然需要拿來用,可是對於我這個小白來說,怎麼學呢?果斷谷歌,百度,不過並沒有找到比較有價值的學習資料,很多人都是貼出了cmake的原始檔CMakeLists.txt,然後對檔案中的每行都講了下作用。看完這些,我依然不知道為什麼要這麼寫,為什麼這行要這樣寫,那行要那樣寫?後來才反應過來,cmake官網肯定有講啊,雖然是英文的,雖然自己英文比較挫,沒辦法,誰叫沒有其它資料呢(其實官網講的才說最權威的,不要因為是英文就畏懼,看多了其實英文也沒那麼難,很多人自認為英文不行或者看英文吃力就去網上找各種中文資料,結果可能花費了大量時間在找資料上,到最後啥都沒學到)。本文主要通過講解cmake中一些比較簡單的命令來構建自己的工程,作為初學者的入門。

 

1、兩行命令幫你構建輸出hello world的vs工程

      為了自動構建工程,需要在原始檔所在的最上層目錄寫一個CMakeLists.txt檔案,它是cmake的原始檔,也可以看作是cmake的指令碼檔案,這個檔案描述了cmake怎樣幫我們自動構建工程。現在我們有一個hello.cpp檔案,需要用這個檔案來構建一個vs工程,手動的方法就是開啟vs,新建一個工程hello,然後把hello.cpp新增到hello工程裡面。而有了cmake,只需要在CMakeLists.txt寫兩行命令,第一行給自己工程命個名hello,第二行hello工程需要的原始檔hello.cpp。然後通過下面幾個步驟,就可以生成一個vs工程了,生成其它工程的步驟相同,只是在選擇目標工程的時候不同。

1.1 編寫CMakeLists.txt檔案和hello.cpp檔案

CMakeLists.txt

project(hello)

add_executable(hello hello.cpp)

hello.cpp

#include <stdio.h>

int main (int argc, char *argv[])
{
    printf("hello world!");
    return 0;
}

1.2 設定路徑

 

1.3 設定目標工程為vs工程

1.4  產生vs工程

1.5 開啟vs工程,編譯執行程式

 

2 新增子模組

      對於比較大的工程來說,包含多個子模組是很常見的事,因為通常每個人只是負責他自己的模組。那麼怎樣將各個模組加入到主工程中呢?首先我們需要使用cmake來建立各個子模組的工程,然後再將這些模組加入到整個工程中。假設現在我們有一個子模組myhello,它提供了一個函式PrintHelloWorld來列印hello world!,主模組hello呼叫這個函式來列印。首先我們在hello.cpp所在目錄建立myhello資料夾,將myhello.cpp和myhello.h放到裡面,然後在這個資料夾中建立CMakeLists.txt。這三個檔案的具體內容如下:

myhello/myhello.h:

void PrintHelloWorld();

myhello/myhello.cpp

#include <stdio.h>

void PrintHelloWorld()
{
    printf("hello world!");
}

myhello/CMakeLists.txt

add_library(myhello myhello.cpp)

這個CMakeLists.txt主要是告訴cmake,為myhello建立一個庫工程。

hello.cpp

#include "myhello/myhello.h"

int main (int argc, char *argv[])
{
    PrintHelloWorld();
    return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.5)

project(hello)

add_subdirectory(myhello)
set (EXTRA_LIBS ${EXTRA_LIBS} myhello)

add_executable(hello hello.cpp)

target_link_libraries (hello ${EXTRA_LIBS})

add_subdirectory將myhello子工程加入到主工程,target_link_libraries將子模myhello連結到hello中。然後重新cmake下,開啟vs就可以編譯執行啦。

3 新增可配置的標頭檔案

      cmake可以通過可配置的標頭檔案來產生實際的標頭檔案,如下面的可配置標頭檔案hello.h.in,裡面@@引用的變數可以通過CMakeLists.txt來設定,最後通過cmake來替換hello.h.in檔案中的變數並生成hello.h內容。

hello.h.in

#define VERSION_MAJOR @[email protected]
#define VERSION_MINOR @[email protected]

CMakeLists.txt

cmake_minimum_required(VERSION 3.5)

project(hello)

include_directories("${PROJECT_BINARY_DIR}")
set(VERSION_MAJOR 1)
set(VERSION_MINOR 0)
configure_file(
    "${PROJECT_SOURCE_DIR}/hello.h.in"
    "${PROJECT_BINARY_DIR}/hello.h"
)

add_subdirectory(myhello)
set (EXTRA_LIBS ${EXTRA_LIBS} myhello)


add_executable(hello hello.cpp)

target_link_libraries (hello ${EXTRA_LIBS})

上面加綠的命令主要用來設定hello.h.in中的兩個變數,並且讓cmake生成hello.h檔案。生成的hello.h如下:

hello.h

#define VERSION_MAJOR 1
#define VERSION_MINOR 0

再修改下hello.cpp檔案使用這兩個變數,

hello.cpp

#include "myhello/myhello.h"
#include "hello.h"
#include <stdio.h>

int main (int argc, char *argv[])
{
    printf("version:%d.%d\n", VERSION_MAJOR, VERSION_MINOR);
    PrintHelloWorld();
    return 0;
}

開啟vs工程,編譯執行輸出者兩個變數的值。這樣就可以通過在CMakeLists.txt中設定變數的內容來動態修改.h檔案,增加了程式碼的靈活性。

 

4 檢測系統是否有支援工程需要的函式

      對於跨平臺的工程來說,檢查系統是否支援某些特性是很有必要的,這樣程式中就可以通過系統的特性來選擇具體執行哪些程式碼。其中檢查是否支援某些函式是我們經常要做的事情,如epoll函式,可能有的linux系統就不支援,對於不支援的系統我們只能用poll來替代等。在cmake中檢查系統是否支援某個函式也很簡單,先包含一個CheckFunctionExists庫,然後使用check_function_exists來判斷就行了。

CMakeLists.txt

cmake_minimum_required(VERSION 3.5)

project(hello)

include (CheckFunctionExists)
check_function_exists (printf HAVE_PRINTF)

include_directories("${PROJECT_BINARY_DIR}")
set(VERSION_MAJOR 1)
set(VERSION_MINOR 0)
configure_file(
    "${PROJECT_SOURCE_DIR}/hello.h.in"
    "${PROJECT_BINARY_DIR}/hello.h"
)

add_subdirectory(myhello)
set (EXTRA_LIBS ${EXTRA_LIBS} myhello)


add_executable(hello hello.cpp)

target_link_libraries (hello ${EXTRA_LIBS})

在配置的標頭檔案hello.h.in中加入#cmakedefine HAVE_PRINTF,這樣如果系統中有printf函式,最終生成的hello.h中會定義HAVE_PRINTF這個巨集,否則不會生成這個巨集,在hello.cpp檔案中可以根據這個巨集來是否定義來判斷是否應該使用printf函式。

hello.h.in

#define VERSION_MAJOR @[email protected]
#define VERSION_MINOR @[email protected]

#cmakedefine HAVE_PRINTF

hello.cpp

#include "myhello/myhello.h"
#include "hello.h"
#include <stdio.h>

int main (int argc, char *argv[])
{
#ifdef HAVE_PRINTF
    printf("version:%d.%d\n", VERSION_MAJOR, VERSION_MINOR);
#endif
    PrintHelloWorld();
    return 0;
}

執行結果:

 

5 配置可選項

有時候程式碼可能包含了所有平臺的模組程式碼,但是對於特定的目標平臺,只需要配置該平臺需要模組的程式碼,而不需要配置其它平臺模組的程式碼。這種需求可以通過cmake的配置可選項來完成,配置可選項就是cmake在生成工程的時候提示你一些選項,根據你的選項來具體選擇需要新增到工程中的模組程式碼。例如我現在需要提高是否使用myhello模組的選項,可以在CMakeLists.txt中加option命令來實現,程式碼如下:

cmake_minimum_required(VERSION 3.5)

project(hello)

include (CheckFunctionExists)
check_function_exists (printf HAVE_PRINTF)

include_directories("${PROJECT_BINARY_DIR}")
set(VERSION_MAJOR 1)
set(VERSION_MINOR 0)

option (USE_MYHELLO 
        "Use myhello" ON) 
        
configure_file(
    "${PROJECT_SOURCE_DIR}/hello.h.in"
    "${PROJECT_BINARY_DIR}/hello.h"
)

add_subdirectory(myhello)
set (EXTRA_LIBS ${EXTRA_LIBS} myhello)


add_executable(hello hello.cpp)

target_link_libraries (hello ${EXTRA_LIBS})

並且在hello.h.in中新增由cmake根據選項來定義USE_MYHELLO巨集。

#define VERSION_MAJOR @[email protected]
#define VERSION_MINOR @[email protected]

#cmakedefine HAVE_PRINTF
#cmakedefine USE_MYHELLO

這樣在執行cmake的時候,會提示我們一些選項來進行選擇:

通過USE_MYHELLO是否被選擇,cmake來確定是否要在hello.h中定義USE_MYHELLO巨集,最終我們可以在hello.cpp中判斷USE_MYHELLO巨集是否定義來是否使用myhello模組中的PrintHelloWorld函式。

hello.cpp

#include "myhello/myhello.h"
#include "hello.h"
#include <stdio.h>

int main (int argc, char *argv[])
{
#ifdef HAVE_PRINTF
    printf("version:%d.%d\n", VERSION_MAJOR, VERSION_MINOR);
#endif
#ifdef USE_MYHELLO
    PrintHelloWorld();
#else
    printf("xx hello world!");
#endif
    return 0;
}

最後通過選中或者不選中USE_MYHELLO選擇,得到的結果會不同。

選中結果

沒選中結果:

 

6 總結

      本文主要介紹了下cmake的比較常用的一些命令:project、include、include_directories、set、option、configure_file、add_subdirectory、add_executable、target_link_libraries、add_library,算是一個入門吧。需要用好cmake,熟悉cmake的命令和多寫cmake指令碼是必須的,具體每個命令的介紹看以參考官方文件:https://cmake.org/cmake/help/v3.5/manual/cmake-commands.7.html,腳步的編寫語法可以參考官網文件:https://cmake.org/cmake/help/v3.5/manual/cmake-language.7.html。以後大點的工程建立完全可以交給cmake來完成,同時也是熟悉cmake的過程。

 

參考:https://cmake.org/cmake/help/v3.5/index.html