makefile: commands commence before first target - makefile

I have a rule like below just for test
test:
ifeq (1,1)
$(info --)
endif
echo kkk
when I run make test, it shows
makefile:41: *** commands commence before first target. Stop.
What is wrong?
edit
according to Florian Weimer, we should indent the $(info --) line. But If I write
test:
ifeq (1,1)
$(info --)
endif
echo kkk
test2:
ifeq (1,1)
$(info --)
endif
echo kkk
then make test will complain that
makefile:11: *** commands commence before first target. Stop.
So doesn't make always scan the whole makefile? It seems that it doesn't stop after rule test is finished

You need to tab-indent the $(info --) line, so that it does not terminate the recipe, like this:
test:
ifeq (1,1)
$(info --)
endif
echo kkk
(Obviously, you need to use tabs here.)
EDIT The make documentation of conditional blocks consistently uses tab indentation within recipes, and spaces or no indentation outside of recipes. This is why there are both styles.
The full example works for me if I indent both $(info --) lines.

Related

conditional inside a Makefile target

This a Makefile:
all:
ifeq (0,0)
echo hello
endif
Note: there is a tab before if, echo, and endif.
Giving make I get:
ifeq (0,0)
/bin/sh: -c: line 0: syntax error near unexpected token `0,0'
/bin/sh: -c: line 0: `ifeq (0,0)'
make: *** [Makefile:2: all] Error 1
Why is it so?
The trouble is that each line in a recipe runs in its own subshell, so the entire conditional must be in one line or it won't work. I'm not sure which shell you're using, but I presume you've tested this conditional on the command line and it works:
ifeq (0,0)
echo hello
endif
Without the right shell I can't test it, but I think you could also do something like this:
ifeq (0,0); echo hello; endif
If so, then it will also work in the makefile:
all:
ifeq (0,0); echo hello; endif
The lines can then be wrapped using backslashes:
all:
ifeq (0,0);\
echo hello;\
endif
(Note that the only TAB needed is before the ifeq.)

How ifeq from Makefile works?

I have a Makefile with content:
define SOME_FUNC
ifeq (n,y)
$(warning TRUE)
else
$(warning FALSE)
endif
endef
.PHONY: all
all:
$(eval $(call SOME_FUNC))
After executing "make" command I've got following output:
$ make
Makefile:10: TRUE
Makefile:10: FALSE
make: Nothing to be done for 'all'.
I cannot explain why it happens.
From the documentation:
It’s important to realize that the eval argument is expanded twice; first by the eval function, then the results of that expansion are expanded again when they are parsed as makefile syntax. This means you may need to provide extra levels of escaping for “$” characters when using eval.
You need to double the dollars to have those $(warning ...) functions evaluated on interpreting ifeq instead of expanding eval/call:
$ cat Makefile
define SOME_FUNC
ifeq (n,y)
$$(warning TRUE)
else
$$(warning FALSE)
endif
endef
.PHONY: all
all:
$(eval $(call SOME_FUNC))
$ make
Makefile:11: FALSE
make: Nothing to be done for 'all'.

How to print text in a makefile outside a target?

For example, I am trying to test whether this works in my makefile preamble:
ifneq (,$(shell latexmk --version 2>/dev/null))
echo Works
else
echo Does not Works
endif
all:
do things...
Which does the error:
*** recipe commences before first target. Stop.
Then, how to prints things outside rules?
Makefile does not allow commands outside rules, or outside result:=$(shell ...).
In GNU Make there are $(info ...), $(warning ...) and $(error ...) built-in functions. Note that syntactically they are text substitutions, yet their return value is always an empty string (except $(error ...) which never returns), as it's with $(eval ...) etc. So they could be used almost everywhere.
Yet another option is $(file >/dev/stdout,...) (under Windows use "con").
After I found this question, https://unix.stackexchange.com/questions/464754/how-to-see-from-which-file-descriptor-output-is-coming
I think this kinda works:
ifneq (,$(shell latexmk --version 2>/dev/null))
useless := $(shell echo Works 1>&2)
else
useless := $(shell echo Does not Works 1>&2)
useless := $(error exiting...)
endif
all:
echo Hey sister, do you still believe in love I wonder...
Bonus:
Can I make a makefile abort outside of a rule?

Stop executing makefile

I implement a recipe in order to pass all the remaining string to the command, as example in this script:
Makefile
run:
# ./bin/run.sh $(filter-out $#,$(MAKECMDGOALS))
#echo $(filter-out $#,$(MAKECMDGOALS))
But when I run as example:
>make run my custom input params
my custom input params
make: *** No rule to make target `my'. Stop.
makefile try to execute also the remaining string so the error:
make: *** No rule to make target `my'. Stop.
How can I prevent this?
NB: As workaround I define a dummy recipe:
%:
#echo
So this will print an empty string instead of the error.
I want to avoid to do something like:
make run-example param="my custom param"
You can probably achieve what you want with a match-anything rule. Example (using a dummy printf recipe instead of a real one):
PARAMS := $(filter-out run,$(MAKECMDGOALS))
run:
#printf './bin/run.sh $(PARAMS)\n'
%:;
Demo:
$ make run my custom input params
./bin/run.sh my custom input params
make: 'my' is up to date.
make: 'custom' is up to date.
make: 'input' is up to date.
make: 'params' is up to date.
You can ignore the make: 'target' is up to date. messages or use the --quiet option (or --silent or -s):
$ make --quiet run my custom input params
./bin/run.sh my custom input params
If your Makefile is more complex than this, the match-anything rule could be a problem because it could catch other targets that you do not want to be caught. In this case make conditionals are an option:
ifeq ($(SECONDPASS),)
PARAMS := $(filter-out run,$(MAKECMDGOALS))
run:
#$(MAKE) --quiet $# PARAMS='$(PARAMS)' SECONDPASS=true
%:;
else
run:
#printf './bin/run.sh $(PARAMS)\n'
# other rules if any
endif
Finally, if the name of the first goal is not always the same, you can adapt this with:
GOAL := $(firstword $(MAKECMDGOALS))
PARAMS := $(filter-out $(GOAL),$(MAKECMDGOALS))
$(GOAL):
#printf './bin/$(GOAL).sh $(PARAMS)\n'
%:;
Or:
GOAL := $(firstword $(MAKECMDGOALS))
ifeq ($(SECONDPASS),)
PARAMS := $(filter-out $(GOAL),$(MAKECMDGOALS))
$(GOAL):
#$(MAKE) --quiet $# PARAMS='$(PARAMS)' SECONDPASS=true
%:;
else
$(GOAL):
#printf './bin/$(GOAL).sh $(PARAMS)\n'
# other rules if any
endif
Demo:
$ make --quiet nur foo bar
./bin/nur.sh foo bar
I don't think you should use a Makefile. You want to do your own parsing of the options, and that's more trouble to do in make.
If you're dead set on it, you could do this:
%:
#true
...which will avoid printing an empty line.
It would be better to do this in Bash, though. Here's one way you could do it:
#!/usr/bin/env bash
if [ $# -lt 1 ]; then
echo Not enough args
exit 1
fi
case "$1" in
"run")
shift
./bin/run.sh $#
;;
*)
echo "Command $1 not recognized"
exit 1
;;
esac
This seems easier and more extensible.
You can always pass / set ENV variables before executing make if you only want to pass variables to make or to a shell script.
MYPARAM1=123 MYPARAM2=456 make
OR using a subshell
(MYPARAM1=123; MYPARAM2=456; echo A=$MYPARAM1 B=$MYPARAM2; make)
(export MYPARAM1=123; export MYPARAM2=456; A=$MYPARAM1 B=$MYPARAM2; make) // exporting
You might also look at the bash-shell-special-parameters

GNU make 4.1: Missing separator when $(if ...) is true in a defined function

I am trying to generate an error in a Makefile when a string is not found in the output of a shell command. The shell command depends on a parameter, therefore the whole thing is in a defined function. Here is a minimalist example:
define check_in_abcdefg
$(eval TMP := $(shell echo abcdefg))
$(if $(findstring $(1),$(TMP)),,$(error $(1) not in $(TMP)))
endef
$(call check_in_abcdefg,def)
all:
#echo Hello, world!
I would like this Makefile to output Hello, world! in this case, but I'd like it to output xyz not in abcdefg if I replace the call line with this one:
$(call check_in_abcdefg,xyz)
The problem is that with the def check I have this output:
Makefile:6: *** missing separator. Stop.
Where line 6 is $(call check_in_abcdefg,def)
Why does the syntax check fail when the $(if ...) condition is true since it's actually empty ?
Note that the echo command in the dummy target all is correctly preceded by a tab, not four spaces. I am running GNU make 4.1.90 built for Windows32, and it seems not to happen for newer version of GNU make. I am looking for any answer that could help me make it work with GNU make 4.1.90
I'm not sure why older make versions choke here, but you can make it work with one big $(eval ) like this:
define check_in_abcdefg
$(eval
TMP := $$(shell echo abcdefg)
ifeq ($$(findstring $$(1),$$(TMP)),)
$$(error $$(1) not in $$(TMP))
endif
)
endef
$(call check_in_abcdefg,def)
all:
#echo Hello, world!
To answer the question about why GNU make 4.1 is throwing this error: that version of GNU make is mishandling the newline. In your example:
define check_in_abcdefg
$(eval TMP := $(shell echo abcdefg))
$(if $(findstring $(1),$(TMP)),,$(error $(1) not in $(TMP)))
endef
$(call check_in_abcdefg,def)
The first line of the defined macro (the eval) expands to the empty string, and so does the second line (the if). So, the call expands to a single newline character.
That version of GNU make is not correctly ignoring this newline character and instead throws an error. You can change your makefile to work in those older versions by removing the newline:
define check_in_abcdefg
$(eval TMP := $(shell echo abcdefg))$(if $(findstring $(1),$(TMP)),,$(error $(1) not in $(TMP)))
endef
$(call check_in_abcdefg,def)

Resources