您的位置:首页 > 移动开发 > Android开发

学习笔记:android.mk 与makefile 的关系

2013-10-10 20:35 316 查看
  makefile文件控制整个工程的编译规则,比如指定需要生成哪些目标文件,指明生成这些目标文件依赖哪些源文件,指明生成的目标文件放在哪个文件夹下等等。而make就是一个命令工具,可以解析makefile文件中的指令的一个命令工具。

  android.mk也是一样的功能,只不过它是android编译环境下的一种特殊的“makefile”文件,
它是经过了android编译系统处理的。所谓android编译系统,就是android顶层目录下的build目录里面的一系列编译控制文件,其实就是一系列makefile文件和 *.mk
文件,这些文件才是编译android系统完整的makefile文件.每个模块里的android.mk只不过是被包含进android编译系统的一小部分而已。经过android编译系统的一大堆处理,android.mk的格式就变得非常简单,且与普通的makefile文件书写格式不一样了,但这有利于为Android增加一个新的Component。

但归根结底,android.mk文件最终还是要被android编译系统层层解析,转化为make命令能够读懂的格式,从而调用gcc编译器进行编译。

先写一个最简单的普通makefile,工作平台:64位Ubuntu系统。

建立一个hello文件夹,假设路径是/home/username/Desktop/hello,在其中新建hello.c文件,内容如下:

#include <stdio.h>

void main ()

{

printf("hello world!\n");

}

再新建一个文本文件命名为Makefile,内容如下:

hello: hello.o

gcc -o hello hello.o

hello.o:hello.c

gcc -c hello.c

$(warning hello in the end!)

clean:

-rm hello.o hello

然后打开terminal进入hello目录运行make命令就可以看到,生成了hello.o和hello两个文件。

可以看到对于普通的makefile文件,格式为

target ... : prerequisites ...
command


这里的command就是 gcc -o hello hello.o gcc -c hello.c -rm hello.o hello 这三个。

可是在android.mk中 是看不到这种编译命令的 那么android模块是在哪里被编译的呢,其实编译命令是有的,只不过是被隐藏在了android编译系统中


先写一个简单的android.mk,进入事先下载好的Android源代码目录,在external目录下新建hello目录,新建hello.c文件,内容和刚才的一样。再新建一个文本文件命名为Android.mk。内容如下:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := hello

LOCAL_SRC_FILES:= hello.c

LOCAL_MODULE_TAGS:= optional

include $(BUILD_SHARED_LIBRARY)

当我们在android源代码目录中的external/hello/目录下运行mm命令时。就会执行mm函数,这个函数定义在android源代码根目录下的build目录中的envsetup.sh脚本文件中,是一段控制脚本。内容如下:

function mm()

{

# If we're sitting in the root of the build tree, just do a

# normal make.

if [ -f build/core/envsetup.mk -a -f Makefile ]; then

make $@

else

# Find the closest Android.mk file.

T=$(gettop)

local M=$(findmakefile)

# Remove the path to top as the makefilepath needs to be relative

local M=`echo $M|sed 's:'$T'/::'`

if [ ! "$T" ]; then

echo "Couldn't locate the top of the tree. Try setting TOP."

elif [ ! "$M" ]; then

echo "Couldn't locate a makefile from the current directory."

else

ONE_SHOT_MAKEFILE=$M make -C $T all_modules $@

fi

fi

}

(这个envsetup.sh脚本需要在运行mm之前手动运行一遍,否则将提示找不到mm命令)

红色部分就开始调用make拉,不过这时候还没有进入到我们hello模块的Android.mk,而是进入到了android顶层目录的makefile,这是由make后面的 -C $T
指定的,因为T的值打印出来就是android顶层目录的路径!

那么什么时候才开始进入模块自己的Android.mk呢?继续往下追踪发现,android顶层目录下的Makefile只有三行:

### DO NOT EDIT THIS FILE ###

include build/core/main.mk

### DO NOT EDIT THIS FILE ###

它只是将另一个main.mk文件的内容包含进来了,这个文件在android顶层目录下的/build/core/目录下,虽然文件后缀为mk,但被makefile文件包含进来后还是按照makefile文件的格式来解析。(这是我推测的,不过应该就是这样)

然后再进入main.mk查看,其中有一段如下:

include $(ONE_SHOT_MAKEFILE)

这个ONE_SHOT_MAKEFILE变量的值就是我们的模块的Android.mk文件的路径。。这个变量的赋值就是在envsetup.sh脚本的mm函数中:ONE_SHOT_MAKEFILE=$M 这个M变量的值就是Android.mk文件的路径。它是在

mm函数里的local M=$(findmakefile)里赋值的。

然后就进入到我们自己写的Android.mk文件,内容如下:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := hello

LOCAL_SRC_FILES:= hello.c

LOCAL_MODULE_TAGS:= optional

include $(BUILD_SHARED_LIBRARY)

前面几行主要就是给几个变量赋值,这些变量是由Android编译系统自己定义的,Android编译系统就是android顶层目录下的/build/目录下的一系列文件。我们的Android.mk文件在被include进来之前,这些变量就已经在别的文件中创建好了。最后一行又include进来一个$(BUILD_SHARED_LIBRARY),这又是一个.mk文件。。BUILD_SHARED_LIBRARY的值为/build/core/shared_library.mk因此
相当于include /build/core/shared_library.mk

这个shared_library.mk是在/build/core/目录下的,它是属于android编译系统的一个文件。

再跟踪这个shared_library.mk
其中有一行:include $(BUILD_SYSTEM)/dynamic_binary.mk,这也是android编译系统的一个文件,位于/build/core/目录下。

进入dynamic_binary.mk文件查看,有一行:include $(BUILD_SYSTEM)/binary.mk,它在同样的目录中,再查看这个文件,这个文件就开始调用gcc编译器准备编译我们的hello模块拉,它怎么知道要编译我们的hello模块呢?编译要依赖哪些源文件呢?前面不是已经在Android.mk中给出了嘛。

在binary.mk中如下一段指令就是调用编译器编译的指令:

cpp_arm_sources := $(patsubst %$(LOCAL_CPP_EXTENSION).arm,%$(LOCAL_CPP_EXTENSION),$(filter %$(LOCAL_CPP_EXTENSION).arm,$(LOCAL_SRC_FILES)))

cpp_arm_objects := $(addprefix $(intermediates)/,$(cpp_arm_sources:$(LOCAL_CPP_EXTENSION)=.o))

cpp_normal_sources := $(filter %$(LOCAL_CPP_EXTENSION),$(LOCAL_SRC_FILES))

cpp_normal_objects := $(addprefix $(intermediates)/,$(cpp_normal_sources:$(LOCAL_CPP_EXTENSION)=.o))

$(cpp_arm_objects): PRIVATE_ARM_MODE := $(arm_objects_mode)

$(cpp_arm_objects): PRIVATE_ARM_CFLAGS := $(arm_objects_cflags)

$(cpp_normal_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)

$(cpp_normal_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)

cpp_objects := $(cpp_arm_objects) $(cpp_normal_objects)

ifneq ($(strip $(cpp_objects)),)

$(cpp_objects): $(intermediates)/%.o: \

$(TOPDIR)$(LOCAL_PATH)/%$(LOCAL_CPP_EXTENSION) \

$(yacc_cpps) $(proto_generated_headers) $(my_compiler_dependencies) \

$(LOCAL_ADDITIONAL_DEPENDENCIES)

$(transform-$(PRIVATE_HOST)cpp-to-o)

-include $(cpp_objects:%.o=%.P)

endif

红色部分就是编译命令,和前面makefile里的

target ... : prerequisites ...
command

其实是一样的格式,只不过这里用了很多变量而已,将这些变量展开就行了。

$(transform-$(PRIVATE_HOST)cpp-to-o)这句其实是调用了/build/core/definitions.mk里的一段命令:查看如下

###########################################################

## Commands for running gcc to compile a C++ file

###########################################################

define transform-cpp-to-o

@mkdir -p $(dir $@)

@echo "target $(PRIVATE_ARM_MODE) C++: $(PRIVATE_MODULE) <= $<"

$(hide) $(PRIVATE_CXX) \

$(addprefix -I , $(PRIVATE_C_INCLUDES)) \

$(addprefix -isystem ,\

$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \

$(filter-out $(PRIVATE_C_INCLUDES), \

$(PRIVATE_TARGET_PROJECT_INCLUDES) \

$(PRIVATE_TARGET_C_INCLUDES)))) \

-c \

$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \

$(PRIVATE_TARGET_GLOBAL_CFLAGS) \

$(PRIVATE_TARGET_GLOBAL_CPPFLAGS) \

$(PRIVATE_ARM_CFLAGS) \

) \

$(PRIVATE_RTTI_FLAG) \

$(PRIVATE_CFLAGS) \

$(PRIVATE_CPPFLAGS) \

$(PRIVATE_DEBUG_CFLAGS) \

$(if $(filter n,$(MEMDECT_LEAK)),,-DMEMDECT_LEAK=1) \

-MD -DQCOM -DGAIA -MF $(patsubst %.o,%.d,$@) -o $@ $<

$(transform-d-to-p)

endef

变量展开后就是标准的调用gcc编译器编译的指令了,很多变量展开后是传递给编译器的参数。至此,在/out/target/product/generic/obj/SHARED_LIBRARIES/hello_intermediates/中,就可以看到刚刚编译生成的hello.o文件拉。

总结:Android.mk只不过是被android编译系统包含的一种文件,需要在android编译系统的支持下解析。本质上android模块在编译时最终还是使用make命令解析一大堆makefile
和*.mk文件(有些文件以.mk为后缀,前面说过应该也是按照makefile文件的规则来解析)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: