Android Makefile编写

一文简单总结Android系统makefile的编写,Mark一下~
Views: 828
3 0
Read Time:2 Minute, 36 Second

当前Android系统中Native原生开发的编译可以有两种方式,一种是借助makefile文件,在Andorid源码内以Android.mk文件命名进行表示,而另外一种则是以Android.bp文件进行编译控制。今天这篇文章主要讲讲使用Android.mk来编写编译配置文件。

通过Android.mk文件,我们可以将我们的源码编译成模块。这个模块可以是static library静态库(.a后缀结尾)、shared library共享库(.so后缀结尾)或者是独立的二进制文件(.bin后缀结尾)。我们可以在一个Android.mk内定义多个模块,也可以在多个模块内引用同一份源码文件。

关于Android.mk编写,比较基础的部分,包括:

1) 获取mk所在文件的路径
LOCAL_PATH :=$(call my-dir)
通常我们会将我们的mk文件放在我们模块源码目录的顶层,这样当我们引用我们的源码文件时可以方便地通过LOCAL_PATH索引到具体的源码文件。在这里,my-dir是由编译系统提供的函数。
2) 清除所有编译相关变量
include $(CLEAR_VARS)
在我们编译前,我们需要通过该命令将编译变量清除,因为有可能在你编译之前有人在同一个编译环境下编译过,其可能设置了不同的编译变量,所以为了保险起见最好在正式开始编译前都清除一下相关变量(主要是LOCAL_开头的这些变量)。
3) 定义编译模块
LOCAL_MODULE := ModuleName
通过该语句可以定义你编译生成模块的名称,需要说明的是,即使你对编译后的文件进行重命名,是无法改变模块名称的。查看真正的模块名称可以使用readelf工具。编译系统会自动为编译后生成的文件设定相应的前缀与后缀。
关于编译模块相关的配置项还包括:LOCAL_MODULE_PATH,可以配置编译后的文件所在的位置。
4) 引入源码文件
LOCAL_SRC_FILES := sourcefiles
通过LOCAL_SRC_FILES可以指定我们模块需要编译的源码文件,在这里一般有两种写法,一种是当源代码文件较少时,可以直接将源码文件一一列出,如下所示:
LOCAL_SRC_FILES :=\
../test1.c \
../test2.c \
….
第二种方法是通过编译系统提供的函数来快速包含所有的源码文件,如下所示:

引用当前文件夹下的所有c文件,“.”代表当前目录

LOCAL_SRC_FILES += $(call all-c-files-under, .)

引用当前文件夹的上层目录下的common文件夹内的所有c文件,“..”代表上层目录

LOCAL_SRC_FILES += $(call all-c-files-under, ../common)

引用当前文件夹下的所有cpp文件

LOCAL_SRC_FILES += $(call all-cpp-files-under, .)
5) 指定编译头文件
LOCAL_C_INCLUDES := pathname,如下所示:

LOCAL_PATH为call my-dir获取的路径

LOCAL_C_INCLUDES := $(LOCAL_PATH)/../common
关于头文件的编译设定,我们还有另外一个相关的设定项,那就是LOCAL_EXPORT_C_INCLUDES,通过该设定项,可以将该模块的头文件进行掏出,如果该模块被其他模块,通过该设置项可以运行其他依赖于该模块的模块直接引用其头文件内的定义。
6) 指定依赖库
如果你的模块依赖于第三方的库,需要在makefile内进行指定,包括:
依赖的静态库:LOCAL_STATIC_LIBRARIES :=
依赖的共享库:LOCAL_SHARED_LIBRARIES :=
7) 设定编译选项
LOCAL_CFLAGS :=
LOCAL_CPPFLAGS :=

以下这些编译选项可以作为编译优化选项

LOCAL_CFLAGS += -Wall -Wextra -Werror -Wno-unused-parameter

除了编译选项外,我们还经常用的就是就传递编译宏,传递编译宏常见的有两种,如你的代码如下:

if defined (MACRO_DEBUG_TEST) 或者 #ifdef MACRO_DEBUG_TEST

……
此时,我们在编译时可以通过LOCAL_CFLAGS+= -DMACRO_DEBUG_TEST来传递该编译宏。

此外还有一种形式,如你的代码中需要编译的时间戳相关信息,代码如下:

#ifndef  TIME_STAMP_DEBUG
#define  TIME_STAMP_DEBUG  ""
#endif

我们在编译时可以通过LOCAL_CFLAGS+= -DTIME_STAMP_DEBUG=\”2020-01-01\”在编译时将2020-01-01这个值传递给TIME_STAMP_DEBUG宏。

关于编译宏,我们还有一些其他的设置项,如:LOCAL_EXPORT_CFLAGS:通过该设置项可以将该模块中定义的宏传递给其他依赖于该模块的模块(相当于在其他模块的makefile中增加LOCAL_CFLAGS选项),例子:

 For example, consider the module 'foo' with the following definition:
 include $(CLEAR_VARS)
 LOCAL_MODULE := foo
 LOCAL_SRC_FILES := foo/foo.c
 LOCAL_EXPORT_CFLAGS := -DFOO=1
 include $(BUILD_STATIC_LIBRARY)
 And another module, named 'bar' that depends on it as:
 include $(CLEAR_VARS)
 LOCAL_MODULE := bar
 LOCAL_SRC_FILES := bar.c
 LOCAL_CFLAGS := -DBAR=2
 LOCAL_STATIC_LIBRARIES := foo
 include $(BUILD_SHARED_LIBRARY)
 Then, the flags '-DFOO=1 -DBAR=2' will be passed to the compiler when
 building bar.c

8)指定编译类型,连接上述所有编译相关设置
include $(BUILD_SHARED_LIBRARY),这一句一定要在整个makefile的结尾。
在这里表明编译为动态库,如果要编译为二进制可执行文件,则使用include $(BUILD_EXECUTABLE),编译为静态库时,则使用include $(BUILD_STATIC_LIBRARY),如果是预编译库,则使用include $(PREBUILT_SHARED_LIBRARY)
9) 使用编译系统内置函数
在Android编译系统中有一些编译系统提供的函数,在mk文件中可以通过call 函数名来实现调用。

如 MY_LOCAL_PATH := $(call my-dir)就是通过call my-dir函数进行获取当前mk所在的文件路径。
还有一些其他函数可以使用,如:

#call all-subdir-makefiles 可以编译当前makefile下的所有子makefile

all-subdir-makefiles

#call this-makefile 返回当前makefile的文件路径

this-makefile

#call parent-makefile 返回当前makefile的父makefile的路径

parent-makefile

拓展:

1.在makefile输出日志信息,可以使用warning或者info或者error。

使用warning:$(warning “here add the debug info”) //可以不用加””号

使用error:$(error “error: this will stop the compile”)//error会停止编译

使用info: $(info $(TARGET_DEVICE) )

2.在makefile中可以调用shell脚本或者调用shell命令,如下所示:

#为test.sh脚本赋予x权限

shell_script=$( LOCAL_PATH)/test.sh
$(shell chmod +x $(shell_script))

#直接调用test.sh脚本

$(shell $(shell_script))
3.在makefile中可以使用ifeq来判断两个参数是否相等,并执行相应逻辑,如:
Ifeq (“isDebug”,”true”)
逻辑块
endif
4.常用函数:makefike中调用函数用法:$( )
一些常用的函数包括:
$(subst ,, ):将text字符串中的from内容替换为to内容,返回替换后的字符串
如:$(subst ee,EE,feet on the street),返回结果为fEEt on the street
$(strip ):将string字符串中开头与结尾的空格去掉,返回新的字符串
$(findstring , ):在in字符串中寻找find内容,如果找到则返回find内容,如找不到则返回空字符串
$(dir ):从文件名序列中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。
$(call function,….):调用函数
$(eval var1:=value):将value赋值给变量var1,如:

$(if $(findstring OK, $(shell_script_result)), \
$(eval result := TRUE), \
$(eval result := FALSE) \
) \

5.编写自己的函数:
以define开头定义自己的函数名,以endef结尾如下例子所示:

获取commitId,通过调用shell脚本获取commitId,以$(commit_id)作为返回值

define get_commit_id
$(strip \
$(eval local_path := $(call my-dir)) \
$(shell chmod +x $(local_path)/test.sh)\
$(eval commit_id := $(shell $(local_path)/ test.sh "GetCommitID")) \
$(warning commit_id:$(ca_evs_commit_id)) \
$(commit_id) \
)
endef
Happy
Happy
100 %
Sad
Sad
0 %
Excited
Excited
0 %
Sleepy
Sleepy
0 %
Angry
Angry
0 %
Surprise
Surprise
0 %
FranzKafka95
FranzKafka95

极客,文学爱好者。如果你也喜欢我,那你大可不必害羞。

Articles: 86

Leave a Reply

Your email address will not be published. Required fields are marked *

en_USEN