That behaviour is not a bug. It is a feature. A feature and a possible user error to be precise.
The feature in question is one of the implicit rules of Make. In your case the implicit rule to "build" *.sh
files. The user error, your error, is not changing the working directory before invoking the makefile in the subdirectories.
TL; DR: to fix this you can do one or more of the following:
Fix the shell script to change the working directory:
#!/bin/bash
for f in *; do
if [[ -d $f && -f $f/makefile ]]; then
echo "Making clean in $f..."
(cd $f; make clean)
fi
done
Make the empty rules explicit:
clean: ;
Make the clean
targets phony:
.PHONY: clean
Detailed explanation:
Make has a bunch of implicit rules. This allows one to invoke make on simple projects without even writing a makefile.
Try this for a demonstration:
- create an empty directory and change in to the directory
- create a file named
clean.sh
.
- run
make clean
Output:
$ make clean
cat clean.sh >clean
chmod a+x clean
BAM! That is the power of implict rules of make. See the make manual about implicit rules for more information.
I will try to answer the remaining open question:
Why does it not invoke the implicit rule for the first makefile? Because you overwrote the implicit rule with your explicit clean
rule.
Why does the clean
rule in the second makefile not overwrite the implicit rule? Because it had no recipe. Rules with no recipe do not overwrite the implicit rules, instead they just append prerequisites. See the make manual about multiple rules for more information. See also the make manual about rules with explicit empty recipes.
Why is it an error to not change the working directory before invoking a makefile in a subdirectory? Because make does not change the working directory. Make will work in the inherited working directory. Well technically this is not necessarily an error, but most of the time it is. Do you want the makefiles in the subdirectories to work in the subdirectories? Or do you want them to work in the parent directory?
Why does make ignore the explicit clean
rule from the first makefile in the second invocation of clean.sh
? Because now the target file clean
already exists. Since the rule clean
has no prerequisites there is no need to rebuild the target. See the make manual about phony targets which describes exactly this problem.
Why does make search for the target three/makefile
in the third invocation? Because make always tries to remake the makefiles before doing anything else. This is especially true if the makefile is explicitly requested using -f
but it does not exists. See the make manual about remaking makefiles for more information.
You can inspect MAKECMDGOALS
to detect the presence of one goal while building another goal. Either make the %: %.icl
run detect the presence of the run
goal, or make the run
goal inspect what executables are mentioned as targets. If you pass more than one executable as a target, the first method causes each to be run right after it's built, while the second causes all the runs to happen at the end.
The downside of this approach is that it doesn't scale well with other features. For example, if you define a target with multiple executables as dependencies, the method with a run
target won't work.
EXE = foo bar experimental
released: foo bar
run: $(filter $(EXE), $(MAKECMDGOALS))
set -e; for x in $^; do ./$x; done
Here make released run
won't run anything.
What I normally do for this case is to define a “run” target for each executable. This keeps each target expressed as a single word, which is a major advantage. It's simple and doesn't break other features.
$(EXE): %.run: %
./$(@:.run=)
all.run: $(EXE:=.run)
Then I run make {foo,bar}.run
if I want to built and test foo
and bar
, or make all.run
to build and run them all.
Best Answer
Problem:
You problem is that
make
doesn't know about your targets.You can run your above Makefile with
make stackoverflow.markdown
for example and it will work.make
only, however, will fail, since you only specified how to create your targets, but not which.As leiaz point's out the above pattern rule is called an implicit rule.
Makefile:
Explanation:
SRC
get's all source files (those ending in.html
) via Makefile'swildcard
.TAR substitutes each source file listed in
SRC
with a target ending with.markdown
instead of.html
..PHONY lists non-physical targets that are always out-of-date and are therefore always executed - these are often
all
andclean
.The target
all
has as dependency (files listed on the right side of the:
) all*.markdown
files. This means all these targets are executed.This snippet says: Each target ending with
.markdown
is depended on a file with the same name, except that the dependency is ending with.html
. The wildcard%
is to be seen as a*
like in shell. The%
on the right side, however, is compared to the match on the left side. Source.Note that the whitespace sequence infront of
pandoc
is a TAB, sincemake
defines that as a standard.Finally, the phony
clean
target depicts how to clean your system from the files you've created with this Makefile. In this case, it's deleting all targets (those files that were named*.markdown
.