1. 程式人生 > >android 整合第三方靜態庫的編譯方法

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使用第三方靜態庫如何配置