我们在Android平台写c/c++程序的时候需要用到Android.mk(Makefile),一般用来编译c/c++源码、引用第三方头文件和库,生成程序所需的so文件。下面是一个cocos2d-x游戏的Android.mk(删除了一些重复的东西),一般默认在jni目录下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
|
下面来简单解释一下这几行代码。
#1:需要了解的知识点(LOCAL_PATH、:=和=、call函数、my-dir)。
(1)定义了LOCAL_PATH 变量,下面的一些函数和变量依赖于它,记住:LOCAL_PATH 必须放到所有include $(CLEAR_VARS)之前定义。
(2):=是即时求值,如:
x := foo
y := $(x) bar
x := xyz
在上例中,y的值将会是 foo bar。
=是等整个makefile展开后,再决定变量的值,就是如果x在下文被改了,y的值也会被更新,如:
x = foo
y = $(x) bar
x = xyz
在上例中,y的值将会是 xyz bar ,而不是 foo bar 。
(3)call用于调用其它函数, 参数以逗号分隔,函数原型:
$(call <expression>,<parm1>,<parm2>,<parm3>,…)
当make执行这个函数时,<expression>参数中的变量,如$(1),$(2),$(3)等,会被参数<parm1>,<parm2>,<parm3>依次取代。
Makefile函数调用形式:$(<function> <arguments>)或${<function> <arguments>},<function>就是函数名,<arguments>为函数的参数,参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。函数调用以“$”开头,以圆括号或花括号把函数名和参数括起。call函数的返回值就是<expression>的返回值。
(4)my-dir由编译系统提供,返回 Android.mk 当前所在的路径(包含Android.mk文件的目录)。
#2. 自定义了一个all_cpp_files_recursively函数,递归遍历返回给定目录下所有C++源文件,google一下有很多类似代码。
需要了解的知识点(eval函数、wildcard函数,foreach函数)
(1)eval: 在Makefile中构造一个可变的规则结构关系(依赖关系链),其中可以使用其它变量和函数。函数“eval”对它的参数进行展开,展开的结果作为Makefile的一部分,make可以对展开内容进行语法解析。“eval”函数执行时会对它的参数进行两次展开。第一次展开过程发是由函数本身完成的,第二次是函数展开后的结果被作为Makefile内容时由make解析时展开的。其实就是动态生成Makefile脚本,类似js中的eval(执行字符串形式的js代码),eval函数没有返回值。
(2)wildcard:用来扩展通配符。在Makefile中使用通配符时存在一定的局限性,通配符自动在规则中进行,但当通配符用在变量或者函数定义中时,通配符将失效,make不会将其展开,而是将其作为简单的字符串处理,这种情况下要使通配符有效,则需要使用wildcard函数。
(3) foreach:做循环用的,函数原型是$(foreach <var>,<list>,<text>)
foreach的意思是,把参数<list>中的单词逐一取出放到参数<var> 所指定的变量中,然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,所返回的每个字符串会以空格分隔,当整个循环结束时,把<text>所返回的每个字符串所组成的字符串(以空格分隔)作为foreach函数的返回值。举一个简单的例子:
names := a b c d
files := $(foreach n,$(names),$(n).o)
$(files)的值是“a.o b.o c.o d.o”
(4) $(src_files:$(LOCAL_PATH)/%=%)进行了文本替换,”%”表示一个或多个任意字符,这里是去掉cpp/c文件路径中的$(LOCAL_PATH)部分,因为LOCAL_SRC_FILES文件的路径需要是Android.mk的相对路径。
#3. 自定义了一个all_c_files_recursively函数,递归遍历返回给定目录下所有C源文件。
#4 #5 #6: 声明预编译库,首先需要读取CLEAR_VARS 变量,是编译系统提供的,指向一个GNU Makefile脚本,为你清除一些LOCAL_XXX变量(e.g. LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, etc…),LOCAL_PATH除外,避免相互影响。因为所有的编译控制文件是在同一个GNU Make执行环境解析的,因此所有变量都是全局的。
ps: Android.mk中可以定义多个编译模块,每个编译模块都是以include $(CLEAR_VARS)开始,以include $(BUILD_XXX)结束,每个预编译的库,都必须声明为一个独立的模块 。
PREBUILT_SHARED_LIBRARY(预编译共享库) 和 PREBUILT_STATIC_LIBRARY(预编译静态库) :
Android NDK r5 开始支持预编译库,这样你就可以把预编译库给第三方NDK开发者使用,而不用暴露源码,也可以使用预编译库来加速项目构建。PREBUILT_SHARED_LIBRARY的LOCAL_SRC_FILES必须是预编译共享库文件(eg:t.so文件),PREBUILT_STATIC_LIBRARY必须是预编译静态库文件(eg:t.a文件)。
#11 这里需要注意LOCAL_WHOLE_STATIC_LIBRARIES和LOCAL_STATIC_LIBRARIES的区别:
前者会包含静态库的所有的源代码,后者会允许链接器移除一些dead code(没有使用的变量或函数,比如一些给共享库使用的接口)。
#12 -Xlinker表示它后面的参数是给链接器使用的,–allow-multiple-definition表示允许发生多重定义并且使用第一个定义(跟链接顺序有关)。
#13 读取BUILD_SHARED_LIBRARY变量,把west_shared编译成共享库,是编译系统提供的,指向一个GNU Makefile脚本,它负责收集自从上次调用include $(CLEAR_VARS)之后的定义的LOCAL_XXXX变量的所有信息,决定怎么正确的编译出所需的共享库。
#14 NDK_MODULE_PATH是一个很重要的变量,当android.mk中使用了$(call import-module,XXX)函数引入外部模块文件时会用到,用以指示该往哪里去找这个Android.mk文件。
$(call import-module, 相对路径),如$(call import-module,cocos2dx),cocos2dx是相对路径,绝对路径是NDK_MODULE_PATH/cocos2dx,NDK_MODULE_PATH可以在执行ndk_build时指定。如:
ndk-build V=0 NDK_LOG=0 NDK_DEBUG=1 NDK_MODULE_PATH=/cygdrive/F/west_game/
下面是ndk编译相关命令:
(1). ndk-build NDK_LOG=1
用于配置LOG级别,打印ndk编译时的详细输出信息。
(2). ndk-build NDK_PROJECT_PATH=.
指定NDK编译的代码路径为当前目录,如果不配置,则必须把工程代码放到Android工程的jni目录下
(3). ndk-build APP_BUILD_SCRIPT=./Android.mk
指定NDK编译使用的Android.mk文件 ,默认是$PROJECT/jni/下
(4). ndk-build NDK_APP_APPLICATION_MK=./Application.mk
指定NDK编译使用的application.mk文件 ,默认是$PROJECT/jni/下
(5). ndk-build clean
清除所有编译出来的临时文件和目标文件
(6). ndk-build -B
强制重新编译已经编译完成的代码
(7). ndk-build NDK_DEBUG=1
执行 debug 编译
(8). ndk-build NDK_DEBUG=0
执行 release 编译
(9). ndk-build NDK_OUT=./mydir
指定编译生成的文件的存放位置
(10). ndk-build -C /opt/mydir/
到指定目录编译native代码
可以在Android.mk中打印log或某个变量的值,如打印LOCAL_PATH变量:
$(warning “the value of LOCAL_PATH is $(LOCAL_PATH)”)
如果想要系统的学习Makefile和了解Android.mk,可参考:
Android.mk file syntax specification:
http://www.kandroid.org/ndk/docs/ANDROID-MK.html
跟我一起写Makefile: