Iterate to next prerequisite file when matching when given variables for statement - makefile

I'm trying to do something like this with make:
SRC := $(src/*.md)
DIST := $(subst -,/,$(patsubst src/%.md, dist/%/index.html, $(SRC)))
all: $(DIST)
$(DIST): $(SRC)
mkdir -p $(#D) && pandoc $< -o $#
E.g., the prerequisite src/2014-04-myfile.md is put into target dist/2014/04/myfile/index.html with the transform pandoc
But when I use $< it only refers to the first argument in the $(SRC) variable.
I know normally we would do something like:
dist/%.html: src/%.md
but since I changed the file name in the output to just index.html for all files and used the original file name to create a new path I'm not sure how to go about iterating over the prerequisites.

Here's one way it could be done. The way this works is that it iterates over $(SRC) to create one rule per source file. The $$ in MAKE_DEP are necessary to prevent make from interpreting the functions when it first reads the contents of MAKE_DEP. The documentation on call and eval are also useful.
SRC := $(wildcard src/*.md)
# Set the default goal if no goal has been specified...
.DEFAULT_GOAL:=all
#
# This is a macro that we use to create the rules.
#
define MAKE_DEP
# _target is a temporary "internal" variable used to avoid recomputing
# the current target multiple times.
_target:=$$(subst -,/,$$(patsubst src/%.md, dist/%/index.html, $1))
# Add the current target to the list of targets.
TARGETS:=$$(TARGETS) $$(_target)
# Create the rule proper.
$$(_target):$1
mkdir -p $$(#D) && pandoc $$< -o $$#
endef # MAKE_DEP
# Iterate over $(SRC) to create each rule.
$(foreach x,$(SRC),$(eval $(call MAKE_DEP,$x)))
.PHONY: all
all: $(TARGETS)
If I create:
src/2000-01-bar.md
src/2014-04-foo.md
and run $ make -n, I get:
mkdir -p dist/2000/01/bar && pandoc src/2000-01-bar.md -o dist/2000/01/bar/index.html
mkdir -p dist/2014/04/foo && pandoc src/2014-04-foo.md -o dist/2014/04/foo/index.html
This could also be done using secondary expansion but it did not appear to me to be simpler or nicer.

Related

How to make a recursive make evaluate sub-directory pre-requisites?

Appreciating that the title is not quite on "target", how can I make it so that when I call make at the top level, it will recursively call the makefiles in the sub-directories?
Having been intrigued by the Kconfig pattern, to learn it, I've applied it to a mark down to pdf generator.
The recursive Makefile resides in ./scripts/Makefile.boilerplate and is defined:
HEADER=$(wildcard section-header.md)
.PHONY:all clean $(md-sub-y)
all clean: $(md-sub-y)
all: $(TARGET)
clean:
# $(RM) $(TARGET)
$(TARGET): $(HEADER) $(md-y) | $(md-sub-y)
# cat /dev/null $^ > $#
$(md-sub-y):
# $(MAKE) -C $(#D) TOPDIR=$(TOPDIR) $(MAKECMDGOALS)
I'm likely using the order-only prerequisite for the $(TARGET) target inappropriately, but it solved a minor problem.
In each directory there is a unique KConfig file (not shown), which lists CONFIG_{OPTION} macros that evaluate to either y or n. Then each directory contains a Makefile that has the form:
include Kconfig
md-$(CONFIG_INTRODUCTION)+= Introduction.md
md-$(CONFIG_FW_UPDATE)+= FW-update.md
md-sub-$(CONFIG_CHAPTERS)+= Chapters/Chapters.md
md-$(CONFIG_CHAPTERS)+= Chapters/Chapters.md
md-$(CONFIG_EXAMPLES)+= Examples.md
md-$(CONFIG_APPENDIX_I)+= Appendix-I.md
md-$(CONFIG_APPENDIX_II)+= Appendix-II.md
md-$(CONFIG_APPENDIX_III)+= Appendix-III.md
include ${TOPDIR}/scripts/Makefile.boilerplate
And finally, the very top level makefile is (abbreviated):
.PHONY: all clean pdf embedded_html
all clean test: JsonAPI/JsonAPI.md
all: pdf embedded_html
pdf: $(MARKDOWN_FILES:.md=.pdf)
embedded_html: $(MARKDOWN_FILES:.md=.html)
MAKEFLAGS += --no-print-directory
clean:
# $(RM) *.pdf *.html
JsonAPI/JsonAPI.md:
# $(MAKE) -C $(#D) TOPDIR=${CURDIR} $(MAKECMDGOALS)
%.html:%.md
# pandoc -s --toc -c /var/www/css/bootstrap.css $< -f markdown -t html -s -o $#
%.pdf:%.md
# pandoc --read=markdown --table-of-contents --toc-depth=3 --preserve-tabs --standalone --template=template.latex $(PANDOC_ENGINE)=pdflatex --listings -V geometry:margin=1in --highlight-style=pygments -H listing-setup.tex -r markdown+simple_tables+table_captions+yaml_metadata_block $< -o $#
If I call make on an unbuilt directory tree, it works fine. But there are a few problems I'm not sure how to address:
How can I ensure that if an updated .md deeply nested in the directory tree will cause the top level PDF file to be updated? Or, How can I force the makefile's in the sub-directories to be called?
The clean target at the top level is problematic, in that it doesn't recurse through the sub-directories. What do I need to do to remedy that?
Is there a better way to include the Makefile.boilerplate makefile, without having to define the TOPDIR on the $(MAKE) command line as I've done?
For 1, and 2, I'm guessing that an empty target dependency (FORCE:) will be required. And for 3, I've tried using $(CURDIR) but it was always evaluating to the directory the Makefile resided in, not the parent directory where the original make command was invoked.
Changing the md-sub-$(CONFIG_EEEE) macro definition to be just the directory was the key, and to make those targets have an empty rule.
Essentially, the per directory Makefile from above becomes:
include Kconfig
md-$(CONFIG_INTRODUCTION)+= Introduction.md
md-$(CONFIG_FW_UPDATE)+= FW-update.md
md-sub-$(CONFIG_CHAPTERS)+= Chapters/Chapters.md
md-$(CONFIG_CHAPTERS)+= Chapters
md-$(CONFIG_EXAMPLES)+= Examples.md
md-$(CONFIG_APPENDIX_I)+= Appendix-I.md
md-$(CONFIG_APPENDIX_II)+= Appendix-II.md
md-$(CONFIG_APPENDIX_III)+= Appendix-III.md
include ${TOPDIR}/scripts/Makefile.boilerplate
and the default Makefile.boilerplate changes the $(md-sub-y) target too:
$(md-sub-y): FORCE
# $(MAKE) -C $# TOPDIR=$(TOPDIR) $(MAKECMDGOALS)
FORCE:
And the top level makefile no longer needs $(#D) on the command line for the JsonAPI recipe, just $#.

Generate a directory if a target name has a directory part

I have this makefile code:
$(DIRS):
#echo " MKDIR build/tmp/base/socket/$#"
$(Q)mkdir -p $#/
%.a.s:
#echo " CC build/tmp/base/socket/$#"
$(Q)$(CC) $(CFLAGS_A) -S $< -o $#
%.so.s:
#echo " CC build/tmp/base/socket/$#"
$(Q)$(CC) $(CFLAGS_SO) -S $< -o $#
%.o: %.s
#echo " AS build/tmp/base/socket/$#"
$(Q)$(AS) $< -o $#
tcp/client.a.s: $(TCP_CLIENT_SRC) $(TCP_CLIENT_INC)
tcp/client.so.s: $(TCP_CLIENT_SRC) $(TCP_CLIENT_INC)
tcp/server.a.s: $(TCP_SERVER_SRC) $(TCP_SERVER_INC)
tcp/server.so.s: $(TCP_SERVER_SRC) $(TCP_SERVER_INC)
I'd like to add a | tcp in the prerequisites of all targets that have a leading tcp/ in their name, but I'd like to be able to do it in one line. I wouldn't want to append that manually to every line that needs it.
I thought of adding this new rule:
tcp/%.s: | tcp
but it isn't doing anything.
I also thought of the more generic, which would be nicer, but same results:
%.s: | $(#D)
How should I write that?
A workaround would be to call mkdir every time (include it in both %.a.s and %.so.s rules), but that would add unnecessary calls to mkdir, wouldn't it?
I am not a fan of pattern rules. They are too arbitrary for my tastes.
(What actually happens depends on what files you may have lying around on your hard disk.)
You cannot just add a prerequisite to a given set of targets using a pattern rule
Well you can if you use static pattern rules. This is a much nicer idiom. Here we prefix a pattern rule with the actual list of sources you want the pattern rule to apply to. This is good where you can describe dependencies using make's noddy pattern matching.
A sketch:
%.a: ; date >$# # Pattern rule
tcp: ; mkdir -p $# # Explicit rule
tcp/a.a: tcp/%.a: | tcp ; # Static pattern rule!
.PHONY: all
all: tcp/a.a
all: c.a
all: dir/b.a
all: ; : $# Success
And we have:
$ make all
mkdir -p tcp
date >tcp/a.a
date >c.a
date >dir/b.a
/bin/sh: dir/b.a: No such file or directory
make: *** [Makefile:3: dir/b.a] Error 1
Here we have told make that before it builds (i.e., "runs the recipe for") tcp/a.a, it must first build tcp. That works. We didn't tell make about the directory for dir/b.a, so that failed. Note that the .a file's recipe is still in an ordinary pattern rule. This is just by way of exposition. I would definitely change this.
Yeah, in this case the pattern rule for tcp/ is over-the-top. Consider though that before you create tcp/a.a, you might first need to create a auto/a.src (say).
tcp/a.a: tcp/%.a: auto/%.src | tcp ; # Static pattern rule!
Easily extensible.
targets := tcp/a.a tcp/b.a tcp/c.a
${targets}: tcp/%.a: auto/%.src | tcp ; # Static pattern rule!
[By the way, in your original makefile, your archive and shared object should depend on the .o files, not the source (???)]
You cannot just add a prerequisite to a given set of targets using a pattern rule. That's not how pattern rules work: they are rules: they must have a recipe associated with them. A pattern rule without a recipe actually deletes that pattern rule (see Canceling Implicit Rules).
You can either create two sets of pattern rules, one for all targets and a second one for just targets that start with tcp/ that have an extra prerequisite, but you have to write the entire pattern rule twice, including a recipe, not just the pattern line.
Or just put the mkdir into the recipe. A mkdir on a directory that already exists won't even be noticeable.
This answer is only to show a working Makefile implementing #bobbogo 's answer.
This Makefile is a leaf of a Makefile tree, so all variables not defined here are exported by upper Makefiles.
Makefile:
#! /usr/bin/make -f
################################################################################
# *AUTHOR*
# FULL NAME "Alejandro Colomar Andrés"
################################################################################
################################################################################
DIRS = \
$(CURDIR)/tcp
OBJ = \
$(CURDIR)/tcp/client.o \
$(CURDIR)/tcp/server.o \
$(CURDIR)/foo.o
SRC = \
$(SRC_DIR)/base/socket/tcp/client.c \
$(SRC_DIR)/base/socket/tcp/server.c \
$(SRC_DIR)/base/socket/foo.c
DEP = $(OBJ:.o=.d)
BOTH_OBJ = $(subst .a.o,.a.o ,$(join $(OBJ:.o=.a.o),$(OBJ:.o=.so.o)))
BOTH_ASM = $(subst .a.s,.a.s ,$(join $(OBJ:.o=.a.s),$(OBJ:.o=.so.s)))
NEEDDIR = $(DEP) $(BOTH_ASM)
################################################################################
PHONY := all
all: $(BOTH_OBJ)
#:
$(DIRS): $(CURDIR)/%:
#echo " MKDIR build/tmp/base/socket/$*"
$(Q)mkdir -p $#
$(NEEDDIR): | $(DIRS)
$(CURDIR)/%.d: $(SRC_DIR)/base/socket/%.c
#echo " CC -M build/tmp/base/socket/$*.d"
$(Q)$(CC) $(CFLAGS_A) -MG -MT"$#" \
-MT"$(CURDIR)/$*.a.s" -MT"$(CURDIR)/$*.so.s" \
-M $< -MF $#
$(CURDIR)/%.a.s: $(SRC_DIR)/base/socket/%.c $(CURDIR)/%.d
#echo " CC build/tmp/base/socket/$*.a.s"
$(Q)$(CC) $(CFLAGS_A) -S $< -o $#
$(CURDIR)/%.so.s: $(SRC_DIR)/base/socket/%.c $(CURDIR)/%.d
#echo " CC build/tmp/base/socket/$*.so.s"
$(Q)$(CC) $(CFLAGS_SO) -S $< -o $#
$(CURDIR)/%.o: $(CURDIR)/%.s
#echo " AS build/tmp/base/socket/$*.o"
$(Q)$(AS) $< -o $#
include $(DEP)
PHONY += clean
clean:
$(Q)rm -rf *.o *.s *.d
################################################################################
# Declare the contents of the PHONY variable as phony.
.PHONY: $(PHONY)
################################################################################
######## End of file ###########################################################
################################################################################
output:
MKDIR build/tmp/base/socket/tcp
CC -M build/tmp/base/socket/foo.d
CC -M build/tmp/base/socket/tcp/server.d
CC -M build/tmp/base/socket/tcp/client.d
CC build/tmp/base/socket/tcp/client.a.s
AS build/tmp/base/socket/tcp/client.a.o
CC build/tmp/base/socket/tcp/client.so.s
AS build/tmp/base/socket/tcp/client.so.o
CC build/tmp/base/socket/tcp/server.a.s
AS build/tmp/base/socket/tcp/server.a.o
CC build/tmp/base/socket/tcp/server.so.s
AS build/tmp/base/socket/tcp/server.so.o
CC build/tmp/base/socket/foo.a.s
AS build/tmp/base/socket/foo.a.o
CC build/tmp/base/socket/foo.so.s
AS build/tmp/base/socket/foo.so.o

Why does this makefile target specific variable not expand as expected?

I have the following simplified makefile and I'm trying to set different paths based on different targets. Unfortunately, I'm not getting the results that I expect. This is with make version 3.81.
.SECONDEXPANSION:
all: Debug32
# Object directory set by target
Debug32: OBJDIR = objdir32
#OBJDIR = wrongdirectory
# ObjDir is empty here. :(
OBJS = $(addprefix $(OBJDIR)/,DirUtil.o)
$(OBJDIR)/%.o : %.cpp
echo Compile: $#
Debug32: $(OBJS)
$(OBJS): | $(OBJDIR)
$(OBJDIR):
echo mkdir $(OBJDIR) - $#
The results are as follows with no setting of OBJDIR:
echo Compile: /DirUtil.o
If I uncomment the "OBJDIR = wrongdirectory" line, I'll get the following results, which are confusing since I see both values of the variable where I think I should only see one:
echo mkdir objdir32 - wrongdirectory -
echo Compile: wrongdirectory/DirUtil.o
I'm assuming that the variables are not being expanded when I think they should, but I can't figure out how to alter this behavior.
From the GNU info manual
Variables and functions in all parts of a makefile are expanded when
read, except for in recipes, the right-hand sides of variable
definitions using '=', and the bodies of variable definitions using
the 'define' directive.
The target-specific variable only applies within recipes. Within
$(OBJS): | $(OBJDIR)
and
$(OBJDIR):
it is getting the global variable.
So working through what happens when you run make Debug32, it sees the contents of OBJS as a prerequisite, which leads to the first rule above. $(OBJDIR) has already been substituted with the global value, and this matches the target-name in the second rule which has also been substituted the same way.
However, when we get to the recipe:
echo mkdir $(OBJDIR) - $#
$(OBJDIR) has not been substituted yet, so it gets the target-specific variable value.
A working version
.SECONDEXPANSION:
all: Debug32
# Object directory set by target
Debug32: OBJDIR = objdir32
OBJDIR = wrongdirectory
Debug32: OBJS = $(addprefix $(OBJDIR)/,obj.o)
OBJS = wrongobjs
Debug32: $$(OBJS)
echo OBJS are $(OBJS)
echo OBJDIR is $(OBJDIR)
%/obj.o: | %
touch $#
OBJDIRS = objdir32 wrongdirectory anotherdirectory
$(OBJDIRS):
# mkdir $(OBJDIR)
mkdir $#
The main change is using $$ in this line:
Debug32: $$(OBJS)
With only a single $, I get the error message
make: *** No rule to make target `wrongobjs', needed by `Debug32'. Stop.
However, with the $$, I get
echo OBJS are objdir32/obj.o
OBJS are objdir32/obj.o
echo OBJDIR is objdir32
OBJDIR is objdir32
The use of secondary expansion has allowed accessing the target-specific variable in the prerequisites.
The other change is that I made OBJS a target-specific variable (because it is). In order to have a rule to build OBJS whatever its value, I had to use a pattern rule:
%/obj.o: | %
To avoid having a separate line for each object file, you could do the following instead:
OBJ_BASENAMES=obj.o obj2.o obj3.o
$(addprefix %/,$(OBJ_BASENAMES)): | %
touch $# # Replace with the proper recipe
The line containing the addprefix macro expands to
%/obj.o %/obj2.o %/obj3.o: | %
Then running make anotherdirectory/obj2.o creates a directory called "anotherdirectory" first, and creates a file called "obj2.o" within it.
Note all possible directories have to be listed in OBJDIRS. There's no way to collect all the rule-specific values of OBJDIR, so listing them is the best choice. The alternative is a % : rule to build any directory, which be capable of matching and building any target, which could be risky. (If you abandon the use of target-specific variables, there is another way of getting a list of directories that could be built: use variables with predictable names like Debug32_OBJDIR instead, and generate a list of their values using make functions.)
Alternatively, a generic rule that doesn't require listing the object files:
SOURCE=$(basename $(notdir $#)).cpp
DIR=$(patsubst %/,%,$(dir $#))
%.o: $$(SOURCE) | $$(DIR)
touch $# # Replace with proper recipe
There is no feature to read a rule in the context of every target, substituting in target-specific variables and acquiring a new rule for each target. Generic rules cannot be written in this way using target-specific variables.
a good way to handle
%/obj.o: | %
touch $#
OBJDIRS = objdir32 wrongdirectory anotherdirectory
$(OBJDIRS):
mkdir $#
is:
%/.:
mkdir -p $#
.SECONDEXPANSION:
then later you can just write, for any target that may need a directory
target: prerequisites | $$(#D)/.
normal recipe for target

make wildcard subdirectory targets

I have a "lib" directory in my applications main directory, which contains an arbitrary number of subdirectories, each having its own Makefile.
I would like to have a single Makefile in the main directory, that calls each subdirectory's Makefile. I know this is possible if I manually list the subdirs, but I would like to have it done automatically.
I was thinking of something like the following, but it obviously does not work. Note that I also have clean, test, etc. targets, so % is probably not a good idea at all.
LIBS=lib/*
all: $(LIBS)
%:
(cd $#; $(MAKE))
Any help is appreciated!
The following will work with GNU make:
LIBS=$(wildcard lib/*)
all: $(LIBS)
.PHONY: force
$(LIBS): force
cd $# && pwd
If there might be something other than directories in lib, you could alternatively use:
LIBS=$(shell find lib -type d)
To address the multiple targets issue, you can build special targets for each directory, then strip off the prefix for the sub-build:
LIBS=$(wildcard lib/*)
clean_LIBS=$(addprefix clean_,$(LIBS))
all: $(LIBS)
clean: $(clean_LIBS)
.PHONY: force
$(LIBS): force
echo make -C $#
$(clean_LIBS): force
echo make -C $(patsubst clean_%,%,$#) clean
There is also a way of listing sub-directories with gmake commands only, without using any shell commands:
test:
#echo $(filter %/, $(wildcard lib/*/))
This will list all sub-directories with trailing '/'. To remove it you can use the substitute pattern:
subdirs = $(filter %/, $(wildcard lib/*/))
test:
#echo $(subdirs:%/=%)
Then to actually create rules executing makefiles in each sub-directory you can use a small trick - a phony target in a non-existent directory. I think in this case an example will tell more than any explanation:
FULL_DIRS =$(filter %/, $(wildcard lib/*/))
LIB_DIRS =$(FULL_DIRS:%/=%)
DIRS_CMD =$(foreach subdir, $(LIB_DIRS), make-rule/$(subdir))
make-rule/%:
cd $* && $(MAKE)
all: DIRS_CMD
Basically, target 'all' lists all sub-directories as prerequisites. For example, if LIB_DIRS contained lib/folder1 lib/folder2 then the expansion would look like this:
all: make-rule/lib/folder1 make-rule/lib/folder2
Then 'make', in order to execute rule 'all', tries to match each prerequisite with an existing target. In this case the target is 'make-rule/%:', which uses '$*' to extract the string after 'make-rule/' and uses it as argument in the recipe. For example, the first prerequisite would be matched and expanded like this:
make-rule/lib/folder1:
cd lib/folder1 && $(MAKE)
What if you want to call different targets than all in an unknown number of subdirectories?
The following Makefile uses macros so create a forwarding dummy-target for a number of subdirectories to apply the given target from the command line to each of them:
# all direct directories of this dir. uses "-printf" to get rid of the "./"
DIRS=$(shell find . -maxdepth 1 -mindepth 1 -type d -not -name ".*" -printf '%P\n')
# "all" target is there by default, same logic as via the macro
all: $(DIRS)
$(DIRS):
$(MAKE) -C $#
.PHONY: $(DIRS)
# if explcit targets where given: use them in the macro down below. each target will be delivered to each subdirectory contained in $(DIRS).
EXTRA_TARGETS=$(MAKECMDGOALS)
define RECURSIVE_MAKE_WITH_TARGET
# create new variable, with the name of the target as prefix. it holds all
# subdirectories with the target as suffix
$(1)_DIRS=$$(addprefix $(1)_,$$(DIRS))
# create new target with the variable holding all the subdirectories+suffix as
# prerequisite
$(1): $$($1_DIRS)
# use list to create target to fullfill prerequisite. the rule is to call
# recursive make into the subdir with the target
$$($(1)_DIRS):
$$(MAKE) -C $$(patsubst $(1)_%,%,$$#) $(1)
# and make all targets .PHONY
.PHONY: $$($(1)_DIRS)
endef
# evaluate the macro for all given list of targets
$(foreach t,$(EXTRA_TARGETS),$(eval $(call RECURSIVE_MAKE_WITH_TARGET,$(t))))
Hope this helps. Really helpfull when dealing with paralelism: make -j12 clean all in a tree with makefiles having these targets... As always: playing with make is dangerous, different meta-levels of programming are too close together ,-)

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