Make is deleting my target. Why? - makefile

From the docs:
Usually when a recipe line fails, if it has changed the target file
at all, the file is corrupted and cannot be used--or at least it is not
completely updated. Yet the file's time stamp says that it is now up to
date, so the next time 'make' runs, it will not try to update that file.
The situation is just the same as when the shell is killed by a signal;
*note Interrupts::. So generally the right thing to do is to delete the
target file if the recipe fails after beginning to change the file.
'make' will do this if '.DELETE_ON_ERROR' appears as a target. This is
almost always what you want 'make' to do, but it is not historical
practice; so for compatibility, you must explicitly request it.
So, here I have a makefile:
# The idea here is to auto-generate the file ('make.include')
# and to use it as a makefile.
# For simplicity, I replaced the "auto-generate" part, with "touch".
# I also simplified the dependency-tree with 'phony'.
# In practice, we re-generate, only when "need" to.
make.include : phony
+touch '$#'
make -f '$#'
.PHONY: phony
Running:
$ make -q
I get:
touch 'make.include'
make: *** Deleting file 'make.include'
Now, i don't see how to prevent make from deleting this newly auto-generated 'make.include' (which may well be quite a costly process to re-run), unless i resort to the .PRECIOUS special target.
But, demanding the user to explicitly define their "precious" targets, is not in-line with that quote from the docs above. Right?

This arises because of your use of + in +touch '$#' and executing make with the -q option. From the GNU Make Manual the -q option is
“Question mode”. Do not run any recipes, or print
anything; just return an exit status that is zero if the specified
targets are already up to date, one if any remaking is required, or
two if an error is encountered. See Instead of Executing Recipes.
The + symbol is only relevant when make is executed with the -t, -n or -q options and it tells make to execute any commands it precedes make is executed with any of the options -t, -n or -q. So, when you execute your current makefile with make -q you are asking make to check if everything is up to date without running any commands but because you've specified + before touch '$#' make must execute this command. To then leave everything the way it was before you executed make -q make must delete the file it created with +touch '$#'.
To answer your question specifically. If you don't want make to delete make.include then you can run make without the -q option on the makefile specified in your question.
However, the recipe for a target, i.e. make.include should not call its self with make -f make.include. It would probably be better to rewrite the makefile so that the recipe for make.include only creates make.include and it is then called with make -f make.include in another recipe.
make.include:
+touch $#
all: make.include
make -f $<

Related

Modify working makefile to do pre-stage before compiling and linking

I'm using this makefile as an example:
https://gist.github.com/mihaitodor/bfb8e7ad908489fdf3ceb496573f306a
Before compiling/linking I need to do a pre-stage, consisting of cd to a directory and run a script. Without the pre-stage it compiles & links fine.
I think I need to change the all rule:
all : $(TESTS)
I have tried this:
all : cd /bla/bla ./my_script $(TESTS)
and I have tried this:
all :
cd /bla/bla ./my_script
$(TESTS)
but it stops the compile/linking stage.
Given the URL above, where should I insert my pre-stage?
The short answer is you should likely create a new recipe, and make any bottom level target that is dependent on your script running be dependent on it. Something like:
.ran_my_script: $(GTEST_SRCS_)
cd /bla/bla ./my_script
touch $#
test_mihai.o gtest_main.o gtest.o : .ran_my_script
This way it will run your script before it attempts to generate any of the listed targets. It then touches a file $# (.ran_my_script). This will then be newer than any of the .o files meaning the script will not rerun unless someone modifies one of the scripts dependencies (i.e. $(GTEST_SRCS_)). You'll have to figure out what artifcats are actually dependent on your script, and what artifacts your script is dependent on.
Notice that if any of the sources change, then .ran_my_script will be considered out of date, as will anything that depends on it. This means that if you modify any source, it will rebuild all .o files. I'm assuming this is what you want.
Because I'm assuming you're new to makefiles, I will point out two things: first $# resolves to the target name (.ran_my_script in the above example), and second, that the last line of this causes .ran_my_script to be a dependency of test_mihai.o and friends -- but because it does not have any recipes associated with it, this line does not override any other recipes against the same targets specified prior to or later on in the makefile.
As to why what you were doing doesn't work:
all: cd /bla/blah ./my_script $(TESTS)
indicates that the target all is dependent on the targets cd, /bla/bla, ./my_script, and $(TESTS). It will attempt to build all of these before it builds all. Of course, you likely do not have a rule to build cd, etc, so this will fail.
Your next example,
all :
cd /bla/bla ./my_script
$(TESTS)
You create a target all, with two recipes. First, it will attempt to run your script, and then it will attempt to run whatever $(TESTS) resolves to. Notice that $(TESTS) in this case is not a bash command, so this would fail as well.
You might be tempted to do something like this:
all : $(TESTS)
cd /bla/bla ./my_script
but this would cause your script to be run as part of the all target, which runs after everything in $(TESTS) has already been completed.

Force run a recipe (--assume-old=target)

I want to force a recipe for "output.file", even though it is up-to-date.
I have already tried make --assume-old=output.file output.file, but it does not run the recipe again.
In case you are curious: use case:
I want to use this together with --dry-run to find out the command that produce a target.
I ended up hiding the file to run make --dry-run output.file, but I was hoping for something more elegant + FMI: for future debugging makefile.
I think you're misunderstanding what that option does: it does exactly the opposite of what you hoped; from the man page:
-o file, --old-file=file, --assume-old=file
Do not remake the file file even if it is older than its dependen‐
cies, and do not remake anything on account of changes in file.
Essentially the file is treated as very old and its rules are
ignored.
You want output.file to be remade, so using -o is clearly not what you want.
There is no option in GNU make to say "always rebuild this target". What you can do is tell make to pretend that some prerequisite of the target you want to be rebuilt has been updated. See this option:
-W file, --what-if=file, --new-file=file, --assume-new=file
Pretend that the target file has just been modified. When used
with the -n flag, this shows you what would happen if you were to
modify that file. Without -n, it is almost the same as running a
touch command on the given file before running make, except that
the modification time is changed only in the imagination of make.
Say for example your output.file had a prerequisite input.file. Then if you run:
make -W input.file
it will show you what rules it would run, which would include rebuilding output.file.

Ignore clean command in Makefile

I have a project to compile with a lot of makefiles including a clean command. That's why i always have to start over again if there is an error (because the clean command is called in many Makefiles)
Question:
Is there any possibility to tell make to ignore the clean command? I would have to touch > 100 Makefiles otherwise. I would like make to start on the last error, not compiling all done stuff again
Example Makefile entries:
clean: cleansubdirs $(DIR) $(DIR1)
$(DIR2)
It's possible to redefine the recipe of an explicit target as simple as that:
noclean.mk
clean:;
cleansubdirs:;
# more stuff...
Now run make -f Makefile -f noclean.mk and it will work without actual cleaning files. However, make will issue several warnings about "overriding/ignoring old recipes".

always run a script when running 'make' before compiling

I'm using automake.
I'd like to have a script run each time I run 'make'.
This script does a git diff and generates an MD5 sum of the diff.
The hash is written as a #define in repos_version.h
e.g.:
#define REPOS_DIFF "-190886e9f895e80c42cf6b426dc85afd"
The script only rewrites this file if it doesn't exist or if the diff has is different to what is in repos_version.h already. But the script needs to be run for each make.
main.c includes repos_version.h and prints out the hash when the executable is run.
Here's Attempt 1 for Makefile.am
all: config.h
#chmod +x gen_diff_hash.sh
#./gen_diff_hash.sh
$(MAKE) $(AM_MAKEFLAGS) all-recursive
This work, but I get the following error
Makefile:1234: warning: overriding recipe for target all'
Makefile:734: warning: ignoring old recipe for targetall'
Here's Attempt 2 for Makefile.am
all-local:
#chmod +x gen_diff_hash.sh
#./gen_diff_hash.sh
main.c: repos_version.h
However, this doesn't work, as all-local seems to be run too late. A second run of 'make' does get the desired result, but that's not a runner.
So neither are great.
Any ideas?
I've been reading through the automake hooks documentation, but I can't see anything that suits my needs.
You could ensure the script is always run every time Make loads the Makefile, by executing it via $(shell ./gen_diff_hash.sh) and assigning it to a throwaway variable (or using it in some other construct like an ifeq or something).
Note, that this is not POSIX, and on Make implementations other than GNU this isn't valid syntax. GNU Make 4.x supports using VAR != ./gen_diff_hash.sh as well, which is compatible with BSD Make at least.
But maybe it would be a better idea to create a .PHONY: gendiff target that runs the script, and make the header depend on this gendiff. The target would then be re-evaluated every time Make checks if repos_version.h is up-to-date, rather than every time Make is run at all.

How to structure makefile where items are dependent on a file created by an external script?

I have a makefile for a project that includes Curl, and several of our items are dependent on this Curl library. On our platform, Curl provides its own non-Make build script that builds to a set location. When the Curl build script is called, it only creates a new Curl library if it needs to; otherwise it exits with the existing artifact untouched.
How can I structure my project's makefile to call the Curl build script, but then only recompile our stuff if the Curl library was actually rebuilt? I'm currently doing this, but it doesn't work as intended:
.PHONY: CURL.7
CURL.7:
#cd $(CURL_PATH); sh makefile.sh
#cp $(CURL_PATH)/CURL.7 $(TGT_DIR)
exp47: exp47.c CURL.7
This unconditionally calls the Curl build script (due to PHONY statement) and then unconditionally copies its output to our project directory. But then 'exp47' is unconditionally recompiled. Is there a way to invoke the Curl recipe every time but then only rebuild 'exp47' if the Curl artifact is actually recreated?
Note it's .PHONY, not PHONY. If you declare CURL.7 to be .PHONY, then it will always be run and always be considered out of date, and everything that depends on it will be considered out of date too. Also since you're always doing a copy operation the target curl file will always be new, and since no one ever actually creates the file CURL.7 (you create $(TGT_DIR)/CURL.7 which is not the same thing) that rule will always run.
You can do this by putting a "buffer" rule between them, where the target only changes if the library changes. Try this:
.PHONY: try-CURL.7
try-CURL.7:
#cd $(CURL_PATH) && sh makefile.sh
$(TGT_DIR)/CURL.7: try-CURL.7
#cp -p $(CURL_PATH)/CURL.7 $#
exp47: exp47.c $(TGT_DIR)/CURL.7
By using the -p flag in the cp you preserve the timestamp when you do the copy, so that if it wasn't changed by the CURL_PATH makefile.sh it won't be changed for you either, and that should mean that targets that depend on it won't be updated.
Note I didn't actually test this so it could be I forgot something.

Resources