Android build system就是編譯系統的意思
在咱們須要向本身編譯的源代碼中增長模塊的時候,須要一些規則,固然這個規則都是相似的。
Android.mk文件解析
讓咱們來看一個 Android.mk 文件的樣子
Java代碼
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE :=Hello
- LOCAL_SRC_FILES := hello.c
- include $(BUILD_SHARED_LIBRARY)
複製代碼
複製代碼
① LOCAL_PATH :=$(call my-dir)
固定寫法, LOCAL_PATH 表示此時位於工程目錄的根目錄中, (call my-dir) 的功能由編譯器提供,被用來返回當前目錄的地址(包含 Android.mk 自己)
② include $(CLEAR_VARS)
固定寫法, CLEAR_VARS 這個變量由編譯系統提供,而且要執行一個 GNU makefile 文件,這個功能會清理掉全部以 LOCAL_ 開頭的內容(好比 LOCAL_MODULE 、 LOCAL_SRC_FILES 等),除了 LOCAL_PATH 。這句話也是必須的,由於若是全部變量都是全局變量的話,全部的可控的編譯文件都須要在一個單獨的 GNU 中被解析並執行
③ LOCAL_MODULE :=Hello
LOCAL_MODLE 變量必須被定義,用來區分 Android.mk 中的每個模塊。文件名必須是惟一的,不能有空格。注意,編譯器會爲你自動加上一些前綴和後綴,來保證文件是一致的。好比:這裏代表一個動態連接庫模塊被命名爲「 Hello 」,可是最後會生成「 libHello.so 」文件。可是在 java 中裝載這個庫的時候還要使用「 Hello 」名稱。
④ LOCAL_SRC_FILES :=hello.c
LOCAL_SRC_FILES 變量必須包含一個 C 和 C++ 源文件的列表,這些會被編譯並聚合到一個模塊中
注意:這裏並不須要列頭文件和被包含的文件,由於編譯系統會自動爲你計算相關的屬性,源代碼的列表會直接傳遞給編譯器
⑤ include $(BUILD_SHARED_LIBRARY)
BUILD_SHARED_LIBRARY 這個變量由系統提供,而且指定給 GNU makefile 的腳本,它能夠收集全部你定義的」include $(CLEAR_VARS)」 中以 LOCAL_ 開頭的變量,而且決定哪些要編譯,哪些應該作的更加準確。咱們一樣可使用 BUILD_STATIC_LIBRARY 來生成一個靜態庫,若是使用 BUILD_STATIC_LIBRARY 編譯系統便會生成一個以「 lib$(LOCAL_MODULE).a 」爲文件名的文件來提供動態庫的調用
⑥ TARGET_ARCH
TARGET_ARCH 是指架構中 CPU 的名字已經被 Android 開源代碼明確指出了,這裏的 arm 包含了任何 ARM-獨立結構的架構,以及每一個獨立的 CPU 版本
⑦ TARGET_PLATFORM
Android 平臺的名字在 Android.mk 文件中被解析,好比 」android-2.3」
⑧ TARGET_ROOT_OUT :表示根文件系統
用法: CAL_MODULE_PATH:=$(TARGET_ROOT_OUT)
⑨ TARGET_OUT: 表示 system 文件系統
⑩ TARGET_OUT_DATA: 表示 data 文件系統
? TARGET_ABI
TARGET_ABI 平臺目標板和 abi 的連接,這裏要定義 $(TARGET_PLATFORM)-$(TARGET_ARCH_ABI) ,它們都很是有用,特變是當你想測試一個具體的系統鏡像在幾個真實設備的時候
下面是 GNU 編譯出來的宏,而且它們都必須使用「 $(call <function>) 」才能返回文字化的信息。
all-subdir-makefiles :返回一個 Android.mk 文件所在位置的列表,以及當前 my-dir 的路徑。
好比: include $(call all-subdir-makefiles)
this-makefile :返回當前 makefile 的路徑(就是哪一個功能被調用了)
parent-makefile :返回 makefile 的包含樹,也就是包含 makefile 的當前文件
Application.mk
要講 C\C++ 編譯爲 so 文件,光有 Android.mk 文件是不行的,還須要 Application.mk 文件。
Application.mk 文件存放的位置是 NDK 工程的根目錄, Application.mk 文件就是用來描述應用程序中所須要的原生的模塊(也就是靜態庫和動態庫),每個 Application.mk 文件都必須放在應用目錄下的子目錄,例如$NDK/apps/HelloNdk/Application.mk ,做爲 GNU makefile 的一部分, Application.mk 文件必需要定義如下部分
一、 APP_MODULES
APP_MODULES 變量是強制性的,而且會列出全部你所須要的模塊(經過 Android.mk )
二、 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 ,這樣編譯系統纔會找到咱們要編譯的庫和原文件
三、 APP_CFLAGS 則是當要編譯模塊中有任何 C 文件的時候, C 編譯器的信號就會被髮出
四、 APP_OPTIM
這個變量是可選的,能夠定義爲發佈版或者測試版
------------------------------------------------------------------------------------------------------------------------
在Android.mk中:
include $(BUILD_EXECUTABLE)表示生成二進制可執行文件
include $(BUILD_STATIC_LIBRARY)表示生成靜態庫.a文件,名字叫作lib<工程名>.a
include $(BUILD_SHARED_LIBRARY)表示生成動態庫.so文件,名字叫作lib<工程名>.so
另外須要注意的是,生成的文件須要放在手機的data/local目錄下,才能夠有執行的權限(未root),若是root了,則會有些目錄是能夠執行,可是某些目錄依然不能執行,固然能夠用umount命令解決。SD卡是沒有執行權限的,因此當你生成的好比二進制可執行文件放到sdcard中時,是沒法運行的。
能夠這樣測試一下哪些是有執行權限,哪些是沒有的:
*將手機插入電腦,並打開USB調試
*在終端輸入adb shell進入
*su(root了的手機)
*mount:能夠看到一大堆的列表,若是對應的目錄的信息中有noexec,說明這個目錄就沒有執行權限,剩下的均可以執行二進制等文件。
Android.mk文件的具體語法參見個人博客:http://hualang.iteye.com/blog/1140414
向Android源代碼中增長模塊主要有以下幾種:
一、增長可執行文件
增長可執行文件,這些可執行文件通常都是由C/C++生成,下面簡單的介紹一些如何向源代碼中生成可執行文件
假設個人源代碼在~/mydroid目錄下
在external/loulijun/test目錄下,建立兩個文件,一個C,一個Android.mk
hello.c
C代碼
- #include <stdio.h>
- int main(void)
- {
- printf("Hello world!\n");
- return 0;
- }
複製代碼
複製代碼
Android.mk
Java代碼
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES :=hello.c
- LOCAL_MODULE_TAGS :=optional
- LOCAL_MODULE :=test
- include $(BUILD_EXECUTABLE)
複製代碼
複製代碼
首先退出到mydroid目錄下,執行
Java代碼
- . build/envsetup.sh
- 或者
- source build/envsetup.sh
複製代碼
複製代碼
進行環境變量的配置
而後進入到test目錄下,執行「mm」(mm表示編譯當前項目),若是想從新執行,能夠"mm -B"
這樣,會在out/target/product/generic/obj/EXECUTABLES/test_intermediates/LINKED/目錄下生成可執行文件test
而後將test文件用adb push test /data/local 到data/local目錄下。
下面開始執行,你能夠在手機中用terminal emulator來執行,也能夠以adb shell後,執行
Java代碼
- ./test
- 顯示:Hello world!
複製代碼
複製代碼
二、增長靜態庫(.a)
Android.mk文件
Java代碼
- LOCAL_PATH :=$(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES := \
- hello.c
- LOCAL_MODULE :=test
- include $(BUILD_STATIC_LIBRARY)
複製代碼
複製代碼
編譯:
mydroid#. build/envsetup.sh
test#mm
生成的結果在out/target/product/generic/obj/STATIC_LIBRARY
目標文件夾{XXX}_static_intermediates下,XXX爲你定義的模塊名稱test
假如這個靜態庫是由hello.c生成的,可是生成的靜態庫是不能直接使用的,而是由動態庫調用它
三、增長動態庫(.so)
編譯動態庫其實能夠用NDK的,那樣生成很是方便,可是有時候仍是須要掌握其餘的方法的
Android.mk
Java代碼
- LOCAL_PATH :=$(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES := \
- hello.c
- LOCAL_MODULE :=test
- include $(BUILD_SHARED_LIBRARY)
複製代碼
複製代碼
編譯的放法都差很少,只不過Android.mk不一樣的是最後一句,若是比較一下就會發現那句話決定了生成的是什麼
不過要想生成動態庫,絕非是這麼簡單的,有時候只須要Android.mk和源文件便可,可是有時候還須要Application.mk文件。Application.mk文件的具體語法很快會在博客中更新
下面是使用 NDK 來生成 .so 文件的,環境是在 ubuntu11.04 下面
一、 下載 Android-NDK-r6 ,將其解壓到 ~/android/android-ndk-r6 目錄下
二、 配置 .bash_profile 文件,加入
NDK=~/android/android-ndk-r6
export NDK
三、 cd $NDK 後,進入 ndk 的目錄,我以它自帶的項目爲例,進入 samples/hello-jni
在終端輸入 $NDK/ndk-build
系統會自動編譯這個文件,並將生成的 libhello-jni.so 文件存放在當前目錄的 libs/armeabi 目錄下
四、 咱們能夠將這個生成的 libhello-jni.so 放在 Android 源代碼的適當的位置便可使用了
下面是相應的文件
hello-jni.c
Java代碼
- #include <string.h>
- #include <jni.h>
- jstring
- Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
- jobject thiz )
- {
- return (*env)->NewStringUTF(env, "Hello from JNI !");
- }
複製代碼
複製代碼
Android.mk 文件
Java代碼
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE := hello-jni
- LOCAL_SRC_FILES := hello-jni.c
- include $(BUILD_SHARED_LIBRARY)
複製代碼
複製代碼
因爲這裏只是使用了一個簡單的 C 文件,因此沒用用到 Application.mk 文件
而後將