2

The GNU standard says it is a good idea to not use the -p command for mkdir:

For example, don’t use ‘mkdir -p’, convenient as it may be, because a few systems don’t support it at all and with others, it is not safe for parallel execution

I would like to adhere to this standard and thus I am running into a problem in my Makefile.

I am recursively compiling all C sources from all directories within the src directory and would like to place them inside the obj directory. The same directory structure is mirrored inside the obj directory, however these directories do not initially exist.

I could very easily do mkdir -p $(@D) to create my directories, but following GNU's standard, I cannot do this, which brings me to my question: How can I work around this in a Makefile?

My Makefile below:

SRCDIR=src
OBJDIR=obj

CC=cc

CFLAGS=
CFLAGS=-g -O2 -pedantic -Wall
ALL_CFLAGS=-std=c89 $(CFLAGS)

# Copied from https://stackoverflow.com/questions/2483182/recursive-wildcards-in-gnu-make/18258352#18258352
rwildcard=$(foreach d, $(wildcard $1/*), $(call rwildcard, $d, $2) $(filter $(subst *, %, $2), $d))

SOURCES=$(call rwildcard, $(SRCDIR), *.c)
OBJECTS=$(subst $(SRCDIR),$(OBJDIR),$(SOURCES:.c=.o))

all: $(OBJECTS)

$(OBJDIR)/%.o: $(SRCDIR)/%.c
    $(CC) $(ALL_CFLAGS) -c -o $@ $<

2 Answers2

1

The GNU standards are outdated in many places. This appears to be one of these places.

Nowadays, mkdir -p is part of POSIX. It is not even a recent invention because it came from System V.

Since your makefile assumes that the C compiler is GCC (or at least has a GCC-compatible command line syntax), you might as well assume that mkdir -p works. If you want users an easy way to override it if it does not for them, you could add this:

MKDIR_P = mkdir -p

and use $(MKDIR_P) in recipes, in place of mkdir -p.

But all this really shouldn't be necessary. Even GNU's supposedly-portable mkinstalldirs script has been using mkdir -p for a long, long time.

Florian Weimer
  • 32,022
  • 3
  • 48
  • 92
  • I thought the standard was up to date (last updated this year) but clearly not. Thank you for the clarification. –  Oct 05 '19 at 11:16
  • There are very old systems which don't support things properly, that are still in use in some dusty corners. Unless you need to be portable to these old and obscure systems you don't have to worry about them. In general, it's probably not worthwhile to try to be that portable unless you're also using the entirety of GNU autotools like autoconf and automake, which have their own facilities for dealing with these older systems. – MadScientist Oct 05 '19 at 19:40
0

You could just use make dependencies. Drop a file, .created say, in each folder. Build the .created files with a pattern rule, which naturally creates the folder as a side effect.

SOURCES=$(call rwildcard, $(SRCDIR), *.c)
OBJECTS=$(subst $(SRCDIR),$(OBJDIR),$(SOURCES:.c=.o))

.PHONY: all
all: $(OBJECTS)

%/.created:
    mkdir -p ${@D}
    touch $@

$(OBJDIR)/%.o: $(SRCDIR)/%.c | ${OBJDIR}/.created
    $(CC) $(ALL_CFLAGS) -c -o $@ $<

It means that folders are created once by make, and only when they are needed. Parallel safe too.

[This is just a sketch. Your substitution doesn't look right to me — I assume you have no src/ folder under another src/ folder(???), and your %.o pattern is not quite right.]

bobbogo
  • 14,989
  • 3
  • 48
  • 57