Hol*_*ger 17 shell makefile sh gnu-make
我想定义一个shell函数
#!/bin/sh
test ()
{
do_some_complicated_tests $1 $2;
if something; then
build_thisway $1 $2;
else
build_otherway $1 $2;
fi
}
Run Code Online (Sandbox Code Playgroud)
以这种方式,我可以在我的Makefile的每个规则中使用它,例如:
foo: bar
test foo baz
Run Code Online (Sandbox Code Playgroud)
要清楚,我希望shell函数成为Makefile的一部分.这样做最优雅的方法是什么?如果您可以在不递归调用make的情况下执行此操作,则可获得积分.
背景:我的实际问题是make -n
产生一个非常长且不可读的输出.每个规则使用几乎相同的不可读shell命令序列,并且有许多规则.上述解决方案将使输出make -n
更有用.
Mic*_*ald 12
此解决方案不依赖于外部临时文件,也不会强制您修改SHELL
变量.
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
Run Code Online (Sandbox Code Playgroud)
的ifneq…endif
块检查所述-n
命令行上的标志,并设置的扩张TESTTOOL
到: TESTTOOL
其易于阅读和安全执行.
如果这是一个选项,最好的解决方案是将shell函数转换为实际程序.
为什么不在Makefile 中使用.ONESHELL并定义像 bash 函数这样的 Makefile 函数:
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$
Run Code Online (Sandbox Code Playgroud)
结果:
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
Run Code Online (Sandbox Code Playgroud)
由于这个问题被标记gnu-make
,你可能会很乐意与Beta版的解决方案,但我认为最好的办法是:1)重命名以外的任何功能test
(避免这样的名字ls
,并cd
为好); 为了讨论的目的,让我们假设你已经重命名了它foo
,2)只需编写一个名为的脚本foo
并从Makefile中调用它.如果您真的想在Makefile中定义shell函数,只需为每个规则定义它:
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
Run Code Online (Sandbox Code Playgroud)
并不是说你必须在定义的每一行都有行继续foo
,并且$
都被转义以便它们被传递给shell.
长话短说:
在你的 makefile 中:
SHELL=bash
BASH_FUNC_command%%=() { ... }
export BASH_FUNC_command%%
Run Code Online (Sandbox Code Playgroud)
长答案
我已经在非 make 上下文中用 bash 做了类似的事情,在这里工作。
我在 bash 中声明了我的 shell 函数,然后使用以下命令导出它们:
export -f function-name
Run Code Online (Sandbox Code Playgroud)
如果同一个 shell 作为子进程(在您的情况下是从 make 调用),那么它们自然可用。
例子:
$ # define the function
$ something() { echo do something here ; }
$ export -f something
$ # the Makefile
$ cat > Makefile <<END
SHELL=bash
all: ; something
END
$
Run Code Online (Sandbox Code Playgroud)
试试看
$ make
something
do something here
$
Run Code Online (Sandbox Code Playgroud)
这在环境中看起来怎么样?
$ env | grep something
BASH_FUNC_something%%=() { echo do something here
Run Code Online (Sandbox Code Playgroud)
因此,您面临的问题是如何从 Makefile 中设置环境变量。该模式似乎是 BASH_FUNC_ function-name %%=() { function-body }
直接在 make 内
那么这对你有什么作用呢?试试这个 makefile
SHELL=bash
define BASH_FUNC_something-else%%
() {
echo something else
}
endef
export BASH_FUNC_something-else%%
all: ; something-else
Run Code Online (Sandbox Code Playgroud)
并尝试一下:
$ make
something-else
something else
Run Code Online (Sandbox Code Playgroud)
Makefile 中有点难看,但make -n并不难看
我认为这不算“优雅”,但似乎可以满足您的要求:
##
## --- 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
Run Code Online (Sandbox Code Playgroud)
运行此命令将产生:
$ 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
Run Code Online (Sandbox Code Playgroud)
运行它-n
会产生:
$ make -f hack.mk -n
f 1 2 3 4
g a b c d
Run Code Online (Sandbox Code Playgroud)
这依赖于这样的事实,在define
和之间定义的宏在实际使用之前根本endef
不会被解释make
,因此您可以直接使用shell语法,而无需在每行后面加反斜杠。当然,如果您调用shell-functions
宏,它将炸弹。
这真的是贫民窟,但无论如何。我使用 zsh,但我确定有 bash 和 sh 等价物。
生成文件:
export ZDOTDIR := ./
SHELL := /usr/bin/env zsh
default:
f
Run Code Online (Sandbox Code Playgroud)
.zshenv,与 Makefile 相同的目录:
f () { echo 'CHECK OUT THIS AWESOME FUNCTION!' }
Run Code Online (Sandbox Code Playgroud)
ZDOTDIR 变量使 zsh 在当前目录中查找点文件。然后你只要坚持你想要的.zshenv
。
$ make
f
CHECK OUT THIS AWESOME FUNCTION!
Run Code Online (Sandbox Code Playgroud)