1. 程式人生 > >Android.mk 極簡入門

Android.mk 極簡入門

  • 適合物件
  • 自定義變數
  • GNU Make 系統變數
  • LOCAL_ 模組描述變數
  • 編譯module模板

一、適合物件

   本部落格僅適合剛接觸Android.mk的新人,簡單上手所作。另外本人只想做一個純粹的僅適合嵌入式C ,不涉及 Java 的Android.mk粗略講解,適合建立一個初步的概念框架,絕不涉及過多更為細節的內容。追求更詳細內容的講解,可以看CSDN 其他大佬博主的Android.mk詳解,非常詳細全面。

  Android.mk檔案用來告知NDK Build 系統關於Source 的資訊,個人理解即是連結標頭檔案、原始檔、各類庫來編譯模組,其中無需額外新增依賴關係。由於Android.mk中所有的變數都是全域性的,所以請儘量減少在Android.mk宣告的變數,也不要認為某些變數在解析過程中不會被定義。

  對於C/C++ 可編譯的模組型別:

module 說明
C/C++應用程式 可執行的 C/C++應用程式
C/C++靜態庫 編譯生成C/C++靜態庫,並打包生成.a檔案
C/C++共享庫 編譯生成共享庫,也可以是動態連結庫, 兩者只是呼叫庫的方式不同,並打包成.so檔案

  編譯指令:

  1. 在編譯模組前, 在android 目錄下, 執行 $. ./build/envsetup.sh
  2. 在Android.mk 所在目錄下,執行 $mm , 開始編譯模組

  我們可以在每一個Android.mk中定義一個或多個模組, 也可以在幾個模組中使用同一個原始碼檔案。編譯系統會為你處理許多細節問題,比如你無需在Android.mk中列出標頭檔案和生成的檔案之間的顯式依賴關係,編譯系統將會為你自動處理這些問題。

符號 作用
:= 賦值
+= 追加
$ 引用某變數的值
\ ‘\’和 Enter鍵配合來換行,可以連線同一變數上下行的檔案

  ‘\’ 使用例項

LOCAL_SRC_FILES:= \
            main.c \
            yan.c \
            pwm.c 

ps: 注意一行中’\‘後不能有空格,否則會編譯出錯;最後一個檔案後儘量避免新增不必要的’\’,以免誤將非空格的無關的下一行新增入變數。另外若一行足夠,同一行的檔案之間可以用空格鍵或Tab鍵進行分割。

四、自定義變數

  在Android.mk中允許自定義變數,但是需要避開NDK編譯系統已保留下來的下列變數名成分。   以下是要避開的變數名成分:    1. 以LOCAL_開頭的名字,例如LOCAL_MODULE    2. 以PRIVATE_, NDK_, 或APP_開頭的名字(內部使用)    3. 小寫名字(內部使用),如" my_dir"

  為了方便在Android.mk中定義自己的變數,建議使用MY_ 作為字首。

五、GNU Make 系統變數

  這些GNU Make變數在你的Android.mk檔案解析之前,就由編譯系統定義好了。注意在某些情況下, NDK可能分析Android.mk幾次, 每一次某些變數的定義會有不同。  1) CLEAR_VARS   指向一個編譯指令碼,幾乎所有未定義的LOCAL_XXX都在"Module-description"節中列出。必須在開始一個新模組之前定義這個指令碼: include $(CLEAR_VARS),用於重置除LOCAL_PATH變數外的所有LOCAL_XXX系列變數。

 2) BUILD_SHARED_LIBRARY   指向編譯指令碼, 把在include $(CLEAR_VARS)之後,include $(BUILD_SHARED_LIBRARY)之前所有的LOCAL_XXX變數列出的原始碼檔案編譯成一個共享庫, 即生成一個libxxx.so檔案。

 3) BUILD_STATIC_LIBRARY   用於編譯一個靜態庫中,編譯的原始檔方法如BUILD_SHARED_LIBRARY。靜態庫不會複製到APK中, 但是能夠用靜態庫編譯共享庫。

 4) BUILD_EXECUTABLE    用於編譯一個可執行程式,如include $(BUILD_EXECUTABLE),生成可執行的 C/C++應用程式。

 5) TARGET_ARCH   目標CPU架構的名字,和android開放原始碼中指定的那樣。如果是arm,表示要生成ARM相容的指令,與CPU架 構的修訂版無關。

 6) TARGET_PLATFORM:   目標Android.mk平臺的名字,詳情可參考/development/ndk/docs/stable- apis.txt。   android-3 -> Official Android 1.5 system images   android-4 -> Official Android 1.6 system images   android-5 -> Official Android 2.0 system images

六、LOCAL_ 模組描述變數

  下面的變數用於向編譯系統描述你的模組,應該定義在 “include $(CLEAR_VARS) " 和"include $(BUILD_XXX)” 之間。正如前面描述的一樣, include $(CLEAR_VARS)用於重置除LOCAL_PATH變數外的所有LOCAL_XXX系列變數。

 1) LOCAL_PATH    用於給出當前需編譯檔案所在的路徑,必須在 Android.mk的開頭定義,使用方法如 LOCAL_PATH := $(call_my_dir),則$(LOCAL_PATH) 為當前 Android.mk所在的路徑。這個變數不會被include $(CLEAR_VARS)清除,因此在每個Android.mk檔案開頭定義一次即可,即使該檔案中已經定義了幾個模組(LOCAL_MOUDEL)。

 2)LOCAL_MOUDEL   給定模組的名字,必須是唯一的,且不含空格,必須在任一包含 $(BUILD_XXX) 的指令碼前定義它。模組的名字決定了生成檔案的名字。例如,一個共享庫的模組的名字為libvp_vpu ,則生成的檔案為libvp_vpu.so。另外此時共享庫模組對應的LOCAL_MODULE的名字最好能有lib字首,因為有的 Android.mk並不會自動生成字首lib ,以致不能正確呼叫共享庫編譯可執行程式,編譯出錯。

 3)LOCAL_MODULE_PATH   指定生成模組的路徑,可和LOCAL_MODULE搭配使用。 若未設定,則生成的模組在編譯系統預設的生成路徑。用法如 LOCAL_MODULE_PATH := $(LOCAL_PATH)。

 4)LOCAL_SRC_FILES   給定要編譯的原始碼檔案列表,檔案也可以是靜態庫,或共享庫。只要列出要傳遞給編譯器的檔案,則編譯系統就能自動計算檔案之間的依賴關係。注意給定的原始碼檔案都相對於 $(LOCAL_PATH),在該路徑下。也可以選擇新增路徑部分,例如 LOCAL_SRC_FILES := taobao/foo.c ,使用的是$(LOCAL_PATH)相對路徑, 絕對路徑為$(LOCAL_PATH)/taobao/。

 5)LOCAL_STATIC_LIBRARIES   表示該模組需要使用哪些靜態庫,以便在編譯時進行連結。

 6)LOCAL_SHARED_LIBRARIES   表示該模組在編譯時要依賴的共享庫,在連結時就需要,以便在生成檔案時嵌入其相應的資訊,但不像靜態庫那樣把程式程式碼加入。所以程式執行的開始,也 要有相應的共享庫和對應的庫路徑。 簡單的庫路徑新增命令: $export LD_LIBRARY_PATH=/…/…/…/,也可以選擇放在/system/lib/等lib目錄下方便載入。

 7)LOCAL_C_INCLUDES   這是一個可選變數,很常用,基本都會用到,表示標頭檔案的搜尋路徑。預設的標頭檔案搜尋路徑是 $(LOCAL_PATH)目錄。如果要新增其他路徑的標頭檔案,新增路徑的方法和 LOCAL_SRC_FILES一樣。

 8)LOCAL_CFLAGS   可選的編譯器選項,在編譯 C 程式碼檔案的時候使用。這可能是有用的,指定一個附加的包含路徑(相對於NDK的頂層目錄),巨集定義,或者編譯選項。   注意:不要在 Android.mk中改變 optimization/debugging 級別,只要在 Application.mk中指定合適的資訊,就會自動地為你處理這個問題,在除錯期間,會讓 NDK自動生成有用的資料檔案。

ps: 以下是C++的內容,不需要的可以跳過  9)LOCAL_CPP_EXTENSION   這同樣是一個可選變數,用來指定C++程式碼檔案的副檔名,預設是".cpp", 但是可以改變副檔名,比如LOCAL_CPP_ENTENSION := .cxx ,則可以用.cxx的副檔名,本人尚未嘗試使用。

 10)LOCAL_CXXFLAGS   與LOCAL_CFLAGS同理,針對C++原始檔。

 11)LOCAL_CPPFLAGS   與LOCAL_CFLAGS同理,對C和C++ 原始檔都適用。

七、編譯module模板

  其實編譯各模組的模板都很相似,光看前面是不易看出究竟是編譯什麼模組的,然而在模組編譯的最後一句可以明確區分是編譯哪種型別的模組。

模組 模組型別確定
C/C++應用程式 include $(BUILD_EXECUTABLE)
C/C++靜態庫 include $BUILD_STATIC_LIBRARY)
C/C++共享庫 include $(BUILD_SHARED_LIBRARY)

 1. 編譯C/C++ 應用程式模板   備註: 帶#的變數是可選項

	#Android.mk 開頭初始化,
	#確定當前編譯檔案路徑以及重置除LOCAL_PATH以外的 LOCAL_XXX變數
	LOCAL_PATH := $(call my-dir)
	include $(CLEAR_VARS)
	
	#生成的模組名,可選是否設定模組存放的路徑,我選擇放在當前路徑下
	LOCAL_MODULE :=   XXX
	#LOCAL_MODULE_PATH :=  $(LOCAL_PATH)
          
	#需要的材料,分別是原始檔(必選)、靜態庫.a檔案(可選)、共享庫.so檔案(可選)、標頭檔案(可選)
	#若未設定路徑,則材料預設的搜尋路徑為 $(LOCAL_PATH),在該目錄下
	#可定義路徑,絕對路徑如 $(TOP)/hardware/rga.c 
	#或相對於 $(LOCAL_PATH)的路徑,hardware/rga.c 等效於 $(LOCAL_PATH)/hardware/rga.c 
        
	LOCAL_SRC_FILES := main.c  \
				        Xxx.c  \
				        xxx.c
	#LOCAL_STATIC_LIBRARIES :=
	#LOCAL_SHARED_LIBRARIES :=
	#LOCAL_C_INCLUDES := 
          
   	#生成可執行程式
	include $(BUILD_EXECUTABLE)

 ps: C/C++執行模組的目標資料夾為 XXX_intermediates,XXX是模組名 。

 2. 編譯C/C++靜態庫

	LOCAL_PATH := $(call my-dir)
	include $(CLEAR_VARS)
	
	#生成的靜態庫模組名,將生成xxx.a檔案
	LOCAL_MODULE := XXX
	#LOCAL_MODULE_PATH := $(LOCAL_PATH)
          
	 #需要的材料,分別是原始檔(必選)、靜態庫(可選)、共享庫(可選)、標頭檔案(可選)        
	LOCAL_SRC_FILES :=    Xxx.c  \
				          xxx.c
	
	#LOCAL_STATIC_LIBRARIES :=
	#LOCAL_SHARED_LIBRARIES :=
	#LOCAL_C_INCLUDES := 
          
	#生成C/C++靜態庫
	include $(BUILD_STATIC_LIBRARY)

  ps: C/C++ 靜態庫模組的目標資料夾為 XXX_static_intermediates,或者XXX_intermediates,我的是後者,編譯系統版本不同會有差異, XXX是模組名。這個在 Android.mk 編譯生成過程中會有顯示。

 3. 編譯C/C++ 共享庫

	LOCAL_PATH := $(call my-dir)
	include $(CLEAR_VARS)
	
	#生成的模組名,xxx最好能有lib字首,生成libxxx.so檔案,以適應不同版本的Android.mk
	LOCAL_MODULE :=   XXX
	#LOCAL_MODULE_PATH :=  $(LOCAL_PATH)
          
	#需要的材料,分別是原始檔(必選)、靜態庫(可選)、共享庫(可選)、標頭檔案(可選) 
	LOCAL_SRC_FILES := Xxx.c \
				       xxx.c 
	
	#LOCAL_STATIC_LIBRARIES :=
	#LOCAL_SHARED_LIBRARIES :=
	#LOCAL_C_INCLUDES := 
	
    #生成C/C++ 共享庫
    include $(BUILD_SHARED_LIBRARY)

 ps: 生成的目標資料夾名帶 "_intermediates"結尾,在編譯過程中有顯示,版本不同則資料夾路徑和資料夾名會有差異,根據實際的目錄查詢即可。同時靜態庫可用來編譯共享庫,則LOCAL_SRC_FILES := Xxx.a。

  當然也可以在一個Android.mk編譯多個模組,模組之間Include $(CLEAR_VARS) 和Include $(BUILD_)相隔, 其中LOCAL_PATH 只需在Android.mk開頭定義一次即可,後續不必重複定義。

附本人的Android.mk例項:

#是否要編譯庫, true 確定便宜庫, false 不編譯庫,編譯執行程式
COMPILE_LIB := true     
ifeq ($(COMPILE_LIB),true)

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE:= libvp_yan
LOCAL_MODULE_PATH := $(LOCAL_PATH)

LOCAL_CFLAGS += -DVERSION=4.3

LOCAL_SRC_FILES:= \
            VP_Yan.c \
            VP_Pwm.c \
            VP_Hdu.c \

LOCAL_SHARED_LIBRARIES := \
    libhardware \
    liblog 

LOCAL_C_INCLUDES += \
    hardware/libhardware/include \
    $(TOP)/hardware/log
    $(LOCAL_PATH)/hfile   #有標頭檔案位於當前編譯路徑的hfile子資料夾下
    
include $(BUILD_SHARED_LIBRARY)

else
#編譯可執行程式vp_yin
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_CFLAGS += -DVERSION=4.3
LOCAL_MODULE:= vp_yin
LOCAL_MODULE_PATH := $(LOCAL_PATH)

LOCAL_SRC_FILES:= \
			VP_Main.c
                     
LOCAL_SHARED_LIBRARIES := \
      libhardware \
      liblog \
      libvp_yan          #新增入true選項編譯的libvp_yan.so 共享庫

LOCAL_C_INCLUDES += \
     hardware/libhardware/include \
    $(TOP)/hardware/log
    $(LOCAL_PATH)/hfile

include $(BUILD_EXECUTABLE)

endif