Makefile that rebuilds on an external condition such as an ENV change - makefile

I would like to rebuild a program if an external condition has changed.
From this example, the condition is the date of the day and here is my program:
#include <stdio.h>
#define STRINGIZE(x) #x
#define STRINGIZE_VALUE_OF(x) STRINGIZE(x)
int main(int argc, char *argv[])
{
printf("%s", STRINGIZE_VALUE_OF(condition));
}
And the Makefile:
condition != date +"%m-%d-%y"
all: foo
foo: foo.c
gcc -Dcondition="$(condition)" $< -o $#
I tried to modify the Makefile as follow:
condition != date +"%m-%d-%y" | perl -pe chomp
$(shell \
if [ ! -f CONDITION] || [ "$(condition)" != "$$(cat CONDITION | perl -pe chomp)" ]; \
then \
echo "$(condition)" > CONDITION; \
fi \
)
all: foo
foo: foo.c | CONDITION
gcc -Dcondition="$(condition)" $< -o $#
Unfortunately, it does not work. I guess because make evaluates if the files have changes right before executing my $(shell) routine.
How can I implement such feature with make?
EDIT
The trick is to not call a sub-makefile

The thing to do here is not to fight make but to use it.
condition != date +"%m-%d-%y"
all: foo
foo: foo.c CONDITION
gcc -Dcondition="$(condition)" $< -o $#
CONDITION: FORCE
[ -f $# ] && [ "$(condition)" = "$$(cat $#)" ] || echo '$(condition)' > $#
FORCE: ;

Another approach is to turn you condition into a file timestamp and then make your target depend on that condition file.
Whenever the condition evaluates to true the condition file gets assigned a new timestamp causing a rebuild of all targets that depend on it. Changing the timestamp of that file must be made by $(shell) command.
E.g.
$ cat Makefile
# Example: update the condition every minute.
$(shell date +%Y%m%d%H%M > condition.txt.tmp; cmp --quiet condition.txt.tmp condition.txt 2>/dev/null || mv -f condition.txt.tmp condition.txt)
all : foo
foo : condition.txt
#echo "rebuild $# because $? changed."
touch $#
$ date; make
Mon 18 Apr 15:17:25 BST 2016
rebuild foo because condition.txt changed.
touch foo
$ date; make
Mon 18 Apr 15:17:27 BST 2016
make: Nothing to be done for 'all'.
$ date; make
Mon 18 Apr 15:18:01 BST 2016
rebuild foo because condition.txt changed.
touch foo
$ date; make
Mon 18 Apr 15:18:03 BST 2016
make: Nothing to be done for 'all'.

I thing I found something. The trick is to mark the target as .PHONY if the condition is true
condition != date +"%m-%d-%y" | perl -pe chomp
need_rebuild != \
if [ ! -f CONDITION ] || [ "$(condition)" != "$$(cat CONDITION | perl -pe chomp)" ]; \
then \
echo "$(condition)" > CONDITION; \
echo YES; \
fi
ifeq ($(need_rebuild),YES)
$(info Rebuild Needed)
.PHONY: foo
endif
all: foo
foo: foo.c | CONDITION
gcc -Dcondition="$(condition)" $< -o $#

Related

Key value pair in MAKEFILE

I am new in makefle, I had need array in makefile then I found that I can achieve that having variable that their items separated with spaces, and then iterating over it.
Now I want something like map(key,value) pair to store values with keys.
Question: can I have that in makefile ?
Thanks in advance..
You can use token pasting for that:
VAR_FOO_KEY := FOO_VAL
VAR_BAR_KEY := BAR_VAL
#example lookup:
KEY := FOO_KEY
LOOKUP_VAL := $(VAR_$(KEY))
Make a file named KV.mk with the following function definitions:
# KV(3 args): in a dictionary directory record a key value pair in a file
# KV(2 args): in a dictionary directory recover value associated with a key
# KV(1 arg ): remove a dictionary directory
# KV(0 arg ): remove all dictionary directories
define KV
$(if $4,$(error K0123.mk: args>3 forbidden), \
$(if $3,mkdir -p $1.key; echo "$3" > $1.key/$2, \
$(if $2,`cat $1.key/$2`, \
$(if $1,rm -fr $1.key/*, \
rm -fr *.key \
))))
endef
Then make a client Makefile with the following contents:
include KV.mk
all: hello.cpp hello
.PHONY: hello.cpp
hello.cpp:
#echo '#include <iostream>' > $#
#echo 'int main(int argc, char** argv)' >> $#
#echo '{ std::cout << "hello" << std::endl; return 0; }' >> $#
hello: hello.cpp
#$(call KV,$#)
#$(call KV,$#,compiler,g++)
#$(call KV,$#,compiler) -o hello hello.cpp
#./$#
clean:
#rm -f hello.cpp hello
#$(call KV)
Copy these two files into an empty directory and run "make".
This method offers much liberty in having multiple dictionaries
with multiple key/value pairs per dictionary.
One dictionary may be used for the entire Makefile.
Another dictionary may be used for all rules having common needs.
Individual dictionaries may be used for each rule.
Simplifying one more step, here is a shell script encapsulating the KV idea.
#!/bin/bash
usage() {
cat <<USAGE
KV(1) -- syre(tm) user command
NAME
KV - key value store management for Makefile
SYNOPSIS
(1) KV [--help]
(2) KV [--clear]
(3) KV [OPTION]... {key}
(4) KV [OPTION]... {key}={value}
1: display usage (this text)
2: clear all dictionaries (make clean)
3: print value associated with key
4: store key value pair in the dictionary and key file
USAGE
if [ "" != "$1" ] ; then echo "KV error: $1"; fi
exit 0
}
if [ $1 = "--help" ] ; then usage;
elif [ $1 = "--clear" ] ; then rm -fr *.key;
else
case $# in
3) mkdir -p $1.key; echo "$3" > $1.key/$2;;
2) cat $1.key/$2;;
1) rm -fr $1.key;;
*) usage "KV error: 1, 2, or 3 args required";;
esac
fi
and here is the same Makefile showing that simplification.
all: hello.cpp hello
.PHONY: hello.cpp
hello.cpp:
#echo '#include <iostream>' > $#
#echo 'int main(int argc, char** argv)' >> $#
#echo '{ std::cout << "hello" << std::endl; return 0; }' >> $#
hello: hello.cpp
#./KV $#
#./KV $# compiler g++
#`./KV $# compiler` -o hello hello.cpp
#./$#
clean:
#rm -f hello.cpp hello
#./KV --clear

How to use eval Makefile variable from recipe to conditionally execute some commands in recipe

Goal is to apply patch ONLY if patch is not present. If patch is present don't do anything.
I used below makefile rule.
C_FILE_PATCH_SIG=###MAGIC_CODE;
C_FILE_CODE=~/code/file.c
C_PATCH_FILE=~/test.patch
.tmp/patch_c:
cp ${C_PATCH_FILE} ${SDK}
ifneq ($(PATCH_DONE), 1)
$(MAKE) applypatch || $(MAKE) helppatch
endif
#echo DONE > .tmp/patch_c
applypatch:
#echo "Patching ${C_FILE_CODE}"
if grep -Fq '${C_FILE_PATCH_SIG}' ${C_FILE_CODE} ; \
then \
echo 1 > .tmp/PATCH_PRESENT_file; \
else \
echo 0 > .tmp/PATCH_PRESENT_file;\
fi
cat .tmp/PATCH_PRESENT_file
# $(eval PATCH_PRESENT := `cat .tmp/PATCH_PRESENT_file`)
$(eval PATCH_PRESENT := $(shell cat .tmp/PATCH_PRESENT_file))
#echo "WWWWWW PATCH_PRESENT=[$(PATCH_PRESENT)] WWWWWWW"
ifeq ($(PATCH_PRESENT), 0)
#echo "Applying the patch $(PATCH_PRESENT)"
cd ~/code && git apply -v ${C_PATCH_FILE}
else
#echo "NOT Applying the patch $(PATCH_PRESENT)"
endif
helppatch:
#echo -e "\n\n\n"
#echo -e "++++++++++ Apply below patch manually then run 'make build PATCH_DONE=1' ++++++++++\n\n"
#echo -e "+++++++++++++++++++++++++++++++++++++"
#cat ${C_PATCH_FILE}
#echo -e "+++++++++++++++++++++++++++++++++++++"
#echo -e "\n\n\n"
false
But it always evaluates to the else part of ifeq.
Where am I doing wrong?
If I use the patch command of git withing the shell multiline I loose the error code returned by the git patch.
Thanks in advance...
Your ifeq will be evaluated when the makefile is first read (as opposed to when the recipe is run). The eval, on the other hand, will not be executed until the recipe is run (afterwards). Thus, PATCH_PRESENT is not equal to 0 at parse time, and make will expand the else portion of the clause. By the time the eval is run, the if statement is already evaluated and gone from memory.
BTW, a cleaner way to do this is to do everything in bash:
applypatch:
#echo "Patching ${C_FILE_CODE}"
#if grep -Fq '${C_FILE_PATCH_SIG}' ${C_FILE_CODE}; then \
echo "NOT Applying the patch"; \
else \
echo "Applying the patch"; \
cd ~/code && git apply -v ${C_PATCH_FILE}; \
fi

Use help2man in parallel automake

I'm working on a project built using GNU autotools (autoconf, automake). It does work well, but I've a problem with help2man: When building parallel (MAKEFLAGS=-j3), the project is built twice - once using "normal" target, once using the foo.1 make call.
The following is the relevant part from Makefile.am:
foo.1 : $(top_srcdir)/man/foo.x $(top_srcdir)/src/main.c $(top_srcdir)/configure.ac
$(MAKE) $(AM_MAKEFLAGS) foo$(EXEEXT)
-$(HELP2MAN) -o $# --include $< $(top_builddir)/foo
So, my question is how to write Makefile.am to support the following:
Distribute foo.1 to support systems without help2man
Do not throw errors
Rebuild manpage if necessary
I'm looking forward to your answer
foo.1 needs correct prerequisites. AIUI, help2man just needs the executable binary to be built:
foo.1 : $(top_srcdir)/man/foo.x $(top_srcdir)/configure.ac $(top_builddir)/foo
-$(HELP2MAN) -o $# --include $< $(top_builddir)/foo
so that's 3)
don't understand what you want out of 2), which is not possible in general.
dist_man_MANS = foo.1
which is 1)
There seems to be no easy solution to this question; the following works for me.
In configure.ac, you have to check for help2man. If cross-compiling, you must not run help2man, as the executable will be run. The following snippet is therefore contained:
# Man pages
AS_IF([test "$cross_compiling" = "no"], [
AM_MISSING_PROG([HELP2MAN], [help2man])
], [
HELP2MAN=:
])
For building, there is a two-level concept. First, you check if the manpage is newer than the executable; if so, to prohibit unnecessary manpage rebuilds, you check against the source using a temporary file being last altered when the manpage itself was. So, Makefile.am contains:
dist_man_MANS = foo.1
EXTRA_DIST += $(dist_man_MANS:.1=.x) common.x
MOSTLYCLEANFILES += $(dist_man_MANS:=-t)
MAINTAINERCLEANFILES += $(dist_man_MANS)
common_dep = $(top_srcdir)/common.x $(top_srcdir)/configure.ac $(top_srcdir)/.version
common_indirect_dep = $(top_srcdir)/common.x $(top_srcdir)/configure $(top_srcdir)/.version
foo.1 : $(common_indirect_dep) $(top_builddir)/foo$(EXEEXT)
foo.1-t : $(common_dep) $(top_srcdir)/main-helpversion.c
SUFFIXES += .x .1 .1-t
.x.1:
test -f $# || if test -f $(top_srcdir)/`echo $# | $(SED) -e 's,.*/,,'`; then \
touch -r $(top_srcdir)/`echo $# | $(SED) -e 's,.*/,,'` $#; \
else \
touch -d #0 $#; \
fi
touch -r $# $#-t
$(MAKE) $(AM_MAKEFLAGS) $#-t
if test -s $#-t; then \
mv -f $#-t $#; \
else \
rm -f $#-t; \
if test -s $#; then touch $#; else rm -f $#; fi; \
fi
.x.1-t:
$(HELP2MAN) -o $# -I $< -I $(top_srcdir)/common.x -s 1 foo$(EXEEXT)

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.

What's the difference between $# and $1 when there is only one parameter?

There are some C code:
apple.c
#include<stdio.h>
int main(void)
{
printf("apple\n");
return 0;
}
Makefile
apple:
gcc -c $#.c
gcc $#.o -o $#
$ make apple
and it works perfectly. But if I modify Makefile as:
apple:
gcc -c $1.c
gcc $1.o -o $1
$ make apple
It does not work. What is the difference between $# and $1 when there is only one parameter?
In a shell script, there'd be no difference. But this is a Makefile, so these references are to make variables. $# is the name of the rule target (apple here), while $1 is a variable named 1—nothing special. Bash does not see these variable references; they're handled by make.
$ cat Makefile
1 = one
target:
#echo '# = $#'
#echo '1 = $1'
$ make
# = target
1 = one

Resources