13

I implement a recipe in order to pass all the remaining string to the command, as example in this script:

Makefile

run:
#   ./bin/run.sh $(filter-out $@,$(MAKECMDGOALS)) 
    @echo $(filter-out $@,$(MAKECMDGOALS))

But when I run as example:

>make run my custom input params
my custom input params
make: *** No rule to make target `my'.  Stop.

makefile try to execute also the remaining string so the error:

make: *** No rule to make target `my'.  Stop.

How can I prevent this?

NB: As workaround I define a dummy recipe:

%:
    @echo

So this will print an empty string instead of the error.

I want to avoid to do something like:

make run-example param="my custom param"
Matteo
  • 37,680
  • 11
  • 100
  • 115
  • 1
    `make` takes a list of targets on the command line. Maybe you are asking about how to change the list of targets? – Corion Jan 08 '19 at 11:01
  • 4
    From the (GNU) make man page: _make [OPTION]... [TARGET]..._ . Hence, you ask make to build the targets _run_, _my_, _custom_, _input_ and _params_. – user1934428 Jan 08 '19 at 11:02
  • 1
    @Corion i'm passing as list of target a list of params, so i don't want the makefile try to execute this list – Matteo Jan 08 '19 at 11:36
  • 1
    @user1934428 yes, exactly, I want to stop the execution of the other list after the first. I try with `exit` without any success – Matteo Jan 08 '19 at 11:37
  • @Matteo : I guess make is checking the existence of all targets initially. If you don't want make to build these targets, why do you write them on the command line of the make command? – user1934428 Jan 08 '19 at 12:02
  • I want to avoid to do something like: `make run-example param="my custom param"` – Matteo Jan 08 '19 at 12:09
  • 9
    But that's how `make` works. I think the best approach is to start working with make instead of against it. Maybe write a shell script wrapper that converts `matteo-make.sh run my custom param` to `make run param="my custom param"` ? – Corion Jan 08 '19 at 12:18
  • related: https://stackoverflow.com/questions/6273608/how-to-pass-argument-to-makefile-from-command-line/6273809 – kvantour Jan 10 '19 at 14:42

3 Answers3

10

You can probably achieve what you want with a match-anything rule. Example (using a dummy printf recipe instead of a real one):

PARAMS := $(filter-out run,$(MAKECMDGOALS))

run:
    @printf './bin/run.sh $(PARAMS)\n'

%:;

Demo:

$ make run my custom input params
./bin/run.sh my custom input params
make: 'my' is up to date.
make: 'custom' is up to date.
make: 'input' is up to date.
make: 'params' is up to date.

You can ignore the make: 'target' is up to date. messages or use the --quiet option (or --silent or -s):

$ make --quiet run my custom input params
./bin/run.sh my custom input params

If your Makefile is more complex than this, the match-anything rule could be a problem because it could catch other targets that you do not want to be caught. In this case make conditionals are an option:

ifeq ($(SECONDPASS),)
PARAMS := $(filter-out run,$(MAKECMDGOALS))

run:
    @$(MAKE) --quiet $@ PARAMS='$(PARAMS)' SECONDPASS=true

%:;
else
run:
    @printf './bin/run.sh $(PARAMS)\n'

# other rules if any
endif

Finally, if the name of the first goal is not always the same, you can adapt this with:

GOAL   := $(firstword $(MAKECMDGOALS))
PARAMS := $(filter-out $(GOAL),$(MAKECMDGOALS))

$(GOAL):
    @printf './bin/$(GOAL).sh $(PARAMS)\n'

%:;

Or:

GOAL   := $(firstword $(MAKECMDGOALS))

ifeq ($(SECONDPASS),)
PARAMS := $(filter-out $(GOAL),$(MAKECMDGOALS))

$(GOAL):
    @$(MAKE) --quiet $@ PARAMS='$(PARAMS)' SECONDPASS=true

%:;
else
$(GOAL):
    @printf './bin/$(GOAL).sh $(PARAMS)\n'

# other rules if any
endif

Demo:

$ make --quiet nur foo bar
./bin/nur.sh foo bar
Renaud Pacalet
  • 25,260
  • 3
  • 34
  • 51
  • There is not need to use `--quiet` to suppress the messages `make: 'target' is up to date`. You can define `@:` inside your target `run` and it will suppress that message. See an example and more references on [Force Makefile to execute script after building any target (just before exiting)](https://stackoverflow.com/a/58359772/4934640) – Evandro Coan Oct 13 '19 at 01:08
8

I don't think you should use a Makefile. You want to do your own parsing of the options, and that's more trouble to do in make.

If you're dead set on it, you could do this:

%:
    @true

...which will avoid printing an empty line.

It would be better to do this in Bash, though. Here's one way you could do it:

#!/usr/bin/env bash

if [ $# -lt 1 ]; then
    echo Not enough args
    exit 1
fi

case "$1" in
    "run")
        shift
        ./bin/run.sh $@
        ;;
    *)
        echo "Command $1 not recognized"
        exit 1
        ;;
esac

This seems easier and more extensible.

Nick ODell
  • 15,465
  • 3
  • 32
  • 66
2

You can always pass / set ENV variables before executing make if you only want to pass variables to make or to a shell script.

MYPARAM1=123 MYPARAM2=456 make

OR using a subshell

(MYPARAM1=123; MYPARAM2=456; echo A=$MYPARAM1 B=$MYPARAM2; make)
(export MYPARAM1=123; export MYPARAM2=456; A=$MYPARAM1 B=$MYPARAM2; make) // exporting

You might also look at the bash-shell-special-parameters

Gillsoft AB
  • 4,185
  • 2
  • 19
  • 35