| 
Home arrow Articles arrow Columns arrow Ask Mr. Make arrow Making directories in GNU Make
Making directories in GNU Make Print
Written by John Graham-Cumming   
Monday, 10 July 2006

One common problem faced by real-world Makefile hackers is the name to build a heirarchy of directories before the build, or at least before commands that use those directories can run. The most common case is that the Makefile hacker wants to ensure that the directories where object files will be created exist, and they want that to happen automatically. This article looks at a variety of ways to achieve directory creation in GNU Make and points out a common trap for the unwary.

A simple example

The following simple Makefile builds an object file /out/foo.o from foo.c using the GNU Make built in variable $(COMPILE.C) to make a .o file from a .c by running the compiler.

foo.c is in the same directory as the Makefile, but foo.o is placed in /out/.

.PHONY: all
all: /out/foo.o

/out/foo.o: foo.c
    @$(COMPILE.C) -o $@ $<

This example works fine as lone as /out/ exists, if it does not you\'ll get an error from the compiler along the lines of

Assembler messages:
FATAL: can\'t create /out/foo.o: No such file or directory
make: *** [/out/foo.o] Error 1

Obviously what you\'d like to do is have the Makefile create /out/ automatically if it is missing.   

What not to do

Since GNU Make excels at making things that don\'t exist when necessary it seems obvious to make /out/ a prerequisite of /out/foo.o and have a rule to make the directory.  That way if we need to build /out/foo.o the directory will get created.

Here\'s the reworked Makefile with the directory as a prerequisite and a rule to build the directory using mkdir.   To make things simple I\'ve stored the name of the output directory in variable OUT so that I don\'t have to keep repeating myself, and I\'ve specified the -p option on the mkdir command which will be all the necessary parent directories.  In this case the path is simple, it\'s just /out/ but -p means that mkdir could make a long chain of directories in one go.

OUT = /out

.PHONY: all
all: $(OUT)/foo.o

$(OUT)/foo.o: foo.c $(OUT)/
    @$(COMPILE.C) -o $@ $<

$(OUT)/:
    mkdir -p $@

This works well for this simple example, but there\'s a major problem.  Since the timestamp on a directory is typically updated when any of the files inside the directory are updated this Makefile does too much work.

For example, just touching a random file inside /out/ forces a rebuild of /out/foo.o.  In a complex example this could mean that many object files are rebuilt for no good reason, just because other files were rebuilt in the same directory.

Solution 1: build the directory when the Makefile is parsed

A simple solution to the problem above is to just create the directory when the Makefile is parsed.   A quick call to $(shell) can achieve that.  Before any targets are created or commands run the Makefile is read and parsed.  If you put $(shell mkdir -p $(OUT)) somewhere in the Makefile then GNU Make will run the mkdir every time the Makefile is loaded.

OUT = /out

.PHONY: all
all: $(OUT)/foo.o

$(OUT)/foo.o: foo.c
    @$(COMPILE.C) -o $@ $<

$(shell   mkdir -p $(OUT))

The disadvantage of the simplicty is that if there are many directories to be created this could be slow.  And GNU Make is doing unnecessary work since it will attempt to build the directories every time you type make.

Solution 2: build the directory when all is built

A related solution is to only build the directory when all is being built.  This means that the directories won\'t get created every time the Makefile is parsed (which could avoid unnecessary work when you type make clean or make depend).

OUT = /out

.PHONY: all
all: make_directories $(OUT)/foo.o

$(OUT)/foo.o: foo.c
    @$(COMPILE.C) -o $@ $<

.PHONY: make_directories
make_directories: $(OUT)/

$(OUT)/:
    mkdir -p $@


TODO use a %/ pattern rule

Another disadvantage of this technique is that you must specify make_directories as a prerequisite of any target that the user might specify after make.  If you don\'t you could run into the situation where the directories have not been built.

Solution 3: use a directory \'marker\' file

If you look back at the original broken solution there\'s one rather nice feature: it builds the directory needed for a specific target and only that directory.   In a more complex example (where there were many such directories to be built) it would be nice to be able to use something like that solution while avoiding the problem of constant rebuilds as the timestamp on the directory changes.

To do that you can store a special empty file, that I call a \'marker\' file in the directory and use that as the prerequisite.  Since it\'s a normal file, normal GNU Make rebuilding rules apply and its timestamp is not affected by changes in its directory.

If you add a rule to build the marker file (and to ensure that its directory exists), it\'s possible to specify a directory as a prerequisite, by specifying the marker file as a proxy for the directory and get the desired effect.

In this example the marker file is called .f

OUT = /out

.PHONY: all
all: $(OUT)/foo.o

$(OUT)/foo.o: foo.c $(OUT)/.f
    @$(COMPILE.C) -o $@ $<

$(OUT)/.f:
    mkdir -p $(dir $@)
    touch $@

Notice how the rule to build $(OUT)/.f both creates the directory, if necessary, and touches the .f file.  Since the target is a file (the .f) it can safely be used as a prerequisite in the $(OUT)/foo.o rule.

The $(OUT)/.f rule uses the GNU Make function $(dir FILE) to extract the directory portion of the target (which is the path to the .f file) and passes that directory to mkdir.

The only disadvantage here is that it\'s necessary to specify the .f files for every rule that builds a target in a directory that might need creating.

Solution 4: use an \'order-only\' prerequisite to build the directory

If you are using GNU Make 3.80 or above another solution is to use an \'order-only\' prerequisite.  An order-only prerequiste is a prerequisite of a target that will be built before the target, but whether it is rebuilt or not does not affect the rebuilding of the target.  So GNU Make will cause an order-only prerequisite to be rebuilt but it will only affect the order in which targets are handled, and not the relationship between targets that causes rebuilding.

This is exactly what we\'d like in the original broken example: make sure that the directory has been rebuilt, but if its timestamp changes don\'t rebuild the .o file. 

Order-only prerequisites are specified by prefixing them with the bar symbol | before the prerequisites (and after any normal prerequisites have been specified). 

In fact a single character change to the broken example makes it work correctly under GNU Make 3.80 and above:

OUT = /out

.PHONY: all
all: $(OUT)/foo.o

$(OUT)/foo.o: foo.c | $(OUT)/
    @$(COMPILE.C) -o $@ $<

$(OUT)/:
    mkdir -p $@


Solution 5: use pattern rules, second expansion and a marker file

In a typical Makefile (i.e. not the sort of simple example people come up with in articles like this) targets are usually built using pattern rules.  For example, the simple example would more likely be written using a pattern rule like this:

OUT = /out

.PHONY: all
all: $(OUT)/foo.o

$(OUT)/%.o: %.c
    @$(COMPILE.C) -o $@ $<

It would be nice to be able to change the pattern rule so that it builds the directory automatically using marker files.

In GNU Make 3.81 there\'s an exciting new feature called \'second expansion\' (which is enabled by specifying the .SECONDEXPANSION target in the Makefile).  With second expansion the prerequisite list of any rule under goes a second expansion (the first expansion happens when the Makefile is read) just before the rule is used.  By escaping any $ signs with a second $ it\'s possible to use GNU Make automatic variables in the prerequisite list.

Using a marker file for each directory and second expansion you can create a Makefile that automatically creates directories only when necessary with a simple addition to the prerequisite list of any rule:

OUT = /tmp/out

.SECONDEXPANSION:

all: $(OUT)/foo.o

$(OUT)/%.o: %.c $$(@D)/.f
    @$(COMPILE.C) -o $@ $<

%/.f:
    mkdir -p $(dir $@)
    touch $@

.PRECIOUS: %/.f

The pattern rule used to make .o files has a special prerequisite $$(@D)/.f which uses the second expansion feature to obtain the directory in which the target (from $@ using the D modifier) is to be built.

That directory will be built by the %/.f pattern rule that knows how to build a .f file (and the containing directory).   Notice that the .f files are marked as precious so that GNU Make will not delete them.  Without this line the .f files are considered to be useless intermediate files and would be cleaned up by GNU Make on exit.

Conclusion

That\'s enough ways to create directories in GNU Make.  I\'ll be back in August with a new column about GNU Make and parallelism. 


John Graham-Cumming is Mr Make and Chief Scientist at Electric Cloud, Inc., a Silicon Valley start-up that speeds up builds by 10-20x using cluster technology.
Trackback(0)
Comments (6)add
...
written by Wade Brainerd , January 19, 2007
Another variant on the $(shelll) option that has no performance penalty and works fine with parallel builds:

ifeq ($(wildcard $(TARGETPATH)/),)
$(shell mkdir -p $(TARGETPATH))
endif


report abuse
vote down
vote up
Votes: +0
...
written by John Graham-Cumming , January 09, 2007
That's a good one, and if I revise this article I'll make sure to add it.

John.

report abuse
vote down
vote up
Votes: +0
...
written by David Boyce , January 09, 2007
Nice article, but you seem to have left out the most basic, obvious way - creating dirs on the fly as part of the build script. E.g.

OUT = /out

.PHONY: all
all: $(OUT)/foo.o

$(OUT)/foo.o: foo.c
@test -d $(@D) || mkdir -p $(@D)
@$(COMPILE.C) -o $@ $<

Or similar. I do this and it works fine for us.
report abuse
vote down
vote up
Votes: +0
...
written by John Graham-Cumming , November 01, 2006
Glad thta you liked the article. You are correct about the DUMMY := $(shell ) thing. If the $(shell) produces any output on STDOUT then you need to do that. As I rule I like to rite:

__DUMMY := $(shell FOO)

with __ in front of DUMMY to indicate that this is a variable I never use.

I'm working on the parallelism article...
report abuse
vote down
vote up
Votes: +0
...
written by Brian K. , October 28, 2006
Great article! But one problem not mentioned which I've often encountered with creating directories relates to parallel builds. Namely, on some systems mkdir -p suffers from race conditions so specifying rules which perform individual mkdir -p commands can be dangerous.

For example, if I want to create $(OUTDIR)/$(DIR1) and $(OUTDIR)/$(DIR2) then having rules which trigger "mkdir -p" calls for these directories can sometimes cause errors on certain unix platforms (e.g., Solaris) in parallel builds.

Of the solutions your article lists for creating I have chosen the $(shell mkdir -p ... ) approach where I specify all the directories needed in a single mkdir -p command. This solves the problem but I do wish I had a better solution.

MINOR ISSUE WITH THE SHELL VARIABLE APPROACH:
In order to work with some older versions of GNU make I needed to add a dummy variable to the front of the line, something like this:

DUMMY:=$(shell mkdir -p ...)

OTHER IDEAS RELATED TO PARALLEL BUILDS:
I eagerly await your article on "parallelism" to which you allude in your Conclusion. I had implemented an approach myself to creating directories in parallel builds which used order-only prerequisites but then learned that I needed to work with versions of GNU make before 3.80. I'd be very interested to learn about any mechanisms which can simulate something close to an order-only prerequisite (I'm guessing there might not be any, hence the reason why order-only prerequisites were introduced).
report abuse
vote down
vote up
Votes: +0
Creating patterned directories without SECONDEXPANSION
written by Jaco Kroon , September 07, 2006
It's also possible to create those directories using pattern-based rules without the SECONDEXPANSION option:

The basic pattern rule then looks something like:

obj/%.o : src/%.c Makefile | $(dir obj/%).d
some_command

And then the rule:

%/.d :
mkdir -p $(dir $@)
touch $@

And finally, thanks for a very informative article.
report abuse
vote down
vote up
Votes: +0
Write comment
smaller | bigger

security image
Write the displayed characters


busy
 
< Prev   Next >

Video News

Whitepaper Spotlight

Stay up to date with Configuration Management and Application Lifecycle Management technology products and services by browsing our featured white papers below: See all the Featured Whitepapers>>
Tool Spotlight
CollabNet
CollabNet Subversion is an enterprise-ready distribution of Subversion® that includes, in one package,...
Read More
AccuRev
AccuRev is a best-of-breed, process-centric software configuration management (SCM) solution for...
Read More
IBM Rational Build Forge Express Edition
IBM Rational Build Forge Express Edition is a flexible and robust build automation framework developed,...
Read More
Sapient ResultSpace
ResultSpace is the Agile Application Lifecycle Management (ALM) solution that enables software development...
Read More