在 Makefile 函数中读取更新的文件

Reading updated file in the Makefile function

我想从文件 version 中读取一行的一部分(某个版本号)。此功能添加到 makefile 函数中。在此之前,我正在使用新版本号更新文件。但是当我尝试将该新版本分配给某个变量时,它正在读取旧版本号。

为了确保,我添加了 cat verison 命令来验证文件内容。

下面是我写的文件内容

文件:版本

me@linux:temp$ cat version 
PROC_V7.1.2.3

文件:生成文件

me@linux:temp$ cat Makefile 
include $(shell pwd)/$(wildcard *.mk)

.PHONY: firmware
firmware:
    $(call version_replace, 100)

文件:image.mk

me@linux:temp$ cat image.mk 
define version_replace
    cat version
    sed -i 's/V7/V177/g' version
    cat version
    $(eval NEW_VER:=$(shell cat version | cut -d '_' -f 2 ))
    echo $(NEW_VER)
endef

所以当我做 make firmware 时,新变量给出旧值 V7.1.2.3 而不是 V177.1.2.3

me@linux:temp$ make firmware 
cat version
PROC_V7.1.2.3
sed -i 's/V7/V177/g' version
cat version
PROC_V177.1.2.3
NEW_VER=V7.1.2.3
echo V7.1.2.3
V7.1.2.3

我的理解是,当调用 version_replace 函数时,首先执行的所有赋值和 NEW_VER 已经填充了旧版本号。

那么如何在 make file 函数中从文件中读取更新的版本号?

可能很难让 call 像您一样使用多个命令,所以我发现有时更容易(尽管可能不太便携)改为创建 shell 脚本:

生成文件

EXEC := $(shell sh script.sh)
FILE := new_version
VERS := $(shell cat $(FILE))

.PHONY: firmware
firmware:
    $(info $(VERS))

script.sh

#!/bin/bash

sed 's/V7/V177/g' version | cut -d '_' -f 2 > new_version
# optional # mv new_version version

您可以选择覆盖 version 文件,方法是取消注释脚本中的最后一行并将 Makefile 中的 FILE 变量更改为 version

$ make firmware
V177.1.2.3
make: Nothing to be done for `firmware'.

我会这样分开这两件事:

.PHONY: firmware
firmware: version
    # replace with firmware generation command that reads from $<
    @echo FIRMWARE $@ $<

.PHONY: version
version:
    # replace with version update command (sequence)
    @echo VERSION $@

构建 firmware 将产生以下序列:

$ make firmware
# replace with version update command (sequence)
VERSION version
# replace with firmware generation command that reads from version
FIRMWARE firmware version

编辑: 您对不同版本增量的要求应该是一个变量,用于控制 version: 目标的命令行,例如make VERSION_INCREMENT=200 firmware.

你的现象可以简化为一个简单但令人费解的例子:

MY_OUTPUT := First line
.PHONY: foo
foo:
    @echo $(MY_OUTPUT)
    $(eval MY_OUTPUT := $(shell $(info eval-time) echo Second line))
    @echo $(MY_OUTPUT)
    $(shell read -p "Hit return:")

输出:

$ make foo
eval-time
Hit return:

...waiting for a keystroke

First line
Second line

配方的评估是这样的:分配一个行缓冲区,这些行将保存没有 make 语法的最终配方命令行。然后遍历所有配方行,执行所有已知的 make 语法,从所有引号 ($$) 中删除一个级别,执行 $(eval) 操作并将行写入缓冲区。 然后 逐步执行生成的命令行列表,并将它们一一提供给 sh$(info) 告诉我们参数字符串已经预先评估,破坏了你的预期顺序。

这是 make 的行为。 make 将在执行之前扩展目标的配方。

配方$(eval NEW_VER:=$(shell cat version | cut -d '_' -f 2 ))将被扩展,变量NEW_VER将存储在变量列表中,其值为V7.1.2.3(来自版本文件)。由于版本文件中尚未更新版本。食谱 echo $(NEW_VER) 将替换为 echo V7.1.2.3

执行前,目标firmware的配方将是

cat version
sed -i 's/V7/V177/g' version
cat version
echo V7.1.2.3

因此最后的输出是打印旧版本。

To make this work, have two different targets. One for changing version in file and other for read it.

生成文件:

all: replace_version read_version

replace_version:
    cat version
    sed -i 's/V7/V177/g' version

read_version:
    cat version
    $(eval NEW_VER:=$(shell cat version | cut -d '_' -f 2 ))
    echo $(NEW_VER)

There is another possible method to make this work with single target. Change the sed command as expandable recipe

生成文件:

firmware:
        $(call version_replace, 100)

image.mk

define version_replace
        $(shell sed -i 's/V7/V177/g' version)
        $(eval NEW_VER:=$(shell cat version | cut -d '_' -f 2 ))
        echo $(NEW_VER)
endef