What would make a good portable clean target in a Makefile? - makefile

What is the appropriate form for a clean target for a portable Makefile? $(RM) does not work for me. I work both from the Windows (7) command prompt and within Eclipse. They both report the same version of make (I do have multiple on my path):
make --version
GNU Make 3.82
Built for i386-pc-mingw32
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Within Eclipse:
make clean
rm -f *.o testScaffolding_* runner.cpp runner.exe *.d
From cmd:
rm -f *.o testScaffolding_* runner.cpp runner.exe *.d
process_begin: CreateProcess(NULL, rm -f *.o testScaffolding_* runner.cpp runner.exe *.d, ...) failed.
make (e=2): The system cannot find the file specified.
make: *** [clean] Error 2
Both report $(OS) as Windows_NT and $(RM) as rm -f. I have which installed as well and if I call it within the Makefile, both environments report the same path.

This is the best I've come up with so far. You can specify UNAME as an environment variable or on the command line. If not specified it tries to run uname. If that fails it assumes Windows. Subsequently, you have a reasonable guess as to your shell environment and can define the corresponding commands appropriately.
Note that this has seen only limited testing and I merely consider it functional, not elegant or 'correct'. Also, the likes of an executable extension (.exe vs. nothing) is probably better identified by the use of $(OS).
ifeq ($(strip $(UNAME)),)
# if not already specified, try running uname
UNAME = $(shell uname)
endif
ifeq ($(strip $(UNAME)),)
# if still not specified, assume Windows
UNAME = Windows
endif
ifeq ($(UNAME),Windows)
define TO_WIN
$(subst /,\,$1)
endef
define MKDIR
-mkdir $(call TO_WIN,$1)
endef
define RM
del $(call TO_WIN,$1)
endef
CAT := type
else
define MKDIR
mkdir -p $1
endef
define RM
rm -f $1
endef
CAT := cat
endif

Related

Make says file is up to date, but it does not exist

I want
I am trying to compile some latex that has snippets of python code and the output of those snippets. I need the document to be always updated with the last changes made in the snippets and in their outputs, so the idea is maintain a makefile that could monitor this changes and generate the updated outputs.
So if I modify the file a/11.py, I want make to execute it to generate a new output a/11.out.
I have
This is my makefile
DOC=myPdf
STY=st
PY_DIR=a/
TEX=pdflatex -shell-escape -interaction=batchmode -file-line-error
$(DOC).pdf: $(PY_DIR)11.out $(PY_DIR)12.out $(DOC).tex $(STY).sty
$(TEX) $(DOC).tex
$(PY_DIR)11.out:
$(cd PY_DIR && python3 11.py > 11.out)
$(PY_DIR)12.out:
$(cd PY_DIR && python3 12.py > 12.out)
.PHONY: clean
clean:
rm *.aux *.log > /dev/null 2>&1
I wonder
Even when the file a/11.out doesn't exist, and I instruct make a/11.out make says: make: 'a/11.out' is up to date. (I am still learning make, so I probably have more mistakes).
I saw
Make in subfolder, but because I am not using $(MAKE), I cannot use it.
Similar question, but I don't think it is the same.
Thank you for your time :)
Update
This is my new version, based in the answer of Renaud (thanks for your help), some python scripts are intended to output text (xxxt.py), and others to plot images (xxxi.py), so there is no redirection for them:
DOC :=myPdf
STY :=st
PY_DIR :=a/
TEX :=pdflatex -shell-escape -interaction=batchmode -file-line-error
PYS := $(wildcard $(PY_DIR)*.py)
OUTS := $(patsubst %.py,%.out,$(PYS))
.PHONY: all clean
all: $(DOC).pdf
%.pdf: %.tex $(STY).sty $(OUTS)
$(TEX) $<
$(PY_DIR)%.out: $(PY_DIR)%t.py
cd $(PY_DIR) && python3 $*t.py > $*.out
$(PY_DIR)%.png: $(PY_DIR)%i.py
cd $(PY_DIR) && python3 $*i.py
clean:
rm *.aux *.log > /dev/null 2>&1
The directory looks like this:
./st.sty
./myPdf.tex
./myPdf.pdf
./a/11t.py
./a/11.out
./a/12i.py
./a/12.png
./a/21t.py
./a/...
However, now right after modifying myPdf.tex, make says make: Nothing to be done for 'all'.
What am I doing wrong?
Your recipes are wrong. Make expands the recipes before passing them to the shell. As there is no make variable named cd PY_DIR && python3 11.py > 11.out, $(cd PY_DIR && python3 11.py > 11.out) expands as the empty string and make considers that there is nothing to do for $(PY_DIR)11.out. Just write your recipes as plain shell (and fix the other bug with the unexpanded PY_DIR):
$(PY_DIR)11.out:
cd $(PY_DIR) && python3 11.py > 11.out
$(PY_DIR)12.out:
cd $(PY_DIR) && python3 12.py > 12.out
Note: if you want make to re-run the recipes when your python scripts change you should let him know that the output files depend on the python scripts. The best is probably to use a pattern rule instead of one specific rule per file:
$(PY_DIR)%.out: $(PY_DIR)%.py
cd $(PY_DIR) && python3 $*.py > $*.out
($* is a make automatic variable, it expands as the stem of the pattern).
A few more improvements:
You could ask make to find alone the python scripts, compute the names of the output files and store all this in make variables that you can used in your other rules.
You can use a pattern rule for the xx.tex -> xx.pdf process. And use another make automatic variable for it: $< that expands as the first prerequisite.
DOC := myPdf
STY := st
PY_DIR := a/
TEX := pdflatex -shell-escape -interaction=batchmode -file-line-error
PYS := $(wildcard $(PY_DIR)*.py)
OUTS := $(patsubst %.py,%.out,$(PYS))
.PRECIOUS: $(OUTS)
.PHONY: all clean
all: $(DOC).pdf
%.pdf: %.tex $(OUTS) $(STY).sty
$(TEX) $<
$(PY_DIR)%.out: $(PY_DIR)%.py
cd $(PY_DIR) && python3 $*.py > $*.out
.PHONY: clean
clean:
rm *.aux *.log > /dev/null 2>&1
Note: I declared $(OUTS) as precious such that make does not delete them when it is done with the building of $(DOC).pdf.
Update with the new specifications and separated python scripts for xx.out and xx.png production:
DOC := myPdf
STY := st
PY_DIR := a
TEX := pdflatex -shell-escape -interaction=batchmode -file-line-error
PYTS := $(wildcard $(PY_DIR)/*t.py)
PYIS := $(wildcard $(PY_DIR)/*i.py)
OUTS := $(patsubst $(PY_DIR)/%t.py,$(PY_DIR)/%.out,$(PYTS))
PNGS := $(patsubst $(PY_DIR)/%i.py,$(PY_DIR)/%.png,$(PYIS))
.PRECIOUS: $(OUTS) $(PNGS)
.PHONY: all clean
all: $(DOC).pdf
%.pdf: %.tex $(STY).sty $(OUTS) $(PNGS)
$(TEX) $<
$(PY_DIR)/%.out: $(PY_DIR)/%t.py
cd $(PY_DIR) && python3 $*t.py > $*.out
$(PY_DIR)/%.png: $(PY_DIR)/%i.py
cd $(PY_DIR) && python3 $*i.py
clean:
rm -f *.aux *.log > /dev/null 2>&1
Notes:
I slightly modified the definition of PY_DIR such that, when used in other parts of the Makefile, it is clear that it is a directory path. Just a matter of taste, I guess.
I added the -f option to your clean recipe such that it doesn't fail if the files to delete do not exist.
Update:
As noted by MadScientist in a comment, using $* is less generic than referring to the target ($#) and the prerequisite ($<). But as we are operating not directly on them but on their directory ($(PY_DIR)) and base file names (xx[it].py, xx.out, xx.png), switching from $* to other, more generic, automatic variables is not that simple.
But make has some more tricks that can help here: $#, $<... have variants ($(#F), $(#D)...) that expand to just the directory part or the file part. Note that, according the GNU make manual:
These variants are semi-obsolete in GNU make since the functions dir
and notdir can be used to get a similar effect.
Anyway, if we wanted to avoid $* here is what we could use instead:
$(PY_DIR)/%.out: $(PY_DIR)/%t.py
cd $(#D) && python3 $(<F) > $(#F)
$(PY_DIR)/%.png: $(PY_DIR)/%i.py
cd $(#D) && python3 $(<F)
Or (modern version):
$(PY_DIR)/%.out: $(PY_DIR)/%t.py
cd $(dir $#) && python3 $(notdir $<) > $(notdir $#)
$(PY_DIR)/%.png: $(PY_DIR)/%i.py
cd $(dir $#) && python3 $(notdir $<)

Debuild Error - Installing files in non-standard directory without getting errors

I'm creating a debian package for my libspellcheck library, and it has turned out to be quite a hassle. After a lot of work, I have been able to eliminate all but one fatal error:
make[1]: Entering directory `/home/iandun/Desktop/deb-build/libspellcheck-1.15'
mkdir /home/iandun/Desktop/deb-build/libspellcheck-1.15/debian/tmp/usr/etc/
mkdir: cannot create directory `/home/iandun/Desktop/deb-build/libspellcheck-1.15/debian/tmp/usr/etc/': No such file or directory
make[1]: *** [install] Error 1
make[1]: Leaving directory `/home/iandun/Desktop/deb-build/libspellcheck-1.15'
dh_auto_install: make -j1 install DESTDIR=/home/iandun/Desktop/deb-build/libspellcheck-1.15/debian/tmp returned exit code 2
make: *** [binary] Error 29
dpkg-buildpackage: error: fakeroot debian/rules binary gave error exit status 2
debuild: fatal error at line 1350:
dpkg-buildpackage -rfakeroot -D -us -uc failed
Here is my makefile:
# SPELLCHECK Makefile
# Copyright (C) 2013 Ian Duncan
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
all: libspellcheck spellcheck
spellcheck: meta.o spellcheck.o
g++ -m32 -o spellcheck spellcheck.o meta.o libspellcheck.a
libspellcheck: checker.o
ar -cvr libspellcheck.a checker.o
spellcheck.o: spellcheck.cpp
g++ -m32 -Wall -c spellcheck.cpp
meta.o: meta.cpp
g++ -m32 -Wall -c meta.cpp
checker.o: checker.cpp
g++ -m32 -Wall -c checker.cpp
clean:
rm -rf *o
install:
mkdir $(DESTDIR)/usr/etc/
cp libspellcheck.a $(DESTDIR)$(libdir)/libspellcheck.a
cp spellcheck.h $(DESTDIR)$(includedir)/spellcheck.h
cp spellcheck $(DESTDIR)$(bindir)/spellcheck
cp english.dict $(DESTDIR)/usr/etc/english.dict
chmod 777 $(DESTDIR)/usr/etc/english.dict
deinstall:
rm /usr/lib/libspellcheck.a
rm /usr/include/spellcheck.h
rm /usr/bin/spellcheck
rm /usr/etc/english.dict
rm /usr/local/man/man1/spellcheck.1.gz
Now, I can see from the error that its having trouble creating the /usr/etc directory in the $DESTDIR folder. However, if I remove $DESTDIR, it will create /usr/etc in the / directory, which is not where I want it. I do not want to have to relocate my dictionary file, because that would create a lot of problems with code consistency and such. There must be a way to do this.
Your mkdir command should have a -p parameter: mkdir -p $(DESTDIR)/usr/etc/

Error: mixed implicit and normal rules

I have just updated make to
GNU Make 3.82
Built for x86_64-pc-linux-gnu
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
and before hand this make file worked
MODULE_NAME = module
$(MODULE_NAME)-objs := Charset.o Connector.o Fpga.o FpgaBus.o FpgaFlash.o I2c.o Key.o Module.o \
Oled.o PhoneAudio.o Splash.o AudioCodecIO.o Connection.o Server.o
EXTRA_CFLAGS=-I./include2 -I./include -DALLOW_LOWER_CASE
ifneq ($(KERNELRELEASE),)
obj-m := $(MODULE_NAME).o
else
KDIR := ./build/
PWD := $(shell pwd)
all:
ARCH=arm CROSS_COMPILE=/usr/bin/arm-softfloat-linux-uclibceabi- $(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
ARCH=arm CROSS_COMPILE=/usr/bin/arm-softfloat-linux-uclibceabi- $(MAKE) -C $(KDIR) M=$(PWD) clean
install:
ARCH=arm CROSS_COMPILE=/usr/bin/arm-softfloat-linux-uclibceabi- $(MAKE) -C $(KDIR) M=$(PWD) modules_install
endif
now it does not with this error
ARCH=arm CROSS_COMPILE=/usr/bin/arm-softfloat-linux-uclibceabi- make -C ./build/ M=/home/liam/Dev/driver modules
Makefile:23: *** mixed implicit and normal rules. Stop.
make[1]: Entering directory `/home/shared/firmware/atmel/kernel'
make: *** [all] Error 2
make[1]: Leaving directory `/home/shared/firmware/atmel/kernel'
Any idea what is causing this issue?
EDIT:
Ok, i have more info now. An auto generated Makefile is pulled in from the ./build/ directory and looks like this.
# Automatically generated by /home/shared/firmware/src/linux/scripts/mkmakefile: don't edit
VERSION = 2
PATCHLEVEL = 6
lastword = $(word $(words $(1)),$(1))
makedir := $(dir $(call lastword,$(MAKEFILE_LIST)))
MAKEARGS := -C /home/shared/firmware/src/linux
MAKEARGS += O=$(if $(patsubst /%,,$(makedir)),$(CURDIR)/)$(patsubst %/,%,$(makedir))
MAKEFLAGS += --no-print-directory
.PHONY: all $(MAKECMDGOALS)
all := $(filter-out all Makefile,$(MAKECMDGOALS))
all:
$(MAKE) $(MAKEARGS) $(all)
Makefile:;
$(all) %/: all
#:
Looking at it and looking at other posts, this may now be a duplicate question. the last few lines should not be this with this version of make
The fix is changing the kernel build generated Makefile lines
From:
$(all) %/: all
#:
To:
$(all) : all
There are other answers that can explain the reasons behind this better then I can.

Copy a file using a makefile at runtime

I used to compile my programs with batch scripts on windows but I recently discovered makefiles which are much more efficient.
I had this line in my .bat file that copied some dlls to the current directory at runtime and it worked perfectly.
copy C:\lib\glfw\glfw.dll
I tried the same line in my makefile and even tried the alternative cp but my terminal prints this error even tho the file is IN the location I specified
process_begin: CreateProcess(NULL, copy C:\lib\glfw\glfw.dll, ...) failed
make (e=2): The system cannot find the file specified.
make: *** [core.exe] Error 2
Here is the full makefile that I am using. Mind you, absent the copy line it works like a charm.. what am I doing wrong or is this possible?
EXEC = core.exe
OBJS = src/obp.o
CC = g++
CFLAGS = -W -Wall
LIBS = -lSOIL -lglew32 -lglfw -lopengl32
LDFLAGS =
$(EXEC): $(OBJS)
$(CC) $(LDFLAGS) -o $# $(OBJS) $(LIBS)
copy C:\lib\glfw\glfw.dll
clean:
rm -f $(EXEC) $(OBJS) *~
It looks like you are running this from an MSYS (or MinGW) environment, which does not know about copy. Instead, you can use
cp C:\lib\glfw\glfw.dll .
If you want to avoid the *nix like cp, then you could use xcopy as follows:
xcopy //Y C:\lib\glfw\glfw.dll
Note the double // which is required to escape the /.
Or you could run this in a regular MS-DOS environment, in which case your clean target will not work because rm will not be found and you should use del.
With your current setup, any built-in DOS command will not be found. See Choosing the shell to read about how make determines which shell to use.
You may need to double the backslashes in order for make to understand what you want:
copy c:\\lib\\glfw\\glfw.dll
Make comes from a Unix background where the file separator is /, so it's sometimes a bit awkward to use it in a Windows environment.

Create directories using make file

I want to create directories using makefile. My project directory is like this
+--Project
+--output
+--source
+Testfile.cpp
+Makefile
I want to put all the objects and output into the respective output folder. I want to create folder structure which would be like this after compiling.
+--Project
+--output
+--debug (or release)
+--objs
+Testfile.o
+Testfile (my executable file)
+--source
+Testfile.cpp
+Makefile
I tried with several options, but could not succeed. Please help me to make directories using make file. I'm posting my Makefile for your consideration.
#---------------------------------------------------------------------
# Input dirs, names, files
#---------------------------------------------------------------------
OUTPUT_ROOT := output/
TITLE_NAME := TestProj
ifdef DEBUG
TITLE_NAME += _DEBUG
else
ifdef RELEASE
TITLE_NAME += _RELEASE
endif
endif
# Include all the source files here with the directory tree
SOURCES := \
source/TestFile.cpp \
#---------------------------------------------------------------------
# configs
#---------------------------------------------------------------------
ifdef DEBUG
OUT_DIR := $(OUTPUT_ROOT)debug
CC_FLAGS := -c -Wall
else
ifdef RELEASE
OUT_DIR := $(OUTPUT_ROOT)release
CC_FLAGS := -c -Wall
else
$(error no build type defined)
endif
endif
# Put objects in the output directory.
OUT_O_DIR := $(OUT_DIR)/objs
#---------------------------------------------------------------------
# settings
#---------------------------------------------------------------------
OBJS = $(SOURCES:.cpp=.o)
DIRS = $(subst /,/,$(sort $(dir $(OBJS))))
DIR_TARGET = $(OUT_DIR)
OUTPUT_TARGET = $(OUT_DIR)/$(TITLE_NAME)
CC_FLAGS +=
LCF_FLAGS :=
LD_FLAGS :=
#---------------------------------------------------------------------
# executables
#---------------------------------------------------------------------
MD := mkdir
RM := rm
CC := g++
#---------------------------------------------------------------------
# rules
#---------------------------------------------------------------------
.PHONY: all clean title
all: title
clean:
$(RM) -rf $(OUT_DIR)
$(DIR_TARGET):
$(MD) -p $(DIRS)
.cpp.o:
#$(CC) -c $< -o $#
$(OBJS): $(OUT_O_DIR)/%.o: %.cpp
#$(CC) -c $< -o $#
title: $(DIR_TARGET) $(OBJS)
In my opinion, directories should not be considered targets of your makefile, either in technical or in design sense. You should create files and if a file creation needs a new directory then quietly create the directory within the rule for the relevant file.
If you're targeting a usual or "patterned" file, just use make's internal variable $(#D), that means "the directory the current target resides in" (cmp. with $# for the target). For example,
$(OUT_O_DIR)/%.o: %.cpp
#mkdir -p $(#D)
#$(CC) -c $< -o $#
title: $(OBJS)
Then, you're effectively doing the same: create directories for all $(OBJS), but you'll do it in a less complicated way.
The same policy (files are targets, directories never are) is used in various applications. For example, git revision control system doesn't store directories.
Note: If you're going to use it, it might be useful to introduce a convenience variable and utilize make's expansion rules.
dir_guard=#mkdir -p $(#D)
$(OUT_O_DIR)/%.o: %.cpp
$(dir_guard)
#$(CC) -c $< -o $#
$(OUT_O_DIR_DEBUG)/%.o: %.cpp
$(dir_guard)
#$(CC) -g -c $< -o $#
title: $(OBJS)
This would do it - assuming a Unix-like environment.
MKDIR_P = mkdir -p
.PHONY: directories
all: directories program
directories: ${OUT_DIR}
${OUT_DIR}:
${MKDIR_P} ${OUT_DIR}
This would have to be run in the top-level directory - or the definition of ${OUT_DIR} would have to be correct relative to where it is run. Of course, if you follow the edicts of Peter Miller's "Recursive Make Considered Harmful" paper, then you'll be running make in the top-level directory anyway.
I'm playing with this (RMCH) at the moment. It needed a bit of adaptation to the suite of software that I am using as a test ground. The suite has a dozen separate programs built with source spread across 15 directories, some of it shared. But with a bit of care, it can be done. OTOH, it might not be appropriate for a newbie.
As noted in the comments, listing the 'mkdir' command as the action for 'directories' is wrong. As also noted in the comments, there are other ways to fix the 'do not know how to make output/debug' error that results. One is to remove the dependency on the the 'directories' line. This works because 'mkdir -p' does not generate errors if all the directories it is asked to create already exist. The other is the mechanism shown, which will only attempt to create the directory if it does not exist. The 'as amended' version is what I had in mind last night - but both techniques work (and both have problems if output/debug exists but is a file rather than a directory).
Or, KISS.
DIRS=build build/bins
...
$(shell mkdir -p $(DIRS))
This will create all the directories after the Makefile is parsed.
make in, and off itself, handles directory targets just the same as file targets. So, it's easy to write rules like this:
outDir/someTarget: Makefile outDir
touch outDir/someTarget
outDir:
mkdir -p outDir
The only problem with that is, that the directories timestamp depends on what is done to the files inside. For the rules above, this leads to the following result:
$ make
mkdir -p outDir
touch outDir/someTarget
$ make
touch outDir/someTarget
$ make
touch outDir/someTarget
$ make
touch outDir/someTarget
This is most definitely not what you want. Whenever you touch the file, you also touch the directory. And since the file depends on the directory, the file consequently appears to be out of date, forcing it to be rebuilt.
However, you can easily break this loop by telling make to ignore the timestamp of the directory. This is done by declaring the directory as an order-only prerequsite:
# The pipe symbol tells make that the following prerequisites are order-only
# |
# v
outDir/someTarget: Makefile | outDir
touch outDir/someTarget
outDir:
mkdir -p outDir
This correctly yields:
$ make
mkdir -p outDir
touch outDir/someTarget
$ make
make: 'outDir/someTarget' is up to date.
TL;DR:
Write a rule to create the directory:
$(OUT_DIR):
mkdir -p $(OUT_DIR)
And have the targets for the stuff inside depend on the directory order-only:
$(OUT_DIR)/someTarget: ... | $(OUT_DIR)
All solutions including the accepted one have some issues as stated in their respective comments. The accepted answer by #jonathan-leffler is already quite good but does not take into effect that prerequisites are not necessarily to be built in order (during make -j for example). However simply moving the directories prerequisite from all to program provokes rebuilds on every run AFAICT.
The following solution does not have that problem and AFAICS works as intended.
MKDIR_P := mkdir -p
OUT_DIR := build
.PHONY: directories all clean
all: $(OUT_DIR)/program
directories: $(OUT_DIR)
$(OUT_DIR):
${MKDIR_P} $(OUT_DIR)
$(OUT_DIR)/program: | directories
touch $(OUT_DIR)/program
clean:
rm -rf $(OUT_DIR)
I've just come up with a fairly reasonable solution that lets you define the files to build and have directories be automatically created. First, define a variable ALL_TARGET_FILES that holds the file name of every file that your makefile will be build. Then use the following code:
define depend_on_dir
$(1): | $(dir $(1))
ifndef $(dir $(1))_DIRECTORY_RULE_IS_DEFINED
$(dir $(1)):
mkdir -p $$#
$(dir $(1))_DIRECTORY_RULE_IS_DEFINED := 1
endif
endef
$(foreach file,$(ALL_TARGET_FILES),$(eval $(call depend_on_dir,$(file))))
Here's how it works. I define a function depend_on_dir which takes a file name and generates a rule that makes the file depend on the directory that contains it and then defines a rule to create that directory if necessary. Then I use foreach to call this function on each file name and eval the result.
Note that you'll need a version of GNU make that supports eval, which I think is versions 3.81 and higher.
given that you're a newbie, I'd say don't try to do this yet. it's definitely possible, but will needlessly complicate your Makefile. stick to the simple ways until you're more comfortable with make.
that said, one way to build in a directory different from the source directory is VPATH; i prefer pattern rules
OS independence is critical for me, so mkdir -p is not an option. I created this series of functions that use eval to create directory targets with the prerequisite on the parent directory. This has the benefit that make -j 2 will work without issue since the dependencies are correctly determined.
# convenience function for getting parent directory, will eventually return ./
# $(call get_parent_dir,somewhere/on/earth/) -> somewhere/on/
get_parent_dir=$(dir $(patsubst %/,%,$1))
# function to create directory targets.
# All directories have order-only-prerequisites on their parent directories
# https://www.gnu.org/software/make/manual/html_node/Prerequisite-Types.html#Prerequisite-Types
TARGET_DIRS:=
define make_dirs_recursively
TARGET_DIRS+=$1
$1: | $(if $(subst ./,,$(call get_parent_dir,$1)),$(call get_parent_dir,$1))
mkdir $1
endef
# function to recursively get all directories
# $(call get_all_dirs,things/and/places/) -> things/ things/and/ things/and/places/
# $(call get_all_dirs,things/and/places) -> things/ things/and/
get_all_dirs=$(if $(subst ./,,$(dir $1)),$(call get_all_dirs,$(call get_parent_dir,$1)) $1)
# function to turn all targets into directories
# $(call get_all_target_dirs,obj/a.o obj/three/b.o) -> obj/ obj/three/
get_all_target_dirs=$(sort $(foreach target,$1,$(call get_all_dirs,$(dir $(target)))))
# create target dirs
create_dirs=$(foreach dirname,$(call get_all_target_dirs,$1),$(eval $(call make_dirs_recursively,$(dirname))))
TARGETS := w/h/a/t/e/v/e/r/things.dat w/h/a/t/things.dat
all: $(TARGETS)
# this must be placed after your .DEFAULT_GOAL, or you can manually state what it is
# https://www.gnu.org/software/make/manual/html_node/Special-Variables.html
$(call create_dirs,$(TARGETS))
# $(TARGET_DIRS) needs to be an order-only-prerequisite
w/h/a/t/e/v/e/r/things.dat: w/h/a/t/things.dat | $(TARGET_DIRS)
echo whatever happens > $#
w/h/a/t/things.dat: | $(TARGET_DIRS)
echo whatever happens > $#
For example, running the above will create:
$ make
mkdir w/
mkdir w/h/
mkdir w/h/a/
mkdir w/h/a/t/
mkdir w/h/a/t/e/
mkdir w/h/a/t/e/v/
mkdir w/h/a/t/e/v/e/
mkdir w/h/a/t/e/v/e/r/
echo whatever happens > w/h/a/t/things.dat
echo whatever happens > w/h/a/t/e/v/e/r/things.dat
See https://www.oreilly.com/library/view/managing-projects-with/0596006101/ch12.html
REQUIRED_DIRS = ...
_MKDIRS := $(shell for d in $(REQUIRED_DIRS); \
do \
[[ -d $$d ]] || mkdir -p $$d; \
done)
$(objects) : $(sources)
As I use Ubuntu, I also needed add this at the top of my Makefile:
SHELL := /bin/bash # Use bash syntax
I use the makefiles in windows environment and my simple solution is as follows,
Create a target makedir and add it as a prerequisites to where ever it is required.
# Default goal
all: gccversion makedir build finalize list sizeafter completed
The makedir target is (applicable only in windows environment)
makedir:
#IF NOT EXIST $(subst /,\,$(BUILD_DIR)) mkdir $(subst /,\,$(BUILD_DIR)) 2> NULL
#IF NOT EXIST $(subst /,\,$(OUTPUT_DIR)) mkdir $(subst /,\,$(OUTPUT_DIR)) 2> NULL
#IF NOT EXIST $(subst /,\,$(DEP_DIR)) mkdir $(subst /,\,$(DEP_DIR)) 2> NUL
#IF NOT EXIST $(subst /,\,$(OBJ_DIR)) mkdir $(subst /,\,$(OBJ_DIR)) 2> NUL
$(subst /,\,$(BUILD_DIR)) converts the directory separator / to \ and
mkdir $(subst /,\,$(BUILD_DIR)) 2> NUL redirects the error if any.
src_dir := src
obj_dir := obj
build_dir := build
dirs := $(src_dir) $(obj_dir) $(build_dir) # new variable
all: $(dirs) $(other_dependencies) # added dependency (*before* any others)
$(dirs): # rule which makes missing directories
mkdir $#
Won't clutter your terminal with "cannot create directory" error messages. If the directories exist, they don't need to be built.
Works like any other dependency, only requires one rule and one variable.

Resources