8

I have a Makefile containing rules for building a small project and cleaning it. For example:

CC:=gcc
LD:=gcc
SOURCES:=$(wildcard src/*.c)
OBJS:=$(SOURCES:src/%.c=build/%.o)
TARGET:=bin/program

all: $(TARGET)

$(TARGET): $(OBJS)
    @mkdir -p bin
    $(LD) $+ -o $@

build/%.o: src/%.c
    @mkdir -p build
    $(CC) $+ -c -o $@

clean:
    rm -rf $(OBJS)
    rm -rf $(TARGET)
    rmdir bin
    rmdir build

.PHONY: clean all

I am now interested in creating a rule rebuild which would perform clean and all in that order. I do not see how properly achieve the correct ordering.


The solutions I have seen are wrong to my knowledge.

rebuild: clean all
.PHONY: rebuild

Is naturally wrong, because there is no guarantee that dependencies are actually performed in the order of their appearance. all may execute before clean.

I have seen answers suggesting order-only dependencies, e.g.

rebuild: | clean all
.PHONY: rebuild

To my knowledge, this does not solve the problem. If you say a: | b c it means that a depends on b and c, but if b or c is taken, it does not force executing the a rule. It has nothing to do with ordering the dependencies.

The only option I see right now is launching a new instance of make, by having

rebuild : clean
    make build

I would really like to avoid launching a new make instance for doing something simple like that!


I did some reasearch on SO. I have seen similar questions but no correct answer. To my knolwedge, making a target .PHONY or using order-only dependencies is not a solution.

CygnusX1
  • 20,968
  • 5
  • 65
  • 109

3 Answers3

10

First, it's not true that in a rule:

rebuild: clean all

that all could be built before clean when running serially (that is, without parallelism -j enabled). Make does always build prerequisites in the order they are listed in the makefile (there is a special case for the rule containing the recipe but that's not relevant here). When parallel builds are used then make still walks the dependency tree in the same order but because rules are built in parallel they may not be started in the same order.

However, you're right that this rule is not a great idea for other reasons (directory caching, etc.)

I recommend you use recursive make invocations to do this:

.PHONY: rebuild
rebuild:
        $(MAKE) clean
        $(MAKE) all
MadScientist
  • 92,819
  • 9
  • 109
  • 136
2

There is a way to do it without recursion; the price is a small amount of redundancy:

.PHONY: rebuild
$(TARGET) rebuild: $(OBJS)
    @mkdir -p bin
    $(LD) $+ -o $(TARGET) # note that I have replaced $@ with $(TARGET)

rebuild: | clean
Beta
  • 96,650
  • 16
  • 149
  • 150
2

You can use MAKECMDGOALS to conditionally add a dependency between clean and all when the goal is rebuild. Something like this:

ifeq (rebuild,$(findstring rebuild,${MAKECMDGOALS}))
all: clean
rebuild: all
endif

Now, I don't really see the benefit of doing this in the makefile when there are other trivial and safe ways to do it (just "make clean && make all" might be a better option)

Come Raczy
  • 1,590
  • 17
  • 26