Я пытаюсь сделать следующее. Есть программа, назовите ее foo-bin
, которая принимает один входной файл и генерирует два выходных файла. Глупое правило Makefile для этого будет:
file-a.out file-b.out: input.in
foo-bin input.in file-a.out file-b.out
Однако это никоим образом не означает, make
что обе цели будут созданы одновременно. Это нормально при make
последовательном запуске , но, скорее всего, вызовет проблемы, если кто-то попытается make -j16
или что-то столь же безумное.
Вопрос в том, существует ли способ написать правильное правило Makefile для такого случая? Ясно, что это сгенерирует DAG, но почему-то в руководстве GNU make не указано, как этот случай может быть обработан.
Выполнить один и тот же код дважды и получить только один результат не может быть и речи, потому что вычисление требует времени (подумайте: часы). Вывод только одного файла также был бы довольно трудным, потому что часто он используется как вход для GNUPLOT, который не знает, как обрабатывать только часть файла данных.
Ответы:
Я бы решил это следующим образом:
В этом случае параллельная программа make будет «сериализовать», создавая a и b, но поскольку создание b ничего не делает, это не требует времени.
источник
file-b.out
был создан, как указано, а затем загадочно удален,make
его невозможно будет воссоздать, потому чтоfile-a.out
он все еще будет присутствовать. Есть предположения?file-a.out
то вышеуказанное решение работает хорошо. Это предполагает взлом: когда существует только один из a или b, тогда зависимости должны быть упорядочены так, чтобы отсутствующий файл отображался как результатinput.in
. Несколько$(widcard...)
s,$(filter...)
s,$(filter-out...)
s и т. Д. Должны помочь. Тьфу.foo-bin
, очевидно, сначала создаст один из файлов (с использованием разрешения в микросекундах), поэтому вы должны убедиться, что у вас есть правильный порядок зависимостей, когда существуют оба файла.touch file-a.out
в качестве второй команды послеfoo-bin
вызова.touch file-b.out
в качестве второй команды в первое правило и продублируйте команды во втором правиле. Если какой-либо файл отсутствует или старшеinput.in
, команда будет выполнена только один раз и восстановит оба файла сfile-b.out
более поздней версией, чемfile-a.out
.Уловка состоит в том, чтобы использовать шаблонное правило с несколькими целями. В этом случае make предположит, что обе цели созданы одним вызовом команды.
Эта разница в интерпретации между шаблонными и обычными правилами не совсем понятна, но она полезна для подобных случаев и задокументирована в руководстве.
Этот прием можно использовать для любого количества выходных файлов, если в их именах есть какая-то общая подстрока для
%
сопоставления. (В этом случае общая подстрока - ".")источник
make
одна и та же команда будет запущена дважды одновременно.foo%in bar%src: input.in
:-)$(subst .,%,$(varA) $(varB)): input.in
(проверено с GNU make 4.1).У Make нет интуитивно понятного способа сделать это, но есть два достойных обходных пути.
Во-первых, если задействованные цели имеют общую основу, вы можете использовать правило префикса (с GNU make). То есть, если вы хотели исправить следующее правило:
Вы могли бы написать это так:
(Используя переменную правила шаблона $ *, которая обозначает совпадающую часть шаблона)
Если вы хотите быть переносимым на реализации Make, отличные от GNU, или если ваши файлы не могут быть названы в соответствии с шаблоном, есть другой способ:
Это сообщает программе make, что input.in.intermediate не будет существовать до запуска make, поэтому его отсутствие (или его временная метка) не приведет к ложному запуску foo-bin. И вне зависимости от того, устарели ли либо file-a.out, либо file-b.out, либо оба (относительно input.in), foo-bin будет запущен только один раз. Вы можете использовать .SECONDARY вместо .INTERMEDIATE, чтобы программа make НЕ удаляла гипотетическое имя файла input.in.intermediate. Этот метод также безопасен для параллельных сборок make.
Точка с запятой в первой строке важна. Он создает пустой рецепт для этого правила, так что Make знает, что мы действительно будем обновлять file-a.out и file-b.out (спасибо @siulkiulki и другим, указавшим на это)
источник
-j1
сборками все в порядке ). Кроме того, если make-файл прерывается и оставляетinput.in.intermediate
файл вокруг, это действительно сбивается с толку.file-a.out file-b.out: input.in.intermediate
наfile-a.out file-b.out: input.in.intermediate ;
См. Stackoverflow.com/questions/37873522/… для получения более подробной информации.Это основано на втором ответе @ deemer, который не полагается на правила шаблона, и исправляет проблему, с которой я столкнулся с вложенным использованием обходного пути.
Я бы добавил это как комментарий к ответу @ deemer, но я не могу, потому что я только что создал эту учетную запись и не имею никакой репутации.
Объяснение: Пустой рецепт нужен для того, чтобы позволить Make надлежащим образом отметить
file-a.out
и пометитьfile-b.out
как восстановленный. Если у вас есть еще одна промежуточная цель, которая зависит отfile-a.out
, то Make решит не создавать внешнюю промежуточную цель , утверждая:источник
После GNU Make 4.3 (19 января 2020 г.) вы можете использовать «сгруппированные явные цели». Заменить
:
на&:
.В файле NEWS GNU Make говорится:
источник
Вот как я это делаю. Сначала я всегда отделяю предварительные требования от рецептов. Тогда в этом случае новая цель сделать рецепт.
В первый раз:
1. Мы пытаемся собрать файл a.out, но сначала нужно сделать dummy-a-and-b.out, поэтому make запускает рецепт dummy-a-and-b.out.
2. Пробуем собрать file-b.out, dummy-a-and-b.out в актуальном состоянии.
Второй и последующие разы:
1. Мы пытаемся собрать file-a.out: make просматривает предварительные требования, обычные предварительные требования актуальны, вторичные предварительные требования отсутствуют, поэтому игнорируются.
2. Мы пытаемся создать file-b.out: make просматривает предварительные требования, обычные предварительные требования актуальны, второстепенные предварительные требования отсутствуют, поэтому игнорируются.
источник
file-b.out
make не сможет создать его заново.В качестве расширения к ответу @ deemer я обобщил его в функцию.
Сломать:
Удалите любые начальные / конечные пробелы из первого аргумента
Создайте переменную цель с уникальным значением для использования в качестве промежуточной цели. В этом случае значение будет .inter.file-a.out_file-b.out.
Создайте пустой рецепт для выходов с уникальной целью в качестве зависимости.
Объявите уникальную цель как промежуточную.
Завершите со ссылкой на уникальную цель, чтобы эту функцию можно было использовать непосредственно в рецепте.
Также обратите внимание, что использование eval здесь связано с тем, что eval не расширяется до нуля, поэтому полное расширение функции является просто уникальной целью.
Следует отдать должное атомарным правилам в GNU Make, на которых основана эта функция.
источник
Чтобы предотвратить параллельное многократное выполнение правила с несколькими выходами
make -jN
, используйте.NOTPARALLEL: outputs
. В твоем случае :источник