跳到主要內容

[C++] CMake 建置 C/C++ 程式

在編譯程式的時候,會設定一些組態(configuration,簡稱 config)。
這些組態包含我們的編譯過程需要用哪一種編譯器,哪一些函式庫,要編譯哪些檔案和編譯的指令等等。
以 Linux 環境而言(Windows 系統的操作也大同小異,原理都是一樣的),在編譯 C++ 專案時,上述這些組態可以寫在一個叫做 Makefile 的檔案裡。在 Makefile 檔案所在資料夾執行 make 指令,make 這隻程式,就會吃進 Makefile 並根據 Makefile 內的組態設定做出對應編譯的動作。
通常這個 Makefile 寫起來很繁瑣,因此才會有 cmake 這個工具的誕生。

CMake 是一個能自動幫我們產生 Makefile 的程式。
CMake 基本的觀念就是,我們只要指定好編譯器種類,以及我們要編譯的目錄、執行檔名稱等等設定,CMake 就會產生 Makefile。

CMake 基本使用方式,假如我們有個資料夾結構如下:
.
├── build
├── CMakeLists.txt
├── lib
├── reference
│   └── matrix_effect.cpp
└── src
    └── meffect.cpp
這種資料夾結構是我習慣的 hierarchy。用這樣的資料夾做管理,專案比較不會凌亂。
src 資料夾裡面放 .cpp 程式碼。
lib 資料夾放程式碼用到的外部函式庫,即 include 的 header file。
而程式碼編譯成執行檔的動作,則在 build 資料夾內操作。
reference 資料夾內則放一些參考資料跟文件。

build 資料夾內有個 CMakeLists.txt,這個檔案就是 cmake 指令需要的檔案,以這個專案資料夾結構為例,CMakeLists.txt 內容可以這樣寫:


cmake_minimum_required(VERSION 2.8.9)
project(meffect)

include_directories(lib)


file(GLOB SOURCES "src/*.cpp")

add_executable(meffect ${SOURCES})

然後在 build 資料夾內執行:
cmake ..
如果順利的話,會出現 Configuring done 和 Generating done 訊息。
接著執行:
make
這個指令會開始編譯程式碼,編譯成功會顯示:

[ 50%] Building CXX object CMakeFiles/meffect.dir/src/meffect.cpp.o
[100%] Linking CXX executable meffect
[100%] Built target meffect

然後就可以在 build 資料夾內找到程式的執行檔了。

注意:
cmake 會產生一些中間檔案,在 build 資料夾內執行 cmake 後,這些中間檔就會產生在 build 資料夾內。cmake 沒有 clean 指令,因此若 cmake 指令有些錯誤或問題的話,要刪除 cmake 產生的這些殘留檔案,必須將整個 build 資料夾砍掉再新增一個新的 build 資料夾。修好 CMakeLists.txt 後,再重新執行 cmake 一次。
為了避免 CMakeLists.txt 不小心被砍掉,我會將 CMakeLists.txt 放在專案第一層的資料夾內,如上述 hierarchy 所示。

Reference: 這個討論串

留言

這個網誌中的熱門文章

[程式競賽] UVa 572, Oil Deposits,Flood Fill 演算法

原題目簡述如下: 以 m x n 大小的 grid 代表一張地圖,現今要在此地圖內探勘,找出油田。某一區塊如果標示 "@" 代表有油,"*" 代表沒有油。 "@" 相鄰的區域的聯集,可視為一個油田。(所謂相鄰,除了上下左右,斜向的相鄰也算進去) 求任意地圖中,油田的個數。 例如輸入的測資為: *    *   *    *  @ *   @  @  *  @ *   @   *   *  @ @ @  @   * @ @ @   *   *  @ 則油田個數為 2。 想法 採用典型的倒水演算法(Flood Fill),走訪 "@" 出現的區域,從此往下倒水,倒過水的區域標上 id,因此透過 id 的編號,可以得知油田的個數。 實作 先實作倒水演算法的子函式: void floodfill(vector<vector<char> >& map,                vector<vector<int>  >& id_table,                int row, int col, int id) {    if(row < 0 || (row >= map.size()) )   return;    if(col < 0 || (col >= map[0].size())) return;    if(map[row][col] != '@' || id_table[row][col] > 0) return;    id_table[row][col] = id;    floodfill(map, id_table, row-1, col-1, id);    floodfill(map, id_table, row-1, col,   id);    floodfill(map, id_table, row-1, col+1, id);    floodfill(map, id_table, row,   col-1, id);    floodfill(map, id_table, row,   col+1,

[Python] print 同時輸出到 file 和 console

在 Python 撰寫程式時,我們會希望螢幕 stdout 輸出可以同時記錄到 log 檔案裡。 但是螢幕輸出可能含有 ASCII escape codes 的顏色資訊,輸出的 log 檔案會有類似 ^[[01;32m 這種字樣出現。 我採用比較簡單的解法: 先將 print 函式輸出的訊息,同時導向到螢幕,同時儲存在指定的 log.txt 檔案中。 再用 sed 指令,將 log.txt 內的 ASCII escape code 清除。 方法如下: import sys class PrintLog(object): def __init__(self): self.console = sys.stdout self.log_file = open("log.txt", "w") def write(self, msg): self.console.write(msg) self.log_file.write(msg) def flush(self): pass def main(): original_stdout = sys.stdout sys.stdout = PrintLog() print " This is a testing message." sys.stdout = original_stdout if(__name__ == "__main__"): main() 也就是將 sys.stdout 指向自定義的 PrintLog class,讓 PrintLog 來處理輸出文字,用完 PrintLog 後再把 sys.stdout 導向回原本的 stdout。 接著使用 sed 指令刪除 log.txt 的 ASCII escape code: sed -i 's/\x1b[^m]*m//g' ./log.txt 上面的正規,\x 後面用來接一個 16 進位 ASCII 編碼,其中 1b 代表的是 ESC 退出鍵。 到此即可獲得沒有顏色編碼的 log.tx

[Python] 單引號,雙引號和三引號

在此解釋各種引號的意義。 雙引號跟單引號,在 Python 中的基本上沒差別,都可以代表字串: "This is a string" 'This is a string' 並且雙引號內可包含單引號,反之,如果用的是單引號,則單引號內可包含雙引號: "We call it 'Dog'...... " 'We call it "Dog"...... ' 三個雙引號,就可以直接輸入有換行的字串: """haha, this is a dog.""" 三個單引號要換行,就要輸入"\": '''haha, \ this is a dog.'''