`
nfer.zhuang
  • 浏览: 61238 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

深入浅出Android makefile(5)--BUILD_HOST_EXECUTABLE

 
阅读更多

上文我们讲完LOCAL_STATIC_LIBRARIES,下面我们就一步步来梳理下。BUILD_HOST_EXECUTABLE


一、 初识BUILD_HOST_EXECUTABLE
我们先看BUILD_HOST_EXECUTABLE的定义:
build/core/config.mk:BUILD_HOST_EXECUTABLE:= $(BUILD_SYSTEM)/host_executable.mk
展开即build/core/host_executable.mk

二、 host_executable.mk
我们先看看host_executable.mk文件的注释:
###########################################################
## Standard rules for building an executable file.
##
## Additional inputs from base_rules.make:
## None.
########################################################### 
注释中明确的说明了,这是个编译可执行文件的makefile,附加的输入来源于 base_rules.make

LOCAL_IS_HOST_MODULE := true      
明确指出了LOCAL_IS_HOST_MODULE 为真,我们在做LOCAL_PATH分析时,多次使用到该变量,当时我们是估计它肯定是为真,但是没有找到其具体的定义位置,看来就是此处了。

ifeq ($(strip $(LOCAL_MODULE_CLASS)),)
LOCAL_MODULE_CLASS := EXECUTABLES
endif 
因为模块的初始化部分调用了include $(CLEAR_VARS),所以$(LOCAL_MODULE_CLASS)的值肯定为空,此处为其赋值为EXECUTABLES。此变量也是用在intermediates-dir-for函数中。

ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)
LOCAL_MODULE_SUFFIX := $(HOST_EXECUTABLE_SUFFIX)
endif 
同理$(LOCAL_MODULE_CLASS)的值肯定为空,因为这里是要编译成可执行文件,linux上可执行文件是没有后缀的,换句话说,在此处 $(HOST_EXECUTABLE_SUFFIX)应该为空,该变量尚未初始化,所以必定为空。

下面又include了一个文件,include $(BUILD_SYSTEM)/binary.mk
但是打开这个文件,好家伙,600多行,怎么分析,不过看来大部分都是一些公共函数的定义。
我们先不管这个文件,继续下面分析。

$(LOCAL_BUILT_MODULE): $(all_objects) $(all_libraries)
     $(transform-host-o-to-executable)
     $(PRIVATE_POST_PROCESS_COMMAND) 
紧 接着定义了一个目标$(LOCAL_BUILT_MODULE),这个目标依赖于$(all_objects)和$(all_libraries),执行 两个操作:$(transform-host-o-to-executable)和 $(PRIVATE_POST_PROCESS_COMMAND)

首 先,我们先猜测下$(LOCAL_BUILT_MODULE)目标的值,因为LOCAL_IS_HOST_MODULE = true、LOCAL_MODULE_CLASS := EXECUTABLES它肯定是要放在out/host/linux-x86/obj/EXECUTABLES目录下,而LOCAL_MODULE = acp,故肯定是在刚才的目录下创建acp_intermediates文件夹,而$(LOCAL_BUILT_MODULE)的值估计就是:out/host/linux-x86/obj/EXECUTABLES/acp_intermediates/acp 。下面我们就一步步分析下$(LOCAL_BUILT_MODULE)

三、LOCAL_BUILT_MODULE
根据host_executable.mk文件开始处的注释,我们首先去查看下base_rules.make,我们发现在base_rules.make中有LOCAL_BUILT_MODULE变量的定义:
LOCAL_BUILT_MODULE := $(built_module_path)/$(LOCAL_BUILT_MODULE_STEM)

$(built_module_path)的定义又来源于:
# OVERRIDE_BUILT_MODULE_PATH is only allowed to be used by the
# internal SHARED_LIBRARIES build files.
OVERRIDE_BUILT_MODULE_PATH := $(strip $(OVERRIDE_BUILT_MODULE_PATH))
ifdef OVERRIDE_BUILT_MODULE_PATH
  ifneq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES)
    $(error $(LOCAL_PATH): Illegal use of OVERRIDE_BUILT_MODULE_PATH)
  endif
  built_module_path := $(OVERRIDE_BUILT_MODULE_PATH)
else
  built_module_path := $(intermediates)
endif 
根 据注释,编译EXECUTABLES时OVERRIDE_BUILT_MODULE_PATH 为空,那么 built_module_path := $(intermediates),而$(intermediates)我们之前已经分析过了,它会调用local-intermediates-dir
intermediates := $(call local-intermediates-dir) 
然后根据LOCAL_MODULE_CLASS 、LOCAL_MODULE 和LOCAL_IS_HOST_MODULE 返回一个字符串,在此处,它返回的是out/host/linux-x86/ob/EXECUTABLES/acp_intermediates

那 么LOCAL_BUILT_MODULE的值就是out/host/linux-x86/ob/EXECUTABLES /acp_intermediates/$(LOCAL_BUILT_MODULE_STEM),下面我们在看看 LOCAL_BUILT_MODULE_STEM的定义

这个变量同样也在base_rules.make中赋值:
LOCAL_BUILT_MODULE_STEM := $(strip $(LOCAL_BUILT_MODULE_STEM))
ifeq ($(LOCAL_BUILT_MODULE_STEM),)
  LOCAL_BUILT_MODULE_STEM := $(LOCAL_INSTALLED_MODULE_STEM)
endif 
同理,此处LOCAL_变量LOCAL_BUILT_MODULE_STEM 肯定为空,那么执行 LOCAL_BUILT_MODULE_STEM := $(LOCAL_INSTALLED_MODULE_STEM)。

LOCAL_INSTALLED_MODULE_STEM在base_rules.make中赋值:
LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE_STEM)$(LOCAL_MODULE_SUFFIX)
 
LOCAL_MODULE_STEM在base_rules.make中赋值:
LOCAL_MODULE_STEM := $(strip $(LOCAL_MODULE_STEM))
ifeq ($(LOCAL_MODULE_STEM),)
  LOCAL_MODULE_STEM := $(LOCAL_MODULE)
endif 
那么LOCAL_MODULE_STEM就为 $(LOCAL_MODULE),即acp

$(LOCAL_MODULE_SUFFIX)已经在host_executable.mk中讨讨论空,那么LOCAL_INSTALLED_MODULE_STEM 同样为acp,LOCAL_BUILT_MODULE_STEM也为acp

经过上述分析,LOCAL_BUILT_MODULE目标的值为:out/host/linux-x86/obj/EXECUTABLES/acp_intermediates/acp ,和我们在上面的猜测是一致的。

四、all_objectsall_libraries
分析完$(LOCAL_BUILT_MODULE),我们来看看$(all_objects) 和$(all_libraries)依赖。

$(BUILD_SYSTEM)/binary.mk中有对all_objects的定义:
all_objects := \
     $(asm_objects) \
     $(cpp_objects) \
     $(gen_cpp_objects) \
     $(gen_asm_objects) \
     $(c_objects) \
     $(gen_c_objects) \
     $(objc_objects) \
     $(yacc_objects) \
     $(lex_objects) \
     $(proto_generated_objects) \
     $(addprefix $(TOPDIR)$(LOCAL_PATH)/,$(LOCAL_PREBUILT_OBJ_FILES)) 
从 all_objects定义看出,它表示了所有的objects文件:asm_objects、cpp_objects、 gen_cpp_objects、gen_asm_objects、c_objects、gen_c_objects、objc_objects等等,但 是在此处我们只是关心$(c_objects)。

c_objects又是由c_arm_objects和c_normal_objects构成:
c_objects        := $(c_arm_objects) $(c_normal_objects) 
在此处,$(c_arm_objects)为空, $(c_normal_objects)已经在分析LOCAL_SRC_FILES时得到其值为:out/host/linux-x86/obj/EXECUTABLES/acp_intermediates/acp.o

$(BUILD_SYSTEM)/binary.mk中同样有对all_libraries的定义:
all_libraries := \
    $(built_shared_libraries) \
    $(built_static_libraries) \
    $(built_whole_libraries) 
all_libraries 表示了所有的libraries文件:built_shared_libraries、built_static_libraries和 built_whole_libraries,不过此处我们只依赖了静态库$(built_static_libraries)。
我 们在分析LOCAL_STATIC_LIBRARIES时,已经得到$(built_static_libraries)的值:out/host /linux-x86/obj/STATIC_LIBRARIES/libhost_intermediates/libhost.a

因此,在编译$(LOCAL_BUILT_MODULE)目标时,我们依赖于:out/host/linux-x86/obj/EXECUTABLES/acp_intermediates/acp.oout/host/linux-x86/obj/STATIC_LIBRARIES/libhost_intermediates/libhost.a 两个目标。
讨论完目标和依赖后,我们来看一看执行的操作。

五、 $(transform-host-o-to-executable)
在build/core/definitions.mk中有该函数的定义:
define transform-host-o-to-executable
@mkdir -p $(dir $@)
@echo "host Executable: $(PRIVATE_MODULE) ($@)"
$(transform-host-o-to-executable-inner)
endef 
该函数只是创建了一下目标文件夹,输出一下目标,然后调用的还是$(transform-host-o-to-executable-inner)函数

但是请注意,在输出目标时,有一个$(PRIVATE_MODULE)变量,通过输出我们知道在此处$(PRIVATE_MODULE)的值为acp。但是PRIVATE_MODULE变量的定义和使用又是在哪里呢?
目标all_objects依赖目标LOCAL_GENERATED_SOURCES
$(all_objects) : | $(LOCAL_GENERATED_SOURCES)      
LOCAL_GENERATED_SOURCES目标的依赖就是PRIVATE_MODULE
$(LOCAL_GENERATED_SOURCES): PRIVATE_MODULE := $(LOCAL_MODULE)      
此处PRIVATE_MODULE 定义为 $(LOCAL_MODULE)即acp
至于为什么还需要创建一个PRIVATE_MODULE 变量来表示当前的LOCAL_MODULE,我个人猜测,LOCAL_MODULE变量一般用于内部使用,而PRIVATE_MODULE 变量可以用于全局

$(transform-host-o-to-executable-inner)
下面我们来看看transform-host-o-to-executable-inner函数的定义
###########################################################
## Commands for running gcc to link a host executable
###########################################################

ifneq ($(HOST_CUSTOM_LD_COMMAND),true)
define transform-host-o-to-executable-inner
$(hide) $(PRIVATE_CXX) \
     -Wl,-rpath-link=$(HOST_OUT_INTERMEDIATE_LIBRARIES) \
     -Wl,-rpath,\$$ORIGIN/../lib \
     $(HOST_GLOBAL_LD_DIRS) \
     $(PRIVATE_LDFLAGS) \
     $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
          $(HOST_GLOBAL_LDFLAGS) \
     ) \
     $(PRIVATE_ALL_OBJECTS) \
     -Wl,--whole-archive \
     $(call normalize-host-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \
     -Wl,--no-whole-archive \
     $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--start-group) \
     $(call normalize-host-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
     $(if $(PRIVATE_GROUP_STATIC_LIBRARIES),-Wl$(comma)--end-group) \
     $(call normalize-host-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
     -o $@ \
     $(PRIVATE_LDLIBS)
endef
endif 

其 中transform-host-o-to-executable-inner函数包含在ifneq ($(HOST_CUSTOM_LD_COMMAND),true)判断中,我们发现只有在darwin平台下,该值才为true。即此处还是会使用公共 的transform-host-o-to-executable-inner函数定义。
在本函数中涉及到一下变量,其中有值的变量如下:
PRIVATE_CXX:                                        prebuilt/linux-x86/ccache/ccache g++
HOST_OUT_INTERMEDIATE_LIBRARIES:   out/host/linux-x86/obj/lib
HOST_GLOBAL_LD_DIRS:                         -Lout/host/linux-x86/obj/lib
PRIVATE_LDFLAGS:                                 -Wl,--no-undefined
HOST_GLOBAL_LDFLAGS:                        -m32
PRIVATE_ALL_OBJECTS:                         out/host/linux-x86/obj/EXECUTABLES/acp_intermediates/acp.o
PRIVATE_ALL_STATIC_LIBRARIES:          out/host/linux-x86/obj/STATIC_LIBRARIES/libhost_intermediates/libhost.a

上面变量中最关键的一个是PRIVATE_CXX,它定义在build/core/binary.mk:
$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CXX := $(LOCAL_CXX)
 
LOCAL_CXX定义如下:
ifeq ($(strip $(LOCAL_CXX)),)
  LOCAL_CXX := $($(my_prefix)CXX)
endif 

my_prefix的定义如下:
ifdef LOCAL_IS_HOST_MODULE
  my_prefix:=HOST_
else
  my_prefix:=TARGET_
endif 

因此在编译acp时,LOCAL_CXX := $(HOST_CXX)
而HOST_CXX的定义并不在HOST_linux-x86.mk文件,而是在select.mk文件中定义:
$(combo_target)CXX := $(ccache) $($(combo_target)CXX)
 
因此:HOST_CXX 和TARGET_CXX 的值在select.mk中已经有了定义,其值分别为:
HOST_CXX:    prebuilt/linux-x86/ccache/ccache g++
TARGET_CXX: prebuilt/linux-x86/ccache/ccache prebuilt/linux-x86/toolchain/arm-linux-androideabi-4.4.x/bin/arm-linux-androideabi-g++

而HOST_CXX和TARGET_CXX的详细定义见下面:

ifneq ($(USE_CCACHE),)                                        //是否采用ccache
  CCACHE_HOST_TAG := $(HOST_PREBUILT_TAG)
  # If we are cross-compiling Windows binaries on Linux
  # then use the linux ccache binary instead.
  ifeq ($(HOST_OS)-$(BUILD_OS),windows-linux)
    CCACHE_HOST_TAG := linux-$(BUILD_ARCH)     //windows下仍然使用linux的ccache
  endif
  ccache := prebuilt/$(CCACHE_HOST_TAG)/ccache/ccache     //指定ccache文件
  # Check that the executable is here.
  ccache := $(strip $(wildcard $(ccache)))
  ifdef ccache                  //判断ccache文件是否存在,
    # prepend ccache if necessary
    ifneq ($(ccache),$(firstword $($(combo_target)CC)))
      $(combo_target)CC := $(ccache) $($(combo_target)CC) //如果支持ccache,而$($(combo_target)CC)中没有$(ccache),则给$($(combo_target)CC)添加上$(ccache) 前缀,注意$(ccache)和$($(combo_target)CXX)之间有空格
    endif
    ifneq ($(ccache),$(firstword $($(combo_target)CXX)))
      $(combo_target)CXX := $(ccache) $($(combo_target)CXX) //同理,和$($(combo_target)CC)处理一致
    endif
    ccache =
  endif
endif
 
其中ccache的定义是在本文件中根据CCACHE_HOST_TAG变量组合而成。
但是 $(HOST_CXX)的定义却是需要跟踪的。在select.mk中有$(combo_target)CXX := $(CXX)赋值,而makefile中 $(CXX)的默认值为g++。

因此,在编译HOST上的文件时,HOST_CXX即为prebuilt/linux-x86/ccache/ccache g++ ,而在编译TARGET上的文件时,TARGET_CXX则是prebuilt/linux-x86/ccache/ccache prebuilt/linux-x86/toolchain/arm-linux-androideabi-4.4.x/bin/arm-linux-androideabi-g++ , 其中TARGET_CXX的定义是在TARGET_linux-arm.mk中 由$(TARGET_TOOLS_PREFIX)g++$(HOST_EXECUTABLE_SUFFIX)组成。 而$(TARGET_TOOLS_PREFIX)则是根据CCACHE_HOST_TAG变量组合而成。

五、  $(PRIVATE_POST_PROCESS_COMMAND)
在编译$(LOCAL_INTERMEDIATE_TARGETS)目标时,会依赖到PRIVATE_POST_PROCESS_COMMAND,在这里同时给PRIVATE_POST_PROCESS_COMMAND赋值:
build/core/base_rules.mk:
$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_POST_PROCESS_COMMAND:= $(LOCAL_POST_PROCESS_COMMAND)
 
我 们发现在build/core/clear_vars.mk中,有LOCAL_POST_PROCESS_COMMAND:=true语句,即我们在调用 include $(CLEAR_VARS)时,LOCAL_POST_PROCESS_COMMAND被设置为true,即 LOCAL_POST_PROCESS_COMMAND的默认值为true

查看build/core/build-system.html,我们发现如下说明:
LOCAL_POST_PROCESS_COMMAND

For host executables, you can specify a command to run on the module
after it's been linked.  You might have to go through some contortions
to get variables right because of early or late variable evaluation。
简要理解,就是如果你需要在执行完编译链接后,还需要做一些其他的操作来得到想要的结果,那么就使用LOCAL_POST_PROCESS_COMMAND来完成这个补充工作。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics