NDK編譯腳本:Android.mk or CMakeLists.txt

Android NDK以前默認使用Android.mk與Application.mk進行構建,但是在Android Studio2.2之後推薦使用CMake進行編譯。CMake是跨平臺編譯工具,全稱爲cross platform make,內建c、c++、java自動相依性分析功能。NDK通過工具鏈支持CMake,工具鏈文件是用於自定義交叉編譯工具鏈的CMake文件。用於NDK的工具鏈位於<NDK>/build/cmake/android.toolchain.cmake,關於CMake更多詳情請參考官網:https://cmake.org/documentation/。下面我們對比下Android.mk與CMakeLists.txt的語法。

一、Android.mk語法

以動態庫編譯hello模塊爲例,完整腳本如下:

WORKING_DIR := $(call my-dir)
LOCAL_PATH := $(WORKING_DIR)

include $(CLEAR_VARS)
LOCAL_ARM_MODE  := arm
LOCAL_MODULE    := libffmpeg
LOCAL_SRC_FILES := $(LOCAL_PATH)/ffmpeg/$(TARGET_ARCH_ABI)/lib$(LOCAL_MODULE).so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/ffmpeg/include
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_ARM_MODE  := arm
#模塊名稱
LOCAL_MODULE    := hello
#源文件
LOCAL_SRC_FILES := hello.c
#頭文件路徑
LOCAL_C_INCLUDES := $(LOCAL_PATH)
#系統庫依賴
LOCAL_LDLIBS    := -llog -lz -lm -landroid
#第三方動態庫
LOCAL_SHARED_LIBRARIES := libffmpeg
#以動態庫形式編譯
include $(BUILD_SHARED_LIBRARY)

1、LOCAL_MODULE

聲明模塊名稱,例如這裏編譯出來的動態庫名稱爲libhello.so。

2、LOCAL_SRC_FILES

聲明源文件列表,文件之間用空格分開,需要換行時使用'\'換行符。

3、LOCAL_C_INCLUDES

聲明頭文件路徑,例如$(LOCAL_PATH)/xxx

4、LOCAL_CPP_EXTENSION

指定C++源文件除.cpp以外的文件擴展名,例如這樣LOCAL_CPP_EXTENSION := .cpp .cxx .cc 

5、LOCAL_CPP_FEATURES

指定依賴c++的某些功能,例如RTTI(運行時類型信息):

LOCAL_CPP_FEATURES := rtti

使用c++異常檢測: 

LOCAL_CPP_FEATURES := exceptions

6、LOCAL_CFLAGS

在編譯c和c++源文件時編譯系統要傳遞的編譯器標記,即指定額外的宏定義或編譯選項。 

LOCAL_CFLAGS += -I<path>

 7、LOCAL_STATIC_LIBRARIES

共享靜態庫,作爲第三方庫被引用

LOCAL_STATIC_LIBRARIES := libavcodec libavutil libavformat libavfilter

8、LOCAL_SHARED_LIBRARIES

共享動態庫,與共享靜態庫一樣作爲第三方庫被引用

LOCAL_SHARED_LIBRARIES := libffmpeg

9、LOCAL_LDLIBS

額外鏈接器,一般爲系統庫,使用-l來引用

LOCAL_LDLIBS := -lz -lm

10、 LOCAL_ARM_MODE

ndk默認使用thumb模式來生成目標二進制文件,每條指令爲16位寬。也可以指定爲ARM模式,來生成32位ARM的目標文件:

LOCAL_ARM_MODE := arm

11、LOCAL_ARM_NEON

用於開啓NEON指令加速,僅對armeabi-v7a平臺有效。爲模塊開啓NEON:

LOCAL_ARM_NEON := true

爲單獨源文件開啓NEON:

LOCAL_SRC_FILES := hello.c.neon

12、TARGET_ARCH

用於指向CPU架構,包括x86、x86_64、armeabi-v7a、 arm64-v8a

13、TARGET_PLATFORM

目標平臺,對應Android API級別號,例如Android5.0系統鏡像對應Android API級別21:android-21

14、打印信息

可用warning、debug、info、error級別來打印信息,如果是打印error信息,會終止編譯。以warning爲例:

$(warning 'This is a test')

15、if條件判斷

採用ifeq關鍵字,然後左右變量放在括號體內,用逗號分隔:

ifeq($(TARGET_ABI), arm64-v8a)
$(debug 'This is arm64-v8a')
endif

 二、Application.mk語法

Android.mk依賴Application.mk文件進行編譯,一般Application.mk腳本如下所示:

APP_STL      := c++_static
APP_DEBUG    := false
APP_OPTIM    := release
APP_CPPFLAGS := -frtti
APP_PLATFORM := android-16
APP_ABI      := armeabi-v7a arm64-v8a

1、APP_ABI

與Android.mk的TARGET_ABI對應,包括CPU架構有:x86、x86_64、armeabi-v7a、arm64-v8a,支持所有平臺這樣表示:

APP_ABI := all

2、APP_BUILD_SCRIPT

指向編譯腳本的路徑,一般Android.mk和Application.mk都位於jni目錄,默認指向jni/Android.mk路徑,如果是其他路徑,需要使用此變量來指定絕對路徑:

APP_BUILD_SCRIPT := /xx/xx/Android.mk

3、APP_OPTIM

編譯優化選項,調試模式爲debug,發佈模式爲release。在調試模式下,會保留symbol符號表;在發佈模式下,會開啓優化,去掉symbol符號表。

4、APP_PLATFORM

指定編譯平臺,面向於Android API級別,對應gradle聲明的minSdkVersion。如果不聲明,默認爲ndk支持的最低API版本

5、APP_STL

聲明使用c++的標準庫,默認爲system STL。其他選項包括c++_static、c++_shared和none

三、CMakeLists.txt語法

以編譯hello模塊以及依賴ffmpeg模塊爲例:

cmake_minimum_required(VERSION 3.4.1)
#添加動態庫,包含源文件路徑
add_library( hello
             SHARED
             src/main/jni/hello.c)
#添加第三方動態庫
add_library( ffmpeg
             SHARED
             IMPORTED )
#指定第三方庫路徑
set_target_properties( ffmpeg
                       PROPERTIES IMPORTED_LOCATION
                       ../../../../libs/${CMAKE_ANDROID_ARCH_ABI}/libffmpeg.so )

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
#指定頭文件路徑
include_directories(src/main/cpp)
if(${CMAKE_ANDROID_ARCH_ABI} MATCHES "armeabi-v7a")
    include_directories(src/main/cpp/include/armeabi-v7a)
    message("This is armeabi-v7a")
elseif(${CMAKE_ANDROID_ARCH_ABI} MATCHES "arm64-v8a")
    include_directories(src/main/cpp/include/arm64-v8a)
    message("This is arm64-v8a")
endif()
#查找系統庫
find_library( # Sets the name of the path variable.
              log-lib
              log )
#鏈接目標庫
target_link_libraries( hello
                       #ffmpeg
                       ${log-lib} )

1、add_library

傳遞三個參數,第一個參數是模塊名稱,第二個參數是SHARED或者STATIC。如果是源文件模塊,第三個參數是源文件列表;如果是第三方庫,第三個參數是IMPORTED。

2、set_target_properties

用於指定第三方庫路徑,IMPORT_LOCATION一般是指向src/main/cpp目錄

3、include_directories

用於指定頭文件路徑,頭文件路徑可以有多個

4、find_library

用於查找系統庫,比如Android系統的log日誌庫

5、target_link_libraries

鏈接目標庫,把依賴庫都鏈接到目標庫中

6、if條件判斷

與Android.mk稍有差異,CMake採用if...MATCHES形式,例如:

if(${CMAKE_ANDROID_ARCH_ABI} MATCHES "armeabi-v7a")
......
endif()

7、打印日誌

與Android.mk不同的是,CMake採用message函數來打印日誌,括號體傳入msg內容

message("hello, cmake")

8、命令行參數

命令行參數前面統一加上-D,常用的參數:

1) -DANRDOID_ABI :android的ABI架構平臺

2) -DANDROID_NDK :ndk路徑

3) -DANDROID_ARM_MODE :arm模式/thumb模式

4) -DANDROID_ARM_NEON :是否開啓arm neon加速,針對armeabi-v7a平臺

5) -DANDROID_TOOLCHAIN :編譯工具鏈

6) -DANDROID_NATIVE_API_LEVEL :與ANDROID_PLATFORM相同,對應minSdkVersion

7) -DCMAKE_BUILD_TYPE :編譯類型,debug或release

8) -DCMAKE_MAKE_PROGRAM :編譯程序

9) -DCMAKE_TOOLCHAIN_FILE :編譯文件

9、命令行編譯

以cmake作爲關鍵字,後面帶着指定參數,示例如下:

    cmake \
    -DANDROID_ABI=armeabi-v7a \
    -DANDROID_NDK=${HOME}/Android/Sdk/ndk-bundle \
    -DCMAKE_BUILD_TYPE=Debug \
    -DCMAKE_MAKE_PROGRAM=${HOME}/Android/Sdk/cmake/3.6.3155560/bin/ninja \
    -DCMAKE_TOOLCHAIN_FILE=${HOME}/Android/Sdk/ndk-bundle/build/cmake/android.toolchain.cmake \
    -DANDROID_NATIVE_API_LEVEL=23 \
    -DANDROID_TOOLCHAIN=clang

四、ndk編譯配置

1、Android.mk方式配置

在gradle的defaultConfig配置ndk:

defaultConfig {
    ......
    ndk {
        moduleName "hello"
        abiFilters "armeabi-v7a", "arm64-v8a"
    }
}

然後配置jni源文件路徑:

sourceSets {
    main {
        jniLibs.srcDir 'src/main/libs' // Enable to use libs
        jni.srcDirs 'src/main/jni' // Enable the automatic ndk-build
    }
}

 另外配置Android.mk文件絕對路徑:

externalNativeBuild {
    ndkBuild {
        "src/main/jni/Android.mk"
    }
}

2、CMake方式配置

前兩步與Android.mk方式一樣,配置腳本路徑稍有差異:

externalNativeBuild {
    cmake {
        path "CMakeLists.txt"
    }
}

另外在defaultConfig設置cppFlags:

externalNativeBuild {
    cmake {
        cppFlags ""
    }
}

五、ndk編譯過程

1、ndk-build編譯

如上圖所示,在命令行輸入ndk-build後,會根據聲明所支持的平臺依次編譯。首先是armeabi-v7a平臺架構,把hello.c源文件編譯成hello目標文件,然後鏈接成libhello.so動態庫,最終安裝到libs/armeabi-v7a目錄下。

2、cmake在Gradle中編譯

 如上圖所示,編譯arm64-v8a平臺架構的hello模塊。首先把hello.c源文件編譯成hello.c.o目標文件,然後鏈接成libhello.so動態庫。生成的debug模式動態庫在/build/intermediates/cmake/debug/obj/arm64-v8a目錄下。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章