$(eval ) in makefile causing recipe commences before first target error - makefile

CFormat:
define Format_File :=
#echo Formatting
ifneq ("$(wildcard $(1))","")
#echo if1
# The default extensions for intermediate files are not used,
# to avoid collisions with backup files open in the editor, etc.
# Save the original source file with an '_X' suffix on the extension.
ifneq("$(wildcard $(1)_X)","")
#echo if2
else
#echo else2
endif
#ren $(1) $(1)_X
# C-Comment is invoked separately, due to pathing limitations
# The redirection is a means to suppress blabbering.
#echo Formatting $(1) . . .
$(CFORMAT_PATH)\Cformat -h$(CFORMAT_PATH) $(1)_X -o$[Base, $(1)].tmp -ino >temp.tmp;
$(CFORMAT_PATH)\Ccomment -h$(CFORMAT_PATH) $[Base, $(1)].tmp -o$(1) >temp.tmp;
else
#echo else1
endif
endef
FormatAll: CFormat
$(foreach loopFile,$(ALL_S_SOURCES),$(eval $(call Format_File,$(loopFile))))
.PHONY: FormatAll
When I replaced eval with info it printed out the function call correctly but every time I try to actually eval the formatter it gives me the error in the title.
Edit: This question was plagued with syntax errors everywhere but following the advice of #MadScientist I was eventually able to get it to work using shell loops.

The shortest answer is, you can't do it that way. A single function like $(foreach ...), or a single variable expansion, can never expand to multiple logical lines in a makefile. That's just not how the make parser works.
Further, $(eval ...) can be used to construct a complete rule but you can't use it to build just part of a rule: before make starts to parse the output of the evaluation it will "close" any rule that's currently being defined (just like you can't put the introduction of a rule in one file and the recipe in another file and use include to include the recipe).
You haven't really explained why you want to do things in just this way. One simple answer is to use shell loops, not makefile loops, like this:
FormatAll: CFormat
#for f in $(ALL_S_SOURCES); do \
echo Formatting; \
if [ -f $$f ]; then \
echo if1; \
if [ -f $${f}_X ]; then \
echo if2; \
else \
echo else2; \
fi; \
ren $$f $${f}_X; \
echo Formatting $$f . . .; \
$(CFORMAT_PATH)\Cformat -h$(CFORMAT_PATH) $F{f}_X -o$[Base, $$f].tmp -ino >temp.tmp; \
$(CFORMAT_PATH)\Ccomment -h$(CFORMAT_PATH) $[Base, $$f].tmp -o$$f >temp.tmp; \
else \
echo else1; \
fi; \
done
I agree with Etan that the $[Base ...] syntax is weird and certainly isn't right.
If you want more details about eval and debugging, you might check out this post and the earlier ones in the series.

The error message is pretty clear: the foreach loop spits out recipe commands before/outside of a target recipe definition.
You can try something like:
all:
$(foreach loopFile,$(ALL_S_SOURCES),$(eval $(call Format_File,$(loopFile))))
.PHONY: all

Related

How to check if a file with a certain extension exists using a makefile

I want to check whether exists files with a certain extension in a makefile, however this piece of code does not work:
ejecutar: $(OUTPUT) clean
ifeq (,$(wildcard *.dat))
./$(OUTPUT) < $(OUTPUT).dat >$(OUTPUT).txt
else
./$(OUTPUT) < $(OUTPUT).dat >$(OUTPUT).txt
The error is said to be in the ifeq line.
The stuff in the recipe should be shell script, not Makefile syntax. Anything that Make interprets gets expanded as the Makefile is being read, where you typically want your recipe to examine things as they are when that specific recipe is executed. (This is a common beginner FAQ.)
Checking whether a wildcard matches any files in shell script is surprisingly unobvious, too.
ejecutar: $(OUTPUT) clean
set -- *.dat \
; if [ -e "$$1" ]; then \
./$< < $<.dat >$<.txt; \
else \
./$< < $<.dat >$<.txt; \
fi
It's also weird that your then and else cases are identical, but I'm not judging.

Target's directory in prerequisites without second expansion

Lets assume, i want to call
make somepath/abc.pot
which depends on somepath/somefiles.c
My target I've created so far looks like
%.pot: $(dir $#)*.c
#echo "it works"
ifeq (,$(wildcard $#))
# pot-file does not exist, do something
else
# pot-file already exists, do something else
endif
but does not work as the Automatic Variables
like $# are not available in the prerequisites.
If found, that i can enable second expansion
.SECONDEXPANSION:
%.pot: $$(dir $$#)*.c
#echo "it works"
ifeq (,$(wildcard $#))
# pot-file does not exist, do something
else
# pot-file already exists, do something else
endif
which allows me to use $# in the prerequisites but breaks my ifeq statement which then always results in the first branch. If I change the ifeq to
ifeq (,$$(wildcard $$#))
it's working again but I really don't get why.
Now there a two questions:
A) Is there another way but to enable second expansion to have the path of the target in my prerequisites?
B) Why does the ifeq (,$(wildcard $#)) statement always result in the first branch if second expansion is enabled?
Don't use ifeq in the recipe at all. Just use normal shell functionality. It works better.
.SECONDEXPANSION:
%.pot: $$(dir $$#)*.c
#echo "it works"
if [ ! -f $# ]; then \
: pot-file does not exist, do something; \
else \
: pot-file already exists, do something else; \
fi
That said using wildcard in prerequisite lists is generally a bad idea because the time that they are globbed is not reliable and can cause odd behaviors. See Pitfalls of Using Wildcards for one example of a problem.
If you need to write different recipe contents based on some external factor (like OS) then you need to detect that at make parse time and have two copies of your recipes/makefile that you switch between correctly. You can do that inline but you can't do that per-recipe inline.
Your original attempts (using ifeq in a recipe) do not work. They don't do what you think they do. They may appear to work but they aren't working the way you expect.
Consider this makefile:
all: c
a:
#touch a
c: a
.SECONDEXPANSION:
c d:
ifeq (,$(wildcard a))
#echo "a doesn't exist (make)"
else
#echo 'a does exist (make)'
endif
#if [ ! -f a ]; then \
echo "a doesn't exist (sh)"; \
else \
echo 'a does exist (sh)'; \
fi
ifeq (,$$(wildcard a))
#echo "a doesn't exist (make se)"
else
#echo 'a does exist (make se)'
endif
In an empty directory you would expect make to output (assuming ifeq works the way you want it to):
a does exist (make)
a does exist (sh)
a does exist (make se)
Right? But it doesn't. You get:
a doesn't exist (make)
a does exist (sh)
a does exist (make se)
Ok, you think, that's just things not working without secondary expansion. But the secondary expansion version is working correctly. But it isn't.
If you run make d in an empty directory (note the d target doesn't list a as a prerequisite so it won't create it) you would expect the following output:
a doesn't exist (make)
a doesn' exist (sh)
a doesn' exist (make se)
Right? But what you actually get is:
a doesn't exist (make)
a doesn't exist (sh)
a does exist (make se)
So it appears that the secondary expansion version isn't working either.
A look at the make database explains why not.
Run make -qprR | awk '/c: a/,/^$/; /d:/,/^$/' in an empty directory and you see:
c: a
# Implicit rule search has not been done.
# File does not exist.
# File has been updated.
# Needs to be updated (-q is set).
# variable set hash-table stats:
# Load=0/32=0%, Rehash=0, Collisions=0/2=0%
# commands to execute (from `Makefile', line 12):
#echo "a doesn't exist (make)"
#if [ ! -f a ]; then \
echo "a doesn't exist (sh)"; \
else \
echo 'a does exist (sh)'; \
fi
#echo 'a does exist (make se)'
d:
# Implicit rule search has not been done.
# Modification time never checked.
# File has not been updated.
# commands to execute (from `Makefile', line 12):
#echo "a doesn't exist (make)"
#if [ ! -f a ]; then \
echo "a doesn't exist (sh)"; \
else \
echo 'a does exist (sh)'; \
fi
#echo 'a does exist (make se)'
Which, as you can see, doesn't contain the ifeq lines but just the "correct" branch of the ifeq logic.
And that's the problem, the ifeq conditionals are evaluated at make parse time which is well before any recipes run (and thus before any files can be created, etc.).

Makefile: recursive macro call running indefinitely

I'm trying to create a Makefile which creates a custom filesystem and my problem is to copy some packages inside it. I need to process a directory containing all the packages that I need to include, but each package may or may not contain a file associated describing its dependencies. Here is an excerpt of what I've tried to do:
PKGS_DIR := $(shell pwd)/packages
PKGS := $(wildcard $(PKGS_DIR)/*.pkg)
.PHONY: $(PKGS)
define getdeps
#if [ ! -f $(2)/$(1) ]; then \
echo $(1) >> $(BASE_DIR)/deplist.tmp; \
if [ -f $(PKGS_DIR)/$(1).dep ]; then \
depapps=`cat $(PKGS_DIR)/$(1).dep`; \
$(foreach depapp,$$depapps,\
$(call getdeps,$(depapp),$(BASE_DIR)/optional); \
) \
fi \
fi
endef
$(PKGS):
#echo processing $(notdir $#)
$(call getdeps,$(notdir $#),$(BASE_DIR)/optional)
With this code, my makefile crashes because it enters in an indefinitely loop and I'm not understanding why.
My question is: Is it possible to make a recursive call in a macro definition? Is there a better way to implement this? Perhaps it would be better using a shell script to implement this and just call it inside my makefile.
Any suggestion will be really appreciated.
Thanks in advance

Getting the name of the makefile from the makefile

How to get the name of the makefile in the makefile?
Thanks.
Note:
I would need that because I would like my makefile to call itself, but the makefile is not called Makefile, so I'd like to write something like this:
target:
($MAKE) -f ($MAKEFILENAME) other_target
location = $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
WHERE_ART_THOU := $(location)
$(warning $(WHERE_ART_THOU))
I also believe this is GNU make-specific, but I'm not too sure.
(Should you have any questions, refer to amazingly written GNU make manual. But remember, that, just like Makefile, this manual should be read completely before putting the concepts into practice).
I couldn't figure out how it is done easily. As far as I understand, you'll have to do some manual job.
Later I will describe how it could be done and show scripts that introduce current_makefile variable. But I would like to stress an important concept at the first place.
You should understand that if we had some kind of variable current_makefile, that expands to the current makefile name, then it will have to change during the process of reading makefiles. That means that it should be used withinin "immediate" expansion context -- i.e. within commands that are executed during reading the makefile. Most commands, however, are executed after makefiles are read. Therefore, some commands will print the correct value smoothly, while in certain places, where "deferred" expansion is used, it will always expand to the root makefile name.
If you would want to use this variable within rule text, for example, you'll have to do tricks, because rule text always has deferred expansion. So, if your have the rule
rule:
echo In makefile $(current_makefile):
echo Making target $#
it will always print the name of the root makefile. Instead, to force immediate expansion, you will have to create another variable with makefile-specific name (i.e. names of such variables should be different in each makefile):
this_makefile_unique_name := $(current_makefile)
rule:
echo In makefile $(current_makefile):
echo Making target $#
or use eval:.
define make_rule
rule:
echo In makefile $(1):
echo Making target $$#
$(eval $(call make_rule,$(current_makefile)))
If you want to use the name of current makefile for debug purpose only, consider special debugging functions, like warning or info:.
$(warning We're in makefile $(current_makefile))
These functions use "immediate" expansion and will print the correct value.
How to define such a $(current_makefile)?
You have to manually maintain stack of makefile inclusions. When you include a makefile, its name is placed to the top of the stack; when you return from included makefile to the outer one, the topmost name is popped out of stack. This is achieved by inserting special calls to the beginning and the end of makefile:
# Beginning of makefile
$(eval $(makefile_names_push))
#... makefile text
$(warning $(current_makefile))
#...
$(eval $(makefile_names_pop))
#End of file
Now define the functions at the beginning of your root makefile.
lastword=$(word $(words $(1)),$(1))
define makefile_names_push
current_makefile := $$(CURDIR)/$$(call lastword,$$(MAKEFILE_LIST))
makefile_stack :=$$(makefile_stack) $$(current_makefile)
endef
define makefile_names_pop
makefile_stack := $$(filter-out $$(current_makefile),$$(makefile_stack))
current_makefile := $$(call lastword,$$(makefile_stack))
endef
If you're sure your make is new enough (version 3.81+), replace lastword call with builtin function:.
#inctead of $$(call lastword,$$(MAKEFILE_LIST))
$$(lastword $$(MAKEFILE_LIST))
Is it useful?
Totally useless. An only use that might be useful here is to make 100 makefiles that are symlinks to one makefile, the rules in these makefiles depending on their names. But it can be achieved within one makefile and foreach-eval technique described in the manual. So my post was a complete waste of time, though I had some fun :-)
This returns the name of the first Makefile called, i.e. the one at the bottom of the call stack:
MAKEFILE_JUSTNAME := $(firstword $(MAKEFILE_LIST))
MAKEFILE_COMPLETE := $(CURDIR)/$(MAKEFILE_JUSTNAME)
When used in non-cross-recursive situations (e.g. for makedepend), it is just the name of the current makefile.
I wanted to do something similar (for echoing the contents of the Makefile) for when I use Make for managing simple repetitive tasks. I came across this page and found it was exactly what I was after and really useful for my limited understanding of make.
My result after reading this page:
# Makefile - 'make' and 'make help' now echo the makefile.
help:
cat $(lastword $(MAKEFILE_LIST))
start:
sudo -u www /path/to/webapp/myhttpd restart
stop:
sudo kill `cat /path/to/webapp/data/httpd.pid`
A quick excursion to Google suggests this site has the answer.
G'day,
If you make a copy of your original makefile, say makefile_test, and then enter the command:
make -np -f makefile_test 2>&1 | tee output
That will evaluate the makefile and your make environment but not execute any of the commands. Looking through the output file for references to makefile_test will show you what is set in make's environment and where that value is being set.
N.B. This can generate a lot of info! And don't add the -d (debug) switch which will generate tons of additional output about make's decision process but minimal additional info about make's env.
HTH
The solutions here addresses 1) POSIX make with 2) Invoked, non included, makefile in 3) A Unix alike platform.
What the OP asked for:
target:
#pid=$$$$; \
while test `ps -ocomm= $$pid` != make; do \
pid=`ps -oppid= $$pid`; \
done; \
MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
export MAKEFILENAME; \
$(MAKE) -e -f $$MAKEFILENAME other_target
The targets depends on the makefile, kind of bloated:
TARGET1_MAKEFILENAME = target1_preamble
all: target1 target2...
target1: $(TARGET1_MAKEFILENAME) other_dependencies...
#test $(TARGET1_MAKEFILENAME) == target1_preamble && exit 0; \
built_instructions_for_target1;
target1_preamble:
#pid=$$$$; \
while test `ps -ocomm= $$pid` != make; do \
pid=`ps -oppid= $$pid`; \
done; \
MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
export MAKEFILENAME; \
$(MAKE) -e -f $$MAKEFILENAME target1;
Can be a bit simplified if make is invoked only for all targets.
MAKEFILENAME = invoked_makefile_placeholder
all: target1 target2...
target1: $(MAKEFILENAME) other_dependencies...
#test $(MAKEFILENAME) == invoked_makefile_placeholder && exit 0; \
built_instructions_for_target1;
invoked_makefile_placeholder:
#pid=$$$$; \
while test `ps -ocomm= $$pid` != make; do \
pid=`ps -oppid= $$pid`; \
done; \
MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
export MAKEFILENAME; \
$(MAKE) -e -f $$MAKEFILENAME
With the previous approach is trivial to implement a solution for included makefiles based in grep and a unique pattern contained in the makefile.
I never answer when I feel the question got a proper solution.

Functions in Makefile

I am writing a Makefile with a lot of repetitive stuff, e.g.
debug_ifort_Linux:
if [ $(UNAME) = Linux ]; then \
$(MAKE) FC=ifort FFLAGS=$(difort) PETSC_FFLAGS="..." \
TARGET=$# LEXT="ifort_$(UNAME)" -e syst; \
else \
echo $(err_arch); \
exit 1; \
fi
where the target 'syst' is defined, the variable 'UNAME' is defined (and is usually Linux, but might also by Cygwin or OSF1) and the variables 'difort' and 'err_arch' are also defined. This block of code is used very many times for different compiler targets (using a name convention of ''). Since this is a huge amount of redundant code, I would like to be able to write it in a more simple manner. E.g., I would like to do something like this:
debug_ifort_Linux:
compile(uname,compiler,flags,petsc_flags,target,lext)
where compile could be a function doing the code above based on the arguments. Does anyone have any idea how I could accomplish this?
There are 3 related concepts:
call function
multi-line variables
conditionals
The refactored result could look like this:
ifeq ($(UNAME),Linux)
compile = $(MAKE) FC=$(1) FFLAGS=$(2) PETSC_FFLAGS=$(3) \
TARGET=$# LEXT="$(1)_$(UNAME)" -e syst
else
define compile =
echo $(err_arch)
exit 1
endef
endif
debug_ifort:
$(call compile,ifort,$(difort),"...")
That one \ that is left is to continue the $(MAKE) line for the shell.
No multi-line variable is necessary here, because it is just one line of shell code.
Multi-line variables are only used in the else block.
If you don't need parameters you can use := assignment and just expand the method with $(compile) (see canned recipes)
Note: Using make prior to version 3.82, the = was not recognized at the end of the define statement for me. I fixed this by using define compile instead.
You're looking for the call function.
compile = \
if [ $(UNAME) = $(1) ]; then \
$(MAKE) FC=$(2) FFLAGS=$(3) PETSC_FFLAGS="..." \
TARGET=$# LEXT="$(4)_$(UNAME)" -e syst; \
else \
echo $(err_arch); \
exit 1; \
fi
debug_ifort_Linux:
$(call compile,Linux,ifort,$(difort),ifort)
If you can restructure your Makefile a bit, though, you should see if you can use make's conditionals instead of sh's.

Resources