« 近日小结 | 返回首页 | 玩了一下 ActionScript »

让 Make 递归所有子目录

最近同事提了一个需求,想方便的调用一个工具校验所有的 xml 文件(包括子目录下的)。我想了一下,最简单的方法是用 Make 来辅助完成这件事情。

问题在于,怎样让 Make 递归的处理所有子目录。因为 GNU Make 默认的 wildcard 只能枚举出当前目录的文件,而不能递归下去。

求助于 Shell 指令当然是一种方法,如果只考虑 unix 环境,我会用 shell 去做。在 Windows 下也有 for 语句可以使用。但我不想处理复杂的平台差异。所以就在 Make 内置的函数里想办法了。

一开始我认为 GNU Make 是个图灵完备的语言,有 call 和 eval 做函数模板和调用。if 函数可以用于递归终止。理论上能写递归就能解决别的语言解决的问题了。

后来试着在 makefile 里写了个简单的递归程序,结果让我很失望。Make (至少是 mingw32 的 make) 被我的小程序整挂了。输出了错误信息:

make: Interrupt/Exception caught (code = 0xc00000fd, addr = 0x7c92e8e7)

迫不得已,我使用了开子进程的方式。(这种方式在 Windows 下效率很低)

下面列出今天写的一段小程序,功能是递归遍历当前目录下的所有子目录。

.PHONY : all

all: 
    echo I'm in $(CURRENT_DIR)

CURRENT_DIR ?= .
FILES = $(wildcard $(CURRENT_DIR)/*)

define DIR_temp
.PHONY : $(1)
all: $(1)
$(1) :
    $$(MAKE) CURRENT_DIR=$$(CURRENT_DIR)/$(strip $(1))
endef

$(foreach e, $(FILES), \
  $(if $(wildcard $(e)/*), \
    $(eval $(call DIR_temp, $(notdir $(e))))))

7 月 8 日补充:

前几天的递归程序是我写错了导致,如果正确的编写是可以成功遍历子目录下的所有文件的。代码如下:

.PHONY : all

# arg1 dir
define EXPAND_temp
  FILES := $(wildcard $(1)*)
  DIRS := 
  $$(foreach e, $$(FILES), $$(if $$(wildcard $$(e)/*), $$(eval DIRS := $$(DIRS) $$(e))))
  FILES := $$(filter-out $$(DIRS),$$(FILES))
  ALLFILES := $$(ALLFILES) $$(FILES)
  $$(foreach e,$$(DIRS),$$(eval $$(call EXPAND_temp,$$(e)/)))
endef

$(eval $(call EXPAND_temp))

all :
    echo $(ALLFILES)

Comments

这个方法管用!
# Make does not offer a recursive wildcard function, so here's one:
rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))

# How to recursively find all files with the same name in a given folder
ALL_INDEX_HTMLS := $(call rwildcard,foo/,index.html)

# How to recursively find all files that match a pattern
ALL_HTMLS := $(call rwildcard,foo/,*.html)

http://stackoverflow.com/questions/3774568/makefile-issue-smart-way-to-scan-directory-tree-for-c-files

用$(wildcard)函数不能区分空文件夹和文件。

用$(wildcard)函数不能区分空文件夹和文件。

获取指定目录下所有文件:

define walk
$(wildcard $(1)) $(foreach e, $(wildcard $(1)/*), $(call walk, $(e)))
endef

ALLFILES = $(call walk, .)

如果你手里有把锤子,那么你将会把任何问题都看作一枚钉子?

为何不考虑装个cygwin

云风的思路就是和别人不一样啊,这玩意用python写也就是2、3行代码的样子,非得用make整成这样,真是不容易啊。

我喜欢天下二,是它的玩家,业余也玩玩程序,很菜。久仰风云的大名。不知风云有没有参与天下二的开发。

python的walk是干这个的最佳选择,跨平台应该没问题。

@viho

回帖请看贴:

原贴写了 "求助于 Shell 指令当然是一种方法,如果只考虑 unix 环境,我会用 shell 去做"

unix 下有 find , windows 下有 for 。

“但我不想处理复杂的平台差异”

我以为最简单的方法是用python来辅助完成这件事情。

虽然看的不是很明白! 学习了!

find -exec 的作用是把所有找到的文件都用-exec后面的命令来处理,找到的文件是这个命令的参数。还可以加xargs这个命令在管道里面做一些参数的处理。find很好很强大,您可以自己去查查find命令的用法。

如果只是要做所有xml的检验的话,用find更简单,
find /<you_path> -name *.xml -exec your_script.sh {} \;

Post a comment

非这个主题相关的留言请到:留言本