Android-JNI開(kāi)發(fā)系列《八》CMakeLists.txt語(yǔ)法&使用

人間觀察

帶飯去上班的都是成年人的奢侈品!

技術(shù)永遠(yuǎn)在不斷的更新升級(jí),Android也一樣。

目前在Android中的JNI開(kāi)發(fā)都是采用的CMake進(jìn)行編譯c,c++代碼來(lái)構(gòu)建項(xiàng)目,早期都是Android.mk、Application.mk文件來(lái)構(gòu)建項(xiàng)目的。
CMake是啥呢?簡(jiǎn)單的說(shuō)它是一個(gè)跨平臺(tái)的編譯工具,它可以用簡(jiǎn)單的配置文件就可以生成編譯的中間產(chǎn)物(Makefile 或者 project 文件),然后用make生成可執(zhí)行的文件。

CMake官網(wǎng)地址 CMake官網(wǎng)

在Android Studio IDE中不需要我們單獨(dú)安裝CMake,因?yàn)镹DK已經(jīng)自帶了。

配置文件就是CMakeLists.txt文件,我們需要熟悉它的語(yǔ)法,不然你看JNI項(xiàng)目的都看不懂,那還搞啥呢!

本篇也是自己學(xué)習(xí)后的記錄,之前自己對(duì)CMakeLists.txt的了解也較少。

CMakeLists.txt 語(yǔ)法介紹

1是有的項(xiàng)目使用cmake的時(shí)候一些函數(shù)都是用大寫(xiě)的,CMakeLists.txt文件不區(qū)分大小寫(xiě),所以看到不要覺(jué)得奇怪,一樣的。
2是這個(gè)文件ide不會(huì)對(duì)api進(jìn)行提示,所以你寫(xiě)錯(cuò)一點(diǎn)就會(huì)很難查。所以寫(xiě)的時(shí)候需要特別注意,我們相信Android studio后續(xù)版本會(huì)支持。

設(shè)置cmake的版本

指定cmake的最小版本,用于編譯該項(xiàng)目的cmake版本

cmake_minimum_required(VERSION 3.4.1)

設(shè)置項(xiàng)目名稱(chēng)

設(shè)置項(xiàng)目的名稱(chēng),這個(gè)是可選的。如果設(shè)置了會(huì)cmake會(huì)自動(dòng)給你定義兩個(gè)變量:demo_SOURCE_DIR,demo_BINARY_DIR。不過(guò)不指定的話默認(rèn)有PROJECT_SOURCE__DIR ,PROJECT_BINARY_DIR,分別代表項(xiàng)目的源文件目錄,項(xiàng)目編譯后生成的二進(jìn)制目錄。

project(demo)

調(diào)試信息打印

支持變量取值輸出${},字符串打印

message("build with debug mode")
message("project source dir=${PROJECT_SOURCE_DIR}")
message(WARNING "輸出了一條警告信息")

設(shè)置生成的類(lèi)型

用于生成可執(zhí)行文件還是so庫(kù),一般自己調(diào)試/寫(xiě)demo的時(shí)候使用add_executable,對(duì)外提供so用add_library。
add_executable(hello hello.cpp) 生成的是可執(zhí)行文件,其中第一個(gè)參數(shù)是生成可執(zhí)行文件的名字,后面的是源文件。就像之前用gcc編譯一樣,采用 gcc hello.cpp -o hello 命令來(lái)生成可執(zhí)行文件hello。

add_executable(hello hello.cpp) # 生成可執(zhí)行文件
add_library(demo STATIC demo.cpp) # 生成靜態(tài)庫(kù)
add_library(demo SHARED demo.cpp) # 生成動(dòng)態(tài)庫(kù)或共享庫(kù)

設(shè)置需要編譯的源文件和頭文件

指定頭文件目錄

include_directories 中可以設(shè)置多個(gè)如下,include_directories不區(qū)分大小寫(xiě)

INCLUDE_DIRECTORIES(
   ${PROJECT_SOURCE_DIR}/include
   ${PROJECT_SOURCE_DIR}
)

明確指定包含哪些源文件
add_library(hello  hello.cpp a.cpp b.cpp)
指定目錄下的源文件

aux_source_directory(dir var) 第一個(gè)是目錄,第二個(gè)是變量,意思就是把當(dāng)前工程目錄下的 src 目錄的下的所有源文件賦值給 SRC_LIST。賦值后用到的時(shí)候通過(guò)${SRC_LIST}即可。

aux_source_directory(${PROJECT_SOURCE_DIR}/src  SRC_LIST)
add_library(hello  ${SRC_LIST})

下面這個(gè)方式等同于上面的,意思是當(dāng)前工程目錄下的 fun1 目錄的下的所有源文件賦值給 SRC_LIST

file(GLOB SRC_LIST_1 "fun1/*.cpp")
add_library(hello ${SRC_LIST_1})

可以調(diào)用多次,比如不同的目錄設(shè)置不同的變量

file(GLOB SRC_LIST_1 "fun1/*.cpp")
file(GLOB SRC_LIST_2 "fun2/*.cpp")
add_library(hello ${SRC_LIST_1}  ${SRC_LIST_2)

當(dāng)然上面的aux_source_directory也可以調(diào)用多次,可以理解成搜索你想要的源文件并設(shè)置到多個(gè)變量上,比如:

aux_source_directory(fun1 SRC_LIST_1)
aux_source_directory(fun2 SRC_LIST_2)
add_library(hello ${SRC_LIST_1}  ${SRC_LIST_2)

接下來(lái),我們先試一下如上的這些。我們不在Android studio測(cè)試,我們直接安裝cmake工具測(cè)試,這樣方便我們更容易理解其中的道理。

如何安裝CMake呢 ?

可以參考網(wǎng)上的這篇 CMake的安裝

我們先測(cè)試一下生成可執(zhí)行文件并打印的有關(guān)的cmake提供的一些常量,我們隨便建立一個(gè)目錄,并在目錄下建立一個(gè)hello.cpp的源文件和CMakeLists.txt文件代碼如下:

#include <stdio.h>
int main(int argc, char* argv[]){
        int a=100;
        int b=200;
        printf("a+b=%d\n",(a+b));
        return 0;
}
# 指定 cmake 最低編譯版本
cmake_minimum_required(VERSION 3.4.1)

# 指定項(xiàng)目名字
project (HELLO)

# 輸出打印構(gòu)建目錄
message(STATUS "This is HELLO_BINARY_DIR " ${HELLO_BINARY_DIR})
# 輸出打印資源目錄
message(STATUS "This is HELLO_SOURCE_DIR " ${HELLO_SOURCE_DIR})

# 輸出打印資源目錄,與HELLO_SOURCE_DIR 一樣 
message(STATUS "This is PROJECT_BINARY_DIR " ${PROJECT_BINARY_DIR})
message(STATUS "This is PROJECT_SOURCE_DIR " ${PROJECT_SOURCE_DIR})

# 輸出打印 CMake 資源目錄,與 PROJECT_SOURCE_DIR 一樣 
MESSAGE(STATUS "This is CMAKE_SOURCE_DIR " ${CMAKE_SOURCE_DIR})


# 生成可執(zhí)行文件 hello 
ADD_EXECUTABLE(hello hello.cpp)

開(kāi)始編譯,新建 build 目錄(為什么要新建這個(gè)make模塊一般都是這個(gè),為了生成的中間產(chǎn)物和我們的源代碼分離),cd 到 build 目錄下,敲 cmake .. 命令,然后make指令生成可執(zhí)行文件。輸出:

B000000073160:build guxiuzhong$ cmake ..
-- The C compiler identification is AppleClang 10.0.0.10001044
-- The CXX compiler identification is AppleClang 10.0.0.10001044
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /Library/Developer/CommandLineTools/usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /Library/Developer/CommandLineTools/usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- This is HELLO_BINARY_DIR /Users/guxiuzhong/Desktop/cmake/cmake-hello/build
-- This is HELLO_SOURCE_DIR /Users/guxiuzhong/Desktop/cmake/cmake-hello
-- This is PROJECT_BINARY_DIR /Users/guxiuzhong/Desktop/cmake/cmake-hello/build
-- This is PROJECT_SOURCE_DIR /Users/guxiuzhong/Desktop/cmake/cmake-hello
-- This is CMAKE_SOURCE_DIR /Users/guxiuzhong/Desktop/cmake/cmake-hello
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/guxiuzhong/Desktop/cmake/cmake-hello/build
B000000073160:build guxiuzhong$ make
Scanning dependencies of target hello
[ 50%] Building CXX object CMakeFiles/hello.dir/hello.cpp.o
[100%] Linking CXX executable hello
[100%] Built target hello
B000000073160:build guxiuzhong$ 

可以看到打印的有關(guān)的cmake提供的一些常量值。
同時(shí)ls 一下會(huì)發(fā)現(xiàn) CMake 幫我們生成了 Makefile 等等一些文件。敲 make 命令生成 hello 可執(zhí)行文件

B000000073160:build guxiuzhong$ ls -al
total 96
drwxr-xr-x   8 guxiuzhong  672505530    256 11  1 13:07 .
drwxr-xr-x   7 guxiuzhong  672505530    224 11  1 13:07 ..
-rw-r--r--@  1 guxiuzhong  672505530   6148 11  1 13:03 .DS_Store
-rw-r--r--   1 guxiuzhong  672505530  13816 11  1 13:07 CMakeCache.txt
drwxr-xr-x  12 guxiuzhong  672505530    384 11  1 13:07 CMakeFiles
-rw-r--r--   1 guxiuzhong  672505530   5315 11  1 13:07 Makefile
-rw-r--r--   1 guxiuzhong  672505530   1558 11  1 13:07 cmake_install.cmake
-rwxr-xr-x   1 guxiuzhong  672505530   8432 11  1 13:07 hello
B000000073160:build guxiuzhong$ ./hello 
a+b=200
B000000073160:build guxiuzhong$ 

來(lái)我們接著看其它語(yǔ)法。

查找指定的鏈接庫(kù)文件

find_library(var path)查找到指定的預(yù)編譯庫(kù),并把它的路徑存儲(chǔ)在變量var中。默認(rèn)的搜索路徑為 cmake 包含的系統(tǒng)庫(kù),所以NDK 的公共庫(kù)只需要指定庫(kù)的 name 即可。比如Android jni中用于打印日志的系統(tǒng)庫(kù)log,可以如下寫(xiě)法:

find_library( # Sets the name of the path variable.
              log-lib
 
              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

設(shè)置鏈接庫(kù)搜索目錄

也可以指定多個(gè)目錄

link_directories(
    ${PROJECT_CURRENT_SOURCE_DIR}/libs

設(shè)置本項(xiàng)目生產(chǎn)的庫(kù)需要鏈接的其它庫(kù)

如果編譯本項(xiàng)目需要依賴其它的so庫(kù),通過(guò)如上的查找后,就可以通過(guò)下面的就進(jìn)行設(shè)置了。

target_link_libraries( # 目標(biāo)庫(kù)
                       hello
 
                       # 目標(biāo)庫(kù)需要鏈接的庫(kù)
                       # log-lib 是上面 find_library 指定的變量名
                       ${log-lib} )                     

到這里,我們演示下如何鏈接別人的庫(kù),首先我們先用add_library創(chuàng)建一個(gè)庫(kù),我們新建一個(gè)目錄cmake-createso然后里面建立2個(gè)名為include src的子目錄,里面放頭文件和源代碼。

include 文件夾下的2個(gè)文件

// add.h 文件
#ifndef _ADD_H
#define _ADD_H

#endif
int add(int num1, int num2);

// sub.h
#ifndef _SUB_H
#define _SUB_H

#endif
int sub(int num1, int num2);

src文件夾下的2個(gè)文件

// add.cpp
#include "add.h"
int add(int num1, int num2){
        return num1 + num2;
}
// sub.cpp
#include "sub.h"
int sub(int num1, int num2){
        return num1 - num2;
}

關(guān)鍵的我們的CMakeLists.txt文件

#版本號(hào)
cmake_minimum_required(VERSION 3.4.1)
#項(xiàng)目名字
project (math)
include_directories(${PROJECT_SOURCE_DIR}/include)
# 指定源文件
file(GLOB SRC_LIST "${PROJECT_SOURCE_DIR}/src/*.cpp")
# 指定輸出 .so 動(dòng)態(tài)庫(kù)的目錄位置
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/libs)
# 指定生成動(dòng)態(tài)庫(kù)
add_library(math SHARED ${SRC_LIST})

同樣的新建 build 目錄,建立完成目錄結(jié)構(gòu)如下:

├── build
│      CMakeLists.txt
└── libs
└── include
    └── add.h
    └── sub.h
└── src
    └── add.cpp
    └── sub.cpp

進(jìn)入到build目錄,cmake .. 沒(méi)有報(bào)錯(cuò)的話然后make,成功后會(huì)在lib下生成名為math的庫(kù),我的是mac電腦生成的是libmath.dylib,如果是在Android studio的ide中生成的是libmath.so 。

ok ,既然生成了so,接下來(lái)我們使用一下。我們?cè)诘谝淮紊蒱ello的demo中鏈接下libmath.so ,既然是使用so我們需要把include頭文件(方法聲明)夾和libs庫(kù)都拷貝到第一次生成hello的demo的build的統(tǒng)計(jì)目錄下。同時(shí)修改下hello.cpp 如下:

#include <stdio.h>
#include "add.h"
#include "sub.h"
int main(int argc, char* argv[]){
        int a = 100;
        int b = 200;
        printf("%d+%d=%d\n",a,b,add(a,b));
        printf("%d-%d=%d\n",a,b,sub(a,b));
        return 0;
}

然后target_link_libraries 通過(guò)CMakeLists.txt添加鏈接的so庫(kù)。如下:

# 指定 cmake 最低編譯版本
cmake_minimum_required(VERSION 3.4.1)

project (HELLO)

#指定頭文件目錄位置
include_directories(${PROJECT_SOURCE_DIR}/include)

#添加共享庫(kù)搜索路徑
LINK_DIRECTORIES(${PROJECT_SOURCE_DIR}/libs)

#生成可執(zhí)行文件
add_library(hello hello.cpp)

#為hello添加共享庫(kù)鏈接
target_link_libraries(hello math)

一樣進(jìn)入到build目錄,cmake .. 沒(méi)有報(bào)錯(cuò)的話然后make,成功后輸出

B000000073160:build guxiuzhong$ make
Scanning dependencies of target hello
[ 50%] Building CXX object CMakeFiles/hello.dir/hello.cpp.o
[100%] Linking CXX executable hello
[100%] Built target hello
B000000073160:build guxiuzhong$ ./hello 
100+200=300
100-200=-100

設(shè)置變量

  1. set 直接設(shè)置變量的值,比如上面的demo
# 指定輸出 .so 動(dòng)態(tài)庫(kù)的目錄位置
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/libs)
  1. 追加設(shè)置變量的值
set(SRC_LIST  hello.cpp)
set(SRC_LIST ${SRC_LIST}  fun1.cpp)
add_executable(hello  ${SRC_LIST})

ok,到這里就結(jié)束了。

上面寫(xiě)的這些知識(shí)只是簡(jiǎn)單的了解和入門(mén),不至于下次看到覺(jué)得一臉懵逼。
cmake的語(yǔ)法不亞于一種腳本語(yǔ)言,比如if…elseif…else…endif等條件控制。

可以參考官網(wǎng),或者用到的時(shí)候再學(xué)習(xí)下。
官網(wǎng)開(kāi)發(fā)文檔

C/C++ 開(kāi)發(fā)工具推薦CLion

上面的代碼都是手寫(xiě)的,太煩了,而且CMakeLists.txt文件也不好寫(xiě)。

最后,推薦CLion,CLion 是 JetBrains 推出的全新的 C/C++ 跨平臺(tái)集成開(kāi)發(fā)環(huán)境。有Mac版本的,界面像Android studio。長(zhǎng)這樣,是不是很喜歡:

CLion-IDE
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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