Android.mk 快速入门

本文主要介绍Android.mk一些基础概念及用法,在文章的开头,会介绍一些Makefile相关的内容,打一些基础,在阅读过程中,可以根据自己的需求,跳过Makefile相关的,直接阅读Android.mk里面的语法及相关的用法;如果想要深入了解Android.mk里面的东西,建议还是了解一些Makefile相关的东西

通过阅读本文,可以了解Android.mk里面的一些基础语法,以及怎么编译apk/动态库、静态库/加系统签名/预置apk等,基本上可以满足客户对于Android系统的一些预制要求

1.Makefile概述
1.1Makefile 规则

Makefile的规则如下:

target: prerequisites …
command …

target可以是一个目标文件,也可以是执行文件和标签。
prerequisites就是生成target所需要的文件或是目标。
command也就是要达到target这个目标所需要执行的命令。这里没有说“使用生成target所需要执行的命令”,是因为target可能是标签。需要注意的是command前面必须是TAB键,而不是空格,因此喜欢在编辑器里面将TAB键用空格替换的人需要特别小心了。

1.2 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时会结合具体代码进行具体分析

1.3 Makefile 变量

Makefile中变量和函数的展开(除规则命令行中的变量和函数以外),是在make读取makefile文件时进行的,这里的变量包括了使用“=”定义和使用指示符“define”定义的。

变量可以用来代表一个文件名列表、编译选项列表、程序运行的选项参数列表、搜索源文件的目录列表、编译输出的目录列表和所有我们能够想到的事物。

变量名是不包括“:”、“#”、“=”、前置空白和尾空白的任何字符串。

变量名是大小写敏感的。推荐的做法是在对于内部定义定义的一般变量(例如:目标文件列表objects)使用小写方式,而对于一些参数列表(例如:编译选项CFLAGS)采用大写方式。

另外有一些变量名只包含了一个或者很少的几个特殊的字符(符号)。称它们为自动化变量。像“<”、“@”、“?”、“*”等。
自动化变量参考:http://blog.chinaunix.net/uid-28458801-id-3495215.html

1.3.1 变量的引用

Makefile中在对一些简单变量的引用,我们也可以不使用“()”和“{}”来标记变量名,而直接使用“$x”的格式来实现,此种用法仅限于变量名为单字符的情况。另外自动化变量也使用这种格式。对于一般多字符变量的引用必须使用括号标记,否则make将把变量名的首字母作为作为变量而不是整个字符串(“$PATH”在Makefile中实际上是“$(P)ATH”)。这一点和shell中变量的引用方式不同。shell中变量的引用可以是“${xx}”或者“$xx”格式。但在Makefile中多字符变量名的引用只能是“$(xx)”或者“${xx}”格式。

1.3.2 变量的定义

两种风格:递归展开式变量和直接展开式变量。前者在引用的地方是严格的文本替换,后者用:=定义,变量值中对其他量或者函数的引用在定义变量时被展开(对变量进行替换),此风格变量在定义时就完成了对所引用变量和函数的展开,因此不能实现对其后定义变量的引用(前者是能够实现这个功能的)。如:

  CFLAGS := $(include_dirs) -o

“?=”操作符
GNU make中,还有一个被称为条件赋值的赋值操作符“?=”。被称为条件赋值是因为:只有此变量在之前没有赋值的情况下才会对这个变量进行赋值。
“+=”操作符
如果被追加值的变量之前没有定义,那么,“+=”会自动变成“=”,此变量就被定义为一个递归展开式的变量。如果之前存在这个变量定义,那么“+=”就继承之前定义时的变量风格。

1.4 Makefile include指示符

Makefile 中包含其它文件的关键字是“include”,和 C 语言对头文件的包含方式一致。
“include”指示符告诉 make 暂停读取当前的 Makefile,而转去读取“include”指定的一个或者多个文件,完成以后再继续当前 Makefile 的读取。Makefile 中指符“include”书写在独立的一行,其形式如下:
include FILENAMES…
FILENAMES 是 shell 所支持的文件名(可以使用通配符)。

2 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

2.1 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_PATH目录

LOCAL_CFLAGS:
可选的编译器选项,在编译 C 代码文件的时候使用。这可能是有用的,指定一个附加的包含路径(相对于NDK的顶层目录),宏定义,或者编译选项。

LOCAL_LDLIBS:
编译模块时要使用的附加的链接器选项。这对于使用‘-l’前缀传递指定库的名字是有用的。

Android.mk中可以定义多个编译模块,每个编译模块都是以include (CLEARVARS)include ( C L E A R V A R S ) 开 始 , 以 i n c l u d e (BUILD_XXX)结束

2.2 Android.mk 常用示例

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.html

Android 中Makefile的详细分析:
http://blog.chinaunix.net/uid-25838286-id-3204120.html

Makefile中的自动化变量:
http://blog.chinaunix.net/uid-28458801-id-3495215.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值