On 2026-04-30 15:30, Heime wrote:
> I have a problem understanding the utility of order-only dependencies
> in make.
>
> For example, consider
>
> obj/main.o: main.c | obj/
> gcc -c main.c -o obj/main.o
>
> obj/:
> mkdir -p obj
>
> obj/ is built before obj/main.o, but updates to obj/ (e.g., adding
> files) won't trigger a rebuild of obj/main.o.
Even if your makefile is somehow written to react to an updated timestamp of
the directory, it would still have to actually figure out the objects
there, e.g. with a $(wildcard obj/*.o) or whatever.
The bigger problem is that .o files in an obj/ directory are not expected
to exist; they are intermediates produced by the Makefile itself.
(Under the predominantly "normal" circumstances.)
The only way they would be primary objects that appear spontaneously
that you have to react to is if you are getting .o files from some
vendor as an SDK or something. Then they become primary objects
(e.g. into building some .a file, etc).
A regular Makefile that makes .c files into .o files, and then links
them, is only concerned with reacting to newly appearing .c files.
It knows the exact list of .o files.
One traditional approach is not to scan anything: the Makefile
specifies some OBJ variable which holds the names of the object
files. This has to be maintained by hand: object files do not exist
in a fresh checkout of the source tree and so their names
cannot be inferred.
Another traditional approach is to specify the source files, in
a variable like SRCS and from them infer the object file names.
The assignment of SRCS can be replaced by $(wildcard ...) operations
that scan the filesystem.
That creates the risk that random .c files lying in the directory
will be pulled into the image. If a configure test forgets to
wipe some conftest.c file, the *.c pattern pulls it in; oops!
We could contemplate the idea of looking for new .c files when
some src/ directory timestamp changes. But it's not clear what
the point would be. Even if the src/ directory timestamp does
not change, we have to react to src/parse.c having a new timestamp.
If you wildcard all of src/*.c, and then filter the list of .c
names to .o names, and declare that your program/library/whatever
image is made of those .o files, then you're more or less golden.
The build system need not be concerned with the existence of
directories and their time stamps, only paths referencing files.
The one time it matters is when a destination directory has to
exist so that a file can be written to it. That's exactly what
you have above with the order-only prerequisite whose rule is
to do a mkdir -p. Beyond ensuring that the obj/ directory exists,
we need not be concerned with its time stamp.
> Why would one add files if the program will not be rebuilt? I think
> that if files are added to a build directory, they are included because
> they are needed for updating the program.
If random .o files are added to an obj/ directory, an obj/ directory
that is normally created by a Makefile and populated entirely under
the control of the Makefile, then that is an unusual, "sneaky"
situation.
Yet, if we must handle such a sneaky situation, it doesn't have to
involve the time stamp of the directory itself. What we can do is
do a $(wildcard objs/*.o), and merge that with our calculated OBJS.
In a clean tree of the project in which nothing has been built,
object files don't exist, $(wildcard objs/*.o) will come up with
nothing; OBJS will not contain any additions.
If the project is built fully, then $(wildcard objs/*.o) will have
the same content as OBJS. We must carefully merge the two together
without duplicates, like using $(sort $OBJS $(wildcard objs/*.o)).
In short, if we have
ourprogram : $(sort $OBJS $(wildcard objs/*.o))
# steps to link ourprogram
that basically should handle the unusual requirement of pulling in
"side injected" object files; no need to deal with objs/
by itself.
Cheers ...