什么是CMake?
- 在Android Studio 2.2及以上,構(gòu)建原生庫的默認(rèn)工具是CMake。
- CMake是一個跨平臺的構(gòu)建工具,可以用簡單的語句來描述所有平臺的安裝(編譯過程)。能夠輸出各種各樣的makefile或者project文件。CMake并不直接構(gòu)建出最終的軟件,而是產(chǎn)生其他工具的腳本(如makefile),然后再依據(jù)這個工具的構(gòu)建方式使用。
- CMake是一個比make更高級的編譯配置工具,它可以根據(jù)不同的平臺、不同的編譯器,生成相應(yīng)的makefile或者vcproj項目,從而達(dá)到跨平臺的目的。Android studio利用CMake生成的是ninja。 ninja是一個小型的關(guān)注速度的構(gòu)建系統(tǒng)。不需要關(guān)心ninja的腳本,知道怎么配置CMake就可以了。
- CMake是一個跨平臺的支持產(chǎn)出各種不同的構(gòu)建腳本的一個工具。
使用Androidstudio創(chuàng)建一個C/C++Support的項目,默認(rèn)在app/src/main目錄下會生成cpp目錄,里面包含CMake Lists.txt和native-lib.cpp。如下代碼為CMakeLists.txt去掉英文注釋格式化后的內(nèi)容。
cmake_minimum_required(VERSION 3.4.1)
add_library(native-lib
SHARED
native-lib.cpp )
find_library(log-lib
log )
target_link_libraries(
native-lib
${log-lib})
常用命令
cmake_minimum_required(VERSION 3.4.1)
# 指定cmake最低支持的版本
aux_source_directory(.DIR_SRCS)
# 查找當(dāng)前目錄所有源文件,并將源文件名稱列表保存DIR_SRCS變量
# 不能查找子目錄
# 添加一個庫
add_library(<name> [STATIC | SHARED |
MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
# 添加一個庫文件,名為<name>
# 指定STATIC,SHARED,MODULE參數(shù)來指定庫類型。(靜態(tài)庫/動態(tài)庫/在使用dyld的系統(tǒng)
# 有效,如不支持,等同于SHARED)
# EXCLUDE_FROM_ALL: 表示該庫不會被默認(rèn)構(gòu)建
# source1... 用來指定庫的源文件
# 導(dǎo)入預(yù)編譯庫
add_library (<name>
<SHARED | STATIC | MODULE | UNKNOWN> IMPORTED)
# 比如
add_library(test SHARED IMPORTED)
set_target_properties(
test # 指明目標(biāo)庫名
PROPERTIES IMPORTED_ LOCATION # 指明要設(shè)置的參數(shù)
庫路徑/${ANDROID_ABI}/libtest.so # 導(dǎo)入庫的路徑
)
# 添加一個已存在的預(yù)編譯庫,名為<name>
# 一般配合set_target_properties 使用
#set命令 設(shè)置CMake變量
# 設(shè)置可執(zhí)行文件的輸出路徑(EXCUTABLE_OUTPUT_PATH是全局變量)
set (EXECUTABLE_OUTPUT_PATH [output_path])
# 設(shè)置庫文件的輸出路徑(LIBRARY_ OUTPUT_ PATH是全局變量)
Set (LIBRARY_ OUTPUT_ PATH [output path])
# 設(shè)置C++編譯參數(shù)(CMAKE_CXX_FLAGS是全局變量)
set (CMAKE_CXX_FLAGS "-Walt std=c++11")
# 設(shè)置源文件集合(SOURCE_FILES是本地變量即自定義變量)
set (SOURCE FILES main.cpp test.cpp ...)
include_directories(./include ${MY_INCLUDE})
# 可以用相對或絕對路徑,也可以用自定義的變量值
# 設(shè)置于頭文件目錄
# 相當(dāng)于g++選項中的-I參數(shù)
add_executable(<name> ${SRC_LIST})
# 添加可執(zhí)行文件
target_link_libraries(<name> lib1 lib2 lib3)
# 如果出現(xiàn)相互依賴的靜態(tài)庫,CMake會允許依賴圖中包含循環(huán)依賴,如:
add_library(A STATIC a.c)
add_library(B STATIC b.c)
target_link_libraries(A B)
target_link_libraries(B A)
add_executable(main main.c)
target_link_libraries(main A)
# 將若干庫連接到目標(biāo)庫文件
# 鏈接的順序應(yīng)當(dāng)符合gcc鏈接順序規(guī)則,被鏈接的庫放在依賴它的庫的后面,即如果上面的的命令中,lib1依賴于lib2,lib2又依賴于lib3,則在上面的命令中必須嚴(yán)格按照 lib1 lib2 lib3的順序排列,否則會報錯
add_definitions(-DF00 -DDEBUG ...)
# 為當(dāng)期路徑以及子目錄的源文件加入由-D引入的define flag
add_subdirecroty(sub_dir [binary_dir])
# sub_dir 指定包含CMakeLists.txt 和 源碼文件的子目錄位置
# binary_dir 是輸出路徑,一般可以不指定
#如果當(dāng)前目錄還有子目錄可以使用add_subdirectory,子目錄也需要包含有CMakeLists.txt
# file 文件操作命令
# 將message寫入filename文件中,會覆蓋文件原有內(nèi)容
file (WRITE filename "message")
# 將message寫入filename文件中,會追加在文件末尾
file(APPEND filename "message")
# 從filename 文件中讀取內(nèi)容井存儲到var變量中,如果指定了numBytes和offset.
# 則從offset處開始最多讀numBytes個字節(jié),另外如果指定了HEX參數(shù),則內(nèi)容會以十六進(jìn)制形式存儲在var交量中
file (READ filename var [LIMIT numeytes] [OFFSET offset] [HEX])
# 重命名文件
file (RENAME <oldname> <newname>)
# 刪除文件,同rm命令
file (REMOVE [file1 ...])
# 遞歸的執(zhí)行刪除文件命令,等于rm -r
file (REMOVE_RECURSE [file1 ...])
# 根據(jù)指定的url下載文件
# timeout超時時間;下載的狀態(tài)會保存到status中;下載日志會被保存到log;sum指定所下載文件預(yù)期的MD5值,如果指定會自動進(jìn)行比,
# 如果不一致,則返回一個錯誤;SHOW_ PROGRESS,進(jìn)度信息會以狀態(tài)信息的形式被打印出來
file(DOWNLOAD url file (TIMEOUT timeout] [STATUS status] [LOG log] [EXPECTED_MD5 sum] [SHOW PROGRESS])
# 創(chuàng)建目錄
file(MAKE_DIRECTORY [dir1 dir2 ...])
# 會把path轉(zhuǎn)換為以unix的/開頭的cmake風(fēng)格路徑,保存在result中
file(TO_CMAKE_PATH path result)
# 它會把cmake風(fēng)格的路徑轉(zhuǎn)換為本地路徑風(fēng)格:windows下用”\”,而unix下用”/°
file(TO_NATIVE_PATH path result)
# 將會為所有匹配查詢表達(dá)式的文件生成一個文件list,并將該list存儲進(jìn)變量variable里,如果一個表達(dá)式指定 了RELATIVE,返回的結(jié)果
# 將會是相對于給定路徑的相對路徑,查詢表達(dá)式例子:*.cxx,*. vt?
# NOTE:按照官方文檔的說法,不建議使用file的GLOB指令來收集工程的源文件
file (GLOB variable [RELATIVE path] (globbing expressions]...)
set_directory_properties(PROPERTIES prop1 value1 prop2 value2)
# 設(shè)置某個路徑的一種屬性
# prop1,prop2代表屬性,取值為 INCLUDE_DIRECTORIES,LINK_DIRECTORIES,INCLUDE_REGULAR_EXPRESSION,ADDITIONAL_MAKE_CLEAN_FILES
set_property(<GLOBAL |
DIRECTORY [dir] |
TARGET [target ...] |
SOURCE [src1 ...] |
TEST [test1 ...] |
CHCHE [entry1 ...] | >
[APPEND]
PROPERTY <name> [value ...] )
# 在給定的作用域內(nèi)設(shè)置一個命名的屬性
# PROPERTY 參數(shù)是必須的
# 第一個參數(shù)決定了屬性可以影響的作用域:
GLOBAL: 全局作用域
DIRECTORY:默認(rèn)當(dāng)前路徑,也可以用[din]指定路徑
TARGET:目標(biāo)作用域,可以是0個或多個已有目標(biāo)
SOURCE:源文件作用域,可以是0個或多個源文件
TEST:測試作用域,可以是0個或多個已有的測試
CACHE:必須指定0個或多個cache中己有的條目
多個源文件處理
如果源文件很多,把所有文件一個個加入很麻煩,可以使用aux_source_directory命令或file命令,會查找指定目錄下的所有源文件,然后將結(jié)果存進(jìn)指定變量名。
cmake_minimum_required(VERSION 3.4.1)
# 查找當(dāng)前目錄所在源文件 并將名稱保存到DIR_SRCS 變量
# 不能查找子目錄
aux_source_directory(. DIR_SRCS)
# 也可以使用
# file(GLOB DIR_SRCE *.c *.cpp)
add_library(
native-lib
SHARED
${DIR_SRCS})
多目錄對源文件處理
主目錄中的CMakeLists.txt中添加add_subdirectory(child)命令,指明本項目包含一個子項目child。并在target_link_libraries指明本項目需要鏈接一個名為child的庫。
子目錄child中創(chuàng)建CMakeLists.txt,這里child編譯為共享庫。
cmake_minimum_required(VERSION 3.4.1)
aux_source_directory(. DIR_SRCS)
# 添加child子目錄下的cmakelist
add_subdirectory(child)
add_library(
native-lib
SHARED
${DIR_SRCS})
target_link_libraries(native-lib child)
-----------------------------------------------------------
#child目錄下的CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
aux_source_directory(. DIR_SRCS)
add_library(
child
SHARED
${DIR_LIB_SRCS})
添加預(yù)編譯庫(Android6.0之前)
- 假設(shè)我們本地項目引用了libimported-lib.so.
- 添加add_library命令,第一個參數(shù)是模塊名,第二個參數(shù)SHARED表示動態(tài)庫,STATIC表示靜態(tài)庫,第三
個參數(shù)IMPORTED表示以導(dǎo)入的形式添加。 - 添加set_target_properties命令設(shè)置導(dǎo)入路徑屬性。
- 將import-lib添加到target_link_libraries命令參數(shù)中,表示native-lib需要鏈接imported-lib模塊
cmake_minimum_required(VERSION 3.4.1)
# 使用 IMPORTED 標(biāo)志告知 CMake 只希望將庫導(dǎo)入到項目中
# 如果是靜態(tài)庫則將shared改為static
add_library( imported-lib
SHARED
IMPORTED )
# 參數(shù)分別為:庫、屬性、導(dǎo)入地址、庫所在地址
set_target_properties (
imported-lib
PROPERTIES
IMPORTED_LOCATION
<路徑>/libimported-lib.so)
aux_source_directory(. DIR_SRCS)
add_library(
native-lib
SHARED
${DIR_SRCS})
target_link_libraries(native-lib imported-lib)
添加預(yù)編譯庫(Android6.0以后)
在Android 6.0及以上版本,如果使用上面的方法添加預(yù)編譯動態(tài)庫的話,會有問題??梢允褂昧硗庖环N方式來配置。
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -L [SO所在目錄]")
# set 命令定義一個變量
# CMAKE_C_FLAGS: c的參數(shù),會傳遞給編譯器
# 如果是c++ 文件,需要CMAKE_CXX_FLAGS
# -L :庫的查找路徑
添加頭文件目錄
為了確保CMake可以在編譯時定位頭文件,使用include_ directories, 相當(dāng)于g++選項中的-I參數(shù)。這樣就可以使用#include <xx.h>,否則需要使用 #include “path/xx.h”
cake minimum required (VERSION 3.4.1)
# 設(shè)置頭文件目錄
include_directories(<文件目錄>)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} - L[so所在目錄]")
aux_source_directory(. DIR_SRCS)
add_library(
native-lib
SHARED
${DIR_SRCS})
target_link_libraries (native-lib imported-lib)
Build.gradle配置
android {
defaultConfig {
externalNativeBuild {
cmake {
// 使用編譯器 clang/gcc
// cmake默認(rèn)就是gnustl_static
arguments "-DANDROID_TOOLCHAIN=clang",
"-DANDROID_STL=gnustl_static"
cFlags " "
cppFlags ""
// 指定需要編譯的cpu架構(gòu)
abiFilters "x86","armeabi-v7a","arm64-v8a"
}
}
ndk {
abiFilters "x86","armeabi-v7a","arm64-v8a"http://指定第三方庫的cpu架構(gòu)
}
}
externalNativeBuild {
cmake {
// 指定CMakeList.txt文件相對當(dāng)前Build.gradle的路徑
path "src/main/cpp/CMakeLists.txt"
version "3.6.0"
}
}
}
CMake
源文件
- CMake的源碼文件可以包含命令、注釋、空格和換行。
- 以CMake編寫的源文件以CMakelists.txt命名或以.cmake為擴(kuò)展名。
- 可以通過add_subdirectory()命令把子目錄的CMake源文件添加進(jìn)來。
- CMake源文件中所有有效的語句都是命令,可以是內(nèi)置命令或自定義的函數(shù)/宏命令。
CMake 注釋
- 單行注釋:#注釋內(nèi)容(注釋從#開始到行尾結(jié)束)
- 多行注釋:可以使用括號來實現(xiàn)多行注釋:#[[多行注釋]]
CMake變量
- CMake中所有變量都是string類型。可以使用set()和unset()命令來聲明或移除一個變量
- 變量的引用: ${變量名}
# 聲明變量: set(變量名 變量值)
set(var 111)
# 引用變量 message命令來打印
message("var = ${var}")
CMake列表
- 列表也是字符串,可以把列表看作一個特殊的變量,這個變量有多個值。
- 語法格式:set(列表名 值1 值2… 值N) 或 set(列表名 "值1;值2;...;值N")
- 列表的引用:${列表名}
CMake流程控制
操作符、布爾常量值、條件命令,循環(huán)命令、循環(huán)遍歷的幾種寫法
CMake自定義宏命令
macro(ma x y z)
message("call macro ma")
message("x = ${x}")
message("y = ${y}")
message("z = ${z}")
endmacro(ma)
ma(1 2 3)
// 自定義宏命令格式:
// macro(<name> [arg1 [arg2 [arg3...]]])
// COMMAND()
//endmacro(<name>)
// 宏命令調(diào)用格式:name(實參列表)
CMake變量的作用域
- 全局層:cache變量,在整個項目范圍可見,一股在set定義變量時,指定CACHE參數(shù)就能定義為cache變量。
- 目錄層:在當(dāng)前目錄CMakelists.txt中定義,以及在該文件包含的其他cmake源文件中定義的變量。
-函數(shù)層:在命令函數(shù)中定義的變量,屬于函數(shù)作用域內(nèi)的變量。