官术网_书友最值得收藏!

2.2 各種部件的構建

一般情況下,在使用了envsetup進行環境設置后,可以通過mmm進行編譯一個工程,如下所示:

$ mmm {project_path}

2.2.1 Android.mk的語法

Android.mk是Android工程的管理文件,其作用基本等同于Linux環境中的Makefile。在語法上,Android.mk和普通的Makefile略有不同,主要區別是Android.mk包含一些Android編譯系統公共的宏。

Android.mk中選項參考以下文件路徑:

build/core/config.mk

各個選項的默認值在以下文件中定義:

build/core/base_rules.mk

Android.mk文件只處理從根目錄開始找到的第一個Android.mk文件,如果需要遞歸,需要在當前目錄的Android.mk文件中做如下處理:

# Android.mk文件的最后
include $(call all-makefiles-under,$(LOCAL_PATH))

增加以上依然內容后,如果當前目錄的子目錄中還有Android.mk,也會對其做出處理。

在一個Android.mk中也可以生成多個目標:可執行程序、動態庫、靜態庫或者Android應用程序包。Android.mk文件可以處理多個內容:

include $(CLEAR_VARS)
# 處理第一個內容
include $(CLEAR_VARS)
# 處理第二個內容

2.2.2 各種部件的構建方式

1.可執行程序

可執行程序是Linux標準的ELF格式文件的一種,具有main入口,可以直接作為一個進程執行。在Android.mk中編譯一個可執行程序的模板如下所示:

LOCAL_PATH := $(my-dir)
# Test Exe
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := eng
LOCAL_SRC_FILES:= \
    main.c
LOCAL_MODULE:= test_exe
LOCAL_C_INCLUDES :=
LOCAL_STATIC_LIBRARIES :=
LOCAL_SHARED_LIBRARIES := libc
include $(BUILD_EXECUTABLE)

編譯一個可執行程序,需要在LOCAL_SRC_FILES中加入源文件路徑(相對于當前的Android.mk目錄的路徑),在LOCAL_C_INCLUDES中加入所需要包含的頭文件路徑;在LOCAL_STATIC_LIBRARIES中加入所需要連接的靜態庫(*.a)的名稱;在LOCAL_SHARED_LIBRARIES加入所需要連接的動態庫(*.so)的名稱。

LOCAL_MODULE表示模塊最終的名稱。最后使用include $(BUILD_EXECUTABLE)表示以一個可執行程序的方式進行編譯。在本例中,LOCAL_MODULE被定義為test_exe,因此最終生成可執行程序的名稱是test_exe。

一個可執行程序編譯后生成獨立的目標目錄在out/target/product/中,路徑如下所示:

<TARGET_PRODUCT>/obj/EXECUTABLES/< LOCAL_MODULE >

2.靜態庫

靜態庫,也稱之為歸檔文件,在Linux中擴展名通常為.a。在Android.mk中編譯一個靜態庫(歸檔文件)的模板如下所示:

LOCAL_PATH := $(my-dir)
# Test static lib
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := eng
LOCAL_SRC_FILES:= \
    hello.c
LOCAL_MODULE:= libtest_static
LOCAL_C_INCLUDES :=
LOCAL_STATIC_LIBRARIES :=
LOCAL_SHARED_LIBRARIES := libc
include $(BUILD_STATIC_LIBRARY)

編譯一個靜態庫,基本的內容和編譯可執行程序相似,區別在于使用include$(BUILD_STATIC_LIBRARY) 表示編譯靜態庫。在本例中,LOCAL_MODULE被定義為libtest_static,因此最終生成靜態庫的名稱是libtest_static.a。

一個靜態庫編譯后生成的獨立的目標目錄在out/target/product/中,路徑如下所示:

<TARGET_PRODUCT>/obj/STATIC_LIBRARIES/< LOCAL_MODULE >

3.動態庫

動態庫,也稱之為共享庫,是Linux標準的ELF格式文件的一種,在Linux中擴展名通常為.so。在Android.mk中編譯一個動態庫(共享庫)的模板如下所示:

LOCAL_PATH := $(my-dir)
# Test shared lib
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := eng
LOCAL_SRC_FILES:= \
    hello.c
LOCAL_MODULE:= libtest_shared
TARGET_PRELINK_MODULE := false
LOCAL_C_INCLUDES :=
LOCAL_STATIC_LIBRARIES :=
LOCAL_SHARED_LIBRARIES := libc
include $(BUILD_SHARED_LIBRARY)

編譯一個動態庫,基本的內容和編譯可執行程序、靜態庫相似,區別在于使用include$(BUILD_SHARED_LIBRARY) 表示編譯動態庫。在本例中,LOCAL_MODULE被定義為libtest_shared,因此最終生成動態庫的名稱是libtest_shared.so。

一個動態庫編譯后生成的獨立的目標目錄在out/target/product/中,路徑如下所示:

<TARGET_PRODUCT>/obj/STATIC_LIBRARIES/< LOCAL_MODULE >

4.Android應用程序包

Android應用程序包是一種特殊的文件,通常以apk為擴展名。在Android.mk中編譯一個應用程序包的模板如下所示:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := eng
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := TestApplication
include $(BUILD_PACKAGE)

這里使用BUILD_PACKAGE宏表示編譯apk,而LOCAL_SRC_FILES使用自動查找的方法,將找到所有的Java文件進行編譯。

在源代碼環境中編譯和在SDK中編譯應用程序包略有不同,涉及的目錄主要有以下兩個。

● out/target/common/obj/APPS:通用Java字節碼目錄。

● out/target/product/<TARGET_PRODUCT>/obj/APPS:Android應用包目錄。

每個包在這兩個目錄中均具有名為{LOCAL_PACKAGE_NAME}_intermediates/的獨立目錄。

例如,對于SkeletonApp包的編譯,公共目錄的生成結構如下所示:

out/target/common/obj/APPS/SkeletonApp_intermediates/
|-- classes-full-debug.jar
|-- classes.jar
|-- classes-jarjar.jar
|-- emma_out
|   `-- lib
|      `-- classes-jarjar.jar
|-- noproguard.classes.dex
|-- noproguard.classes.jar
|-- noproguard.classes-with-local.dex
|-- proguard_options
|-- public_resources.xml
`-- src                              [自動生成的Java文件目錄]
  |-- com
  |   `-- example
  |      `-- android
  |          `-- skeletonapp
  |             `-- R.java
  `-- R.stamp

一般都會根據資源目錄res自動生成R.java文件,在具有aidl時還會生成由它自動生成的java源代碼文件。

SkeletonApp的目標目錄out/target/product/<TARGET_PRODUCT>/obj/APPS中為SkeletonApp_intermediates。其中包含3個apk包,如下所示。

● package.apk.unsigned:第一步生成的沒有簽名的apk包。

● package.apk.unaligned:第二步生成具有簽名,但是沒有對齊的apk包。

● package.apk:第三步生成最終的apk包。

在編譯應用程序包的時候,有一個額外的宏可以控制編譯的行為,如下所示:

WITH_DEXPREOPT := true

如果WITH_DEXPREOPT被定義為true,一個應用程序包將由兩個部分組成:一個是不包含Java字節碼(classes.dex)的apk文件,一個是名稱為classes.odex的字節碼文件。兩個文件同時預置在系統中依然可以構成能運行的應用程序。但是,這種生成的結果就不能再進行動態的安裝了。

提示:WITH_DEXPREOPT可以在每個應用程序包的Android.mk文件中定義,也可以全局定義。Android 4.x之前的版本此宏的全局默認沒有打開,Android 4.x中則將此宏全局默認定義為true。

5.各部分內容的生成路徑和安裝

可執行程序、靜態庫(*.a)、動態庫(*.so)和Android應用程序包(*.apk)生成的編譯結果分別放在以下的目錄中。

● out/target/product/<TARGET_PRODUCT>/obj/EXECUTABLES/。

● out/target/product/<TARGET_PRODUCT>/obj/STATIC_LIBRARIES/。

● out/target/product/<TARGET_PRODUCT>/obj/SHARED_LIBRARIES/。

● out/target/product/<TARGET_PRODUCT>/obj/APPS/。

幾種模塊的目標的目錄分別為如下所示。

● 可執行程序:<LOCAL_MODULE>__intermediates。

● 靜態庫:<LOCAL_MODULE>_static_intermediates。

● 動態庫:<LOCAL_MODULE>_shared_intermediates。

● Android應用程序包:<LOCAL_PACKAGE_NAME>_intermediates。

其中<LOCAL_MODULE>和<LOCAL_PACKAGE_NAME>將決定每一個工程私有目錄的名稱。對于可執行程序和動態庫,一般在LINK子目錄中是帶有符號的庫(沒有經過符號剝離)。

編寫Android.mk文件的過程中,有以下兩個注意點。

(1)Android的編譯過程根據擴展名自動識別文件類型,統一加入LOCAL_SRC_FILES中即可。

(2)在編譯過程中LOCAL_SRC_FILES盡量不要使用../source.cpp類型的文件路徑,因為各個源代碼的編譯結果也會放置到相對路徑中,使用這種路徑的文件將會被放置在編譯目錄的上級目錄中。

在編譯過程中,可以編譯目標機的內容,也可以編譯主機的內容。以上的例子是編譯目標機的內容,可執行程序、靜態庫、動態庫和Android應用程序包使用的編譯宏分別如下所示:

include $(BUILD_EXECUTABLE)             # 可執行程序
include $(BUILD_STATIC_LIBRARY)         # 靜態庫
include $(BUILD_SHARED_LIBRARY)         # 動態庫
include $(BUILD_PACKAGE)                # Android應用程序包

編譯主機的內容時,可執行程序、靜態庫、動態庫使用宏分別如下所示:

include $(BUILD_HOST_EXECUTABLE)        # 主機可執行程序
include $(BUILD_HOST_STATIC_LIBRARY)    # 主機靜態庫
include $(BUILD_HOST_SHARED_LIBRARY)    # 主機動態庫

幾種模塊默認的安裝路徑如下所示。

● Android應用程序包:TARGET_OUT/app。

● 動態庫:TARGET_OUT/lib。

● 可執行程序:TARGET_OUT/bin。

● 靜態庫不需要安裝,因此在目標文件系統中沒有靜態庫。

在Android.mk文件中,可以指定最后的目標安裝路徑,使用LOCAL_MODULE_PATH宏來指定模塊的安裝路徑。

增加可以安裝到不同文件系統路徑的方式如下所示:

LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)

不同的文件系統路徑使用以下的宏進行選擇。

TARGET_ROOT_OUT表示根文件系統,路徑為

out/target/product/<TARGET_PRODUCT>/root

TARGET_OUT表示system文件系統,路徑為

out/target/product/<TARGET_PRODUCT>/system

TARGET_OUT_DATA表示data文件系統,路徑為

out/target/product/<TARGET_PRODUCT>/data

如果需要進一步指定子目錄,可以使用這3個宏加上其中子目錄的方式。如果不指定LOCAL_MODULE_PATH,安裝到默認的目錄中。

2.2.3 預編譯內容的安裝

在各個部分的構建過程中,除了編譯各種內容外,有時還需要向目標文件系統復制一些文件,例如運行時配置腳本、資源文件、預制的程序和庫等,也有時需要在目標文件系統中創建子目錄。預編譯內容的安裝有兩種方法,一種是使用命令向文件系統中復制,另一種是使用Android的預編譯模板。

1.使用命令安裝

使用命令的安裝方式,實際上就是在Android.mk中調用主機命令行的程序,執行向文件系統中復制等工作。

在Android.mk中,使用命令行進行目錄創建和安裝的方法如下所示:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
copy_from := \
A.txt \
B.txt
copy_to := $(addprefix $(TARGET_OUT)/txt/,$(copy_from))
$(copy_to) : PRIVATE_MODULE := txt
$(copy_to) : $(TARGET_OUT)/txt/% : $(LOCAL_PATH)/% | $(ACP)
$(transform-prebuilt-to-target)
ALL_PREBUILT = $(copy_to)
DIRS := $(addprefix $(TARGET_OUT)/, \
txt \
$(DIRS):
@echo Directory: $@
@mkdir -p $@

其中執行的echo和mkdir都是標準的Shell命令,$(ACP)是Android中對cp命令的一個封裝(Android cp),執行的也是復制工作。此時,Android.mk將會進行如下的工作。

(1)在system文件系統(TARGET_OUT)中創建目錄txt。

(2)將當前路徑下的A.txt和B.txt文件復制到system/txt目錄中。

2.使用預編譯模板的安裝

Android具有一個特殊的預編譯模板:BUILD_PREBUILT,使用這個模板可以將內容復制到目標系統中,也可以自動建立子目錄。

在Android.mk中,使用預編譯模板進行目錄創建和安裝的方法如下所示:

LOCAL_PATH := $(my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := target.txt
LOCAL_MODULE_CLASS := TEXT
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE_PATH := $(TARGET_OUT)/text
LOCAL_SRC_FILES := source.txt
include $(BUILD_PREBUILT)

此時,LOCAL_SRC_FILES和LOCAL_MODULE兩個變量被賦予了不同的含義,前者表示需要復制的源,后者表示要復制的目標命令。LOCAL_MODULE_PATH依然用于指定目標的路徑。LOCAL_MODULE_CLASS表示模塊的類型,其中可以使用自定義的字符串。

此時,Android.mk將會進行如下的動作:

(1)在system文件系統(TARGET_OUT)中創建目錄txt。

(2)將當前路徑下的source.txt復制為system/txt目錄中的target.txt。

在進行處理的過程中,也會生成模塊的中間目錄,路徑為:

out/target/product/<TARGET_PRODUCT>/obj/text/target.txt_intermediates/

其中將具有target.txt文件,text來自于LOCAL_MODULE_CLASS的定義,實際上表示的是一個在obj目錄中的新目標的類型。

主站蜘蛛池模板: 老河口市| 凯里市| 彭州市| 平顶山市| 德惠市| 铜鼓县| 西藏| 沭阳县| 鄂尔多斯市| 平阳县| 大厂| 广安市| 秀山| 阿城市| 西盟| 青田县| 沙雅县| 南京市| 那曲县| 大宁县| 高安市| 江北区| 绵竹市| 龙陵县| 应城市| 威远县| 丽江市| 盐津县| 九寨沟县| 图木舒克市| 灵璧县| 天台县| 商都县| 隆化县| 新巴尔虎右旗| 新蔡县| 嘉兴市| 布尔津县| 海南省| 高雄县| 和田县|