How to create a directory in a makefile when mkdir -p is not available?
Solution 1
You can replace your forest of mkdir
s with this:
$(Release_target_OBJDIR)/%.o: %.cpp
$(foreach d,$(subst /, ,${@D}),mkdir $d && cd $d && ):
∶
This will create a shell command somethng like this:
mkdir projects && cd projects && mkdir htc && cd htc && mkdir arm && cd arm && :
This runs for every compile. Not very elegant. You could optimise this by using some sort of sentinel file. For instance:
$(Release_target_OBJDIR)/%.o: %.cpp ${Release_target_OBJDIR}/.sentinel
∶
%/.sentinel:
$(foreach d,$(subst /, ,$*),mkdir $d && cd $d && ):
touch $@
.sentinel
gets created once before all objects, and is make -j
friendly. In fact you should do it this way even if mkdir -p
works for you (in which case you would use mkdir -p
rather than the $(foreach)
hacksolution).
Solution 2
You can tell make
to ignore any failure return code from a command using -
:
$(Release_target_OBJDIR)/%.o: %.cpp
-mkdir $(dir $(dir $(dir $@)))
-mkdir $(dir $(dir $@))
-mkdir $(dir $@)
$(COMPILE.cpp) $< $(CFLAGS) $(INCLUDES) -o $@
(Note that this doesn't address the trailing slash problem.)
Simon Elliott
Updated on July 14, 2022Comments
-
Simon Elliott almost 2 years
I have a makefile which does the usual directory creation:
$(Release_target_OBJDIR)/%.o: %.cpp mkdir -p $(dir $@) $(COMPILE.cpp) $< $(CFLAGS) $(INCLUDES) -o $@
Unfortunately when I run this under scratchbox2 the mkdir -p command always fails silently.
I attempted the following kludge which doesn't work:
$(Release_target_OBJDIR)/%.o: %.cpp mkdir $(dir $(dir $(dir $@))) mkdir $(dir $(dir $@)) mkdir $(dir $@) $(COMPILE.cpp) $< $(CFLAGS) $(INCLUDES) -o $@
This outputs:
mkdir -p /home/foo/projects/htc/arm/obj/cbar/release/ mkdir -p /home/foo/projects/htc/arm/obj/cbar/release/ mkdir -p /home/foo/projects/htc/arm/obj/cbar/release/
... the trailing slash prevents the dir function from stripping the last directory in the way I wanted.
Short of writing a script or small C app to replicate the "-p" functionality, does anyone have any ideas for creating the subdirectories within the makefile?
Without the -p option mkdir will give an error when the makefile tries to create a directory which already exists. I can do mkdir blah 2> /dev/null but then I risk losing other error messages.
Does anyone have any thoughts as to why mkdir -p doesn't work under scratchbox2?
EDIT
Based on suggestions by bobbogo I put this together. It looks fairly convoluted, but seems to work, even under scratchbox2.
# Generic variables for use in functions comma:= , empty:= space:= $(empty) $(empty) # Make directory function forlooprange = $(wordlist 1,$(words $1),1 2 3 4 5 6 7 8 9 10) forloop = $(foreach n,$(call forlooprange,$1),$(call $2,$n,$3)) mkdirfunc0 = test -d $1 || mkdir $1; mkdirfunc1 = $(call mkdirfunc0,/$(subst $(space),/,$(foreach n,$(wordlist 1,$1,$2),$n))) mkdirfunc2 = $(call forloop,$1,mkdirfunc1,$1) mkdirmain = $(call mkdirfunc2,$(subst /, ,$1)) .PRECIOUS: %/.sentinel %/.sentinel: $(call mkdirmain,$*) touch $@
-
Simon Elliott over 13 yearsThanks for this. One strange thing: I've added a sentinel at the link stage as well as at the compile stage, to create the output directory. This has (somehow) triggered the make file to generate an rm command for those sentinels, but not the compile time ones. I've added a "touch" to the sentinel rule to create the sentinel. The shell command isn't quite right but you've given me enough information to get something going.
-
bobbogo over 13 years@Simon: Ah yes, I've added the
touch
so that I don't look too stupid. TVM. -
bobbogo over 13 years@Simon: You might need some combination of
.PRECIOUS
and.SECONDARY
. You could also turn the sentinel pattern rule into a Static Pattern Rule (yay!). -
Simon Elliott over 13 years@ bobbogo: Thanks, .PRECIOUS did the business, and as a bonus led me to the right part of the gnu make docs to find out all about intermediate targets.
-
zeuxcg over 10 years@bobbogo Can you expand on 'make -j'-friendliness? I'm currently using mkdir -p in every compilation, but I got this to work: gist.github.com/zeux/8906196. I kinda liked the old approach better from the simplicity point.
-
bobbogo over 10 years@Simon No, the old approach (by which I assume you mean "every compile does a mkdir") is a hack. Consider what happens if you use
make -j
with the old approach. You potentially get manymkdir
s all running at once! Ugh, anything could happen. With the.sentinel
formulation, themkdir
runs at most only once. I say "at most," because once the sentinal has been created, make will never feel the urge to run themkdir
again. Even on a second run.