How can i split string with dot in makefile - makefile

I have make target like this
test.%
export var1=$(basename $*) && export var2=$(subst .,,$(suffix $*))
and i use like test.var1.var2
Now i want to do one more level like test.var1.var2.var3 how can i get that in makefile
edit:
The reason i want to do this is because i am using Make file for deploying multiple apps and i want many variables . so that user ca deploy like
make install.{app1}.{test}.{build_number}

Use subst to replace dots with spaces so that it becomes a list. Then use word to access a specific element:
word-dot = $(word $2,$(subst ., ,$1))
test.%:
export var1=$(call word-dot,$*,1) && export var2=$(call word-dot,$*,2) && export var3=$(call word-dot,$*,3)
Which outputs:
$ make test.foo.bar.baz
export var1=foo && export var2=bar && export var3=baz
As an aside (that will actually take up most of my answer), if you know in advance what the options are, you could go with some robust metaprogramming. Say you want to generate test-{app} targets for some APPS:
tmpl-for = $(foreach x,$2,$(call $1,$x))
rule-for = $(foreach x,$2,$(eval $(call $1,$x)))
APPS := foo bar baz
tmpl-test = test-$1
define test-vars-rule
$(call tmpl-test,$1): APP := $1
.PHONY: $(call tmpl-test,$1)
endef
$(call rule-for,test-vars-rule,$(APPS))
$(call tmpl-for,tmpl-test,$(APPS)):
#echo Testing app: $(APP)
The first two lines are "library" functions that will call a "template" (tmpl-for) or generate a rule (rule-for) for each element in the list you provide as the second argument. I create a tmpl-test which takes the app name and gives test-{app}. I define a rule template which takes the app name and sets a target-specific APP variable for the appropriate test-{app} target (which is also made phony by the way). Then I use rule-for to create all my rules for setting APP. Finally I write the actual body of my target, and I get the list of all possible targets using tmpl-for.
$ make test-foo
Testing app: foo
$ make test-bar
Testing app: bar
$ make test-baz
Testing app: baz
$ make test-blah
make: *** No rule to make target 'test-blah'. Stop.
It sounds complex, and it is, but if you properly abstract the templating functions it can produce flexible and easily maintainable build systems.

Related

Accessing exported variables in a shell function

Is there a way to make use of exported variables in shell function without the need for sub-make?
Take the following example.
FOO := BAR
.EXPORT_ALL_VARIABLES:
.PHONY: buzz
buzz:
$(info buzz)
$(error finish)
.PHONY: fizz
fizz: $(if $(shell echo $$FOO),buzz,)
$(info fizz)
$(MAKE) fizz
If I run the fizz target like so I get the following output.
$ make fizz
fizz
make fizz
make[1]: Entering directory '/home/jshbrntt/test'
buzz
Makefile:8: *** finish. Stop.
make[1]: Leaving directory '/home/jshbrntt/test'
make: *** [Makefile:13: fizz] Error 2
As you can see only the second run of make fizz had the shell function expanded and cause the buzz target to also run.
Is there a way to make use of exported variables in shell function without the need for sub-make?
No.
Remember always that GNU make functions such as $(shell) are evaluated while the makefile is being parsed, not when make runs recipes (refer to section 3.7 of the manual), regardless of where in the makefile the $(shell) invocation appears. make determines which variables are exported based on its own environment and the combination of all rules and export / unexport directives in the makefile, potentially including rules and / or directives generated via $(shell) and other functions. In this way it ensures that it is consistent about the environment used to execute recipes.
Although it is conceivable that make would expose expose intermediate forms of its export list to the $(shell) function (and its documentation doesn't clearly specify whether it does so), as a practical matter it would be surprising for it to do so, and your experiment shows that it does not do. And although the manual does not explicitly speak directly to the question, it should be noted that its documentation of export, etc. is in a section entitled Communicating Variables to a Sub-make.
Personally, I recommend avoiding $(shell) (and $(wildcard)) altogether. If you do use $(shell) then I recommend reserving such use to outside recipes. Inside recipes, use shell code directly. This is clearer, certainly in terms of makefile semantics (what is evaluated when), but often in terms of the actual code, too. For your particular example, that might look like so:
FOO := BAR
.EXPORT_ALL_VARIABLES:
.PHONY: fizz
fizz:
#echo fizz
#if test -n "$$FOO"; then echo FOO found; else echo FOO missing; make fizz; fi
In all current versions of GNU make, exported variables are not sent to the shell function. There are some very nasty recursive behaviors that can happen (what if you write export BAR = $(shell echo $$FOO) ???)
In the next release of GNU make, make variables will be exported to the shell function.
However, there's never any good reason to use shell in a recipe. The recipe is running in a shell, so you can just write the commands that you want directly. So if your example is accurate in that you want to use this facility in a recipe, just take out the shell invocation:
.PHONY: fizz
fizz:
#echo fizz
#test -n $$FOO && echo FOO found || echo FOO missing

Make: evaluating result of call: recipe commences before first target

I have several files for my GNU make setup. In this.mk, I have
define this_template
THIS = $(1)
THIS_DIR = $(2)
THIS_LIBNAME = $(3)
THIS_EXTERNAL_DIRS = $(4)
...
endef
In my Makefile, I have
include this.mk
... # define VAR1 and VAR2
include util/make.mk
...
util/make.mk contains one line:
$(eval $(call this_template,UTIL,$(VAR1),plutil,$(VAR2)))
However, when I run make, I get
util/make.mk:1: *** recipe commences before first target. Stop.
Reading up on other questions that relate to this error message, what I'm understanding is that this error is caused by evaluating a string which begins in a way that looks like it's inside of a recipe. However, what I'm evaluating does not.
This error means that (a) the line begins with a TAB (or more specifically, with the character defined as .RECIPE_PREFIX if your version of GNU make supports it), and (b) it is not recognized as any sort of make command such as a rule introduction, etc.
Given what you've shared with us here, that cannot happen. So there must be something going on that you haven't shared with us. Maybe one of the other included makefiles is modifying the this_template variable to contain something else.
The way to debug eval problems is always the same no matter what they are: change the eval to info so that make will print out what it will evaluate. This usually makes it pretty obvious what the problem is. So use:
$(info $(call this_template,UTIL,$(VAR1),plutil,$(VAR2)))
$(eval $(call this_template,UTIL,$(VAR1),plutil,$(VAR2)))
and see what make shows you.

How can you make a % wildcard make rule phony?

I have the following wildcard "programming" make rule that uploads a binary to a device. This obviously does not produce a real file, so should be marked phony. However, how do you mark a % percent wildcard rule phony?
%-tangnano-prog: %-tangnano.fs
openFPGALoader -b tangnano $^
.PHONY: %-tangnano-prog clean all
The phony rule does not give any error whatever you put there, so hard to tell if it worked. But I believe it did not:
$ touch blinky-tangnano-prog
$ make blinky-tangnano-prog
make: 'blinky-tangnano-prog' is up to date.
Thee are basically two possibilities:
You know in advance what %-tangnano-prog targets you can encounter. Just assign all their prefixes to a make variable, use make functions to compute the full target names and declare them as phony:
P := blinky foo bar
T := $(addsuffix -tangnano-prog,$(P))
.PHONY: tangnano-prog $(T)
tangnano-prog: $(T)
%-tangnano-prog: %-tangnano.fs
openFPGALoader -b tangnano $^
You do not know in advance what targets you can encounter. Use the same Makefile but pass the list of target prefixes to build on the command line:
$ make tangnano-prog P="blinky foo bar"

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...

How do I make an easy-to-update makefile?

My makefile looks something like this:
FOO_OBJECT_FILES := $(OBJDIR)/Foo.cpp.o
BAR_OBJECT_FILES := $(OBJDIR)/Bar.cpp.o $(OBJDIR)Bar.c.o
ALL_OBJECT_FILES := $(FOO_OBJECT_FILES) $(BAR_OBJECT_FILES)
$(BINDIR)/Foo.a: $(FOO_OBJECT_FILES)
# Rules for making a static library out of Foo's object files go here.
$(BINDIR)/Bar.a: $(BAR_OBJECT_FILES)
# This uses the exact same command sequence as the previous rule.
$(BINDIR)/All.a: $(ALL_OBJECT_FILES)
# Ditto.
# ...
When (not if) more targets are added to the project, the developer will have to update at least three things:
The list of the new target's object files
The list of all object files
Targets for making the new target, even if it uses the same rules as the others
Is there a way to simplify this process, or am I stuck with it?
I tried using wildcard rules, but it doesn't look like they work with macros.
$(BINDIR)/%.a: $(%_OBJECT_FILES)
# ...
You could treat the lists of object files as rules, but then the final target rules can't access them directly.
OBJECT_FILES_Foo: $(OBJDIR)/Foo.cpp.o
OBJECT_FILES_Bar: $(OBJDIR)/Bar.cpp.o $(OBJDIR)Bar.c.o
OBJECT_FILES_All: FOO_OBJECT_FILES BAR_OBJECT_FILES
$(BINDIR)/%.a: OBJECT_FILES_%
# This rule can't see into the object file lists to use them to build.
Is there no better way?
There are probably plenty of ways to do this. One such way is the following. All that needs to be done for a new target is add its name to the list of modules, and give the list of dependencies for it.
BINDIR := bin
OBJDIR := obj
MODULES := Foo Bar
Foo_OBJS := $(OBJDIR)/Foo.cpp.o
Bar_OBJS := $(OBJDIR)/Bar.cpp.o $(OBJDIR)/Bar.c.o
#####################################################
# #
# Nothing below here should need to be altered. #
# #
#####################################################
All_OBJS := $(foreach mod, $(MODULES),$($(mod)_OBJS))
define rule
$(BINDIR)/$(1).a: $($(1)_OBJS)
#echo
#echo 'Target: $$#'
#echo 'Deps : $$^'
endef
$(foreach lib, All $(MODULES), $(eval $(call rule,$(lib))))
###########################################
# #
# The next part is just here for testing. #
# #
###########################################
.PHONY: all
all: $(foreach lib, All $(MODULES),$(BINDIR)/$(lib).a)
%.o:
#echo Making $#
You can't do much about 1 and 2, those are arbitrary things that Make cannot possibly deduce. You can improve 3 slightly:
$(BINDIR)/%.a:
# commands for making a static library
# adding a new target:
QUARTZ_OBJECT_FILES := $(OBJDIR)/Quartz.cpp.o $(OBJDIR)Arbitrary.o
ALL_OBJECT_FILES += $(QUARTZ_OBJECT_FILES)
$(BINDIR)/Quartz.a: $(QUARTZ_OBJECT_FILES)
You could use a template to reduce those three lines to one:
$(eval $(call template, QUARTZ_OBJECT_FILES, $(OBJDIR)/Quartz.cpp.o $(OBJDIR)Arbitrary.o))
but is it really worth it?
While the other answers have provided good solutions for manual makefile writing, you could simply use automake to ease the build process.

Resources