Android.mk 快速入门
大纲
- Makefile概述
Makefile 规则
Makefile 变量
Makefile include指示符 - Android.mk
mm/mmm 简析
Android.mk 常用变量
Android.mk 模块编译示例
GNU Makefile
Makefile 规则
Makefile的规则如下:
target: prerequisites …
command
…
target可以是一个目标文件,也可以是执行文件和标签。
prerequisites就是生成target所需要的文件或是目标。
command也就是要达到target这个目标所需要执行的命令。这里没有说“使用生成target所需要执行的命令”,是因为target可能是标签。需要注意的是command前面必须是TAB键,而不是空格,因此喜欢在编辑器里面将TAB键用空格替换的人需要特别小心了。
Makefile 示例
我们写程序一般喜欢写helloworld,当我们写了一个c的helloworld之后,我们该如何写Makefile来编译helloworld.c呢?
下面就是编译helloworld的Makefile:
helloworld : helloworld.o
cc -o helloworld helloworld .o
helloworld.o : helloworld.c
cc -c main.c
clean:
rm helloworld helloworl.o
之后我们执行make就可以编译helloworld.c了,执行make clean就可以清除编译结果了(其实就是删除helloworld helloworl.o)。
可能有人问为什么执行make就会生成helloworld呢?这得从make的默认处理说起:make将makefile的第一个target作为作为最终的target,凡是这个规则依赖的规则都将被执行,否则就不会执行。所以在执行make的时候,clean这个规则就没有被执行。
上面的是最简单的makefile,复杂点makefile就开始使用高级点的技巧了,例如使用变量,使用隐式规则,执行shell命令(常见的是字符串处理和文件处理等),这里不打算介绍这些规则,后面在分析android的makefile时会结合具体代码进行具体分析
Makefile 变量
Makefile中变量和函数的展开(除规则命令行中的变量和函数以外),是在make读取makefile文件时进行的,这里的变量包括了使用“=”定义和使用指示符“define”定义的。
变量可以用来代表一个文件名列表、编译选项列表、程序运行的选项参数列表、搜索源文件的目录列表、编译输出的目录列表和所有我们能够想到的事物。
变量名是不包括“:”、“#”、“=”、前置空白和尾空白的任何字符串。
变量名是大小写敏感的。推荐的做法是在对于内部定义定义的一般变量(例如:目标文件列表objects)使用小写方式,而对于一些参数列表(例如:编译选项CFLAGS)采用大写方式。
另外有一些变量名只包含了一个或者很少的几个特殊的字符(符号)。称它们为自动化变量。像“ <”、“ <script id="MathJax-Element-116" type="math/tex"><”、“</script>@”、“ ?”、“ *”等。
自动化变量参考:http://blog.chinaunix.net/uid-28458801-id-3495215.html变量的引用
Makefile中在对一些简单变量的引用,我们也可以不使用“()”和“{}”来标记变量名,而直接使用“$x”的格式来实现,此种用法仅限于变量名为单字符的情况。另外自动化变量也使用这种格式。对于一般多字符变量的引用必须使用括号标记,否则make将把变量名的首字母作为作为变量而不是整个字符串(“$PATH”在Makefile中实际上是“$(P)ATH”)。这一点和shell中变量的引用方式不同。shell中变量的引用可以是“${xx}”或者“$xx”格式。但在Makefile中多字符变量名的引用只能是“$(xx)”或者“${xx}”格式。变量的定义
两种风格:递归展开式变量和直接展开式变量。前者在引用的地方是严格的文本替换,后者用:=定义,变量值中对其他量或者函数的引用在定义变量时被展开(对变量进行替换),此风格变量在定义时就完成了对所引用变量和函数的展开,因此不能实现对其后定义变量的引用(前者是能够实现这个功能的)。如:
CFLAGS := $(include_dirs) -o
“?=”操作符
GNU make中,还有一个被称为条件赋值的赋值操作符“?=”。被称为条件赋值是因为:只有此变量在之前没有赋值的情况下才会对这个变量进行赋值。
“+=”操作符
如果被追加值的变量之前没有定义,那么,“+=”会自动变成“=”,此变量就被定义为一个递归展开式的变量。如果之前存在这个变量定义,那么“+=”就继承之前定义时的变量风格。
Makefile include指示符
Makefile 中包含其它文件的关键字是“include”,和 C 语言对头文件的包含方式一致。
“include”指示符告诉 make 暂停读取当前的 Makefile,而转去读取“include”指定的一个或者多个文件,完成以后再继续当前 Makefile 的读取。Makefile 中指符“include”书写在独立的一行,其形式如下:
include FILENAMES…
FILENAMES 是 shell 所支持的文件名(可以使用通配符)。
Android.mk
Android.mk 简介
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 中定义一个或多个模块,你也可以在几个模块中使用同一个
源代码文件。 编译系统为你处理许多细节问题。
Android.mk – mm/mmm
- mm: Builds all of the modules in the current directory, but not their dependencies.
- mmm: Builds all of the modules in the supplied directories, but not their dependencies.
mm命令是在build/envsetup.sh里面实现,即shell脚本里面的function mm(),会调用函数findmakefile()寻找当前目录下面的Android.mk并解析:
function findmakefile()
{
TOPFILE=build/core/envsetup.mk
local HERE=$PWD
T=
while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
T=`PWD= /bin/pwd`
if [ -f "$T/Android.mk" ]; then
echo $T/Android.mk
\cd $HERE
return
fi
\cd ..
done
\cd $HERE
}
mmm –> 可以参考build/envsetup.sh里面的function mmm();会去以对应的路径匹配Android.mk
Android.mk – 常用变量
LOCAL_PATH:必须位于Android.mk文件的最开始。它是用来定位源文件的位置,$(call my-dir)的作用就是返回当前目录的路径。
LOCAL_PATH := $(call my-dir)
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
LOCAL_MODULE 变量必须定义,以标识你在 Android.mk 文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。注意编译系统会自动产生合适的前缀和后缀(注意应用程序名称用LOCAL_PACKAGE_NAME而不是LOCAL_MODULE)
LOCAL_MODULE_PATH 生成模块的路径
LOCAL_MODULE_TAGS 生成模块的标记
LOCAL_OVERRIDES_PACKAGES 需要用当前模块override哪些模块
LOCAL_SRC_FILES 变量必须包含将要编译打包进模块中的 C 或 C++源代码文件。不用
在这里列出头文件和包含文件,编译系统将会自动找出依赖型的文件,当然对于包含文件,你包含时指定的路径应该正确。
BUILD_SHARED_LIBRARY 是编译系统提供的变量,指向一个 GNU Makefile 脚本(就是 build/core/shared_library.mk) ,将根据LOCAL_XXX系列变量中的值,来编译生成共享库。
如果想生成静态库,则用BUILD_STATIC_LIBRARY
LOCAL_C_INCLUDES: 可选变量,表示头文件的搜索路径。默认的头文件的搜索路径是LOCAL_PATH目录
LOCAL_CFLAGS: 可选的编译器选项,在编译 C 代码文件的时候使用。这可能是有用的,指定一个附加的包含路径(相对于NDK的顶层目录),宏定义,或者编译选项。
LOCAL_LDLIBS: 编译模块时要使用的附加的链接器选项。这对于使用‘-l’前缀传递指定库的名字是有用的。
Android.mk中可以定义多个编译模块,每个编译模块都是以include $(CLEAR_VARS)开始,以include $(BUILD_XXX)结束
Android.mk 编译一个普通APK
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Build all java files in the subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Name of the APK to build-
LOCAL_PACKAGE_NAME := LocalPackage
LOCAL_MODULE_TAGS := optional
# Tell it to build an APK
include $(BUILD_PACKAGE)
Android.mk 编译一个依赖静态Java库的APK
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# List of static libraries to include in the package
LOCAL_STATIC_JAVA_LIBRARIES := static-java-library
# Build all java files in the subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Name of the APK to build-
LOCAL_PACKAGE_NAME := LocalPackage
LOCAL_MODULE_TAGS := optional
# Tell it to build an APK
include $(BUILD_PACKAGE)
Android.mk 编译一个预编译静态Java库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
download:libs/download.jar \
universalimageloader:libs/universalimageloader.jar
include $(BUILD_MULTI_PREBUILT)
Android.mk 编译一个需要用系统签名的APK
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# List of static libraries to include in the package
LOCAL_STATIC_JAVA_LIBRARIES := static-library
# Build all java files in the subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Name of the APK to build-
LOCAL_PACKAGE_NAME := LocalPackage
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := optional
# Tell it to build an APK
include $(BUILD_PACKAGE)
Android.mk 编译一个需要用特定key签名的APK
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# List of static libraries to include in the package
LOCAL_STATIC_JAVA_LIBRARIES := static-library
# Build all java files in the subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Name of the APK to build-
LOCAL_PACKAGE_NAME := LocalPackage
LOCAL_CERTIFICATE := vendor/example/certs/app
LOCAL_MODULE_TAGS := optional
# Tell it to build an APK
include $(BUILD_PACKAGE)
Android.mk 编译一个预编译APK
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Module name should match apk name to be installed.
LOCAL_MODULE := LocalModuleName
LOCAL_SRC_FILES := $(LOCAL_MODULE)$(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_MODULE_TAGS := optional
include $(BUILD_PREBUILT)
Android.mk 编译一个可执行程序
LOCAL_PATH:= $(call my-dir)
common_src_files := \
Mstar_base64.cpp \
eshell.cpp
common_shared_libraries := \
libcutils
common_static_libraries := \
libc \
liblog
include $(CLEAR_VARS)
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_SRC_FILES:= $(common_src_files)
LOCAL_SHARED_LIBRARIES := $(common_shared_libraries)
LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
LOCAL_MODULE:= eshell
LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)
Android.mk 编译一个共享库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := \
libcutils libutils
LOCAL_CERTIFICATE := platform
LOCAL_MODULE := libscifly_security_jni
LOCAL_SRC_FILES := \
scifly_security_SecurityNative.cpp \
Mstar_base64.cpp
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)
Android.mk 编译一个32位的共享库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := \
libcutils libutils
LOCAL_CERTIFICATE := platform
LOCAL_MODULE := libscifly_security_jni
LOCAL_SRC_FILES := \
scifly_security_SecurityNative.cpp \
Mstar_base64.cpp
ifeq ($(TARGET_ARCH), arm64)
LOCAL_32_BIT_ONLY := true
endif
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)
Android.mk 编译一个预编译共享库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_PREBUILT_LIBS := libAuthorizeNative20:libAuthorizeNative20.so
LOCAL_MODULE_TAGS := optional
include $(BUILD_MULTI_PREBUILT)
Android.mk 模板
LOCAL_PATH := $(call my-dir)
define PREBUILT_template
LOCAL_MODULE := $(1)
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $$(LOCAL_MODULE)$(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_REQUIRED_MODULES := $(2)
include $(BUILD_PREBUILT)
endef
define PREBUILT_APP_template
include $(CLEAR_VARS)
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_TAGS := optional
$(call PREBUILT_template, $(1), $(2))
endef
prebuilt_apps := \
GooglePlay \
AnalyzerService
$(foreach app,$(prebuilt_apps), \
$(eval $(call PREBUILT_APP_template, $(app),)))
Android.mk 综合示例1
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := \
$(call all-subdir-java-files) \
$(call all-subdir-Iaidl-files)
LOCAL_JNI_SHARED_LIBRARIES := libjni_sciflyime
ifeq ($(TARGET_ARCH), arm64)
LOCAL_32_BIT_ONLY := true
endif
LOCAL_REQUIRED_MODULES := libjni_sciflyime
LOCAL_PACKAGE_NAME := SciflyIme
LOCAL_CERTIFICATE := platform
LOCAL_OVERRIDES_PACKAGES := MLatinIME MPinyinIME
LOCAL_JAVA_LIBRARIES := \
scifly.android
# Make sure our dictionary file is not compressed, so we can read it with
# a raw file descriptor.
LOCAL_AAPT_FLAGS := -0 .dat
LOCAL_AAPT_FLAGS += -0 .dict
LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE)
MY_PATH := $(LOCAL_PATH)
include $(MY_PATH)/jni/Android.mk
Android.mk 综合示例2
LOCAL_PATH := $(call my-dir)
### shared library
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
android/com_eostek_scifly_ime_pinyin_PinyinDecoderService.cpp \
share/dictbuilder.cpp \
share/dictlist.cpp \
share/dicttrie.cpp \
share/lpicache.cpp \
share/matrixsearch.cpp \
share/mystdlib.cpp \
share/ngram.cpp \
share/pinyinime.cpp \
share/searchutility.cpp \
share/spellingtable.cpp \
share/spellingtrie.cpp \
share/splparser.cpp \
share/userdict.cpp \
share/utf16char.cpp \
share/utf16reader.cpp \
share/sync.cpp
LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)
LOCAL_LDLIBS += -pthread
LOCAL_MODULE := libjni_sciflyime
LOCAL_SHARED_LIBRARIES := libcutils libutils
LOCAL_MODULE_TAGS := optional
ifeq ($(TARGET_ARCH), arm64)
$(info jni --> TARGET_ARCH=arm64)
LOCAL_32_BIT_ONLY := true
endif
include $(BUILD_SHARED_LIBRARY)
参考
Android.mk详解:
http://www.2cto.com/kf/201310/253386.htmlAndroid 中Makefile的详细分析:
http://blog.chinaunix.net/uid-25838286-id-3204120.htmlMakefile中的自动化变量:
http://blog.chinaunix.net/uid-28458801-id-3495215.html