Log wrapper in makefile? - makefile

I m trying to write a simple log wrapper in makefile as shown below
define do_log
ifeq ($(1),1)
$(info ---------- $(shell date +%H:%M:%S) $(2))
else ifeq ($(1),2)
$(warning ^^^^^^^^^^ $(shell date +%H:%M:%S) $(2))
else ifeq ($(1),3)
$(error !!!!!!!!!! $(shell date +%H:%M:%S) $(2))
endif
endef
But every-time i call this function
$(call do_log,1,"hello")
All statements are getting executed
$ make
---------- 18:22:45 "hello"
Makefile:16: ^^^^^^^^^^ 18:22:45 "hello"
Makefile:16: *** !!!!!!!!!! 18:22:45 "hello". Stop.
Can someone please help me understand, on what am i doing wrong ?

ifeq etc. are more like preprocessor statements. They are only meaningful to make's makefile parser. They have no special meaning or significance to variable expansion etc., just like how you can't use C/C++ preprocessor statements at runtime.
The behavior you see is because call first expands the variable, and when that happens only variables are considered. This means that call sees something like this:
<text> $(1) <text>
$(info ---------- $(shell date +%H:%M:%S) $(2))
<text> $(1) <text>
$(warning ^^^^^^^^^^ $(shell date +%H:%M:%S) $(2))
<text> $(1) <text>
$(error !!!!!!!!!! $(shell date +%H:%M:%S) $(2))
<text>
and all those variable/function references are expanded. In general you cannot use ifeq etc. in a define variable unless you expect to use it with eval (which parses its string using the makefile parser).
Since you've not really shown us how you want to use this it's hard to suggest The best alternative, but to literally translate what you're doing here you could use:
define do_log
$(if $(filter-out 1,$(1)),,$(info ---------- $(shell date +%H:%M:%S) $(2)))
$(if $(filter-out 2,$(1)),,$(warning ^^^^^^^^^^ $(shell date +%H:%M:%S) $(2)))
$(if $(filter-out 3,$(1)),,$(error !!!!!!!!!! $(shell date +%H:%M:%S) $(2)))
endef

This can be achieved with eval :
define do_log
ifeq ($(1),1)
$$(info ---------- $(shell date +%H:%M:%S) $(2))
else ifeq ($(1),2)
$$(warning ^^^^^^^^^^ $(shell date +%H:%M:%S) $(2))
else ifeq ($(1),3)
$$(error !!!!!!!!!! $(shell date +%H:%M:%S) $(2))
endif
endef
$(eval $(call do_log,3,"hello"))

Related

In Makefiles, how to display percentage of compilation followed by compiler's warnings/errors

This is a bit tricky...
I want my Makefile to display progression percentage of my compilation. This is easy as long as I use tput or escaped sequences to move my cursor to modify my percentage.
But if a warning appears, the next warning will be on the previous one and so on...
How can I do something like that ?
I've already found some ideas like storing cursor position inside a variable, but I failed.
I've thought about redirect gcc's (or clang's idc) output to a variable but it failed too.
I can output the cursor position with : echo -en "\033[6n" ; read -sdR CURPOS ; CURPOS=$${CURPOS#*[} ; echo "$${CURPOS}" but I cannot use $(shell ..) because it just infinit loop (I guess) so $(eval VAR = $(shell echo ..)) is impossible.
Maybe I'm looking the problem at the wrong side ?
I'm really stuck tbh so thank for your help
EDIT:
224 all : $(TARGET)
225 if [[ ! '$(FILE_TODO)' == '0' ]]; then echo -e $(RPOS)$(ENDRASE)Done\n$(DEBUG_COLOR)[Linkage]$(NORMAL) - Done'; fi
226 $(ECHO) '$(DEBUG_MODE_MSG) : $(if $(ISXX),$(CXXFLAGS),$(CFLAGS))'
227
228 $(TARGET) : $(OBJFILES)
230 $(CC) $(OUTPUT_OPTION) $(LDFLAGS) $(LDLIBS) $(LINKAGE) $(if $(UNITARY),--coverage,) $^
231
232 display :
233 $(eval OBJ_NBR := $(shell if [[ -d '$(OBJDIR)' ]]; then find $(OBJDIR) -type f -name \*$(OBJEXT); fi))
234 $(eval OBJ_NBR := $(shell echo -n "$$(($(words $(OBJ_NBR))))"))
235 $(eval FILE_TODO := $(shell echo "$$(($(SRC_NBR)-$(OBJ_NBR)))"))
236 if [[ ! '$(FILE_TODO)' == '0' ]]; then echo -en '$(DEBUG_COLOR)[Compilation]$(NORMAL) - $(SPOS)'; fi
237
238 $(OBJDIR)/%$(OBJEXT) : %$(SRCEXT) | display
239 $(eval I := $(shell echo -n "$$(($(I)+1))"))
240 $(eval PERCENT := $(shell echo "$$((100*$(I)/$(FILE_TODO)))"))
241 $(ECHO) '$(RPOS)$(ENDRASE)$(PERCENT)% ($<)'
242 $(MKDIR) $(#D) $(patsubst $(OBJDIR)%,$(DEPDIR)%,$(#D))
243 $(CC) -c $(OUTPUT_OPTION) $(DEPFLAGS) $(if $(ISXX),$(CXXFLAGS),$(CFLAGS)) $(CPPFLAGS) $(WARNING) $(WNO) $(COMPILATION) $<
As an alternative to injecting rules, targets in Makefile, which have the the potential to have negative impact on the build process, consider a 'wrapper' tool that will work around the make program.
Query make for number of require build steps
Execute make, periodically checking for remaining steps, showing %-age.
In general, assuming the make process is simple (e.g., no recursion), executing 'make -n' will be fast, provide (relatively) accurate result, and will not have negative impact on the build process
Well, everything seems to be working even if it is dirty
238 count : $(OBJFILES)
239 $(ECHO) $(COUNTING)
240
241 display :
242 ifneq ($(MAKECMDGOALS), count)
243 $(eval FILE_TODO := $(shell $(MAKE) count))
244 if [[ ! '$(FILE_TODO)' == '' ]]; then echo -e '$(DEBUG_COLOR)[Compilation]$(NORMAL) - $(SPOS)'; fi
245 echo -en "\033[6n" ; read -sdR CURPOS ; echo $${CURPOS#*[} > .curs_pos.tmp
246 endif
247
248 CURPOS = $(shell cat .curs_pos.tmp)
249
250 $(OBJDIR)/%$(OBJEXT) : %$(SRCEXT) | display
251 ifneq ($(MAKECMDGOALS), count)
252 $(eval I := $(shell echo -n "$$(($(I)+1))"))
253 $(eval PERCENT := $(shell printf %.2f "$$((10000*$(I)-1)/($(FILE_TODO))))e-2"))
254 $(ECHO) -n '$(HIDECURS)$(RPOS)$(ENDRASE)$(PERCENT)% ($<)'
255 $(eval CURPOS = $(shell cat .curs_pos.tmp))
256 $(ECHO) -n "\033[$(CURPOS)H$(SHOWCURS)"
257 $(MKDIR) $(#D) $(patsubst $(OBJDIR)%,$(DEPDIR)%,$(#D))
258 $(CC) -c $(OUTPUT_OPTION) $(DEPFLAGS) $(if $(ISXX),$(CXXFLAGS),$(CFLAGS)) $(CPPFLAGS) $(WARNING) $(WNO) $(COMPILATION) $<
259 echo -en "\033[6n" ; read -sdR CURPOS ; echo $${CURPOS#*[} > .curs_pos.tmp
260 else
261 $(eval COUNTING = $(shell echo -n "$$(($(COUNTING)+1))"))
262 endif
Thank you, and I'm listening every comment...

What is wrong with this makefile function?

I am not proficient at makefiles. I have RTFM and looked on SO, but I still don't get what I'm doing wrong in this simple example.
This works:
TARGET = nim
ifeq ($(CONFIG),Release)
$(eval $(call LINK_RULE,$(TARGET)_unstripped,$(APP_SOURCES) Neonim.cc,$(LIBS),$(DEPS)))
$(eval $(call STRIP_RULE,$(TARGET),$(TARGET)_unstripped))
else
$(eval $(call LINK_RULE,$(TARGET),$(APP_SOURCES) Neonim.cc,$(LIBS),$(DEPS)))
endif
$(eval $(call NMF_RULE,$(TARGET),))
TARGET = ttt
ifeq ($(CONFIG),Release)
$(eval $(call LINK_RULE,$(TARGET)_unstripped,$(APP_SOURCES) TTToe3D.cc,$(LIBS),$(DEPS)))
$(eval $(call STRIP_RULE,$(TARGET),$(TARGET)_unstripped))
else
$(eval $(call LINK_RULE,$(TARGET),$(APP_SOURCES) TTToe3D.cc,$(LIBS),$(DEPS)))
endif
$(eval $(call NMF_RULE,$(TARGET),))
This doesn't work:
define bld =
SOURCES = $(APP_SOURCES) $(2)
ifeq ($(CONFIG),Release)
$(eval $(call LINK_RULE,$(1)_unstripped,$(SOURCES),$(LIBS),$(DEPS)))
$(eval $(call STRIP_RULE,$(1),$(1)_unstripped))
else
$(eval $(call LINK_RULE,$(1),$(SOURCES),$(LIBS),$(DEPS)))
endif
$(eval $(call NMF_RULE,$(1),))
endef
$(eval $(call bld,nim,Neonim.cc))
$(eval $(call bld,ttt,TTToe3D.cc))
Are they not equivalent? How do I write this function?
You haven't given us enough information to reproduce the error (and I really don't think your example is minimal), but I'll go out on a limb. Change this:
define bld =
SOURCES = $(APP_SOURCES) $(2)
ifeq ($(CONFIG),Release)
$(eval $(call LINK_RULE,$(1)_unstripped,$(SOURCES),$(LIBS),$(DEPS)))
$(eval $(call STRIP_RULE,$(1),$(1)_unstripped))
else
$(eval $(call LINK_RULE,$(1),$(SOURCES),$(LIBS),$(DEPS)))
endif
$(eval $(call NMF_RULE,$(1),))
endef
to this:
define bld
SOURCES = $(APP_SOURCES) $(2)
ifeq ($(CONFIG),Release)
$(call LINK_RULE,$(1)_unstripped,$(SOURCES),$(LIBS),$(DEPS))
$(call STRIP_RULE,$(1),$(1)_unstripped)
else
$(call LINK_RULE,$(1),$(SOURCES),$(LIBS),$(DEPS))
endif
$(call NMF_RULE,$(1),)
endef

makefile iteration and retrieving the filename

In a makefile, I am trying to iterate through c files and use the path as well as the filename.
For example for /dir/dir2/file.c
I want to execute "cc /dir/dir2/file.c -o file"
I do not understand why basename and patsubst do not work. It just shows me the path as is.
Can anyone please help?
test_files := Test/src/test_*.c
compile_tests:
#for f in $(test_filenames); do \
echo ">>> $(basename $(patsubst %.c, %, $$f ))";\
done
You cannot mix and match make functions with shell operations. Make will fully expand all the variables and functions first, then it passes the result of the expansion to the shell and the shell runs it as a script.
You are trying to use a make function inside a shell loop, but the make function is expanded first, then the loop will run on the results. The basename and patsubst run on the literal string $f, which doesn't have any pathname and does not match the %.c pattern, so those functions have no effect.
If you want to do it this way you must use 100% shell operations, or else modify the variable before the shell gets it like this:
test_filenames := $(wildcard Test/src/test_*.c)
compile_tests:
#for f in $(basename $(patsubst %.c,%,$(test_filenames))); do \
echo ">>> $$f";\
done
ETA: if you want to do it all in the shell, you can use:
test_filenames := $(wildcard Test/src/test_*.c)
compile_tests:
#for f in $(test_filenames); do \
echo ">>> $$(basename $$f .c)";\
done
Or, perhaps more clearly:
test_filenames := $(wildcard Test/src/test_*.c)
compile_tests:
#for f in $(test_filenames); do \
echo ">>> `basename $$f .c`";\
done

Makefile for input/output pairs

I have pairs of input/output files. I generate the name of the output file from a script: output=$(generate input). For example the pairs could be:
in1.c out1.o
in2.txt data.txt
in3 config.sh
sub/in1.c sub/out1.o
All those pairs obey the same set of rules in the makefile:
$(out): $(in) $(common)
$(run) $< > $#
What is a concise and efficient way to write such a Makefile?
I would rather avoid generating the Makefile from another script.
I wouldn't generate Makefile fragments from a script, but you could use an include:
INS := in1.c in2.txt in3 sub/in1.c
include rules.mk
rules.mk: Makefile
rm -f $#
for f in $(INS); do \
out=`generate "$$f"`; \
echo -e "$$out: $$f\n\t\$$(run) \$$<> > \$$#\n\n" >> $#; \
done
If you include a file gmake will try to generate and include it before any other targets. Combining this with a default rule should get you close to what you want
# makefile
gen=./generate.sh
source=a b c
run=echo
# Phony so the default rule doesn't match all
.PHONY:all
all:
# Update targets when makefile changes
targets.mk:makefile
rm -f $#
# Generate rules like $(target):$(source)
for s in $(source); do echo "$$($(gen) $$s):$$s" >> $#; done
# Generate rules like all:$(target)
for s in $(source); do echo "all:$$($(gen) $$s)" >> $#; done
-include targets.mk
# Default pattern match rule
%:
$(run) $< > $#
Testing with generate.sh like
#!/bin/bash
echo $1 | md5sum | awk '{print $1}'
give me
$ make
rm -f targets.mk
for s in a b c; do echo "$(./generate.sh $s):$s" >> targets.mk; done
for s in a b c; do echo "all:$(./generate.sh $s)" >> targets.mk; done
echo a > 60b725f10c9c85c70d97880dfe8191b3
echo b > 3b5d5c3712955042212316173ccf37be
echo c > 2cd6ee2c70b0bde53fbe6cac3c8b8bb1
What is a concise and efficient way to write such a Makefile?
It is possible given a list of inputs and a shell script that generates output file name to generate targets, dependencies and rules using GNU make features:
all :
inputs := in1.c in2.txt in3 sub/in1.c
outputs :=
define make_dependency
${1} : ${2}
outputs += ${1}
endef
# replace $(shell echo ${in}.out) with your $(shell generate ${in})
$(foreach in,${inputs},$(eval $(call make_dependency,$(shell echo ${in}.out),${in})))
# generic rule for all outputs, and the common dependency
# replace "echo ..." with a real rule
${outputs} : % : ${common}
#echo "making $# from $<"
all : ${outputs}
.PHONY : all
Output:
$ make
making in1.c.out from in1.c
making in2.txt.out from in2.txt
making in3.out from in3
making sub/in1.c.out from sub/in1.c
In the above makefile one little used by powerful GNU make construct is used: $(eval $(call ...)). It requests make to expand the macro to produce a piece of text and then evaluate that piece of text as a piece of makefile, i.e. make generates makefile of the fly.

Makefile If-Then Else and Loops

Can someone explain how to use if-then statements and for loops in Makefiles? I can't seem to find any good documentation with examples.
Conditional Forms
Simple
conditional-directive
text-if-true
endif
Moderately Complex
conditional-directive
text-if-true
else
text-if-false
endif
More Complex
conditional-directive
text-if-one-is-true
else
conditional-directive
text-if-true
else
text-if-false
endif
endif
Conditional Directives
If Equal Syntax
ifeq (arg1, arg2)
ifeq 'arg1' 'arg2'
ifeq "arg1" "arg2"
ifeq "arg1" 'arg2'
ifeq 'arg1' "arg2"
If Not Equal Syntax
ifneq (arg1, arg2)
ifneq 'arg1' 'arg2'
ifneq "arg1" "arg2"
ifneq "arg1" 'arg2'
ifneq 'arg1' "arg2"
If Defined Syntax
ifdef variable-name
If Not Defined Syntax
ifndef variable-name
foreach Function
foreach Function Syntax
$(foreach var, list, text)
foreach Semantics
For each whitespace separated word in "list", the variable named by "var" is set to that word and text is executed.
Here's an example if:
ifeq ($(strip $(OS)),Linux)
PYTHON = /usr/bin/python
FIND = /usr/bin/find
endif
Note that this comes with a word of warning that different versions of Make have slightly different syntax, none of which seems to be documented very well.
Have you tried the GNU make documentation? It has a whole section about conditionals with examples.
You do see for loops alot of the time, but they are usually not needed. Here is an example of how one might perform a for loop without resorting to the shell
LIST_OF_THINGS_TO_DO = do_this do_that
$(LIST_OF_THINGS_TO_DO):
run $# > $#.out
SUBDIRS = snafu fubar
$(SUBDIRS):
cd $# && $(MAKE)

Resources