关于Android.mk,可以很容易找到关于它怎么编写的资料,不过最近在移植一个开源的linux项目,抛弃源工程的configure+make机制放入android源码里面用 mm命令来来编译,总是各种坑,在android源码里面编译要求很高比如 一个int 函数没有return,也会报错,编译的模块需要依赖一个自己私自开发的so库时候,用参数 LOCAL_LDFLAGS来指定,也是种种问题,这些问题比如最终发现多了一个空格等等,甚至于解决了也是不知其然,被迫无奈,来探究一下mm这个命令到底是干了啥:
具体的分析细节不赘述,直接上理论成果吧(请忽略上下文,直接看4.0):
1.0 编译首要的工作都是一个 source build/envsetup.sh 和 lunch,这个source 命令当然是linux的shell所支持的,他的作用就是把后面的 build/envsetup.sh 脚本文件中定义的变量和函数 导出来,可以直接在当前shell上调用。 这其中的函数就有 m mm mmm 和lunch等等,然后我们就可以直接在终端里面敲这些函数。详细分析 参考:
2.0 那么mm函数做了什么? 一句shell命令而已: make -C /home/XXX -f build/core/main.mk all_modules ,就是在前面先定义了几个变量,然后执行make ,所以后续所有的工作,就是makefile的工作,那么实际上还是linux的 make,只是这个makefile,可是相当庞大,可以当做一个project去分析了,完整的具备了变量,函数,条件判断+系统命令。看这个工程需要足够的makefile知识,后续单独总结一篇吧。
3.0 makefile中基本就三种东西:依赖关系、命令规则、变量和函数,它的执行顺序,多线程的,可不是像shell脚本一样顺序执行,而是直接根据目标文件,查找依赖关系,然后执行命令规则生成文件,里面唯一可以执行的,就是命令规则,在这里面可以用shell脚本的命令,可以调用shell里面的语法,至于 makefile中的变量和函数,也只有在这个命令规则中使用的时候才会调用,函数其实和变量用法一样,所以就不要想着突然在Android.mk文件中echo 了,这不是shell,除非echo放在命令规则里面,而且该命令规则会被执行
4.0 终极问题:拿一个 hello_world.c文件编译成一个 test_hello库来看,编写Android.mk文件,最终怎么 用什么路径的gcc把这个.c文件怎么个编译连接成一个 so库并且放到什么目录下面了?注:个人的android源码 工程 为7.0
使用Android.mk文件:
...
LOCAL_SRC_FILES:= hello.c
LOCAL_MODULE:= libhello_test23
include $(BUILD_SHARED_LIBRARY)
...#呵呵,就是不完整
在shared_library_internal.mk文件中,有这么一条规则
(找到这个地方可是花费了不少时间,开始当然是从源头而下,结果在makefile众多的变量嵌套里面一下子就跟丢了线索,就开始另谋出路,在build目录下grep“:”,企图逐一查看所有的命令规则,结果太多太多,想到Android.mk中的句法include $(BUILD_SHARED_LIBRARY),通过$(warning "BUILD_SHARED_LIBRARY $(BUILD_SHARED_LIBRARY)") 查看了下build/core/shared_library.mk 这个文件,没有什么线索,同样的还有两个句法LOCAL_LDFLAGS和LOCAL_CFLAGS,果然,顺着这两个变量的调用找到了想要的结果) 两个echo是我加上去跟踪信息的(notepad++里面设置把所有的符号都显示出来,比如看到上面的空格和tab,最后一个没有续航符号,下一行就是以tab开头,这就是一个典型的 make的命令规则)
这个transform-o-to-shared-lib, 是一个预定义的函数,看名字也能知道它的作用了,在definitions.mk文件中还有很多这样的定义
比如把.c文件编译成.o文件的函数,如上图。要查看这些函数具体做了什么,直接在里面echo
比如刚刚$(transform-o-to-shared-lib) 这个函数:
define transform-o-to-shared-lib
@echo "target SharedLib: $(PRIVATE_MODULE) ($@)"
@mkdir -p $(dir $@)
$(transform-o-to-shared-lib-inner)
endef==>define transform-o-to-shared-lib-inner
echo "wang PRIVATE_CXX:$(PRIVATE_CXX)" #当然这个又是我加进去的
$(hide) $(PRIVATE_CXX) \
-nostdlib -Wl,-soname,$(notdir $@) \
-Wl,--gc-sections \
$(if $(filter true,$(PRIVATE_CLANG)),-shared,-Wl$(comma)-shared) \
$(PRIVATE_TARGET_GLOBAL_LD_DIRS) \
$(PRIVATE_TARGET_CRTBEGIN_SO_O) \
$(PRIVATE_ALL_OBJECTS) \
-Wl,--whole-archive \
$(call normalize-target-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \
-Wl,--no-whole-archive \
$(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--start-group) \
$(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
$(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--end-group) \
$(if $(filter true,$(NATIVE_COVERAGE)),$(PRIVATE_TARGET_COVERAGE_LIB)) \
$(PRIVATE_TARGET_LIBATOMIC) \
$(PRIVATE_TARGET_LIBGCC) \
$(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
-o $@ \
$(PRIVATE_TARGET_GLOBAL_LDFLAGS) \
$(PRIVATE_LDFLAGS) \
$(PRIVATE_TARGET_CRTEND_SO_O) \
$(PRIVATE_LDLIBS)endef
上面这一大坨,可以直接用echo "XXXX" ,复制到xxx可以输出看到完整的实际展开的命令参数
输出了下,大概是这样子的。。。
嗯,很可怕,没敢贴完整。。这只是编译一个64位库的输出,还有一份同样的32bit的库的编译,所有其他的各种文件各种变量,无非就是根据具体情况来确定用什么编译器,文件的路径,连接什么库,需要什么参数。所以说整个编译架构,是一份完备的proj,用makefile写的proj,直接在linux终端上通过 grep来查看这些变量的定义和调用如:
在build目录下:grep "(transform" * -rn 查看这些函数的调用