cmake 學習筆記(一)

本文轉自:http://blog.csdn.net/dbzhang800/article/details/6314073python

  • 最大的Qt4程序羣(KDE4)採用cmake做爲構建系統
  • Qt4的python綁定(pyside)採用了cmake做爲構建系統
  • 開源的圖像處理庫 opencv 採用cmake 做爲構建系統
  • ...

看來不學習一下cmake是不行了,一點一點來吧,找個最簡單的C程序,慢慢複雜化,試試看:編程

例子一windows

單個源文件 main.cide

例子二學習

==>分解成多個 main.c hello.h hello.cui

例子三.net

==>先生成一個靜態庫,連接該庫blog

例子四get

==>將源文件放置到不一樣的目錄it

例子五

==>控制生成的程序和庫所在的目錄

例子六

==>使用動態庫而不是靜態庫

例子一

一個經典的C程序,如何用cmake來進行構建程序呢?

//main.c
#include <stdio.h>
int main()
{
    printf("Hello World!/n");
    return 0;
}

編寫一個 CMakeList.txt 文件(可看作cmake的工程文件):

project(HELLO)
set(SRC_LIST main.c)
add_executable(hello ${SRC_LIST})

而後,創建一個任意目錄(好比本目錄下建立一個build子目錄),在該build目錄下調用cmake

  • 注意:爲了簡單起見,咱們從一開始就採用cmake的 out-of-source 方式來構建(即生成中間產物與源代碼分離),並始終堅持這種方法,這也就是此處爲何單首創建一個目錄,而後在該目錄下執行 cmake 的緣由
cmake .. -G"NMake Makefiles"
nmake

或者

cmake .. -G"MinGW Makefiles"
make

便可生成可執行程序 hello(.exe)

目錄結構

+
| 
+--- main.c
+--- CMakeList.txt
|
/--+ build/
   |
   +--- hello.exe

cmake 真的不太好用哈,使用cmake的過程,自己也就是一個編程的過程,只有多練才行。

咱們先看看:前面提到的這些都是什麼呢?

CMakeList.txt

第一行 project 不是強制性的,但最好始終都加上。這一行會引入兩個變量

  • HELLO_BINARY_DIR 和 HELLO_SOURCE_DIR

同時,cmake自動定義了兩個等價的變量

  • PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR

由於是out-of-source方式構建,因此咱們要時刻區分這兩個變量對應的目錄

能夠經過message來輸出變量的值

message(${PROJECT_SOURCE_DIR})

set 命令用來設置變量

add_exectuable 告訴工程生成一個可執行文件。

add_library 則告訴生成一個庫文件。

  • 注意:CMakeList.txt 文件中,命令名字是不區分大小寫的,而參數和變量是大小寫相關的。

cmake命令

cmake 命令後跟一個路徑(..),用來指出 CMakeList.txt 所在的位置。

因爲系統中可能有多套構建環境,咱們能夠經過-G來制定生成哪一種工程文件,經過 cmake -h 可獲得詳細信息。

要顯示執行構建過程當中詳細的信息(好比爲了獲得更詳細的出錯信息),能夠在CMakeList.txt內加入:

  • SET( CMAKE_VERBOSE_MAKEFILE on )

或者執行make時

  • $ make VERBOSE=1

或者

  • $ export VERBOSE=1
  • $ make

例子二

一個源文件的例子一彷佛沒什麼意思,拆成3個文件再試試看:

  • hello.h 頭文件
#ifndef DBZHANG_HELLO_
#define DBZHANG_HELLO_
void hello(const char* name);
#endif //DBZHANG_HELLO_
  • hello.c
#include <stdio.h>
#include "hello.h"

void hello(const char * name)
{
    printf ("Hello %s!/n", name);
}
  • main.c
#include "hello.h"
int main()
{
    hello("World");
    return 0;
}
  • 而後準備好CMakeList.txt 文件

 

project(HELLO)
set(SRC_LIST main.c hello.c)
add_executable(hello ${SRC_LIST})

執行cmake的過程同上,目錄結構

 

+
| 
+--- main.c
+--- hello.h
+--- hello.c
+--- CMakeList.txt
|
/--+ build/
   |
   +--- hello.exe

例子很簡單,沒什麼可說的。

例子三

接前面的例子,咱們將 hello.c 生成一個庫,而後再使用會怎麼樣?

改寫一下前面的CMakeList.txt文件試試:

project(HELLO)
set(LIB_SRC hello.c)
set(APP_SRC main.c)
add_library(libhello ${LIB_SRC})
add_executable(hello ${APP_SRC})
target_link_libraries(hello libhello)

和前面相比,咱們添加了一個新的目標 libhello,並將其連接進hello程序

而後想前面同樣,運行cmake,獲得

+
| 
+--- main.c
+--- hello.h
+--- hello.c
+--- CMakeList.txt
|
/--+ build/
   |
   +--- hello.exe
   +--- libhello.lib

裏面有一點不爽,對不?

  • 由於個人可執行程序(add_executable)佔據了 hello 這個名字,因此 add_library 就不能使用這個名字了
  • 而後,咱們去了個libhello 的名字,這將致使生成的庫爲 libhello.lib(或 liblibhello.a),很不爽
  • 想生成 hello.lib(或libhello.a) 怎麼辦?

添加一行

set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")

就能夠了

例子四

在前面,咱們成功地使用了庫,但是源代碼放在同一個路徑下,仍是不太正規,怎麼辦呢?分開放唄

咱們期待是這樣一種結構

+
|
+--- CMakeList.txt
+--+ src/
|  |
|  +--- main.c
|  /--- CMakeList.txt
|
+--+ libhello/
|  |
|  +--- hello.h
|  +--- hello.c
|  /--- CMakeList.txt
|
/--+ build/

哇,如今須要3個CMakeList.txt 文件了,每一個源文件目錄都須要一個,還好,每個都不是太複雜

  • 頂層的CMakeList.txt 文件
project(HELLO)
add_subdirectory(src)
add_subdirectory(libhello)
  • src 中的 CMakeList.txt 文件
include_directories(${PROJECT_SOURCE_DIR}/libhello)
set(APP_SRC main.c)
add_executable(hello ${APP_SRC})
target_link_libraries(hello libhello)
  • libhello 中的 CMakeList.txt 文件
set(LIB_SRC hello.c)
add_library(libhello ${LIB_SRC})
set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")

恩,和前面同樣,創建一個build目錄,在其內運行cmake,而後能夠獲得

  • build/src/hello.exe
  • build/libhello/hello.lib

回頭看看,此次多了點什麼,頂層的 CMakeList.txt 文件中使用 add_subdirectory 告訴cmake去子目錄尋找新的CMakeList.txt 子文件

在 src 的 CMakeList.txt 文件中,新增長了include_directories,用來指明頭文件所在的路徑。

例子五

前面仍是有一點不爽:若是想讓可執行文件在 bin 目錄,庫文件在 lib 目錄怎麼辦?

就像下面顯示的同樣:

   + build/
   |
   +--+ bin/
   |  |
   |  /--- hello.exe
   |
   /--+ lib/
      |
      /--- hello.lib
  • 一種辦法:修改頂級的 CMakeList.txt 文件
project(HELLO)
add_subdirectory(src bin)
add_subdirectory(libhello lib)

不是build中的目錄默認和源代碼中結構同樣麼,咱們能夠指定其對應的目錄在build中的名字。

這樣一來:build/src 就成了 build/bin 了,但是除了 hello.exe,中間產物也進來了。還不是咱們最想要的。

  • 另外一種方法:不修改頂級的文件,修改其餘兩個文件

src/CMakeList.txt 文件

include_directories(${PROJECT_SOURCE_DIR}/libhello)
#link_directories(${PROJECT_BINARY_DIR}/lib)
set(APP_SRC main.c)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
add_executable(hello ${APP_SRC})
target_link_libraries(hello libhello)

libhello/CMakeList.txt 文件

set(LIB_SRC hello.c)
add_library(libhello ${LIB_SRC})
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")

例子六

在例子三至五中,咱們始終用的靜態庫,那麼用動態庫應該更酷一點吧。 試着寫一下

若是不考慮windows下,這個例子應該是很簡單的,只須要在上個例子的 libhello/CMakeList.txt 文件中的add_library命令中加入一個SHARED參數:

add_library(libhello SHARED ${LIB_SRC})

但是,咱們既然用cmake了,仍是兼顧不一樣的平臺吧,因而,事情有點複雜:

  • 修改 hello.h 文件
#ifndef DBZHANG_HELLO_
#define DBZHANG_HELLO_
#if defined _WIN32
    #if LIBHELLO_BUILD
        #define LIBHELLO_API __declspec(dllexport)
    #else
        #define LIBHELLO_API __declspec(dllimport)
    #endif
#else
    #define LIBHELLO_API
#endif
LIBHELLO_API void hello(const char* name);
#endif //DBZHANG_HELLO_
  • 修改 libhello/CMakeList.txt 文件
set(LIB_SRC hello.c)
add_definitions("-DLIBHELLO_BUILD")
add_library(libhello SHARED ${LIB_SRC})
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")

恩,剩下來的工做就和原來同樣了。

相關文章
相關標籤/搜索