Why don't Phony pre-requisites appear in `$?`? - makefile

Example: let's say I have a bar.c and baz.c, I want to produce foo.a. So:
foo.a: bar.o baz.o
ar -rc $# $?
Now I decide I always want bar.o to be updated. I decide to make it a Phony target. This also requires an explicit recipe for it, since implicit rules don't get searched for Phony targets. So:
.PHONY: bar.o
bar.o: bar.c
cc -c -o $# $<
foo.a: bar.o baz.o
ar -rc $# $?
Now when I run make foo.a, bar.c always gets compiled. Since bar.o gets updated, foo.a's recipe always gets run.
But: $? has an empty value so foo.a doesn't get modified. Why?

Good question. I'm not sure exactly but I suspect it is because phony targets are exactly that "phony" as-in they are explicitly expected not to represent files and so having them in the list of prerequisites newer than the target doesn't really make all that much sense.
That said if you want to keep this behavior but handle things correctly you want to avoid a .PHONY target and instead use a forced target.
If a rule has no prerequisites or recipe, and the target of the rule is a nonexistent file, then make imagines this target to have been updated whenever its rule is run. This implies that all targets depending on this one will always have their recipe run.
An example will illustrate this:
clean: FORCE
rm $(objects)
FORCE:
Here the target ‘FORCE’ satisfies the special conditions, so the target clean that depends on it is forced to run its recipe. There is nothing special about the name ‘FORCE’, but that is one name commonly used this way.
As you can see, using ‘FORCE’ this way has the same results as using ‘.PHONY: clean’.
Using ‘.PHONY’ is more explicit and more efficient. However, other versions of make do not support ‘.PHONY’; thus ‘FORCE’ appears in many makefiles. See Phony Targets.

Related

Makefile and subdirectories

I'm struggling with the correct syntax of a makefile.
This is my folder structure:
project
│ Makefile
│ HERE SHOULD BE THE OUT FILE AFTER make command
│
└───include
│ header.h
│
│
└───src
function1.c
function2.c
How must the makefile look to ensure correct results?
This is my current makefile:
SOURCES = src/function.c src/function.c src/function.c
OBJECTS = $(SOURCES:.c=.o)
CC = cc
RM = rm -f
CFLAGS = -Wall -Wextra -Werror
NAME = output.a
all: $(NAME)
$(NAME):
$(CC) $(CFLAGS) -c $(SOURCES)
ar rcs $(NAME) $(OBJECTS)
clean:
$(RM) $(OBJECTS)
fclean: clean
$(RM) $(NAME)
re: fclean $(NAME)
my current output if i run make all:
cc -Wall -Wextra -Werror -c src/function1.c src/function2.c
ar rcs output.a src/function1.o src/function2.o src/function1.o
ar: src/function1.o: No such file or directory
make: *** [Makefile:15: output.a] Error 1
if i run make flcean the output looks the following:
rm -f src/function1.o src/function2.o
rm -f output.a
So it doesn´t remove anything because the .o files are stored in the root, not the /src subdirectory
I'm struggling with the correct syntax of a makefile.
Your makefile syntax is fine. In fact, your makefile is syntactically valid and reasonably well structured. It looks a lot better than many of the ones we see around here.
I guess what you're actually struggling with is that it doesn't work, even after you fix the weird disagreement between the source file list in the makefile itself and the actual source files on disk. But "doesn't work" is a pretty vague, though lamentably common, description. You would get better help, faster, by saying something along the lines of "the .o files are created in the top-level directory instead of in the src/ directory." (Which is exactly what I expect to happen, and as a result, the ar command will fail, and the clean target will not clean the .o files.)
You need to understand that make itself doesn't know much about building software. What it knows is how to match rules to patterns so as to execute associated recipes of shell commands. The particular kinds of patterns it matches and the built-in rules that come with it are oriented toward building software, but you can't expect it to go very far with anticipating what you mean. It, like any other computer program, will happily do what you say, instead, when that differs. In this case, it just runs the cc command with the arguments you specify, and cc will choose under those circumstances to put the .o files in the working directory.
From a stylistic and best-practices standpoint, it's best to write rules that build only their target file, unlike your rule for $(NAME) that attempts to build not just $(NAME) but also all the component object files. The object files would be better built according to their own rule or rules. Making the object files prerequisites of the rule for $(NAME) will ensure that they get built when needed. That will also allow for them to not be built when that is not needed. That variation on your rule would look like this:
$(NAME): $(OBJECTS)
ar rcs $# $^
Note also that in the recipe, I have substituted automatic variable $# for a repetition of the rule target name. That's good form, but not obligatory. I have also substituted automatic variable $^ for a repetition of the prerequisite list. That's less clear-cut, in part because $^ is specific to GNU make, but if you're ok with that dependency then it's a great way to avoid repeating yourself.
Now, about building the object files: you could write a pattern rule (GNU make only) or a suffix rule that builds an object file from a corresponding C source file, or you could even write a separate rule for each object file. But you don't actually need to do that. make comes with a built in rule that will serve your needs in that area just fine, so your best bet may be to not attempt to provide your own rule for that at all. That is: modifying the rule for $(NAME) as suggested above should be sufficient for successful building, supposing, again, that the contents of your SOURCES variable accurately reflect the source files you want to build.

Is my understanding correct for the first rule of the makefile?

I'm learning make, and try to understand the following makefile from Prerequisite-Types
OBJDIR := objdir
OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)
$(OBJDIR)/%.o : %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
all: $(OBJS)
$(OBJS): | $(OBJDIR)
$(OBJDIR):
mkdir $(OBJDIR)
The first rule confuses me. It's to be firstly applied. Since $(OBJDIR) is not there, the last rule will be applied to mkdir objdir. Then, since there's nothing in the newly created directory, there's no stem.o, and correspondingly, no stem.c So the prerequisites and recipes seem to be meaningless.
The only thing that the first rule does is to make a directory, which seems to be unreal.
The first rule is a pattern rule, so it cannot be the default rule; it will not be the first applied unless you specify e.g. make thing.o.
The first rule might not do what you expect. The recipe is $(COMPILE.c) $(OUTPUT_OPTION) $<, but you don't assign a value to OUTPUT_OPTION in the makefile; unless you provide a value from outside (e.g. make thing.o OPTION_OUTPUT=...) this recipe has no special instructions about where to put the file it builds, it has never heard of objdir/, and will use the compiler's default which is probably the working directory.
The last rule will build objdir (if objdir does not already exist, and if Make invokes that rule). The command make objdir will work perfectly. If you try to build one of the object files listed in OBJS, Make will construct the directory (if the directory does not exit) -- not because it needs a place to put the file, but because $(OBJDIR) is a prerequisite of every member of $(OBJS), according to the third rule. It will not construct the directory if you try to build objdir/thing.o, because $(OBJDIR) is not a prerequisite of that target. You think Make should build it because it is obviously needed? Well, Make isn't that smart.
The first rule has a prerequisite pattern %.c, and the recipe looks for the source file in the working directory, not in any newly constructed subdirectory. If there is no such source file in the working directory, Make will not run that rule.
I don't really follow your logic.
The first rule in your makefile is this:
all: $(OBJS)
so the default goal for this makefile (if you don't specify one on the command line) is all.
all depends on all the .o files. All the .o files have an order-only dependency on the directory. So, first the directory is created, then the .o files are created, then all is done (there is no recipe here to create a program or library or anything out of those .o files).

makefile: dependency not build

The question was edited after MadScientist's answer. See history for the original makefile, but the problem stays the same.
I have a small makefile:
DEPFLAGS=-MD -Mo $(OUTDIR)/$*.Td
POSTCOMPILE=#mv -f $(OUTDIR)/$*.Td $(OUTDIR)/$*.d && touch $#
VPATH=../src
OUTDIR=../out
SOURCES:=$(notdir $(wildcard ../src/*.c))
OBJECTS:=$(SOURCES:%.c=$(OUTDIR)/%.o)
all: $(OBJECTS) $(OBJECTS:%.o=%.d)
$(OUTDIR)/%.o : %.c
$(OUTDIR)/%.o : %.c $(OUTDIR)/%.d
#$(CC) $(DEPFLAGS) -c $< -o $#
#$(POSTCOMPILE)
$(OUTDIR)/%.d : ;
.PRECIOUS: $(OUTDIR)/%.d
Directory structure looks like:
src
contains file.c
out
empty, after make: contains file.o and file.d
make
contains the makefile
When I call the makefile everything works fine and two files are generated: file.o and file.d
However, when I delete file.d nothing happens. I would expect that make finds a missing dependency for file.c and starts a rebuild. Why doesn't it happen?
Make version is 3.81 built for i386-pc-mingw32 under Windows 7.
Marking a file as .PRECIOUS does not remove all aspects of it's "intermediateness". All it does is prevent it from being deleted, but this feature of intermediate files is still in effect:
If an ordinary file b does not exist, and make considers a target that depends on b, it invariably creates b and then updates the target from b. But if b is an intermediate file, then make can leave well enough alone. It won’t bother updating b, or the ultimate target, unless some prerequisite of b is newer than that target or there is some other reason to update that target.
This is why your .d file is not recreated. In order for it to be recreated you need to ensure it's not an intermediate file. Fortunately this is trivial to do: you just need to mention the files explicitly somewhere as a target or prerequisite. You can do it like this:
all: $(OBJECTS) $(SOURCES:%.c=$(OUTDIR)/%.d)
Or if you prefer like this:
depends: $(SOURCES:%.c=$(OUTDIR)/%.d)
which would allow you to run make depends to update the dependency files, if you wanted to.
I'll just point out in passing that this method of managing dependencies is considered outdated. There's a better, more advanced way it can be done described here among other places.
(I'll be a horrific necromancer here, but I've ran into same problem, and found that actual issue isn't one mentioned in answer or comments here)
Dependency rule generated by compiler by default sports file name with ALL suffixes replaced by single suffix .o and path removed. Which doesn't match the pattern of rule in makefile.
For gcc 4.x and later correct options would be
$(OUTDIR)/%.o : %.c $(OUTDIR)/%.d
#$(CC) -MF $(OUTDIR)/$*.Td -MT $# -c $< -o $#
Mo flag no longer exist, you have to use only MF flag to specify dependency file name.MT flag allows to provide a literal line for target name.

make implicit rule with force vs .phony

Can someone shed light on the difference here:
$(tsdir)/proj has prerequisites $(tsdir)/proja and $(tsdir)/projb. I want proja's and projb's makefile to be called every time I have to build proj. If proja or projb are out of date and are updated, then their makefile will touch $(tsdir)/proja and $(tsdir)/projb respectively. If those files are then newer than $(tsdir)/proj, then rebuild proj.
I have this working by using the below code and the FORCE target. If I try and switch to use .PHONY targets, this doesn't work. I prefer .PHONY as that is supposedly the more 'correct' way of doing this. But it doesen't work and I don't know why. proja's and projb's makefiles aren't called with .PHONY targets, but proj is rebuilt.
I am using GNU make 3.81.
Thanks
Nachum
$(tsdir)/proj: $(tsdir)/proja $(tsdir)/projb
...
$(tsdir)/%: FORCE
make -C $(prereqdir)/$*
FORCE:
#or
$(tsdir)/proj: $(tsdir)/proja $(tsdir)/projb
...
.PHONY: $(addprefix $(tsdir)/, $(projects))
$(tsdir)/%:
make -C $(prereqdir)/$*
.PHONY targets are supposed to represent tasks, not real files, where implicit rule search works only for files. Thus, there is no way to build a phony target with an implicit rule.
From Phony Targets chapter:
Since it knows that phony targets do not name actual files that could be remade from other files, make skips the implicit rule search for phony targets
In your case I would just use explicit rule, may be with a static pattern:
.PHONY: $(addprefix $(tsdir)/, $(projects))
$(addprefix $(tsdir)/, $(projects)) : $(tsdir)/% :
make -C $(prereqdir)/$*
I suspect you can get the result you want by adding a phony dependency to the (real) time-stamp files, one removed from the master project proj.
.PHONY: phony
phony: ; : $#
ts := $(addprefix $(tsdir)/, $(projects))
${ts} : $(tsdir)/%: phony
make -C $(prereqdir)/$*
$(tsdir)/proj: $(tsdir)/proja $(tsdir)/projb
...

making all rules depend on the Makefile itself

When I change a Makefile, its rules may have changed, so they should be reevaluated, but make doesn't seem to think so.
Is there any way to say, in a Makefile, that all of its targets, no matter which, depend on the Makefile itself?
(Regardless of its name.)
I'm using GNU make.
This looks like one more simple, useful, logical thing that Make should be able to do, but isn't.
Here is a workaround. If the clean rule is set up correctly, Make can execute it whenever the makefile has been altered, using an empty dummy file as a marker.
-include dummy
dummy: Makefile
#touch $#
#$(MAKE) -s clean
This will work for most targets, that is targets that are actual files and that are removed by clean, and any targets that depend on them. Side-effect targets and some PHONY targets will slip through the net.
Since GNU make version 4.3 it is now possible with the use of those two special variable:
.EXTRA_PREREQS
To add new prerequisite to every target
MAKEFILE_LIST
To get the path of the make file
To have every target depend on the current make file:
Put near the top of the file (before any include since it would affect the MAKEFILE_LIST) the following line:
.EXTRA_PREREQS:= $(abspath $(lastword $(MAKEFILE_LIST)))
To have every target depend on the current make file and also the make files which were included
Put the following line at the end of your file:
.EXTRA_PREREQS+=$(foreach mk, ${MAKEFILE_LIST},$(abspath ${mk}))
The only answer I know to this is to add makefile explicitly to the dependencies. For example,
%.o: %.c makefile
$(CC) $(CFLAGS) -c $<

Resources