【问题标题】:Makefile assign command output to variableMakefile 将命令输出分配给变量
【发布时间】:2012-02-23 07:23:17
【问题描述】:

我有一个脚本可以压缩我的 css 文件并输出输出文件的文件名。
我正在尝试构建一个 makefile 来自动化该过程:

all:
    @echo "Compiling CSS"
    CSS_OUTPUT=$(shell php minify_css.php )
    echo $(CSS_OUTPUT)

我正在尝试将输出文件名存储在 CSS_OUTPUT 变量中,但我做错了,因为整个 makefile 只是打印:

$ make
abcdefg.css
Compiling CSS
CSS_OUTPUT=
echo 

所以输出没有分配给CSS_OUTPUT。另外,为什么在@echo "Compiling CSS"之前打印php输出?

我试过这个:

all:
    @echo "Compiling CSS"
    CSS_OUTPUT=$(shell echo php minify_css.php )
    echo $(CSS_OUTPUT)

但它只会变得更糟:

$ make
Compiling CSS
CSS_OUTPUT=php minify_css.php
./minify_css.php: line 1: ?php: No such file or directory
./minify_css.php: line 3: syntax error near unexpected token `dirname'
./minify_css.php: line 3: `require_once( dirname(__FILE__) . DIRECTORY_SEPARATOR . 'maintenance.php' );'
make: *** [css] Error 2

编辑 按照 cmets 中提供的答案,建议使用 eval:

@echo "Compiling CSS"
$(eval CSS_OUTPUT:=$(shell php minify_css.php))
echo ${CSS_OUTPUT}

输出:

make: *** No targets specified and no makefile found.  Stop.

【问题讨论】:

标签: makefile


【解决方案1】:

制作作品分两个阶段。首先,它读入 makefile,评估变量并构建依赖图。在此阶段也会评估 Make 函数。在第二阶段,它执行必要的配方来更新主要目标。这意味着$(shell ...) 函数调用在第一阶段被扩展,在任何配方运行之前。命令的输出被替换为 shell 函数调用,但我怀疑 php 命令的输出不会进入 STDOUT,所以不是以 CSS_OUTPUT=abcdefg.css(这将是你想要的)结束,abcdefg.css回显到屏幕上,shell 函数的结果为空。

接下来执行配方,但完成后,每一行都在单独的 shell 实例中运行,因此配方中不同行上的命令无法访问另一行上设置的 shell 变量(这在无论如何,由于上一个问题,这种情况下)。解决此问题的一种方法是通过以分号和反斜杠结束它们来将线条粘合在一起。现在它们在单个 shell 实例中作为一行执行。

另一个问题是,在配方的最后一行中,您没有引用 shell 变量(这就是 CSS_OUTPUT),而是引用了一个从未设置过的 makefile 变量。

你有什么理由不这样做:

all:
    @echo "Compiling CSS"
    php minify_css.php

【讨论】:

  • 我需要它的输出的原因是我想对它生成的文件进行操作(移动、删除、复制等),但我简化了 makefile 以暴露我的问题。我将尝试修改 php 脚本以输出到 STDOUT(我正在使用 echo`s)。
  • 有没有办法提前知道文件名?是自动生成的吗?我开始怀疑make 是否真的是最好的工具;从这个简化的 makefile 很难看出,但也许一个好的脚本更容易。
  • 文件名是自动生成的,有点随机
【解决方案2】:

问题与解决方案

我自己也遇到过同样的问题。我找到了一种解决方法,但它肯定不太理想。

如上所述,问题归结为$(shell) 命令的执行时间。它在生成文件变量被评估时执行,这是在任何目标被解析或处理之前。要解决此问题,您可以中断$(shell) 命令,使其在生成文件的单独调用中发生。为了变得棘手,我刚刚在同一个文件中为我的所有后文件创建工作创建了一个“隐藏”目标,并对我的同一个 makefile 进行了递归调用。结果如下所示:

override MY_MAKEFILE:=$(MAKEFILE_LIST)
override MY_MAKE_INVOCATION_CMD_LINE:=$(MAKE) -C $(CURDIR) $(if $(MY_MAKEFILE),-f $(MY_MAKEFILE),) --no-print-directory

all: minify_css.php
    @echo "Compiling CSS"
    @$(MY_MAKE_INVOCATION_CMD_LINE) all_hid

minify_css.php:
    <create the minify_css.php file here>    

all_hid:
    @$(MY_MAKE_INVOCATION_CMD_LINE) print_css_hid CSS_OUTPUT=$(shell php minify_css.php )

print_css_hid :
    echo $(CSS_OUTPUT)

说明

覆盖命令

此处理的第一行强制设置一个不能从命令行覆盖的变量以收集 makefile 的名称。第一行必须被称为 makefile 中的绝对第一行,因为包含其他 makefile 可以改变这一点,并且它在包含的 makefile 中不起作用。值得注意的是,如果您没有在命令行上指定 makefile,这将是空的,这意味着它可以在从 makefile 中调用时以相同的方式调用。

第二行只是通过设置一些默认选项来构造对 makefile 的调用,例如更改为最初调用 makefile 命令行的当前目录,并且仅包括要使用的 makefile 的规范(如果它包含在原始文件中)打电话。

请注意,前两行目前在我的构建系统中实现,因此它们实际上适用于对同一个 makefile 的多个嵌套递归调用。

递归调用

以下两行是回答您原始查询的重要行。为了演示文件创建而不是仅使用 shell 命令的返回,我假设您的文件还需要创建 minify_css.php 文件。当我对目标all_hid 的makefile 进行递归调用时,我知道目标all 正在被评估。这意味着minify_css.php 依赖已经被处理,所以我可以保证它已经存在,假设允许$(shell php minify_css.php) 命令在对makefile 的第一次递归调用中被评估并对生成的文件进行操作。下一步是收集CSS_OUTPUT 值并将其放入另一个递归调用中。为此,我再次从已经递归的调用中递归地调用 makefile,并将其传递到命令行。请记住,在调用 makefile 期间,只有那些显式导出的变量在子 make 中可用,因此我们不能只设置它然后在下一行进行递归调用,它实际上必须作为命令的一部分传递-线。在下一级递归 makefile 中,当我们调用 print_css_hid 目标时,该变量将显示为已从命令行设置并可以使用。

子制作注意事项

在这种情况下,您实际上是在递归调用自己的 makefile,因此必须担心您的所有其他变量都没有正确设置。幸运的是这不会发生。传递给原始命令行的选项的任何命令行值都会自动提供给从文件中调用的每个 make 子调用。由于唯一会影响 makefile 中变量构造的输入源是您正在操作的目录的内容、命令行设置、调用 makefile 的目录以及 makefile 本身中的设置,因此您只需以确保您不会在嵌套调用 make 期间不小心将新生成的文件包含在“源”列表中,并且您可以保证环境设置相同。

【讨论】:

    猜你喜欢
    • 2011-01-02
    • 1970-01-01
    • 2012-07-16
    • 2017-09-01
    • 1970-01-01
    • 2011-01-23
    • 2010-12-29
    • 2014-01-08
    • 2011-01-28
    相关资源
    最近更新 更多