Android中C運(yùn)用 講解(二)

前言

Android中C運(yùn)用 講解(一)中主要表述了如何建一個(gè)JNI DEMO,什么是JNI,什么是NDK。接下來我要為大家總結(jié)Android.mk的編譯姿勢和cmaklist的編譯姿勢,這篇文章先為大家?guī)韆ndroid.mk編譯姿勢講解。
感謝:https://blog.csdn.net/ruglcc/article/details/7814546/(如作者覺得侵權(quán),請及時(shí)聯(lián)系本人)
感謝:《GNU Make手冊》。
感謝:https://blog.csdn.net/gjq_1988/article/details/12573721(如作者覺得侵權(quán),請及時(shí)聯(lián)系本人)

背景

  • 一般來說,無論是C、C++、還是pas,首先要把源文件編譯成中間代碼文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,這個(gè)動(dòng)作叫做編譯(compile)。然后再把大量的Object File合成執(zhí)行文件,這個(gè)動(dòng)作叫作鏈接(link)。
  • 編譯時(shí),編譯器需要的是語法的正確,函數(shù)與變量的聲明的正確。對于后者,通常是你需要告訴編譯器頭文件的所在位置(頭文件中應(yīng)該只是聲明,而定義應(yīng)該放在C/C++文件中),只要所有的語法正確,編譯器就可以編譯出中間目標(biāo)文件。一般來說,每個(gè)源文件都應(yīng)該對應(yīng)于一個(gè)中間目標(biāo)文件(O文件或是OBJ文件)。
  • 鏈接時(shí),主要是鏈接函數(shù)和全局變量,所以,我們可以使用這些中間目標(biāo)文件(O文件或是OBJ文件)來鏈接我們的應(yīng)用程序。鏈接器并不管函數(shù)所在的源文件,只管函數(shù)的中間目標(biāo)文件(Object File),在大多數(shù)時(shí)候,由于源文件太多,編譯生成的中間目標(biāo)文件太多,而在鏈接時(shí)需要明顯地指出中間目標(biāo)文件名,這對于編譯很不方便,所以,我們要給中間目標(biāo)文件打個(gè)包,在Windows下這種包叫“庫文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。
  • 總結(jié)一下,源文件首先會(huì)生成中間目標(biāo)文件,再由中間目標(biāo)文件生成執(zhí)行文件。在編譯時(shí),編譯器只檢測程序語法,和函數(shù)、變量是否被聲明。如果函數(shù)未被聲明,編譯器會(huì)給出一個(gè)警告,但可以生成Object File。而在鏈接程序時(shí),鏈接器會(huì)在所有的Object File中找尋函數(shù)的實(shí)現(xiàn),如果找不到,那到就會(huì)報(bào)鏈接錯(cuò)誤碼(Linker Error),在VC下,這種錯(cuò)誤一般是:Link 2001錯(cuò)誤,意思說是說,鏈接器未能找到函數(shù)的實(shí)現(xiàn)。你需要指定函數(shù)的ObjectFile.

Android編譯C、C++ 需要:1??、Android.mk 2??、Application.mk 3??、ndk-build 。
1??、Android.mk 本質(zhì)上是android編譯環(huán)境下的一種特殊的“makefile”文件, 它是經(jīng)過了android編譯系統(tǒng)處理的。
那么什么是Makefile讓我們來看看怎么說:

Makefile 介紹

百科的詮釋:一個(gè)工程中的源文件不計(jì)其數(shù),其按類型、功能、模塊分別放在若干個(gè)目錄中,makefile定義了一系列的規(guī)則來指定,哪些文件需要先編譯,哪些文件需要后編譯,哪些文件需要重新編譯,甚至于進(jìn)行更復(fù)雜的功能操作,因?yàn)?makefile就像一個(gè)Shell腳本一樣,其中也可以執(zhí)行操作系統(tǒng)的命令。
百科里面說的并不是很精髓,我把《GNU Make手冊》中一句話粘貼出來:You need a file called a makefile to tell make what to do. Most often, the makefile tells make how to compile and link a program.
簡單的說:就是我們需要一個(gè)文件來告訴make如何去編譯、鏈接一個(gè)程序,這個(gè)文件的名字是makefile

Android.mk和makefile的聯(lián)系

makefile的描述已經(jīng)說過了,android.mk描述的是android編譯環(huán)境下的一種特殊的“makefile”。那首先我們來闡釋一下android編譯環(huán)境:是android頂層目錄下的build目錄里面的一系列編譯控制文件,其實(shí)就是一系列makefile文件和 .mk 文件,這些文件才是編譯android系統(tǒng)完整的makefile文件. 每個(gè)模塊里的android.mk只不過是被包含進(jìn)android編譯系統(tǒng)的一小部分而已。經(jīng)過android編譯系統(tǒng)的一大堆處理,android.mk的格式就變得非常簡單,且與普通的makefile文件書寫格式不一樣了,但這有利于為Android增加一個(gè)新的Component。
但歸根結(jié)底,android.mk文件最終還是要被android編譯系統(tǒng)層層解析,轉(zhuǎn)化為make命令能夠讀懂的格式,從而調(diào)用gcc編譯器進(jìn)行編譯。
Android.mk只不過是被android編譯系統(tǒng)包含的一種文件,需要在android編譯系統(tǒng)的支持下解析。本質(zhì)上android模塊在編譯時(shí)最終還是使用make命令解析一大堆makefile 和
.mk文件(有些文件以.mk為后綴,應(yīng)該也是按照makefile文件的規(guī)則來解析)

Android.mk詳解

Android.mk 文件位于項(xiàng)目 jni/ 目錄的子目錄中,用于向構(gòu)建系統(tǒng)描述源文件和共享庫。 它實(shí)際上是構(gòu)建系統(tǒng)解析一次或多次的微小 GNU makefile 片段。 Android.mk 文件用于定義 Application.mk、構(gòu)建系統(tǒng)和環(huán)境變量所未定義的項(xiàng)目范圍設(shè)置。 它還可替換特定模塊的項(xiàng)目范圍設(shè)置。

Android.mk 的語法用于將源文件分組為模塊。 模塊是靜態(tài)庫、共享庫或獨(dú)立可執(zhí)行文件。 可在每個(gè) Android.mk 文件中定義一個(gè)或多個(gè)模塊,也可在多個(gè)模塊中使用同一個(gè)源文件。 構(gòu)建系統(tǒng)只會(huì)將共享庫放入應(yīng)用軟件包。 此外,靜態(tài)庫可生成共享庫。

基礎(chǔ)知識

Android.mk文件.png

標(biāo)識C/C++源文件。在該項(xiàng)目中,my-dir 是一個(gè)由編譯系統(tǒng)提供的宏函數(shù),用于返回Android.mk所在目錄的路徑。

LOCAL_PATH := $(call my-dir) 

CLEAR_VARS編譯系統(tǒng)預(yù)定義的一個(gè)變量,它指向一個(gè)特殊的Makefile,這個(gè)Makefile負(fù)責(zé)清除 LOCAL_xxx 的變量

include $(CLEAR_VARS) 

用來指定模塊編譯時(shí)的其余連接器標(biāo)志。例如:這里表示它依賴于系統(tǒng)提供的 liblog.so

LOCAL_LDLIBS :=-llog 

LOCAL_MODULE 用來給每個(gè)模塊定義一個(gè)名字,不同模塊的名字不能相同,不能有空格。這里的名字會(huì)傳給NDK編譯系統(tǒng),然后加上lib前綴和.so后綴 (例如,變成libhello-jni.so)。注意,如果你在LOCAL_MODULE定義中自己加上了lib前綴,則ndk在處理的時(shí)候就不會(huì)再加上lib前綴了(為了兼容Android系統(tǒng)的一些源碼)。

LOCAL_MODULE    := test 

定義C++文件路徑

MY_CPP_LIST :=  $(wildcard $(LOCAL_PATH)/src/*.cpp)  

定義C文件路徑

MY_CPP_LIST +=  $(wildcard $(LOCAL_PATH)/src/*.c)    

在LOCAL_SRC_FILES 變量里面列舉出對應(yīng)于同一個(gè)模塊的、要編譯的那些文件,這里不要把頭文件加進(jìn)來,編譯系統(tǒng)可以自動(dòng)檢測頭文件依賴關(guān)系。默認(rèn)情況下,C++源碼文件的擴(kuò)展名應(yīng)該是cpp

LOCAL_SRC_FILES := $(MY_CPP_LIST) 

指定應(yīng)該鏈接到當(dāng)前模塊的靜態(tài)庫(可指定多個(gè))。當(dāng)前模塊是動(dòng)態(tài)庫時(shí),該選項(xiàng)才有意義。

LOCAL_STATIC_LIBRARIES := cpufeatures 

用來指定模塊編譯時(shí)的其余連接器標(biāo)志

# LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog 
# NDK_DEBUG = 1

這個(gè) BUILD_SHARED_LIBRARY也是預(yù)定義的變量,也是指向一個(gè)Makefile,負(fù)責(zé)將你在 LOCAL_XXX 等變量中定義信息收集起來,確定要編譯的文件,如何編譯。如果要編譯的是靜態(tài)庫而不是動(dòng)態(tài)庫,則可以用 BUILD_STATIC_LIBRARY。

include $(BUILD_SHARED_LIBRARY) 

import-module該函數(shù)用于按模塊名查找另一個(gè)模塊的Android.mk文件,并包含進(jìn)來。

$(call import-module,android/cpufeatures) 
更全更詳細(xì)Android 官方提供的文檔:https://developer.android.com/ndk/guides/android_mk?hl=zh-cn

接下來我們說下Application.mk:

Application.mk詳解

官方描述:Application.mk 文件實(shí)際上是定義要編譯的多個(gè)變量的微小 GNU Makefile 片段。 它通常位于 $PROJECT/jni/ 下,其中 PROJECT 指向應(yīng)用的項(xiàng)目目錄。 另一種方式是將其放在頂級 NDK/apps/目錄的子目錄下。

APP_PROJECT_PATH 這個(gè)變量存儲(chǔ)應(yīng)用程序的項(xiàng)目根目錄的絕對路徑。
APP_OPTIM 配置release和debug
APP_CFLAGS 這個(gè)變量存儲(chǔ)一組構(gòu)建系統(tǒng)的C編譯器標(biāo)志傳遞給編譯器編譯任何C或c++源代碼的任何模塊,可以修改應(yīng)用需要的構(gòu)建模塊而不用修改Android.mk文件
APP_CPPFLAGS 和 APP_CFLAGS類似
APP_LDFLAGS A set of linker flags that the build system passes when linking the application,只對 shared libraries 和 executables有效
APP_BUILD_SCRIPT 指定Android.mk文件
APP_ABI 指定abi
APP_PLATFORM 指定android api版本
APP_STL 鏈接其他的c++支持
NDK_TOOLCHAIN_VERSION gcc編譯版本
APP_PIE

官方地址:https://developer.android.com/ndk/guides/application_mk?hl=zh-cn

Application.mk文件.png

Androidmk更多的是側(cè)重于定義源文件、依賴庫之間的關(guān)系、模塊名稱等等。
Application.mk更側(cè)重于指定基于什么環(huán)境(C++11庫等)、關(guān)聯(lián)哪些庫編譯的編譯參數(shù)。

ndk-build

ndk-build 文件是 Android NDK r4 中引入的一個(gè) shell 腳本。其用途是調(diào)用正確的 NDK 構(gòu)建腳本。
內(nèi)部構(gòu)建
運(yùn)行 ndk-build 腳本相當(dāng)于運(yùn)行以下命令:

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

$GNUMAKE 指向 GNU Make 3.81 或更新版本,<ndk> 指向 NDK 安裝目錄。 您可以使用此信息從其他 shell 腳本甚至您自己的 Make 文件調(diào)用 ndk-build。

從命令行調(diào)用

ndk-build 文件位于 NDK 安裝目錄的頂層。若要從命令行運(yùn)行該文件,請?jiān)趹?yīng)用項(xiàng)目目錄中或其子目錄中調(diào)用它。例如:
在此示例中,<project> 指向項(xiàng)目的根目錄,<ndk> 是您安裝 NDK 的目錄。

選項(xiàng)

ndk-build 的所有參數(shù)將直接傳遞到運(yùn)行 NDK 構(gòu)建腳本的底層 GNU make。 將 ndk-build 和表單 ndk-build <option> 中的選項(xiàng)結(jié)合使用。 例如:
提供的選項(xiàng)如下:
移除以前生成的任意二進(jìn)制文件。</d
clean
移除以前生成的任意二進(jìn)制文件。
V=1
啟動(dòng)構(gòu)建,并顯示構(gòu)建命令。
-B
強(qiáng)制執(zhí)行完全的重新構(gòu)建。
-B V=1
強(qiáng)制執(zhí)行完全的重新構(gòu)建,并顯示構(gòu)建命令。
NDK_LOG=1
顯示內(nèi)部 NDK 日志消息(用于調(diào)試 NDK 本身)。
NDK_DEBUG=1
強(qiáng)制執(zhí)行可調(diào)試版構(gòu)建(請參閱表 1)。
NDK_DEBUG=0
強(qiáng)制執(zhí)行發(fā)布版構(gòu)建(請參閱表 1)。
NDK_HOST_32BIT=1
始終使用 32 位模式下的工具鏈(請參閱 64 位和 32 位工具鏈)。
NDK_APPLICATION_MK=<file>
使用 NDK_APPLICATION_MK 變量指向的特定 Application.mk 文件構(gòu)建。
-C <project>
構(gòu)建位于 <project> 的項(xiàng)目路徑的原生代碼。如果您不想在終端通過 cd 切換到該路徑,則此選項(xiàng)非常有用。

要求:
一般情況下,您需要安裝 GNU Make 3.81 或更新版本才能使用 ndk-build 或 NDK。構(gòu)建腳本將檢測不兼容的 Make 工具,并生成錯(cuò)誤消息。

如果您已安裝 GNU Make 3.81,但默認(rèn) make 命令不能啟動(dòng)它,則在您的環(huán)境中定義 GNUMAKE 以便在啟動(dòng) ndk-build 之前指向它。 例如:

$ export GNUMAKE=/usr/local/bin/gmake
$ ndk-build

您可以在 $NDK/prebuilt/<OS>/bin/ 中將其他主機(jī)預(yù)構(gòu)建工具替換為下列環(huán)境變量:

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

相關(guān)閱讀更多精彩內(nèi)容

  • 我出生在一個(gè)農(nóng)村家庭,家里向上數(shù)三代都是農(nóng)民。小時(shí)候就一直聽家里老人說養(yǎng)兒防老,那時(shí)候年紀(jì)還小,沒有自己的思想,一...
    斜杠home閱讀 309評論 0 1
  • 相信很多人都已經(jīng)對CAD看圖PC端的操作了如指掌了,所以今天小編就來教教大家安卓手機(jī)如何快速下載安裝CAD手機(jī)看圖...
    周周周大璇閱讀 985評論 0 0
  • 夜幕降臨,望著窗外那輪明月,內(nèi)心復(fù)雜萬千,是壓力、焦慮,還是痛恨、奢望……壓抑的透不過氣,感覺好像要爆炸一樣,不明...
    大_0閱讀 1,383評論 0 2
  • 央視:保險(xiǎn)連續(xù)2年成百姓投資首選 一年一度的《經(jīng)濟(jì)生活大調(diào)查》顯示:在選擇投資的受訪者中,有高達(dá)40.85%的人首...
    是高潔啊閱讀 356評論 0 0
  • 《雙十一》修改一 期盼已久的雙十一終于到來了,各個(gè)商家打出了許多促銷活動(dòng)。這天,媽媽興奮地對我說:“寶貝兒,雙...
    陳鑫怡123閱讀 153評論 0 1

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