Force exit from a Makefile target without raising an error
Asked Answered
F

5

31

I work with a Makefile generated by an external tool (Netbeans), where I can not change the logic of the main target, but I am able to "inject" logic in a target that is executed before the actual build (.build-pre to be specific in Netbeans-generated Makefile)

I would like for that target to conditionally terminate the make execution, but without raising an error. If I do

exit

inside the "pre" rule, nothing happens (I mean, the rule terminates, but make continues). If I add

exit 1

make will terminate, but it will return an error status.

Is there a way to force make to exit in a clean way? I searched for make functions, but found only error/warn/info, but nothing like exit.

Thanks in advance!

EDIT: Based on comments it does not seem possible. Pity.

For completeness, a more specific example of what I'd like to achieve:

default: pre
  @echo "Doing default"

pre: 
  @echo "Doing pre"
ifeq "$(SOME_VARIABLE)" "yes"
  exit 0
fi

With Makefile like above, I'd like to be able for pre to execute, and conditionally prevent 'default' from executing, but still return 0 to the shell.

Fiendish answered 4/11, 2013 at 17:34 Comment(9)
Like I said - I want to force exiting "make" without raising an error. I don't want to have Netbeans-generated rules continue in some cases.Fiendish
There is no way to do that. If you say make foo then make will try to make foo, and the only way it will stop making foo is if it sees an error. You can't ask it to give up without making foo and without generating an error. If this is some way not an accurate description of what you're trying to achieve please make your question more specific.Martelle
Thanks @MadScientist. I actually suspected it might be impossible, but if you don't ask, you don't learn, right? :) Thanks!Fiendish
@Marcin: I read you, but you are repeating strange technical steps you want. You still haven't stated your real problem – which probably has a completely different solution.Bugbear
@Bugbear OK, to be more specific. I use Netbeans-generated Makefiles, integrated with IDE. However, I also sometimes have ability to do faster compilation than with Netbeans-generated Makefiles (using unity builds), so I want to prevent the actual Netbeans-generated compilation logic from executing. I know I could do it if I switched to manually- or automake- generated Makefiles, but if what I described worked, I could just put my logic in "pre", and then exit. I know it's all a bit obscure, and it was a long shot. Thanks for all the help.Fiendish
There might still be a way, but almost by definition it would be by some horrible black magic. For instance, you could redefine variables so that the first command in the default recipe would expand to exit;..., or have the pre rule call a rule to temporarily rebuild the makefile itself with a default do-nothing rule. In short, you should talk to the person who imposed this protocol and explain the advantage of unity builds, or else accept the slow compilation and keep some crossword puzzles on hand.Jetton
I don't know how oversimplified your example is, but in this case it would be as easy as calling make pre. This doesn't even require modifying the makefile, but you could, if pre were in fact a long list of targets.Bugbear
@Bugbear Of course, it was a simplification. Problem is that how make is called is managed by Netbeans in this case, and also that the exit is only conditional on some external factors. Thanks everyone!Fiendish
DIFFERENT USE CASE: verifying a server state that does not have a filesystem manifestation - e.g. 'database up' or 'stop the database'. I have a target 'is the database up?' and when I'm trying to stop the database, I can check this by running $(MAKE) isDatabaseUp. If it returns false, I don't need to anything else in the target. I don't really want to (re-) run the shutdown sequence needlessly in this configuration.Bacitracin
R
27

You can have the all target do nothing if a variable is not set:

ifeq ($(SOME_VAR),)
$(info SOME_VAR not set!)
all:
else
all: target1 target2 targetetc
endif
Regime answered 15/5, 2014 at 16:53 Comment(1)
There are 3 possible functions - info, warning and error. For more information visit make's documentation about control functionDashed
F
6

What you are seeing happens because each line in a recipe runs in its own shell.

You can use .ONESHELL, so that all commands run in the same shell. That way, "exit" works:

.ONESHELL:

sayhi:
    @echo Hi
    exit
    @echo "I won't be printed"
Footwall answered 8/7, 2021 at 14:38 Comment(6)
wow how is it I never ran into ONESHELL before.. btw the "Bye" should not be printed ..?Discomfortable
@Discomfortable exactly, Bye won't be printed in this exampleCimex
You might change that to say "I won't be printed" rather than "Bye" . I tried ONESHELL and it did not work even after some fiddling. I'm not saying it can not work , but it's not simple in any caseDiscomfortable
@Discomfortable want to share what you tried and didn't work?Cimex
I dropped in a snippet of bash code and after about 30 minutes of tinkering it still was not happy. I do not remember the details. My position on this is - it does not just "drop in " to a make target - so I put it into a script instead.Discomfortable
FYI, .ONESHELL support was added in GNU Make 3.82Wines
I
4

Just expanding a bit on @Ken's excellent response. You can also do this:

ifeq ("test", "test")
postinstall:
    @echo "test = test"
else
postinstall:
    @echo "test != test"
endif
Isotone answered 14/9, 2017 at 4:15 Comment(0)
E
0

You can first get all the arguments passed into make (see below). E.g. make clean all => $(COMMAND) = 'clean' and $(FIRST_ARGUMENT) = 'build', suppose you have two targets, then this will trigger first make clean build, then make build.

COMMAND := $(firstword $(MAKECMDGOALS))
ARGUMENTS := $(filter-out --,$(filter-out $(firstword $(MAKECMDGOALS)),$(MAKECMDGOALS)))
EXECUTABLE := $(firstword $(ARGUMENTS))
FIRST_ARGUMENT := $(word 1, $(ARGUMENTS))
SECOND_ARGUMENT := $(word 2, $(ARGUMENTS))
THIRD_ARGUMENT := $(word 3, $(ARGUMENTS))

Next, because you want to stop make from triggering make build after make clean build is done, you can do the following to the build target.

build:
    @if [ "$(COMMAND)" = clean ]; then \
        exit 0; \

make will execute this script under target build and see that indeed the value of $(COMMAND) is 'clean' and exit immediately.

You must also have the .ONESHELL: target in the Makefile to tell make to use only one shell.

Electrokinetics answered 12/7, 2024 at 18:38 Comment(0)
S
-1

Instead of exit 0 try $(eval $(exit 0))

Stone answered 30/8, 2023 at 0:25 Comment(2)
how is that any different?Elman
@Elman No idea tbh, it just worked when I was stuck on the same issue.Stone

© 2022 - 2025 — McMap. All rights reserved.