Make: Set variable depending on which target is to be build - makefile

In (GNU) make, I want a variable to be assigned a value when the build chain of specific targets are starting. For targets a.so and b.so, each depending on b.o, I can do this manually like this:
# Rules
all: a.so b.so
a.so: b.o
#echo Current .so is $(current)
touch $#
b.so: b.o
#echo Current .so is $(current)
touch $#
b.o:
#echo Current .so is $(current)
touch $#
# Set the "current" variable based on which .so is being build
a.so: current = a.so
b.so: current = b.so
Here, current has the value a.so when executing the rules for both a.so and b.o, while current has the value b.so only when executing the rule for b.so. This is what I want. The mental model I have of this is that the line a.so: current = a.so gets triggered as soon as make finds out that a.so should be build (that is, before the rules of either a.so or b.so are run). Please correct me if I'm wrong.
In my actual case, I have many more .so files, and so I want to replace the two last lines with something like this;
%.so: current = $#
or this;
sofiles = a b
$(addsuffix .so, $(sofiles)): current = $#
However, both of these result in the same, wrong behavior, where the current variable gets set anew even when the b.o target is about to be build. Why does the b.o target trigger these lines? How can I achieve the same behavior as when manually writing a.so: current = a.so for every .so, but without actually wiring this for every single .so?
Note: I know that the use case shown in the example code is weird and that the sought after effect can be achieved without this awkward current variable. I am however not interested in any solution which solves the problem without using this variable.

There are many things to discuss here. First, your mental model of how target-specific variables works is not quite accurate ("the line a.so: current = a.so gets triggered as soon as make finds out that a.so should be build"). What really happens is that each target make encounters creates a new "scope" which is active while that target and all its prerequisites are being created, and the target-specific variables for that target are in effect inside that scope. Once that scope exits (the target is finished) then those variable settings are gone.
In your example above, current will have the value a.so inside the recipe for b.o only when b.o is being created as a prerequisite of a.so. If b.o was being created as a prerequisite of b.so, then current would be b.so. Because your makefile lists a.so first it will be built first, and because b.o will only be built once (per invocation of make), it may appear that it's always using a.so as the value of current but that's not precisely accurate. Consider this:
$ make all
Current .so is a.so
touch b.o
Current .so is a.so
touch a.so
Current .so is b.so
touch b.so
But also this:
$ rm -f b.o
$ make b.so
Current .so is b.so
touch b.o
Current .so is b.so
touch b.so
See how current is set to b.so if b.o is built because it's a prerequisite of b.so not a.so.
And also this:
$ rm -f b.o
$ make b.o
Current .so is
touch b.o
Here because b.o isn't a prerequisite of any .so, then current is not set at all!
The next thing to think about is this line:
%.so : current = $#
It's not true that this line is "triggered" for b.o. However, this sets the pattern-specific variable current to the value $# (note, NOT to the expansion of the $# variable!). This means that each time $(current) is expanded it will expand to $#, which is the current target name, for whatever recipe it's used in. So this won't expand to a.so or b.so unless the current target name is actually a.so or b.so. If the target name is b.o then $# expands to b.o.
If you want to do this you can't use a variable like $#, you have to use the real value. You can do this with eval, for example:
sofiles = a b
$(foreach SO,$(sofiles),$(eval $(SO).so: current = $(SO).so))
However, based on the first comments above I'm not sure that's sufficient for your needs.

Related

Re-evaluating GNU make makefile variable

I have inherited a large branched project? that requires a volatile set of .a archives $(LIB_FILES) to be included into link target, located in some directories $(LIB_DIRS). I can write an expression like this:
LIBDEP = $(foreach ldir, $(LIB_DIRS), \
$(filter $(addprefix %/, $(LIB_FILES)), $(wildcard $(ldir)/* )))
The problem is that they might not exist at moment of make's invocation and would be built by invoking $(MAKE) inside of another target's rule, which is a prerequisite to the link step.
The problem is actual list of files that should be created varies on external factors determined at their build steps, that I can't hard-code it properly, without turning makefile into a spaghetti mess and said variable is not re-evaluated at the moment of link command invocation.
I have suspicion that $(eval ) function can be used somehow, but manual is not very forthcoming as well as I didn't found examples of its use in this way.
Toolchain: GCC and binutils, make 3.81
Another solution is to create an explicit dependency of your make script on the output of the step which currently creates the variable $(LIB_FILES). This is what the manual is dealing with in the chapter How makefiles are remade and it aims at the technique which make is best at, namely deriving dependencies from the existence and timestamp of files (instead of variables). The following hopefully depicts your situation with the process of deducing a new set of libraries simulated by the two variables $(LIBS_THIS_TIME) and $(LIB_CONFIG_SET).
LIBS_THIS_TIME = foo.a:baz.a:bar.a
LIB_CONFIG_SET = $(subst :,_,$(LIBS_THIS_TIME))
include libdeps.d
linkstep:
#echo I am linking $^ now
touch $#
libdeps.d: $(LIB_CONFIG_SET)
-rm libdeps.d
$(foreach lib,$(subst :, ,$(LIBS_THIS_TIME)),echo linkstep: $(lib) >> libdeps.d;)
$(LIB_CONFIG_SET):
touch $#
If make finds that libdeps.d is not up to date to your current library configuration it is remade before make executes any other rule, although it is not the first target in the makefile. This way, if your build process creates a new or different set of libraries, libdeps.d would be remade first and only then make would carry on with the other targets in your top makefile, now with the correct dependecy information.
It sometimes happens that you need to invoke make several times in succession. One possibility to do this is to use conditionals:
ifeq ($(STEP),)
all:
<do-first-step>
$(MAKE) STEP=2 $#
else ifeq ($(STEP),2)
all:
<do-second-step>
$(MAKE) STEP=3 $#
else ifeq ($(STEP),3)
all:
<do-third-step>
endif
In each step you can generate new files and have them existing for the next step.

Makefile directory rule dependency rebuild

I got Makefile like this
all: sub-dir my-rule
sub-dir:
$(MAKE) -C $#
my-rule: sub-dir
g++ main.cpp
.PHONY sub-dir
So basically I want to wait for sub-dir to finish before building my-rule but my-rule is rebuilt everytime - even if there where no changes in sub-dir.
How can I make it to wait for sub-dir and rebuild my-rule only when there were changes in sub-dir?
When you write a rule like:
my-rule: sub-dir
the target (my-rule) will be rebuilt if the prerequisite (sub-dir) is newer. Make doesn't care whether the target or prerequisite are files are directories, only what their last modified time is.
Your makefile has many issues. The simplest one is that you never create a target my-rule, so as far as make is concerned that target is always out of date (non-existent == out of date).
You have to write your rule like this so that the recipe updates the target:
my-rule: sub-dir
g++ main.cpp -o $#
Of course change my-rule if that's not the program you want to create.
Second, directory modification times are updated when the directory changes. The directory changes when something in that directory is renamed, added, or removed. So if you invoke the sub-make and the sub-make renames, adds, or removes something in that directory then the my-rule target will be out of date. If nothing is renamed, added, or removed, then my-rule will NOT be out of date.
In general you almost never want to list a directory as a prerequisite. Instead, you should list the targets that the sub-make creates as the prerequisite, like this (supposing the sub-make creates libfoo.):
sub-dir/libfoo.a: FORCE
$(MAKE) -C $(#D)
FORCE:
my-rule: sub-dir/libfoo.a
...
The FORCE rule is there to force the sub-make to be invoked even if sub-dir/libfoo.a already exists.

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.

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

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.

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