2012年9月14日

Cmake---跨平台安裝的好朋友

曾幾何時你寫了個跨平台的專案,由於你一開始的開發平台是Linux,理所當然的你寫了makefile來維護專案的結構,雖然內容很長但運作起來十分美好,直到你準備移往win32平台時才發現一切都要重來?反過來說VS是由GUI控制專案結構的,你甚至根本無從選擇,只好來來回回在兩個平台間維護你的專案結構。

最後你崩潰了,你開始詛咒這個世界,專案結構就是專案結構啊,為什麼他們要逼你寫那麼多不一樣的東西?



於是在血與淚的吶喊中,Cmake誕生了,這是一個跨平台組織專案的工具,專門處理剪不斷理還亂的專案結構,她對於最擾人的C/C++專案特別有辦法,以下我將簡單示範用Cmake組織基本C專案的方法:

檔案結構


Demo /
    build /
    src /
        CMakeLists.txt
        App_Path /
            CMakeLists.txt
            main.c
        Old_Path /
            old.h
            Old.lib
        New_Path /
            CMakeLists.txt
            new.h
            new.h

檔案內容說明


src/CMakeLists.txt:專案結構的起點,放置於原始碼的樹狀結構頂端

# 要求使用的Cmake的最低版本,怕麻煩可直接填你使用的Cmake版本,以後忽略說明
cmake_minimum_required( VERSION 2.6 )

# RUNTIME的意思是執行檔,表示把編譯出的執行檔輸出到${CMAKE_BINARY_DIR}/bin底下
# ${CMAKE_BINARY_DIR}之後說明
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
# ARCHIVE的意思是靜態函數庫,表示把編譯出的庫輸出到${CMAKE_BINARY_DIR}/lib底下
# Linux底下是 .a檔;Win32底下是 .lib檔
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )

# 加入一個引入表頭檔的路徑,${CMAKE_SOURCE_DIR}表示原始碼的樹狀結構頂端及包含這個CMakeLists.txt的src
# 事實上只要包含CMakeLists.txt的檔案夾都可以被視為${CMAKE_SOURCE_DIR},所以需要在Cmake下指令時決定
include_directories( ${CMAKE_SOURCE_DIR} )

# 為原始碼的樹狀結構加入子檔案夾,注意Old_Path並不需要編譯,所以沒加進樹狀結構
add_subdirectory( New_Path )
add_subdirectory( App_Path )

到這裡先停下來說明我已經做了什麼:
1. 用cmake_minimum_required指定了Cmake的最低版本
2. 用set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ... 指定了執行檔的輸出位置
3. 用set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ... 指定了靜態庫的輸出位置
4. 用include_directories加入了表頭檔的參考路徑
5. 用add_subdirectory擴展了專案的結構

到目前為止我們已經有了專案結構的輪廓,現在來描述其內容:

Old_Path /


old.h:簡單的表頭檔內容不需說明

char* old_fun(void);

Old.lib:已經編譯好的靜態庫

Old_Path下的檔在在編譯時需要,但沒啥需要說明的

New_Path /


new.h:簡單的表頭檔內容不需說明

char* new_fun(void);

new.c:簡單的原始碼內容不需說明

#include "new.h"

static char str[16] = "new lib ! >w<";

char *
new_fun(void)
    {
    return str;
    }

CMakeLists.txt:描述如何將new.h、new.c編譯成靜態庫

# 略過
cmake_minimum_required( VERSION 2.6 )

# project表示建立一個專案,一個完整的編譯動作如編譯執行檔或靜態庫都需要,名稱不拘
project( new_lib )

# add_library表示這個專案會產生一個庫,New是庫的名稱,STATIC表示要產生靜態庫,new.c表示產生庫時需要的檔案,若有很多個檔案只需以空白分隔一直往下寫
add_library( New STATIC new.c )

注意到New_Path資料夾本身也是一個完整的專案,若將Cmake的${CMAKE_SOURCE_DIR}設定於此,最後的結果將產生New.lib,之前在src下的CMakeLists.txt中並沒有project敘述但卻加入了子檔案夾,這表示src下的專案並不輸出檔案,而是遞迴往下組織子檔案夾下的專案,而因為set敘述,New_Path輸出的靜態庫也會被放到${CMAKE_BINARY_DIR}/bin中,有關CMAKE_BINARY_DIR與CMAKE_SOURCE_DIR,之後會在講解。

App_Path /


main.c:最後的程式,需要Old.lib與New.lib作靜態連結,由於已經使用include_directories將../src當成引入標頭檔的路徑,所以能找到相對路徑上的Old_Path與New_Path,不需說明

#include <stdio.h>
#include "Old_Path/old.h"
#include "New_Path/new.h"
int
main ()
    {
    puts(old_fun());
    puts(new_fun());
    }

CMakeLists.txt:描述如何產生執行檔的檔案

# 略過
cmake_minimum_required( VERSION 2.6 )

# 我們需要產生執行檔,略過
project( cmake_demo )

# ADD_LIBRARY表示Old庫會被引入,STATIC表示其為靜態庫,IMPORTED表示從專案結構外引入
ADD_LIBRARY( Old STATIC IMPORTED )
# SET_PROPERTY將設定Old的屬性,PROPERTY IMPORTED_LOCATION表示要設定引入路徑,${CMAKE_SOURCE_DIR}/Old_Path/Old.lib是其路徑加上完整名稱
SET_PROPERTY( TARGET Old PROPERTY IMPORTED_LOCATION 
${CMAKE_SOURCE_DIR}/Old_Path/Old.lib )

# add_executable表示專案要產生名為App的執行檔,需要用到main.c檔,若有很多檔案一樣用空白分格繼續接
add_executable( App main.c )
# target_link_libraries表示執行檔App會使用Old與New作靜態連結,以空白分隔不是很明顯
# 注意到New並不需要ADD_LIBRARY,因為它存在於專案結構中,不需由外部引入
target_link_libraries( App Old New )

OK,來說明一下整個專案結構最後會做什麼吧:
1. Old.lib會從外部被引入,外部指的是不在原始碼的樹狀結構中,記得add_subdirectory並沒有加入她對吧
2. New.lib會被編譯出來,並被放入${CMAKE_BINARY_DIR}/lib中
3. App.exe會被編譯出來,並被放入${CMAKE_BINARY_DIR}/bin中

最後來產生專案吧


1.
首先將當前資料夾移到Demo/build/底下,是的,當前資料夾就是你的${CMAKE_BINARY_DIR},所以最後New.lib會被放到Demo/build/lib/底下,而App.exe會被放到Demo/build/bin/底下

2.
鍵入:

> cmake --help

可以看到你可以產生哪些種類的編譯器的專案

3.
鍵入:

> cmake -G "Visual Studio 10" ../src

-G "Visual Studio 10"表示要產生讓VS2010使用的專案檔,你應該用--help查看適合你有的編譯器並更改這段指令

../src指定了${CMAKE_SOURCE_DIR},所以Demo/src/就是你的原始碼的樹狀結構頂端,當然,如果你指定的是../src/New_Path/,那麼原始碼的樹狀結構頂端就是Demo/src/New_Path/,最後專案就只會產生New.lib,而且因為跳過了set敘述,所以New.lib會被輸出到編譯器預設的地方

4. 來編譯吧
最後Demo/build會出現完整的專案組態檔,以Linux來說你現在只要使用make就能在Demo/build/bin/底下得到App.exe,在Win32上就是使用VS囉,最後檔案結構應該會是:

Demo /
    build /
        bin/
            Debug/App.exe
        lib/
            Debug/New.lib
        ...
    src /
        CMakeLists.txt
        App_Path /
            CMakeLists.txt
            main.c
        Old_Path /
            old.h
            Old.lib
        New_Path /
            CMakeLists.txt
            new.h
            new.h


如果你很好奇有Debug怎麼沒有Realse,或是動態庫該怎麼使用,你在安裝Cmake後應該可以找到她的說明文件,Cmake已經被使用在多個大型專案中並得到成功,或許他能給你幫助、改善你的生活,如果你對本文的範例有興趣,載點在此。




沒有留言:

張貼留言