14

Is it possible to perform a make clean from the parent directory which also recursively cleans all sub-directories without having to include a makefile in each sub-directory?

For example, currently in my Makefile, I have something like:

SUBDIRS = src, src1

.PHONY: clean subdirs $(SUBDIRS)

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

$(SUBDIRS):
    $(MAKE) -C $(SUBDIRS) clean 

However, this requires me to have a Makefile in both src and src1. Otherwise, I would get the error

No rule to make target clean

Since I only want to run the command "rm -rf *.o ~ core .depend ..cmd *.ko *.mod.c" in each subdirectory anyways, it seems redundant to have to include a Makefile in every subdirectory with the exact same line for clean. Is there no way to simply have the same clean command run in each of the subdirectories?

pqnet
  • 6,070
  • 1
  • 30
  • 51
Tony
  • 1,839
  • 10
  • 27
  • 48
  • You probably want to just have the clean command delete files recursively. See: https://superuser.com/questions/61258/use-rm-to-remove-files-and-directories-recursively – Daniel Waechter Sep 24 '14 at 01:21
  • That or just loop in clean over `$(SUBDIRS)` and run `rm` in each one manually. – Etan Reisner Sep 24 '14 at 01:24
  • Actually, if you write your toplevel makefile carefully (or write a separate `clean.mk` makefile that you include) then you can `$(MAKE) -C $@ -f clean.mk clean` in that recipe I think. (Notice `$@` instead of `$(SUBDIRS)`. `-C` takes a single argument not a list.) – Etan Reisner Sep 24 '14 at 01:26
  • You will get a copy of SUBDIRS variable on submakes breaking the recursive part of the process. Only in case you have the same subdirectories (with the same names) that plan will work. – Luis Colorado Sep 24 '14 at 17:39

5 Answers5

6

I agree that you could just have the rm command operate on subdirs. But something like the following allows recursive make using only a single 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
robert
  • 161
  • 4
  • How do you recurse once in src or src1... how do you know how many subdirectories are there and construct a new version of variable `SUBDIRS`? – Luis Colorado Sep 24 '14 at 17:20
  • The example is complete. Running `make --debug` will show what's happening. Something like `find` could probably detect the subdirectories if a hard-coded list won't do. – robert Sep 26 '14 at 14:23
  • As it stands, if subdir `src` has not been made, then the `%clean` rule will try to make it before running `clean_curdir`. A solution is to change `clean` to `.clean` in the 2nd line, remove the trailing `%` in the `%clean` rule and change `$<` to `$(basename $@)` – James Brusey Mar 04 '23 at 02:25
1

Instead of using recursion, you could shell out to find to get a list of directories and do a single iteration to generate the wildcards:

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)
Christoph
  • 164,997
  • 36
  • 182
  • 240
1

A variation of @Christoph answer:

# Exclude directory from find . command
# https://stackoverflow.com/questions/4210042/exclude-directory-from-find-command
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)

This is an example for latex files. The first pattern on GARBAGE_TYPES has the double quotes around it because of the parenthesis on the file type name. Without it, rm cannot remove them. The other patterns does not need the quotes.

The second DIRECTORIES_TO_CLEAN uses the opposite of a list of directories to clean, i.e., a list of directories to not clean. This is useful when you have only one or two directories as .git and images which you do not want to clean, but want to clean everything else.

Evandro Coan
  • 8,560
  • 11
  • 83
  • 144
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)

add '.' to the suffix and use $(basename ...) instead of original target otherwise will be re-making all files every time need just to clean the project

Oleg Kokorin
  • 2,288
  • 2
  • 16
  • 28
0

You cannot without help of an external program. The best is a shell script that does the recursion and calls make in each of the subdirectories (look at my comment to @robert in his response) Something like this will do the work (and does not depend on GNU make features)

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

of course, you can put this sequence (in target cleanrec) inside your Makefile

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

and conserve your clean target for local cleaning of a single directory. The reason is that Makefile has only static info to do the make, and you have to get some external help to know what subdirectories you have in each directory. So, in case you are going to get external help, you'd better to use a good tool as find(1) and sh(1)

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31