人間觀察
帶飯去上班的都是成年人的奢侈品!
技術(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è)置變量
- set 直接設(shè)置變量的值,比如上面的demo
# 指定輸出 .so 動(dòng)態(tài)庫(kù)的目錄位置
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/libs)
- 追加設(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)這樣,是不是很喜歡:
