用CMake代替makefile進行跨平臺交叉編譯

 在開始介紹如何使用CMake編譯跨平臺的靜態庫以前,先講講我在沒有使用CMake以前所趟過的坑。由於不少開源的程序,好比png,都是自帶編譯腳本的。咱們可使用下列腳原本進行編譯:html

1linux

2android

3ios

./configure  --prefix=/xxx/xx --enable-static=YESc++

makegit

make installgithub

  相信手動在類Unix系統上面編譯過開源程序的同窗對上面的命令確定很是熟悉。更悲慘的是,有些開源庫是不提供configure配置文件的,只有一個Makefile或者Makefile.gcc。個人體會是,Makefile是一個很複雜的東西,沒有必定的積累咱們是看不懂的,更別說去修改它了。而本文的CMake能夠更傻瓜更簡單地達到咱們的目的,你不須要理會複雜的makefile語法。Just follow me!編程

   若是不配置編譯器和一些編譯、連接參數,這樣的操做,最後編譯出來的靜態庫只能在本系統上面被連接使用。好比你在mac上面運行上面的命令,編譯出來的靜態庫就只能給mac程序連接使用。若是在Linux上面運行上述命令,則也只能給Linux上面的程序所連接使用。若是咱們想要在Mac上面編譯出ios和android的靜態庫,就必需要用到交叉編譯。windows

  要進行交叉編譯,通常來講要指定目標編譯平臺的編譯器,一般是指定一個CC環境變量,根據編譯的是c庫仍是c++庫,要分別指定C_flags和CXX_flag,固然還須要指定c/c++和系統sdk的頭文件包含路徑。總之,很是之繁瑣。xcode

 


 

爲何要使用CMake

  爲何咱們不使用autoconf?爲何咱們不使用QMake,JAM,ANT呢?具體緣由你們能夠參考我在本文最後的參考連接裏面的《Mastering CMake》一書的第一章。我本身使用CMake的感覺就是:我原來編寫bash,配置configure參數,讀各個開源庫的INSTALL文件(由於不一樣庫的configure參數有差異),配置各類編譯flag,頭文件包含等。最後3天時間,折騰了png和jepg兩個庫的編譯。固然,中間我還寫了android和linux的編譯腳本。而換用CMake之後,我2天時間編譯完了Box2D,spine和Chipmunk的編譯。而且配置腳本至關簡單,添加新的庫,基本上只是拷貝腳本,修改一兩個參數便可。有了CMake,編譯跨平臺靜態庫和生成跨平臺可執行程序So Easy!

  


 編寫CMakeLists.txt

  編寫一個靜態庫的CMake配置文件過程以下:(這裏我以Box2D爲例)

一、指定頭文件和源文件

1

2

3

4

5

include_directories(

  ${CMAKE_CURRENT_SOURCE_DIR}

)

 

file(GLOB_RECURSE box2d_source_files "${CMAKE_CURRENT_SOURCE_DIR}/Box2D/*.cpp")

  個人CMakeLists.txt和Box2D的文件結構關係以下圖所示:

  這裏的${CMAKE_CURRENT_SOURCE_DIR}表示CMakeLists.txt所在的目錄。而GLOB_RECURSE能夠遞歸地去搜索Box2D目錄下面全部的.cpp文件來參與靜態庫的編譯。而include_directories和file指令,顯而易見,它們是用來指定靜態庫的頭文件和實現文件。

  注:指定頭文件的原則是:能夠多引入,但不能缺。交叉編譯本質也是編譯,所以基本的要求是語法沒問題,若是必要的頭文件缺乏了天然編譯會失敗!因此,原則上能夠把整個根目錄的頭文件都引入進去,不過這樣雖然省事,可是會致使生成的庫文件體積過大,可是會更保險一些,好比:

 

1

2

3

4

5

include_directories(

  "../../../myWindows"

  "../../../"#很殘暴地引入了整個根目錄

  "../../../include_windows"

)

 

  

二、添加環境變量(可選, added by 編程小翁, 博客園)

1

add_definitions( -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_REENTRANT -DENV_UNIX -DBREAK_HANDLER -DUNICODE -D_UNICODE)

若是須要判斷平臺,能夠這麼寫:

1

2

3

4

IF(APPLE)

  add_definitions(-DENV_MACOSX)

  FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation )

ENDIF(APPLE)

其中-D_FILE_OFFSET_BITS=64表示定義一個環境變量_FILE_OFFSET_BITS且值爲64。添加環境變量用在何時呢?咱們經常能夠在一些開源的項目工程代碼中看到這樣的形式:

1

2

3

4

5

#ifdef _UNICODE

  AString name = nameWindowToUnix2(fileName);

#else

  const char * name = nameWindowToUnix(fileName);

#endif

以上代碼中_UNICODE就是環境變量,那像這種變量該經過何時定義呢?一種是像上面同樣經過add_definitions寫咱們的編譯腳本CMakeLists.txt,另外一種是新建一個.h文件,寫在裏面而後引用。兩種方式徹底等效,我在個人交叉編譯工程中實踐過。例如,上面的add_definitions能夠轉化爲:

1

2

3

4

5

6

7

#define FILE_OFFSET_BITS    64

#define _LARGEFILE_SOURCE    1

#define _REENTRANT           1

#define ENV_UNIX            1

#define BREAK_HANDLER       1

#define UNICODE             1

#define _UNICODE            1

 

三、設置庫的名字跟類型

1

add_library(Box2D STATIC ${box2d_source_files})

  這裏add_library表示最終編譯爲一個庫,static表示是靜態庫,若是想編譯動態庫,能夠修改成shared. 至此,一個靜態庫的配置就完成了。固然,由於這個庫沒有包括其它外部的頭文件,因此會比較簡單。但這也遠比本身寫一個Makefile要簡單N倍,請記住這句話

 

  以上就是編寫一個CMakeLists.txt配置文件的所有必要過程,一些更復雜的配置文件可能會增長一些其餘東西,不過以上部分是基本逃不掉的。只要包含以上步驟就能成功交叉編譯出目標平臺的庫文件。下面是一個完整的CMakeLists.txt文件示例(文件名不能改):

複製代碼

1 cmake_minimum_required(VERSION 3.2)
 2 
 3 #一、添加頭文件目錄,能夠多引用,可是不能缺,由於缺了就編譯不過
 4 include_directories(
 5   "../../../myWindows"
 6   "../../../"
 7   "../../../include_windows"
 8 )
 9 
10 #二、添加環境變量,請結合實際項目要求,不是必須的
11 add_definitions( -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_REENTRANT -DENV_UNIX -DBREAK_HANDLER -DUNICODE -D_UNICODE)
12 
13 IF(APPLE)
14   add_definitions(-DENV_MACOSX)
15   FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation )
16 ENDIF(APPLE)
17 
18 #三、源文件
19 file(GLOB_RECURSE src_files
20   "../../../../C/7zCrc.c"
21   "../../../../C/7zCrcOpt.c"
22   "../../../../C/7zStream.c"
23   "../../../../C/Aes.c"
24   "../../../../C/Alloc.c"
25   "../../../../C/Bra.c"
26   "../../../../C/Bra86.c"
27 )
28 
29 #四、設置生成靜態庫以及名稱
30 add_library(myLibName STATIC ${src_files})
31 
32 IF(APPLE)
33    TARGET_LINK_LIBRARIES(myLibName ${COREFOUNDATION_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
34 ELSE(APPLE)
35 
36 IF(HAVE_PTHREADS)
37    TARGET_LINK_LIBRARIES(myLibName ${CMAKE_THREAD_LIBS_INIT})
38   ENDIF(HAVE_PTHREADS)
39 ENDIF(APPLE)

複製代碼

 


 

 

編譯iOS靜態庫

  咱們有了配置完畢的CMakeLists.txt文件,但不要覺得這樣就萬事大吉了!不知道你發現了沒,上述內容並不涉及目標平臺的相關信息,所以編譯出來的庫只能在運行該配置文件的當前系統上使用。如今須要配合接下來的操做才能最終達到目的。

  編譯iOS庫,通常要先使用cmake指令生成xcode工程,再用xcode工程運行編譯出靜態庫(也就是工程的product是靜態庫,而不是**.app)。插播一段MAC系統下cmake安裝與使用方法介紹:

複製代碼

MAC默認是沒有cmake指令的。要測試你的MAC是否已經裝過cmake,能夠這樣作:打開Terminal,輸入cmake --version,若是已經安裝,則會顯示具體的版本號;不然就是沒安裝或者沒配置成功。

一、從這裏http://mac.softpedia.com/get/Development/Compilers/CMake.shtml下載cmake.app,而後安裝到默認位置;

二、將cmake.app與terminal相連接。打開terminal,輸入如下命令:export PATH=/Applications/CMake.app/Contents/bin:$PATH

三、配置成功。此次再輸入cmake就有效了。不過,以上連接只對本terminal窗口有效,一旦關閉或者其餘新建的terminal一樣要再作一遍!

複製代碼

  回到iOS交叉編譯上來,使用cmake命令生成xcode工程能夠這麼作:

1

cmake -GXcode .

  經過該命令能夠生成一個project.xcodeproject工程。可是,上述命令並不包含任何關於iOS的信息,所以該xcode工程只能用於MAC庫的編譯。不過咱們能夠藉助ios-cmake開源項目。 下載iOS_64.cmake這個toolchain文件,而後使用下列命令來生成ios工程:

1

cmake -DCMAKE_TOOLCHAIN_FILE=iOS_64.cmake  -DCMAKE_IOS_DEVELOPER_ROOT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/ -DCMAKE_IOS_SDK_ROOT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/  -GXcode .

  這個過程很容易出錯,出錯了不要慌,根據terminal的提示大膽地更改iOS_64.cmake(記得提早備份)。我也是一步步調試過來的,如下的iOS_64.cmake是我本身更改後的,SDK是iOS8.3,Xcode6.3,若是環境跟我同樣的話理論上說能夠直接使用個人.cmake:

 個人iOS_64.cmake

  若是上面的操做都沒錯,就會順利生成一個project.xcodeproject文件,打開後記得作下面幾件事情:

一、設置Product->Scheme->Edit Scheme爲release模式

二、其餘設置如圖:

 

 

設置完畢後,點擊運行,就能生成.a靜態庫了。這時候,你可使用下面的命令測試一下生成的靜態庫是否真的是iOS下的庫。

打開terminal,cd到.a所在目錄,假設靜態庫名字爲libMyLib.a,輸入: lipo -info libMyLib.a ,若是顯示 Architectures in the fat file: lib7z_C++_938.a are: armv7 arm64  就說明操做無誤了。而後,盡情享用你的靜態庫吧!

 


 

 

編譯linux靜態庫(含64位和32位)

編譯linux的靜態庫是很是簡單的,只須要安裝好cmake之後,運行如下命令便可:

1

2

cmake .

make

注意,若是是64位的系統,那麼這樣只能生成64位的靜態庫。想要編譯出32位的靜態庫,則必需要先安裝32位系統的編譯工具鏈。

1

2

3

4

sudo apt-get install libx32gcc-4.8-dev

sudo apt-get install libc6-dev-i386

sudo apt-get install lib32stdc++6

sudo apt-get install g++-multilib

  而後,只須要指定cxx_flags爲-m32便可,對應的CMake的寫法爲:

1

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")

  最後用cmake生成makefile並make便可生成32位的靜態庫。

相關文章
相關標籤/搜索