Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android NDK編程淺入深出之--Android.mk

Android NDK編程淺入深出之--Android.mk

編輯:關於Android編程

Android.mk

Android.mk是一個向Android NDK構建系統描述NDK項目的GUN Makefile片段。它是每一個NDK項目的必備組件。構建系統希望它出現在jni子目錄中。下面是hello-jni項目中Android.mk文件的內容。


  1. # Copyright (C) 2009 The Android Open Source Project
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the License);
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an AS IS BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. #
  15. LOCAL_PATH := $(call my-dir)
  16.  
  17. include $(CLEAR_VARS)
  18.  
  19. LOCAL_MODULE := hello-jni
  20. LOCAL_SRC_FILES := hello-jni.c
  21.  
  22. include $(BUILD_SHARED_LIBRARY)

為了更好地理解它的句法,我們逐行分析。因為這個是一個GUN Makefile片段,所以它的句法和其他Makefile是一樣的。每行都包含一個單獨的指令,以“#”開頭的是注釋行,GUN Make 工具不處理它們。根據命名規范,變量名要大寫。

注釋塊後的第一條指令是用來定義LOCAL_PATH變量的。根據Android構建系統的要求,Android.mk文檔必須以LOCAL_PATH變量的定義開頭。


  1. LOCAL_PATH :=$(call my-dir)

Android構建系統利用LOCAL_PATH來定位源文件。因為將該變量設置為硬編碼值並不合適,所以Android構建系統提供了一個名為my-dir的宏功能。通過將該變量設置為my-dir 宏功能的返回值,可以將其放在當前目錄下。

Android構建系統將CLEAR_VARS變量設置為clear-vars.mk片段的位置。包含Makefile片段可以清除除了LOCAL_PATH以外的LOCAL_變量,例如LOCAL_MODULE與LOCAL_SRC_FILES等。


  1. Include $(CLEAR_VARS)

這樣做是因為Android構建系統在單次執行中解析多個構建文件和模塊定義,而LOCAL_是全局變量。清除它們可以避免沖突,每一個原生組件被稱為一個模塊。

LOCAL_MODULE變量用來給這些模塊設定一個唯一的名稱。下面的代碼將該模塊的名稱設為hello-jni:


  1. LOCAL_MODULE := hello-jni

因為模塊名稱也被用於給構建過程所生成的文件命名,所以構建系統給該文件添加了適當的前綴和後綴。本例中,hello-jni模塊會生成一個共享庫文件且構建系統會將它命名為libhello-jni.so。

用LOCAL_SRC_FILES變量定義用來建立和組裝這個模塊的源文件列表。


  1. LOCAL_SRC_FILES := hello-jni.c

這裡,hello-jni模塊只由一個源文件生成,而LOCAL_SRC_FILES變量可以包含用空格分開的多個源文件名。

至此,Android.mk文件中定義的構建系統變量簡單描述了原生項目。編譯和生成實際模塊的構建系統還需要包含合適的構建系統片段,具體需要包含哪些片段取決於想要生成模塊的類型。

1. 構建共享庫

為了建立可供主應用程序使用的模塊,必須將該模塊變成共享庫。Android NDK構建系統將BUILD_SHARED_LIBRARY變量設置成build-shared-library.mk文件的保存位置。該Makefile片段包含了將源文件構建和組裝成共享庫的必要過程:


  1. include $(BUILD_SHARED_LIBRARY)

hello-jni是一個簡單的模塊;然而,除非你的模塊需要特殊處理,否則Android.mk文檔將會包含一模一樣的流程和指令。

 

2. 構建多個共享庫

基於不同的應用程序的體系結構,一個單獨的Android.mk文檔可能產生多個共享庫模塊。如程序:


  1. LOCAL_PATH := $(call my-dir)
  2.  
  3. #
  4. #模塊 1
  5. #
  6. include $(CLEAR_VARS)
  7.  
  8. LOCAL_MODULE := module1
  9. LOCAL_SRC_FILES := module1.c
  10. include $(BUILD_SHARED_LIBRARY)
  11.  
  12. #
  13. #模塊 2
  14. #
  15. include $(CLEAR_VARS)
  16.  
  17. LOCAL_MODULE := module2
  18. LOCAL_SRC_FILES := module2.c
  19.  
  20. include $(BUILD_SHARED_LIBRARY)

在處理完這個Android.mk構建文檔之後,Android NDK構建系統會產生libmodule1.so和libmodule2.so兩個共享庫。

3. 構建靜態庫

Android NDK構建系統也支持靜態庫。實際的Android應用程序並不直接使用靜態庫,並且應用程序包中也不包含靜態庫。靜態庫可以用來構建共享庫。例如,在將第三方代碼添加到現有原生項目中時,不用直接將第三方源代碼包括在原生項目中,而是將第三方代碼編譯成靜態庫然後並入共享庫,如程序:


  1. LOCAL_PATH := $(call my-dir)
  2.  
  3. #
  4. # 第三方AVI庫
  5. #
  6. include $(CLEAR_VARS)
  7.  
  8. LOCAL_MODULE := avilib
  9. LOCAL_SRC_FILES := avilib.c platform_posix.c
  10.  
  11. include $(BUILD_STATIC_LIBRARY)
  12.  
  13. #
  14. # 原生模塊
  15. #
  16. include $(CLEAR_VARS)
  17.  
  18. LOCAL_MODULE := module
  19. LOCAL_SRC_FILES := module.c
  20.  
  21. LOCAL_STATIC_LIBRARIES := avilib
  22.  
  23. include $(BUILD_SHARED_LIBRARY)

在將第三方代碼模塊生成靜態庫之後,共享庫就可以通過將它的模塊名添加到LOCAL_STATIC_LIBRARIES變量中來使用該模塊。

4. 用共享庫共享通用模塊

靜態庫可以保證源代碼模塊化;但是,當靜態庫與共享庫相連時,它就變成了共享庫的一部分。在多個共享庫的情況下,多個共享庫與同一個靜態庫連接時,需要將通用模塊的多個副本與不同共享庫重復相連,這樣就增加了應用程序的大小。在這種情況下,不用構建靜態庫,而是將通用模塊作為共享庫建立起來,而動態連接依賴模塊以便消除重復的副本,如程序:


  1. LOCAL_PATH := $(call my-dir)
  2.  
  3. #
  4. # 第三方AVI庫
  5. #
  6. include $(CLEAR_VARS)
  7.  
  8. LOCAL_MODULE := avilib
  9. LOCAL_SRC_FILES := avilib.c platform_posix.c
  10.  
  11. include $(BUILD_SHARED_LIBRARY)
  12.  
  13. #
  14. # 原生模塊 1
  15. #
  16. include $(CLEAR_VARS)
  17.  
  18. LOCAL_MODULE := module1
  19. LOCAL_SRC_FILES := module1.c
  20.  
  21. LOCAL_SHARED_LIBRARIES := avilib
  22.  
  23. include $(BUILD_SHARED_LIBRARY)
  24.  
  25. #
  26. # 原生模塊 2
  27. #
  28. include $(CLEAR_VARS)
  29.  
  30. LOCAL_MODULE := module2
  31. LOCAL_SRC_FILES := module2.c
  32.  
  33. LOCAL_SHARED_LIBRARIES := avilib
  34.  
  35. include $(BUILD_SHARED_LIBRARY)

5. 在多個NDK項目間共享模塊

同時使用靜態庫和共享庫時,可以在模塊間共享通用模塊。但要說明的是,所有這些模塊必須屬於同一個NDK項目。從R5版本開始,Android NDK也允許在NDK項目間共享和重用模塊。考慮前面講過的示例,可以通過以下步驟在多個NDK項目間共享avilib模塊:

首先,將avilib源代碼移動到NDK項目以外的位置,例如:C:androidshared-modules avilib。為了避免命名沖突,目錄結構也可以包含模塊提供者的名字,例如:C: androidshared-modules ranscodeavilib。

注意:

在Android NDK構建系統中,共享模塊路徑不能包含空格。

作為共享模塊,avilib需要自己的Android.mk文件,如程序:


  1. LOCAL_PATH := $(call my-dir)
  2.  
  3. #
  4. #第三方AVI庫
  5. #
  6. include $(CLEAR_VARS)
  7.  
  8. LOCAL_MODULE := avilib
  9. LOCAL_SRC_FILES := avilib.c platform_posix.c
  10.  
  11. include $(BUILD_SHARED_LIBRARY)

現在,可以將avilib模塊從NDK項目的Android.mk文件中移除。為了使用這個共享模塊,將以transcode/avilib為參數調用函數宏import-module部分添加在構建文檔的末尾。為了避免構建系統的沖突,應該將import-module函數宏調用放在Android.mk文檔的末尾。如程序:


  1. #
  2. # 原生模塊
  3. #
  4. include $(CLEAR_VARS)
  5.  
  6. LOCAL_MODULE := module
  7. LOCAL_SRC_FILES := module.c
  8. LOCAL_SHARED_LIBRARIES := avilib
  9.  
  10. include $(BUILD_SHARED_LIBRARY)
  11.  
  12. $(call import-module,transcode/avilib)

import-module函數宏需要先定位共享模塊,然後再將它導入到NDK項目中。默認情況下,import-module函數宏只搜索/sources目錄。為了搜索c:androidshared-modules目錄,定義一個名為NDK_MODULE_PATH的新環境變量並將它設置成共享模塊的根目錄,例如:c:androidshared-modules。

6. 用Prebuilt庫

使用共享模塊要求有共享模塊的源代碼,Android NDK構建系統簡單地把這些源文件包含在NDK項目中並每次構建它們。自R5版本以後,Android NDK也提供對Prebuilt庫的支持。在下面的情況下,Prebuilt庫是非常有用的:

想在不發布源代碼的情況下將你的模塊發布給他人。

想使用共享模塊的預建版來加速構建過程。

盡管已經被編譯了,但預建模塊仍需要一個Android.mk構建文檔,如程序:


  1. LOCAL_PATH := $(call my-dir)
  2.  
  3. #
  4. # 第三方預構建AVI庫
  5. #
  6. include $(CLEAR_VARS)
  7.  
  8. LOCAL_MODULE := avilib
  9. LOCAL_SRC_FILES := libavilib.so
  10.  
  11. include $(PREBUILT_SHARED_LIBRARY)

LOCAL_SRC_FILES變量指向的不是源文件,而是實際Prebuilt庫相對於LOCAL_PATH的位置。

注意:

Prebuilt庫定義中不包含任何關於該庫所構建的實際機器體系結構的信息。開發人員需要確保Prebuilt庫是為與NDK項目相同的機器體系結構而構建的。

PREBUILT_SHARED_LIBRARY變量指向prebuilt-shared-library.mk Makefile片段。它什麼都沒有構建,但是它將Prebuilt庫復制到了NDK項目的libs目錄下。通過使用PREBUILT_STATIC_LIBRARY變量,靜態庫可以像共享庫一樣被用作Prebuilt庫,NDK項目可以像普通共享庫一樣使用Prebuilt庫了。


  1. ...
  2. LOCAL_SHARED_LIBRARIES :=avilib

7. 構建獨立的可執行文件

在Android平台上使用原生組件的推薦和支持的方法是將它們打包成共享庫。但是,為了方便測試和進行快速原型設計,Android NDK也支持構建獨立的可執行文件。這些獨立的可執行文件是不用打包成APK文件就可以復制到Android設備上的常規Linux應用程序,而且它們可以直接執行,而不通過Java應用程序加載。生成獨立可執行文件需要在Android.mk構建文檔中導入BUILD_EXECUTABLE變量,而不是導入BUILD_SHARED_ LIBRARY變量,如程序:


  1. #
  2. # 獨立的可執行的原生模塊
  3. #
  4. include $(CLEAR_VARS)
  5.  
  6. LOCAL_MODULE := module
  7. LOCAL_SRC_FILES := module.c
  8.  
  9. LOCAL_STATIC_LIBRARIES := avilib
  10.  
  11. include $(BUILD_EXECUTABLE)

BUILD_EXECUTABLE變量指向build-executable.mk Makefile片段,該片段包含了在Android平台上生成獨立可執行文件的必要步驟。獨立可執行文件以與模塊相同的名稱被放在libs/目錄下。盡管放在該目錄下,但在打包階段它並沒有被包含在APK文件中。

8. 其他構建系統變量

除了在前幾節提到的變量之外,Android NDK構建系統還支持其他變量,本節將對這些變量進行簡要說明。

構建系統定義的變量有:

TARGET_ARCH:目標CPU體系結構的名稱,例如arm

TARGET_PLATFORM:目標Android平台的名稱,例如:android-3

TARGET_ARCH_ABI:目標CPU體系結構和ABI的名稱,例如:armeabi-v7a

TARGET_ABI:目標平台和ABI的串聯,例如:android-3-armeabi-v7a

可被定義為模塊說明部分的變量有:

LOCAL_MODULE_FILENAME:可選變量,用來重新定義生成的輸出文件名稱。默認情況下,構建系統使用LOCAL_MODULE的值作為生成的輸出文件名稱,但變量LOCAL_MODULE_ FILENAME可以覆蓋LOCAL_MODULE的值。

LOCAL_CPP_EXTENSION:C++源文件的默認擴展名是.cpp。這個變量可以用來為C++源代碼指定一個或多個文件擴展名。


  1. LOCAL_CPP_ EXTENSION :=.cpp .cxx

LOCAL_CPP_FEATURES:可選變量,用來指明模塊所依賴的具體C++特性,如RTTI、exceptions等。


  1. LOCAL_CPP_FEATURES :=rtti

LOCAL_C_INCLUDES:可選目錄列表,NDK安裝目錄的相對路徑,用來搜索頭文件。


  1. LOCAL_C_INCLUDES :=sources/shared-module
  2. LOCAL_C_INCLUDES :=$(LOCAL_PATH)/include

LOCAL_CFLAGS:一組可選的編譯器標志,在編譯C和C++源文件的時候會被傳送給編譯器。


  1. LOCAL_CFLAGS :=-DNDEBUG -DPORT=1234

LOCAL_CPP_FLAGS:一組可選的編譯標志,在只編譯C++源文件時被傳送給編譯器。

LOCAL_WHOLE_STATIC_LIBRARIES:LOCAL_STATIC_LIBRARIES的變體,用來指明應該被包含在生成的共享庫中的所有靜態庫內容。

(當幾個靜態庫之間有循環依賴時,LOCAL_WHOLE_STATIC_LIBRARIES很有用。)

LOCAL_LDLIBS:鏈接標志的可選列表,當對目標文件進行鏈接以生成輸出文件時該標志將被傳送給鏈接器。它主要用於傳送要進行動態鏈接的系統庫列表。例如:要與Android NDK日志庫鏈接,使用以下代碼:


  1. LOCAL_LDFLAGS :=?llog

LOCAL_ALLOW_UNDEFINED_SYMBOLS:可選參數,它禁止在生成的文件中進行缺失符號檢查。若沒有定義,鏈接器會在符號缺失時生成錯誤信息。

LOCAL_ARM_MODE:可選參數,ARM機器體系結構特有變量,用於指定要生成的ARM二進制類型。默認情況下,構建系統在拇指模式下用16位指令生成,但該變量可以被設置為arm來指定使用32位指令。


  1. LOCAL_ARM_MODE :=arm

該變量改變了整個模塊的構建系統行為;可以用.arm擴展名指定只在arm模式下構建特定文件。


  1. LOCAL_SRC_FILES :=file1.c file2.c.arm

LOCAL_ARM_NEON:可選參數,ARM機器體系結構特有變量,用來指定在源文件中應該使用的ARM高級單指令流多數據流(Single Instruction Multiple Data,SIMD)(a.k.a. NEON)內聯函數。


  1. LOCAL_ARM_NEON :=true

該變量改變了整個模塊的構建系統行為;可以用.neon擴展名指定只構建帶有NEON內聯函數的特定文件。


  1. LOCAL_SRC_FILES :=file1.c file2.c.neon

LOCAL_DISABLE_NO_EXECUTE:可選變量,用來禁用NX Bit安全特性。NX Bit代表Never Execute(永不執行),它是在CPU中使用的一項技術,用來隔離代碼區和存儲區。這樣可以防止惡意軟件通過將它的代碼插入應用程序的存儲區來控制應用程序。


  1. LOCAL_DISABLE_NO_EXECUTE :=true

LOCAL_EXPORT_CFLAGS:該變量記錄一組編譯器標志,這些編譯器標志會被添加到通過變量LOCAL_STATIC_LIBRARIES或LOCAL_SHARED_LIBRARIES使用本模塊的其他模塊的LOCAL_CFLAGS定義中。


  1. LOCAL_MODULE := avilib
  2. ...
  3. LOCAL_EXPORT_CFLAGS := − DENABLE_AUDIO
  4. ...
  5. LOCAL_MODULE := module1
  6. LOCAL_CFLAGS := − DDEBUG
  7. ...
  8. LOCAL_SHARED_LIBRARIES := avilib

編譯器在構建module1時會以-DENABLE_AUDIO –DDEBUG標志執行。

LOCAL_EXPORT_CPPFLAGS:和LOCAL_EXPORT_CLAGS一樣,但是它是C++特定代碼編譯器標志。

LOCAL_EXPORT_LDFLAGS:和LOCAL_EXPORT_CFLAGS一樣,但用作鏈接器標志。

LOCAL_EXPORT_C_INCLUDES:該變量允許記錄路徑集,這些路徑會被添加到通過變量LOCAL_STATIC_LIBRARIES或LOCAL_SHARED_LIBRARIES使用該模塊的LOCAL_C_INCLUDES定義中。

LOCAL_SHORT_COMMANDS:對於有大量資源或獨立的靜態/共享庫的模塊,該變量應該被設置為true。諸如Windows之類的操作系統只允許命令行最多輸入8 191個字符;該變量通過分解構建命令使其長度小於8 191個字符。在較小的模塊中不推薦使用該方法,因為使用它會讓構建過程變慢。

LOCAL_FILTER_ASM:該變量定義了用於過濾來自LOCAL_SRC_FILES變量的裝配文件的應用程序。

9. 其他的構建系統函數宏

本節概括了Android NDK構建系統支持的其他函數宏。

all-subdir-makefiles:返回當前目錄的所有子目錄下的Android.mk構建文件列表。例如,調用以下命令可以將子目錄下的所有Android.mk文件包含在構建過程中:


  1. include $(call all-subdir-makefiles)

this-makefile:返回當前Android.mk構建文件的路徑。

parent-makefile:返回包含當前構建文件的父Android.mk構建文件的路徑。

grand-parent-makefile:和parent-makefile一樣但用於祖父目錄。

10. 定義新變量

開發人員可以定義其他變量來簡化他們的構建文件。以LOCAL_和NDK_前綴開頭的名稱預留給Android NDK構建系統使用。建議開發人員定義的變量以MY_開頭,如程序:


  1. ...
  2. MY_SRC_FILES := avilib.c platform_posix.c
  3. LOCAL_SRC_FILES := $(addprefix avilib/, $(MY_SRC_FILES))
  4. ...

11. 條件操作

Android.mk構建文件也可以包含關於這些變量的條件操作,例如:在每個體系結構中包含一個不同的源文件集,如程序:

 

  1. ...
  2. ifeq ($(TARGET_ARCH),arm)
  3. LOCAL_SRC_FILES + = armonly.c
  4. else
  5. LOCAL_SRC_FILES + = generic.c
  6. endif
  7. ...

    Application.mk

    Application.mk是Android NDK構建系統使用的一個可選構建文件。和Android.mk文件一樣,它也被放在jni目錄下。Application.mk也是一個GUN Makefile片段。它的目的是描述應用程序需要哪些模塊;它也定義所有模塊的通用變量。以下是Application.mk構建文件支持的變量:

    APP_MODULES:默認情況下,Android NDK構建系統構建Android.mk文件聲明的所有模塊。該變量可以覆蓋上述行為並提供一個用空格分開的、需要被構建的模塊列表。

    APP_OPTIM:該變量可以被設置為release或debug以改變生成的二進制文件的優化級別。默認情況下使用的是release模式,並且此時生成的二進制文件被高度優化。該變量可以被設置為debug模式以生成更容易調試的未優化二進制文件。

    APP_CLAGS:該變量列出了一些編譯器標志,在編譯任何模塊的C和C++源文件時這些標志都會被傳給編譯器。

    APP_CPPFLAGS:該變量列出了一些編譯器標志,在編譯任何模塊的C++源文件時這些標志都會被傳給編譯器。

    APP_BUILD_SCRIPT:默認情況下,Android NDK構建系統在項目的jni子目錄下查找Android.mk構建文件。可以用該變量改變上述行為,並使用不同的生成文件。

    APP_ABI:默認情況下,Android NDK構建系統為armeabi ABI生成二進制文件。可以用該變量改變上述行為,並為其他ABI生成二進制文件,例如:

    
    
    1. APP_ABI := mips

    另外,可以設置多個ABI

    
    
    1. APP_ABI := armeabi mips

    為所有支持的ABI生成二進制文件

    
    
    1. APP_ABI := all

    APP_STL:默認情況下,Android NDK構建系統使用最小STL運行庫,也被稱為system庫。可以用該變量選擇不同的STL實現。

    
    
    1. APP_STL :=stlport_shared

    APP_GNUSTL_FORCE_CPP_FEATURES:與LOCAL_CPP_EXTENSIONS變量相似,該變量表明所有模塊都依賴於具體的C++特性,如RTTI、exceptions等。

    APP_SHORT_COMMANDS:與LOCAL_SHORT_COMMANDS變量相似,該變量使得構建系統在有大量源文件的情況下可以在項目中使用更短的命令。

     

     

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved