make wildcard not finding files I just copied - makefile

I would like to copy pdf files from several directories into a build directory, then use pdfunite to compile them into one pdf. The following make recipe works, but I have to run it twice because the first time through, I get an error from pdfunite - no files are found in the build directory (the PDFS variable is empty) even though they were just copied in the previous lines. How can I fix this so it works in one pass? I have simplified the recipe for clarity; I am actually pulling from various folders and making some pdfs on the fly as well, so I can't easily concatenate a full list of files from various subfolders (folder1 and folder2 in the example) to pass to pdfunite.
notebook:
mkdir -p $(out)
mkdir -p $(build)/notebook
$(eval PR := $(sort $(wildcard $(data)/folder1/*.pdf)) )
cp $(PR) $(build)/notebook
$(eval SR := $(sort $(wildcard $(data)/folder2/*.pdf)) )
cp $(SR) $(build)/notebook
$(eval PDFS := $(sort $(wildcard $(build)/notebook/*.pdf)) )
pdfunite $(PDFS) $(out)/notebook.pdf

Your Makefile is not in line with make's philosophy. You are using make as another scripting language, while make is more than this. It compares targets and prerequisites dates, based on this decides which must be built or re-built, and passes the recipes to the shell. So, for your particular problem, you should rather try something like:
PR := $(wildcard $(data)/folder1/*.pdf)
SR := $(wildcard $(data)/folder2/*.pdf)
PDFS1 := $(patsubst $(data)/folder1/%.pdf,$(build)/notebook/%.pdf,$(PR))
PDFS2 := $(patsubst $(data)/folder2/%.pdf,$(build)/notebook/%.pdf,$(SR))
PDFS := $(sort $(PDFS1) $(PDFS2))
.PHONY: notebook
notebook: $(out)/notebook.pdf
$(PDFS1): $(build)/notebook/%.pdf: $(data)/folder1/%.pdf | $(build)/notebook
cp $< $#
$(PDFS2): $(build)/notebook/%.pdf: $(data)/folder2/%.pdf | $(build)/notebook
cp $< $#
$(build)/notebook $(out):
mkdir -p $#
$(out)/notebook.pdf: $(PDFS) | $(out)
pdfunite $(PDFS) $#
The variables definitions are reasonably straightforward: patsubst, as its name says, substitutes strings. The target: pattern: prerequisites is a static pattern rule. And the prerequisites after | are order-only prerequisites.
What this makefile says, basically, is that $(out)/notebook.pdf depends on a set of pdf files in $(build)/notebook/ and that these pdf files depend on source pdf files with the same basenames in $(data)/folder1/ and $(data)/folder2/. It also says that directories must be created before being populated. Thanks to all this only what needs to be done will be done, no more, no less. And it is more in line with make's philosophy.
If you have many source folders and do not want to replicate the copying rules, you can use more advanced features like:
FOLDERS := folder1 folder2
.PHONY: notebook
notebook: $(out)/notebook.pdf
define MY_rule
$(1)_SRCS := $$(wildcard $$(data)/$(1)/*.pdf)
$(1)_DSTS := $$(patsubst $$(data)/$(1)/%.pdf,$$(build)/notebook/%.pdf,$$($(1)_SRCS))
PDFS += $$($(1)_DSTS)
$(1)_DSTS: $$(build)/notebook/%.pdf: $$(data)/$(1)/%.pdf | $$(build)/notebook
cp $$< $$#
endef
$(foreach f,$(FOLDERS),$(eval $(call MY_rule,$(f))))
$(build)/notebook $(out):
mkdir -p $#
$(out)/notebook.pdf: $(PDFS) | $(out)
pdfunite $(PDFS) $#

I would do another way.
PR:=$(sort $(wildcard $(data)/folder1/*.pdf))
SR:=$(sort $(wildcard $(data)/folder2/*.pdf))
PDFS=$(sort $(wildcard $(build)/notebook/*.pdf))
all: copy
pdfunite $(PDFS) $(out)/notebook.pdf
copy:
mkdir -p $(out)
mkdir -p $(build)/notebook
cp $(PR) $(build)/notebook
cp $(SR) $(build)/notebook
.PHONY: all copy
Please check: PDFS= and not PDFS:=. If you use simple = the variable's value will calculate when it needed (not sooner!).
When you run make it want to build all. The all's requirement is copy - so make does some mkdir and cp. After it return the all: the value of PDFS is needed so will evalute now - we have many-many pdf in $(build)/notebook :)

Related

How to use make to build a static website into a dist directory

I have a website in an src directory that is several levels deep, containing html, markdown, less, coffee, png, jpg and other assets. I'd like to build my website into a dist directory using make. With build, I mean
converting markdown files to html
compiling coffee to js
compiling less to css
minifying html files
minifying js files (that where not compiled from coffee)
minifying css files (that where not compiled from less or sass)
preparing images (logo.png becomes logo#1x.png logo#2x.png logo#3x.png)
I have the following file. The cp statements will be replaced with the respective tools to do the transformation.
sources = $(shell find src -type f)
t1 := $(sources:.md=.html)
t2 := $(t1:.less=.css)
targets := $(t2:.coffee=.js)
targetdirs = $(dir $(targets))
all: $(targets)
%.html: %.md
cp $< $#
%.css: %.less
cp $< $#
%.js: %.coffee
cp $< $#
This creates outputs side by side. So src/index.md becomes src/index.html, src/assets/stylesheets/app.less becomes src/assets/stylesheets/app.css and src/assets/scripts/app.coffee becomes src/assets/scripts/app.js. What I'd like to do is change the make file such that it stores the output in the dist directory, so src/index.md is converted to dist/index.html, src/assets/stylesheets/app.less compiled to dist/assets/stylesheets/app.css and and src/assets/scripts/app.coffee becomes dist/assets/scripts/app.js.
So I changed the makefile as follows:
sources = $(shell find src -type f)
t0 := $(subst src/,dist/,$(sources:.md=.html))
t1 := $(t0:.md=.html)
t2 := $(t1:.less=.css)
targets := $(t2:.coffee=.js)
targetdirs = $(dir $(targets))
all: $(targets)
%.html: %.md
mkdir -p $(targetdirs)
cp $< $#
%.css: %.less
mkdir -p $(targetdirs)
cp $< $#
%.js: %.coffee
mkdir -p $(targetdirs)
cp $< $#
Now make fails with the following:
make: *** No rule to make target `dist/assets/scripts/app.js', needed by `all'. Stop.
Most examples I can find, is limited to a single directory, or compiles multiple source files into a single target.
How would one achieve this without knowing the contents of the source directory?
Environment:
GNU Make 3.81
OS X 10.11.1
In make pattern rules, the stems represented by the patterns in the target and prerequisite must match exactly. Take this rule:
%.html: %.md
mkdir -p $(targetdirs)
cp $< $#
If the target make wants to build is dist/index.html, then the stem is dist/index. So, it will look for the prerequisite dist/index.md. Which doesn't exist. So make ignores that pattern rule as not matching, and continues to look for more implicit rules that might match... and doesn't find any so it fails.
You have to fix your rules so that the change in directory is reflected in the pattern:
dist/%.html: src/%.md
mkdir -p $(#D)
cp $< $#
(I'm not sure why you're creating all directories in every recipe instead of just the current one). Now the stem for dist/index.html is just index, and the prerequisite matches src/index.md and it will work.

Create directories when generating object files in gcc

gcc -o abc/def.o def.c generates def.o file in a directory abc; only when there exists a directory abc.
Is there a way to make gcc to create a directory when the enclosing directory of the generated object file does not exist? If not, what could be the easiest way to make the directory in advance automatically, especially for Makefile?
From this post, it seems like that there is no way to create a directory from gcc.
For makefile, I can use this code snippet.
OBJDIR = obj
MODULES := src src2
...
OBJDIRS := $(patsubst %, $(OBJDIR)/%, $(MODULES))
build: $(OBJDIRS)
echo $^
$(OBJDIRS):
mkdir -p $#
make build will create directories, and echo the results.
We also can make the object files are created automatically without invoking the make build.
PROG := hellomake
LD := gcc
...
$(PROG): $(OBJDIRS) obj/src/hellofunc.o obj/src/hellomake.o
$(LD) $(filter %.o, $^) -o $(PROG)
The way I do it is I find out what the paths will be, and create the dirs for them:
SOURCES:=$(shell find $(SRC)/ -type f -name '*.c') #Get all the c files
#Get the files they're gonna be compiled to
OBJECTS:=$(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SOURCES))
#Get just the paths so you can create them in advance
OBJDIRS:=$(dir $(OBJECTS))
#call mkdir with the directories (using a dummy var because too much
#trouble to deal with priorities) someone could prob do better
#--parents ignores the repeated and already existing errors
DUMMY:=$(shell mkdir --parents $(OBJDIRS))
Sauce:
https://www.gnu.org/software/make/manual/html_node/File-Name-Functions.html
In addition to precedent solutions, you can create the directories in your rule for building your .o files
$(OBJ_DIR)/%.o: $(SRCS_DIR)/%.c
#mkdir -p $(shell dirname $#)
$(CC) $(CFLAGS) $(INC) -c $< -o $#
The "#" before mkdir silent the output of mkdir (we don't want a message for each directory created).
The "-p" option tell mkdir to create all intermediate directories in path if they do not exist.
The "dirname" method takes the file path ("$#") and keep only the file directory path.

Makefile to generate JSON from Python scripts in separate directories

I have a Makefile which generates JSON from several different Python scripts (the scripts print to stdout) in a single directory, e.g.
/src
scriptOne.py
scriptTwo.py
scriptThree.py
Which outputs the JSON to a folder:
/templates
scriptOne.json
scriptTwo.json
scriptThree.json
I'm trying to restructure so that, for example, each script is in its own subdirectory and the Makefile creates the JSON templates in their consequent subdirectories as follows:
/src
/importantTemplates
scriptOne.py
/notSoImportantTemplates
scriptTwo.py
scriptThree.py
And the output:
/templates
/importantTemplates
scriptOne.json
/notSoImportantTemplates
scriptTwo.json
scriptThree.json
The current Makefile is as follows:
SOURCES := $(shell echo src/*.py)
TARGETS := $(patsubst src/%.py,templates/%.json,$(SOURCES))
all: $(TARGETS)
clean:
rm -f $(TARGETS)
templates/%.json: src/%.py
python2 $< > $#
I've tried changing the wildcards to include a subdirectory for each line e.g. /src/*/*.py, although I just end up with the following:
make: Nothing to be done for `all'.
You want a static pattern rule (4.12 Static Pattern Rules) for this.
SOURCES := $(wildcard src/*/*.py)
TARGETS := $(patsubst src/%.py,templates/%.json,$(SOURCES))
all: $(TARGETS)
clean:
rm -rf templates
$(TARGETS) : templates/%.json: src/%.py
mkdir -p $(#D)
python2 $< > $#
You could avoid needing mkdir -p in that rule body if you wanted to (and go with an order-only prerequisite on the directory instead) but I'm not sure the effort is worth the savings in execution cost. You could avoid the extra shell by combining the two lines mkdir -p $(#D) && python2 $< > $# if you wanted to though.
Please note that the second time you run the make, it will give you the message (if there are no new files):
make: Nothing to be done for `all'.
Try to run make clean and see if you get the same message.
Here is the Makefile which will do what you want:
SOURCES := $(wildcard src/*/*.py)
TARGETS := $(patsubst src/%.py,templates/%.json,$(SOURCES))
FOLDERS := $(sort $(dir $(TARGETS)))
all: $(TARGETS)
clean:
rm -rf $(TARGETS) $(FOLDERS)
$(FOLDERS):
mkdir -p $#
$(TARGETS): $(SOURCES) $(FOLDERS)
python2 $< > $#
The FOLDERS variable will contain the folders you need to create in the template directory. (sort will remove duplicates, so each folder will be there only once)
The $(FOLDERS) rule will create the folders.
The clean rule will remove the folders also.
If you need to add more sources, just do it like this:
SOURCES := $(wildcard src/*/*.py)
SOURCES += $(wildcard src/*.py)
...

Makefile rule for copying files with multiple extensions into target directory

I would like make to copy files from the source directory into a target directory. And i would like to avoid copying unchanged files. Therefore, I am trying to utilize make function of checking for newer files with a %.:%. rule.
Now, in this instance, the source and target EXTENSION are the same. There are object files elsewhere but not for the graphical assets.
I use make to copy images.
TARGET := target
SOURCE := source
GRAPHICS := $(foreach dir,$(SOURCE), $(wildcard $(dir)/*.jpg ) $(wildcard $(dir)/**/*.jpg ) $(wildcard $(dir)/*.png ) $(wildcard $(dir)/**/*.png ) $(wildcard $(dir)/*.gif ) $(wildcard $(dir)/**/*.gif ) )
JPG = $(GRAPHICS:.jpg=.tmp)
PNG = $(GRAPHICS:.png=.tmp)
GIF = $(GRAPHICS:.gif=.tmp)
And then use the following rule to copy files into target directory:
%.tmp:%.jpg
find $< | cpio -p -d -v $(TARGET)
Questions
Is it possible to string replace the TARGET directory here
and thereby use the make newer capability?
I tried
JPG = $(GRAPHICS:$(TARGET).jpg=.tmp)
But that fails with No rule to make target. Is it only possible to compare source and object in the same directory?
Can one make a rule such that the source and object are the same extension?
%.jpg:%.jpg
The closest I can come up with is:
$(TARGET)%.jpg:%.jpg
but that never runs. Even after a clean.
Here is the solution.
This 'setup' remains the same.
TARGET := target
SOURCE := source GRAPHICS := $(foreach dir,$(SOURCE), $(wildcard $(dir)/*.jpg )
$(wildcard $(dir)/**/*.jpg ) $(wildcard $(dir)/*.png ) $(wildcard $(dir)/**/*.png )
$(wildcard $(dir)/*.gif ) $(wildcard $(dir)/**/*.gif ) )
Now add a prefix to ALL (space separated) values in the $GRAPHICS string
TARGET_GRAPHICS := $(addprefix $(TARGET)/, $(GRAPHICS) )
Note that patsubst aka -- TARGET_GRAPHICS := $(patsubst %, $(TARGET)/%, $(GRAPHICS) ) -- only worked for the first entry. Perhaps I was doing it incorrectly.
Provide a pattern match for each file type. Here do whatever you wish. In my case, copy the input into the target directory. cpio will make the needed directories. cp -p might also work as needed.
$(TARGET)/%.jpg : %.jpg
#echo "--- .jpg copying " $< " to " $# " into dir " $(<D)
#find $< | cpio -p -d -v $(TARGET)
$(TARGET)/%.png : %.png
#echo "--- .png copying " $< " to " $#
#find $< | cpio -p -d -v $(TARGET)
$(TARGET)/%.gif : %.gif
#echo "--- .gif Copying " $< " to " $#
#find $< | cpio -p -d -v $(TARGET)
So you want the copy of d1/d2/f.png into out/d1/d2/f.png to make use of make's dependency checking?
out/d1/d2/f.png: d1/d2/f.png
out/d1/d2/f.png:
cp $< $#
Adding a jpg file d3/g.jpg say,
out/d3/g.jpg: d3/g.jpg
out/d1/d2/f.png: d1/d2/f.png
out/d1/d2/f.png out/d3/g.jpg:
cp $< $#
We can express this more cleanly with a static pattern rule.
out/d3/g.jpg out/d1/d2/f.png: out/% %
cp $< $#
Nice. So adding your ${GRAPHICS} and fleshing things out a bit
outdir := out/
targets := $(addprefix ${outdir},${GRAPHICS})
.PHONY: all
all: ${targets} ; : $# Success
${targets}: ${outdir}%: %
echo $< | cpio -d -p ${outdir}
Parallel safe too, so make -j9 will exercise your 8 CPUs nicely.

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