繁体   English   中英

如何将 makefile 目标更改为函数?

[英]How do I change a makefile target into a function?

我正在寻找一种干净的方法来将我在 makefile 中创建的一些目标声明更改为更具功能性的类型声明,在该声明中我能够传递变量并且输出将保持一致。

例如:

default: clean run1 run2

run1:
    for entity in $(FLIST_01); do \
        echo $entity ; \
    done

run2:
    for entity in $(FLIST_02); do \
         echo $entity ; \
    done

理想情况下,我想删除重复的运行目标声明并且只有 1 个。

FLIST_01 = my_flist.txt
FLIST_02 = other.txt

default: clean run

run:
    $(run_func $(FLIST_01))
    $(run_func $(FLIST_02))

如何在make创建自定义函数来执行 run_func 应该执行的操作(传递给它的文件列表变量的 for 循环读取?

更新:

我的尝试是这样的:

run:
    runfunc() { \
        echo "test" \
        for entity in $1; do \
            echo $(entity); \
        done \
    }

    runfunc $(FLIST_01)
    runfunc $(FLIST_02)

但是我在 do 行出现语法错误:syntax error near unexpected token `do'

通常,在 Makefile 中维护 shell 函数并不容易。 主要原因:

  • 每个 shell '$' 需要转义为 '$$'
  • $(...) 构造在 shell 和 Make 中具有不同的含义。
  • Makefile 中的每一行都由新的 shell 实例执行,这意味着变量分配、函数定义等在行之间不共享。

考虑几个选项:编写 shell helper 脚本(推荐),使用 make 用户定义的函数或将函数内联到 Make 中。 对于“回声”的具体情况,可能可以使用其他方法之一,请参阅“替代解决方案”

从个人经验来看,对于任何重要的函数,最好使用 shell 帮助脚本。 它们更容易开发和测试。

使用 Shell 帮助脚本

编写一个小助手脚本“print-list.sh”

#! /bin/bash
echo "test" \
for entity ; do
    echo $entity
done

然后从目标调用它

FLIST_01 = a.txt b.txt c.txt

t1:
        print-list.sh ${FLIST_01)

内联shell函数

如上所述,将 shell 函数嵌入 Makefile 需要匹配有关引用、转义等的 make 规则。调试脚本也需要付出很多努力,因为在此设置中错误消息非常神秘。

请注意,必须在使用该命令的每个操作行之前包含 RUNFUNC。

RUNFUNC = runfunc() { \
        echo "test" ; \
        for entity in $$*; do \
            echo $$entity; \
        done \
    } ;

FLIST_01 = a.txt b.txt c.txt

t2:
        $(RUNFUNC) runfunc ${FLIST_01)

请注意,该函数可以编写为一个 liner,或者使用 'define'/'endef' 来简化行尾转义。

使用 make 用户定义函数

可以使用 make 创建简单的函数。 此选项需要经验和时间。

UFUNC = \
        echo "test" ; \
        for entity in $1; do \
            echo $$entity ; \
        done


FLIST_01 = a.txt b.txt c.txt

t3:
        $(call UFUNC, ${FLIST_01))

替代方案

最后一个选项是使用现有的构造(假设该函数的唯一目标是将空格分隔的文件列表转换为换行符

t4:
        echo ${FLIST_01} | tr " " "\n"

首先,除非您指定.ONESHELL ,否则目标中的命令将在单独的子 shell 中运行。 因此,您的runfunc()声明将在声明该函数的 shell 中运行,然后退出。 下一条语句将在一个新的 shell 中运行,该 shell 对声明的函数一无所知,然后基本上被遗忘了。

其次,

echo "test" \
for entity in $1;

将被make扩展为

echo "test" for entity in ;

这显然包含多个错误。 你的意思是

echo "test"; \
for entity in $$1;

正确地将美元符号传递给外壳。 但总的来说,我会说你的方法是有缺陷的。 您可以将其重构为您最初希望的make函数;

define run
   for entity in $(1); do \
       echo $$entity; \
   done
endef

现在你可以这样称呼它

run:
    $(call run,$(FLIST_01))
    $(call run,$(FLIST_02))

但是这个特定的循环可以很容易地用一个 shell 语句替换。

run:
    printf '%s\n' $(FLIST_01)
    printf '%s\n' $(FLIST_02)

另一个双彭北。

首先,我们要扩大$FLIST_01的目标RUN1FLIST_02为目标RUN2。 一个干净的方法可能是:

FLIST_run1 := a b c
FLIST_run2 := 1 2 3

.PHONY: run1 run2
run1 run2:
    for entity in ${FLIST_$@}; do echo $entity; done

对于稍微模糊的解决方案,我们注意到您尝试做的事情可以用make的 noddy 模式匹配来描述:

.PHONY: run1 run2
run%: run1 run2:
    for entity in ${FLIST_$*}; do echo $entity; done

这里我们使用静态模式规则。 在配方中, $*扩展为与模式中的%匹配的任何内容。 这个解决方案对我黄疸的眼睛来说看起来很干净。

我敦促你寻找惯用的make而不是惯用的外壳。 您的 makefile 几乎总是会为此感谢您。

在第一种情况下,保留您的两个运行目标,而不是将它们合并到同一个配方中。

.PHONY: run1 run2
run%: run1 run2:
    for entity in ${FLIST_$*}; do echo $entity; done

.PHONY: run
run: run1
run: run2
run: ; echo $@ Success

在这里,你的实例运行首先进行了RUN1的配方,那么几招RUN2,终于为运行的配方。

这有什么好处吗? 当然。 如果你运行make -j2运行RUN1RUN2命令将并行运行。 make的全部意义。

最后一个问题是摆脱 shell 循环,并用单独的命令替换它。 这样, make将为您检查echo的退出代码,如果合适,甚至可以并行运行它们。 当然,在这个 noddy 示例中没有帮助,但它们可以是例如编译命令。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM