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

  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文件的规则来解析)。






  • 13
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值