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

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.

Related

makefile target-specific changing srcs files

I'm trying to write a Makefile with a rule to make the project with another main.cpp file, because I'm testing my code with different options
I have different versions of the main function, that I put inside differents files : main.cpp, main_1.cpp, main_2.cpp, ..., to test different versions of my code, and they all have the same dependencies
first I was just commenting and un-commenting the Makefile variable MAIN that define the main.cpp file, but I was hoping there is a way to choose the one I want to try with a specific rule ?
I tried something with target-specific variables but it didn't work :
# # # # # # #
# VARIABLES #
# # # # # # #
NAME = my_program
VPATH = srcs
CXX = c++
CXXFLAGS = -I ./headers
OBJS = $(SRCS:%.cpp=%.o)
MAIN = main.cpp
#MAIN = main_1.cpp
SRCS = $(MAIN) file.cpp
# # # # #
# RULES #
# # # # #
all: $(NAME)
# target-specific variables
test-1: MAIN = main_1.cpp
test-1: re
$(NAME) : $(OBJS)
$(CXX) $(OBJS) -o $(NAME)
clean:
rm -f $(OBJS)
fclean: clean
rm -f $(NAME)
re: fclean all
.PHONY : all clean fclean re
the error output for main test_1 is :
c++ -I ./headers -c -o main.o srcs/main.cpp
c++ -I ./headers -c -o file.o srcs/file.cpp
c++ main_1.o Webserv.o -o my_program
c++: error: main_1.o: No such file or directory
Makefile:21: recipe for target 'my_program' failed
make: *** [my_program] Error 1
I think, then, that target-specific is not the right tool for what I'm trying to do.
Does Make provide a way to accomplish that (modifying the list of srcs files when calling a specific rule, and having the compilation working great with the new srcs files) ?
I'm vaguely thinking something like this.
test-%: main_%.cpp file.cpp
Now, make test-1 will produce an executable with that name from main_1.cpp instead of main.cpp, and similarly test-2 from main_2.cpp, etc.
If you have subsequent targets which hardcode my_program which should actually depend on which version you made, this might not be suitable, or at a minimum, you'd have to refactor those to use the current output executable. Similarly, you might want to add test-[1-9] to the files to remove in the clean target (or perhaps add a realclean target to remove them too).
Tangentially, several of your make variables don't seem to serve any immediate purpose. Putting stuff in variables makes sense for things you want to be able to override at compile time, or vaguely for making a general-purpose Makefile which can be applied with only minor modifications across several projects; but in isolation, these seem like unnecessary complexities you should probably avoid for the time being.
Your immediate problem could perhaps be solved by refactoring the dependency chain, but on the whole, I'd recommend keeping it as simple as possible. make already knows how to compile common source formats; all you really need to put in the Makefile are the dependencies which are not trivially obvious and any .PHONY targets, and overrides to select e.g. a specific default action.

Apply a single Makefile target rule to multiple input files

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

Simple Makefile reporting circular dependency -- possibly from suffix rules?

I'm using mingw32-make and attempting to create a simple rule to run windres to include an icon for a Windows executable.
The structure consists of a simple C program in a.c, an a.rs file containing only the line:
1 ICON "a.ico"
..the icon file itself, and the Makefile.
The Makefile:
CC = gcc
all: a
%.rc.o: %.rc
windres $< $#
a: a.o a.rc.o
The output I get:
>make
gcc -c -o a.o a.c
make: Circular a.rc <- a.rc.o dependency dropped.
windres a.rc a.rc.o
gcc a.o a.rc.o -o a
The output files are all created correctly, but I can't figure out how to write the .rc->.rc.o rule to get rid of the circular dependency message. From what I can tell it is interpreting it as a suffix rule where %.rc.o indicates the rule is intended to create %.rc from %.o, thus the dependency on a %.rc is circular...
I can use .rco instead of .rc.o and it doesn't generate this error, but I prefer keeping it to the compound extension if possible.
Is there any way to create a pattern rule giving outputs with an extension of the .ext1.ext2 sort, without having it be interpreted as a suffix rule?
The problem is that make has a built-in rule to build an executable file foo from an object file foo.o. In your situation make matches the rule %.rc.o for the target a.rc.o. Then it tries to find a rule that will update a.rc and when it looks it sees that a.rc.o will exist, and so it matches the rule % : %.o, but then realizes that it has a circular dependency for a.rc.o : a.rc and a.rc : a.rc.o.
The simplest thing to do is define an explicit rule for a.rc so that it won't look for a pattern rule:
a.rc : ;
Alternatively, if you don't need the built-in rule to create an executable from an object file, you can cancel it by adding:
%: %.o
with no recipe.

.PHONY in linux makefile

SUBDIRS = foo bar baz
.PHONY: dirs $(SUBDIRS)
dirs: $(SUBDIRS)
$(SUBDIRS):
#echo $#
#ls $#
Anybody can just help me out to understand this make-file?
If possible explain me each statement (why do we need it?, what is the purpose? etc.)
And how exactly this make-file works?
It have wrong formatting, so i can only guess what it was before... Well, so it is:
first line assignes list "foo bar baz" to variable named SUBDIRS
second line is special command that makes all specified targets 'phonetical' - you can invoke "make dirs" or "make foo", and it will find target with that name and execute it, but it's no actual file with this name (like usual non-phony targets)
third one - creates target named 'dirs' which depends on value of SUBDIRS variable. space-separated list. this target have no real actions
fourth line creates rules for SUBRIDS variable contents, with no dependencies. The rest of text is actions that have to be performed to 'make' this target (so, in your case - if you just call "make", it will call "make dirs" (because it's the first target), which depends on foo, bar and baz - so these targets will be invoked. to perform each of these targets, make will call echo and ls - so eventually you'll get these three directory names and list of their files)

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