如何自动设好 Android.mk 的 LOCAL

2024年11月30日 00:49
有1个网友回答
网友(1):

  用Cocos2d-x开发Android游戏时,需要在Android.mk文胡脊件中,为LOCAL_SRC_FILES变量指定要编译的源代码,以及为LOCAL_C_INCLUDES变量指定头文件。当项目文件越来越多时,这种手动修改很浪费时间。好在Android.mk其实就是一个makefile,我们可以借助makefile语法来自动完成这部分工作。   使用外部命令   最简单的方式就是调用shell外部命令。首先我们指定要搜索的源文件根目录,设为SRC_ROOT这个变量。LOCAL_C_INCLUDES变量直接就是用find -type d命令去搜索根目录下的目录。LOCAL_SRC_FILES稍微复杂一些,首先我们先用find -type f得到所有的普通文件路径,再指定源代码文件名的匹配模式(例如我用的是c++,所以我指定了变量SRC_SUFFIX存放一般c++源代码文件的后缀名),用filter命令筛选出所有的源代码文件路径。   完整的代码如下:   # WARNING: Shell command is used, it is only works on a UNIX-like OS.   # Replace it with Makefile rules if you want to run on Windows.   裤亮渗SRC_SUFFIX := *.cpp *.c   SRC_ROOT := $(LOCAL_PATH)/../../Classes   ALL_FILES := $(shell find $(SRC_ROOT) -type f)   SRC_FILES := $(filter $(subst *,%,$(SRC\_SUFFIX)),$(ALL_FILES))   LOCAL_SRC_FILES := hellocpp/main.cpp   LOCAL_SRC_FILES += $(SRC\_FILES:$(LOCAL_PATH)/%=%)   SRC_DIRS := $(shell find $(SRC_ROOT) -type d)   LOCAL_C_INCLUDES := $(SRC_DIRS)   使用纯Makefile语法   使用外部命令是最简单实用的解决方案,但正如上面的代码注释所提及的,这种方式只能在Unix系统上才能用,对于需要跨平台适用的情况,还是需要采用纯Makefile语法才行。   我们知道,Makefile的wildcard命令可以部分实现类似find的功能,例如找到当前目录下的.c文件可以用$(wildcard *.c),可惜wildcard毕竟不够强大,该命令的结果并不包含子目录以下的.c文件。想要实现这一功能,我们可以借用StackOverflow上大神用纯Makefile语法写键笑的递归wildcard:   # recursive wildcard   rwildcard = $(foreach d,$(wildcard $1\*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)))该rwildcard命令传入两个参数,第一个参数$1是目录,第二个参数$2是匹配模式。该命令首先用$(wildcard $1*)得到目录下的所有文件和一级子目录,再遍历一遍:对于当前$d变量是目录的情况,对$d/目录递归调用rwildcard;对于$d是普通文件的情况,递归调用会因为$(wildcard $d/*)找不到匹配而终止,接下来便调用filter函数对$2的模式进行筛选。   完整的代码如下:   SRC_SUFFIX := *.cpp *.c   SRC_ROOT := $(LOCAL_PATH)/../../Classes   # recursive wildcard   rwildcard = $(foreach d,$(wildcard $1\*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)))   SRC_FILES := $(call rwildcard,$(SRC_ROOT)/,$(SRC_SUFFIX))   LOCAL_SRC_FILES := hellocpp/main.cpp   LOCAL_SRC_FILES += $(SRC\_FILES:$(LOCAL_PATH)/%=%)   筛除不需要编译的源代码文件   上面介绍的方法有一个适用的前提,那就是$SRC_ROOT下每个源代码文件都需要被编译。而有时候这个条件并不成立,像本渣所在项目中就用到了一些外部库,这些库的源代码是不用被编译的(例如设为ASIO_HEADER_ONLY的Asio库)。这个时候就需要把这部分源代码排除在LOCAL_SRC_FILES之外。   第一种方法:filter-out   第一种方法是用Makefile的filter-out命令:   # ASIO library is set as ASIO_HEADER_ONLY, so it will be excluded from source code   EXCLUDE_SRC_FILES := $(SRC_ROOT)/3rdParty/Asio/asio/impl/%.cpp   EXCLUDE := $(filter $(EXCLUDE_SRC_FILES),$(SRC_FILES))   SRC_FILES := $(filter-out $(EXCLUDE_SRC_FILES),$(SRC_FILES))这种方式虽然可行,但是filter-out无法用于多级目录的模式匹配,所以这种方法暴露了太多关于外部库源代码路径的细节。有没有可能指定要排除的库名或关键字,再根据这个信息去筛除匹配的源代码文件呢?   第二种方法:改进rwildcard   第二种方式是在rwildcard中加入一个判断用于筛除:如果当前的目录/文件名匹配到所要筛除的关键字,则什么都不做;否则就继续递归调用和执行filter命令。   # ASIO library is set as ASIO_HEADER_ONLY, so it will be excluded   EXCLUDE_LIB := Asio   # recursive wildcard   rwildcard = $(foreach d,$(wildcard $1\*),$(if $(findstring $(EXCLUDE_LIB),$d),,$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)))   SRC_FILES := $(call rwildcard,$(SRC_ROOT)/,$(SRC_SUFFIX))这种方式是好一些了,而且直接在递归wildcard搜索时就进行了排除,不过rwildcard变得更复杂了,可读性不佳。   第三种方法:FILTER_OUT_PATTERN   最后的方法也来自于StackOverflow大神用纯Makefile语法改造过的filter-out,不过本质上和第二种方法的实现是类似的,这里就不详细解释了:   # ASIO library is set as ASIO_HEADER_ONLY, so it will be excluded from source code   EXCLUDE_SRC_PATTERN := asio   FILTER_OUT_PATTERN = $(foreach v,$(2),$(if $(findstring $(1),$(v)),,$(v)))   SRC_FILES := $(call FILTER\_OUT\_PATTERN,$(EXCLUDE_SRC_PATTERN),$(SRC_FILES))