I want to define a shell function
#!/bin/sh
test ()
{
do_some_complicated_tests $1 $2;
if something; then
build_thisway $1 $2;
else
build_otherway
I don't think this qualifies as "elegant", but it seems to do what you want:
##
## --- Start of ugly hack
##
THIS_FILE := $(lastword $(MAKEFILE_LIST))
define shell-functions
: BEGIN
# Shell syntax here
f()
{
echo "Here you define your shell function. This is f(): $@"
}
g()
{
echo "Another shell function. This is g(): $@"
}
: END
endef
# Generate the file with function declarations for the shell
$(shell sed -n '/^: BEGIN/,/^: END/p' $(THIS_FILE) > .functions.sh)
# The -i is necessary to use --init-file
SHELL := /bin/bash --init-file .functions.sh -i
##
## -- End of ugly hack
##
all:
@f 1 2 3 4
@g a b c d
Running this produces:
$ make -f hack.mk
Here you define your shell function. This if f(): 1 2 3 4
Another shell function. This is g(): a b c d
Running it with -n
produces:
$ make -f hack.mk -n
f 1 2 3 4
g a b c d
This relies on the fact that macros defined between define
and endef
are not interpreted by make
at all until they are actually used, so you can use shell syntax directly and you don't need to end each line with a backslash. Of course, it will bomb if you call the shell-functions
macro.
This solution does not rely on an external temporary file and does not force you to tinker with the SHELL
variable.
TESTTOOL=sh -c '\
do_some_complicated_tests $$1 $$2; \
if something; then
build_thisway $$1 $$2;
else
build_otherway $$1 $$2;
fi' TESTTOOL
ifneq (,$(findstring n,$(MAKEFLAGS)))
TESTTOOL=: TESTTOOL
endif
foo: bar
${TESTTOOL} foo baz
The ifneq…endif
block checks for the -n
flag on the command line and sets the expansion of TESTTOOL
to : TESTTOOL
which is easy to read and safe to execute.
The best solution could be to turn the shell function into an actual program if this is an option for you.
Something tells me you'd be better off filtering the output of make -n
, but what you ask is possible:
define test
@echo do some tests with $(1) and $(2); \
SOMETHING=$(1)_3 ; \
if [ $$SOMETHING == foo_3 ]; then \
echo build this way $(1) $(2); \
else \
echo build another way $(1) $(2) ; \
fi
endef
someTarget:
$(call test,foo,bar)
someOtherTarget:
$(call test,baz,quartz)
Since the question is tagged gnu-make
, you might be happy with Beta's solution, but I think the best solution is to 1) rename the function anything other than test
(avoid names like ls
and cd
as well); for the purpose of discussion let's assume you've renamed it foo
, 2) simply write a script named foo
and invoke it from the Makefile. If you really want to define a shell function in the Makefile, just define it for each rule:
F = foo() { \
do_some_complicated_tests $$1 $$2; \
if something; then \
build_thisway $$1 $$2; \
else \
build_otherway $$1 $$2; \
fi \
}
all: bar
@$(F); foo baz bar
Not that you must have line continuations on each line of the definition of foo
, and the $
are all escaped so that they are passed to the shell.
Why not using .ONESHELL in Makefile and define a Makefile function like bash function:
jeromesun@km:~/workshop/hello.test$ tree
.
├── Makefile
└── mkfiles
└── func_test.mk
1 directory, 2 files
jeromesun@km:~/workshop/hello.test$ cat Makefile
.ONESHELL:
-include mkfiles/*.mk
test:
@$(call func_test, one, two)
jeromesun@km:~/workshop/hello.test$ cat mkfiles/func_test.mk
.ONESHELL:
define func_test
echo "func_test parameters: 0:$0, 1:$1, 2:$2"
if [ ! -d abc ]; then
mkdir abc
echo "abc not found"
else
echo "abc found"
fi
endef
jeromesun@km:~/workshop/hello.test$
Result:
jeromesun@km:~/workshop/hello.test$ make test
func_test parameters: 0:func_test, 1: one, 2: two
abc not found
jeromesun@km:~/workshop/hello.test$ make test
func_test parameters: 0:func_test, 1: one, 2: two
abc found
This is really ghetto, but whatever. I use zsh, but I'm sure there are bash and sh equivalents.
Makefile:
export ZDOTDIR := ./
SHELL := /usr/bin/env zsh
default:
f
.zshenv, same directory as Makefile:
f () { echo 'CHECK OUT THIS AWESOME FUNCTION!' }
The ZDOTDIR variable makes zsh look in the current directory for dotfiles. Then you just stick what you want in .zshenv
.
$ make
f
CHECK OUT THIS AWESOME FUNCTION!