nested defines in makefile - makefile

I'm very new to creating makefiles, so this question might be trivial. If I'm using bad coding practices, I would appreciate to be pointed at it.
I am trying to create a generic makefile that creates rules based on input variables. At the end of my posting is a "minimal" example.
I got 2 problems.
In line 35 I instantiate the meta_template. I would expect the variable $(Dirs) to be expanded and then the meta_template to be called with each entry of $(Dirs). But it only expands till the second last, ie. the result of make terminates with:
make: *** No rule to make target 'Dir03Target01Tag01', needed by 'Dir03'. Stop.
In line 21 I want to create a shortcut rule, a rule that prerequisites all tags to a given example.
Eg: Dir01Target02: Dir01Target02Tag04 Dir01Target02Tag05
First I was puzzled that I couldn't use $($$(1)Tags) but had to use $$($$(1)Tags) to access the according Tags. (stored in Target01Tags for example).
Using $(addprefix) does not seem to expand as I expected, ie the result of make Dir01Target01 is:
Create Rule Dir01Target01 with Dir01Target01Tag01 Tag02 Tag03
where I expected it to be:
Create Rule Dir01Target01 with Dir01Target01Tag01 Dir01Target01Tag02 Dir01Target01Tag03
edit: I found the answer to question 2. I had $(addprefix $(1)$$(1),$$($$(1)Tags))alter to $$(addprefix $(1)$$(1),$$($$(1)Tags)). I think it's because this needs to be expanded in 2 runs. So after the first run $(addprefix Dirxx$(1),$($(1)Tags)) remains.
Thanks in advance.
# Targets to compile
Targets = Target01 Target02
# Tags to add to Targets
Target01Tags = Tag01 Tag02 Tag03
Target02Tags = Tag04 Tag05
# Some List (in the actual makefile a list of directories $(wildcard NumeratedDir*))
Dirs = Dir01 Dir02 Dir03
all: $(Dirs)
define meta_template
$(1): $(foreach target,$(Targets),$(foreach tag,$($(target)Tags),$(1)$(target)$(tag)))
define compile_meta
# $(1) 1st argument of meta_compile (<Dirs>xx, eg Dir03)
# $$(1) 1st argument of compile_template (<Targets>, eg Target01)
$(1)$$(1): #$(addprefix $(1)$$(1),$($$(1)Tags))
#echo Create Rule $(1)$$(1) with $(addprefix $(1)$$(1),$$($$(1)Tags)) <---- Line21
endef
$(foreach target,$(Targets),$(eval $(call compile_meta,$(target))))
define compile_template
# $(1) 1st argument of meta_compile (<Dirs>xx, eg Dir03)
# $$(1) 1st argument of compile_template (<Targets>, eg Target01)
# $$(2) 2nd argument of compile_template (<Targets>Tags, eg Tag01)
# create rule using arguments (eg Dir03Target01Tag01)
$(1)$$(1)$$(2):
#echo Create Rule $(1)$$(1)$$(2)
endef
$(foreach target,$(Targets),$(foreach tag,$($(target)Tags),$(eval $(call compile_template,$(target),$(tag))))) <--- Line35
endef
$(foreach prog,$(Dirs),$(eval $(call meta_template,$(prog))))

Related

Two targets with the same name in a Makefile

I was wondering what happens if there is 2 targets with the same name in a Makefile:
According to This question, Having 2 targets with the same name throws warnings.
However, i don't understand how this Makefile in openwrt works:
In include/package.mk:
define Build/DefaultTargets
$(if $(QUILT),$(Build/Quilt))
$(if $(USE_SOURCE_DIR)$(USE_GIT_TREE),,$(if $(strip $(PKG_SOURCE_URL)),$(call Download,default)))
$(call Build/Autoclean)
download:
$(foreach hook,$(Hooks/Download),
$(call $(hook))$(sep)
)
Note here the call to Downloadfunction in the 3rd line and the definition of the target download in the 6th line.
By having a look at the definition of the function Download in include/download.mk:
define Download
$(eval $(Download/Defaults))
$(eval $(Download/$(1)))
$(foreach FIELD,URL FILE $(Validate/$(call dl_method,$(URL),$(PROTO))),
ifeq ($($(FIELD)),)
$$(error Download/$(1) is missing the $(FIELD) field.)
endif
)
$(foreach dep,$(DOWNLOAD_RDEP),
$(dep): $(DL_DIR)/$(FILE)
)
download: $(DL_DIR)/$(FILE)
I see that the download target is redefined.
What i know is that using call will expand the function, so how this could work?
If a target is specified without a recipe, it's simply adding a dependency to the target. You're allowed as many of these as you want. So the following is valid:
foo: dep1 dep2
foo: dep3
recipe1
In which case, if dep1, dep2, or dep3 are rebuilt, then recipe1 is run. But this is not:
foo:
recipe1
foo:
recipe2
At this point make wouldn't know which recipe to run, and/or in which order, so Make complains. (note that you can have multiple definitions for pattern rules, in which case make chooses the first one that matches, but that's not allowed with static rules)

Generic make rules using define

I'm trying to create a common template to generate rules build a set of test cases and place them in unique target locations but have hit a bit of snag with the define directive. The following is the relevant section of my Makefile:
ROOT=../..
PLATFORM=akyboard_gcc
# include all the test cases for the current platform
# They add to the TEST_CASES variable
TEST_CASES=A B
A_FILES = a1.c a2.c
B_FILES = b1.c b2.c
check: $(TEST_CASES:%=check_%)
define CHECK_template
# build artifact directories
$(1)_BLDDIR=$(ROOT)/build/$(PLATFORM)/$(1)
$(1)_OBJDIR=$$($(1)_BLDDIR)/obj
$(1)_EXEDIR=$$($(1)_BLDDIR)/exe
# prepend src/ to all the files that are part of the test case
$(1)_FILES := $($(1:%=%_FILES):%=src/%)
# add the test runner as one of the files to be compiled
$(1)_FILES += test_runner/$(PLATFORM)/main.c
# construct list of objects generated by sources
$(1)_OBJ = $($(1)_FILES:%.c=$$($(1)_OBJDIR)/%.o)
# This creates a rule such as check_{test_case}:
check_$(1): $$($(1)_OBJ)
#echo 1 $(1)
#echo 2 $$($(1)_FILES)
#echo 3 $$($(1)_OBJ)
#echo 5 $$($(1)_OBJDIR)
#echo 4 $$($(1)_BLDDIR)
#echo 6 $$($(1)_EXEDIR)
$$($(1)_OBJDIR)/%.o: $(ROOT)/%.c
#echo coconut
endef
$(foreach testcase, $(TEST_CASES), \
$(eval $(call CHECK_template,$(testcase))) \
)
Issuing "make check" gives the following error
*** No rule to make target `../../build/akyboard_gcc/A/obj/a1.o', needed by `check_A'. Stop.
If I manually create the target rules below it builds without errors
../../build/akyboard_gcc/A/obj/a1.o:
../../build/akyboard_gcc/A/obj/a2.o:
../../build/akyboard_gcc/B/obj/b1.o:
../../build/akyboard_gcc/B/obj/b2.o:
But changing rules like below causes the build error:
../../build/akyboard_gcc/A/obj/%.o:
Would be grateful for any help.
You can find what wrong with your template by replacing eval with info call.
$(foreach testcase, $(TEST_CASES), $(info $(call CHECK_template,$(testcase))))
And your template is fine. But you probably have a problem with pattern rules.
From GNU make manual:
A pattern rule can be used to build a given file only if there is a
target pattern that matches the file name, and all prerequisites in
that rule either exist or can be built.
Perhaps you don't have required sources in $(ROOT) directory. And make just can't create a rule for object file from pattern.

Using the call function correctly in a makefile

I am trying to compile for different software directories with different optimization levels etc. I created the following makefile to do so:
OWNER = betsy molly fred
DOG = poodle mutt doberman
COLOUR = brown red yellow
ATTR = big small
LEGS = 0 3
#we want every possible combination to be excercised
OUTPUT_STUFF = $(foreach own,$(OWNER),$(foreach dog,$(DOG),$(foreach col,$(COLOUR),$(foreach attr,$(ATTR),$(foreach legs,$(LEGS),new/$(own)/$(dog)/$(col)/$(attr)/$(legs)/dogInfo.txt)))))
.PHONY: all
all: $(OUTPUT_STUFF)
define PROGRAM_template
own = $(1)
dog = $(2)
col = $(3)
attr = $(4)
legs = $(5)
BUILD_DIR = new/$(own)/$(dog)/$(col)/$(attr)/$(legs)
#for each build directory, we are going to put a file in it containing the build dir. string
$$(BUILD_DIR)/dogInfo.txt:
#echo "$$#"
mkdir $$(BUILD_DIR)
#echo "$$(BUILD_DIR)" > $$(BUILD_DIR)/dogInfo.txt
endef
#call the function many times
$(foreach own,$(OWNER),$(foreach dog,$(DOG),$(foreach col,$(COLOUR),$(foreach attr,$(ATTR),$(foreach legs,$(LEGS),$(eval $(call PROGRAM_template,$(own),$(dog),$(col),$(attr),$(legs))))))))
As you can see, this simple test program loops through different combinations of owner, dog etc. The end goal is to have a directory, new, that has all owners as dirs, and in those, all dogs, etc. At the bottom is just a file with the path in it.
When I run this, the output is:
new/betsy/poodle/brown/big/0/dogInfo.txt
mkdir new/fred/doberman/yellow/small/3
mkdir: cannot create directory `new/fred/doberman/yellow/small/3': No such file or directory
make: *** [new/betsy/poodle/brown/big/0/dogInfo.txt] Error 1
So, for some reason, the target is ok, but the seemingly exact same variable is the last in my loops. Fundamentally, I don't understand what is happening that well.
Weird foreach + user-defined function behavior in Makefiles seems to answer, but I don't fully get it. In my mind, when the function is called, it fills in all instances with one $, and the escaped ones become $(BUILD_DIR). It then 'pastes' the code to the temporary file, and after it's done all the calls it evaluates the file, substituting the variables as normal.
One (ugly) solution I thought of is to make the BUILD_DIR variable different every time like so:
B_D_$(1)_$(2)_$(3)_$(4)_$(5) = ~~~
Alex is correct (although I think he means recipe, not receipt :-)). The best way to debug complex eval issues is to replace the eval function with a call to info instead. So if you have something like:
$(foreach A,$(STUFF),$(eval $(call func,$A)))
then you can rewrite this as:
$(foreach A,$(STUFF),$(info $(call func,$A)))
Now make will print out to you exactly what the eval is going to parse. It's usually pretty clear, looking at the makefile output, what the problem is. In your case you'll see something like this in the output (leaving out all the extra variable settings):
BUILD_DIR = new/betsy/poodle/brown/big/0
$(BUILD_DIR)/dogInfo.txt:
#echo "$$#"
mkdir $(BUILD_DIR)
#echo "$(BUILD_DIR)" > $(BUILD_DIR)/dogInfo.txt
BUILD_DIR = new/betsy/poodle/brown/big/3
$(BUILD_DIR)/dogInfo.txt:
#echo "$$#"
mkdir $(BUILD_DIR)
#echo "$(BUILD_DIR)" > $(BUILD_DIR)/dogInfo.txt
etc. Notice how you're setting the global variable BUILD_DIR every time. In make, variables have only one value (at a time). While make is reading the makefile it expands the target and prerequisite lists immediately, so whatever value BUILD_DIR has at that time will be used for targets/prerequisites, so this works for you.
But when make finishes reading the makefile, the value of BUILD_DIR will always be the last thing you set it to; in this case new/fred/doberman/yellow/small/3. Now make starts to invoke the recipes for each target, and when it does that it will expand BUILD_DIR in the recipes then, and so ALL the recipes will get that same value.
As Alex points out, you should ensure that your recipe uses only automatic variables like $#, which are set correctly for each rule. If you do that you'll notice that you don't really need to redefine the rule at all because it's actually the same recipe for all the targets. And if you notice THAT, you'll notice you don't need the whole eval or call complexity in the first place.
All you have to do is compute the names of all the targets, then write a single rule:
ALLDOGINFO = $(foreach own,$(OWNER),$(foreach dog,$(DOG),$(foreach col,$(COLOUR),$(foreach attr,$(ATTR),$(foreach legs,$(LEGS),new/$(own)/$(dog)/$(col)/$(attr)/$(legs)/dogInfo.txt)))))
$(ALLDOGINFO):
#echo "$#"
mkdir $(dir $#)
#echo "$(dir $#)" > $#
If you don't want the trailing slash you have to use $(patsubst %/,%,$(dir $#)) instead.
The problem is that when $$(BUILD_DIR) is evaluated in receipt, the loop is already complete. The solution is to rewrite the receipt:
$$(BUILD_DIR)/dogInfo.txt:
#echo "$$#"
mkdir $$(#D)
#echo "$$(#D)" > $$#
I don't think your problem is necessarily with something to do with make. This command:
mkdir new/fred/doberman/yellow/small/3
will fail if one of the parent directories (for example, yellow) doesn't already exist. The error it spits out in this case is the one you're getting, so it seems likely this is the case. If you want a command that makes all parent directories of a given directory as needed, you should run mkdir -p, like this:
mkdir -p $$(BUILD_DIR)
See the mkdir man page for a full description of what -p does.

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.

Using multiple % in Makefile

I have to convert a set of file (let's say format fa) into another format (fb) by a command (fa2fb). Each target fb depends only on one fa file.
Data structure is in a format like this:
source:
./DATA/L1/fa/L1.fa
./DATA/L2/fa/L2.fa
...
./DATA/Ln/fa/Ln.fa
target:
./DATA/L1/fb/L1.fb
./DATA/L2/fb/L2.fb
...
./DATA/Ln/fb/Ln.fb
How can I implement it with make?
I have tried this but of course it did not work:
./DATA/%/fb/%.fb : ./DATA/%/fa/%.fb
#fa2fb $< $#
Is there any simple solution without changing the data directories?
Many thanks!
Use secondary expansion and the subst function to create a rule where the prerequisites are constructed as a more complex function of the target name:
.SECONDEXPANSION:
DATA/%.fb: $$(subst fb,fa,$$#)
#fa2fb $< $#
Note that this approach assumes that fb will not occur anywhere else in the filename (which holds true if all of your filenames are of the form DATA/Ln/fb/Ln.fb, for some integer n).
This may be the sloppiest makefile I have ever written.
define template
$(2) : $(1)
echo hi
endef
sources=DATA/L1/fa/L1.fa DATA/L2/fa/L2.fa
$(foreach source,$(sources),$(eval $(call template,$(source),$(subst /fa/,/fb/,$(subst .fa,.fb,$(source))))))
The idea is to define a macro to generate your rules, then use foreach and eval+call to invoke it once for each source. The source is the first argument to the call, so it becomes $(1) in the macro. The second argument is just the transformation from a source file name to a destination file name; it becomes $(2) in the macro.
Replace echo hi with your own rule and you should be good to go. And be sure to write a nice big clear comment or someday someone will surely show up at your door with a baseball bat.
This is basically the same as Nemo's answer. I just tried to make the foreach call a bit more readable, by creating a list of modules, containing simply L1 L2 ... Ln, instead of the list of full source names.
MODULES := $(notdir $(wildcard ./DATA/L*))
define rule
./DATA/$(1)/fb/$(1).fb: ./DATA/$(1)/fa/$(1).fa
#fa2fb $< $#
endef
$(foreach module, $(MODULES), $(eval $(call rule,$(module))))

Resources