如何自动设好 Android.mk 的 LOCAL

2024-12-02 07:38:37
推荐回答(2个)
回答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))

回答2:

用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命令筛选出所有的源代码文件路径。