I am new to GNUmake and I need to check if a string does not ends with "FEATURE" in it.
ifeq (<check_if_a_string_doesnot_end_with_"FEATURE">)
....Do someting
endif
You could use patsubst to check if after removing everything that ends in FEATURE, you don't end up with the empty string:
ifneq ($(patsubst %FEATURE,,$(var)),)
So for a minimal Makefile like
.PHONY: checksuffix
checksuffix:
ifneq ($(patsubst %FEATURE,,$(var)),)
#echo "$(var) does not end with FEATURE"
else
#echo "$(var) does end with FEATURE"
endif
the behaviour would be
$ make checksuffix var=MYFEATURE
MYFEATURE does end with FEATURE
$ make checksuffix var=FEATUREABC
FEATUREABC does not end with FEATURE
Related
Here's the piece of my Makefile:
MY_CONFIG ?= .config.yaml
...
.PHONY: %/foo
%/foo: %/bar.yaml
ifneq ($(wildcard $*/$(MY_CONFIG)),)
$(info **FOUND CONFIG FILE** )
else
$(info **DIDNT FIND CONFIG FILE** )
endif
I call this target like make myfolder/foo and there's bar.yaml and .config.yaml files under that myfolder directory. However, that ifneq doesn't seem to work and it always prints DIDN'T FIND CONFIG FILE so I'm wondering how can I fix my condition (wildcard?) in ifneq to make it find a config file.
File structure:
-- Makefile
-- myfolder
---- bar.yaml
---- .config.yaml
The problem is that this kind of statement:
ifneq (...)
$(info ... )
else
$(info ... )
endif
is in Make syntax. It is not really part of the rule. Make will evaluate it before executing any rule. In this case:
ifneq ($(wildcard $*/$(MY_CONFIG)),)
...
before Make matches the pattern rule to anything, $* has no value and expands to nothing, so unless you have a config file in your root directory, the search will always come up negative.
There are a couple of ways to get the result you want. The simplest is probably to divide the rule into two rules:
.PHONY: %/foo
%/foo: %/bar.yaml %/$(MY_CONFIG)
#echo **FOUND CONFIG FILE**
%/foo: %/bar.yaml
#echo **DIDNT FIND CONFIG FILE**
Alternatively, you could put a shell conditional inside the rule, in the syntax of the shell you use.
Hello I have some debug target in my makefile.
How I can control what code will be inside this target?
I don't want to something after echo will be in target test, but now it is.
.PHONY: test
test:
#echo ${TYPE}
ifndef SOME
SOME=some123
endif
$ make test
typevalue
SOME=some123
I think I understand what you're asking.
A makefile rule lasts from the introduction of the target, up until the next line which is not part of a recipe. Blank lines, comment lines, and preprocessor lines like ifeq do not count as ending a recipe.
Other lines that begin with TAB characters are part of the recipe. Lines that don't begin with a TAB will end the recipe.
So, in your makefile above simply ensure that the assignment SOME=some123 does not start with a TAB character.
It's a constant thorn in my side when trying to read a makefile with nested logic, that Make does not allow indented if statements. Why is this, and is there a good way to work around this limitation, and still have readable makefiles?
Update: I now realise that this question is based on a false premise, but I believe that leaving it here may be valuable to anyone who makes the same mistake that I did.
Thanks to the help of others, I now realize that my question is posed on a false premise. Makefiles absolutely do allow for indented if statements, or rather indented conditionals to be more precise. What they don't allow for - at least out of the box - are tabbed conditionals. This is because, by default, Make interprets tabs as especially meaningful characters. Almost any line beginning with a tab character is interpreted to be part of a recipe. Therefore, any line that is not intended to be part of a recipe - such as conditionals - should not begin with a tab.
As far as answering the part of my question that asked why they chose to use the tab character in this way, I haven't found an answer. Perhaps the designers intended for conditionals to be used sparingly.
As for workarounds, here I will attempt to describe a couple.
The first solution is a terrible pain if you don't have an editor that shows whitespace characters, but if you do, the simplest thing to do might be to just add some spaces to indent your non recipe code. This is a rather hackish workaround though and probably ill advised.
Another solution (courtesy of #Stefan Becker) is to set the special variable, .RECIPEPREFIX to a character other than tab. Here's an example of what I tried:
.RECIPEPREFIX := >
# Now, all recipes will begin with the > character rather than a tab.
things = something another_thing something_else nothing
nothing = true
something: another_thing something_else
# See how each line of a recipe now begins with >.
# You can see I also added a tab after the >.
# These tabs doesn't mean anything to Make; it's just for readability.
> $(info Making $#.)
> #touch $#
another_thing:
> $(info Making $#.)
# See also how lines like comments can be tabbed,
# but no longer add anything meaningful to recipes.
> #touch $#
something_else:
> $(info Making $#.)
> #touch $#
# And just to prove the situation with conditionals is resolved...
# See how the #touch command begins with the new RECIPEPREFIX
# but the conditionals don't.
ifeq ($(nothing),true)
> $(info Also making nothing, because nothing is true.)
> #touch nothing
endif
.PHONY: everything_clean
everything_clean:
> $(info Cleaning up everything.)
> rm -f $(things)
One thing worth remembering is that recipe lines must begin with the new RECIPEPREFIX. That is to say, that something like this won't work:
something: another_thing something_else
# Remember that the RECIPEPREFIX must come first.
# Indenting your recipe lines first and then using the RECIPEPRIFX will not work.
>$(info Making $#.)
>#touch $#
I don't know why you are under the impression that indented conditionals aren't supported. They do seem to work fine when I use them in the following example:
.PHONY: all
all:
CONFIGS :=
ifeq ($(CONFIG1),1)
$(info CONFIG1 selected)
CONFIGS += config1
all: config1
config1:
#echo $#
ifeq ($(CONFIG2),1)
$(info CONFIG2 selected)
CONFIGS += config2
all: config2
config2:
#echo $#
else
$(info CONFIG2 not selected)
endif
else
$(info CONFIG1 NOT selected)
endif
all:
#echo "all: $(CONFIGS)"
NOTE: the TABS in my example will probably not survive copy & paste. So you'll have to re-enter them for the recipes.
Test run:
$ make
CONFIG1 NOT selected
all:
$ make CONFIG1=1
CONFIG1 selected
CONFIG2 not selected
config1
all: config1
$ make CONFIG1=1 CONFIG2=1
CONFIG1 selected
CONFIG2 selected
config1
config2
all: config1 config2
But...
There is one case where indentation can lead to problems. To quote the GNU make manual:
A recipe is an action that make carries out. A recipe may have more than one command, either on the same line or each on its own line. Please note: you need to put a tab character at the beginning of every recipe line! This is an obscurity that catches the unwary.
As GNU make takes all TAB indented lines after a rule to be part of the recipe for the rule the following will fail for make CONFIG1=1:
.PHONY: all
all:
CONFIGS :=
config1:
# TAB in the following line
#echo $#
# the following lines are indented with TABs
ifeq ($(CONFIG1),1)
CONFIGS += config1
test1:
#echo $#
endif
ifeq ($(CONFIG1),1)
all: config1
endif
all:
# TAB in the following line
#echo "all: $(CONFIGS)"
$ make CONFIG1=1
config1
ifeq (1,1)
/bin/sh: -c: line 0: syntax error near unexpected token `1,1'
/bin/sh: -c: line 0: `ifeq (1,1)'
make: *** [Makefile:9: config1] Error 1
Solution
organize the makefile to have conditionals first, then rules, i.e. no TAB indentation after rules anymore except for recipes, or
always make sure to use SPACEs for conditional, variable assignment and rule lines.
set .RECIPEPREFIX to a non-whitespace character, e.g. > and use that to indicate recipe lines.1
Unless you have an editor which shows the difference between TABs and SPACEs, alternative 2 will probably drive you insane. I would suggest alternative 1 instead...
The following works for make CONFIG2=1:
.PHONY: all
all:
CONFIGS :=
config2:
# TAB in the following line
#echo $#
# the following lines are indented with SPACES
ifeq ($(CONFIG2),1)
CONFIGS += config2
test2:
# 2 TABs in the following line
#echo $#
endif
ifeq ($(CONFIG2),1)
all: config2
endif
all:
# TAB in the following line
#echo "all: $(CONFIGS)"
$ make CONFIG2=1
config2
all: config2
1 you might be tempted to set .RECIPEPREFIX to SPACE like this:
_empty :=
_space := $(_empty) $(_empty)
.RECIPEPREFIX := $(_space)
and then switch your editor to use only SPACEs. But that makes things worse, i.e. now make can't distinguish between normal and recipe indentation. If you try this with the above example you will note that it now fails for any invocation that enables one of the indented rules.
I have a very simple makefile as follows
FRUIT = apple orange
all : $(FRUIT)
$(FRUIT) :
ifeq ($(#),apple)
#echo "APPLE!"
else
#echo "ORANGE!"
endif
When I execute
make all
I see
ORANGE!
ORANGE!
I have checked my tabbing and I believe my spacing on ifeq is correct. What have I missed?
The ifeq is evaluated when the Makefile is first parsed, not when the individual recipe is executed. At that point, $(#) is always the empty string.
You can perform the same logic in shell script in the recipe itelf.
$(FRUIT):
case $# in apple) echo "APPLE!";; *) echo "ORANGE!";; asac
Conditional directives are expanded as soon as make reads them, long before the recipe is executed. $#expands to the empty string when make parses that line so you always end up with the else part of the conditional.
Just use something like the following instead:
FRUIT = apple orange
all: $(FRUIT)
$(FRUIT):
#echo "$#!"
If you need separate recipes then write them
apple:
#echo "APPLE!"
orange:
#echo "ORANGE!"
I've written a fairly simple test Makefile where I define two targets, all & clean. I've got two different conditional statements. One checks for the existence of the $(MAKECMDGOALS) special variable and the other detects whether any of the command line targets matched those listed in a variable (NODEPS). The problem I'm having is that none of the branches within my conditionals get executed. Ultimately I want to use a conditional to decide whether the target I'm supplying should include some autogenerated dependency files but at the moment I'm struggling to get either expression to even evaluate. I'm running GNU make version 3.81 and I've tried it under Ubuntu and Mac OS X to no avail.
NODEPS := clean
INCLUDE = $(filter $(NODEPS),$(MAKECMDGOALS))
.PHONY : all clean
ifndef $(MAKECMDGOALS)
#echo "$$(MAKECMDGOALS) is not defined"
else
#echo "$(MAKECMDGOALS) is defined"
endif
ifneq (0, $(words $(INCLUDE)))
#echo "INCLUDE = $(INCLUDE) != 0"
else
#echo "INCLUDE = $(INCLUDE) == 0"
endif
all :
#echo "all : $(MAKECMDGOALS)"
clean :
#echo "clean : $(MAKECMDGOALS)"
I eventually managed to work out what was wrong. #eriktous was right, pointing out that I should be using $(info) rather than #echo. More subtly though, part of the problem was that I'd indented the #echos with a tab. It seems that tabs are mandatory for rules but not allowed in conditionals. The other mistake was I'd expanded the $(MAKECMDGOALS) variable in the test condition when it should have been written as just ifndef MAKECMDGOALS.
https://www.gnu.org/software/make/manual/html_node/Make-Control-Functions.html
A makefile is not a shell script. You can not "randomly" place executable statements anywhere you like and expect them to be executed.
There are various ways of communicating with the outside world from within a makefile: $(info ...), $(warning ...), $(error ...) and $(shell #echo ...) (some or all of these may be GNU make extensions).
Ps: you misspelled PHONY.