makefile execute another target
I have a makefile structured 开发者_Python百科something like this:
all :
compile executable
clean :
rm -f *.o $(EXEC)
I realized that I was consistently running "make clean" followed by "clear" in my terminal before running "make all". I like to have a clean terminal before I try and sift through nasty C++ compilation errors. So I tried to add a 3rd target:
fresh :
rm -f *.o $(EXEC)
clear
make all
This works, however this runs a second instance of make (I believe). Is there a right way to get the same functionality without running a 2nd instance of make?
Actually you are right: it runs another instance of make. A possible solution would be:
.PHONY : clearscr fresh clean all
all :
compile executable
clean :
rm -f *.o $(EXEC)
fresh : clean clearscr all
clearscr:
clear
By calling make fresh
you get first the clean
target, then the clearscreen
which runs clear
and finally all
which does the job.
EDIT Aug 4
What happens in the case of parallel builds with make’s -j
option?
There's a way of fixing the order. From the make manual, section 4.2:
Occasionally, however, you have a situation where you want to impose a specific ordering on the rules to be invoked without forcing the target to be updated if one of those rules is executed. In that case, you want to define order-only prerequisites. Order-only prerequisites can be specified by placing a pipe symbol (|) in the prerequisites list: any prerequisites to the left of the pipe symbol are normal; any prerequisites to the right are order-only: targets : normal-prerequisites | order-only-prerequisites
The normal prerequisites section may of course be empty. Also, you may still declare multiple lines of prerequisites for the same target: they are appended appropriately. Note that if you declare the same file to be both a normal and an order-only prerequisite, the normal prerequisite takes precedence (since they are a strict superset of the behavior of an order-only prerequisite).
Hence the makefile becomes
.PHONY : clearscr fresh clean all
all :
compile executable
clean :
rm -f *.o $(EXEC)
fresh : | clean clearscr all
clearscr:
clear
EDIT Dec 5
It is not a big deal to run more than one makefile instance since each command inside the task will be a sub-shell anyways. But you can have reusable methods using the call function.
log_success = (echo "\x1B[32m>> $1\x1B[39m")
log_error = (>&2 echo "\x1B[31m>> $1\x1B[39m" && exit 1)
install:
@[ "$(AWS_PROFILE)" ] || $(call log_error, "AWS_PROFILE not set!")
command1 # this line will be a subshell
command2 # this line will be another subshell
@command3 # Use `@` to hide the command line
$(call log_error, "It works, yey!")
uninstall:
@[ "$(AWS_PROFILE)" ] || $(call log_error, "AWS_PROFILE not set!")
....
$(call log_error, "Nuked!")
You already have a sequential solution which could be rewritten as:
fresh:
$(MAKE) clean
clear
$(MAKE) all
This is correct and a very safe approach.
Sequential target execution is possible in GNU make with a proper dependency graph:
fresh: _all
_all: _clear
Recipe for all
_clear: _clean
Recipe for clear
_clean:
Recipe for clean
The above rules define the following dependency graph: fresh
<- _all
<- _clear
<- _clean
which guarantees the following recipe execution order: Recipe for clean
, Recipe for clear
, Recipe for all
.
Recipes can be shared with multiple targets using:
target1 target2 target…:
recipe1
Merging your script with the above concepts results in:
all _all :
compile executable
clean _clean :
rm -f *.o $(EXEC)
clear _clear :
clear
fresh: _all
_all: _clear
_clear: _clean
With syntactic sugar using chains.mk
from https://github.com/pkoper/mk/ you can write:
all all@fresh :
compile executable
clean clean@fresh :
rm -f *.o $(EXEC)
clear clear@fresh :
clear
@fresh = clean clear all
include chains.mk
fresh: @fresh
Or better:
all: compile
@fresh = clean clear compile
include chains.mk
fresh: @fresh
compile compile@fresh:
compile executable
clear clear@fresh:
clear
clean clean@fresh:
rm -f *.o $(EXEC)
If you removed the make all
line from your "fresh" target:
fresh :
rm -f *.o $(EXEC)
clear
You could simply run the command make fresh all
, which will execute as make fresh; make all
.
Some might consider this as a second instance of make, but it's certainly not a sub-instance of make (a make inside of a make), which is what your attempt seemed to result in.
精彩评论