Compile several projects (with makefile), but stop on first broken build? - bash

I want to do something like:
for i in *
do
if test -d $i
then
cd $i; make clean; make; cd -;
fi;
done
And this works fine, but I want "break" the for-loop in case of a broken build.
Is there a way to do this? Maybe some kind of if-statement, that can check for success of make?

You can use Make itself to achieve what you're looking for:
SUBDIRS := $(wildcard */.)
.PHONY : all $(SUBDIRS)
all : $(SUBDIRS)
$(SUBDIRS) :
$(MAKE) -C $# clean all
Make will break execution in case when any of your target fails.
UPD.
To support arbitrary targets:
SUBDIRS := $(wildcard */.) # e.g. "foo/. bar/."
TARGETS := all clean # whatever else, but must not contain '/'
# foo/.all bar/.all foo/.clean bar/.clean
SUBDIRS_TARGETS := \
$(foreach t,$(TARGETS),$(addsuffix $t,$(SUBDIRS)))
.PHONY : $(TARGETS) $(SUBDIRS_TARGETS)
# static pattern rule, expands into:
# all clean : % : foo/.% bar/.%
$(TARGETS) : % : $(addsuffix %,$(SUBDIRS))
#echo 'Done "$*" target'
# here, for foo/.all:
# $(#D) is foo
# $(#F) is .all, with leading period
# $(#F:.%=%) is just all
$(SUBDIRS_TARGETS) :
$(MAKE) -C $(#D) $(#F:.%=%)

You can check whether the make has exited successfully by examining its exit code via the $? variable, and then have a break statement:
...
make
if [ $? -ne 0 ]; then
break
fi

Related

What is the difference between '$$directoryName' and '$(directoryName)' in makefile

I have two makefiles
The first :
dir = ../dir1
dis = ../dir2
test:
$(MAKE) -C $(dir)
The second one :
DIRS = dir1 dir2 dir3
test:
for dir in $(DIRS); do \
if $(MAKE) -C $$dir ; then \
true; \
else \
exit 1; \
fi; \
done
Why in the for loop I need $$dir when in a simple recipe I have to write $(dir)
Another question:
I have this other makefile, in which I have this other for loop:
all clean dep depend print:
for dir in $(DIRS); do \
if $(MAKE) $(MAKE_FLAGS) -C $$dir $#; then \
true; \
else \
exit 1; \
fi; \
done
What is the meaning of $# in the line
if $(MAKE) $(MAKE_FLAGS) -C $$dir $#; then \
I know this is an Automatic Variable that matches the file name of the target of the rule.
Here the target appears to be a command like cancel:
cancell:
rm -rf *.o
One is a makefile variable the other is a shell variable. $(directoryName) will be resolved by make, as it's reading. For $$directoryName, make converts the $$ to $, and passes that to the shell (so $$directoryName becomes$directoryName). The shell expands it to whatever it has in its environment.
A bit more detail:
If, in make, you define (outside of any recipe)
var := 1
then var is a make variable. If you then call
all:
echo $(var)
Then make expands the recipe to echo ../dir1 before passing the command to the shell. If, on the other hand, you do:
all:
var=1; echo $$var
Then make passes var=1; echo $var to the shell. The shell sets var to 1, and then prints it. Notice if you tried:
all:
var=1;
echo $$var
Then the recipe will not print anything -- that's because the first line is run in a shell, which sets var to 1, but then exits. The next line runs, which passes echo $var in a new shell, but this shell doesn't know what var was set to in the previous shell.
In addition, if you run var=1;make, then make will set a makefile variable var to be 1 when it starts. Thus a $(info $(var)) will show 1, in this case.
For syntax, in make you can do var := 1 (with spaces). In bash you cannot add spaces. In bash, $var refers to the variable var. In make $var refers to $v followed by the literal ar. (In make you have to use either {} or () to refer to multicharacter variables). In bash, you can either use {}, or no braces ($(var) has a different meaning in bash).
make also has two flavors of variables, one defined with := and one with =.

make fclean && make all works, make re doesn't

I'm working on an advanced makefile that I found in a book. I've got some simple rules inside:
clean to delete binaries
fclean to delete some extra files too (links to binaries generated by ln)
all to make all
re to make fclean then make all
when I do make fclean then make all, it works perfectly. When i do make re, an error occurs:
error: unable to open output file '/Users/malberte/work/libft/bin/libft/common/ft_atoi.o':
'No such file or directory'
1 error generated.
So here is my code:
$(_MODULE_NAME)_OBJS := $(addsuffix $(_OBJEXT),$(addprefix $($(_MODULE_NAME)_OUTPUT)/,$(basename $(SRCS)))) $(DEPS)
$(_MODULE_NAME)_BINARY := $($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(BINARY_EXT)
$(_MODULE_NAME)_EXPOSE_BINARY := $(_ROOT)/$(BINARY)$(BINARY_EXT)
ifneq ($(_NO_RULES),T)
ifneq ($($(_MODULE_NAME)_DEFINED), T)
_CLEAN := clean-$(_MODULE_NAME)
_FCLEAN := fclean-$(_MODULE_NAME)
_ALL := all-$(_MODULE_NAME)
_RE := re-$(_MODULE_NAME)
_IGNORE := $(shell mkdir -p $($(_MODULE_NAME)_OUTPUT))
.PHONY: all re $(_ALL) $(_RE)
re: fclean all
# re: $(_RE)
# $(_RE): $(_FCLEAN) $(_ALL)
all: $(_ALL)
$(_ALL): $($(_MODULE_NAME)_BINARY)
.PHONY: $(_MODULE_NAME)
$(_MODULE_NAME): $($(_MODULE_NAME)_BINARY)
.PHONY: fclean clean $(_CLEAN)
fclean: $(_FCLEAN)
$(_FCLEAN): $(_CLEAN)
rm -rf $($(patsubst fclean-%,%,$#)_EXPOSE_BINARY)
clean: $(_CLEAN)
$(_CLEAN):
rm -rf $($(patsubst clean-%,%,$#)_OUTPUT)
$($(_MODULE_NAME)_OUTPUT)/%.o: $(_MODULE_PATH)/%.c
#$(COMPILE.c) -o '$#' '$<'
$($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(_LIBEXT): $($(_MODULE_NAME)_OBJS)
#if [ "$(LIBMERGE)" = "F" ]; \
then \
$(AR) r '$#' $^; \
ranlib '$#'; \
else \
libtool -static -o '$#' $^; \
fi
$($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(_EXEEXT): $($(_MODULE_NAME)_OBJS)
$(LINK.c) $^ -o '$#'
$(_MODULE_NAME)_DEFINED := T
endif
endif
I've tried lot of things, I really don't understand what is happening when I use make re and it throws the error above.
Someone has an idea please ?
You have this line in your makefile:
_IGNORE := $(shell mkdir -p $($(_MODULE_NAME)_OUTPUT))
which creates the output directory, as the makefile is being parsed. Then you run your clean target which invokes this recipe:
rm -rf $($(patsubst clean-%,%,$#)_OUTPUT)
which causes the output directory to be deleted. Then you run your all target which invokes the compiler and asks it to write the output file to $($(_MODULE_NAME)_OUTPUT)/%.o but that directory no longer exists.
So the compiler gives you the error:
error: unable to open output file '...': No such file or directory
If you run make twice, then the first time you clean and delete the directory, then when you run make all it will run the _IGNORE shell command and create the directory again so it will exist.
If you run make re one time, then the makefile is only parsed one time and the output directory is only created one time (before it's deleted).
Okay thank you so much. It drove me to think about how a makefile really works, so here is my basic solution, thanks to you:
$(_MODULE_NAME)_OBJS := $(addsuffix $(_OBJEXT),$(addprefix $($(_MODULE_NAME)_OUTPUT)/,$(basename $(SRCS)))) $(DEPS)
$(_MODULE_NAME)_BINARY := $($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(BINARY_EXT)
$(_MODULE_NAME)_EXPOSE_BINARY := $(_ROOT)/$(BINARY)$(BINARY_EXT)
ifneq ($(_NO_RULES),T)
ifneq ($($(_MODULE_NAME)_DEFINED), T)
_OUTPUT_TREE := output-tree-$(_MODULE_NAME)
_CLEAN := clean-$(_MODULE_NAME)
_FCLEAN := fclean-$(_MODULE_NAME)
_ALL := all-$(_MODULE_NAME)
_RE := re-$(_MODULE_NAME)
# _IGNORE := $(shell mkdir -p $($(_MODULE_NAME)_OUTPUT))
.PHONY: all re $(_ALL) $(_RE)
re: fclean all
# re: $(_RE)
# $(_RE): $(_FCLEAN) $(_ALL)
all: $(_ALL)
$(_ALL): $($(_MODULE_NAME)_BINARY)
.PHONY: $(_MODULE_NAME)
$(_MODULE_NAME): $($(_MODULE_NAME)_BINARY)
.PHONY: fclean clean $(_CLEAN)
fclean: $(_FCLEAN)
$(_FCLEAN): $(_CLEAN)
rm -rf $($(patsubst fclean-%,%,$#)_EXPOSE_BINARY)
clean: $(_CLEAN)
$(_CLEAN):
rm -rf $($(patsubst clean-%,%,$#)_OUTPUT)
$($(_MODULE_NAME)_OUTPUT)/%.o: $(_MODULE_PATH)/%.c | $(_OUTPUT_TREE)
#$(COMPILE.c) -o '$#' '$<'
$($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(_LIBEXT): $($(_MODULE_NAME)_OBJS)
#if [ "$(LIBMERGE)" = "F" ]; \
then \
$(AR) r '$#' $^; \
ranlib '$#'; \
else \
libtool -static -o '$#' $^; \
fi
$($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(_EXEEXT): $($(_MODULE_NAME)_OBJS)
$(LINK.c) $^ -o '$#'
.PHONY: output-tree $(_OUTPUT_TREE)
output-tree: $(_OUTPUT_TREE)
$(_OUTPUT_TREE):
mkdir -p $($(_MODULE_NAME)_OUTPUT)
$(_MODULE_NAME)_DEFINED := T
endif
endif
I added a prerequisite order to the atomic target
$($(_MODULE_NAME)_OUTPUT)/%.o: $(_MODULE_PATH)/%.c | $(_OUTPUT_TREE)
Here is the rule:
.PHONY: output-tree $(_OUTPUT_TREE)
output-tree: $(_OUTPUT_TREE)
$(_OUTPUT_TREE):
mkdir -p $($(_MODULE_NAME)_OUTPUT)
I'll see if I need more adjustments but it seems to be the right way !

Master Makefile - going to directories from current path

I would like to create a Master Makefile from which I go to subdirectories and call other Makefiles. For this master Makefile, I did :
DIR_1D = $(dir $(mkfile_dir))1D
DIR_2D = $(dir $(mkfile_dir))2D
DIR_3D = $(dir $(mkfile_dir))3D
# Phony target
.PHONY: all clean
all:
#(cd $(DIR_1D) ; $(MAKE))
#(cd $(DIR_2D) ; $(MAKE))
#(cd $(DIR_3D) ; $(MAKE))
# Clean target
clean:
#(cd $(DIR_1D) ; $(MAKE) $#)
#(cd $(DIR_2D) ; $(MAKE) $#)
#(cd $(DIR_3D) ; $(MAKE) $#)
UPDATE : Sorry, stupid typo error, fixed, thanks
You set variables DIR_1D, DIR_2D, DIR_3D, but your cd commands use DIR_1, DIR_2, DIR_3. Since you didn't set those variables, you're running cd with no arguments, and cd with no arguments means cd "$HOME".

Makefile improvement when intermediate files do not really change

I have a Makefile like this
all: *.foo
./finalstep *.foo > $#
%.foo: %.bar
./secondstep < $< > $#
%.bar: %.baz
./firststep < $< > $#
The thing is that often changes in a .baz file are minor in the sense that the .bar file produced after the change is the same (content-wise, or as would be detected by diff) as before the change.
Since secondstep and finalstep (and possible some more intermediate steps) are expensive it would be preferable if the lack of change in the .bar files could be detected and thus the invocation of secondstep (and maybe even finalstep) spared. Is there any way to achieve this?
My attempt to do something like this is as follows:
%.bar: %.baz
touch $#; cp $# $#.backup; ./firststep < $< > $#
%.foo: %.bar
diff -q $< $<.backup || ./secondstep < $< > $#
But this has a lot of drawbacks (and does not work correctly if one invokes make with arguments).
Is there any better method? Basically, make should consider two different filetimes for .bar files: One that gets updated each time firststep is run and that is used to determine whether .bar itself needs to be remade. Another that is only updated when a run of firststep results in a net change of content of the file and that is used to determine whethr .foo needs to be remade ...
I think you should experiment with something like this (not tested):
all: %.foo
./finalstep %.foo > $#
%.foo: %.bar
#if [[ -f $< && -f $<.backup ]]; then \
if diff -q $< $<.backup; then \
echo "IDENTICAL => do nothing"; \
else \
echo "DIFFERENT => proceed"; \
./secondstep < $< > $#; \
fi; \
else \
echo "NOT FOUND => proceed"; \
./secondstep < $< > $#; \
fi; \
cp $< $<.backup
%.bar: %.baz
./firststep < $< > $#;
Basically, the intention of the big recipe is to handle all the relevant combinations of $< and $<.backup (in the same subshell!):
both files present and equal (diff returns 0) => do nothing
both files present and differ (diff returns 1) => execute
one of the files not present => execute
Whatever is the above choice, make a backup at the end.

Makefile rule always been processed

My recipe $(HDAIMG) is always been processed, even when already there is a $(HDAIMG) file in the folder. What am I doing wrong?
HDAIMG := $(TESTDIR)/$(PROJECT)-hda.img
HDAIMG value, actually, is test/project-hda.img
PHONY: $(PROJECT)
all: $(PROJECT) $(HDAIMG)
$(PROJECT): check-env
$(call v_exec, 1, $(MAKE) -C $(SRCDIR) $#)
$(HDAIMG): $(PROJECT) check-env
$(call print_white_init, HDAIMG)
$(call print, Creating $#)
$(call v_exec, 2, dd if=/dev/zero of=$# count=0 bs=1 seek=$(HDAIMGSIZE) &> /dev/null)
$(call print, Partitioning $#)
$(call v_exec, 2, parted --script $# mklabel msdos mkpart primary ext4 1 100%)
$(call print, Creating $# device maps)
$(call v_exec, 2, sudo kpartx -a $# -s)
$(call v_exec, 2, sudo mkfs.ext4 /dev/mapper/loop0p1 -q)
$(call v_exec, 2, sudo mount /dev/mapper/loop0p1 $(TESTDIR)/mnt)
$(call v_exec, 2, sudo umount $(TESTDIR)/mnt)
$(call v_exec, 2, sudo kpartx -d $#)
$(call print_white_done, HDAIMG)
check-env:
ifneq ($(ERROR),)
$(call print_error, $(ERROR))
exit 1
endif
That called functions are used to print with color or to execute with choosed verbose; there are in my Makeconfig.mk already included. Some:
v_exec = $(V$(strip $(1)))$(strip $(2))
print = #echo -e '$(LEAD_SUB_STR) $(strip $(1))'
print_white_init= #echo -e '$(subst PATTERN,$(strip $(1)),$(WHITE_INIT)) $(strip $(2))'
print_white_done= #echo -e '$(subst PATTERN,$(strip $(1)),$(WHITE_DONE)) $(strip $(2))'
$(HDAIMG) has check-env as a prerequisite, and Make always thinks that check-env must be rebuilt, because check-env is not actually a file that exists. Therefore Make decides that $(HDAIMG) must be rebuilt.
It would make more sense to perform the check as the first command in the rule, rather than as a prerequisite.

Resources