3

I am using gnu Make 3.82 and have an annoying problem.

I have a rule setting dependencies between directories.

OBJDIR=../obj

$(objdir)/%.o: %.C
    $(COMPILE) -MM -MT$(objdir)/$(notdir $@) $< -o $(DEPDIR)/$(notdir $(basename $<).d )
$(COMPILE) -o $(objdir)/$(notdir $@ ) -c $<

In order to do this, the obj directory must exist. I want to mkdir the directory as a prerequisite

$(objdir)/%.o: %.C $(objdir)
    $(COMPILE) -MM -MT$(objdir)/$(notdir $@) $< -o $(DEPDIR)/$(notdir $(basename $<).d )
$(COMPILE) -o $(objdir)/$(notdir $@ ) -c $<

$(objdir):
    mkdir $(objdir)

This doesn't work, because it fails when the directory is there and then the make stops I tried shell

if [ ! -d $(objdir) ] ; then  \
  mkdir $(objdir)             \
fi

but obviously I've got something wrong. What's the best way of doing this?

Dov
  • 8,000
  • 8
  • 46
  • 75
  • 1
    For the correct way to make the directory, see my answer over here: http://stackoverflow.com/questions/3477418/suppress-make-rule-error-output/3554442#3554442 – Jack Kelly Dec 09 '10 at 23:34

2 Answers2

5

One simple way is to use:

mkdir -p ../obj

It doesn't fail when the directory exists.

I usually create a macro, MKPATH, for this:

MKPATH = mkdir -p

and then reference the macro in the rule:

$(objdir):
    $(MKPATH) $(objdir)

That way, I can change the behaviour without changing the makefile if it becomes necessary.


Your shell fragment:

if [ ! -d $(objdir) ] ; then
  mkdir $(objdir)
fi

does not work as written because make executes each line separately.

You could write (note the added semi-colon):

if [ ! -d $(objdir) ] ; then \
  $(MKPATH) $(objdir) ; \
fi

Or:

if [ ! -d $(objdir) ] ; then $(MKPATH) $(objdir); fi

Or:

[ -d $(objdir) ] || $(MKPATH) $(objdir)

Note that the command line must be successful overall, so do not try:

[ ! -d $(objdir) ] && $(MKPATH) $(objdir)

If the directory exists, the first alternative fails, but the shell exits with a non-zero status, thus failing...and causing the build to fail.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    @Dov: Your rule says that the object file depends on the object directory - which means that if the object directory has changed since the object file was last built (for example, because you compiled another file), then your file needs to be rebuilt. Your overall build target depends on the object directory existing; the individual object files do not need rebuilding just because the directory changed. – Jonathan Leffler Dec 14 '10 at 18:33
2

mkdir

"mkdir -p"

Change:

$(objdir): mkdir $(objdir) 

to =>

$(objdir): 
   mkdir -p $(objdir)

If that particular mkdir does not have -p then:

$(objdir): 
   test -d $(objdir) || mkdir $(objdir)

Makefiles

Keep the target: and the comands (mkdir, etc) on seperate lines.

Also, in make, to ignore failed commands, prefix command with minus:

$(objdir):
    -mkdir $(objdir)

Commands (if-then-else; for loops, etc) with multiple lines require adding `\;' to represent newlines to the shell:

$(objdir):
    if [ ! -d $(objdir) ] ; then \
      mkdir $(objdir) ; \
    fi

This particular usage of if-then-else can also written as:

$(objdir):
    if [ ! -d $(objdir) ] ; then mkdir $(objdir) ; fi

The following Makefile that demonstrates each point above

all: setup dirs report

# Create an intefering dir1
# Remove dir2.  It is work to be done later.
setup: 
    @mkdir -p dir1
    @if test -d dir2 ; then rmdir dir2 ; fi

# Continue (with dir2), even though dir1 re-creation fails
dirs:
    -mkdir dir1
    mkdir -v dir2

# Show we're still running
report:
    @echo DIRS:
    @for d in dir?;  do \
        test -d $$d || break ; \
        echo -n "$$d " ; \
    done
    @echo

Output from running running make:

mkdir dir1
mkdir: cannot create directory `dir1': File exists
make: [dirs] Error 1 (ignored)
mkdir -v dir2
mkdir: created directory `dir2'
DIRS:
dir1 dir2 
frayser
  • 1,754
  • 10
  • 17
  • Thanks, the target and the commands are on separate lines, and I tried to use backslashes to escape \n but it didn't work. I'm confused that you have a backslash before the semicolon, that looks like a typo? But you have at least two new solutions to try, one -p, and one test || and I will see if those work. – Dov Dec 10 '10 at 13:58
  • I had a leading - on the mkdir, but that just stops the error from displaying, the rule still fails – Dov Dec 10 '10 at 16:47
  • @Dov: That's right the backslash goes last(corrected). However, -mkdir (with the dash) should allow make to continue: A tested demo Makefile has been added. GNU make 3.81, 3.82 – frayser Dec 10 '10 at 23:38