Android.mk的高級寫法

轉:http://blog.csdn.net/langresser_king/article/details/8275291

原本只是想記錄一些常用的使用技巧,但是越寫越得意(>_<),忍不住想要做出一份相對完善的說明文檔,以供大家研究探討。

        寫這篇文章的起因當然是實際工程需要,在搭建一個網遊的android客戶端時遇到種種噁心的問題,比如文件過多導致"Argument list too long" 的錯誤,又比如增加和刪除文件時都需要維護好Android.mk配置,雖然可以通過寫個腳本自動生成android.mk,但是終歸不是很漂亮的解決方 案。通過本文所提到的幾個方法,可以使Android.mk變得十分簡潔,減少增加和刪減工程文件時對Android.mk的修改,工程文件數目較多時會 非常實用。

       我先附上修改後的cocos2d-x工程配置(在本文的最後,我還會附上原始的配置,有興趣的同學可以對比下看看),然後在此基礎上逐一說明:

 

複製代碼
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := cocos2dx_static
LOCAL_MODULE_FILENAME := libcocos2d

#1 定義查找所有cpp文件的宏
define all-cpp-files-under
$(patsubst ./%,%, $(shell find $(LOCAL_PATH) -name "platform" -prune -o -name "*.cpp" -and -not -name ".*"))
endef

define all-subdir-cpp-files
$(call all-cpp-files-under,.)
endef

#2 定義查找所有c文件的宏,android有默認定義,此處可酌情省略
define all-c-files-under
$(patsubst ./%,%, $(shell find $(LOCAL_PATH) -name "platform" -prune -o -name "*.c" -and -not -name ".*"))
endef

define all-subdir-c-files
$(call all-c-files-under,.)
endef

#3 通過查找獲取所有工程文件列表
CPP_FILE_LIST := $(call all-subdir-cpp-files) \
                $(wildcard $(LOCAL_PATH)/platform/*.cpp) \
                $(wildcard $(LOCAL_PATH)/platform/android/*.cpp) \
                $(wildcard $(LOCAL_PATH)/platform/android/jni/*.cpp)
C_FILE_LIST := $(call all-subdir-c-files)

#4 加入工程文件,之所以不直接加是需要進行一個LOCAL_PATH的替換
LOCAL_SRC_FILES := $(CPP_FILE_LIST:$(LOCAL_PATH)/%=%) 
LOCAL_SRC_FILES += $(C_FILE_LIST:$(LOCAL_PATH)/%=%) 

LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) \
                    $(LOCAL_PATH)/include \
                    $(LOCAL_PATH)/kazmath/include \
                    $(LOCAL_PATH)/platform/android


LOCAL_EXPORT_LDLIBS := -llog\
                       -lz \
                       -lGLESv2

#5 加入頭文件
LOCAL_C_INCLUDES := $(LOCAL_PATH) \
                    $(LOCAL_PATH)/include \
                    $(LOCAL_PATH)/kazmath/include \
                    $(LOCAL_PATH)/platform/android

#6 需要鏈接的系統默認庫
LOCAL_LDLIBS := -lGLESv2 \
                -lEGL \
                -llog \
                -lz 

#7 加入靜態庫,加了LOCAL_WHOLE_STATIC_LIBRARIES代表編譯器會將靜態庫完整鏈接而不會進行刪減優化
LOCAL_WHOLE_STATIC_LIBRARIES := cocos_libpng_static
LOCAL_WHOLE_STATIC_LIBRARIES += cocos_jpeg_static
LOCAL_WHOLE_STATIC_LIBRARIES += cocos_libxml2_static
LOCAL_WHOLE_STATIC_LIBRARIES += cocos_libtiff_static

#8 預編譯宏
# define the macro to compile through support/zip_support/ioapi.c                
LOCAL_CFLAGS := -DUSE_FILE32API
LOCAL_EXPORT_CFLAGS := -DUSE_FILE32API

#9 聲明生成靜態庫
include $(BUILD_STATIC_LIBRARY)

#10 添加外部導入庫目錄
$(call import-add-path,$(LOCAL_PATH))

#11 添加導入庫(基於上一行添加的導入庫目錄)
$(call import-module,platform/third_party/android/prebuilt/libjpeg)
$(call import-module,platform/third_party/android/prebuilt/libpng)
$(call import-module,platform/third_party/android/prebuilt/libxml2)
$(call import-module,platform/third_party/android/prebuilt/libtiff)
複製代碼

 

 

 

首先是自動遍歷整個工程文件的方法,當解決這個問題,我們的目的也就實現了一大半。我先介紹makefile裏面的三個常用命令:

1、wildcard : 擴展通配符,用於查找一個目錄下的所有符合條件的文件
2、notdir : 去除路徑,僅保留文件名
3、patsubst :替換通配符,也可以是任意文本替換

#1  #2這兩處我定義了兩個宏,用於遍歷整個工程的cpp和c文件。

$(patsubst ./%,%, $(shell find $(LOCAL_PATH) -name "platform" -prune -o -name "*.c" -and -not -name ".*"))

如上文所 說,patsubst是文本替換,也就是把$(shell....)所返回的查找到的所有文件名去除掉開頭的"./",具體替換原因我們下文會說明。 $(shell...)會執行任意的shell命令(如果是在windows下,就一定要用cygwin來執行ndk-build)。 

      find可以查找指定目錄下的所有符合條件的文件。find後面跟查找目錄,這裏就是Android.mk所在目錄。需要特別注意後面的 -name "platform" -prune這個參數,它可以過濾掉特定文件夾("platform"),使用時要先通過-name把文件夾名字傳遞過去,然後添加-prune,如果沒 有需要過濾的文件夾,就不需要這個參數了。 -o可以指定輸出,如果使用了-prune就一定要有這個參數,表示在過濾結果的基礎上進行查找。  最後跟查找的判定式,示例中的判定式代表查找所有.c文件但是忽略以.開頭的文件(通常是系統文件或隱藏文件)。  find命令是實現文件遍歷的基礎。

#3處我們定義了一個宏代表文件列表

CPP_FILE_LIST := $(call all-subdir-cpp-files) \
                $(wildcard $(LOCAL_PATH)/platform/*.cpp) \
                $(wildcard $(LOCAL_PATH)/platform/android/*.cpp) \
                $(wildcard $(LOCAL_PATH)/platform/android/jni/*.cpp)
C_FILE_LIST := $(call all-subdir-c-files)

調用 $(call all-subdir-cpp-files) 返回了所有cpp文件列表,但是由於我們有指定忽略platform目錄的文件,所以需要在後面把這個目錄下android平臺相關的文件加進來。

$(wildcard $(LOCAL_PATH)/platform/*.cpp)

這個我們一開始有介紹,就是遍歷某一目錄下的所有特定擴展名的文件。這個命令的缺點是不支持遞歸目錄遍歷(否則也就不需要我們前面寫一大堆東西了),不過如果你的工程沒有太多目錄的話,可以直接使用這個。

 

#4處正式把文件加入到android工程中

LOCAL_SRC_FILES := $(CPP_FILE_LIST:$(LOCAL_PATH)/%=%) 
LOCAL_SRC_FILES += $(C_FILE_LIST:$(LOCAL_PATH)/%=%)

$(CPP_FILE_LIST:$(LOCAL_PATH)/%=%) 這個又是一個文本替換技巧。意思是,把CPP_FILE_LIST裏面的所有$(LOCAL_PATH)/去掉。之所以有這樣的替換是因爲LOCAL_SRC_FILES已經包含了LOCAL_PATH,其文件名應該是相對於Android.mk的相對路徑。如果SRC_FILES裏面還包含LOCAL_PATH的路徑那就出錯了。同樣這也是我們在#1 #2處需要將查找到的文件名中的"./"去除掉的原因。

#5~#9可以直接看註釋,沒有什麼需要特別說明的。

我們再重點說下#10 #11,也就是導入庫的使用。

LOCAL_WHOLE_STATIC_LIBRARIES += cocos_jpeg_static
#LOCAL_STATIC_LIBRARIES += cocos_jpeg_static
$(call import-module,platform/third_party/android/prebuilt/libjpeg)

 加入靜態 庫(第一行和第二行看實際情況使用,差別不大),然後使用call import-module引入導入庫。在編譯我們這個Android.mk模塊時編譯器會自動查找並編譯libjpeg模塊。通過使用導入庫可以更加清 晰和方便的複用現有庫,或者像我一樣把一個大的工程拆分成多個子模塊,然後再鏈接到一起。這樣還有一個附帶的好處是,當我們修改Android.mk文件 時,本文件的代碼都會重新編譯,但是導入庫引入的模塊不會。如果我們的工程很大,那麼使用導入庫後就可以避免修改一下配置就重新編譯整個工程的情況。

prebuilt庫的使用:

 

目的:複用沒有源代碼的靜態庫或動態庫。

 

方式:新建一個文件夾如libpng,將頭文件放到裏面的include目錄,.a靜態庫放到裏面的libs目錄,新建一個Android.mk其內容如下

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := cocos_libpng_static
LOCAL_MODULE_FILENAME := png
LOCAL_SRC_FILES := libs/$(TARGET_ARCH_ABI)/libpng.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)


重點有兩個LOCAL_MODULE是prebuilt庫的名字,外部使用時添加的就是這個名字, LOCAL_EXPORT_C_INCLUDES是導出頭文件,方便外部頭文件包含。

外部使用時像普通的導入庫一樣使用,再次強調名字一定要一致,否則prebuilt庫無法正確導入,就會出現頭文件找不到或者是鏈接錯誤:

 

 

LOCAL_WHOLE_STATIC_LIBRARIES := cocos_libpng_static
$(call import-module,cocos2dx/platform/third_party/android/prebuilt/libpng)

 

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