thetic
thetic

Reputation: 193

Is this a robust solution to creating output directories in a Makefile?

I've seen a few approaches to making output directories in Make. These include making all directories ahead of time outside of any rule, and making an object's destination directory as part of the object's rule. Both of these approaches involve making directories that likely already exist.

Am I missing any gotchas or drawbacks that explain why I haven't seen the below approach?

.SECONDEXPANSION:

$(OBJDIR)%.o: %.c | $$(@D)/
    # Compile command

.PRECIOUS: %/
%/:
    # mkdir Command

Upvotes: 2

Views: 111

Answers (2)

user2371524
user2371524

Reputation:

The problem with directories is that (contrary to any other target) you don't care for their timestamp, you only need them to exist. Many Makefiles get directories somehow wrong, and creating them over and over again is what you observe, so make will never detect "Nothing to be done for ...".

In fact, the only thing you need for correct handling of directories with GNU make is an "order only dependency", like shown in your example. The trailing slash normally isn't needed (you seem to use it in order to have a pattern rule, I'm not sure whether this works), and you don't need .PRECIOUS either. Your trick with .SECONDEXPANSION looks quite neat, I guess this will work, given the pattern rule indeed works that way (didn't try).

For an alternative, most Makefiles that handle directories correctly take a simpler approach by concatenating all needed output directories for a rule in a single variable and use this variable as a target for another rule, e.g. like in this simplified example:

MODULES:=src/main

OBJDIR?=obj

OBJS:=$(addprefix $(OBJDIR)/,$(addsuffix .c,$(MODULES)))
DIRS:=$(sort $(addprefix $(OBJDIR)/,$(dir $(OBJS))))

TARGET:= myprogram

all: $(TARGET)

myprogram: $(OBJS)
    $(CC) -o$@ $^

$(DIRS):
    mkdir -p $(DIRS)

$(OBJDIR)/%.o: %.c Makefile | $(DIRS)
    $(CC) -c -o$@ $<

clean:
    rm -fr $(OBJDIR)

.PHONY: all clean

Upvotes: 0

ndim
ndim

Reputation: 37787

make is very good at dealing with files. make is not very good at dealing with directories.

So treating directories as implementation detail internal to the target rule makes sense, because then make never has to consider the directory at all:

MKDIR_P = mkdir -p

$(objdir)%.o: %.c
    @$(MKDIR_P) $(@D)
    $(COMPILE.c) -o $@ -c $<

Note that the processing and IO required for the mkdir -p can be neglected next to the processing and IO required for the compilation.

Upvotes: 1

Related Questions