承接上篇Android游戲開發實踐(1)之NDK與JNI開發01分享完JNI的基礎和簡要開發流程之后,再來分享下在Android環境下的JNI的開發,以及涉及到的NDK相關的操作。當然,本篇仍是以Eclipse作為開發IDE,雖然Google官方已經不再支持Eclipse了,推薦是用AndroidStudio進行開發。但對于游戲開發來說,IDE的影響并沒有那么大,且從Eclipse那個時代過來的,對Eclipse還是感情很深的。后續,還有專門一篇來分享下AndroidStudio的使用以及使用CMake編譯等,會提到JNI這方面的內容。

按照慣例,每一篇文章都喜歡附上官方的文檔。因為,只有官方的文檔才是最準確,最實時,且內容最豐富的。那么,NDK官方開發地址為:

Getting Started with the NDK:
https://developer.android.com/ndk/guides/index.html

本文目錄如下:

1、NDK環境搭建

(1)安裝CDT

CDT全成是C/C++ DevelopmentTools,安裝該插件使得Eclipse也得支持C/C++的開發。須下載和Eclipse版本對應的CDT插件。是喜歡Eclipse的便捷,同時又開發C/C++的必裝插件。CDT的下載地址為:
http://www.eclipse.org/cdt/downloads.php

安裝成功后,在Eclipse的Window-Preferences中看到多了一項C/C++的支持:

(2)NDK的下載
目前,NDK的最新版本為android-ndk-r13b,下載地址為:
https://developer.android.com/ndk/downloads/index.html
這里需要說明下,為了方便演示筆者所使用的NDK版本為android-ndk-r8b。最新版本已經不再支持GCC編譯,默認改用Clang。還修復了相關的bug,建議線上的產品更新最新的穩定版。

(3)NDK的集成
將下載好的NDK解壓,并將該路徑添加到Path環境變量中,然后集成至Eclipse中。如圖:

2、交叉編譯

NDK編譯的環境有很多,但基本都是通過ndk-build工具來完成的。有直接通過Eclipse安裝CDT即可完編譯,也可以通過安裝Cygwin來編譯。事實上,在android-ndk-r7之后的版本,已經不需要安裝Cygwin就可以編譯出.so了。但這里還是想介紹下,因為筆者開發曾用Cygwin編譯過一段時間,而且多了解一種編譯途徑也沒什么壞處。當然,熟悉Linux平臺或者Mac平臺開發的朋友會感覺更親切些。

2.1 Cygwin編譯

Cygwin是一套在Window上模擬類Unix系統環境的工具。而Android底層又是基于Linux的。因此,對Linux環境下的開發支持也更好。只要在Cygwin中安裝gccg++gdbmakeGUN工具集即可。
(1)Cygwin的安裝
Cygwin的下載地址為:
https://cygwin.com/install.html

(2)Cygwin的安裝步驟:
下載完setup-x86_64.exe,直接下一步:

這里有三個選項,分別是從網絡安裝,只下載不安裝,從本地目錄安裝(如果,之前安裝過)。可根據自己的實際情況選擇。這里選擇從網絡安裝。然后,下一步

這里選擇第一項,Direct Connection。然后,下一步

這里下載地址選擇mirrors.kernel.org即可。也可選擇國內163的鏡像地址。

這里選擇要安裝的包(autoconfautomakemakegccg++gdbpcregawk等)。這里偷懶就直接把AdminDebugDevelDocEditorsShells,當然還有Python。然后,下一步

接著經過漫長的等待,大概下載3,4G的文件。

安裝完,運行Cygwin,輸入如下命令

(3)在Cygwin下配置NDK環境變量
在當前當前用戶目錄下運行,(例如,我的目錄為D:\env\cygwin\home\John):

配置NDK環境變量,注意別覆蓋原來的PATH環境變量。

(4)驗證NDK配置
嘗試在Cygwin下用ndk-build來編譯NDK下的hello-jni的samples。如圖:

可以看到正確編譯出libhello-jni.so庫(在項目目錄下的libs,不同cup架構命名的文件夾里)。如遇到各種權限錯誤,請將samples下的hello-jni項目的權限修改為可寫入、可修改等。

2.2 Eclipse編譯

(1)將hello-jni的項目導入到Eclipse中。


(2)給hello-jni添加builder,來編譯C/C++代碼。
右鍵HelloJni項目,選擇Properties,然后,選擇Builders,點擊New新建一個Builder。選擇Program,點擊OK即可。

分別填寫Builder名稱。找到Cygwin的Shell程序和工作目錄。將要編譯的項目目錄和執行的命令當成參數參數Shell命令執行。

注意: cd/cygdrive/d/android-ndk-r8b/samples/hello-jni中間有個空格。
ANDROID_NDK_ROOT:是在Cygwin中配置NDK環境變量的名稱。

通過Cygwin中輸入bash --login -h可以獲取更詳細的信息:

Your group is currently "mkpasswd".  This indicates that your gid is not in /etc/group and your uid is not in /etc/passwd.  The /etc/passwd (and possibly /etc/group) files should be rebuilt. See the man pages for mkpasswd and mkgroup then, for example, run  mkpasswd -l [-d] >> /etc/passwd mkgroup  -l [-d] >> /etc/group

如果遇到這種,按照提示在Cygwin終端執行,mkpasswd -l [-d] >> /etc/passwdmkgroup -l [-d] >> /etc/group命令即可。

(3)將配置JNI_Builder優先級設為最高

(4)編譯HelloJni工程。
選中HelloJni工程,在Eclipse中選擇Project-clean。這樣,Eclipse便可自動編譯HelloJni工程了。

2.3 AndroidStudio和CMake編譯

這里就不花篇幅介紹這相關的內容,下一篇專門介紹下AndroidStudio的使用及在AndroidStudio下NDK的開發。希望能給從其它IDE遷移到IntelliJ IDEA系開發或許剛接觸AndroidStudio一些啟發。所以,這里先留個伏筆。

ndk-build、Android.mk與Application.mk

雖然,已經成功的將samples下的hell-jni項目成功編譯出了.so動態庫。在整個交叉編譯過程中,涉及到了三個比較重要的文件,分別是ndk-buildAndroid.mkApplication.mk,所以,有必要了解一下,這三個文件在整個交叉編譯過程中起了什么作用。

3、ndk-build

首先,ndk-build是一個shell腳本,目標是幫助你正確的調用NDK的構建腳本。ndk-build<ndk-root-path>(NDK安裝目錄根路徑下)有個ndk-build的shell腳本文件,或ndk-build.cmd的文件。

ndk-build的官方指南為:

https://developer.android.com/ndk/guides/ndk-build.html

3.1 ndk-build用法
cd $PROJECT_PATH $ <ndk>/ndk-build

用法:在項目的根目錄下,執行ndk-build腳本命令。

例如:

進到hello-jni的項目根目錄執行ndk-build,ANDROID_NDK_ROOT是在Cygwin中配置NDK環境變量的名稱。

$ <ndk>/ndk-build -C <PROJECT_PATH>

用法:在任意目錄下執行ndk-build,用-C來指定要編譯的項目的目錄。

例如:

實際上:執行ndk-build相當于執行了以下命令:

$GNUMAKE -f <ndk>/build/core/build-local.mk <parameters>

例如:

3.2 ndk-build可選參數
$ ndk-build clean 清除編譯生成的二進制文件。  $ ndk-build -C <project> 指定項目路徑  $ ndk-build NDK_DEBUG=0 NDK_DEBUG為0是編譯為release版,為debug版。

更多的ndk-build的參數介紹,請參考上面貼出的ndk-build的官方指南。

4、Android.mk

Android.mk是用來向編譯系統指定項目中C/C++源代碼文件編譯、鏈接規則的一種描述文件。它是GUN makefile文件的一小部分。那么,簡單來說,就是用來起指定編譯引用的頭文件目錄、編譯出的so的名字、需要編譯的源文件或庫等作用。熟悉makefile語法的,肯定熟悉這種用法。Android.mk文件在$project-path/jni/Android.mk路徑下。

Android.mk官方說明文檔地址為:
https://developer.android.com/ndk/guides/android_mk.html

4.1 Android.mk初級

首先,仍以hello-jni為例,看看都定義了哪些內容。打開<ndk-path>/samples/hello-jni/jni/Android.mk文件。

LOCAL_PATH := $(call my-dir)  include $(CLEAR_VARS)  LOCAL_MODULE    := hello-jni LOCAL_SRC_FILES := hello-jni.c  include $(BUILD_SHARED_LIBRARY)

說明:
LOCAL_PATH := $(call my-dir)
Android.mk必須首先定義LOCAL_PATH,它用來在開發的樹文件夾中定位文件的。my-dir是由編譯系統提供的宏,用來返回當前目錄的路徑。(注意:這個路徑是包含了Android.mk的路徑)

include $(CLEAR_VARS)
CLEAR_VARS變量也是由編譯系統提供的,include $(CLEAR_VARS)是引用一個特殊的GUN makefile文件,這個makefile文件所做的就是清除定義了很多LOCAL_XXX(例如: LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_STATIC_LIBRARIES等)這種格式的變量,但這里不會清除LOCAL_PATH變量。這么做是很有必要的,因為編譯系統解析這些編譯控制文件都是在單一的GUN make上下文環境中,解析出來的LOCAL_XXX變量都是全局的。

LOCAL_MODULE := hello-jni
LOCAL_MODULE必須是唯一的,且不能包含空格。編譯系統會根據這個名字生成相應的共享庫,并自動添加前綴和后綴。本例中,最終生成的共享庫的名稱為libhello-jni.so

注意:編譯生成的共享庫都是lib開頭的,如果,你聲明的名稱已經包含lib(libhello-jni),那么,最終生成的共享庫名稱就不添加lib前綴了,生成的仍為libhello-jni.so

LOCAL_SRC_FILES := hello-jni.c
LOCAL_SRC_FILES用來指明要編進共享庫(.so)的C/C++源文件的列表。

注意:這里只需要指定要編譯的.c或者.cpp等源文件即可,不需要指定.h頭文件。

include $(BUILD_SHARED_LIBRARY)
BUILD_SHARED_LIBRARY變量是由系統提供,include $(BUILD_SHARED_LIBRARY)會引用一個Gun makefile腳本,用來收集你定義的LOCAL_XXX格式的變量。同時,決定哪些要編譯,如何編譯等。

注意:顯然BUILD_SHARED_LIBRARY是用來編譯出共享庫.so文件的,同理,也可以使用BUILD_STATIC_LIBRARY來編譯出靜態庫.a文件。

以上便是編寫一個簡單的Android.mk文件的所有元素。通過上面的描述發現,如果我們要編寫一個自己的Android.mk文件,沒有特殊需求的話,可以直接將hello-jni工程中的Android.mk文件拷貝,然后,修改庫的名稱(LOCAL_MODULE)和要編譯的源文件列表(LOCAL_SRC_FILES)變量即可。

4.2 Android.mk高級

這里來詳細了解下Android.mk其他的一些變量及語法規則。

(1)NDK變量與宏
Android.mk中還有一些其他變量,是作為NDK編譯系統的保留變量,你只能依賴它或者定義它。這些變量的規則如下:

  • LOCAL_開頭的變量名稱(如:LOCAL_MODULELOCAL_PATH等)
  • PRIVATE_NDK_APP開頭的變量名稱(編譯系統內部使用)
  • 小寫的名稱(例如:my-dir,同樣,也是作為內部使用)

如果你需要在Android.mk中定義自己的變量,推薦用MY_作為前綴。

(2)NDK定義的變量
CLEAR_VARS
上面已經介紹過了,這里就不在贅述。記住一點,在定義LOCAL_XXX前,必須引用這個腳本。用法:

include $(CLEAR_VARS)

BUILD_SHARED_LIBRARY
該變量指向了一個腳本,這個腳本會收集你在每個模塊定義的LOCAL_XXX變量信息,并且這個變量還確定了怎樣使用你的源碼去編譯一個共享庫。注意,使用這個變量需要你至少已經定義了LOCAL_MODULELOCAL_SRC_FILES。該變量會使編譯系統生成一個以.so結尾的庫。用法:

include $(BUILD_SHARED_LIBRARY)

BUILD_STATIC_LIBRARY
該變量是BUILD_SHARED_LIBRARY的一個變體,是用來生成一個靜態庫。構建系統并不會把靜態庫包含進你的工程里面,但是可以利用靜態庫生成共享庫。該變量會使編譯系統生成一個以.a結尾的庫。

include $(BUILD_STATIC_LIBRARY)

PREBUILT_SHARED_LIBRARY
指向一個腳本,這個腳本被用來指定一個預構建的共享庫。與BUILD_SHARED_LIBRARYBUILD_STATIC_LIBRARY不同,LOCAL_SRC_FILES的值不能是一個源文件,它必須是一個單獨的指向預構建的共享庫的路徑,例如:foo/libfoo.so。用法:

PREBUILT_STATIC_LIBRARY
該變量與PREBUILT_SHARED_LIBRARY相同,只是指向的一個預構建的靜態庫。

TARGET_ARCH
這個變量是目標CPU架構的名字,就像Android Open Source Project里面指定了目標CPU架構。這個變量用于任意的ARM兼容的構建,或者ARM,或者是獨立于CPU結構的修訂,或者ABI。

TARGET_PLATFORM
編譯到目標平臺的api等級。例如,Android5.1對應的是Android api22。用法:

TARGET_PLATFORM := android-22

TARGET_ARCH_ABI
當編譯系統解析Android.mk文件的時候,這個變量存儲CPU和架構的名字。你可以指定一個或者多個下面列出的名字,使用空格分隔兩個名字

用法:

TARGET_ARCH_ABI := arm64-v8a

注意:android-ndk-1.6_r1之前這個這個變量被定義為arm。

TARGET_ABI
該變量將API的級別和ABI聯系在一起,當你在真機上調試系統的時候特別有用。用法:

TARGET_ABI := android-22-arm64-v8a

LOCAL_MODULE_FILENAME
這是一個可選變量。允許你重新指定一個變量的名稱來覆蓋默認生成的名稱。例如:強制生成libnewfoo.so

LOCAL_MODULE := foo LOCAL_MODULE_FILENAME := libnewfoo

LOCAL_MODULE_FILENAME不需要指定文件路徑或擴展名

LOCAL_SRC_FILES:
該變量用來指定要編譯的源文件列表。這里推薦使用相對路徑。用法:

LOCAL_SRC_FILES := foo1.c \ ../Module2/foo2.c

注意:使用Unix風格的/,多個文件使用\換行,注意\后面沒有空格。

LOCAL_CPP_EXTENSION
同樣,LOCAL_CPP_EXTENSION也是一個可選變量。用來指定C++源文件的擴展名。默認是.cpp。從NDK r7版本后,可以指定一系列的擴展名。用法:

LOCAL_CPP_EXTENSION := .cxx .cpp .cc

LOCAL_CPP_FEATURES
同樣,LOCAL_CPP_FEATURES也是一個可選變量。如果,你用到了C++的一些特殊功能(例如:RTTI,異常支持等),并且正確的編譯和鏈接,可以使用該變量來聲明。用法:

LOCAL_CPP_FEATURES := rtti exceptions

建議使用該變量來代替LOCAL_CPPFLAGS中直接定義-frtti-fexceptions這種用法

LOCAL_C_INCLUDES
可選變量,可以通過該變量來指定一個相對于NDK根目錄的路徑列表,并在編譯C/C++時添加到搜索路徑中。用法:

LOCAL_C_INCLUDES := $(LOCAL_PATH)//foo

注意:該聲明要放在LOCAL_CFLAGSLOCAL_CPPFLAGS的聲明前面。

LOCAL_CFLAGS
可選變量,在編譯C/C++源代碼時,能給編譯器傳遞一個編譯標志集合。用來指定附加宏的定義和編譯選項是很有用的。

LOCAL_CPPFLAGS
可選變量,在編譯C++源代碼時(只編譯C++),能給編譯器傳遞一個編譯標志集合。

LOCAL_STATIC_LIBRARIES:
該變量定義了本模塊編譯鏈接過程中用到的靜態庫列表。

LOCAL_SHARED_LIBRARIES
該變量定義了本模塊編譯鏈接過程中用到的共享庫列表。

LOCAL_WHOLE_STATIC_LIBRARIES
該變量跟LOCAL_STATIC_LIBRARIES類似,不同的是在編譯鏈接過程中會載入靜態庫的所有源代碼目標文件。這在解決幾個庫之間循環引用時,非常有用。可以通過GUN的--whole-archive標志來查看相關說明。

LOCAL_LDLIBS
該變量用來定義本模塊編譯時用到的附加的鏈接器選項。用-l前綴指定。例如:要鏈接/system/lib/libz.so

LOCAL_LDLIBS := -lz

注意:如果,你編譯一個靜態庫是定義了該變量,編譯系統會忽略它,并且ndk-build會打印一個警告。

LOCAL_LDFLAGS
該變量定義了在編譯給編譯系統傳遞一些其他的鏈接標志。用法:

LOCAL_LDFLAGS += -fuse-ld=bfd

注意:如果,你編譯一個靜態庫是定義了該變量,編譯系統會忽略它,并且ndk-build會打印一個警告。

LOCAL_ALLOW_UNDEFINED_SYMBOLS
默認情況下,在編譯一個共享庫的時候,任何未定義的引用,將會拋出"符號未定義(undefined symbol)"的錯誤。該變量能幫助你捕捉代碼中的bug。如果,要禁用這項檢查可以把該變量設置為true。這么設置

注意:如果,你編譯一個靜態庫是定義了該變量,編譯系統會忽略它,并且ndk-build會打印一個警告。

LOCAL_ARM_MODE
默認情況下,編譯系統會在ARM平臺"thumb"模式下生成16位的二進制文件。定義該變量則強制生成32位"arm"模式下的對象文件。例如:

LOCAL_ARM_MODE := arm

你也可以加上.arm后綴來告訴編譯系統,只想對某個源文件使用arm指令。例如:

LOCAL_SRC_FILES := foo.c bar.c.arm

LOCAL_ARM_NEON
只有當目標平臺為armeabi-v7a指令集時,才定義它。它允許你的C/C++代碼中使用ARM高級指令。也可以在匯編文件中使用NEON指令。你可以使用.neon后綴來指定編譯器支持NEON指令編譯。例如:

LOCAL_SRC_FILES = foo.c.neon bar.c zoo.c.arm.neon

注意:不是所有的ARMv7架構的CPU都支持NEON擴展。

LOCAL_DISABLE_NO_EXECUTE
Android NDK r4版本開始支持這種"NX bit"的安全功能。默認是啟用的,你也可以設置該變量的值為true來禁用它。但不推薦這么做。該功能不會修改ABI,只在ARMv6+CPU的設備內核上啟用。

LOCAL_DISABLE_RELRO
默認情況下,NDK編譯代碼是只讀重定位和GOT保護的。這個會指示運行時鏈接器標記特定的內存區是只讀的,在移動位置之后。這樣會使得某些安全漏洞(如GOT覆蓋)更難執行。默認是啟用的,你也可以把該變量的值設為true來禁用。但不推薦這么做。

LOCAL_DISABLE_FORMAT_STRING_CHECKS
默認情況下,編譯系統編譯代碼時會檢查格式化字符串,如果printf樣式的函數使用了非常嚴格的字符串,那么編譯出錯。默認是開啟的,你也可以通過設置該變量的值為true來禁用。但不推薦這么做。

LOCAL_EXPORT_CFLAGS
該變量是用來記錄C/C++編譯器標志集合,這些編譯器標志會被添加到通過變量LOCAL_STATIC_LIBRARIESLOCAL_SHARED_LIBRARIES使用本模塊的其他模塊的LOCAL_CFLAGS變量中。例如:

include $(CLEAR_VARS) LOCAL_MODULE := foo LOCAL_SRC_FILES := foo/foo.c LOCAL_EXPORT_CFLAGS := -DFOO=1 include $(BUILD_STATIC_LIBRARY)   include $(CLEAR_VARS) LOCAL_MODULE := bar LOCAL_SRC_FILES := bar.c LOCAL_CFLAGS := -DBAR=2 LOCAL_STATIC_LIBRARIES := foo include $(BUILD_SHARED_LIBRARY)

編譯bar模塊時,"-DFOO=1 -DBAR=2"標志將一起傳遞給編譯器。

LOCAL_EXPORT_CPPFLAGS
該變量與LOCAL_EXPORT_CFLAGS類似,但只適用于C++。

LOCAL_EXPORT_C_INCLUDES
該變量與LOCAL_EXPORT_CFLAGS類似,但是該變量用來包含路徑的。例如,上例中bar.c需要包含foo模塊的頭文件。

LOCAL_EXPORT_LDFLAGS:
該變量與LOCAL_EXPORT_CFLAGS類似,但是它用作鏈接器標志。

LOCAL_EXPORT_LDLIBS
該變量與LOCAL_EXPORT_CFLAGS一樣,該變量的值將會被添加到其它模塊引用到本模塊的其它模塊的LOCAL_LDLIBS變量中。例如:

include $(CLEAR_VARS) LOCAL_MODULE := foo LOCAL_SRC_FILES := foo/foo.c LOCAL_EXPORT_LDLIBS := -llog include $(BUILD_STATIC_LIBRARY)  include $(CLEAR_VARS) LOCAL_MODULE := bar LOCAL_SRC_FILES := bar.c LOCAL_STATIC_LIBRARIES := foo include $(BUILD_SHARED_LIBRARY)

編譯bar的時候,會在鏈接log的系統日志庫。

LOCAL_SHORT_COMMANDS
當你的模塊有很多源代碼文件或依賴很多靜態庫或者共享庫時,設置為true。這樣會強制編譯系統使用@語法來包含中間對象或鏈接庫來生成歸檔文件

注意:這個功能在Windows上很有用,因為Windows上的命令行支持的最大字符數為8191個,這對于復雜的項目來說太小。默認是不推薦啟用這個功能,因為它會使編譯變得很慢。

LOCAL_THIN_ARCHIVE
編譯靜態庫是,如果該變量值設為true,會生成一個較小的歸檔文件。并不包含目標文件,而是用目標文件的路徑替代。有效值是truefalse和空。

注意:如果該模塊不是編譯為靜態塊,或者預編譯靜態庫,該值將被忽略。

LOCAL_FILTER_ASM
該變量的值將作為一個Shell命令,它會過濾從LOCAL_SRC_FILES生成的文件或匯編文件。定義該變量將會發生下面的情況:

  • 編譯系統會將C/C++源文件生成臨時的匯編文件,而不是將他們編譯到目標文件中。
  • 編譯系統會對匯編文件和LOCAL_SRC_FILES中列出的文件執行LOCAL_FILTER_ASM中的Shell命令,生成另外一個匯編文件。
  • 編譯系統將這些過濾后的匯編文件編譯進目標文件。

NDK提供的函數宏
NDK提供了GNU Make的函數宏。使用$(call <function>)的方式調用,它們會返回相應的文本信息。
my-dir
該宏返回的是最后包含的makefile文件路徑,一般是當前Android.mk的路徑。my-dir對于Android.mk開頭定義的LOCAL_PATH變量很有用。例如:

LOCAL_PATH := $(call my-dir)

由于GNU Make的工作方式,這個宏返回的是構建系統在解析構建腳本時包含的最后一個makefile的路徑。因此,你不應該在include其他的文件之后再繼續使用my-dir。例如:

LOCAL_PATH := $(call my-dir)  # ... declare one module  include $(LOCAL_PATH)/foo/`Android.mk`  LOCAL_PATH := $(call my-dir)  # ... declare another module

這里的問題在于第二個my-dir的調用將LOCAL_PATH的值設置為了$PATH/foo,因為$PATH/foo才是最近包含的路徑。你可以通過在Android.mk文件中放置額外的包含來避免這個問題。例如:

LOCAL_PATH := $(call my-dir)  # ... declare one module  LOCAL_PATH := $(call my-dir)  # ... declare another module  # extra includes at the end of the Android.mk file include $(LOCAL_PATH)/foo/Android.mk

如果這種方式不可行,那么可以將第一次調用my-dir的值存在另外一個變量里面,例如:

MY_LOCAL_PATH := $(call my-dir)  LOCAL_PATH := $(MY_LOCAL_PATH)  # ... declare one module  include $(LOCAL_PATH)/foo/`Android.mk`  LOCAL_PATH := $(MY_LOCAL_PATH)  # ... declare another module

all-subdir-makefiles
該宏返回的是當前my-dir路徑下的所有子目錄中的Android.mk文件的列表。你可以使用此函數向構建系統提供深層嵌套的源目錄層次結構。默認情況下,NDK僅查找包含Android.mk文件的目錄中的文件。

this-makefile
該宏返回的是當前makefile文件的路徑(從構建系統調用這個函數中)。

parent-makefile
該宏返回當前目錄樹中的父makefile的路徑(包含當前makefile的makefile路徑)。

grand-parent-makefile
該宏返回包含樹中的祖父類makefile的路徑(包含當前makefile的makefile的路徑)。

import-module
該宏允許你通過模塊的名稱找到并包含模塊的Android.mk文件。例如:

$(call import-module,<name>)

在這個示例中,構建系統會根據NDK_MODULE_PATH這個環境變量所指示的目錄里面尋找名為<name>的模塊,然后自動為你include對應的Android.mk文件。

5、Application.mk

通過上面的介紹,大體了解了Android.mk文件的用法及規則。但通常編譯本地C/C++代碼光有Android.mk還不夠,還得需要Application.mk文件。Application.mk也是一種makefile文件,跟Android.mk有相似之處。如果說,Android.mk用來描述單獨某個模塊的編譯規則的描述文件,那么Application.mk則是描述整個應用程序的模塊的描述文件。Application.mk文件一般在$project-path/jni/Application.mk下($project-path是項目根目錄)。當然,也可以放在$NDK/apps/<myapp>/Application.mk路徑下。這兩種方式,造成Application.mk也有細微的區別。

(1)變量
APP_PROJECT_PATH
該變量用來指定項目根目錄的絕對路徑。

注意:假如Application.mk文件的路徑是$NDK/apps/<myapp>/Application.mk,那么該變量為強制定義的。如果,Application.mk文件在$project-path/jni/Application.mk路徑下,則是可選變量。

APP_OPTIM:
該變量為可選變量,值為releasedebug。編譯應用程序模塊的時,可以用來改變優化級別。默認是release模式,并且會生成高度優化的二進制文件。debug模式生成的是未優化的二進制代碼,但更容易調試。

APP_CFLAGS
在編譯任何模塊的任何C/C++代碼時,構建系統會通過該變量給編譯器傳遞一個C編譯標志集合。你可以使用該變量根據應用程序中給定的模塊的需要來改變其構建,而不需要修改Android.mk文件本身了。
這些標志的所有路徑必須相對于NDK的頂級目錄。例如:如果你有以下設置:

sources/foo/Android.mk sources/bar/Android.mk

在編譯期間,你需要在foo/Android.mk中指定你要添加的foo的源路徑,你應該使用:

APP_CFLAGS += -Isources/bar

或者:

APP_CFLAGS += -I$(LOCAL_PATH)/../bar

使用-I../bar將不能正常工作,因為它等價于-I$NDK_ROOT/../bar

注意:在android-ndk-1.5_r1版本中,該變量只適用于C,不能作用于C++。之后的所有的版本,該變量可適用C/C++源碼上。

APP_CPPFLAGS
該變量包含一組C++編譯器標志,構建系統僅在構建C++源代碼時傳遞給編譯器。

APP_LDFLAGS
在鏈接的時候,構建系統系統會想鏈接器傳遞一組鏈接標志。該變量僅在構建系統構建共享庫和可執行文件的時候才生效,當構建靜態庫時,將被忽略。

APP_BUILD_SCRIPT
默認情況下,NDK構建系統會在$project-path/jni/目錄下查找Android.mk文件。如果你想修改此行為,你可以定義APP_BUILD_SCRIPT變量,并指向備用的構建腳本。編譯系統總是將一個非絕對路徑解釋為NDK的頂級目錄。

APP_ABI
默認情況下,編譯系統會根據armeabi ABI生成機器碼。該機器碼基于ARMv5TE并且支持浮點運算的CPU。你可以使用APP_ABI參數來指定不同的ABI。不同的指令集的APP_ABI設置如下:

注意:all是android-ndk-r7版本開始支持的。
你也可以指定多個值,將它們放在同一行,中間用空格隔開。例如:

APP_ABI := armeabi armeabi-v7a x86 mips

APP_PLATFORM
此變量包含目標Android的名稱。例如:android-3對應的是Android1.5的系統鏡像。

APP_STL
默認情況下,NDK構建系統只為最小的C++運行庫(/system/lib/libstdc++.so)提供C++頭文件。此外,你還可以在自己的應用程序中使用或鏈接其他C++實現。可以使用APP_STL來選擇其中的一個。例如:

APP_STL := stlport_static APP_STL := stlport_shared APP_STL := system

APP_SHORT_COMMANDS
該變量相當于整個項目的Android.mk中定義了LOCAL_SHORT_COMMANDS

NDK_TOOLCHAIN_VERSION
將此變量定義為4.9版本的GCC編譯器。在android-ndk-r13默認是Clang編譯器。

APP_PIE
從Android 4.1(API級別16)開始,Android的動態鏈接器支持位置無關可執行文件(PIE)。從Android 5.0(API級別21),可執行文件需要PIE。要使用PIE構建可執行文件,需設置-fPIE標志。這個標志會使得通過隨機代碼的位置來查找內存損壞的bug更加困難。默認情況下,如果您的項目目標為Android-16或更高版本,ndk-build會自動將此值設置為true。您可以將其手動設置為true或false。

注意:此標志僅適用于可執行文件。它在構建共享或靜態庫時沒有任何作用。

APP_THIN_ARCHIVE
相當于在Android.mk文件中為此項目中的所有靜態庫模塊設置LOCAL_THIN_ARCHIVE的默認值。

以上內容可能不一定完全正確,大部分內容是根據Android官方文檔,通過自己的理解轉述的。并沒有每個變量在.mk文件中有實踐過。所以,如果遇到你覺得有問題的地方,歡迎與我交流。

本文發布:Android開發網
本文地址:http://www.cpbsu.com/android/game/703.html
2017年8月14日
發布:雞啄米 分類:Android游戲開發 瀏覽: 評論:0