为什么我的Makefile模式规则多次运行其配方?

时间:2017-10-24 15:29:16

标签: makefile gnu-make

根据the gnu make documentation,模式规则" ...配方只执行一次以制作所有目标。"但是,我有以下Makefile

.PHONY: entrypoint
entrypoint: test_1.cpp test_2.cpp

test_%.cpp:
    echo $@

运行make会产生:

echo test_1.cpp
test_1.cpp
echo test_2.cpp
test_2.cpp

我是新手,我可能会误解某些内容,但如果明确的话,文档似乎会产生误导。

$ make -v
GNU Make 4.0
...

2 个答案:

答案 0 :(得分:4)

您误读了文档。这意味着,假设将创建该规则中的所有目标模式,配方仅运行一次。

由于规则中只有一个目标模式(test _%。cpp`),因此make知道每次运行该配方时,它将创建一个与该模式匹配的输出文件。要创建与该模式匹配的不同目标,它将运行配方的多个实例。

如果您有这样的规则:

<div id="grid"></div>
<script>
$("#grid").kendoGrid({
  columns: [
    { field: "ID" },
    { field: "Checkbox", type: "boolean", template: '<input type="checkbox" style="margin-left: 4px;" class="checkone" />', editable: "false"},
    { field: "RollUp" },
    { field: "age" }
  ],
  editable: false,
  pageable: {
    input: true,
    numeric: false,
    refresh: true,
    pageSizes: [ 5, 10, 20, "all"],
    pageSize: 5,
  },
  dataSource: [
      { ID: "1", Checkbox: "false", RollUp: "Jane Doe", age: 30 },
      { ID: "2", Checkbox: "false", RollUp: "John Doe", age: 33 },
      { ID: "3", Checkbox: "false", RollUp: "Jane Doe", age: 30 },
      { ID: "4", Checkbox: "false", RollUp: "John Doe", age: 33 },
      { ID: "5", Checkbox: "false", RollUp: "Jane Doe", age: 30 },
      { ID: "6", Checkbox: "false", RollUp: "John Doe", age: 33 },
      { ID: "7", Checkbox: "false", RollUp: "Jane Doe", age: 30 },
      { ID: "8", Checkbox: "false", RollUp: "John Doe", age: 33 },
  ],
  dataBound: function (e) {
    var gridDataView = $("#grid").data().kendoGrid.dataSource.view();
    for (var i = 0; i < gridDataView.length; i++) {
      var panelApplicationId = gridDataView[i].Checkbox;
      if (ShouldBeChecked(panelApplicationId)) {
        $('#grid tr td input').eq(i).prop("checked", true);
      }
    }
  }
});

$(document).on('click', 'input.checkone', function (e) {
    var checked = $(this).prop("checked");
    var gridDataView = $("#grid").data().kendoGrid.dataSource.view();
    console.log(gridDataView);
    var idx = $(this).closest("tr").find("td:nth-child(1)").text();
    var gridData = $("#grid").data().kendoGrid.dataSource.data();
    for(var i = 0; i < gridData.length; i++) {
      if (gridData[i].ID == idx) {
        gridData[i].Checkbox = checked;
        break;
      }
    }
});

  function ShouldBeChecked(panelApplicationId) {
    var shouldBeChecked = false;
    var gridData = $("#grid").data().kendoGrid.dataSource.data();
    for (var i = 0; i < gridData.length; i++) {
      if (gridData[i].Checkbox == panelApplicationId) {
        if (gridData[i].Checkbox) {
          shouldBeChecked = true;
        }
        break;
      }
    }
    return shouldBeChecked;
  }

然后make会期望单个调用配方%.x %.y %.z : dothings 会创建与此模式匹配的所有目标(例如,dothingsfoo.xfoo.y)。

将此与这样的明确规则进行对比:

foo.z

在这里对待这个就像你写的那样:

foo.x foo.y foo.z :
        dothings

也就是说,要构建所有这三个目标,它将运行三次配方。

没有办法告诉make“请运行此配方一次,它将产生可能与模式foo.x : dothings foo.y : dothings foo.z : dothings 匹配的每个目标。”

答案 1 :(得分:0)

以下函数构造多目标依赖项的动态列表,其中不存在的文件最后命名。这或多或少是您给出的链接中名为“另一次尝试”的方法,除了它不会跳过丢失的文件,并且能够通过在命令行上将其作为目标来制作丢失的文件。它没有:如果其中一个多目标相对于其他目标已经过时,则执行多目标配方,但我认为这更多是想要的副作用而不是问题。唯一的缺点是语法丑陋,因为你必须把它写成一个eval表达式,它迫使你引用配方中的所有变量,这些变量应该在执行时进行评估。

define newline :=


endef
list2rules = $(firstword $1) $(if $(word 2,$1),: $(word 2,$1)$(newline)$(call list2rules,$(wordlist 2,1000,$1)))
multitarget = $(call list2rules,$(wildcard $1) $(filter-out $(wildcard $1),$1))

.PHONY: all

targets = test1 test2 footest3
#$(info $(call multitarget,$(targets)))

all: somefile

somefile: $(targets)
    touch somefile

# here we generate the dependency list on the spot. Only one recipe to update all targets.
$(eval $(call multitarget,\
$(targets)) : ; \
    touch $(targets) \
)