24

我想定义一个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更有用。

4

7 回答 7

15

此解决方案不依赖于外部临时文件,也不会强迫您修改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阅读和安全执行。

如果您愿意,最好的解决方案可能是将 shell 函数转换为实际程序。

于 2014-08-29T18:58:22.413 回答
7

由于问题被标记为gnu-make,您可能对 Beta 的解决方案感到满意,但我认为最好的解决方案是 1) 将函数重命名为test(避免使用lsand之类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

并不是说您必须在 的定义的每一行上都有行继续foo,并且$都被转义以便将它们传递给 shell。

于 2012-10-08T12:12:03.660 回答
7

我认为这不符合“优雅”的条件,但它似乎可以满足您的要求:

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

这依赖于这样一个事实,即定义在define和之间的宏在实际使用之前endef根本不会被解释make,因此您可以直接使用 shell 语法,并且不需要以反斜杠结束每一行。当然,如果你调用shell-functions宏,它会爆炸。

于 2012-10-08T17:00:19.470 回答
7

为什么不在 Makefile 中使用.ONESHELL并定义一个 Makefile 函数,如 bash 函数:

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
于 2018-11-01T03:38:28.933 回答
5

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

生成文件:

export ZDOTDIR := ./

SHELL := /usr/bin/env zsh

default:
    f

.zshenv,与 Makefile 相同的目录:

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

ZDOTDIR 变量使 zsh 在当前目录中查找点文件。然后你只要坚持你想要的.zshenv

$ make
f
CHECK OUT THIS AWESOME FUNCTION!
于 2013-03-01T01:06:17.003 回答
5

TL;博士:

在您的生成文件中:

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

长答案

我在非 make 上下文中使用 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来说并不难看

于 2016-06-06T12:26:10.983 回答
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)
于 2012-10-08T03:53:13.807 回答