206

I have a makefile structured 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?

sas4740
  • 4,510
  • 8
  • 26
  • 23

3 Answers3

277

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!")
Community
  • 1
  • 1
Dacav
  • 13,590
  • 11
  • 60
  • 87
  • 7
    @sas4740: basically everything follows `.PHONY : ` is treated as some keyword that gets always executed, while non-phony targets are intended to be files. – Dacav Jul 16 '10 at 17:27
  • are "Order-only prerequisites" conditional? for target t2 I want first do t0, than only if t0 succeds run t1, and only if both succeds run some task in t3 – Fantastory Mar 12 '15 at 07:32
  • 1
    @fantastory, no, I **think** they are independent. `t2` will depend on `t0`, `t1` and `t3`. If you need this you should put `t3` as required by `t2`, `t1` as required by `t3` and `t0` as required by `t1`. This means 3 different rules. You should verify this, however. I'm not 100% sure. – Dacav Mar 12 '15 at 10:34
  • 3
    "Order-only prerequisites" are independent – Fantastory Mar 23 '15 at 13:57
  • 4
    I don't see where is the guarantee that 'clean' performs before 'all'? The fact that you put them right from | does not make them execute in-order. Order-only dependency means that target is not necessairly updated after such operation. It has nothing to do with ordering of the dependent elements... or? – CygnusX1 Nov 13 '16 at 17:06
  • 2
    You are right @CygnusX1, order-only dependencies have nothing to do with executing dependencies in a certain order; only with ordering the dependency before the target that references it. The `EDIT Aug 4` part of this answer is wrong. – giorgiosironi Nov 21 '19 at 14:53
  • Ugh! this breaks parallel _make_. Parallel operation is kinda the whole point of _make_. – bobbogo Nov 15 '21 at 13:58
19

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)
Piotr Koper
  • 191
  • 1
  • 3
8

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.

codenaugh
  • 857
  • 2
  • 12
  • 27