I don't know why you consider adding mkdir -p
before each compiler operation to be "hacky"; that's probably what I'd do. However, you can also do it like this if you don't mind all the directories created all the time:
First, you should use :=
for assigning shell variables, not =
. The former is far more efficient. Second, once you have a list of filenames it's easy to compute the list of directories. Try this:
SOURCES := $(shell find src/ -type f -name '*.c')
OBJECTS := $(patsubst src/%.c,obj/%.o,$(SOURCES))
# Compute the obj directories
OBJDIRS := $(sort $(dir $(OBJECTS))
# Create all the obj directories
__dummy := $(shell mkdir -p $(OBJDIRS))
If you really want to have the directory created only when the object is about to be, then you'll have to use second expansion (not tested):
SOURCES := $(shell find src/ -type f -name '*.c')
OBJECTS := $(patsubst src/%.c,obj/%.o,$(SOURCES))
# Compute the obj directories
OBJDIRS := $(sort $(dir $(OBJECTS))
.SECONDEXPANSION:
obj/%.o : src/%.c | $$(@D)
$(CC) -c $< -o $@
$(OBJDIRS):
mkdir -p $@