Optional depencency on target in Makefile - makefile

Is it possible to create an optional dependency on a target in Makefile (GNU Make)?
help:
#echo Usage:
clean:
#echo Cleaning...
build: clean?
#echo Building...
The expected output:
$ make
Usage:
$ make clean
Cleaning...
$ make build
Building...
$ make build clean
Cleaning...
Building...
$ make clean build
Cleaning...
Building...
The set of targets is fixed and can not be extended, i.e.
rebuild: clean build
.PHONY: rebuild
is not acceptable.

Yes, you can do this:
build: $(filter clean,$(MAKECMDGOALS))
#echo Building...
See: https://www.gnu.org/software/make/manual/html_node/Goals.html
and https://www.gnu.org/software/make/manual/html_node/Text-Functions.html#index-filter

Related

Impose an order for the prerequisites of a target

I have a makefile snippet:
all: $(objects)
fresh: clean all
clean: ;rm $(objects)
Here, I want to ensure that when I do make fresh - clean should precede all.
But how can I make sure this, given that when I do make all, clean should not be made?
I can imagine that one way could be like this
fresh: clean
make all
Is it the right (or the only) way to solve this issue?
If you use GNU make:
all:
#echo $#
#sleep 1
#echo end $#
clean:
#echo $#
#sleep 1
#echo end $#
fresh:: clean
fresh:: all
.PHONY: clean fresh all
Please note the double colon after targets fresh! See the documentation:
The double-colon rules for a target are executed in the order they
appear in the makefile.
If you run make -j2 fresh it shows it works as expect:
clean
end clean
all
end all
But with fresh:: clean all doesn't work properly parallel (maybe unexpected).
With BSD make:
all:
#echo $#
#sleep 1
#echo end $#
clean:
#echo $#
#sleep 1
#echo end $#
fresh: clean all
#echo $#
.ORDER: clean all
.PHONY: clean all fresh
Note the line begin with .ORDER. It works well in parallelization too (see man make). Without parallelization the order of dependencies in line fresh: counts.
As you already suggest in your question, calling make recursively on the same makefile for the target all in a recipe whose prerequisite is clean:
# At the very beginning of the makefile
CURRENT_MAKEFILE := $(lastword $(MAKEFILE_LIST))
# ...
.PHONY: fresh
fresh: clean
$(MAKE) -f $(CURRENT_MAKEFILE) all
This imposes an order, since the target fresh depends on the prerequisite clean, clean's recipe will be executed before fresh's recipe, which in turn will execute all's recipe.
Note that I'm using here $(MAKE) instead of make for the recursion.

Impose an order for Order-only-prerequisites of a target

I have a makefile snippet:
all: $(objects)
fresh: all | clean directory
directory: ;mkdir -p OutputDirectory
clean: ;rm $(objects); rm -rf OutputDirectory
Here, I want to ensure that when I do make fresh - clean should succeed by directory which should be followed by all.
Semantically, here it might not make sense for clean to be order only prerequisite. Assume it to some order only dependency that has to be executed in some order.
The following link shows similar problem but for normal dependencies:
makefile - Impose an order for the prerequisites of a target - Stack Overflow
In fresh's recipe, you could call make twice recursively on the same makefile, for the target that creates the directory and the all target, respectively:
# At the very beginning of the makefile
CURRENT_MAKEFILE := $(lastword $(MAKEFILE_LIST))
# ...
.PHONY: all clean fresh
directory := OutputDirectory
all: $(objects)
fresh: clean
$(MAKE) -f $(CURRENT_MAKEFILE) $(directory)
$(MAKE) -f $(CURRENT_MAKEFILE) all
$(directory): ;mkdir -p $#
clean: ;rm -f $(objects); rm -rf $(directory)
This way, the target all is preceded by the target $(directory), which is in turn preceded by clean.

Why does this make file only call one target?

I am new to make and I am trying to make a super simple build script. This is what I have:
.PHONY: all main
all:
mkdir -p build && cd build
main: main.o install
g++ -o main main.o
main.o: ../src/main.cpp
g++ -c ../src/main.cpp
.PHONY: install
install:
mkdir -p build
mv main.o build
.PHONY: clean
clean:
rm -r build/
I would expect it to call all followed by main. In actuality, here's what happens:
$ make
mkdir -p build && cd build
Only all is called and main is not ran. Why? I have main as a prerequisite after all in the .PHONY line. And help?
.PHONY is not a real target (it is a special make construct), and does not cause its prerequisites to be run. Instead, the first real target mentioned is all, and if you just type make, it will invoke the all as the default target. Because all is not dependent on anything, it is the only target that is run.
You can add a line at the very top:
default: all main
which will cause both all and main to run (don't forget to add default to .PHONY. Notice though that you are not guaranteed that all will run before main. If you want to guarantee this, you would also have to add
main: all
which would force all to be run before main

makefile parallel clean+compile issue

I have a simple makefile with 3 build rules:
clean (that cleans the .o)
debug (compiles my code with debgging stuff)
release (compiles my code with optimization stuff)
sometimes I want to switch between debug mode and release so I would issue this
make clean debug -j8
or
make clean release -j8
that has a drawback because while it's doing the clean stuff, the -j8 allows make to jump some command since the .o are still there Then those .o are removed by the clean rule and the compiler complains because it can't find those .o
I could do something like
make clean; make debug -j8
but since I use an odd makefile in another dir, the command becomes
make -C ../src -f nMakefile clean ; make -C ../src -f nMakefile -j8 release
that is more annoying. I was wondering if there was an hiddedn-guru-mode-rule that allows me to do it in one line
Hope it's clear enough...
I needed to solve this very same problem, and the solution I came up was to parse the MAKECMDGOALS for clean, and dispatch a shell command to do the actual cleaning work; RATHER than clean the build as a target. This way, any MAKECMDGOALS that include "clean" will clean the build as part of that build, first, sequentially, rather than clean running asynchronously as its own target.
-include $(deps)
bin/%.o : %.cpp
#mkdir -p $#D
g++ $(flags) $(includes) -MMD -c $< -o $#
.PHONY : clean
clean:
#echo rm -rf bin/
ifneq ($(filter clean,$(MAKECMDGOALS)),)
$(shell rm -rf bin/)
endif
As I stated above, the normal practice is to have different sub directories for the object files. As you are running in parallel I would think you need to enforce serial execution so that clean is completed before release. One way of doing it could be:
clean_release: clean
+#$(MAKE) -s --no-print-directory release
or if you prefer
clean_release:
+#$(MAKE) -s --no-print-directory clean && $(MAKE) -s --no-print-directory release

How can I get a Makefile target to be called multiple times?

In the simple example below, I want to do a make dist and have the distclean target executed before distdebug and distrelease.
.PHONY: distclean
dist: distdebug distrelease
#echo in dist
distdebug: distclean
#echo in distdebug
distrelease:
#echo in distrelease
distclean:
#echo in distclean
Unfortunately, despite using .PHONY, distclean is only called once:
vagrant#precise32:/tmp$ make dist
in distclean
in distdebug
in distrelease
in dist
I'm guessing that Make knows that its already run distclean once, so it's not running it again. How can I make it run distclean multiple times?
You can't using normal rules. Make will only ever build any given target exactly one time. You can do it using recursive make invocations:
distdebug:
$(MAKE) distclean
#echo in distdebug
distrelease:
$(MAKE) distclean
#echo in distrelease
In general it's a good idea to plan your build differently, though, and have targets for different types of builds put into different subdirectories. That way you don't HAVE to clean in between.
I was looking for something similar, i.e. performing the same pattern (%) target more than once. And I don't like recursive make invocations. Anyway, I ended up in this solution:
run-%: build-%
#echo "run-$(basename $*)"
build-%:
#echo "build-$*"
build-%.2: build-%; #true
build-%.3: build-%; #true
run: run-foo run-foo.2 run-foo.3 run-bar
#true
alternatively:
run: run-foo run-foo.2 run-bar run-foo.3
#true
build-%:
#echo "build-$*"
.SECONDEXPANSION:
run-%: build-$$(basename $$*)
#echo "run-$(basename $*)"
This makefile can run-anything up to three times by adding the extensions .2 and .3 to anything, but it will only build anything once. I hope you get the idea.
> make run
build-foo
run-foo
run-foo
run-foo
build-bar
run-bar
You don't need recursive make calls or enumerated rules. You can just using pattern matching to generate a different distclean alias for your debug and release steps:
.PHONY: dist%
dist: distdebug distrelease
#echo in dist
distclean-%: # matches to distclean-debug, distclean-release etc
#echo in distclean
distdebug distrelease: dist% : distclean-% dist-% # clean first, then run
dist-debug:
#echo in distdebug
dist-release:
#echo in distrelease
So you can make and get:
in distclean
in distdebug
in distclean
in distrelease
in dist

Resources