gnu make - force exit without deleting the target - makefile

I'm incrementally verifying my build output and I want to be able to exit after a given recipe is executed.
If the original recipe is
$(HEADER_BUILD)/mpversion.h: FORCE | $(HEADER_BUILD)
$(Q)$(PYTHON) $(PY_SRC)/makeversionhdr.py $#
I want to be able to add one line at the end like so
$(HEADER_BUILD)/mpversion.h: FORCE | $(HEADER_BUILD)
$(Q)$(PYTHON) $(PY_SRC)/makeversionhdr.py $#
some_command
and some_command should merely stop the execution of the makefile without interfering with the rest of the recipe.
If I set some_command as exit 1, I get
../py/py.mk:269: recipe for target 'build-gnu/genhdr/mpversion.h'
failed make: * [build-gnu/genhdr/mpversion.h] Error 1 make: *
Deleting file 'build-gnu/genhdr/mpversion.h'
If I set some_command as $(error), the recipe isn't even executed even though it's BEFORE the $(error)
Is there such a command that stops executing the makefile but doesn't delete the target?
UPDATE
I've found this hack: make .PRECIOUS depend on the target and add exit 1 as the last line in the recipe.

If the file you want to keep is an intermediate file (not mentioned as target or dependency of a rule - but possibly implied via a pattern rule), then you'll need to make it a dependency of .PRECIOUS.
Otherwise, it should be sufficient to temporarily remove or comment out the .DELETE_ON_ERROR: target that we all put in every Makefile.

Related

Make: No main target

Is it possible to make make not have a main target, meaning that invocations to make without a specified target would fail, even if a first target exists?
Not specifically that, but if you are using GNU make there are ways to ensure users provide a goal on the command line.
First, you could check MAKECMDGOALS:
ifeq ($(MAKECMDGOALS),)
$(error Missing command line goal!)
endif
Or alternatively you could use .DEFAULT_GOAL to force the default goal to be a rule that fails:
.DEFAULT_GOAL = fail
fail:; #echo Missing command line goal; exit 1

How to let Makefile see target from another file

I have such Makefile with a content for creating a script:
.PHONY cluster-run
cluster-run:
make $(TARGET) --just-print >> tmp_script.sh;
And another one nn.mk:
.PHONY nn-model
include Makefile
nn-model:
python run-nn.py
I have two separate Makefiles for readability, because their content is big and I have another '*.mk' files, like nn-lstm.mk, nn-conv.mk, etc.
I launch as follows:
make -f nn.mk cluster-run TARGET=nn-model
But make gives an error:
make nn-model --just-print >> tmp_script.sh;
make[1]: *** No rule to make target `nn-model'. Stop.
make: *** [cluster-run] Error 2
For me such behaviour is strange because target nn-model actually exists. How can I fix this problem?
First you should never use raw make in recipes. Always use the $(MAKE) variable.
Second, the problem is because when you run the sub-make you don't provide the -f option:
make nn-model --just-print >> tmp_script.sh;
Because of that, it reads Makefile but not nn.mk, and so there's no rule to build the target nn-model.
Remember if you run a sub-make like this it's starting an entirely new make process with a clean slate: none of the targets defined in the parent make process are known to the sub-make when it starts.
I don't know what you mean by target nn_model actually exists but there's definitely no file named nn_model or you wouldn't get that error.
So what's happening is that when you build cluster-run it invokes a recursive make, which reads Makefile, and asks it to build $(TARGET) (which will include nn-model).
Notice that the recursive make is a new make and does not inherit variables or rules from the parent make, so this make instance has no clue how to build nn-model If you want the child make to see this, then the child make must include the parent one...

Makefile rule only works if file exists before make is invoked

Consider the following (MCVE of a) Makefile:
my_target: prepare test.bin
prepare:
echo >test.dat
%.bin: %.dat
cp $? $#
If you run make in a clean directory, it fails:
echo >test.dat
make: *** No rule to make target 'test.bin', needed by 'my_target'. Stop.
Run it again and it succeeds:
echo >test.dat
cp test.dat test.bin
What seems to happen is that the rule to make *.bin from *.dat only recognises that it knows how to make test.bin if test.dat exists before anything is executed, even though according to the output it has already created test.dat before it tries to create test.bin.
This is inconvenient for me as I have to prepare a few files first (import them from a different, earlier part of the build).
Is there a solution? Perhaps some way to allow the rules to be (re)evaluated in the light of the files which are now present?
There are a number of issues with your makefile. However based on your comments I'm inclined to assume that the MCVE here is just a little too "M" and it's been reduced so much that it has a number of basic problems. So I won't discuss them, unless you want me to.
The issue here is that you're creating important files without indicating to make that that's what you're doing. Make keeps internally a cache of the contents of directories that it's worked with, for performance reasons, and that cache is only updated when make invokes a rule that it understands will modify it.
Here your target is prepare but the recipe actually creates a completely different file, test.dat. So, make doesn't modify its internal cache of the directory contents and when it checks the cache to see if the file test.dat exists, it doesn't.
You need to be sure that your makefile is written such that it doesn't trick make: if a recipe creates a file foo then the target name should be foo, not bar.
This happens for wildcard targets, like %.bin. They get evaluated at the first pass. You could add an explicit target of test.bin. Or, follow the advice of tkausl and have test.dat depend on prepare (a phony target). In this case, you don't need the double dependency anymore:
my_target: test.bin
you have to write
test.dat: prepare
or (when when you want to stay with wildcards)
%.dat: prepare
#:
Usually, you might want to create and use .stamp files instead of a prepare target.

Using Make with force snippet to override other file

I've been trying to get a makefile, a, to include another makefile, b, if the target specified is not found in file a. I'm using this snippet to try and achieve this, but from echos I've put into the file I can see that makefile b is being accessed even when the target is found in a and run.
The snippet I'm using from the link above is:
foo:
frobnicate > foo
%: force
#echo "No target found locally, running default makefile"
#$(MAKE) -f Makefile $#
force: ;
Specifically I'm getting "Nothing to be done" outputs when makefile b is being used, and makefile a is behaving as expected. This is shown below:
$ make all # all target appears in both make files
No target found locally, running default makefile
make[1]: Entering directory `/home/user/currdir' # (b)
make[1]: Nothing to be done for `Makefile'.
make[1]: Leaving directory `/home/user/currdir'
Local all # (a)
Is there a better way to be doing this?
addition: After adding another echo to the % rule, I've found that $# is "Makefile", when it should be the target trying to be built.
I don't really understand your question based on the example you gave; there is no "a" or "b" in that example, just one Makefile.
However, the behavior you're seeing is due to GNU make's re-making makefiles capability. When you create match-anything pattern rules as you've done, you have to consider that every single target or prerequisite that make wants to build will match that rule. That's a large burden.
You can avoid having remade makefiles match by creating explicit rules for them, such as:
Makefile: ;

Specifying different GNUmakefile for GNU make command

I have two GNUmakefiles in my directory as follows,
GNUmakefile &
GNUmakefile2
Could someone please let me know the command I have to use, if I have to let the "make" command to process "GNUmakefile2" instead of "GNUmakefile".
I used the below command,
make -f GNUmakefile2
but in that case, I am getting the following errors,
This is gnustep-make 2.6.1. Type 'make print-gnustep-make-help' for help.
make[1]: ** No rule to make target `internal-master-tool-all'. Stop.*
make: ** [internal-all] Error 2*
I think it is considering GNUmakefile as makefile (when I use make with -f command), so it is checking for rules in GNUmakefile.
At present what I am doing is I am renaming the required file (which I want, make command to execute) to "GNUmakefile". And I am not getting any errors while executing "make" command, but I don't think this is the correct solution.
Please let me know which command I need to use for this scenario. Thanks for your help.
After checking Beta's solution (i.e.,but that makefile is invoking Make a second time, and the second Make process is probably reading GNUmakefile) what I have done is I renamed existing "GNUmakefile" to "GNUmakefile3".
So at present in my directory the following makefiles are present:- "GNUmakefile2" & "GNUmakefile3".
And then I executed the following command:- $ make -f GNUmakefile2
I recieved the below errors,
This is gnustep-make 2.6.1. Type 'make print-gnustep-make-help' for help.
make[1]: GNUmakefile: No such file or directory
make[1]: * No rule to make target `GNUmakefile'. Stop.
make: * [internal-all] Error 2
Please let me know what is the problem here
Your makefile includes two huge makefiles from the FSF. The second, library.make, contains this rule:
ifeq ($(GNUSTEP_MAKE_PARALLEL_BUILDING), no)
# Standard building
...
else
# Parallel building. ...
internal-library-all_:: $(GNUSTEP_OBJ_INSTANCE_DIR) $(OBJ_DIRS_TO_CREATE)
$(ECHO_NOTHING_RECURSIVE_MAKE)$(MAKE) -f $(MAKEFILE_NAME) ...
endif
and the first, common.make contains this assignment:
# The default name of the makefile to be used in recursive invocations of make
ifeq ($(MAKEFILE_NAME),)
MAKEFILE_NAME = GNUmakefile
endif
So try either make -f GNUmakefile2 GNUSTEP_MAKE_PARALLEL_BUILDING=no or make -f GNUmakefile2 MAKEFILE_NAME=GNUmakefile2, and see if that solves the problem.

Resources