Makefile: save errors and print at the end - makefile

I have the following Makefile snipt which runs some tests:
MKFILES = $(wildcard $(addprefix $(UNIT_TEST_SRC)/,$(subst $(comma), ,*.mk))))
TESTS = $(MKFILES:.mk=.test)
%.test:%.mk
#make -f $?
run_tests: $(TESTS)
The above works fine, but I'm trying to change it so that instead of aborting when a test errors, it continues and prints out all failures at the end.
FAILURES :=
%.test:%.mk
#make -f $? || $(MAKE) $#.error
.PHONY: %.test.error
%.test.error:
$(eval FAILURES += $#)
run_tests: $(TESTS)
#echo "Failures:" $(FAILURES)
It seems like my eval is not surviving the %.test.error scope, If I move the eval to the %.test then it does survive, although that's not what I want.
Is there a way to do this?

In the recipe of the following rule:
%.test:%.mk
#make -f $? || $(MAKE) $#.error
make is called, and another new running instance of make is created. It is in that instance where the eval takes place. Therefore, it has no effect for the parent make instance that executed that recipe above.
Instead, you could create true .test.error files when an error occurs:
%.test:%.mk
#make -f $? || touch $#.error
and remove them when you are done:
run_tests: $(TESTS)
#echo "Failures:" $(FAILURES)
#$(RM) *.test.error
The definition of FAILURES could be then:
FAILURE = $(wildcard *.test.error)
That way, FAILURE will be expanded when run_tests recipe's is executed (i.e.: deferred expansion by using a recursively expanded variable) and therefore it will contain the list of the .test.error files (if any).

When you call $(MAKE) $#.error, it invokes a new instance of make, and FEATURES gets set there. The new value does not get propagated to the parent make.
I might suggest using a temporary file/folder as so:
.PHONY: prep
prep:
rm -rf failures;
mkdir failures;
%.test:%.mk: | prep
#make -f $? || touch failures/$#.error
run_tests: $(TESTS)
#echo "Failures:"
#cd failures && ls || true
#rm -rf failures

Related

error in a recursively-called make doesn't stop the calling make

I have a makefile that loops over all child directories that have files called Makefile in them, and runs a new make in that directory with my current target. It works fine when it works.
However, if the child make process finds an error it stops... but my calling make doesn't. The calling make continues to run, giving a false impression it is working. How can I make the calling make halt in this case?
# Make the same target in all subdirectories with a Makefile.
SubMakefiles = $(shell find -L . -maxdepth 2 -type f -name Makefile)
SubMakeDirs = $(filter-out ./,$(dir $(SubMakefiles)))
clean depend all::
#for dir in $(SubMakeDirs); do \
make -C $$dir $#
done
Without changing your targeting, you can capture the return value of one or more commands in sh with parenthesis, and exit with their error code should there be an error code, as follows:
# Make the same target in all subdirectories with a Makefile.
SubMakefiles = $(shell find -L . -maxdepth 2 -type f -name Makefile)
SubMakeDirs = $(filter-out ./,$(dir $(SubMakefiles)))
clean depend all::
#for dir in $(SubMakeDirs); do \
( make -C $$dir $# ) || exit $$?; \
done
Ultimately I settled on the following approach. Since the recursive make is run as a command directly by make, any error code produced will cause the master make to abort as well.
Walkthrough: we recurse to any sub-directory with a Makefile in it.
MAKECMDGOALS is a gmake-managed variable holding the command goal. A little counterintuitively, this top-level makefile says that whatever command you're trying to run depends on all the subdirectories with makefiles, and the rule to "create" those subdirectories (of course they already exist, but gmake isn't specifically aware of that) it recursively runs the same command in the subdirectory (specified with -C).
SUBDIRS = ${sort ${dir ${wildcard */Makefile}}}
all $(MAKECMDGOALS):: $(SUBDIRS)
.PHONY: $(SUBDIRS)
$(SUBDIRS)::
$(MAKE) --no-print-directory -C $# $(MAKECMDGOALS)

Dynamic include directive in a Makefile

Let's consider this Makefile:
.SUFFIXES:
.DEFAULT_GOAL := all
out=foo
clean:
rm -f vars.mk
rm -f $(out)
vars.mk: vars.mk.default
#echo "Regenerating $#..."
cp $< $# # Let's assume the translation is much complex than a cp
-include vars.mk
ifeq ($(filter foo,$(FOO)),)
$(error FOO undefined)
endif
all: $(out)
$(out): vars.mk
echo "Cow says: I am not a $(FOO)." > $#
And the file vars.mk.default
FOO = foo bar
I would like to regenerate my targets if vars.mk.default is updated. Furthermore, as double check, one must check that foo exists in $(FOO).
How to force make to regenerate vars.mk if vars.mk.default is updated?
In other words, I would like this output:
$ make clean
$ sed 's/dog/cat/' vars.mk.default
$ make foo
Regenerating vars.mk...
echo "Cow says: I am not a cat" > all
$ make foo
make: Nothing to be done for 'all'.
$ sed 's/cat/dog/' vars.mk.default
$ make
Regenerating vars.mk...
echo "Cow says: I am not a dog" > all
$ rm vars.mak
$ make
Regenerating vars.mk...
echo "Cow says: I am not a dog" > all
To avoid failing if vars.mk doesn't exist, just check for it first:
ifeq ($(wildcard vars.mk),vars.mk)
ifeq ($(filter foo,$(FOO)),)
$(error FOO undefined)
endif
endif
My goal is to regenerate my targets if vars.mk.default is updated.
In this case make your targets depend on that file, but filter it out in the recipes, e.g.
foo.o : foo.cc vars.mk.default
$(COMPILE) $(filter-out vars.mk.default,$^)
In the case vars.mk does not exist, make fails on the ifeq and do not generates vars.mk.
Make is going to build vars.mk and restart, see How Makefiles Are Remade for more details.
So, to avoid that error, check first if FOO is defined with ifdef FOO.
A couple of things. First, you should put a - in front of the include to prevent a warning from popping up if the file does not exist:
-include vars.mk
This will not cause a fatal error if vars.mk is not generated, but because the vars.mk rule would fail in this case, you would get your error from there.
You can then check if $(FOO) contains foo from within a recipe:
checkForFoo: vars.mk
#[[ $(FOO) =~ .*foo.* ]] || false
all:checkForFoo
The recipe is only run after the vars.mk was generated and included, so it should only fail in the conditions you want.

make: automatic execution of targets

In my project, I have a set of programs that are build from sources:
SRC_FILES = $(wildcard $(SRC_DIR)/*.cpp)
TARGETS = $(patsubst $(SRC_DIR)/%.cpp,$(BIN_DIR)/%,$(SRC_FILES))
My build target is simple, and works fine:
all: $(TARGETS)
#echo "- Done target $#"
Now, I want a run target so that all these programs are run from the shell on request. Say, if I have 3 files, I want make to run automatically:
>$ ./test1
>$ ./test2
>$ ./test3
Or
>$ ./test1 && ./test2 && ./test3
I tried this:
run: $(TARGETS)
$(addsuffix && ,$(TARGETS))
That generates the following command:
./test1&& ./test2&&
but it fails, due to the trailing &&
(Of course, I want these to be generated automatically as there can be 3... or 30.)
Edit: actually, the && separator is not required, so something like this:
>$ ./test1; ./test2; ./test3;
will be fine too.
Have some .PHONY line near start of Makefile with
.PHONY: all run
You might have
run: $(TARGETS)
$(addsuffix && ,$(TARGETS)) true
but it is a dirty trick.
Maybe you want to produce the output of test2 into test2.out then you might have
TESTSCRIPTS= $(wildcard test*[0-9])
run: $(patsubst %, %.out, $(TESTSCRIPTS))
test%.out: test%
# here some command to run the test%
As alternatives to Basile Starynkevitch's entirely correct answer here there are (at least) two other options as well.
You can avoid the need to run an unnecessary command (builtin though it might be) to end the list by manually pulling off the first entry (this may in fact be more costly then the shell builtin though).
run: $(TARGETS)
$< $(addprefix &&,$(wordlist 2,$(words $^),$^))
A better option I think, assuming that connecting the commands with && isn't a necessity would be to use $(foreach) to generate the command to be run.
run: $(TARGETS)
$(foreach t,$^,$t;)
The trailing ; in that is crucial as the output from $(foreach) is a single line and you need ; to terminate each shell command (or it is seen as one long command with arguments).

Setting target variable for the name of the makefile from a subdirectory

How can I have the variable for $(MAKEFILE) be defined during target execution?
Basically I have a few make files in subdirectories that are named for a specific platform "Makefile.aix" and just Makefile in all other directories. I would like to set a variable for $(MAKEFILE) that gets defined in each subdirectory. Code would look something like this.
MAKEFILE = Makefile
SUBDIR = ./sub ./sub2
ifneq ($(wildcard Makefile),)
MAKEFILE = Makefile
else
MAKEFILE = Makefile.$(PLATFORM)
endif
all:;
#for i in $(SUBDIR);\
do (\
echo Making $$i ...;\
cd $$i;\
make -f $(MAKEFILE)\
); done
Is there just one Makefile.$(PLATFORM) in each subdirectory, or are there several, for different platforms?
In the first case, you could do something like this:
SUBDIR = ./sub ./sub2
define script
cd $(1); \
$(MAKE) -f Makefile*
endef
all:
$(foreach dir, $(SUBDIR), $(call script,$(dir)))
(The empty line inside the define is significant. It can be omitted, if you add a semicolon at the end of the line $(MAKE) ..., leading to one long command line, containing the commands for all directories, which will then be executed in one chunk.)
An alternative script would be (just a matter of personal preference which you like better):
define script
$(MAKE) -C $(1) -f $(notdir $(wildcard $(1)/Makefile*))
endef
If there are several Makefile.$(PLATFORM) files in a directory it becomes more difficult. I'll have to think about that one some more.
UPDATE: In response to your comment, something like this should work:
define script
$(MAKE) -C $(1) -f $(notdir $(wildcard $(1)/Makefile $(1)/Makefile.$(PLATFORM)))
endef
Following your logic, I'd propose update do () section with:
do (\
echo Making $$i ...;\
cd $$i;\
if [ -f Makefile.$(PLATFORM) ] \
then\
make -f Makefile.$(PLATFORM) \
else\
make -f Makefile\
fi\
); done
This is actually not a make style, but I can't suggest anything better without specific of your project
You can do most of this, including the loop over directories, using GNU make's built-in functions. Put the following in a central place, say $(TOP_DIR)/mk/subdir.mk:
makefile-for-dir = \
$(if $(wildcard $(1)/Makefile),Makefile,Makefile.$(PLATFORM))
make-recursive = \
$(foreach _d,$(1),$(MAKE) -C $(_d) -f $(call makefile-for-dir,$(_d)) && ) :
In each makefile that start recursive makes, use
include $(TOP_DIR)/mk/subdir.mk
SUBDIRS = dir1 dir2 dir3
.PHONY: all
all:
+#$(call make-recursive,$(SUBDIRS))

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