У Make нет интуитивно понятного способа сделать это, но есть два достойных обходных пути.
Во-первых, если вовлеченные цели имеют общий ствол, вы можете использовать правило префикса (с GNU make). То есть, если вы хотите исправить следующее правило:
object.out1 object.out2: object.input
foo-bin object.input object.out1 object.out2
Вы могли бы написать это так:
%.out1 %.out2: %.input
foo-bin $*.input $*.out1 $*.out2
(Используя переменную правила шаблона $ *, которая обозначает совпавшую часть шаблона)
Если вы хотите быть переносимым на реализации не-GNU Make или если ваши файлы не могут быть названы так, чтобы соответствовать шаблонному правилу, есть другой способ:
file-a.out file-b.out: input.in.intermediate ;
.INTERMEDIATE: input.in.intermediate
input.in.intermediate: input.in
foo-bin input.in file-a.out file-b.out
Это говорит make, что input.in.intermediate не будет существовать до запуска make, поэтому его отсутствие (или его временная метка) не приведут к ложному запуску foo-bin. И независимо от того, является ли файл-a.out или file-b.out или оба устаревшими (относительно input.in), foo-bin будет запущен только один раз. Вы можете использовать .SECONDARY вместо .INTERMEDIATE, что даст команду make NOT удалить гипотетическое имя файла input.in.intermediate. Этот метод также безопасен для параллельных сборок.
Точка с запятой в первой строке важна. Он создает пустой рецепт для этого правила, поэтому Make знает, что мы действительно будем обновлять file-a.out и file-b.out (спасибо @siulkiulki и другим, кто указал на это)