如何在Makefile中定义全局shell函数?

时间:2012-10-08 03:00:54

标签: shell makefile gnu-make sh

我想定义一个shell函数

#!/bin/sh

test ()
{
  do_some_complicated_tests $1 $2;
  if something; then
    build_thisway $1 $2;
  else
    build_otherway $1 $2;
  fi
}

以这种方式我可以在我的Makefile的每个规则中使用它,例如:

foo: bar
  test foo baz

要清楚,我希望shell函数成为Makefile的一部分。这样做最优雅的方法是什么?如果您可以在不递归调用make的情况下执行此操作,则可获得奖励积分。


背景: 我的实际问题是make -n产生了一个非常长且不可读的输出。每个规则使用几乎相同的不可读shell命令序列,并且有许多规则。上述解决方案将使make -n的输出更有用。

7 个答案:

答案 0 :(得分:10)

此解决方案不依赖于外部临时文件,也不会强迫您修改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

ifneq…endif块检查命令行上的-n标志,并将TESTTOOL的扩展设置为: TESTTOOL,易于阅读并且可以安全执行。< / p>

最好的解决方案是将shell函数转换为实际程序,如果这是一个选项。

答案 1 :(得分:6)

由于问题标记为gnu-make,您可能对Beta的解决方案感到满意,但我认为最佳解决方案是1)将函数重命名为test以外的其他函数(避免使用{{1}这样的名称}和ls以及);为了讨论的目的,我们假设您已将其重命名为cd,2)只需编写一个名为foo的脚本并从Makefile中调用它。如果你真的想在Makefile中定义一个shell函数,只需为每个规则定义它:

foo

并非您必须在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 的定义的每一行上都有续行,并且foo都已转义,以便将它们传递给shell。

答案 2 :(得分:5)

我认为这不符合“优雅”的要求,但似乎可以做你想做的事情:

##
## --- 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

运行它会产生:

$ 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

使用-n运行它会产生:

$ make -f hack.mk -n
f 1 2 3 4
g a b c d

这取决于defineendef之间定义的宏在实际使用之前根本不被make解释的事实,因此您可以直接使用shell语法而不是不需要用反斜杠结束每一行。当然,如果你调用shell-functions宏,它会爆炸。

答案 3 :(得分:4)

有些东西告诉我你最好过滤make -n的输出,但你问的是可能的:

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)

答案 4 :(得分:4)

这真的是贫民窟,但无论如何。我使用zsh,但我确定有bash和sh等价物。

<强>生成文件:

export ZDOTDIR := ./

SHELL := /usr/bin/env zsh

default:
    f

.zshenv,与Makefile相同的目录:

f () { echo 'CHECK OUT THIS AWESOME FUNCTION!' }

ZDOTDIR变量使zsh在dotfiles的当前目录中查找。然后你只需在.zshenv中坚持你想要的东西。

$ make
f
CHECK OUT THIS AWESOME FUNCTION!

答案 5 :(得分:3)

<强> TL; DR:

在你的makefile中:

SHELL=bash
BASH_FUNC_command%%=() { ... }
export BASH_FUNC_command%%

答案很长

我在非制作语境中使用bash做了类似的事情。这里有效。

我在bash中声明了我的shell函数,然后使用:

导出它们
export -f function-name

如果在您的情况下从make调用相同的shell作为子进程,则它们自然可用。

示例:

$ # define the function
$ something() { echo do something here ; }
$ export -f something

$ # the Makefile
$ cat > Makefile <<END
SHELL=bash
all: ; something
END
$

试一试

$ make
something
do something here
$

这在环境中看起来如何?

$ env | grep something
BASH_FUNC_something%%=() { echo do something here

所以你的问题是如何在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

并尝试一下:

$ make
something-else
something else

在Makefile中有点难看但对 make -n

没有任何丑陋

答案 6 :(得分:2)

为什么不在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$ 

结果:

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