NDK(五):CMake基礎語法及As導入靜動態庫

這篇主要介紹CMake語法學習以及怎樣把上一篇文章中編譯生成的交叉編譯庫導入到Android項目中
由于其他原因耽誤導致這篇文章間隔這么久才寫好,導入的時候才發現上一篇文章中交叉編譯后的庫有點問題又進行重新修改。這篇文章是NDK系列的最后一篇了,算是大概知道了NDK系列的入門知識。

NDK系列文章

mk

之前是使用,現在Google基本放棄了,都采用CMake

CMake

在學習CMake之前,我們或多或少了解過其他Make工具。其他的Make工具遵循不同的標準到時執行的Makefile格式不同,比如要保證在不同平臺下編譯,就需要一個跨平臺的Make工具,CMake就是一個跨平臺的構建工具。
Cmake并不直接構建出最終的軟件,通過編寫CmakeList.txt文件,根據目標用戶的平臺進一步生成對應的Makefile文件,從而達到跨平臺的目的,如Android Studio就是通過ninja。所以可以這么理解為,編寫Cmake語法,通過ninja,生成Makefile。

安裝CMake

brew install cmake
# 查看版本
cmake -version
cmake version 3.13.2

CMake語法入門

  • 官網語法手冊(最好的資料)

    學下CMake語法,可以參考官網的文檔,進入官網,然后Resource --> Documentation --> 選擇最新的Documentation --> Reference Manuals中的cmake-commands(7)。就可以看到所有的命令了。

實踐

Demo1 單文件

main.c文件

#include <stdio.h>
int main(){
    printf("hello world\n");
    return 0;
}

CMakeLists.txt文件

# CMake 最低版本號要求
cmake_minimum_required (VERSION 3.4.1)
# 項目信息(如果不設置該參數也可以正常運行)
project (MyProject1)
# 指定生成目標
add_executable(Demo1 main.c)

利用CMake命令編譯生成執行文件

# 編譯生成Makefile文件
cmake .
# 執行Makefile文件生成可執行文件
make
# 執行可執行文件,輸出hello world
./Demo1
hello world
Demo2 多文件

如果源文件有很多個,比如有main.c、hello.c、hello.h等,那么一個個寫進去比較麻煩,如

main.c文件

#include "hello.h"

int main()
{
    hello("world");
    return 0;
}

hello.h文件

#ifndef HELLO_H
#define HELLO_H
 
void hello(const char *name);
 
#endif //HELLO_H

hello.c文件

#include <stdio.h>
 
void hello(const char *name)
{
    printf("Hello %s!\n", name);
}

CMakeLists.txt

# CMake 最低版本號要求
cmake_minimum_required (VERSION 3.4.1)
# 項目信息
project (MyProject2)

# 這樣多的話,寫進去就比較麻煩,導入只需要寫源文件,.h可以不寫
add_executable(Demo2 main.c hello.c)
# 查找當前目錄所有源文件 并將名稱保存到 DIR_SRCS 變量
# 注意這樣不能查找子目錄,也不會自動往子目錄找
aux_source_directory(. DIR_SRCS)
# 或者也可以使用如果通配符的方式,引入所有.c文件,同樣保存到 DIR_SRCS變量中
file(GLOB DIR_SRCS *.c)

# $() 為引用變量DIR_SRCS的值
add_executable(Demo2 ${DIR_SRCS})
Demo3 多文件多目錄

新建文件夾helloDir,把Demo2中的hello.c、hello.h和新建一個新的CMakeLists.txt文件放到該目錄下

main.c文件

// 記得修改頭文件的引用路徑
#include "helloDir/hello.h"

int main()
{
    hello("world");
    return 0;
}

CMakeLists.txt

# CMake 最低版本號要求
cmake_minimum_required (VERSION 3.4.1)
# 項目信息
project (MyProject3)
# 添加當前目錄下的所有源文件
aux_source_directory(. DIR_SRCS)

# 添加 helloDir 子目錄下的cmakelist
add_subdirectory(helloDir)
# 指定生成目標
add_executable(Demo3 ${DIR_SRCS})
# 添加鏈接庫,hello為helloDir子目錄的生成的鏈接庫名稱
target_link_libraries(Demo3 hello)

helloDir/CMakeLists.txt

# CMake 最低版本號要求
cmake_minimum_required (VERSION 3.4.1)
# 指定生成目標
aux_source_directory(. DIR_LIB_SRCS)
# 生成鏈接庫,默認編譯為靜態庫
# add_library (hello ${DIR_LIB_SRCS})

# 指定編譯為靜態庫,與上面的一樣
add_library (hello STATIC ${DIR_LIB_SRCS})
# 指定編譯為動態庫
add_library (hello SHARED ${DIR_LIB_SRCS})

add_library (hello STATIC ${DIR_LIB_SRCS})

NDK項目之CMake

通過Android Stuido創建支持NDK的項目,就可以看到,多了一個CMakeList.txt文件。在Android Studio 2.2及其以上,構建原生庫的默認工具就是CMake。

# 設置cmake最低支持版本
cmake_minimum_required(VERSION 3.4.1)
# 編譯library庫
add_library( 
             # 設置編譯后的library名稱
             native-lib
             # 設置library模式,STATIC為靜態庫,如果想編譯為動態庫,則修改為SHARED
             STATIC  
             # 設置編譯使用到的源代碼,如果添加了源代碼,都需要修改此處添加源代碼的路徑
             src/main/cpp/native-lib.cpp )

# 查找library,因為NDK中已經有一部分預構建庫,已經被配置為cmake搜索路徑的一部分,所以,可以直接添加庫的名稱。
find_library(
             # 此處為查找名稱為log的庫,將絕對路徑賦值到變量log-lib
             log-lib
             log )

# 鏈接library
target_link_libraries(
                       # 鏈接自己編寫的native-lib庫和上面查找到的lib庫
                       # 位置不能更改,native-lib為目標的庫,必須放在前面
                       native-lib
                       ${log-lib} )
                       
# 如果上面不預先使用find_library的話,也可以直接使用
target_link_libraries( native-lib 
                       log)

同時build.gradle文件也多了一些配置

android {
    compileSdkVersion 28
    defaultConfig {
        externalNativeBuild {
            // 主要是配置了cmake的命令參數
            cmake {
                cppFlags ""
                // 設置編譯c/c++ 源文件的cpu類型
                // 如果不設置,連接不同的設備,gralde會自動根據連接的設備,如armeabi-v7a架構的手機,則編譯出armeabi-v7a,如模擬器,則編譯出x86。
                // 由于考慮編譯出來后包的大小,一般實際開發中,都只是編譯兼容性最多的armeabi-v7a架構
                abiFilters 'armeabi-v7a'
            }
        }
    }
    externalNativeBuild {
        // 主要定義了CMake的構建腳本CMakeLists.txt的路徑
        cmake {
            // 當前的CMakeLists.txt與build.gradle在同一個目錄下
            // CMakeLists.txt也可以在其他路徑下,比如可以在cpp目錄下創建一個CMakeList.txt配置cpp目錄下的源文件
            path "CMakeLists.txt"
        }
    }
}

添加預編譯庫

在上一篇文章中,分別對怎樣編譯Android平臺上的靜、動態庫進行介紹,接下來,就把其編譯好的庫(即預編譯庫)放到Android Stuido項目中

引入靜態庫

引入libmain.a的靜態庫,在新建armeabi-v7a在cpp文件下,把libmain.a放入其中。

cmake_minimum_required(VERSION 3.6)

add_library( native-lib
             SHARED
             src/main/cpp/native-lib.cpp )

# hello-jni: 變量名 最終會生成的so名字
# SHARED: 動態庫  STATIC:靜態庫
add_library(hello-jni SHARED hello-jni.c)

# 引入預編譯庫
# Main:引入的庫的名字,可以隨便定義,但是要和下面的保存一致
# STATIC:聲明導入的是動態庫或者靜態庫,STATIC為靜態庫
# IMPORTED:表示這個庫是以導入的方式導入
add_library(Main STATIC IMPORTED)

# 設置導入的路徑
# Main:庫的名稱
# 設置目標屬性 導入路徑 當前庫的路徑
# ${ANDROID_ABI}引用的變量,在上面的build.grale中,我們設置abiFilters為armeabi-v7a
set_target_properties(Main PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/cpp/${ANDROID_ABI}/libmain.a)

find_library( log-lib
              log )
              
target_link_libraries(hello-jni Main)

修改native-lib.cpp文件,修改后如下

#include <jni.h>
#include <string>
#include <android/log.h>
// 使用上面的log庫

// 該文件為c++,庫為c,因此需要添加extern "C"
extern "C" int main();

extern "C" JNIEXPORT jstring
JNICALL
Java_cn_guidongyuan_studyndk_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    // 如果輸出結果與庫中main定義的返回值一致,則表示調用成功
    __android_log_print(ANDROID_LOG_ERROR,"Log","%d", main());
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

引入動態庫

引入libTest.so動態庫,動態庫必須放在src/jniLibs/armeabi-v7a(不同CPU架構不同目錄)下,否則不會打包進去

cmake_minimum_required(VERSION 3.6)

add_library( native-lib
             SHARED
             src/main/cpp/native-lib.cpp )
             
find_library( log-lib
              log )

# 設置庫的查找路徑
# set方法 定義一個變量,此處為在原來的CMAKE_CXX_FLAGS變量上,添加-L加庫的查找路徑
# 因為項目中的native-lib.cpp為C++,因此存在C++則使用CMAKE_CXX_FLAGS,完全沒有就使用CMAKE_C_FLAGS
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI} ")

# 鏈接動態庫的名字,庫名詞為libmain.so,這里必須使用main,就會結合上面設置的庫查找路徑以及庫名稱查找到對應的庫
target_link_libraries( native-lib
                       ${log-lib}
                       main)

message("ANDROID_ABI : ${ANDROID_ABI}")
# set 方法 定義一個變量
#CMAKE_C_FLAGS = "${CMAKE_C_FLAGS} XXXX"
# -L: 庫的查找路徑 libTest.so
# 如果庫用c寫的,則用CMAKE_C_FLAGS,如果使用c++或者c++和c混合使用,則用CMAKE_CXX_FLAGS
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI} ")

find_library(log-lib log)
# 此處的Test名字,必須為動態庫的名稱,查找路徑在上面的set設置了
target_link_libraries(hello-jni Test ${log-lib} )

修改native-lib.cpp文件,修改內容與上面靜態庫一樣

參考資料

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372