Makefile依赖关系不会触发重新编译?

时间:2019-07-29 08:52:38

标签: makefile

我要在

的makefile中声明非文件目标
  • 可以被文件目标用作依赖项,而不会导致始终被重建,并且
  • 仅当某些依赖关系已重建时才执行命令。

在最简单的情况下,这样的目标只是充当目标列表的“名称”。

但是,依赖于这种“名称”目标的任何文件目标将始终被重建,例如:

>> make --debug
   File 'PREPROCESS' does not exist.
  Must remake target 'PREPROCESS'.
  Successfully remade target file 'PREPROCESS'.
 Prerequisite 'PREPROCESS' of target 'config.ini' does not exist.
Must remake target 'config.ini'.

我能想到的唯一解决方法是使用touch $@创建一个虚拟文件,但这会使源目录中包含不需要的文件。

通常,makefile通过在文件的开头将一组文件定义为变量并在依赖项列表中使用该变量来解决此问题,但这不允许在此阶段结束时执行代码。

是否有更好的方法来命名一组依赖项?

示例生成文件

config.ini: PREPROCESS
    touch $@

.PHONY: clean

PREPROCESS: preproc.ini preproc.x
    : ./print_preprocess_report

preproc.ini:
    touch $@

preproc.x:
    touch $@

clean:
    rm -f preproc.ini preproc.x config.ini

2 个答案:

答案 0 :(得分:2)

也许有一种方法,但是我拥有的所有想法可能都毫无用处,难以理解和维护。

老实说,设计一个怪物Makefile只是为了避免创建空的时间戳文件,这对我来说似乎有些过激(但请看此答案的最后一部分)。您是否知道可以在任何位置创建这些文件,例如在源代码树的子目录中创建,以便于清理,甚至可以删除这些文件?示例:

# separate subdir of source tree
TAGSDIR    := .tags
# out of tree directory
# TAGSDIR  := /tmp/.tags
PREPROCESS := preproc.ini preproc.x

config.ini: $(TAGSDIR)/preprocess
    touch $@

.PHONY: clean

$(TAGSDIR)/preprocess: $(PREPROCESS)
    : ./print_preprocess_report
    mkdir -p $(@D)
    touch $@

preproc.ini:
    touch $@

preproc.x:
    touch $@

clean:
    rm -f $(PREPROCESS) config.ini $(TAGSDIR)

好的,现在,如果您真的坚持这样做,这是一个复杂的解决方案。它使用一个空的伪文件,但使用一个临时位置(/tmp),以便不污染您的源树。因为我们无法期望运行make时它会在那里,所以我们将在make解析Makefile时创建它,并为它提供最新PREPROCESS目标的最后修改时间。这应该可以按您期望的那样工作:

PREPROCESS := preproc.ini preproc.x
PP         := $(wildcard $(PREPROCESS))
TAGFILE    := /tmp/preprocess
ifneq ($(PP),)
NAMESTIMES := $(shell stat -c"%n %Y" $(PP) | sort -rnk2)
MOSTRECENT := $(firstword $(NAMESTIMES))
$(shell touch --reference=$(MOSTRECENT) $(TAGFILE))
endif

config.ini: $(TAGFILE)
    touch $@

$(TAGFILE): $(PREPROCESS)
    : ./print_preprocess_report
    @touch $@

.PHONY: clean

preproc.ini: foo
    touch $@

preproc.x: bar
    touch $@

clean:
    rm -f $(PREPROCESS) config.ini $(TAGFILE)

请注意,我向preproc.inipreproc.x添加了先决条件,以演示如果在它们已经存在的情况下对其进行重建会发生什么。演示:

$ touch foo bar
$ make
touch preproc.ini
touch preproc.x
: ./print_preprocess_report
touch config.ini
$ touch foo bar
$ make
touch preproc.ini
touch preproc.x
: ./print_preprocess_report
touch config.ini

答案 1 :(得分:1)

  

我想在makefile中声明非文件目标

     
      
  • 可以被文件目标用作依赖项,而不会导致始终被重建,并且
  •   
  • 仅当某些依赖关系已重建时才执行命令。
  •   

标准make不支持这种特性组合。如果您为目标定义了先决条件,并且该先决条件最初不存在,那么make 认为目标已过期。如果该前提条件的规则并未实际创建(并且该规则尚不存在),那么原始目标将始终被视为过时的。

如果您愿意专门依赖GNU make,那么您可以走得更远。 GNU make具有仅订单先决条件的概念,它满足您的第一个目标和第二个目标的“ if”部分,但不满足“ only if”。当遇到不存在的先决条件和为该目标提供配方的规则时,GNU make也会执行该配方。仅当先决条件已过时时,才能表达过时目标的想法。但这也许足够好。可能看起来像这样:

DEPS = preproc.ini preproc.x

# order-only prerequisites are separated from standard ones by a pipe symbol (|)
# ... and you probably do need regular prerequisites too, else the target is out of date
# only if it does not already exist at all.
config.ini: $(DEPS) | PREPROCESS
    touch $@

# marking PREPROCESS phony is appropriate, but it solves a different problem
.PHONY: clean PREPROCESS

PREPROCESS: $(DEPS)
    : ./print_preprocess_report

preproc.ini:
    touch $@

preproc.x:
    touch $@

clean:
    rm -f preproc.ini preproc.x config.ini

如果您想完全实现两个要点,则需要让位“非文件目标”部分。这基本上是问题中提出的解决方法,尽管在这种情况下,让目标创建 benefide 构建结果而不是虚拟文件也很有意义。例如,在示例makefile中,PREPROCESS的规则不仅可以将报告写入标准输出,还可以将报告写入名为PREPROCESS的文件。顺便说一句,这不需要使用仅订购的先决条件,因此它是可移植的。