我们也说说Android.mk(3) - 宏

我们也说说Android.mk(3) - 宏

传统上我们一直称这种东西为makefile中的变量,其实本质上就是一个宏,只是做的是字符串替换。我们何如就把它叫做宏呢。

宏的命名

makefile的宏可以包含字符、数字、下划线。需要注意的一点是,宏就是宏,反正是要做字符串替换的,所以名字以数字开头是没问题的。makefile本身也不是用于数值处理的,以字符串为主。

使用一个宏的时候需要使用$符号,所以如果字符串中要使用shell变量,需要用$$。
从中可以看到,我们前面学到的函数,其实本质上也就是宏,只不过是带有了参数的宏。

宏的赋值

= 和 :=

宏可以用其他宏的值来定义自己。最强大的是用=运算符。makefile是定义式的语言,不是按顺序一条一条执行的,所以,可以使用在这一行还没有定义的宏来为当前宏赋值,反正就是个宏展开么。

但是,这样如果造成的循环引用,就会引发意想不到的错误。在这种情况下,使用:=运算符,它是只允许引用在它之前定义的变量的。

?=

如果不知道定义没定义,可以使用?=来赋值,如果未定义,则定义。如果已经定义了,就什么也不做。

+=

如果之前未定义,则相当于=。如果之前有:=定义,则将新值按:=的规则添加到原值后面。

对宏进行比较

有4个关键字用于对宏的比较:

  • ifeq:判断相等
  • ifneq:判断不相等
  • ifdef:判断非空,相当于ifneq(<参数>,)
  • ifndef:如果为空,相当于ifeq(<参数>,)

例:

.PHONY : all7
ifeq ($(DEX2OAT_HOST_INSTRUCTION_SET_FEATURES),)
DEX2OAT_HOST_INSTRUCTION_SET_FEATURES := default
endif
all7 :
    @echo $(DEX2OAT_HOST_INSTRUCTION_SET_FEATURES)

输出:

$ make all7
default

请注意,ifeq, else, endif这些语句之前不能有Tab。

上面的例子是从Android.oat.mk中改造的。其实,跟下面的是等价的:

ifndef DEX2OAT_HOST_INSTRUCTION_SET_FEATURES
DEX2OAT_HOST_INSTRUCTION_SET_FEATURES := default
endif

定义多行的宏

可以通过使用define / endef来定义多行的大宏。

我们看一个例子:

 # $(1): compiler - default, optimizing, jit or interpreter.
 # $(2): wrapper.
 # $(3): dex2oat suffix.
define create-core-oat-host-rule-combination
  $(call create-core-oat-host-rules,$(1),no-pic,,$(2),$(3))
  $(call create-core-oat-host-rules,$(1),pic,,$(2),$(3))

  ifneq ($(HOST_PREFER_32_BIT),true)
    $(call create-core-oat-host-rules,$(1),no-pic,2ND_,$(2),$(3))
    $(call create-core-oat-host-rules,$(1),pic,2ND_,$(2),$(3))
  endif
endef

这个宏其实是个函数了。

eval函数

eval函数用于在宏定义或者是分支、循环结构中使用其他makefile语句时。

我们看一个在foreach中使用eval的例子:

$(foreach m,$(ALL_MODULES), \
  $(eval r := $(ALL_MODULES.$(m).REQUIRED)) \
  $(if $(r), \
    $(eval r := $(call module-installed-files,$(r))) \
    $(eval t_m := $(filter $(TARGET_OUT_ROOT)/%, $(ALL_MODULES.$(m).INSTALLED))) \
    $(eval h_m := $(filter $(HOST_OUT_ROOT)/%, $(ALL_MODULES.$(m).INSTALLED))) \
    $(eval t_r := $(filter $(TARGET_OUT_ROOT)/%, $(r))) \
    $(eval h_r := $(filter $(HOST_OUT_ROOT)/%, $(r))) \
    $(eval t_m := $(filter-out $(t_r), $(t_m))) \
    $(eval h_m := $(filter-out $(h_r), $(h_m))) \
    $(if $(t_m), $(eval $(call add-required-deps, $(t_m),$(t_r)))) \
    $(if $(h_m), $(eval $(call add-required-deps, $(h_m),$(h_r)))) \
   ) \
 )

vpath宏

vpath宏用于指定makefile搜索源文件的目录,这个倒是有点变量的意思了

引用其他makefile

makefile基本上就是字符串替换,我们当然也可以通过include命令将一系列的其他makefile引用进当前的makefile.

例:

include art/build/Android.common_build.mk

自动宏

自动宏就是由make工具已经定义好的变量。

$@ 目标文件集

$@是指整个目标的文件集

例:

$(built_odex) : $(dir $(LOCAL_BUILT_MODULE))% : $(built_dex)
    $(hide) mkdir -p $(dir $@) && rm -f $@
    $(add-dex-to-package)
    $(hide) mv $@ $@.input
    $(call dexpreopt-one-file,$@.input,$@)
    $(hide) rm $@.input
endif

$<

依赖的第一个文件名,可以和$@一起共用。

例:

$(built_odex) : $(my_prebuilt_src_file)
    $(call dexpreopt-one-file,$<,$@)

调试与错误处理

输出消息

makefile的命令,默认都会输出出来。可以用@来隐藏显示。
通过echo命令,可以输出消息。一般都是用@echo,免得看见两次。

几条命令一起执行

如果这条命令用到上条命令的结果,需要在上条命令后面加分号

错误处理

如果makefile遇到命令出错,就会中止当前的依赖执行。可以通过在命令之前加上“-”来忽略错误。
我们也可以将忽略错误的目标以.IGNORE来声明。

如:

.IGNORE : all7
时间: 2025-01-31 01:55:21

我们也说说Android.mk(3) - 宏的相关文章

Android.mk中添加宏定义

在Boardconfig.mk 中添加一个 IS_FLAG := true 由于Boardconfig.mk和各目录的Android.mk是相互关联的 所以我们可以在Android.mk 中添加 一个 ifdef IS_FLAG            LOCAL_CFLAGS += -DHHHH endif 在Android.mk中添加的这行相当于在 该目录下的文件中定义了一个宏 #define HHHH 如果我们想要在某个工程中根据工程的不同来对相关的代码中的某段参数进行选择时,可以参考此方法

我们也说说Android.mk(5) - 计算怎么办?

我们也说说Android.mk(5) - 计算怎么办? 计算怎么办? 前面我们把Makefile做为一门语言的主要特性大致做了一个描述,它集合了目标式的模式和函数式的模式,还有大量的宏的思想,已经可以写出相当复杂的Makefile了. 但是,很不幸的是,虽然已经很复杂了,我们在实际的Android.mk当中还是会发现有很多事情光用Makefile的字符串替换搞不定啊,这可如何是好? 其实,这就是我们在第一讲的最开始就讲shell函数的原因,因为这要靠shell脚本来解决了. 我们看个实际的例子:

我们也说说Android.mk(6) - 案例教程

我们也说说Android.mk(6) - 案例教程 userdata是如何生成的 我们先看看userdataimage的伪目标是如何定义的: .PHONY: userdataimage userdataimage: $(INSTALLED_USERDATAIMAGE_TARGET) ifneq (,$(filter userdataimage, $(MAKECMDGOALS))) $(call dist-for-goals, userdataimage, $(BUILT_USERDATAIMAG

jni使用基础(三)之Android.mk文件详解

    Android.mk文件详细说明: LOCAL_PATH:=$(call my-dir)//调用编译链的函数 LOCAL_PATH是定义源文件在哪个目录用的. my-dir 是个定义的宏方法,  $(call my-dir)就是调用这个叫 my-dir的宏方法,这个方法返回值就是Android.mk文件所在的目录   include $(CLEAR_VARS)//类似工具的初始化操作 CLEAR_BARS 变量是build system里面的一个变量 这个变量指向了所有的类似 LOCAL

Android.mk简介

Android.mk文件是GNU Makefile的一小部分,它用来对Android程序进行编译. 因为所有的编译文件都在同一个 GNU MAKE 执行环境中进行执行,而Android.mk中所有的变量都是全局的.因此,您应尽量少声明变量,不要认为某些变量在解析过程中不会被定义. 一个Android.mk文件可以编译多个模块,每个模块属下列类型之一: 1)APK程序   一般的Android程序,编译打包生成apk文件 2)JAVA库   java类库,编译打包生成jar文件 3)C\C++应用

Android.mk详细说明

Android.mk文件是GNU Makefile的一小部分,它用来对Android程序进行编译. 因为所有的编译文件都在同一个 GNU MAKE 执行环境中进行执行,而Android.mk中所有的变量都是全局的.因此,您应尽量少声明变量,不要认为某些变量在解析过程中不会被定义. 一个Android.mk文件可以编译多个模块,每个模块属下列类型之一:   1)APK程序   一般的Android程序,编译打包生成apk文件   2)JAVA库   java类库,编译打包生成jar文件   3)C

对android.mk debug

1.DEBUG android makefile编写一直是我这边的痛,目前找到一个好的办法对makefile进行debug的工作 $(warning value of LOCAL_OPPO_SRC_FILES is: $(LOCAL_OPPO_SRC_FILES))  如上就可以实现打印当前变量. 原文具体帖子见        http://stackoverflow.com/questions/3826604/android-mk-debug-output 2.各种文件的拷贝方式: 可详见 h

Android.mk&amp;amp;nbsp;介绍

在一个Android.mk中可以生成多个可执行程序.动态库和静态库. 1,编译应用程序的模板: 01 #Test Exe 02 LOCAL_PATH := $(call my-dir) 03   04 #include $(CLEAR_VARS) 05 LOCAL_SRC_FILES:= main.c 06 LOCAL_MODULE:= test_exe 07   08 #LOCAL_C_INCLUDES := 09 #LOCAL_STATIC_LIBRARIES := 10 #LOCAL_SH

详解安卓系统中的Android.mk文件_C 语言

概述    Android.mk文件用来向编译系统描述如何编译你的源代码.更确切地说,该文件其实就是一个小型的Makefile.由于该文件会被NDK的编译工具解析多次,因此应该尽量减少源码中声明变量,因为这些变量可能会被多次定义从而影响到后面的解析.这个文件的语法允许把源代码组织成模块,每个模块属于下列类型之一:     APK程序:一般的Android程序,编译打包生成apk文件.     JAVA库:java类库,编译打包生成jar包文件.     C\C++应用程序:可执行的C/C++应用