Apply a single Makefile target rule to multiple input files - makefile

Say I have the following set of inputs:
list = foo \
bar \
baz
And say I have a rule such as follows:
$(somedir)/%:
# Do something here
I know I am able to invoke the rule by statically defining the target and its dependency:
$(somedir)/foo : foo
$(somedir)/bar : bar
$(somedir)/baz : baz
However, would there be a way to apply this rule to an evergrowing $(list) of inputs rather than having to statically define them all?
To be more specific, I am looking for a way to run a rule for each input and get an output (which is $(somedir)/input). Is this possible in Make?

Well, not sure I understand all the details but it seems to me that pattern rules are exactly what you need:
$(somedir)/%: %
# your recipe
This tells make that any $(somedir)/foo depends on foo and is built by the given recipe. Of course, you will also need to tell make which target you want to build:
make somedir=there there/foo there/bar
Bonus: if you know the list you can add a phony target to build them all at once:
list = foo bar baz
.PHONY: all
all: $(addprefix $(somedir)/,$(list))
$(somedir)/%: %
# your recipe
Second bonus: to help writing your recipe you can use automatic variables: $# expands as the target, $< as the first prerequisite, $^ as all prerequisites, etc. So your recipe could resemble this:
$(somedir)/%: %
build $# from $<

Related

How "make" reads/resolves Makefile with percentage-matched target vs. explicit?

I thought I understood that % pattern matching:
%.o : %.c
#...
...was equivalent to "explicitly" writing the targets' rules:
f1.o : f1.c
#...
f2.o : f2.c
#...
(assuming those are the only .c files)
I also know that writing multiple rules for the same explicit target results in the rule being overwritten by the last defined rule:
r :
#1
r :
#2
(make outputs #2)
So, how exactly does make resolve the Makefile below?
foo : bar
bar :
#bar
% : %.c
#%
When bar.c is the only .c file in the directory (or there's none), it outputs:
#bar
If there's only foo and foo.c (and the Makefile):
#bar
#%
I wasn't sure what to expect when there was a bar.c file, but I thought when there wasn't that the only "command" being run would be #bar.
So, how are these rules/dependencies being resolved like this?
The reason that I'm interested is because this implicit/explicit "double-rule" syntax seems essential to understanding how -M and include work to automate header dependencies, for example:
a.out: main.o
g++ main.o
%.o: %.cpp
g++ $< -c
main.o: main.cpp foo.h # <-- what *.d files look like
...causes desired behaviour like:
a.out: main.o
g++ main.o
main.o: main.cpp foo.h
g++ $< -c
I thought I understood that % pattern matching ... was equivalent to "explicitly" writing the targets ... (assuming those are the only .c files)
That's not right.
A pattern rule is a template that tells make how it CAN build a target. Pattern rules are only used if there's no explicit rule for that target already. A pattern rule doesn't say "go find all files that match this pattern and create explicit rules for them". It says, "if you find you need to build a target but you don't have any explicit rule already, and the target matches this pattern, then here's how you can build it".
For example, if you have a makefile that contains ONLY pattern rules, even if those pattern rules match existing files, and you just type make, you'll get a message that make has nothing to do. That's because you've not asked make to build anything, you just told make HOW to build something.
I also know that writing multiple rules for the same explicit target results in the rule being overwritten by the last defined rule
Of course, you will get a warning if you do this about overwriting an existing recipe.
It's important to understand that you can have as many different lines adding new prerequisites to a target as you like. It's not until you create a recipe that you have created an explicit rule. Until then, there's no actual rule that matches the target.
On top of MadScientist's expert answer, I thought I'd explain how it applies to both outputs for the Makefile (which might be useful for make beginners like me):
foo: bar
bar :
#bar
% : %.c
#%
#bar
When there's no bar file:
bar :
#bar
...then bar is a "non-file" (phony) target. A phony target with no prerequisites always executes the recipe - as opposed to a file target where no prerequisites means that the target never has its recipe executed (not sure why this doesn't result in warnings). So, ironically, when there's no bar file, we see #bar output, otherwise we don't.
#%
As MadScientist says in his answer, % : %.c can never match bar/bar.c because there's an explicit rule for bar (ie. one with a recipe). However, we can use the recipe in
% : %.c
#%
... if we have the files foo/foo.c. This is because foo : bar isn't an explicit rule (has no recipe). The Makefile becomes equivalent to:
foo: bar
bar :
#bar
foo : foo.c
#%
...which is equivalent to:
foo: bar foo.c
#%
bar :
#bar
So make will output #% if there's no bar file, or if foo's timestamp is older than either bar or foo.c.
#bar
#%
To work out the combination of files we need to get both #bar and #%, we use our logic from the #bar section (above) which is simpler: We cannot have a bar file. Looking at the #% section, we see that if we have no bar file then we still need both foo and foo.c files (but timestamps won't matter). So the directory will be: foo, foo.c, Makefile, plus any other file except for bar.

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"

GNU make - transform every prerequisite into target (implicitly)

I have another make-like tool that produces an XML as an artifact after parsing my makefile which I'll then further process with Python.
It'd simplify things for me - a lot - if I could have make consider every single prerequisite to be an actual target because then this other tool
will classify each and every file as a "job".
This is a fragment of my makefile:
.obj/eventlookupmodel.o: C:/Users/User1/Desktop/A/PROJ/src/AL2HMIBridge/LookupModels/eventlookupmodel.cpp C:\Users\User1\Desktop\A\PROJ\src\AL2HMIBridge\LookupModels\eventlookupmodel.h \
C:/Users/User1/Desktop/A/PROJ/qt5binaries/include/QtCore/qabstractitemmodel.h \
C:/Users/User1/Desktop/A/PROJ/qt5binaries/include/QtCore/qvariant.h \
...
I'd want for make to think I have a dummy rule for each prerequisite such as below:
C:/Users/User1/Desktop/A/PROJ/qt5binaries/include/QtCore/qvariant.h:
#echo target pre= $#
C:/Users/User1/Desktop/A/PROJ/qt5binaries/include/QtCore/qabstractitemmodel.h:
#echo target pre=$#
C:/Users/User1/Desktop/A/PROJ/src/AL2HMIBridge/LookupModels/eventlookupmodel.cpp :
#echo target pre=$#
C:\Users\User1\Desktop\A\PROJ\src\AL2HMIBridge\LookupModels\eventlookupmodel.h:
#echo target pre=$#
I don't care about the exact form of the rule just that each file is considered an actual target.
My method of passing in this rule would be by setting the MAKEFILES variable like so
make all MAKEFILES=Dummy.mk
with Dummy.mk containing this rule so that I do not modify the makefiles.
I've tried the following so far.
Dummy.mk:
%.h:
#echo header xyz = $#
%:
#echo other xyz= $#
This partially works.
I run make all --trace --print-data-base MAKEFILES=Dummy.mk and I can see that
make does "bind" the %.h: rule to the header files. In the --print-data-base section, I see that rule being assigned to the header files.
C:/Users/User1/Desktop/A/QNX_SDK/target/qnx6/usr/include/stddef.h:
# Implicit rule search has been done.
# Implicit/static pattern stem: 'C:/Users/User1/Desktop/A/QNX_SDK/target/qnx6/usr/include/stddef'
# Last modified 2016-05-27 12:39:16
# File has been updated.
# Successfully updated.
# recipe to execute (from '#$(QMAKE) top_builddir=C:/Users/User1/Desktop/A/HMI_FORGF/src/../lib/armle-v7/release/ top_srcdir=C:/Users/User1/Desktop/A/HMI_FORGF/ -Wall CONFIG+=release CONFIG+=qnx_build_release_with_symbols CONFIG+=rtc_build -o Makefile C:/Users/User1/Desktop/A/HMI_FORGF/src/HmiLogging/HmiLogging.pro
', line 2):
#echo header xyz = $#
However, I do NOT see the "echo header xyz $#"-rule being executed.
Regarding the %: rule, it is neither executed for the .cpp files nor "bound" to them in the --print-data-base section.
However, it is bound and executed for existing targets which have no suffix i.e.
all: library binary
binary: | library
ifs: | library
For the %: rule, the reason for this behavior is because of 10.5.5 Match-Anything Pattern Rules: If you do not mark the match-anything rule as terminal, then it is non-terminal. A non-terminal match-anything rule cannot apply to a file name that indicates a specific type of data. A file name indicates a specific type of data if some non-match-anything implicit rule target matches it.
If I make it non-terminal - no double colon - then the rule doesn't apply to built-in types like .cppunless I un-define the built-in rules that negate my intended %: rule.
If I make it terminal, "it does not apply unless its prerequisites actually exist". But a .h or .cpp doesn't technically have prerequisites; can I just create a dummy file and have that as its prerequisite?
NOTE: This has NOTHING to do with gcc -M generation. Yes the -M option would help in the specific case of header and source files but this question is for more generic targets and prerequisites that already exist in the makefile when make is launched.
This may take a few iterations. Try:
%.h: null
#echo header xyz = $#
%: null
#echo other xyz= $#
null:
#:
Try generating static pattern rules for the header files. See one of the answers to Make ignoring Prerequisite that doesn't exist.
Static pattern rules only apply to an explicit list of target files like this:
$(OBJECTS): %.o: %.c
*recipe here*
where the variable OBJECTS is defined earlier in the makefile to be a list of target files (separated by spaces), for example:
OBJECTS := src/fileA.c src/fileB.c src/fileC.c
Note that you can use the various make utility functions to build that list of target files. For example, $(wildcard pattern), $(addsuffix), etc.
You should also ensure that the recipe "touches" the header file to change the timestamp.
I've found that using static pattern rules instead of pattern rules fixes problems where make doesn’t build prerequisites that don’t exist, or deletes files that you want.
Here is an example of using wildcard to copy files from one directory to another.
# Copy images to build/images
img_files := $(wildcard src/images/*.png src/images/*.gif src/images/*.jpg \
src/images/*.mp3)
build_images := $(subst src/,$(BUILD_DIR)/,$(img_files))
$(build_images): $(BUILD_DIR)/images/% : src/images/%
mkdir -p $(dir $#)
cp -v -a $< $#
There are other make functions like addprefix that could be used to generate a more complex file specification.

using the same make file in a separate directory

I have a makefile in directory foo and would like to use the same makefile in a subdirectory bar. I have been doing the following:
all:
<do work in foo>
cd bar;
make -f ../Makefile <target to make in bar>
This gets very messy when I try to do target specific variable values as I need to pass them on the command line when calling make in bar. Is there a cleaner way to do this?
I cannot tell from the question whether the following solution suites your needs, it might - or might not - work for you.
If your situation is that you simply want the same Makefile features available, include could be a solution. You can create a Makefile in directory bar in which you do everything you need specific to bar, and besides that, you do:
include ../foo/Makefile
Caveat! This doesn't work straight-forward. There cannot be two recipes with the same name. For example, if you want foo/Makefile to do recipeBar for all, and you want foo/Makefile to do recipeFoo and recipeBar for all, the following does not work:
foo/Makefile:
.PHONY: all
all:
recipeFoo
bar/Makefile:
.PHONY: all
all:
reciveBar
include foo/Makefile
Instead, the recipes have to be separated into unique names. However, dependency rules can be there multiple times, so it's not really a challenge to workaround this caveat. So, the following would work:
foo/Makefile:
.PHONY: all
all: allFoo
.PHONY: allFoo
allFoo:
recipeFoo
bar/Makefile:
.PHONY: all
all: allBar
.PHONY: allBar
allBar:
recipeBar
include foo/Makefile
Now, if you run make in bar, it would run recipeFoo and recipeBar.
If the sequence matters to you and recipeFoo must run before recipeBar, make allBar dependent on allFoo, like this:
bar/Makefile:
.PHONY: all
all: allBar
.PHONY: allBar
allBar: allFoo
recipeBar
include foo/Makefile
If you want your target-specific variables available when you call another make (for which I recommend to use $(MAKE) not make), you can export your variables - with the corresponding consequences (environment space overflow risk on some Windows versions, .
For example, if you have a target-specific variable FOO for target all in Makefile, and you want that when calling Submake.mak that variable is known, it works like this:
Makefile:
all: export FOO:=bar
.PHONY: all
all:
$(MAKE) -f Submake.mak
Submake.mak:
.PHONY: all
all:
echo $(FOO)
Create a link (hard or symbolic, your choice) in bar to ../Makefile. Then, as Carl points out in his comment, you can make -C bar and everything should work. (As of gmake 3.81, at least, make switches to the new directory first, then does its thing. I cannot speak for gmake 4.0.)

include files depended on target

I have a Makefile which includes makefiles from sub-directories.
However, what I want is to include these "sub"-makefiles on base of a selected target.
Background is, that the sub-makefiles define different object files and depending on these object files the target executable should be created.
Assuming sub-makefile1 sets the variable
OBJECTS := foo.o foo1.o
sub-makefile2 sets
OBJECTS := bar.o bar1.o
And the generic rule would be:
lib/%.so: $(OBJECTS)
link $^ -o $#
The targets are (for example):
foo: lib/foo.so
bar: lib/bar.so
whereas target foo should include the foo makefile, target bar the bar-makefile.
Any idea how to handle this situation?
Thanks,
Christian
Beta has mentioned the $(MAKECMDGOALS), but not described it:
ifeq ($(MAKECMDGOALS),foo)
include sub-makefile1
endif
ifeq ($(MAKECMDGOALS),bar)
include sub-makefile2
endif
# Rest of Makefile follows...
This isn't such a great idea, as it will only work when make is called interactively. You can hack around this by making rules for foo and bar that recursively invoke make:
ifeq ($(MAKECMDGOALS),foo)
include sub-makefile1
foo: # ...
# Normal commands to build foo
else
foo:
$(MAKE) $<
endif
ifeq ($(MAKECMDGOALS),bar)
include sub-makefile2
bar: # ...
# Normal commands to build bar
else
bar:
$(MAKE) $<
endif
The specific thing you're asking for -- conditional inclusion -- is difficult in Make. It can be done, but it's not elegant.
There are several ways to get the effect you want. You could use a conditional on MAKECMDGOALS. You could could have your makefile invoke a second makefile, and pass it the name of the subdirectory to use. But (without knowing more about the situation) I think this way is the tidiest:
include sub-makefile1
FOO_OBJECTS := $(OBJECTS)
include sub-makefile2
BAR_OBJECTS := $(OBJECTS)
lib/%.so:
link $^ -o $#
lib/foo.so: $(FOO_OBJECTS)
lib/bar.so: $(BAR_OBJECTS)
foo bar : % : lib/%.so
(You could be clever with variable names like foo_OBJECTS to save a line or two, but I advise against that.)

Resources