9

I have the following in my makefile. Its a GNUmakefile, so the additional make features are supported:

# Undefined Behavior Sanitzier (Clang and G++)
ifeq ($(findstring ubsan,$(MAKECMDGOALS)),ubsan)
CXXFLAGS += -fsanitize=undefined
MAKECMDGOALS := $(subst ubsan,,$(MAKECMDGOALS))
endif # UBsan    

Running it results in:

make ubsan
make: *** No rule to make target 'ubsan'. Stop.

I know the code path is being executed (by introducing an error in the block). How do I remove the target from the command goals?

jww
  • 97,681
  • 90
  • 411
  • 885

3 Answers3

6

You cannot do what you want here as far as I know.

You can modify the variable value and you will see the changes if you check the value yourself but modifying the value of MAKECMDGOALS will not affect make in any way.

If you look at Appendix A Quick Reference in the GNU Make Manual you will see that it says:

MAKECMDGOALS

The targets given to make on the command line. Setting this variable has no effect on the operation of make.

See Arguments to Specify the Goals.

The closest you could get to what you are trying to do here, I think, would be to re-execute make on the Makefile (or whatever) manually.

That being said this sort of thing is probably better done as variables instead of targets.

$ cat Makefile
$(info $(origin UBSAN))
$ make
undefined
$ make UBSAN=
command line

So something like this.

# Undefined Behavior Sanitzier (Clang and G++)
ifneq (undefined,$(origin UBSAN))
CXXFLAGS += -fsanitize=undefined
endif # UBsan
Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
  • Its part of the Crypto++ project. We decided to add `ubsan` and `asan` as first class targets (with the `CXXFLAGS` modifications): [Added targets for UBsan and Asan](http://github.com/weidai11/cryptopp/commit/04429b291d711655ba4803b76ad4fe2d7bc3ad55). It seemed easier than trying to pound a square peg into a round hole. (We even though about the environmental variable trick. But we did not want to `set`/`unset` them because it would get in the way of laziness). – jww Jul 16 '15 at 05:13
  • If you use a variable name that is custom to your project (`CRYPTOXX_UBSAN=` or `CRYPTOXX_SAN=ubsan`, etc.) you don't really need to unset the variable anywhere if you only use it in the toplevel makefile to add/etc. to `CXXFLAGS` but *shrug*. – Etan Reisner Jul 16 '15 at 13:39
  • 1
    **`You cannot do what you want here as far as I know.`** I managed to do it, see my answer: https://stackoverflow.com/a/58360632/4934640 – Evandro Coan Oct 13 '19 at 04:38
1

Yes, you can. Based on my recent answer to this other question Force Makefile to execute script after building any target (just before exiting), it was very easy to do what you would like.

This has only a drawback, you will lose any command-line arguments you pass directly to make as make all --silent. However, you can look into the question: How to detect if the makefile `--silent/--quiet` command line option was set?, and recapture the command-line arguments you would like to repass to the second make call command on make -f ${MAKEFILE_JUSTNAME} ${MAKECMDGOALS}.

ECHOCMD:=/bin/echo -e
SHELL := /bin/bash


ifeq (${IS_MAKEFILE_RUNNING_TARGETS},)

MAKEFILE_JUSTNAME := $(firstword ${MAKEFILE_LIST})

# UBsan
ifeq ($(findstring ubsan,${MAKECMDGOALS}),ubsan)
    MAKECMDGOALS := $(subst ubsan,,${MAKECMDGOALS})
    USELESS := $(eval export IS_UBSAN_CXXFLAGS_SET=1)
endif

define DEFAULTTARGET :=
    printf 'Calling "%s" "%s"\n\n' "${MAKEFILE_JUSTNAME}" "${MAKECMDGOALS}"
    make -f ${MAKEFILE_JUSTNAME} ${MAKECMDGOALS}

    printf '\n'
    printf 'Running something after all rules finished\n'
endef

%:
    @:
#   printf 'IS_MAKEFILE_RUNNING_TARGETS="%s"\n' "${IS_MAKEFILE_RUNNING_TARGETS}"
    $(if ${IS_MAKEFILE_RUNNING_TARGETS},,${DEFAULTTARGET})
    $(eval export IS_MAKEFILE_RUNNING_TARGETS=1)

all:
    @:
#   printf 'IS_MAKEFILE_RUNNING_TARGETS="%s"\n' "${IS_MAKEFILE_RUNNING_TARGETS}"
    $(if ${IS_MAKEFILE_RUNNING_TARGETS},,${DEFAULTTARGET})
    $(eval export IS_MAKEFILE_RUNNING_TARGETS=1)


else

# UBsan
ifneq (${IS_UBSAN_CXXFLAGS_SET},)
    CXXFLAGS += -fsanitize=undefined
endif

all:
    printf 'Calling my all rule, CXXFLAGS="%s"\n' "${CXXFLAGS}"

foo:
    printf 'Calling my foo rule, CXXFLAGS="%s"\n' "${CXXFLAGS}"

bar:
    printf 'Calling my bar rule, CXXFLAGS="%s"\n' "${CXXFLAGS}"

xyzzy:
    printf 'Calling my xyzzy rule, CXXFLAGS="%s"\n' "${CXXFLAGS}"


endif

Usage examples:

  1. make

    printf 'Calling "%s" "%s"\n\n' "Makefile" ""
    Calling "Makefile" ""
    
    make -f Makefile
    make[1]: Entering directory '/cygdrive/d/User/Downloads'
    printf 'Calling my all rule, CXXFLAGS="%s"\n' ""
    Calling my all rule, CXXFLAGS=""
    make[1]: Leaving directory '/cygdrive/d/User/Downloads'
    printf '\n'
    
    printf 'Running something after all rules finished\n'
    Running something after all rules finished
    
  2. make foo bar

    printf 'Calling "%s" "%s"\n\n' "Makefile" "foo bar"
    Calling "Makefile" "foo bar"
    
    make -f Makefile foo bar
    make[1]: Entering directory '/cygdrive/d/User/Downloads'
    printf 'Calling my foo rule, CXXFLAGS="%s"\n' ""
    Calling my foo rule, CXXFLAGS=""
    printf 'Calling my bar rule, CXXFLAGS="%s"\n' ""
    Calling my bar rule, CXXFLAGS=""
    make[1]: Leaving directory '/cygdrive/d/User/Downloads'
    printf '\n'
    
    printf 'Running something after all rules finished\n'
    Running something after all rules finished
    
  3. make foo bar ubsan

    printf 'Calling "%s" "%s"\n\n' "Makefile" "foo bar "
    Calling "Makefile" "foo bar "
    
    make -f Makefile foo bar
    make[1]: Entering directory '/cygdrive/d/User/Downloads'
    printf 'Calling my foo rule, CXXFLAGS="%s"\n' "-fsanitize=undefined"
    Calling my foo rule, CXXFLAGS="-fsanitize=undefined"
    printf 'Calling my bar rule, CXXFLAGS="%s"\n' "-fsanitize=undefined"
    Calling my bar rule, CXXFLAGS="-fsanitize=undefined"
    make[1]: Leaving directory '/cygdrive/d/User/Downloads'
    printf '\n'
    
    printf 'Running something after all rules finished\n'
    Running something after all rules finished
    
Evandro Coan
  • 8,560
  • 11
  • 83
  • 144
0

I am using the following pattern to pass parameters around, it has its limitations (only [a-z,0-9] characters) but can become quite handy:

ifeq (console,$(firstword $(MAKECMDGOALS)))
  CONSOLE_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS))
  $(eval $(CONSOLE_ARGS):;@:)
endif
console::
    run-some-command $(CONSOLE_ARGS)

For your case the following works:

# Undefined Behavior Sanitzier (Clang and G++)
ifeq (ubsan,$(firstword $(MAKECMDGOALS)))
  CXXFLAGS += -fsanitize=undefined
  $(eval $(CONSOLE_ARGS):;@:)
endif # UBsan