Android編譯系統(Android.mk文件詳解)


【Android-NDK(Native Development Kit)  docs文檔】
NDK提供了一系列的工具,幫助開發者快速開發C(或C++)的動態庫,並能自動將so和java應用一起打包成apk。
 
Android.mk文件是GNU Makefile的一小部分,它用來對Android程序進行編譯。
因為所有的編譯文件都在同一個 GNU MAKE 執行環境中進行執行,而 Android.mk中所有的變量都是全局的。因此,您應盡量少聲明變量,不要認為某些變量在解析過程中不會被定義。
一個Android.mk文件可以編譯多個模塊,每個模塊屬下列類型之一
  1)APK程序
  一般的Android程序,編譯打包生成apk文件
  2)JAVA庫
  java類庫,編譯打包生成jar文件
  3)C\C++應用程序
 可執行的C\C++應用程序
  4)C\C++靜態庫 
編譯生成C\C++靜態庫,並打包成.a文件
  5)C\C++共享庫
編譯生成共享庫(動態鏈接庫),並打包成.so文, 有且只有共享庫才能被安裝/復制到您的應用軟件(APK)包中。
 
  可以在每一個Android.mk file 中定義一個或多個模塊,你也可以在幾個模塊中使用同一個源代碼文件。  編譯系統為你處理許多細節問題。例如,你不需要在你的 Android.mk 中列出頭文件和依賴文件。編譯系統將會為你自動處理這些問題。這也意味着,在升級 NDK 后,你應該得到新的toolchain/platform支持,而且不需要改變你的 Android.mk 文件。
 
  注意,NDK的Anroid.mk語法同公開發布的Android平台開源代碼的Anroid.mk語法很接近,然而編譯系統實現他們的方式卻是不同的,這是故意這樣設計的,可以讓程序開發人員重用外部庫的源代碼更容易。
 
  在描述語法細節之前,咱們來看一個簡單的"hello world"的例子,比如,下面的文件:
 sources/helloworld/helloworld.c
 sources/helloworld/Android.mk
 'helloworld.c'是一個 JNI 共享庫,實現返回"hello world"字符串的原生方法。相應的Android.mk 文件會象下面這樣:
 
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= helloworld
LOCAL_SRC_FILES := helloworld.c
include $(BUILD_SHARED_LIBRARY)

 

  一、解釋一下幾行代碼:
  LOCAL_PATH := $(call my-dir)
  一個Android.mk file首先必須定義好LOCAL_PATH變量。它表示是當前文件的路徑。在這個例子中, 宏函數‘my-dir’,  由編譯系統提供, 用於返回當前路徑(即包含Android.mk file文件的目錄)。
 
  include $(CLEAR_VARS)
  CLEAR_VARS 由編譯系統提供(可以在 android 安裝目錄下的/build/core/config.mk 文件看到其定義,為 CLEAR_VARS:=$(BUILD_SYSTEM)/clear_vars.mk),指定讓GNU MAKEFILE該腳本為你清除許多 LOCAL_XXX 變量 ( 例如 LOCAL_MODULE , LOCAL_SRC_FILES ,LOCAL_STATIC_LIBRARIES,等等…),除 LOCAL_PATH。這也是必要的,因為所有的編譯文件都在同一個 GNU MAKE 執行環境中,所有的變量都是全局的。所以我們需要先清空這些變量(LOCAL_PATH除外)。 又因為LOCAL_PATH總是要求在每個模塊中都要進行設置,所以並不需要清空它。
注意:該語句的意思就是把CLEAR_VARS變量所指向的腳本文件包含進來。
 
  LOCAL_MODULE := helloworld
  LOCAL_MODULE 變量必須定義,以標識你在 Android.mk 文件中描述的每個模塊。名稱必須是唯一的,而且不包含任何空格。注意編譯系統會自動產生合適的前綴和后綴,換句話說,一個被命名為'foo'的共享庫模塊,將會生成'libfoo.so'文件。
注意:如果把庫命名為‘libhelloworld’,編譯系統將不會添加任何的 lib 前綴,也會生成 libhelloworld.so。
 
  LOCAL_SRC_FILES := helloworld.c
  LOCAL_SRC_FILES 變量必須包含將要編譯打包進模塊中的 C 或 C++源代碼文件。不用在這里列出頭文件和包含文件,編譯系統將會自動找出依賴型的文件,當然對於包含文件,你包含時指定的路徑應該正確。
注意:默認的 C++ 源碼文件的擴展名是‘.cpp’ 。指定一個不同的擴展名也是可能的,只要定義LOCAL_DEFAULT_CPP_EXTENSION 變量,不要忘記開始的小圓點(也就是定義為  ‘.cxx’,而不是‘cxx’)
 
  include $(BUILD_SHARED_LIBRARY)
  BUILD_SHARED_LIBRARY 是編譯系統提供的變量,指向一個 GNU Makefile 腳本(應該就是在 build/core  目錄下的 shared_library.mk) ,將根據LOCAL_XXX系列變量中的值,來 編譯生成共享庫(動態鏈接庫)
如果想生成靜態庫,則用BUILD_STATIC_LIBRARY, 在NDK的sources/samples目錄下有更復雜一點的例子,寫有注釋的  Android.mk 文件。
 
二、自定義變量
 以下是在 Android.mk中依賴或定義的變量列表, 可以定義其他變量為自己使用,但是NDK編譯 系統保留下列變量名:
 -- 以 LOCAL_開頭的名字(例如 LOCAL_MODULE)
 -- 以 PRIVATE_, NDK_ 或 APP_開頭的名字(內部使用)
 -- 小寫名字(內部使用,例如‘my-dir’)
 
如果為了方便在 Android.mk 中定義自己的變量,建議使用 MY_前綴,一個小例子:
MY_SOURCES := foo.c
ifneq ($(MY_CONFIG_BAR),)
 MY_SOURCES += bar.c
endif
LOCAL_SRC_FILES += $(MY_SOURCES)

注意:‘:=’是賦值的意思;'+='是追加的意思;‘$’表示引用某變量的值。

 

三、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:  指向編譯腳本,根據所有的LOCAL_XXX 變量把列出的源代碼文件編譯成一個共享庫。
  注意: 必須至少在包含這個文件之前定義 LOCAL_MODULE 和 LOCAL_SRC_FILES。
  (3) BUILD_STATIC_LIBRARY:  一個 BUILD_SHARED_LIBRARY 變量用於編譯一個靜態庫。靜態庫不會復制到APK包中,但是能夠用於編譯共享庫。
示例:include $(BUILD_STATIC_LIBRARY) 
注意: 這將會生成一個名為 lib$(LOCAL_MODULE).a 的文件
  (4)TARGET_ARCH: 目標 CPU平台的名字,  和 android 開放源碼中指定的那樣。如果是arm,表示要生成 ARM 兼容的指令,與 CPU架構的修訂版無關。
  (5)TARGET_PLATFORM: Android.mk 解析的時候,目標 Android 平台的名字.詳情可參考/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
  (6)TARGET_ARCH_ABI:  暫時只支持兩個 value, armeabi 和 armeabi-v7a。在現在的版本中一般把這兩個值簡單的定義為 arm, 通過 android  平台內部對它重定義來獲得更好的匹配。其他的 ABI 將在以后的 NDK 版本中介紹,它們會有不同的名字。注意雖然所有基於ARM的ABI都會把 'TARGET_ARCH'定義成‘arm’, 但是會有不同的‘TARGET_ARCH_ABI’。 
  注:ABI ( application binary interface,應用二進制接口, 縮寫為 ABI. ABI不同於應用程序接口(API),API定義了源代碼和庫之間的接口,因此同樣的代碼可以在支持這個API的任何系統中編譯,然而ABI允許編譯好的目標代碼在使用兼容ABI的系統中無需改動就能運行。)
  (7)  TARGET_ABI:  目標平台和 ABI 的組合,它事實上被定義成$(TARGET_PLATFORM)-$(TARGET_ARCH_ABI)  ,在想要在真實的設備中針對一個特別的目標系統進行測試時,會有用。在默認的情況下,它會是'android-3-arm'。
 
五、模塊描述變量
  下面的變量用於向編譯系統描述你的模塊。你應該定義在'include  $(CLEAR_VARS)'和'include $(BUILD_XXXXX)'之間。正如前面描寫的那樣,$(CLEAR_VARS)是一個腳本,清除所有這些變量。
  (1) LOCAL_PATH:  這個變量用於給出當前文件的路徑。必須在 Android.mk 的開頭定義,可以這樣使用:LOCAL_PATH := $(call my-dir)  這個變量不會被$(CLEAR_VARS)清除,因此每個 Android.mk 只需要定義一次(即使在一個文件中定義了幾個模塊的情況下)。
  (2)LOCAL_MODULE: 這是模塊的名字,它必須是唯一的,而且不能包含空格。必須在包含任一的$(BUILD_XXXX)腳本之前定義它。模塊的名字決定了生成文件的名字。例如, 如果一個共享庫模塊的名字是xxx,那么生成文件的名字就是 libxxx.so。但是,在的 NDK 生成文件中(或者 Android.mk 或者 Application.mk),應該只涉及(引用)有正常名字的其他模塊。
  (3)LOCAL_SRC_FILES:  這是要編譯的源代碼文件列表。只要列出要傳遞給編譯器的文件,因為編譯系統自動計算依賴。 注意源代碼文件名稱都是相對於 LOCAL_PATH的,你可以使用路徑部分,例如:
LOCAL_SRC_FILES := foo.c toto/bar.c\
                   Hello.c

 

 文件之間可以用空格或Tab鍵進行分割,換行請用"\".如果是追加源代碼文件的話,請用LOCAL_SRC_FILES +=
注意:在生成文件中都要使用UNIX風格的斜杠(/). windows風格的反斜杠不會被正確的處理。
注意:可以LOCAL_SRC_FILES := $(call all-subdir-java-files)這種形式來包含local_path目錄下的所有java文件。
  (4) LOCAL_CPP_EXTENSION:  這是一個可選變量, 用來指定C++代碼文件的擴展名,默認是'.cpp',但是可以改變它,比如:
LOCAL_CPP_EXTENSION := .cxx
  (5) LOCAL_C_INCLUDES:  可選變量, 表示頭文件的搜索路徑。默認的頭文件的搜索路徑是LOCAL_PATH目錄。
  示例:LOCAL_C_INCLUDES := sources/foo或LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo
  LOCAL_C_INCLUDES需要在任何包含LOCAL_CFLAGS/LOCAL_CPPFLAGS標志之前進行設置。
  (6)LOCAL_CFLAGS:  可選的編譯器選項,在編譯 C 代碼文件的時候使用。這可能是有用的,指定一個附加的包含路徑(相對於NDK的頂層目錄),宏定義,或者編譯選項。
  注意:不要在 Android.mk 中改變 optimization/debugging 級別,只要在 Application.mk 中指定合適的信息,就會自動地為你處理這個問題,在調試期間,會讓 NDK自動生成有用的數據文件。
  (7)LOCAL_CXXFLAGS:  與 LOCAL_CFLAGS同理,針對 C++源文件。
  (8)LOCAL_CPPFLAGS:  與 LOCAL_CFLAGS同理,但是對 C 和 C++ source files都適用。
  (9)LOCAL_STATIC_LIBRARIES: 表示該模塊需要使用哪些靜態庫,以便在編譯時進行鏈接。
  (10)LOCAL_SHARED_LIBRARIES:  表示模塊在運行時要依賴的共享庫(動態庫),在鏈接時就需要,以便在生成文件時嵌入其相應的信息。
  注意:它不會附加列出的模塊到編譯圖,也就是仍然需要在Application.mk 中把它們添加到程序要求的模塊中。
  (11)LOCAL_LDLIBS:  編譯模塊時要使用的附加的鏈接器選項。這對於使用‘-l’前綴傳遞指定庫的名字是有用的。
例如, LOCAL_LDLIBS := -lz 表示告訴鏈接器生成的模塊要在加載時鏈接到/system/lib/libz.so
  可查看 docs/STABLE-APIS.TXT 獲取使用 NDK發行版能鏈接到的開放的系統庫列表。
  (12) LOCAL_ALLOW_UNDEFINED_SYMBOLS:  默認情況下, 在試圖編譯一個共享庫時,任何未定義的引用將導致一個“未定義的符號”錯誤。這對於在源代碼文件中捕捉錯誤會有很大的幫助。然而,如果因為某些原因,需要不啟動這項檢查,可把這個變量設為‘true’。 注意相應的共享庫可能在運行時加載失敗。(這個一般盡量不要去設為 true)。
  (13) LOCAL_ARM_MODE: 默認情況下, arm目標二進制會以 thumb 的形式生成(16 位),你可以通過設置這個變量為 arm, 如果你希望你的 module 是以 32 位指令的形式。
'arm' (32-bit instructions) mode. E.g.:
LOCAL_ARM_MODE := arm
注意:可以在編譯的時候告訴系統針對某個源碼文件進行特定的類型的編譯
比如,LOCAL_SRC_FILES := foo.c bar.c.arm  這樣就告訴系統總是將 bar.c 以arm的模式編譯。
(14)LOCAL_MODULE_PATH 和 LOCAL_UNSTRIPPED_PATH:在 Android.mk 文件中, 還可以用LOCAL_MODULE_PATH 和LOCAL_UNSTRIPPED_PATH指定最后的目標安裝路徑.
不同的文件系統路徑用以下的宏進行選擇:
  TARGET_ROOT_OUT:表示根文件系統。
   TARGET_OUT:表示 system文件系統。
   TARGET_OUT_DATA:表示 data文件系統。
用法如: LOCAL_MODULE_PATH :=$(TARGET_ROOT_OUT) 
至於LOCAL_MODULE_PATH 和LOCAL_UNSTRIPPED_PATH的區別,暫時還不清楚。
 
七、GNU Make‘功能’宏
GNU Make‘功能’宏,必須通過使用'$(call  )'來調用,調用他們將返回文本化的信息。
(1)my-dir:返回當前 Android.mk 所在的目錄的路徑,相對於 NDK 編譯系統的頂層。這是有用的,在 Android.mk 文件的開頭如此定義:
LOCAL_PATH := $(call my-dir)
(2)all-subdir-makefiles: 返回一個位於當前'my-dir'路徑的子目錄中的所有Android.mk的列表。
例如,看下面的目錄層次:
sources/foo/Android.mk
sources/foo/lib1/Android.mk
sources/foo/lib2/Android.mk
 如果 sources/foo/Android.mk 包含一行:
include $(call all-subdir-makefiles)
那么它就會自動包含 sources/foo/lib1/Android.mk 和 sources/foo/lib2/Android.mk。
這項功能用於向編譯系統提供深層次嵌套的代碼目錄層次。
注意:在默認情況下,NDK 將會只搜索在 sources/*/Android.mk 中的文件。
(3)this-makefile:  返回當前Makefile 的路徑(即這個函數調用的地方)
(4)parent-makefile:  返回調用樹中父 Makefile 路徑。即包含當前Makefile的Makefile 路徑。
(5)grand-parent-makefile:返回調用樹中父Makefile的父Makefile的路徑
 
八、 Android.mk 使用模板
  在一個 Android.mk 中可以生成多個APK應用程序,JAVA庫,C\C++可執行程序,C\C++動態庫和C\C++靜態庫。
(1)編譯APK應用程序模板。
關於編譯APK應用程序的模板請參照《Android.mk編譯APK范例》
(2)編譯JAVA庫模板
  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)
  # Build all java files in the java subdirectory
  LOCAL_SRC_FILES := $(call all-subdir-java-files)
  # Any libraries that this library depends on
  LOCAL_JAVA_LIBRARIES := android.test.runner
  # The name of the jar file to create
  LOCAL_MODULE := sample
  # Build a static jar file.
  include $(BUILD_STATIC_JAVA_LIBRARY)

  注:LOCAL_JAVA_LIBRARIES := android.test.runner表示生成的JAVA庫的jar文件名

(3)編譯C/C++應用程序模板如下:
LOCAL_PATH := $(call my-dir)
#include $(CLEAR_VARS)
LOCAL_SRC_FILES := main.c
LOCAL_MODULE := test_exe
#LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
include $(BUILD_EXECUTABLE)

注:‘:=’是賦值的意思,'+='是追加的意思,‘$’表示引用某變量的值

LOCAL_SRC_FILES中加入源文件路徑,LOCAL_C_INCLUDES中加入需要的頭文件搜索路徑
LOCAL_STATIC_LIBRARIES 加入所需要鏈接的靜態庫(*.a)的名稱,
LOCAL_SHARED_LIBRARIES 中加入所需要鏈接的動態庫(*.so)的名稱,
LOCAL_MODULE表示模塊最終的名稱, BUILD_EXECUTABLE 表示以一個可執行程序的方式進行編譯
(4)編譯C\C++靜態庫
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
 helloworld.c
LOCAL_MODULE:= libtest_static
 #LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
include $(BUILD_STATIC_LIBRARY)

和上面相似,BUILD_STATIC_LIBRARY 表示編譯一個靜態庫。

(5)編譯C\C++動態庫的模板
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := helloworld.c
LOCAL_MODULE := libtest_shared
TARGET_PRELINK_MODULES := false
#LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
include $(BUILD_SHARED_LIBRARY)

和上面相似,BUILD_SHARED_LIBRARY 表示編譯一個共享庫。

以上三者的生成結果分別在如下目錄中,generic 依具體 target 會變:
out/target/product/generic/obj/APPS
out/target/product/generic/obj/JAVA_LIBRARIES
out/target/product/generic/obj/EXECUTABLE
out/target/product/generic/obj/STATIC_LIBRARY
out/target/product/generic/obj/SHARED_LIBRARY

每個模塊的目標文件夾分別為:

1)APK程序:XXX_intermediates
2)JAVA庫程序:XXX_intermediates
 3)C\C++可執行程序:XXX_intermediates
 4)C\C++靜態庫: XXX_static_intermediates
 5)C\C++動態庫: XXX_shared_intermediates
 
 

LOCAL_NO_MANIFEST

如果你的Package沒有Manifest(AndroidManifest.xml),你可以設置

LOCAL_NO_MANIFEST:=true.

------分隔符,方便下次編輯修改------

If your package doesn't have a manifest (AndroidManifest.xml), then set

LOCAL_NO_MANIFEST:=true.

The common resources package does this.

LOCAL_PACKAGE_NAME

LOCAL_PACKAGE_NAME變量是一個App的名字,例如:Dialer、Contacts等等。它可能在我們使用ant編譯系統編譯App時會發生改變。

LOCAL_PACKAGE_NAME is the name of an app. For example, Dialer, Contacts, etc. This will probably change or go away when we switch to an ant-based build system for the apps.

LOCAL_PATH

LOCAL_PATH := $(call my-dir):每個Android.mk文件都必須以定義LOCAL_PATH變量開始,其目的是為了定位源文件的位置。例如:

LOCAL_PATH := $(my-dir)

my-dir宏函數使用的是MAKEFILE_LIST變量,你必須在include其它任何makefile之前來調用它。另外,考慮到當你include任何子目錄時都要重新設置LOCAL_PATH,你必須在include它們之前設置它。

The directory your Android.mk file is in. You can set it by putting the following as the first line in your Android.mk:

LOCAL_PATH := $(my-dir)

The my-dir macro uses the MAKEFILE_LIST variable, so you must call it before you include any other makefiles. Also, consider that any subdirectories you inlcude might reset LOCAL_PATH, so do your own stuff before you include them. This also means that if you try to write several include lines that reference LOCAL_PATH, it won't work, because those included makefiles might reset LOCAL_PATH.

LOCAL_PREBUILT_EXECUTABLES

LOCAL_PREBUILT_EXECUTABLES預編譯including $(BUILD_PREBUILT)或者$(BUILD_HOST_PREBUILT)時所用,指定需要復制的可執行文件。

When including $(BUILD_PREBUILT) or $(BUILD_HOST_PREBUILT), set these to executables that you want copied. They're located automatically into the right bin directory.

LOCAL_PREBUILT_LIBS

LOCAL_PREBUILT_LIBS變量是在預編譯including $(BUILD_PREBUILT)或者$(BUILD_HOST_PREBUILT)時所用, 指定需要復制的庫.

When including $(BUILD_PREBUILT) or $(BUILD_HOST_PREBUILT), set these to libraries that you want copied. They're located automatically into the right lib directory.

LOCAL_SHARED_LIBRARIES

LOCAL_SHARED_LIBRARIES變量用來列出模塊所需的共享庫的列表,不需要加上.so后綴。例如:

LOCAL_SHARED_LIBRARIES := /

libutils /

libui /

libaudio /

libexpat /

libsgl

------分隔符,方便下次編輯修改------

These are the libraries you directly link against. You don't need to pass transitively included libraries. Specify the name without the suffix:

LOCAL_SHARED_LIBRARIES := \

    libutils \

    libui \

    libaudio \

    libexpat \

    libsgl

LOCAL_SRC_FILES

LOCAL_SRC_FILES變量必須包含一系列將被構建和組合成模塊的C/C++源文件。

注意:不需要列出頭文件或include文件,因為生成系統會為你自動計算出源文件的依賴關系。默認的C++源文件的擴展名是.cpp,但你可以通過定義LOCAL_DEFAULT_EXTENSION來指定一個擴展名。

The build system looks at LOCAL_SRC_FILES to know what source files to compile -- .cpp .c .y .l .java. For lex and yacc files, it knows how to correctly do the intermediate .h and .c/.cpp files automatically. If the files are in a subdirectory of the one containing the Android.mk, prefix them with the directory name:

LOCAL_SRC_FILES := \

    file1.cpp \

    dir/file2.cpp

LOCAL_STATIC_LIBRARIES

LOCAL_STATIC_LIBRARIES變量和LOCAL_SHARED_LIBRARIES類似,用來列出你的模塊中所需的靜態庫的列表,你可以在你的module中包含一些想使用的靜態庫,通常我們使用共享庫,但是有些地方,像在sbin下的可執行程序和主機上的可執行程序我們要使用靜態庫。例如:

LOCAL_STATIC_LIBRARIES := /

libutils /

libtinyxml

------分隔符,方便下次編輯修改------

These are the static libraries that you want to include in your module. Mostly, we use shared libraries, but there are a couple of places, like executables in sbin and host executables where we use static libraries instead.

LOCAL_STATIC_LIBRARIES := \

libutils \

libtinyxml

LOCAL_MODULE

LOCAL_MODULE變量必須定義,用來標識在Android.mk文件中描述的每個模塊。名稱必須是唯一的,而且不包含任何空格。如果有其它moudle中已經定義了該名稱,那么你在編譯時就會報類似這樣的錯誤:

libgl2jni already defined by frameworks/base/opengl/tests/gl2_jni/jni. Stop.

下面就是該錯誤的截圖:

Android <wbr>編譯系統 <wbr>(二)

接下來就是修改你的module的名字了,或者找到跟你重名的module把它干掉,但不建議你那么做,因為有可能會帶來未知的錯誤(你修改了別人的module的名字,而別人不一定知道,當他再編譯或者做其它時,就會出錯)。

LOCAL_MODULE is the name of what's supposed to be generated from your Android.mk. For exmample, for libkjs, the LOCAL_MODULE is "libkjs" (the build system adds the appropriate suffix -- .so .dylib .dll).

注意:編譯系統會自動產生合適的前綴和后綴,例如:

LOCAL_MODULE := screenshot

一個被命名為“screenshot”的共享庫模塊,將會生成“libscreenshot.so”文件。

補充1:變量命名的規范性

如果LOCAL_MODULE變量定義的值可能會被其它module調用時,就要考慮為其變量命名的規范性了。特別是在使用JNI時,既在LOCAL_JNI_SHARED_LIBRARIES變量中定義的值,最好要和LOCAL_MODULE變量定義的值保存一致(具體請參考LOCAL_JNI_SHARED_LIBRARIES變量的使用說明)。

這時的LOCAL_MODULE變量的命名最好以lib開頭,既“libxxx”,例如:

LOCAL_MODULE := libscreenshot

LOCAL_MODULE_PATH

通知編譯系統將module放到其它地方而不是它通常的類型。如果你重寫這個變量,確保你還要再設置LOCAL_UNSTRIPPED_PATH變量的值。如果你忘了設置LOCAL_UNSTRIPPED_PATH變量的值的話,就會報錯。

Instructs the build system to put the module somewhere other than what's normal for its type. If you override this, make sure you also set LOCAL_UNSTRIPPED_PATH if it's an executable or a shared library so the unstripped binary has somewhere to go. An error will occur if you forget to.

LOCAL_WHOLE_STATIC_LIBRARIES

LOCAL_WHOLE_STATIC_LIBRARIES 指定模塊所需要載入的完整靜態庫(這些靜態庫在鏈接是不允許鏈接器刪除其中無用的代碼)。通常這在你想往共享庫中增加一個靜態庫時是非常有用的,共享庫就會接受到靜態庫暴露出的content,例如:

LOCAL_WHOLE_STATIC_LIBRARIES := /

libsqlite3_android

------分隔符,方便下次編輯修改------

These are the static libraries that you want to include in your module without allowing the linker to remove dead code from them. This is mostly useful if you want to add a static library to a shared library and have the static library's content exposed from the shared library.

LOCAL_WHOLE_STATIC_LIBRARIES := \

libsqlite3_android

LOCAL_REQUIRED_MODULES

LOCAL_REQUIRED_MODULES 指定模塊運行所依賴的模塊(模塊安裝時將會同步安裝它所依賴的模塊)

Set LOCAL_REQUIRED_MODULES to any number of whitespace-separated module names, like "libblah" or "Email". If this module is installed, all of the modules that it requires will be installed as well. This can be used to, e.g., ensure that necessary shared libraries or providers are installed when a given app is installed.

LOCAL_PRELINK_MODULE

LOCAL_PRELINK_MODULE變量用來規定是否需要預連接處理(默認需要,用來做動態庫優化)。LOCAL_PRELINK_MODULE只有在編譯.so的時候才會有的選項,主要是通過預鏈接的方式來加快程序啟動和執行的速度,如果在你的代碼(/jni/Android.mk)中有下面一條語句:

LOCAL_PRELINK_MODULE := true

那么你要在build/core/prelink-linux-arm.map中定義你的庫所需要使用的空間,如果不定義或者空間不夠的話,在編譯的時候就會報錯。如下圖所示:

Android <wbr>編譯系統 <wbr>(二)

當在build/core/prelink-linux-arm.map中定義了我們這里使用的libhello-jni.so庫的空間之后,既在該文件中加入一條語句:

libhello-jni.so                   0x99E00000

注意:在prelink-linux-arm.map文件的開頭部分有明確的規定,指定的內存取值范圍分配給不同的部分使用,而我們的App的庫也給指定了一個范圍:

0x90000000 - 0x9FFFFFFF Prelinked App Libraries

重新編譯,就不會再報錯了,下面的截圖中很清晰地看到已經將libhello-jni.so庫預編譯成功了:

Android <wbr>編譯系統 <wbr>(二)

注意:

在給我們的應用庫分配地址空間時,最好以1M為邊界,地址空間大小按照由大到小的降序進行排序。

下面是對於Prelink的說明:

Prelink利用事先鏈接代替運行時鏈接的方法來加速共享庫的加載,它不僅可以加快起動速度,還可以減少部分內存開銷。程序運行時的動態鏈接尤其是重定位(relocation)的開銷對於大型系統來說是很大的。動態鏈接和加載的過程開銷很大,並且在大多數的系統上,函數庫並不會常常被更動,每次程序被執行時所進行的鏈接動作都是完全相同的,對於嵌入式系統來說尤其如此。因此,這一過程可以改在運行時之前就可以預先處理好,即花一些時間利用Prelink工具對動態共享庫和可執行文件進行處理,修改這些二進制文件並加入相應的重定位等信息,節約了本來在程序啟動時的比較耗時的查詢函數地址等工作,這樣可以減少程序啟動的時間,同時也減少了內存的耗用。

Prelink的這種做法當然也有代價的,每次更新動態共享庫時,相關的可執行文件都需要重新執行一遍Prelink才能保證有效,因為新的共享庫中的符號信息、地址等很可能與原來的已經不同了,這就是為什么android framework代碼一改動,這時候就會導致相關的應用程序重新被編譯。

LOCAL_JNI_SHARED_LIBRARIES

LOCAL_JNI_SHARED_LIBRARIES變量主要是用在JNI的編譯中,如果你要在你的Java代碼中引用JNI中的共享庫*.so,此變量就是共享庫的名字。

那么你要注意的一點是:在你的Project根目錄下的Android.mk中要定義此變量用來引用你要使用的JNI中的共享庫*.so。例如:

$(Project)/Android.mk

LOCAL_JNI_SHARED_LIBRARIES := libsanangeles

而在你的jni目錄下的Android.mk中則要定義LOCAL_MODULE變量的值,一定要讓這兩個變量的值相同。假如你沒有這么做,而是像這樣:

$(Project)/jni/Android.mk

LOCAL_MODULE := sanangeles

那么,在編譯的時候就會出現下圖的錯誤:

Android <wbr>編譯系統 <wbr>(二)

這說明在編譯libsanangeles.so找不到其規則,因為在上面的代碼中定義的是sanangeles。重新修改LOCAL_MODULE變量的值:

$(Project)/jni/Android.mk

LOCAL_MODULE := libsanangeles

即可正常編譯。

LOCAL_EXPORT_CFLAGS

定義這個變量用來記錄C/C++編譯器標志集合,並且會被添加到其他任何以LOCAL_STATIC_LIBRARIES和LOCAL_SHARED_LIBRARIES的模塊的LOCAL_CFLAGS定義中。例如:這樣定義"foo"模塊:

foo/Android.mk

include $(CLEAR_VARS)

LOCAL_MODULE :=foo

LOCAL_SRC_FILES :=foo/foo.c

LOCAL_EXPORT_CFLAGS :=-DFOO=1

include $(BUILD_STATIC_LIBRARY)

另一個模塊,叫做"bar",並且依賴於上面的模塊:

bar/Android.mk

include $(CLEAR_VARS)

LOCAL_MODULE :=bar

LOCAL_SRC_FILES :=bar.c

LOCAL_CFLAGS:=-DBAR=2

LOCAL_STATIC_LIBRARIES:=foo

include $(BUILD_SHARED_LIBRARY)

然后,當編譯bar.c的時候,標志"-DFOO=1 -DBAR=2"將被傳遞到編譯器。輸出的標志被添加到模塊的LOCAL_CFLAGS上,所以你可以很容易重寫它們。它們也有傳遞性:如果"zoo"依賴"bar",“bar”依賴"foo",那么"zoo"也將繼承"foo"輸出的所有標志。

最后,當編譯模塊輸出標志的時候,這些標志並不會被使用。在上面的例子中,當編譯foo/foo.c時,-DFOO=1將不會被傳遞給編譯器。

LOCAL_EXPORT_CPPFLAGS

類似LOCAL_EXPORT_CFLAGS,但適用於C++標志。

具體請參考LOCAL_EXPORT_CFLAGS條目。

LOCAL_EXPORT_C_INCLUDES

類似LOCAL_EXPORT_CFLAGS,但是只有C能包含路徑,如果"bar.c"想包含一些由"foo"模塊提供的頭文件的時候這會很有用。

具體請參考LOCAL_EXPORT_CFLAGS條目。

LOCAL_EXPORT_LDLIBS

類似於LOCAL_EXPORT_CFLAGS,但是只用於鏈接標志。注意,引入的鏈接標志將會被追加到模塊的LOCAL_LDLIBS,這是由UNIX連接器的工作方式決定的。

當模塊foo是一個靜態庫的時候並且代碼依賴於系統庫時會很有用的。LOCAL_EXPORT_LDLIBS可以用於輸出依賴,例如:

#Frist build the static library libfoo.a

include $(CLEAR_VARS)

LOCAL_MODULE := foo

LOCAL_SRC_FILES := foo/foo.c

LOCAL_EXPORT_LDLIBS := -llog

include $(BUILD_STATIC_LIBRARY)

#Then build the shared library libbar.so

include $(CLEAR_VARS)

LOCAL_MODULE := bar

LOCAL_SRC_FILES := bar.c

LOCAL_STATIC_LIBRARIES := foo

include $(BUILD_SHARED_LIBRARY)

這里,在連接器命令最后,libbar.so將以”-llog”參數進行編譯來表明它依賴於系統日志庫,因為它依賴於foo。

LOCAL_ALLOW_UNDEFINED_SYMBOLS

默認情況下,當試圖編譯一個共享庫的時候遇到任何未定義的引用都可能導致"未定義符號"(undefined symbol)的錯誤。這在你的源代碼中捕獲bug會很有用。

然而,但是由於某些原因,你需要禁用此檢查的話,設置變量為"true"即可。需要注意的是,相應的共享庫在運行時可能加載失敗。

LOCAL_ARM_MODE

LOCAL_ARM_MODE變量主要是應用與嵌入式產品的編譯系統中,可以指定為arm模式。例如:

LOCAL_ARM_MODE := arm

注意:你需要執行編譯系統為在ARM模式下通過文件的名字增加后綴的方式編譯指定的源文件。例如:

LOCAL_SRC_FILES :=foo.c bar.c.arm

這會告訴編譯系統一直以ARM模式編譯"bar.c",並且通過LOCAL_ARM_MODE的值編譯foo.c。

2.2.2.BUILD_XXX變量

2.2.2.1.BUILD_SHARED_LIBRARY

BUILD_SHARED_LIBRARY:指明要編譯生成動態共享庫。指向一個生成腳本,這個腳本通過LOCAL_XXX變量收集關於組件的信息,並決定如何根據你列出來的源文件生成目標共享庫。

注意:在include這個腳本文件之前你必須至少已經定義了LOCAL_MODULE和LOCAL_SRC_FILES。例如:

include $(BUILD_SHARED_LIBRARY)

注意:這會生成一個名為lib$(LOCAL_MODULE).so的動態庫。

2.2.2.2.BUILD_STATIC_LIBRARY

BUILD_STATIC_LIBRARY與BUILD_SHARED_LIBRARY類似,但用來生成目標靜態庫。靜態庫不會被拷貝至你的project/packages文件夾下,但可用來生成共享庫。

例如:

include $(BUILD_STATIC_LIBRARY)

注意:這會生成一個靜態庫,名叫lib$(LOCAL_MODULE).a的靜態庫。

2.2.2.3.BUILD_PACKAGE

BUILD_PACKAGE變量用於在最好編譯時生成*.apk,例如:

include $(BUILD_STATIC_LIBRARY)

注意:這會生成一個apk安裝包,名字就叫$(LOCAL_MODULE).apk的安裝包。

2.2.3.其它變量

2.2.3.1.CLEAR_VARS

CLEAR_VARS變量是生成系統提供的,它指向一個特殊的GNU Makefile,它將會為你自動清除許多名為LOCAL_XXX的變量(比如:LOCAL_MODULE、LOCAL_SRC_FILES、LOCAL_STATIC_LIBRARIES等),但LOCAL_PATH是例外,它不會被清除。

注意:這些變量的清除是必須的,因為所有的控制文件是在單一的Makefile,執行環境中解析的,在這里所有的變量都是全局的。

2.2.3.2.TARGET_PLATFORM

TARGET_PLATFORM:當解析該Android.mk文件時用它來指定Andoid目標平台的名稱。例如:android-3與Android 1.5相對應。

2.2.4.NDK提供的宏函數

下面是GNU Make的宏函數,必須通過這樣的形式調用:

$(call <function>)

2.2.4.1.my-dir

my-dir:返回放置當前Android.mk的文件夾相對於NDK生成系統根目錄的路徑。可用來在Android.mk的開始處定義LOCAL_PATH的值:

LOCAL_PATH := $(call my-dir)

2.2.4.2.all-subdir-makefiles

all-subdir-makefiles:返回my-dir子目錄下的所有Android.mk。例如:

代碼的結構如下:

sources/foo/Android.mk

sources/foo/lib1/Android.mk

sources/foo/lib2/Android.mk

如果sources/foo/Android.mk里有這樣一行:

include $(call all-subdir-makefiles)

那么,它將會自動地包含sources/foo/lib1/Android.mk和

sources/foo/lib2/Android.mk。這個函數能將深層嵌套的代碼文件夾提供給生成系統。

注意:默認情況下,NDK僅在source/*/Android.mk里尋找文件。

2.2.4.3.this-makefile

this-makefile:返回當前Makefile所在目錄的路徑。

2.2.4.4.parent-makefile

parent-makefile:返回父Makefile所在目錄makefile的路徑。

2.2.4.5.import-module

一個允許你通過名字找到並包含另一個模塊的的Android.mk的功能,例如:

$(call import-module,<name>)

這將會找到通過NDK_MODULE_PATH環境變量引用的模塊<name>的目錄列表,並且將其自動包含到Android.mk中。

3.Application.mk

3.1.作用

Application.mk目的是描述在你的應用程序中所需要的模塊(即靜態庫或動態庫)。

Application.mk文件通常被放置在$PROJECT/jni/Application.mk下,$PROJECT指的是您的項目。另一種方法是將其放在頂層的子目錄下,既$NDK/apps目錄下,例如:

$NDK/apps/<myapp>/Application.mk

<myapp>是一個簡稱,用於描述你的NDK編譯系統的應用程序(這個名字不會生成共享庫或者最終的包),這個方法是Android NDK r4以前的,現在仍然兼容。但是我們強烈建議你使用第一種方法,因為它更簡單並且不用修改NDK安裝樹的目錄。

3.2.詳細說明

下面是Application.mk中定義的幾個變量:

3.2.1.APP_MODULES

APP_MODULES 變量是強制性的,並且會列出所有你所需要的模塊。它不允許用一個空格來分隔其模塊列表,這個模塊名字被定義在Android.mk文件中的LOCAL_MODULE中。

3.2.2.APP_PROJECT_PATH

APP_PROJECT_PATH變量也是強制性的,並且會給出應用程序工程的根目錄一個絕對路徑。這是用來復制或者安裝一個沒有任何版本限制的JNI庫,從而給 APK 生成工具一個詳細的路徑。例如:

\HelloNDK\Application.mk

APP_PROJECT_PATH := $(call my-dir)/project

APP_MODULES := HelloNdk

這里定義了工程路徑為$(call my-dir)/project,而要編譯的模塊則是HelloNdk,這樣編譯系統才會找到我們要編譯的庫和源文件。

3.2.3.APP_CFLAGS

APP_CFLAGS則是當要編譯模塊中有任何C文件或者C++文件的時候,C編譯器的信號就會被發出。這里可以在你的應用中需要這些模塊時,進行編譯的調整,這樣就不許要直接更改Android.mk為文件本身了。

3.2.4.APP_OPTIM

這個變量是可選的,可以定義成兩個值release或者debug,用於修改編譯程序模塊時的優化層級。release模式是默認的,會產生高優化的文件,debug模式會生成不優化的文件,使得調試更容易進行。

注意:調試release和debug文件都是可能的,但是release版在調試節提高的信息很少,一些變量被優化輸出,無法檢查,代碼被重排序,使得跟蹤代碼很困難,堆棧追蹤也不可靠,等等。

3.2.5.APP_CPPFLAGS

當編譯的只有C++源文件的時候,可以通過這個C++編譯器來設置。

注意:在Android NDK-1.5_r1中,這個標志可以應用於C和C++源文件中。並且得到了糾正,以建立完整的與系統相匹配的Android編譯系統。你先可也可以使用APP_CFLAGS來應用於C或者C++源文件中。建議使用APP_CFLAGS。

4.補充

4.1.兩種不同級別的應用apk

目前我所理解是在Android開發中我們會遇到兩種不同級別的應用apk:系統級應用apk和普通級應用apk。

下面分別描述兩種apk:

4.1.1.編譯系統級應用apk

將應用程序的代碼放到源代碼目錄路徑下,然后進行編譯。將編譯生成的*.apk通過adb或者其它方式放到/system/app目錄下即可。

4.1.2.編譯普通級應用apk

應用程序的代碼並沒有放到平台的源代碼目錄下,然后通過編譯生成的*.apk通過adb install的方式放到/data/app目錄下,就是普通級的apk。

5.參考資料

1.

Android ndk r7b for linux/ Android ndk r6b for windows

2.

Android NDK 概覽($(NDK)/doc/OVERVIEW.html)

http://hualang.iteye.com/blog/1135105

3.

NDK使用方法($(NDK)/doc/HOWTO.html)

http://hualang.iteye.com/blog/1136209

4.

Android.mk 文件($(NDK)/doc/ANDROID-MK.html)

http://hualang.iteye.com/blog/1140414

5.

Application.mk 文件($(NDK)/doc/APPLICATION-MK.html)

http://hualang.iteye.com/blog/1149359

6.

Android Building System 總結

http://blog.csdn.net/yili_xie/article/details/5004205

7.

build-system.html

Android 源碼下:platform/build/core/build-system.html

 

轉自:http://blog.sina.com.cn/s/blog_6bdbc71601010v67.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM