gmake ifeq comparison does not give desired result - compilation

I have a somewhat complex makefile that I want to change and I don't know much about make.
BUILD_TYPE = SERVER
BAS_CSRC = a.c \
b.c \
c.c
What I want to do is conditionally add things to BAS_CSRC like so:
ifeq ($(BUILD_TYPE), SERVER)
USR_CSRC = $(BAS_CSRC) \
d.c \
e.c
endif
all_csrc = $(USR_CSRC) $(foreach var, $(COMMON_OBJECTS), $($(var)_csrc))
But when I compile the d.c and e.c are just ignored so the ifeq fails. Why? What about quoting?

Based on what you've typed here it should work (and I cut and pasted this into a test makefile which worked for me), which likely means there's something different about your real environment, than this example. Can you show us where you use the variable all_csrc? Maybe it's that that's the problem, not the assignment of the variable.
Make sure you don't have any trailing whitespace. Make sure you have matching case in your variables and values (make, like all UNIX tools, is case-sensitive). You can try adding $(info ...) statements to your makefile and it will print out what it's doing. Put one inside the ifeq to see if it fires, and after the endif to see what the value of USR_CSRC is.
Also, in general it's not a good idea to add whitespace into if statements or function calls like foreach. In the above situations it shouldn't matter but in general it's best avoided.

Related

Using $(call ....) in a makefile recipe

So I have a really nice make macro that creates me a pretty message during compilation that is done in such a way that the destination of the message can be implemented externally.
It uses the $(shell ) make function something like this.
send_msg = $(shell $do_send_msg "$1")
Where do_send_msg can be expanded to be echo, wall, email, etc.
I use this lots in the makefiles.
I'd like to be able to use the same macro in recipes as the result of the recipe may change what is needed to be sent, dependent on some test made to the result of an external program.
However variables in recipes are expanded by make entirely before the lines in the recipe are called one at a time.
So if I write (e.g.)
if test_prog; then \
$(call send_msg,PASS);\
else \
$(call send_msg,FAIL);\
fi
Effectively this becomes (not strictly true syntax, but close enough):
(bash -c 'echo "PASS"') &
(bash -c 'echo "FAIL"') &
(bash -c 'if test_prog; then \
\
else \
\
fi)'
This of course will not work, it will run test_prog and, separately, regardless of the exit status both send_msg lines will expand and be executed.
So it will print both PASS and FAIL as the two subshells are run independently and in parallel by make.
I don't want to 'cheat' and use another variable in the $(call ) function or even worse a global that, ok, would allow an if some_var ... in the send_msg implementation but would reduce it's flexability as the implementation would have to understand that variable in all cases.
Another way would be to just have two different send_msg macros, one with and one without the $(shell ) function. Simple, but not elegant.
Right now I am using a 'hack' and calling the same makefile with a variable. If that variable is set then it sends it's contents otherwise does nothing. This works fine but to me it seems clunky and wrong, there must be a better way.....
e.g.
if test_prog; then \
MESSAGE=PASS $(MAKE) message;\
else \
MESSAGE=FAIL $(MAKE) message;\
fi
Where the Makefile says (and this is an abreviated version to convey the idea)
ifneq ($(MESSAGE),)
message:
$(call send_msg,$(MESSAGE))
else:
message:
endif
Question:
How would I make (make) detect if the macro is being expanded inside a Makefile recipe or inside the Makefile body and effectively keep or remove the $(shell ) call that wraps how the work is done?
e.g. (if make_or_shell existed)
ifdef some_test
$(make_or_shell send_msg,"Message from Make")
endif
goal:
$(make_or_shell send_msg,"Message from Recipe")

How can I add a directory to the search path of GNU Make?

I have a makefile that looks something like this:
include anotherFile.mk
all:
someStuff
The file anotherFile.mk is like this:
include yetAnotherFile.mk
export SOME_VAR = 93
The problem is that anotherFile.mk and yetAnotherFile.mk are in a different directory from my Makefile. So my makefile can't just be changed to this:
include $(OTHER_PROJECT_PATH)/anotherFile.mk
all:
someStuff
The problem with this approach is that the include statement in anotherFile.mk will fail because it will be searching in the current directory.
A partial solution that I found is to pass the --include-dir=$OTHER_PROJECT_PATH flag to the invocation of make, but that's a bit user-unfriendly.
So my question is: Is there something I can put inside my makefile that will add to the directories that make searches for when executing an include? Something like MAKE_INCLUDE_DIRS += $(OTHER_PROJECT_PATH)
Surprisingly there doesn't seem to be a good answer to that question. Forcing .INCLUDE_DIR doesn't help and there doesn't seem to be any way around invoking make with --include-dir=$OTHER_PROJECT_PATH.
It is however possible to put the appropriate recursive make invocation inside the makefile but, in order to get it to work for all reasonable cases it quickly becomes too complicated to be worth it. In summary it requires:
a top level condition to check if the OTHER_PROJECT_PATH is in .INCLUDE_DIR
the appropriate target with the recipe invoking make recursively
possibly additional targets if there are multiple command goals
the real make file enclosed in the else part of the conditional
You Makefile would look like this:
OTHER_PROJECT_PATH := other
ifeq (,$(filter $(OTHER_PROJECT_PATH), $(.INCLUDE_DIRS)))
# this is the mechanism to add the include dir in a recursive make
$(or $(firstword $(MAKECMDGOALS)),all):
$(MAKE) -I$(OTHER_PROJECT_PATH) $(MAKECMDGOALS)
# add empty targets for additional goals if needed
ifneq (,$(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)))
$(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)):
endif
else
# this is where the real makefile starts
all more:
echo $#: $< $^
include a.mak
endif
It still does not seem possible from a makefile, but if you have a script that sets up environment variables, you can use MAKEFLAGS (e.g. export MAKEFLAGS=I/your/path ordentlich on Linux, or SET on Windows)

Makefile says target is empty but it shouldn't be

I am trying to have a Makefile which is able to use different instances of g++ based on whether I give along a certain target or not. So: If I run make home I want CC to be the g++ executable in /usr/bin, and otherwise in some long path <longpath>/bin.
So I tried checking for my target:
ifeq ("$(TARGET)", "home")
GCCPATH = /usr
HSPARG = home
endif
$(info "$(TARGET)")
$(info "$#")
GCCPATH ?= <longpath>
CC = $(GCCPATH)/bin/g++
GCCLIBPATH = $(GCCPATH)/lib64
However, the outcome of this is:
$ make home
""
""
<further build information>
and GCCPATH is in all occasions equal to <longpath>.
Now my questions are:
1. What do I do wrong?
2. How to fix it?
First of all, make home doesn't set TARGET to home. So you have to execute make TARGET='home' to set it.
Secondly, make cares about spaces, including spaces after commas. So when you wrote ifeq ("$(TARGET)", "home"), make didn't toss away the space after the comma like you might have expected. So what make ended up comparing was "home" and " home".
You can see this by running make TARGET=' home' and seeing what you get. Remove that space and you'll fix the problem.
That said all your quoting isn't doing anything for make either. It doesn't generally care. Quotes are just literal characters to make in almost all places (except in ifeq "arg1" "arg2" cases, etc. and maybe one or two other places but I can't think of any offhand), so you don't need them in the calls to $(info) or even in your ifeq test since you are using the ifeq (arg1,arg2) version.

Programmatically selecting a sub-makefile to include when running make

I have the following logic in a Makefile:
ifdef INCLUDE_FILE
$(shell cp $(INCLUDE_FILE) include.make)
else
$(shell cp -n default.make include.make)
endif
include include.make
The intended behavior is:
If one just runs make and include.make exists, include.make is included. Otherwise, a default file is copied to include.make and then included.
If one runs make INCLUDE_FILE=myinclude.make, then myinclude.make is copied and included.
This seems to work fine to allow makefile customizations (compiler flags, etc) in include.make, which will persist if one does something like
$make INCLUDE_FILE=myinclude.make
$...
$make
but also allow a new user to simply type make and see default behavior.
My questions are
Is this good/standard (gnu) makefile practice?
Are there any serious portability concerns? [That is, is relying on cp in this way dangerous?]
Is there a better alternative method to implement similar behavior?
If the intention of this is to persist then I think this idea is reasonable though I wouldn't implement it this way.
I'd probably do something like this instead.
include include.mk
include.mk:
#cp $(or $(INCLUDE_FILE),default.mk) $#
Assuming you want the copy to only happen once (unless include.mk is manually deleted). There are other ways this could be done to handle copying again (if default.mk changes, etc. but those require more information about your goal).
The simplest (albeit hack) way to get make INCLUDE_FILE=myinclude.mk to always copy over include.mk is likely to add something like the following to the above makefile snippet.
ifdef INCLUDE_FILE
.PHONY: $(INCLUDE_FILE)
include.mk: $(INCLUDE_FILE)
endif
Though this does copy the file multiple times. You could add a check on $(MAKE_RESTARTS) also to avoid that.
include include.mk
include.mk:
cp $(or $(INCLUDE_FILE),default.mk) $#
ifdef INCLUDE_FILE
ifndef MAKE_RESTARTS
.PHONY: $(INCLUDE_FILE)
include.mk: $(INCLUDE_FILE)
endif
endif
This is slightly abusive of the behaviour of a normal target when it specifies a .PHONY target as a prerequisite but it seems to work and is, I believe, only depending on documented behaviour.
Why don't you just use:
INCLUDE_FILE := $(firstword $(wildcard include.make default.make))
include $(INCLUDE_FILE)
? This will include either include.make, if it exists, or else default.make, or if you define INCLUDE_FILE on the command line it will override the setting in the makefile.

makefile is missing separator

Alright I am stuck on this and I have no idea what I am doing wrong. Everything was going great working on a more complicated makefile but then all of a sudden I got the "Missing separator" error. I was able to isolate it down to a very simple scenario:
test.mk
define push_dir
$(info ${1})
endef
define pop_dir
$(info ${1})
endef
define include_submake
$(call push_dir,${1})
$(call pop_dir,${1})
endef
Simple
include test.mk
INITIAL_SUBMAKE:= includeme.mk
$(call include_submake,${INITIAL_SUBMAKE})
process:
#echo Processed...
And the output:
C:\project>make -f Simple process
includeme.mk
includeme.mk
Simple:4: *** missing separator. Stop.
includeme.mk does not actually exist. I have no idea what is going wrong here I have tried a multitude of things. If I surround the call to include_submake in info like so:
$(info $(call include_submake,${INITIAL_SUBMAKE}))
The missing separator error goes away. Also If in the include_submake define I only call one of the functions it works fine. Additionally if I directly call the functions instead of calling them include_submake it works as well:
include test.mk
INITIAL_SUBMAKE:= includeme.mk
$(call push_dir,${INITIAL_SUBMAKE})
$(call pop_dir,${INITIAL_SUBMAKE})
process:
#echo Processed...
C:\project>make -f Simple process
includeme.mk
includeme.mk
Processed...
I feel like I'm overlooking something fundamental here. Thanks for your help.
The missing separator error happens because of a non-empty return value of include_submake, which is a single line feed character in your case. Make only permits whitespace characters (that is, a space or tab) to occur in an expression which is not assumed to be a part of some rule or another directive.
Rewrite your functions using plain-old Make variable assignment and the error should go away:
push_dir = \
$(info $1)
pop_dir = \
$(info $1)
include_submake = \
$(call push_dir,$1) \
$(call pop_dir,$1)
UPD.: define vs plain old variable assignment
Answering to a question from the first comment. Personally I would prefer using define directive in several cases.
Using with eval function
As the GNU Make manual suggests, define directive is very useful in conjunction with the eval function. Example from the manual (emphasis is mine):
PROGRAMS = server client
server_OBJS = server.o server_priv.o server_access.o
server_LIBS = priv protocol
client_OBJS = client.o client_api.o client_mem.o
client_LIBS = protocol
# Everything after this is generic
.PHONY: all
all: $(PROGRAMS)
define PROGRAM_template
$(1): $$($(1)_OBJS) $$($(1)_LIBS:%=-l%)
ALL_OBJS += $$($(1)_OBJS)
endef
$(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))
$(PROGRAMS):
$(LINK.o) $^ $(LDLIBS) -o $#
clean:
rm -f $(ALL_OBJS) $(PROGRAMS)
Generator templates
Verbatim variables fit perfectly for cases when you want to generate a file from GNU Make. For example, consider generating a header file based on some information from Makefile.
# Args:
# 1. Header identifier.
define header_template
/* This file is generated by GNU Make $(MAKE_VERSION). */
#ifndef $(inclusion_guard)
#define $(inclusion_guard)
$(foreach inc,$($1.includes),
#include <$(inc).h>)
/* Something else... */
#endif /* $(inclusion_guard) */
endef
# 1. Unique header identifier.
inclusion_guard = \
__GEN_$1_H
# Shell escape.
sh_quote = \
'$(subst ','"'"',$1)'
foo.includes := bar baz
HEADERS := foo.h
$(HEADERS) : %.h :
#printf "%s" $(call sh_quote,$(call header_template,$(*F)))&gt $#
Extended Make syntax
In our project we use our own build system called Mybuild, and it is implemented entirely on top of GNU Make. As one of low-level hacks that we used to improve the poor syntax of the builtin language of Make, we have developed a special script which allows one to use extended syntax for function definitions. The script itself is written in Make too, so it is a sort of meta-programming in Make.
In particular, one can use such features as:
Defining multiline functions without the need to use backslash
Using comments inside functions (in plain-old Make comments can only occur outside variable assignment directives)
Defining custom macros like $(assert ...) or $(lambda ...)
Inlining simple functions like $(eq s1,s2) (string equality check)
This is an example of how a function can be written using the extended syntax. Note that it becomes a valid Make function and can be called as usual after a call to $(def_all).
# Reverses the specified list.
# 1. The list
# Return:
# The list with its elements in reverse order.
define reverse
# Start from the empty list.
$(fold ,$1,
# Prepend each new element ($2) to
# the result of previous computations.
$(lambda $2 $1))
endef
$(def_all)
Using these new features we were able to implement some really cool things (well, at least for Make :-) ) including:
Object-Oriented layer with dynamic object allocation, class inheritance, method invocations and so on
LALR parser runtime engine for parsers generated by GOLD Parser Builder
Modelling library with runtime support for models generated with EMF
Feel free to use any part of the code in your own projects!
I ran into the same problem. I inserted 'tab', deleted 'tab', reinserted to be sure. Same error message.
But, I did all of this inside of XCodem which to my surprise inserted white spaces, not '\t'. Once I used different editor these 'phantom' errors went away.
HTH...

Resources