8

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 $@
jww
  • 97,681
  • 90
  • 411
  • 885
Simon Elliott
  • 2,087
  • 4
  • 30
  • 39

2 Answers2

4

You can replace your forest of mkdirs 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).

bobbogo
  • 14,989
  • 3
  • 48
  • 57
  • Thanks 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. – Simon Elliott Mar 04 '11 at 14:18
  • @Simon: Ah yes, I've added the `touch` so that I don't look too stupid. TVM. – bobbogo Mar 04 '11 at 14:26
  • @Simon: You might need some combination of `.PRECIOUS` and `.SECONDARY`. You could also turn the sentinel pattern rule into a Static Pattern Rule (yay!). – bobbogo Mar 04 '11 at 14:30
  • @ 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. – Simon Elliott Mar 04 '11 at 15:56
  • @bobbogo Can you expand on 'make -j'-friendliness? I'm currently using mkdir -p in every compilation, but I got this to work: https://gist.github.com/zeux/8906196. I kinda liked the old approach better from the simplicity point. – zeuxcg Feb 09 '14 at 21:30
  • @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 _many_ `mkdir`s all running _at once_! Ugh, anything could happen. With the `.sentinel` formulation, the `mkdir` runs _at most_ only once. I say "at most," because once the sentinal has been created, _make_ will never feel the urge to run the `mkdir ` again. Even on a second run. – bobbogo Feb 12 '14 at 17:56
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.)

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285