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_CXX
  • LOCAL_CFLAGS
  • LOCAL_CPPFLAGS
  • LOCAL_CPP_EXTENSION
  • 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.mkmulti_prebuilt.mk,對應的方法宏是BUILD_PREBUILTBUILD_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_MODULE_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)

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