开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜