【问题标题】:force a script to run at the beginning of a makefile, and display its output强制脚本在生成文件的开头运行,并显示其输出
【发布时间】:2015-06-03 04:34:26
【问题描述】:

我有一个脚本,它会生成一个 version.h 文件,其中包含有关特定构建的各种详细信息

脚本运行各种命令,如:

readonly VERSION=$(git describe --always --dirty --long --tags)
readonly NUM_COMMITS=$(git rev-list HEAD | wc -l | bc)
readonly BRANCH=$(git rev-parse --abbrev-ref HEAD)
readonly AHEAD_BY=$(git log --oneline origin/${BRANCH}..${BRANCH} | wc -l | bc)
readonly NUM_UNTRACKED=$(git ls-files --exclude-standard --others --full-name -- . | wc -l | bc)
readonly HOSTNAME=$(hostname)

然后它会写入一个临时文件,检查预先存在的生成文件,如果它们不同,则用新文件覆盖旧的 version.h

#pragma once

/*
 * Version information
 *
 * - This is a generated file - do not edit
 */

namespace foo { namespace app {

static const char VERSION[]       = "26b75cd";
static const char NUM_COMMITS[]   = "224";
static const char BRANCH[]        = "master";
static const char AHEAD_BY[]      = "0";
static const char NUM_UNTRACKED[] = "0";
static const char USER[]          = "steve";
static const char HOSTNAME[]      = "steve-linux";
static const char BUILD_VARIANT[] = "debug";
static const char BUILD_DATE[]    = __DATE__ " "  __TIME__;

}}

文件更新时也会打印到标准输出

version updated: 26b75cd

版本脚本应该是第一个运行的东西,并且应该在每次调用 makefile 时运行。

目前我通过在makefile中使用简单扩展变量来实现这一点

new_ver := $(shell ./app/gen_version.sh $(BUILD))

它可以工作,但脚本的任何输出都在new_var 中捕获,我无法显示该输出。

这是一个可以接受的权衡(不显示新版本),但在理想情况下,我想显示脚本输出。

我不确定添加到默认 all 目标中的 .PHONY 目标是否有效,因为每个应用都有自己的目标,因此您可以构建它。

define do-make-bin
    binaries += $(addprefix $(BIN_DIR),$1)
    bin_sources += $2

    # a target so users can call 'make <bin-name>' and build only the bin and its dependencies
    $1: $(addprefix $(BIN_DIR),$1)

    # the target to build the actual binary
    $(addprefix $(BIN_DIR),$1): $(call src_to_obj,$2) $(addsuffix .so,$(addprefix $(LIB_DIR)lib,$3)) $(addsuffix .a,$(addprefix $(LIB_DIR)lib,$4))
    @$(CXX) $(call src_to_obj,$2) -L/usr/lib -L$(SDK_LIB_DIR) -L$(LIB_DIR) $(TARGET_LINK_FLAGS) -Wl$(,)-Bstatic $(addprefix -l,$4) $(addprefix -l,$6) -Wl$(,)-Bdynamic -Wl$(,)-rpath$(,)$(RPATH) $(linkpath) -L$(LIB_DIR) $(sdk_libs) $(tcmalloc_libs) -lpthread -lrt $(addprefix -l,$3) $(addprefix -l,$5) -rdynamic -o $$@
endef

调用make foo 仍应检查version.h 是否需要更新

【问题讨论】:

  • 这是最简单的方法。我不知道你说“更优雅”是什么意思。
  • @MadScientist 抱歉,我已经编辑了问题。鉴于您的回复,我意识到我要克服的问题是在分配后我无法echo $(new_ver)

标签: makefile gnu-make


【解决方案1】:

确保它在每个目标之前运行的唯一方法,如果你限制自己使用普通的 makefile 方法,就是将内容放在一个配方中,然后让构建该内容的目标成为 每个目标的先决条件 在你的 makefile 中。虽然这可以以这样的方式完成,它不会导致所有东西每次都重建,但它很糟糕。另外,它不会让你将输出放入像new_ver 这样的变量中(你说你想显示它,但你没有说你是否也需要在变量中)。

但是,如果唯一的问题是您没有显示 new_ver 的值, 很容易解决:

new_ver := $(shell ./app/gen_version.sh $(BUILD))
$(info $(new_ver))

【讨论】:

  • 谢谢 - 这么简单,我现在觉得很蹩脚! :)
  • 如果$(new_ver) 不为空,有没有办法只调用$(info ...)
  • 没关系 - 使用 ifdef new_ver 效果很好。再次感谢!
【解决方案2】:

当一个目标没有先决条件并且作为文件存在时,什么都不会发生。您需要的是在创建临时文件时做出条件先决条件。你可以使用gnu/make的wildcard函数。

如果临时文件被称为version.new.h 并假设脚本检查version.hversion.new.h 之间的差异。如果它们相同,则将其删除。然后你可以使用类似的东西:

version.h : $(wildcard version.new.h)
       mv -f version.new.h version.h

为了验证是否首先创建了 version.h,您应该拆分作业。 然后,您确保第一个作业首先运行。 Makefile 代码的示例是:

all: version_h other_staff

.PHONY: version_h
version_h:
    ./app/gen_version.sh $(BUILD)
    $(MAKE) version.h

version.h : $(wildcard version.new.h)
    mv -f version.new.h version.h

other_staff: $(EXE) $(LIBS)

【讨论】:

  • 感谢您的回复。将version_h 添加到all 仅在我们正在构建的目标是all 时才有效。无论目标是什么,我如何让它运行?
  • @SteveLorimer 如果all 是第一个目标,它将默认工作。正如@MadScientist 所提到的,最明显的反应是将其声明为prerequisite 对每个目标。我以为你已经知道了,你不想这样做,因为它的开销。所以我建议了上面的解决方法。无论如何,我将编辑完整的响应
猜你喜欢
  • 2010-11-28
  • 1970-01-01
  • 2015-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-13
相关资源
最近更新 更多