$(error) - *** commands commence before first target. Stop - makefile

In the below code:
GREEN := "\e[1;33m]"
NCOLOR := "\e[0m"
INFO := #bash -c '\
printf $(GREEN); \
echo "=> $$1"; \
printf $(NCOLOR)' VALUE
ifeq (tag, $(firstword $(MAKECMDGOALS)))
ifneq (2, $(words $(MAKECMDGOALS)))
$(error Wrong number of tag arguments)
endif
LIST_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
VERSION := $(word 1, $(LIST_ARGS))
COMMIT_HASH := $(word 2, $(LIST_ARGS))
ifeq ($(LIST_ARGS),)
$(error You must specify a tag)
endif
$(eval $(LIST_ARGS):;#:)
endif
Indentation is done using tabs
Error: Makefile:12: *** commands commence before first target. Stop. occurs after running make tag
If I remove Line 11, 12 and 13 it works.
Line 24 is space indented, rest all is TAB indented
How to resolve this indentation error?

Related

How can I split a string by a delimiter in Makefile?

Here's the code snippet from my Makefile:
%/foo: %/bar.yaml
$(BINARY) generate -g go \
--package-name {COOL_VALUE}
# COOL_VALUE should be the parent folder of a `foo`, e.g., `foo1/foo2/foo -> foo2`
the question is how can I split $# string by / to get the second last element:
E.g.,
make foo1/foo2/foo
> ./binary generate -g go \
--package-name foo2
make foo3/foo
> ./binary generate -g go \
--package-name foo3
My attempts:
I came up with
$(eval package_name := $(word 1,$(subst /, ,$#)))
% pick second last element somehow
If you are really talking about / as a delimiter, then your best bet is to use the filename functions like this:
PARENT = $(notdir $(patsubst %/,%,$(dir $#)))
$(eval package_folders := $(filter-out foo,$(subst /, ,$#)))
$(eval package_name := $(word $(words $(package_folders)), $(package_folders)))
#echo "$(package_name)"

Succinctly adding verbose output to GNU make

I would like to add verbose output to a gnu makefile if V=1 is passed on the command line.
I can already do it in three lines as follows:
ifeq ($(V),1)
$(info SRC_FILES=$(SRC_FILES))
endif
Is there some more terse idiom, e.g., that appears all on one line? Ideally I'd like something like:
$(verbose SRC_FILES=$(SRC_FILES))
which may not be possible, or at least a one-liner like:
$(if $(V) $(info SRC_FILES=$(SRC_FILES)))
$(info $(if $(V),SRC_FILES=$(SRC_FILES))) almost does what you want. Its only drawback is that it outputs an empty line when V is undefined.
EDIT: from MadScientist's remark, and as initially suggested by BeeOnRope, $(if $(V),$(info SRC_FILES=$(SRC_FILES))) works exactly as expected, without the empty line when V is undefined.
You could also define a macro that prints an info message about a variable if and only if V is defined:
define verbose
$(if $(V),$(info $(1) = $($(1))))
endef
$(call verbose,SRC_FILES)
Of course, if you want a more generic macro, you can pass it the text to print:
define verbose
$(if $(V),$(info $(1)))
endef
$(call verbose,SRC_FILES = $(SRC_FILES))
Note that there are other types of information that you may want to control with a verbosity level variable. For the commands echoing, the commands outputs and the quiet command options I frequently use the following:
# Verbosity
ifeq ($(V),)
Q := #
MQ := --quiet
ECHO := echo
OUT := &> /dev/null
else ifeq ($(V),1)
Q :=
MQ :=
ECHO := echo
OUT :=
else
$(error V: invalid value ($(V)))
endif
foo:
$(Q)$(ECHO) 'making $#' && \
some-command $# $(OUT)
bar: cuz
$(Q)$(MAKE) $(MQ) $#
...
Maybe this will be practical:
verbose = $(if $(VERBOSITY),$(info $(-verbose)))
-verbose = $(if $(findstring 0,$(VERBOSITY)),$1 )$(if $(findstring 1,$(VERBOSITY)),$2 )$(if $(findstring 2,$(VERBOSITY)),$3 )$(if $(findstring 3,$(VERBOSITY)),$4 )$(if $(findstring 4,$(VERBOSITY)),$5 )$(if $(findstring 5,$(VERBOSITY)),$6 )$(if $(findstring 6,$(VERBOSITY)),$7 )$(if $(findstring 7,$(VERBOSITY)),$8 )$(if $(findstring 8,$(VERBOSITY)),$9 )$(if $(findstring 9,$(VERBOSITY)),$(10) )
VERBOSITY = 0
$(call verbose,warninglevel 1,warninglevel 2,warninglevel 3,info 1,info 2,info 3)
VERBOSITY = 2
$(call verbose,warninglevel 1,warninglevel 2,warninglevel 3,info 1,info 2,info 3)
VERBOSITY = 012345
$(call verbose,warninglevel 1,warninglevel 2,warninglevel 3,info 1,info 2,info 3)
Output:
warninglevel 1
warninglevel 3
warninglevel 1 warninglevel 2 warninglevel 3 info 1 info 2 info 3
I have added it to gmtt.

Makefile expanding variable inside define

define func1
include $(shell pwd)/test/$(strip $1)/component.mk
$(info :::::::${NAME} ::::::::::::::: )
endef
INCLUDES := a b c
$(foreach dir, $(INCLUDES), $(eval $(call func1, $(dir)) ))
all : $(objs)
Contents of each makefile:
cat test/a/component.mk
NAME := AA
cat test/b/component.mk
NAME := BB
cat test/c/component.mk
NAME := CC
Output is
::::::: :::::::::::::::
:::::::AA :::::::::::::::
:::::::BB :::::::::::::::
It looks like first time NAME is empty.
Let's look at the expansion of $(foreach dir, ${INCLUDES}, $(eval $(call func1, ${dir}) )) in painful detail.
${INCLUDES} is expanded, giving $(foreach dir,a b c,$(eval $(call func1,${dir})))
Next dir is set to a
$(call func1,a) is expanded
1 is set to a
func1 is expanded:
include $(shell pwd)/test/$(strip $1)/component.mk
$(info :::::::${NAME} ::::::::::::::: )
$(shell pwd) becomes HERE, say (N.B. Use ${CURDIR} instead)
$(strip $1) becomes $(strip a) becomes a
${NAME} expands to nothing
$(info ::::::: ::::::::::::::: ) expands to nothing
As a side effect ::::::: ::::::::::::::: appears on stdout
$(eval $(call func1,a)) expands to $(eval include HERE/test/a/component.mk), expands to nothing
As a side effect, the include is processed by make
Presumably HERE/test/a/component.mk exists and contains valid make syntax,
and the variable NAME gets a value.
1 is set to b. Lather, rinse, repeat.
Tip
To get a hint of problems in code like this, always run make with --warn:
$ make --warn -Rr
Makefile:8: warning: undefined variable 'NAME'
::::::: :::::::::::::::
⋮
Fix
To get some insight, replace the $(eval stuff) with $(error [stuff])
$ make
::::::: :::::::::::::::
Makefile:8: *** [ include /cygdrive/c/Users/somewhere/a/component.mk
]. Stop.
Here we see the $(info …) has disappeared even before it has got to the eval.
The naive fix is pretty horrible.
define func1
include $(shell pwd)/test/$(strip $1)/component.mk
$$(info :::::::$${NAME} ::::::::::::::: )
endef
Running this with the $(error …) in place gives
$ make
Makefile:8: *** [ include /cygdrive/c/Users/somewhere/a/component.mk
$(info :::::::${NAME} ::::::::::::::: )]. Stop.
That stuff between the [ and ] is valid make syntax.
Tidied up it looks like:
include /cygdrive/c/Users/somewhere/a/component.mk
$(info :::::::${NAME} ::::::::::::::: )
Job done. There are cleaner ways, but you need to understand the pain first!

Missing separate error from makefile

With this Makefile, I'm not sure why I'm thrown a missing separator error.
define foo
$(eval a := $(1))
$(eval b := $(1))
endef
$(call foo,hello)
$(info $(a))
$(info $(b))
all: ;
If I however replaces the first eval with this,
$(eval a := $(1)) \
then the error goes away. eval expands to nothing, and it's happy with only one eval inside the define. But I'm not sure why it's complaining in this case, nor why the trailing back slash solves it.
define foo
$(eval a := $(1))
$(eval b := $(1))
endef
bar:=$(call foo,hello)
$(info $(a))
$(info $(b))
$(info bar=[$(bar)])
all: ;
Running this makefile outputs:
$ make -f Makefile.sample
hello
hello
bar=[
]
make: 'all' is up to date.
So $(foo) function outputs new line character. It should either output nothing or value should be captured in variable or trapped with $(eval) or $(strip).
$(eval a := $(1)) \ results in no new line output from $(foo) that is why it fixes the problem.
Alternatives to adding backslashes to your $(foo) are:
#1:
define fooBody
$(eval a := $(1))
$(eval b := $(1))
endef
foo = $(strip $(call fooBody,$1,$2))
#2
define fooBody
$(eval a := $(1))
$(eval b := $(1))
endef
foo = $(eval $(call fooBody,$1,$2))
#3
$(strip $(call foo,hello))
#4
$(eval $(call foo,hello))
#5
.:=$(call foo,hello)
My personal choice is #1.
The call expansion results in a single newline (because, as you say, both the eval's expand to the empty string. When make tries to parse that, it doesn't understand it and throws this error.
The important thing to understand is that make will break the input into logical lines before it tries to run expansion. So after the expansion is complete, make expects to see a single line of output and it doesn't understand newlines existing in that single line.
Probably this could be handled better, if you wanted to file a bug at https://savannah.gnu.org/bugs/?func=additem&group=make
ETA Actually I checked and this has already been fixed in GNU make 4.2:
$ make-4.1
Makefile:5: *** missing separator. Stop.
$ make-4.2
make: *** No targets. Stop.

Custom Make function doesn't get parameter

I want to add Modules to my build system.
To keep my makefile clean when adding new modules, they all follow the same pattern, so I tried to generalize it with a function:
uc = $(shell echo $1 | tr '[a-z]' '[A-Z]')
define driver-mod
$(eval CFLAGS += -DUSE_$(call uc, $1));
$(eval include $(DRIVERS_SRC)/$1/Makefile.include);
endef
ifneq (,$(filter led,$(USEMODULE)))
$(call driver-mod, led)
endif
ifneq (,$(filter uart,$(USEMODULE)))
$(call driver-mod, uart)
endif
ifneq (,$(filter button,$(USEMODULE)))
$(call driver-mod, button)
endif
(the ifneq is going to be replaced with a $(foreach x, $(USEMODULE), $(call driver-mod, $(x))
However, it seems like $1 in driver-mod is not evaluated, I get
make: *** $(DRIVERS_SRC): Is a directory. Stop.
(doesn't actually output $(DRIVERS_SRC) but it's value, edited for clarity)
When I replace the $1 with e.g. led, it works as expected.
What am I missing?
Turns out I have to escape the $ for eval:
define driver-mod
$(eval CFLAGS += -DUSE_$(call uc, $1));
$(eval include $(DRIVERS_SRC)/\$1/Makefile.include);
endef
works!
Can be simplified as follows:
uc = $(shell echo $1 | tr '[a-z]' '[A-Z]')
define __driver-mod
CFLAGS += -DUSE_$(uc)
include $(DRIVERS_SRC)/$1/Makefile.include
endef
driver-mod = $(eval $(call __driver-mod,$(strip $1)))
$(foreach 1,$(USEMODULE),$(driver-mod))

Resources