Makefile clean target and .PHONY - makefile

I wrote this Makefile with a PHONY target that loops over a set of subdirectories and executes $(MAKE)
SUBDIRS:= dir_1 dir_2 dir_3 dir_n
libs:$(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $#;
.PHONY: libs $(SUBDIRS)
I have tried with this code but it doesn't work.
SUBDIRS:=dir_1 dir_2 dir_3 dir_n
CLEANDIRS:=dir_1 dir_2 dir_3 dir_n
libs:$(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $#;
clean:$(CLEANDIRS)
$(CLEANDIRS):
$(MAKE) -C $# clean;
.PHONY: libs $(SUBDIRS)
.PHONY: clean $(CLEANDIRS)
Any suggestion? Thanks

Related

Makefile: how do I get the same behaiour as $# when using a variable directly?

I'm using similar code to https://stackoverflow.com/a/17845120/1375972 which includes:
TOPTARGETS := zip test clean
SUBDIRS := $(wildcard */.)
$(TOPTARGETS): $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $# $(MAKECMDGOALS)
This runs whichever make command is passed (from TOPTARGETS) on every subdirectory, which is what I want.
However I want to have a target deploy which has this same behaviour only when an environment variable is set. Here's what I've tried:
deploy:
ifdef GITLAB_CI
#echo "GITLAB_CI variable is defined, running deploy on all subdirectories"
$(MAKE) -C $(SUBDIRS) $(MAKECMDGOALS)
else
#echo "snip"
endif
Notice the $(MAKE) line is the same as the $(SUBDIRS) one, just with the $# replaced with $(SUBDIRS) directly.
So the logic is when it runs in my CI it'll run the deploy recursively on all subdirectories, but when it's run locally it doesn't. The problem is that the $(SUBDIRS) in deploy doesn't behave as expected. When I run make deploy in a directory with 2 subdirectories:
> make deploy
GITLAB_CI variable is defined, running deploy on all subdirectories
/Library/Developer/CommandLineTools/usr/bin/make -C subdir1/. subdir2/. deploy
make[1]: *** No rule to make target `subdir2/.'. Stop.
make: *** [deploy] Error 2
Compared to make clean (one of my TOPTARGETS):
> make clean
/Library/Developer/CommandLineTools/usr/bin/make -C subdir1/. clean
/Library/Developer/CommandLineTools/usr/bin/make -C subdir2/. clean
So when the TOPTARGETS are used, the $# seems to unroll $(SUBDIRS) in a different way to when I write the same line myself with $# substituted to $(SUBDIRS). Is there any way to get that behaviour myself in the deploy line? Or do I need to write my own for loop over $(SUBDIRS) inside that target?
You could keep what works and add a minimal deploy rule:
TOPTARGETS := zip test clean
SUBDIRS := $(wildcard */.)
$(TOPTARGETS): $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $# $(MAKECMDGOALS)
ifdef GITLAB_CI
deploy: $(SUBDIRS)
else
deploy:
#echo "snip"
endif
Or, maybe even simpler:
TOPTARGETS := zip test clean
ifdef GITLAB_CI
TOPTARGETS += deploy
else
deploy:
#echo "snip"
endif
SUBDIRS := $(wildcard */.)
$(TOPTARGETS): $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $# $(MAKECMDGOALS)

Unable to do make clean despite .PHONY

I have created a Makefile, to convert markdown to other formats. I have used .PHONY: clean but I am still unable to do make clean. It searches for clean.md file. I know its because of $(MAKECMDGOALS) but I need it to convert just one file.
SOURCE= $(wildcard *.md)
## Pattern Substitution
HTML=$(SOURCE:.md=.gen.html)
PDF=$(SOURCE:.md=.gen.pdf)
## Targets and dependencies
.PHONY: all
all : $(HTML) $(PDF)
html: clean $(HTML)
pdf: clean $(PDF)
.PHONY: clean
clean:
- $(RM) -f *.gen.*
.PHONY: $(MAKECMDGOALS)
$(MAKECMDGOALS): $(MAKECMDGOALS:%=%.html) $(MAKECMDGOALS:%=%.gen.pdf)
%.gen.html : %.md
$(PANDOC) $(PANDOC_OPTIONS) $(PANDOC_HTML_OPTIONS) -o $# $<
%.gen.pdf : %.md
$(PANDOC) $(PANDOC_OPTIONS) $(PANDOC_PDF_OPTIONS) -o $# $<
Please suggest changes.
GOALS := $(filter-out clean, $(MAKECMDGOALS))
.PHONY: $(GOALS)
$(GOALS): $(GOALS:%=%.html) $(GOALS:%=%.gen.pdf)

How to depend on targets of a previous make invocation

I have a lib (say mylib) and two executables and one of these (say exe1) depends on lib. In file system i have:
src
Makefile
...
lib
mylib
Makefile
...
exe1
Makefile
...
exe2
Makefile
...
and by launching make in src dir all is builded.
Makefile in src:
EXE=exe1 exe2
LIB=mylib
all: $(LIB) $(EXE)
.PHONY: $(LIB) $(EXE)
$(LIB):
$(MAKE) -C lib/$#
$(EXE): $(LIB)
$(MAKE) -C $#
and, for example, Makefile for exe1 is:
...
all: exe1 copy
exe1: exe1.o
$(CC) $(CFLAGS) $(OBJ) $(LDFLAGS) -o $#
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $#
...
My problem is that if i change a file in mylib dir the library is correctly rebuilded but exe1 obsiously no...is there a way to teel make that exe1 target depends on a target (mylib) that is specified in a previous invocation of make without specifing dependencies on mylib's files in exe1 Makefile?
Thank you all.
#tripleee and #JackKelly (curse his name) are right, this is not a healthy makefile system.
You can get something like what you want by changing src/Makefile:
EXE=exe1 exe2
LIB=lib/mylib/mylib
all: $(LIB) $(EXE)
.PHONY: $(EXE)
$(LIB):
$(MAKE) -C lib/mylib
exe1: $(LIB)
$(EXE):
$(MAKE) -C $#
and changing exe1/makefile so that it will always rebuild exe1:
.PHONY: exe1
This still has many problems, but at least it will correctly rebuild lib/mylib/mylib and src/exe1/exe1 when you run Make in src. (It will not work if you run Make in src/exe1/.)

How do I avoid these repetitions when using recursive make?

Suppose I have a project with two or more subfolders foo, bar, etc. I have a Makefile at the root of the project, and also in each subdirectory.
I would like to have certain targets (e.g. all, clean, etc) to run recursively in each subdirectory. My top-level Makefile looks like this:
all:
$(MAKE) -C foo all
$(MAKE) -C bar all
clean:
$(MAKE) -C foo clean
$(MAKE) -C bar clean
Seems to me there's a lot of duplication going on here. Is there a way I can avoid such tedious duplication in my Makefiles?
How about this:
SUBDIRS=foo bar
all clean:
for dir in $(SUBDIRS) ; do \
$(MAKE) -C $$dir $# ; \
done
A bit scary:
SUBDIRS=foo bar
SUBDIR_TARGETS=all clean
define subdir_rule
$(2): $(1)-$(2)
$(1)-$(2):
make -C $(1) $(2)
endef
$(foreach targ,$(SUBDIR_TARGETS),\
$(foreach dir,$(SUBDIRS),\
$(eval $(call subdir_rule,$(dir),$(targ)))))
Here's how I'd do it:
SUBDIRS=foo bar baz
TARGETS = clean all whatever
.PHONY:$(TARGETS)
# There really should be a way to do this as "$(TARGETS):%:TARG=%" or something...
all: TARG=all
clean: TARG=clean
whatever: TARG=whatever
$(TARGETS): $(SUBDIRS)
#echo $# done
.PHONY: $(SUBDIRS)
$(SUBDIRS):
#$(MAKE) -s -C $# $(TARG)

Makefile adds itself as target

I have a Makefile for a C++ program that uses automatic dependency generation. The %.d recipe is taken from the GNU Make manual.
The problem is that somehow "Makefile" is being added as a target and then an implicit rule is causing it to assume it's an executable and using my src/%.cpp rule to try to compile src/Makefile.cpp. When looking at the debug info, this always happens right after the include is executed.
No need to remake target `build/Sprite.d'.
Considering target file `Makefile'.
Looking for an implicit rule for `Makefile'.
...
Trying pattern rule with stem `Makefile'.
Trying implicit prerequisite `Makefile.o'.
Looking for a rule with intermediate file `Makefile.o'.
I know include causes the given Makefiles to be rebuilt if necessary. Does it also try to rebuild the current Makefile? If so how do I stop it, and if not, then why is "Makefile" being added as a target?
Also, the include is executed, causing the .d files to be remade even if I specify a target on the command line, such as make clean. Is there any way to stop that from happening?
# $(call setsuffix,newsuffix,files)
# Replaces all the suffixes of the given list of files.
setsuffix = $(foreach file,$2,$(subst $(suffix $(file)),$1,$(file)))
# $(call twinfile,newdir,newsuffix,oldfile)
# Turns a path to one file into a path to a corresponding file in a different
# directory with a different suffix.
twinfile = $(addprefix $1,$(call setsuffix,$2,$(notdir $3)))
MAIN = main
SOURCE_DIR = src/
INCLUDE_DIR = include/
BUILD_DIR = build/
SOURCES = $(wildcard $(SOURCE_DIR)*.cpp)
OBJECTS = $(call twinfile,$(BUILD_DIR),.o,$(SOURCES))
DEPENDENCIES = $(call twinfile,$(BUILD_DIR),.d,$(SOURCES))
CXX = g++
LIBS = -lpng
CXXFLAGS = -I $(INCLUDE_DIR)
.PHONY: all
all: $(MAIN)
$(MAIN): $(OBJECTS)
$(CXX) $(LIBS) $^ -o $(MAIN)
include $(DEPENDENCIES)
%.o: $(BUILD_DIR)stamp
$(CXX) $(CXXFLAGS) -c $(call twinfile,$(SOURCE_DIR),.cpp,$#) -o $#
$(BUILD_DIR)%.d: $(SOURCE_DIR)%.cpp $(BUILD_DIR)stamp
# echo Generate dependencies for $ $#.$$$$; \
sed 's,\($*\)\.o[ :]*,$(BUILD_DIR)\1.o $# : ,g' $#; \
rm -f $#.$$$$
$(BUILD_DIR)stamp:
mkdir -p $(BUILD_DIR)
touch $#
.PHONY: clean
clean:
rm -rf $(BUILD_DIR)
.PHONY: printvars
printvars:
# echo $(SOURCES)
# echo $(OBJECTS)
# echo $(DEPENDENCIES)
Make will always try to remake the Makefile before executing the Makefile. To do so, make will look for rules which can be used to recreate the Makefile. Make will look for quite a few implicit rules and other obscure methods to (re)create the Makefile.
In your case, make somehow decided that the pattern rule %.o: $(BUILD_DIR)/stamp should be used to recreate the Makefile, which failed.
To prevent make from remaking the Makefile you can write a rule with an empty recipe:
Makefile: ;
Read the chapter Remaking Makefiles in the make manual for more explanation.
About the included Makefiles: Included Makefiles will always be included, regardless of the target. If the included makefiles are missing (or older than their prerequisites) then they will first be (re)created. That means a make clean will first generate the .d Makefiles, only to delete them again.
You can prevent the including for specific goals by wraping the include directive in a conditional:
ifneq ($(MAKECMDGOALS),clean)
include $(DEPENDENCIES)
endif
Here is your entire Makefile with some fixes. I marked the places where I changed something.
# Makefile
# $(call setsuffix,newsuffix,files)
# Replaces all the suffixes of the given list of files.
setsuffix = $(foreach file,$2,$(subst $(suffix $(file)),$1,$(file)))
# $(call twinfile,newdir,newsuffix,oldfile)
# Turns a path to one file into a path to a corresponding file in a different
# directory with a different suffix.
twinfile = $(addprefix $1/,$(call setsuffix,$2,$(notdir $3)))
MAIN = main
SOURCE_DIR = src
INCLUDE_DIR = include
BUILD_DIR = build
SOURCES = $(wildcard $(SOURCE_DIR)/*.cpp)
OBJECTS = $(call twinfile,$(BUILD_DIR),.o,$(SOURCES))
DEPENDENCIES = $(call twinfile,$(BUILD_DIR),.d,$(SOURCES))
CXX = g++
LIBS = -lpng
CXXFLAGS = -I $(INCLUDE_DIR)
.PHONY: all
all: $(MAIN)
$(MAIN): $(OBJECTS)
$(CXX) $(LIBS) $^ -o $(MAIN)
# -------> only include if goal is not clean <---------
ifneq ($(MAKECMDGOALS),clean)
include $(DEPENDENCIES)
endif
# ---------> fixed this target <--------------
$(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.cpp $(BUILD_DIR)/stamp
$(CXX) $(CXXFLAGS) -c $(call twinfile,$(SOURCE_DIR),.cpp,$#) -o $#
# ---------> and this target <---------------
$(BUILD_DIR)/%.d: $(SOURCE_DIR)/%.cpp $(BUILD_DIR)/stamp
# echo Generate dependencies for $#;
#set -e; rm -f $#; \
$(CC) -M $(CPPFLAGS) $< > $#.$$$$; \
sed 's,\($*\)\.o[ :]*,$(BUILD_DIR)\1.o $# : ,g' < $#.$$$$ > $#; \
rm -f $#.$$$$
$(BUILD_DIR)/stamp:
mkdir -p $(BUILD_DIR)
touch $#
.PHONY: clean
clean:
rm -rf $(BUILD_DIR)
.PHONY: printvars
printvars:
# echo $(SOURCES)
# echo $(OBJECTS)
# echo $(DEPENDENCIES)

Resources