Test whether a directory exists inside a makefile

BashMakefile

Bash Problem Overview


In his answer @Grundlefleck explains how to check whether a directory exists or not. I tried some to use this inside a makefile as follow:

foo.bak: foo.bar
	echo "foo"
	if [ -d "~/Dropbox" ]; then
		echo "Dir exists"
	fi

Running make foo.bak (given that foo.bar exists) yields the following error:

echo "foo"
foo
if [ -d "~/Dropbox" ]; then
/bin/sh: -c: line 1: syntax error: unexpected end of file
make: *** [foo.bak] Error 2

The workaround I made was to have a standalone bash script where the test is implemented and I called the script from the makefile. This, however, sounds very cumbersome. Is there a nicer way to check whether a directory exists from within a makefile?

Bash Solutions


Solution 1 - Bash

Make commands, if a shell command, must be in one line, or be on multiple lines using a backslash for line extension. So, this approach will work:

foo.bak: foo.bar
    echo "foo"
    if [ -d "~/Dropbox" ]; then echo "Dir exists"; fi

Or

foo.bak: foo.bar
    echo "foo"
    if [ -d "~/Dropbox" ]; then \
        echo "Dir exists"; \
    fi

Solution 2 - Bash

This approach functions with minimal echos:

.PHONY: all
all:
ifneq ($(wildcard ~/Dropbox/.*),)
        @echo "Found ~/Dropbox."
else
        @echo "Did not find ~/Dropbox."
endif

Solution 3 - Bash

Act upon the absence of a directory

If you only need to know if a directory does not exist and want to act upon that by for example creating it, you can use ordinary Makefile targets:

directory = ~/Dropbox

all: | $(directory)
    @echo "Continuation regardless of existence of ~/Dropbox"
                                                                                                                        
$(directory):
	@echo "Folder $(directory) does not exist"
    mkdir -p $@

.PHONY: all

Remarks:

  • The | indicates that make shouldn't care about the timestamp (it's an order-only-prerequisite).
  • Rather than write mkdir -p $@, you can write false to exit, or solve your case differently.

If you also need to run a particular series of instructions upon the existence of a directory, you cannot use the above. In other words, it is equivalent to:

if [ ! -d "~/Dropbox" ]; then
    echo "The ~/Dropbox folder does not exist"
fi

There is no else statement.

Act upon the presence of a directory

If you want the opposite if-statement this is also possible:

directory = $(wildcard ~/Dropbox)

all: | $(directory)
    @echo "Continuation regardless of existence of ~/Dropbox"
                                                                                                                        
$(directory):
	@echo "Folder $(directory) exists"

.PHONY: all $(directory)

This is equivalent to:

if [ -d "~/Dropbox" ]; then
    echo "The ~/Dropbox folder does exist"
fi

Again, there is no else statement.

Act upon both the presence and the absence of a directory

This becomes a bit more cumbersome, but in the end gives you nice targets for both cases:

directory = ~/Dropbox
dir_target = $(directory)-$(wildcard $(directory))
dir_present = $(directory)-$(directory)
dir_absent = $(directory)-

all: | $(dir_target)
	@echo "Continuation regardless of existence of ~/Dropbox"

$(dir_present):
	@echo "Folder $(directory) exists"

$(dir_absent):
	@echo "Folder $(directory) does not exist"

.PHONY: all

This is equivalent to:

if [ -d "~/Dropbox" ]; then
    echo "The ~/Dropbox folder does exist"
else
    echo "The ~/Dropbox folder does not exist"
fi

Naturally, the wildcard expansion might be slower than an if-else-statement. However, the third case is probably quite rare and is just added for completeness.

Solution 4 - Bash

Try this:

.PHONY: all
something:
	echo "hi"
all:
	test -d "Documents" && something

This will execute the commands under something only if Documents exists.

In order to address the problem noted in the comments, you can make a variable like this:

PATH_TEST = ~/SomeDirectory

test -d $(PATH_TEST) && something

Solution 5 - Bash

I had a case where I wanted to define a variable based on the test whether a directory exists or not at the top-most level of the Makefile where the approaches described above don't work. I found here a nice solution which can be used like this:

MY_DIRNAME=../External
ifneq "$(wildcard $(MY_DIRNAME) )" ""
  # if directory MY_DIRNAME exists:
  INCLUDES += -I../External
else
  # if it doesn't:
  INCLUDES += -I$(HOME)/Code/External
endif

This will modify the variable INCLUDES based on whether the directory stored in MY_DIRNAME exists or not.

(Motivation: In my case this variable would be used in another Makefile included later by the first:

include $(SFRAME_DIR)/Makefile.common

I wanted to have the same Makefile work in two different environments in a simple way.)

Solution 6 - Bash

There is a very different answer that allows you to use your if statements as you envisioned them in one shell:

.ONESHELL:
foo.bak: foo.bar
    echo "foo"
    if [ -d "~/Dropbox" ]; then
        echo "Dir exists"
    fi

Note that the only difference is the ONESHELL special target.

Solution 7 - Bash

I use the following to detect if a file or a directory exists and act upon it :

$(if $(filter expected,$(wildcard *)), the expected file exists)

With your request :

.PHONY: ~/Dropbox

~/Dropbox:
    echo "Dir exists"

foo.bak: foo.bar | $(if $(filter ~/Dropbox,$(wildcard ~/*)), the expected file exists)

Which can further be simplify :

foo.bak: foo.bar | $(wildcard ~/Dropbox)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDrorView Question on Stackoverflow
Solution 1 - BashlurkerView Answer on Stackoverflow
Solution 2 - BashcforbishView Answer on Stackoverflow
Solution 3 - BashAnne van RossumView Answer on Stackoverflow
Solution 4 - Bashuser1508519View Answer on Stackoverflow
Solution 5 - BashfuenfundachtzigView Answer on Stackoverflow
Solution 6 - BashAnne van RossumView Answer on Stackoverflow
Solution 7 - Bashuser3020921View Answer on Stackoverflow