【问题标题】:Makefile dependency without declaring a rule?Makefile依赖而不声明规则?
【发布时间】:2020-08-20 23:45:31
【问题描述】:

我有一个模式规则,可以将打字稿文件中的类型定义转换为 JSON 模式文件。进行这种转换的程序需要两个参数:

  • source file 的名称
  • 要从该文件中提取的typename

我决定将所需的类型名编码为目标文件的名称。

<--------- Typename --------->        <------- source file ------->
BlockLanguageGeneratorDocument.json : block-language.description.ts

我定义了这个模式规则来进行转换:

%.json : %.ts
    # $^ is the name of the input file
    # $(notdir $(basename $@)) is the filename of the target file (without the .json suffix)
    $(TYPESCRIPT_JSON_SCHEMA_BIN) --path $^ --type $(notdir $(basename $@)) > "$@.json"

遗憾的是,与模式规则相比,我设置为依赖项的&lt;typename&gt;.json: &lt;sourcefile&gt; 规则更具体,因此模式规则永远不会执行。因此,我决定将转换封装在 define CONVERT_COMMAND 中,并在上述每个定义中简单地使用它:

BlockLanguageGeneratorDocument.json : block-language.description.ts
    $(CONVERT_COMMAND)

虽然这确实有效,但重复让我觉得很难看。有没有办法声明从一个文件到另一个文件的依赖关系,同时仍然更喜欢模式规则?

最小重现:使用make BlockLanguageGeneratorDocument.json Unrelated.json 运行此程序,观察echo 永远不会执行。

block-language.description.ts :
    touch $@

another.description.ts :
    touch $@

%.json : %.ts
    echo "generic target"

BlockLanguageGeneratorDocument.json : block-language.description.ts
Unrelated.json : another.description.ts

如果有帮助:调试输出如下。

❯❯❯ make --debug=verbose BlockLanguageGeneratorDocument.json                                                                                          
GNU Make 4.3
Built for x86_64-pc-linux-gnu
Copyright (C) 1988-2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Reading makefiles...
Reading makefile 'Makefile'...
Reading makefile 'Makefile.json' (search path) (no ~ expansion)...
Reading makefile '../../Makefile.common' (search path) (no ~ expansion)...
Updating makefiles....
Updating goal targets....
Considering target file 'BlockLanguageGeneratorDocument.json'.
  Considering target file 'block-language.description.ts'.
   Finished prerequisites of target file 'block-language.description.ts'.
  No need to remake target 'block-language.description.ts'.
 Finished prerequisites of target file 'BlockLanguageGeneratorDocument.json'.
 Prerequisite 'block-language.description.ts' is newer than target 'BlockLanguageGeneratorDocument.json'.
Must remake target 'BlockLanguageGeneratorDocument.json'.
Successfully remade target file 'BlockLanguageGeneratorDocument.json'.
make: 'BlockLanguageGeneratorDocument.json' is up to date.

【问题讨论】:

  • 我不明白。以BlockLanguageGeneratorDocument.json 为目标,您有多少条规则(模式与否)?如果你有不止一个,为什么会这样?注意:在您的模式规则的配方中,您可能应该重定向到 "$@" 而不是 "$@.json"

标签: makefile gnu-make


【解决方案1】:

没有看到minimal complete example 就很难回答这个问题,但我想我看到了问题所在。此模式规则:

%.json : %.ts
    ...whatever...

使用foo.ts 构建foo.json。 Make 不会尝试使用它来构建BlockLanguageGeneratorDocument.json,因为没有BlockLanguageGeneratorDocument.ts

我认为您正在寻找的是:

%.json :
    @echo do various things with $@ and $^

BlockLanguageGeneratorDocument.json : block-language.description.ts

请注意,第二条规则没有配方,它只是提供了先决条件。

【讨论】:

  • 为了未来,我添加了一个最小的完整示例,我被难住了:我的印象是,如果模式规则本身具有定义的依赖关系,我只能使用 $^。但这很有效,非常感谢!
  • 不,自动变量始终可用,您可以根据需要多次添加先决条件。但是,只有一个规则可以有一个 recipe。请注意,$^ 很可能是错误的自动变量。你想要$&lt;
【解决方案2】:

假设从依赖项到目标名称的映射定义明确,您可以编写该部分的脚本,将输出发送到 makefile 中——比如deps.mk——并将其包含在主 makefile 中。

名称映射似乎类似于...

<text-with-dashes>.description.ts --> <Capitalized text without dashes>.GeneratorDocument.json

以下 perl 脚本从其 arg 列表中读取每个 .ts 文件名并生成合适的依赖项...

#!/usr/bin/perl -w
# -*- perl -*-

use strict;

while (defined(my $file = shift(@ARGV))) {
    my($d) = ($file =~ m,^(.*)\.description\.ts,);
    my @d = split("-",$d);
    s/(.)/\u\L$1/ for @d;
    $d = join("",@d);
    print "\nall: $d"."GeneratorDocument.json\n$d"."GeneratorDocument.json: $file\n\t\$(CONVERT_COMMAND)\n";
}

如果我们将上面的脚本命名为mapname.pl 那么...

./mapname.pl block-language.description.ts

应该给出输出...

all: BlockLanguageGeneratorDocument.json
BlockLanguageGeneratorDocument.json: block-language.description.ts
        $(CONVERT_COMMAND)

现在您的主 makefile 将是...

ALL_TS_FILES := $(wildcard *.description.ts)
MAPNAME      := ./mapname.pl

all:

include deps.mk

deps.mk: $(ALL_TS_FILES) $(MAPNAME)
    $(MAPNAME) $(ALL_TS_FILES) > $@.tmp
    diff -q $@ $@.tmp || mv $@.tmp $@

【讨论】:

  • 遗憾的是,映射并不是那么简单,可能有多个类型名是从单个文件中生成的。
  • 我添加了一个最小的完整示例来说明这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-19
相关资源
最近更新 更多