Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a directory in a makefile when mkdir -p is not available?

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 $@
like image 522
Simon Elliott Avatar asked Mar 04 '11 11:03

Simon Elliott


People also ask

How do I create a Makefile folder?

To use this makefile, simply cd to the directory and type “ makepp ”. Makepp will attempt to build the first target in the makefile, which is my_program . (If you don't want it to build the first target, then you have to supply a the name of the target you actually want to build on the command line.)

How do I get the current directory in Makefile?

You can use shell function: current_dir = $(shell pwd) . Or shell in combination with notdir , if you need not absolute path: current_dir = $(notdir $(shell pwd)) .

Can Makefile target be a directory?

Yes, a Makefile can have a directory as target. However, you shouldn't. When a file is added or removed from a directory, its mtime is updated. This can cause weird behaviour by causing multiple targets depending on a single directory to become implicitly rebuilt by one another.

How to create a directory in Java if it does not exist?

You can use the Java File class to create directories if they don't already exists. The File class contains the method mkdir() and mkdirs() for that purpose. The mkdir() method creates a single directory if it does not already exist.


2 Answers

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).

like image 94
bobbogo Avatar answered Oct 13 '22 11:10

bobbogo


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.)

like image 32
Greg Hewgill Avatar answered Oct 13 '22 11:10

Greg Hewgill