android 整合第三方靜態庫的編譯方法
最近為java層將一個靜態庫通過jni層封裝成了一個動態庫工他們呼叫,遇到了一些編譯上的疑惑,所以索性將其徹底搞清楚算了,免得以後誤事。
下面的圖片列出了所有相關檔案,可以看到引用靜態庫的檔案是com_xxx.cpp檔案,而.a檔案是放在lib目錄下的libHWRecog.a,而庫提供出來的標頭檔案在include下的兩個.h檔案。
原始碼檔案寫好了之後,首先就是要編譯通過吧,這裡有兩個方案來寫mk檔案:第一種採用類似c/c++的編譯方式:直接指定庫名字;第二種採用android的獨特方式:需要將靜態庫假意生成到out目錄的專用靜態庫目錄下去。
編譯模組命令:
TARGET_PRODUCT=ginwave73_gb ./mk orig -t mm frameworks/base/freestylus/jni/
第一種方式:類似c/c++直接指定的編譯方式
在linux下程式設計時,當使用到了標準或者特定的庫時我們大多使用如下的形式來指定名字或者目錄:
gcc –I<特定標頭檔案路徑> -L<特定庫檔案路徑> -l<特定庫> -l<標準庫> xxx.c –o xxx
如:gcc –I./include –L./lib –lHWrecog –lm –lc {–static} test.c –o test
gcc命令的常用選項見後面附錄A。
那麼在android的編譯過程中,也可以使用類似於這種方式來指定引數,不過在這之前,我們需要了解以下一些編譯變數:
LOCAL_C_INCLUDES
LOCAL_CC
LOCAL_CFLAGS
LOCAL_CPP_EXTENSION
LOCAL_CPPFLAGS
LOCAL_CXX
LOCAL_LDLIBS
LOCAL_LDFLAGS
LOCAL_FORCE_STATIC_EXECUTABLE
這些LOCAL_開頭的變數都是模組編譯內的區域性變數,因為通常在Android.mk開頭都要包含:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
這兩句,特別是include $(CLEAR_VARS)這個會清除上一個模組編譯時候留下的所有LOCAL_變數,以準備給當前模組使用。
LOCAL_C_INCLUDES:額外的C/C++編譯標頭檔案路徑,用LOCAL_PATH表示本檔案所在目錄,像gcc的-I引數。如:LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_CC:另外指定c編譯器,不使用預設的。
LOCAL_CFLAGS:為C編譯器傳遞額外的引數(如巨集定義),舉例:LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1
LOCAL_CPP_EXTENSION:如果你的C++檔案不是以cpp為檔案字尾,你可以通過LOCAL_CPP_EXTENSION指定C++檔案字尾名
如:LOCAL_CPP_EXTENSION := .cc注意統一模組中C++檔案字尾必須保持一致。
LOCAL_CPPFLAGS:傳遞額外的引數給C++編譯器,如:LOCAL_CPPFLAGS += -ffriend-injection -DLIBUTILS_NATIVE
LOCAL_CXX:指定C++編譯器
下面的三個編譯變數都是和ld有關的,所以比較重要:
LOCAL_LDLIBS:故名思議,ldlibs,就是指定那些存在於系統目錄下本模組需要連線的庫。如果某一個庫既有動態庫又有靜態庫,那麼在預設情況下是連結的動態庫而非靜態庫。
如:LOCAL_LDLIBS += -lm –lz –lc -lcutils –lutils –llog …
如果你的Android.mk檔案中只有這麼一行,那麼將會採用動態連結。這個類似於上面用gcc編譯時直接指定庫是一樣的道理。
LOCAL_LDFLAGS:這個編譯變數傳遞給連結器一個一些額外的引數,比如想傳遞而外的庫和庫路徑給ld,或者傳遞給ld linker的一些連結引數,-On,-EL{B}(大小端位元組序),那麼就要加到這個上面,如:
LOCAL_LDFLAGS += -L$(LOCAL_PATH)/lib/ -lHWrecog –EB{EL} –O{n} …
或者直接加上絕對路徑庫的全名:
LOCAL_LDFLAGS += $(LOCAL_PATH)/lib/libHWrecog.a –EB{EL} –O{n}
LOCAL_FORCE_STATIC_EXECUTABLE:如果編譯時候需要連結的動態庫庫存在靜態庫形式,那麼在這個編譯變數等於true的情況下,將會連結到對應的靜態庫而不是動態庫。比如上面列出的libm,libz,libc,libcutils,libutils,liblog等動靜態庫都存在,那麼在該變數被置true的時候,將會連結對應的靜態庫。當然對於本來就是靜態庫的libHWrecog.a來說,該變數值不會影響它是被靜態連結的。所以可以想到這個引數的設定是和前面用gcc編譯時候指定-static引數一樣的效果, 推薦只是編譯特殊ELF檔案才用。
通常這種情況只會在編譯root/sbin目錄下的應用才會用到,應為通常他們執行的時間比較早,檔案系統的其他部分都沒載入,所以動態庫就會連結不上,這個時候靜態連結是最好不過了。不過在android的系統中好像沒有怎麼用到,因為linux一起來就是執行的init程序,就開始引導android系統了。
第二種方法:android的prebuilt方式
利用android的prebuilt機制將靜態庫複製到out目錄下的obj中去,然後在連線的時候就會在對於目錄下去找這個靜態庫,動態庫或者其他可執行檔案,甚至是配置檔案都可以使用這個機制來進行copy。
prebuilt機制簡介
Android提供了Prebuilt編譯方法,兩個檔案prebuilt.mk和multi_prebuilt.mk,對應的方法巨集是BUILD_PREBUILT和 BUILD_MULTI_PREBUILT。
prebuilt.mk就是prebuilt的具體實現,它是針對獨立一個檔案的操作,multi_prebuilt.mk是針對多個檔案的,它對多個檔案進行判斷,然後呼叫prebuilt對獨立一個檔案進行處理。
如果直接用prebuilt.mk的話還是比較麻煩的,得仔細看好需要的巨集,如果使用multi_prebuilt.mk會更方便些,很多它都幫忙處理。實際上這個prebuilt的機制,就是一個copy的過程,目標目錄就是out/.../obj/下的各個目錄。
## prebuilt etc
include $(CLEAR_VARS)
LOCAL_MODULE :=
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE_CLASS :=
LOCAL_MODULE_PATH :=
LOCAL_SRC_FILES :=
include $(BUILD_MULTI_PREBUILT)
##prebuilt so/a
include $(CLEAR_VARS)
LOCAL_PREBUILT_LIBS := *.so/*.a
include $(BUILD_MULTI_PREBUILT)
將其封裝的更簡單的方式是:
$(call add-prebuilt-files, ETC, pv_player.cfg)
它會將pv_player.cfg copy to system/etc下,還可以設定型別:
ETC,APPS,EXECUTABLES,SHARED_LIBRARIES,STATIC_LIBRARIES
add-prebuilt-files的定義是在build/core/definitions.mk下,如下:
###########################################################
## Set up the dependencies for a prebuilt target
## $(call add-prebuilt-file, srcfile, [targetclass])
###########################################################
define add-prebuilt-file
$(eval $(include-prebuilt))
endef
define include-prebuilt
include $$(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
/* 紅色這一句在android2.3上是沒有的,不過如果沒有這一句,這種方式是用不了的,它始終會提示你這個模組沒有定義LOCAL_MODULE_TAGS,提示說你必須定義它再能繼續編譯,optional是所有編譯模式都會編譯的關鍵字。原來在這個函式中包含了CLEAR_VARS ,每次呼叫這個函式都是已經清除乾淨的。所以要使用這種方式,這一句是必須要加上。*/
LOCAL_SRC_FILES := $(1)
LOCAL_BUILT_MODULE_STEM := $(1)
LOCAL_MODULE_SUFFIX := $$(suffix $(1))
LOCAL_MODULE := $$(basename $(1))
LOCAL_MODULE_CLASS := $(2)
include $$(BUILD_PREBUILT)
endef
###########################################################
## do multiple prebuilts
## $(call target class, files ...)
###########################################################
define add-prebuilt-files
$(foreach f,$(2),$(call add-prebuilt-file,$f,$(1)))
endef
下面就是使用這種方式所必須要做的動作了:
1. 在jni/lib目錄下新建Android.mk檔案,內容如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
$(call add-prebuilt-files, STATIC_LIBRARIES, libHWRecog.a)
2. 修改jni下的Android.mk檔案,新增如下兩行:
LOCAL_STATIC_LIBRARIES := libHWRecog
…
include $(LOCAL_PATH)/lib/Android.mk
jni/Android.mk完整檔案見附錄B.
關鍵的編譯變數:
LOCAL_SHARED_LIBRARIES
LOCAL_STATIC_LIBRARIES
他們指定動靜態庫的方式為libxxx,例如:
LOCAL_STATIC_LIBRARIES := libHWRecog
LOCAL_SHARED_LIBRARIES := \
libcutils \
libnativehelper \
libutils \
附錄C為Android.mk中的所有LOCAL_XXX編譯變數。
附錄A:
gcc命令的常用選項
選項 解釋
-ansi 只支援 ANSI 標準的 C 語法。這一選項將禁止 GNU C 的某些特色,
例如 asm 或 typeof 關鍵詞。
-c 只編譯並生成目標檔案。
-DMACRO 以字串“1”定義 MACRO 巨集。
-DMACRO=DEFN 以字串“DEFN”定義 MACRO 巨集。
-E 只執行 C 預編譯器。
-g 生成除錯資訊。GNU 偵錯程式可利用該資訊。
-IDIRECTORY 指定額外的標頭檔案搜尋路徑DIRECTORY。
-LDIRECTORY 指定額外的函式庫搜尋路徑DIRECTORY。
-lLIBRARY 連線時搜尋指定的函式庫LIBRARY。
-m486 針對 486 進行程式碼優化。
-o FILE 生成指定的輸出檔案。用在生成可執行檔案時。
-O0 不進行優化處理。
-O 或 -O1 優化生成程式碼。
-O2 進一步優化。
-O3 比 -O2 更進一步優化,包括 inline 函式。
-shared 生成共享目標檔案。通常用在建立共享庫時。
-static 禁止使用共享連線。
-UMACRO 取消對 MACRO 巨集的定義。
-w 不生成任何警告資訊。
-Wall 生成所有警告資訊。
附錄B:jni/Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
com_ginwave_fs_com_FreeStylusJNI.cpp \
onload.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
$(LOCAL_PATH)/include
LOCAL_STATIC_LIBRARIES := libHWRecog
LOCAL_SHARED_LIBRARIES := \
libcutils \
libnativehelper \
libutils \
#LOCAL_LDFLAGS += $(LOCAL_PATH)/lib/libHWRecog.a -O2
#LOCAL_LDFLAGS += -L$(LOCAL_PATH)/lib/ -lHWRecog -O2
#LOCAL_LDLIBS += -lz -lm -llog
#LOCAL_FORCE_STATIC_EXECUTABLE :=
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := libjni_freestylus
LOCAL_PRELINK_MODULE := false
# build/core/prelink-linux-arm.map
# libgw_Rfid.so 0xA2500000 # [~1M]
LOCAL_MODILE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)
include $(BUILD_SHARED_LIBRARY)
include $(LOCAL_PATH)/lib/Android.mk
///////////////////////////////////////////////////////////////////////////////////////////////
jni/lib/Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
$(call add-prebuilt-files, STATIC_LIBRARIES, libHWRecog.a)
附錄C:
Android編譯系統模組中的LOCAL_XXX變數:
LOCAL_AAPT_FLAGS
LOCAL_ACP_UNAVAILABLE
LOCAL_ADDITIONAL_JAVA_DIR
LOCAL_AIDL_INCLUDES
LOCAL_ALLOW_UNDEFINED_SYMBOLS
LOCAL_ARM_MODE
LOCAL_ASFLAGS
LOCAL_ASSET_DIR
LOCAL_ASSET_FILES 在Android.mk檔案中編譯應用程式(BUILD_PACKAGE)時設定此變數,表示資原始檔,
通常會定義成LOCAL_ASSET_FILES += $(call find-subdir-assets)
LOCAL_BUILT_MODULE_STEM
LOCAL_C_INCLUDES 額外的C/C++編譯標頭檔案路徑,用LOCAL_PATH表示本檔案所在目錄
舉例如下:
LOCAL_C_INCLUDES += extlibs/zlib-1.2.3
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
LOCAL_CC 指定C編譯器
LOCAL_CERTIFICATE 簽名認證
LOCAL_CFLAGS 為C/C++編譯器定義額外的標誌(如巨集定義),舉例:LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1
LOCAL_CLASSPATH
LOCAL_COMPRESS_MODULE_SYMBOLS
LOCAL_COPY_HEADERS install應用程式時需要複製的標頭檔案,必須同時定義LOCAL_COPY_HEADERS_TO
LOCAL_COPY_HEADERS_TO install應用程式時複製標頭檔案的目的路徑
LOCAL_CPP_EXTENSION 如果你的C++檔案不是以cpp為檔案字尾,你可以通過LOCAL_CPP_EXTENSION指定C++檔案字尾名
如:LOCAL_CPP_EXTENSION := .cc
注意統一模組中C++檔案字尾必須保持一致。
LOCAL_CPPFLAGS 傳遞額外的標誌給C++編譯器,如:LOCAL_CPPFLAGS += -ffriend-injection
LOCAL_CXX 指定C++編譯器
LOCAL_DX_FLAGS
LOCAL_EXPORT_PACKAGE_RESOURCES
LOCAL_FORCE_STATIC_EXECUTABLE 如果編譯的可執行程式要進行靜態連結(執行時不依賴於任何動態庫),則設定LOCAL_FORCE_STATIC_EXECUTABLE:=true
目前只有libc有靜態庫形式,這個只有檔案系統中/sbin目錄下的應用程式會用到,這個目錄下的應用程式在執行時通常
檔案系統的其它部分還沒有載入,所以必須進行靜態連結。
LOCAL_GENERATED_SOURCES
LOCAL_INSTRUMENTATION_FOR
LOCAL_INSTRUMENTATION_FOR_PACKAGE_NAME
LOCAL_INTERMEDIATE_SOURCES
LOCAL_INTERMEDIATE_TARGETS
LOCAL_IS_HOST_MODULE
LOCAL_JAR_MANIFEST
LOCAL_JARJAR_RULES
LOCAL_JAVA_LIBRARIES 編譯java應用程式和庫的時候指定包含的java類庫,目前有core和framework兩種
多數情況下定義成:LOCAL_JAVA_LIBRARIES := core framework
注意LOCAL_JAVA_LIBRARIES不是必須的,而且編譯APK時不允許定義(系統會自動新增)
LOCAL_JAVA_RESOURCE_DIRS
LOCAL_JAVA_RESOURCE_FILES
LOCAL_JNI_SHARED_LIBRARIES
LOCAL_LDFLAGS 傳遞額外的引數給聯結器(務必注意引數的順序)
LOCAL_LDLIBS 為可執行程式或者庫的編譯指定額外的庫,指定庫以"-lxxx"格式,舉例:
LOCAL_LDLIBS += -lcurses -lpthread
LOCAL_LDLIBS += -Wl,-z,origin
LOCAL_MODULE 生成的模組的名稱(注意應用程式名稱用LOCAL_PACKAGE_NAME而不是LOCAL_MODULE)
LOCAL_MODULE_PATH 生成模組的路徑
LOCAL_MODULE_STEM
LOCAL_MODULE_TAGS 生成模組的標記
LOCAL_NO_DEFAULT_COMPILER_FLAGS
LOCAL_NO_EMMA_COMPILE
LOCAL_NO_EMMA_INSTRUMENT
LOCAL_NO_STANDARD_LIBRARIES
LOCAL_OVERRIDES_PACKAGES
LOCAL_PACKAGE_NAME APK應用程式的名稱
LOCAL_POST_PROCESS_COMMAND
LOCAL_PREBUILT_EXECUTABLES 預編譯including $(BUILD_PREBUILT)或者$(BUILD_HOST_PREBUILT)時所用,指定需要複製的可執行檔案
LOCAL_PREBUILT_JAVA_LIBRARIES
LOCAL_PREBUILT_LIBS 預編譯including $(BUILD_PREBUILT)或者$(BUILD_HOST_PREBUILT)時所用, 指定需要複製的庫.
LOCAL_PREBUILT_OBJ_FILES
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES
LOCAL_PRELINK_MODULE 是否需要預連線處理(預設需要,用來做動態庫優化)
LOCAL_REQUIRED_MODULES 指定模組執行所依賴的模組(模組安裝時將會同步安裝它所依賴的模組)
LOCAL_RESOURCE_DIR
LOCAL_SDK_VERSION
LOCAL_SHARED_LIBRARIES 可連結動態庫
LOCAL_SRC_FILES 編譯原始檔
LOCAL_STATIC_JAVA_LIBRARIES
LOCAL_STATIC_LIBRARIES 可連結靜態庫
LOCAL_UNINSTALLABLE_MODULE
LOCAL_UNSTRIPPED_PATH
LOCAL_WHOLE_STATIC_LIBRARIES 指定模組所需要載入的完整靜態庫(這些精通庫在連結是不允許連結器刪除其中無用的程式碼)
LOCAL_YACCFLAGS
OVERRIDE_BUILT_MODULE_PATH
參考網址:
android編譯系統 makefile(Android.mk)寫法.txt
Android Build Cookbook
Android編譯C程式碼時連結其他庫檔案的方法(附make file詳解手冊)
Makefile 判斷檔案是否存在
g++ gcc編譯選項
動態庫與靜態庫的原理介紹
android makefile prebuild
Android – 帶有動態庫、靜態庫、jar包的Makefile檔案的編寫
Android NDK使用第三方靜態庫如何配置