Android CMake學(xué)習(xí)

什么是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)的變量。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容