使用Makefile清理子目录

14

是否可以从父目录执行make clean,同时递归清理所有子目录,而无需在每个子目录中包含一个makefile文件?

例如,目前在我的Makefile中,我有类似以下内容的东西:

SUBDIRS = src, src1

.PHONY: clean subdirs $(SUBDIRS)

clean: $(SUBDIRS)
    rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c

$(SUBDIRS):
    $(MAKE) -C $(SUBDIRS) clean 
然而,这需要我在src和src1文件夹中都有Makefile文件。否则,我会收到错误信息。
No rule to make target clean

既然我只想在每个子目录中运行命令"rm -rf *.o ~ core .depend ..cmd *.ko *.mod.c",那么在每个子目录中都包含有相同的clean命令似乎是多余的。是否有简便的方法可以使得相同的clean命令在每个子目录中都能运行?


你可能只想使用“clean”命令来递归删除文件。请参见:https://superuser.com/questions/61258/use-rm-to-remove-files-and-directories-recursively - Daniel Waechter
或者只需在$(SUBDIRS)上循环并手动在每个目录中运行rm - Etan Reisner
实际上,如果你仔细编写顶层的makefile(或者编写一个单独的clean.mk makefile并将其包含进来),那么我认为你可以在该配方中使用$(MAKE) -C $@ -f clean.mk clean。 (注意使用$@而不是$(SUBDIRS)-C只接受单个参数而不是列表。) - Etan Reisner
在子make文件中,您将获得SUBDIRS变量的副本,从而打破了进程的递归部分。只有在您拥有相同的子目录(具有相同的名称)的情况下,该计划才能正常工作。 - Luis Colorado
5个回答

6

我同意您可以让rm命令只操作子目录。但是像下面这样做可以使用一个单一的makefile来进行递归式的编译:

SUBDIRS = . src src1
SUBDIRSCLEAN=$(addsuffix clean,$(SUBDIRS))

clean: $(SUBDIRSCLEAN)

clean_curdir:
    rm -rfv *.o *~ core .depend .*.cmd *.ko *.mod.c

%clean: %
    $(MAKE) -C $< -f $(PWD)/Makefile clean_curdir

如何在src或src1中进行一次递归... 如何知道有多少个子目录并构建变量SUBDIRS的新版本? - Luis Colorado
示例已经完成。运行 make --debug 将显示正在发生的事情。如果硬编码列表无法完成,类似于 find 的东西可能可以检测到子目录。 - robert
目前情况是,如果子目录src还没有被创建,则%clean规则将在运行clean_curdir之前尝试创建它。解决方案是将第二行中的clean更改为.clean,删除%clean规则中的尾随,并将$ <更改为$(basename $ @) - James Brusey

1
一种@Christoph答案的变体:

# Exclude directory from find . command
# https://dev59.com/O2855IYBdhLWcg3wq2XL
GARBAGE_TYPES         := "*.gz(busy)" *.aux *.log *.pdf *.aux *.bbl *.log *.out *.synctex.gz *.fls
DIRECTORIES_TO_CLEAN  := $(shell find -not -path "./.git**" -not -path "./images**" -type d)
GARBAGE_TYPED_FOLDERS := $(foreach DIR, $(DIRECTORIES_TO_CLEAN), $(addprefix $(DIR)/,$(GARBAGE_TYPES)))

clean:
    $(RM) -rf $(GARBAGE_TYPED_FOLDERS)
    # echo $(GARBAGE_TYPED_FOLDERS)

这是一个关于latex文件的示例。在GARBAGE_TYPES的第一个模式中,由于文件类型名称中有括号,因此需要加上双引号以便于rm删除。其他模式则不需要引号。
第二个DIRECTORIES_TO_CLEAN使用了不清理目录列表的相反方式,即列出不需要清理的目录列表。当你只有一个或两个目录(如.gitimages)不想清理,但想清理其他所有内容时,这很有用。

1

不必使用递归,可以通过调用find获取目录列表,并进行单次迭代以生成通配符:

SUBDIR_ROOTS := foo bar
DIRS := . $(shell find $(SUBDIR_ROOTS) -type d)
GARBAGE_PATTERNS := *.o *~ core .depend .*.cmd *.ko *.mod.c
GARBAGE := $(foreach DIR,$(DIRS),$(addprefix $(DIR)/,$(GARBAGE_PATTERNS)))

clean:
    rm -rf $(GARBAGE)

1
ALL_MOD = $(shell find . -maxdepth 1 -type d )
ALL_MOD_CLEAN = $(addsuffix .clean, $(ALL_MOD))

.PHONY: $(ALL_MOD_CLEAN) $(ALL_MOD) default all clean

$(ALL_MOD_CLEAN): 
    $(E) "cleaning $(basename $@)"
    if [ -e $(basename $@)/Makefile ] ; then \
            $(MAKE) -C $(basename $@) clean ;\
    fi 

clean: $(ALL_MOD_CLEAN)
    $(E) "cleaning complete: " $(ALL_MOD)

在后缀名中添加“.”,并使用$(basename ...)替代原始目标,否则每次只需清理项目即可,否则将重新制作所有文件。

0

你不能没有外部程序的帮助。最好的方法是编写一个shell脚本,在每个子目录中进行递归并调用make命令(请查看我在@robert回答中的评论)。类似下面这样的代码可以完成工作(而且不依赖于GNU make的特性)

#!/bin/sh
ROOTDIR=`/bin/pwd`
for dir in `find . -type d -print`
do
    make -C "${dir}" -f "${ROOTDIR}"/Makefile clean
done

当然,你可以将这个序列(在目标cleanrec中)放入你的Makefile中。

cleanrec:
    ROOT=`/bin/pwd`; \
    for dir in `find . -type d -print`; \
    do \
        make -C "$${dir}" -f "$${ROOTDIR}"/Makefile clean; \
    done

并保留您的clean目标,以便本地清理单个目录。原因是Makefile仅具有执行make所需的静态信息,而您必须获取一些外部帮助才能知道每个目录中有哪些子目录。因此,如果您要获取外部帮助,最好使用一个好工具,如find(1)sh(1)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接