I have been creating a Makefile to compile code into an executable. For my setup, the directory structure of my source files needs to match up with the structure of the intermediate object files. Hence, a source file of path Sources/foo/bar.c
would need to have a corresponding object file of path Build/foo/bar.o
. And, hence, a directory of path Build/foo/
would need to be created automatically for the bar.o
object file.
And, to accomplish this, I used a pattern rule and the dir
function to retrieve the directory part of the target. What follows is how I attempted to achieve this:
Build/%.o: Sources/%.c | $(dir Build/%)
gcc ... compiler stuff
%/:
mkdir -p "$@"
However, when running Make, the %
in $(dir Build/%)
was interpreted by Make literally and not replaced by the file name. Thus, if the %
were foo/bar/baz
, $(dir Build/%)
would still be interpreted as just Build/
. What I intended for Make to do was to replace $(dir Build/%)
with, say $(dir Build/foo/bar/baz)
and expand that to Build/foo/bar/
- getting rid of the baz
.
Because this didn't work, I tried to find any other way to do it, but couldn't. And, I don't want to ever unnecessarily run mkdir
by putting the command in the recipe of the object files. So, I have turned to Stack Overflow for a solution.
That doesn't work, because (see the GNU Make manual) variables and functions in target and prerequisite lists are expanded immediately when the makefile is read in, while pattern rules are not matched until much later. So this:
Build/%.o: Sources/%.c | $(dir Build/%)
expands to this:
Build/%.o: Sources/%.c | Build/
By far the simplest, most reliable, and easiest to understand solution is to add mkdir -p
into the recipe like this:
Build/%.o: Sources/%.c
@mkdir -p $(@D)
gcc ... compiler stuff
If you are really committed to not running this, then you'll have to use secondary expansion, like this:
.SECONDEXPANSION:
Build/%.o: Sources/%.c | $$(@D)
gcc ... compiler stuff
(note I didn't test this solution).