Reputation: 102296
We have a library project and its transitioning from unaligned data access common in the old i386/i686/x86_64 to aligned data access. The impetus is GCC and vectorization, which is causing crashes at -O3
(GCC is even vectorizing string compares with SSE4 primitives).
If someone runs make and a particular define is not present, then we want to print a info
message warning them. I'm having trouble crafting the recipe.
The following recipe produces an error when run (it uses TAB, and not spaces):
.PHONY check_defines
check_defines:
CONFIG_ALIGNED := $(shell $(EGREP) -i -c "[[:space:]]*#[[:space:]]*define[[:space:]]*NO_UNALIGNED_DATA_ACCESS" config.h)
CXX_ALIGNED := $(shell echo $(value CXXFLAGS) | $(EGREP) -i -c "-DNO_UNALIGNED_DATA_ACCESS")
ifeq ($(CONFIG_ALIGNED)$(CXX_ALIGNED),00)
$(info Warning: NO_UNALIGNED_DATA_ACCESS is not defined)
endif
The error is:
$ make check_defines
GNUmakefile:383: *** missing separator. Stop.
Line 383 is the CONFIG_ALIGNED := ...
. The manual on 5.1 Recipe Syntax is not really helpful to me.
How do I craft the rule to perform the test within a recipe?
Upvotes: 0
Views: 879
Reputation: 2090
In the recipee, you need to put make's parser out of shell mode with any $(...)
expression.
.PHONY: check_defines
check_defines:
$(eval CONFIG_ALIGNED := $(shell echo 0))
$(eval CXX_ALIGNED := $(shell echo 0))
$(if $(filter 00,$(CONFIG_ALIGNED)$(CXX_ALIGNED)), \
$(warning Warning: NO_UNALIGNED_DATA_ACCESS is not defined) \
)
Note that the variables declared are not local to the rule, so you might as well not declare them and put the shell expressions directly in the if expression.
Upvotes: 3
Reputation: 171333
Makefiles are not parsed like a script and executed imperatively. A recipe is not a sub-routine.
The page you linked to explicitly tells you that the body of the recipe is treated as shell commands, not make declarations/expressions:
- A variable definition in a “rule context” which is indented by a tab as the first character on the line, will be considered part of a recipe, not a make variable definition, and passed to the shell.
- A conditional expression (ifdef, ifeq, etc. see Syntax of Conditionals) in a “rule context” which is indented by a tab as the first character on the line, will be considered part of a recipe and be passed to the shell.
So you can't declare make variables or use make conditionals. You can only use shell commands.
Try something like:
CONFIG_ALIGNED := $(shell $(EGREP) -i -c "[[:space:]]*#[[:space:]]*define[[:space:]]*NO_UNALIGNED_DATA_ACCESS" config.h)
CXX_ALIGNED := $(shell echo $(value CXXFLAGS) | $(EGREP) -i -c "-DNO_UNALIGNED_DATA_ACCESS")
ifeq ($(CONFIG_ALIGNED)$(CXX_ALIGNED),00)
CHECK_FAILED := 1
endif
.PHONY check_defines
check_defines:
if [ $(CHECK_FAILED) = 1 ]; then echo Warning: NO_UNALIGNED_DATA_ACCESS is not defined; exit 1; fi
Or check the condition using the shell:
CONFIG_ALIGNED := $(shell $(EGREP) -i -c "[[:space:]]*#[[:space:]]*define[[:space:]]*NO_UNALIGNED_DATA_ACCESS" config.h)
CXX_ALIGNED := $(shell echo $(value CXXFLAGS) | $(EGREP) -i -c "-DNO_UNALIGNED_DATA_ACCESS")
.PHONY check_defines
check_defines:
if [ $(CONFIG_ALIGNED)$(CXX_ALIGNED) = 00 ]; then echo Warning: NO_UNALIGNED_DATA_ACCESS is not defined; exit 1; fi
Or do it all with the shell, declaring shell variables instead of make variables:
.PHONY check_defines
check_defines:
config_aligned=`$(EGREP) -i -c "[[:space:]]*#[[:space:]]*define[[:space:]]*NO_UNALIGNED_DATA_ACCESS" config.h`
cxx_aligned=`echo $(value CXXFLAGS) | $(EGREP) -i -c "-DNO_UNALIGNED_DATA_ACCESS")`
if [ $${config_aligned}$${cxx_aligned} = 00 ]; then echo Warning: NO_UNALIGNED_DATA_ACCESS is not defined; exit 1; fi
(This version might not work if CXXFLAGS contains any shell-outs such as `pkg-config --cflags foo`
as that would confuse the shell)
Upvotes: 2