Makefile rule is never up-to-date - makefile

(Yes, I know this question gets asked a lot. I think this case is different.)
Makefile:
dependency := ./dependency
target_file := ./target
.PHONY: target
target: $(target_file)
$(dependency):
touch $#
$(target_file): $(dependency)
touch $#
Running make --trace once yields this output:
Makefile:8: target 'dependency' does not exist
touch dependency
make: Circular target <- target dependency dropped.
Makefile:11: update target 'target' due to: dependency
touch target
That's pretty much as expected, although the circular target bit is odd.
Subsequent invocations of the make --trace yield this output:
make: Circular target <- target dependency dropped.
Makefile:11: update target 'target' due to: dependency
touch target
There's still that circular target, and it recreates the target file even though it was already up-to-date. It also claims the dependency file was out of date, but it doesn't recreate that.
What is causing this behavior? One possible clue: if you replace ./target with ./foo it behaves as you'd expect.

Traditionally for make, and generally speaking for GNU make, target and prerequisite names are flat strings, in that make does not know about directories or paths, and it does not attempt to canonicalize target names as paths or any such thing.
Except, GNU make does provide special handling in the particular case of leading sequences of ./ in target and prerequisite names. Per the manual, such sequences are stripped, with the effect that ./file and file are equivalent as target and / or prerequisite names to this make implementation. That's apparently a GNU invention, and it is the source of the odd behavior you observe.
Safest, especially if you want portability to other makes, is to avoid target and prerequisite names starting with ./.

Related

Target dependency: Makefile no rule to make target error

Here is the make file that I am running,
.PHONY: build
build: pre_build_script $(OUTPUTDIR)/%.cpp
$(OUTPUTDIR)/%.cpp: $(INTXTDIR)/%.txt
python.exe $(SOMEDIR)/somepythonscript.py $(INTXTDIR) $(OUTPUTDIR)
.PHONY: pre_build_script
pre_build_script:
pythonscript.exe $(PREBUILDDIR)
This is the output that I get:
$ make build
pythonscript.exe $(SAMPLEDIR)
make: *** No rule to make target '../obj/CPP/%.cpp', needed by 'build'. Stop.
Looks like I'm missing on some sytanx as I get this error inspite of declaring the target dependency. Any suggestions?
This means make cannot find a file named $(OUTPUTDIR)/%.cpp, a prerequisite for the first rule.
You cannot use % as a wildcard anywhere in a rules like this:
build: pre_build_script $(OUTPUTDIR)/%.cpp
it needs to be a part of pattern rule or a static pattern rule.
You can use $(wildcard $(OUTPUTDIR)/*.cpp) to get a complete list of files, but it's an anti-pattern (pun intended). You are supposed to either exactly know what files are used in what rules, or (know it even better and) create a generic pattern rule.
The second pattern rule (one using somepythonscript.py) is supposed to work on a single source-target file pair, $(INTXTDIR)/%.txt -> $(OUTPUTDIR)/%.cpp. The command seems to process all the files in the directory, which is not incremental: it will redo all the work even if only one file was updated.

make: trigger without rebuilding dependency

Basically I have the usual Makefile construct:
target: dependency1 dependency2 dependency3
runtargetscript.sh
However in this case, the target only needs one of the dependencies and some dependencies may not be buildable. (so I cannot just build all dependencies)
Is it possible to tell make to trigger "target" when one of the dependencies changed/was created (i.e. normal behaviour) but NOT to try to rebuild any missing dependencies?
With GNU make you can use shell escapes to build the dependencies dynamically, adding them only if they already exist:
if_exist = $(shell if [ -e $(1) ]; then echo $(1); fi)
target: $(call if_exist,dependency1) $(call if_exist,dependency2) $(call if_exist,dependency3)
runtargetscript.sh
This will run the script if target does not exist, or if it is older than any of the dependencies that do exist at the time the makefile was read, but will not attempt to build them if they do not exist at that time.
Note the important caveat there -- if the file(s) do not exist, but some other unrelated rule runs an action that creates them, it won't rebuild target, unless you rerun make target again.
Assuming your rule body does not do anything special depending on which dependencies are newer than the target you can use the -W flag to make to instruct it to consider certain targets as always new (and thus not in need of building).
So for the given example assuming you can (and want) to build dependency2 but not dependency1 or dependency3 you would run:
make -W dependency1 -W dependency3 target
Edit: As pointed out in the comments this does not work correctly when dependency2 is not newer than target as target will still be built.
In that case I believe the only solution (given the comments below) is to use something like:
DEPENDENCY_BIN := $(or $(wildcard /path/to/mysql),$(wildcard /path/to/sqlite3),/path/that/does/not/exist)
target: $(DEPENDENCY_BIN) dependency2

Make command using default target name 'Makefile'

Why is the following makefile using Makefile target?
Makefile1:
Initially I have the following makefile which worked as expected when invoked as make abc xyz -s.
%::
echo $#
I would get
abc
xyz
Makefile2:
Now after adding an empty rule named test.
%:: test
echo $#
test:
the following invocation
make abc xyz -s
results in
Makefile
abc
xyz
Why am I getting Makefile as my output even though I am giving only abc and xyz as targets? Thanks in advance.
Because make always tries to rebuild the build files before building the actual targets. If it finds a rule for Makefile and if it is out-of-date, it will be rebuilt and reloaded and the requested targets will be built according to the new makefile. This is a feature so that if the build-files are themselves generated (rather common with autotools, cmake and similar), it won't use stale build instructions.
For more details see GNU Make Manual section 3.5
In the specific examples above the rule has target % and that matches absolutely anything, including Makefile. So make will find it as rule for remaking makefile and will evaluate it.
Now in the first case Makefile exists and is newer than all of it's dependencies trivially because there are none and none of it's dependencies need to be remade also because there are none. So make will conclude that Makefile does not need to be remade.
In the second case however Makefile exists, but it's dependency test needs to be remade. So make runs the (empty) rule and than comes back and runs the rule for Makefile. Because make does not check the timestamps after making dependencies. It simply assumes that when it remade them, the dependent targets need to be remade as well.

Get error for "make: Nothing to be done for 'target'"

Let me illustrate it with an example.
mkdir test
cd test
touch target
make target
This will result in: make: Nothing to be done for 'target'.
So make tells me there is nothing to do. This is because make did not find a rule to make target, but because the target already exists make tells me there is nothing to do.
Now, I don't want that. I want make to give me an error when it cannot find a rule for target, even though the target already exists.
I have tried the following:
echo '.DEFAULT:
echo make: *** No rule to make target `$#'. Stop.
false'> Makefile
But this does not stop the make when making multiple targets.
The problem is, that make assumes the target name is also a file which will be build by the given commands.
But sometimes this is not true (e.g. think of "clean").
To tell make that some targets don't build this file, you need to make them "phony". Put the following line into your Makefile:
.PHONY: target
If you think about it, you would end up with a chicken-and-egg situation (or infinite regress). Suppose you managed to have a rule that said 'You must have a rule to create target before it is legitimate', then you'd have a rule that says 'target depends on X' for some other file X. That's written:
target: X
command to build target from X
But then you'd be back to the starting point: you'd also want a rule to create X. How can you do that? You might have a rule that depends on nothing and magically creates the file X when it is needed:
X:
command to build X from nothing
Without a rule like that, you have an infinite regress.
So, make is designed to ensure that files exist and are up to date. If a file exists and there are no rules - implicit or explicit - to specify how it is made, then the file is up to date. What you are seeking to do is not possible.
Actually this sounds like a perfectly reasonable (if misguided ;-)) request. You will have to explicitly list every source file with a rule with an empty recipe though.
Here you go:
Makefile: ;
.PHONY: dodgy
dodgy%: dodgy; $(error You must provide a rule for $*)
%: dodgy% ;
The proof of the pudding:
$ rm aa
$ make aa
Makefile:4: *** You must provide a rule for aa. Stop.
$ touch aa
$ make aa
Makefile:4: *** You must provide a rule for aa. Stop.
Note that the line Makefile: ; is necessary. After all, the first thing make tries to do is rebuild the Makefile.
Note also that the catch-all pattern rule is non-terminal. This can be a massive performance hit. As the manual says about match anything rules "They are very useful, but it can take a lot of time for make to think about them."

Make seeks dependencies in a directory before switching to it?

I tried to write a make rule of this form:
lib%.so: computations/%.h
make -C computations/ -f makefile $<
Output:
make -C computations/ -f makefile computations/test.h
make[1]: Entering directory `/home/shai/csm/csm2/src/computations'
make[1]: *** No rule to make target `computations/test.h'. Stop.
make[1]: Leaving directory `/home/shai/csm/csm2/src/computations'
make: *** [libtest.so] Error 2
Well, it seems that it looks for %.h after switching library. No biggie, I'll just try removing
lib%.so: %.h
make -C computations/ -f makefile $<
but now it doesn't recognize the rule at all!
Output:
shai#ubuntu:~/csm/csm2/src$ make libtest.so
`make: *** No rule to make target `libtest.so'. Stop.
Is make playing with my mind? How could a change in the dependencies make it stop recognizing the target?
How could a change in the dependencies make it stop recognizing the target?
Most likely because it can't find a file that fits the pattern of the dependency, because test.h is in another directory. You could use VPATH to have it search for files in other directories.
Edit: but this still won't fix your problem completely, because $< will be substituted by computations/test.h, which won't be found in the directory computations (as in the first error you got). You might try $(notdir $<), but I think it's an ugly hack (if it works; I haven't tried it). This results from the design of your makefile and project structure.
You're using make wrong.
Make's design is actually fairly simple at heart: you tell it
how to create targets (The body of the rule).
which targets it needs to create first (The dependency list).
Make then topological sorts the dependency information so it executes the bodies in the right order.
Your rule with $< (which expands to the list of dependencies) means you're telling the invoked make to create the dependency %.h not the target lib%.so.
The rule has to create the target. If the dependency needs to be created, you need to tell make how to do so with another, separate rule, not try to ensure it is up to date in this rule.
As you're trying to coordinate across directories, I'll recommend the paper Recursive make considered harmful, which can often be a nice way to organize things, though not the only possible way to do things.
It might just be the way you copy/pasted it, but you're missing a tab in the second form.

Resources