本章以一個簡單的例子說明如何使用 CMake 建置基本的執行檔,並且在隨後章節中透過修改這個範例達到一些常見的需求:
ex2/
main.c
#include <stdio.h>
#include "calc.h"
int main()
{
printf("Square of 2 is %d \n", Square(2));
printf("Square of 5 is %d \n\n", Square(5));
printf("Cube of 2 is %d \n", Cube(2));
printf("Cube of 5 is %d \n\n", Cube(5));
return 0;
}
int Cube(int x)
{
return x * x * x;
}
int Square(int x)
{
return x * x;
}
#ifndef CALC_H
#define CALC_H
int Cube(int x);
int Square(int x);
#endif
cmake_minimum_required(VERSION 2.6)
project(ex2)
add_executable(ex2 main.c calc.c)
本例的 CMakeLists.txt 內容只有三行,其中真正有作用的其實只有最後一行,前兩行皆可省略。
cmake_minimum_required(VERSION 2.6)
這一行功能在確定目前使用的 CMake 版本符合需求,當版本不滿足最低需求時會發出錯誤訊息。即使沒加上這一檢查仍然可以繼續完成建置工作,只是 CMake 會發出警告。
project(ex2)
這一行指定專案的名稱,指定專案名稱最主要的目的在於啟用幾個和環境相關的變數,另外也會在 makefile 增加對應的 target。在這個簡單的專案裡並不會去利用這些變數,所以對建置沒有任何影響。
add_executable(ex2 main.c calc.c)
這一行告訴 CMake 加入一個名為 ex2 之執行檔 target,而且此執行檔是由 main.c 和 calc.c 所編譯成。在同一個 CMakeLists 裡面可以加入多個不同的target,也不一定要和 project 同名。 當一個執行檔所需的原始碼很多時,可以透過變數在前面收集原始碼列表,之後再用 add_executable() 加入。
add_executable() 指令的完整格式如下:
add_executable(<name> [WIN32] [MACOSX_BUNDLE]
[EXCLUDE_FROM_ALL]
source1 source2 ... sourceN)
WIN32 和 MACOSX_BUNDLE 用於指定平臺專屬的執行檔類型。舉例來說,在 Windows 建立視窗程式時以 WinMain() 做為程式進入點,如果沒有特別指定連結成視窗程式會發生 _mainCRTStartup 連結錯誤,解決的方式為:
add_executable(exe_name WIN32 source1 source2 ... sourceN)
在非 Windows 環境下參數 WIN32 會被自動忽略。 如果加入了 EXCLUDE_FROM_ALL 表示此 target 不會在 make all 時主動建置,必須在 make 時手工指明或者是當其他 target 提出依賴需求時才會被建置。
當所需的檔案備齊之後,將當前目錄移到 ex2/src/ 下,若所在的平臺備有系統預設的編譯器,執行:
$ cmake .
不要忽略後面的句號(.),這代表目前目錄。也可以依照所使用的編譯環境指定適用的 Generator
$ cmake -G "Unix Makefiles"
$ cmake -G "MSYS Makefiles"
> cmake -G "Visual Studio 9 2008"
完成後 CMake 會在目前的目錄下輸出 makefile 或者 IDE 專案檔,另外還會產生這次建置相關資訊的快取檔,接著執行 make 即可建立程式:
$ make
Note |
CMake 所產生出來的 makefile 無法獨立運作,建置過程仍然必須依賴 CMake,因此無法直接將 makefile 搬移到沒有 CMake 的環境中使用。事實上,只要更換環境就應該重新執行 CMake,針對目前環境配置產生 makefile。 |
使用 make clean 即可刪除剛才 make 輸出的中間檔和 target。
$ make clean
然而 CMake 並不會刪除自身產出的快取和中間檔,也不會生成像 make distclean、make dist 之類的規則。讓這些檔案汙染原始碼是非常糟糕的事情,CMake 對此的解決之道是把 build tree 和 source tree 徹底分離,我們在下一節將會介紹 out-of-source build