You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何在Makefile中导出含shell命令的变量耗时极长?

问题描述

我的测试Makefile内容如下:

VERSION ?= $(shell cat VERSION | grep "VERSION" | cut -d'=' -f2)
VERSION_MAJOR ?= $(shell echo $(VERSION) | cut -d'.' -f1)
VERSION_MINOR ?= $(shell echo $(VERSION) | cut -d'.' -f2)
VERSION_PATCH ?= $(shell echo $(VERSION) | cut -d'.' -f3)

DATE ?= "$(shell date +"%Y-%m-%dT%H:%M")"

SOME_NESTED ?= "-X=aaa=$(VERSION_MAJOR) -X=bbb=$(VERSION_MINOR) -X=ccc=$(VERSION_PATCH) -X=ddd=$(DATE)"

#.EXPORT_ALL_VARIABLES:


t:
        echo "Test $(SOME_NESTED)"

测试用的VERSION文件通过以下命令生成:

echo "VERSION=1.2.3" > VERSION

原本Makefile运行正常,但取消注释.EXPORT_ALL_VARIABLES后,执行make t的耗时从约0.02秒骤增至近5秒。用make -d t查看日志时,发现大量递归展开相关信息,且命令显示后实际输出有明显延迟。我的Make版本是GNU Make 4.4.1,想了解该现象的原因以及是否存在不良用法。

原因分析

.EXPORT_ALL_VARIABLES会让Make把所有变量都导出给后续执行的shell进程,核心问题出在变量的递归展开特性:

  • 你定义的VERSION_MAJOR、VERSION_MINOR、VERSION_PATCH都是依赖VERSION的递归展开变量,SOME_NESTED又依赖这三个变量和DATE,而DATE本身也是带shell函数的递归展开变量。
  • 启用.EXPORT_ALL_VARIABLES后,Make需要为每一个要导出的变量完成完整递归展开,确保传递给shell的是最终值。这些变量里嵌套了多个shell函数,每一次展开都会触发新的shell进程执行命令(比如cat VERSION、date等)。
  • 递归展开还会导致变量被多次重复展开:比如展开SOME_NESTED时会触发VERSION_MAJOR的展开,VERSION_MAJOR又会触发VERSION的展开,VERSION再执行一次shell命令。大量重复的shell调用叠加后,就导致了耗时剧增。
不良用法与优化建议
  1. 避免无差别导出所有变量:除非确实需要把所有变量传递给子进程,否则不要用.EXPORT_ALL_VARIABLES,而是用export单独指定需要导出的变量,比如只导出SOME_NESTED:

    export SOME_NESTED
    
  2. 改用简单展开变量(:=)替代递归展开(=):对于不需要延迟展开的变量,用:=定义,这样变量会在定义时就完成一次性展开,避免后续重复触发shell命令。修改后如下:

    VERSION := $(shell cat VERSION | grep "VERSION" | cut -d'=' -f2)
    VERSION_MAJOR := $(shell echo $(VERSION) | cut -d'.' -f1)
    VERSION_MINOR := $(shell echo $(VERSION) | cut -d'.' -f2)
    VERSION_PATCH := $(shell echo $(VERSION) | cut -d'.' -f3)
    DATE := "$(shell date +"%Y-%m-%dT%H:%M")"
    SOME_NESTED := "-X=aaa=$(VERSION_MAJOR) -X=bbb=$(VERSION_MINOR) -X=ccc=$(VERSION_PATCH) -X=ddd=$(DATE)"
    

    这样即使启用.EXPORT_ALL_VARIABLES,变量也只会展开一次,不会重复执行shell命令,耗时会大幅降低。

  3. 简化shell命令:如果VERSION文件里只有一行VERSION=xxx的内容,提取VERSION的命令可以简化,去掉多余的grep:

    VERSION := $(shell cut -d'=' -f2 VERSION)
    

内容的提问来源于stack exchange,提问作者LingSamuel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 23:27:07