I'm looking to save myself some effort further down the line by making a fairly generic makefile that will put together relatively simple C++ projects for me with minimal modifications required to the makefile.
So far I've got it so it will use all .cpp
files in the same directory and specified child directories, place all these within a matching structure in a obj
subdir and place the resulting file in another subdir called bin
. Pretty much what I want.
However, trying to get it so that the required obj and bin directories is created if they don't exist is providing awkward to get working cross-platform - specifically, I'm just testing with Windows 7 & Ubuntu (can't remember version), and I can't get it to work on both at the same time.
Windows misreads mkdir -p dir
and creates a -p
directory and obviously the two platforms use \
and /
respectively for the path separator - and I get errors when using the wrong one.
Here is a few selected portions of the makefile that are relevant:
# Manually edited directories (in this example with forward slashes)
SRC_DIR = src src/subdir1 src/subdir2
# Automagic object directories + the "fixed" bin directory
OBJ_DIR = obj $(addprefix obj/,$(SRC_DIR))
BIN_DIR = bin
# Example build target
debug: checkdirs $(BIN)
# At actual directory creation
checkdirs: $(BIN_DIR) $(OBJ_DIR)
$(BIN_DIR):
@mkdir $@
$(OBJ_DIR):
@mkdir -p $@
This has been put together by me over the last week or so from things I've been reading (mostly on Stack Overflow), so if it happens to be I'm following some horrible bad practice or anything of that nature please let me know.
Question in a nutshell:
Is there a simple way to get this directory creation to work from a single makefile in a way that provides as much portability as possible?
I don't know autoconf. Every experience I've had with it has been tedious. The problem with zwol's solution is that on Windows mkdir returns an error, unlike mkdir -p
on Linux. This could break your make rule. The workaround is to ignore the error with -
flag before the command, like this:
-mkdir dir
The problem with this is that make
still throws an ugly warning for the user. The workaround for this is to run an "always true" command after the mkdir
fails as described here, like this:
mkdir dir || true
The problem with this is that Windows and Linux have different syntax for true.
Anyway, I spent too much time on this. I wanted a make
file that worked in both POSIX-like and Windows environments. In the end I came up with the following:
ifeq ($(shell echo "check_quotes"),"check_quotes")
WINDOWS := yes
else
WINDOWS := no
endif
ifeq ($(WINDOWS),yes)
mkdir = mkdir $(subst /,\,$(1)) > nul 2>&1 || (exit 0)
rm = $(wordlist 2,65535,$(foreach FILE,$(subst /,\,$(1)),& del $(FILE) > nul 2>&1)) || (exit 0)
rmdir = rmdir $(subst /,\,$(1)) > nul 2>&1 || (exit 0)
echo = echo $(1)
else
mkdir = mkdir -p $(1)
rm = rm $(1) > /dev/null 2>&1 || true
rmdir = rmdir $(1) > /dev/null 2>&1 || true
echo = echo "$(1)"
endif
The functions/variables are used like so:
rule:
$(call mkdir,dir)
$(call echo, CC $@)
$(call rm,file1 file2)
$(call rmdir,dir1 dir2)
Rationale for the definitions:
del
doesn't delete any files if one of the files is specified to be in a directory that doesn't exist. For example, if you try to delete a set of files and dir/file.c
is in the list, but dir
doesn't exist, no files will be deleted. This implementation works around that issue by invoking del
once for each file.""
in Windows.I spent a lot of time on this. Perhaps I would have been better off spending my time learning autoconf.
See also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With