Как мне обработать очень длинные списки файлов в рецепте make? - PullRequest
9 голосов
/ 12 августа 2011

Поскольку GNU make позволяет переменным быть настолько большими, насколько позволяет память, у него нет проблем с созданием массивных списков зависимостей. Однако, если вы действительно хотите использовать эти списки файлов в рецепте (последовательность команд оболочки для построения цели), вы столкнетесь с проблемой: команда может превысить ограничение длины командной строки оболочки, что приведет к ошибка типа «Список аргументов слишком длинный».

Например, предположим, что я хочу объединить несколько файлов, содержащихся в списке $(INPUTS), для создания файла combined.txt. Обычно я мог бы использовать:

combined.txt: $(INPUTS)
        cat $^ > $@

Но если $(INPUTS) содержит много тысяч файлов, как это происходит в моем случае, вызов cat будет слишком длинным и не удастся. Есть ли способ обойти эту проблему вообще? Можно с уверенностью предположить, что существует некоторая последовательность команд, поведение которой идентично одной огромной команде - в данном случае это серия cat команд, по одной на входной файл, которые используют >> для добавления к combined.txt должно сработать. Но как убедить make генерировать эти команды?

1 Ответ

10 голосов
/ 12 августа 2011

В поисках ответа, лучшее предложение, которое я мог найти, было , чтобы разбить список на серию меньших списков и обработать их, используя shell for loops .Но вы не всегда можете сделать это, и даже когда это возможно, это грязный хак: например, не очевидно, как получить обычное make поведение остановки, как только команда не выполняется.К счастью, после долгих поисков и экспериментов выясняется, что общее решение действительно существует.

Рецепты и переводы строк

make рецепты вызывают отдельный подоболочку для каждой строки в рецепте.Такое поведение может быть раздражающим и нелогичным: например, команда cd в одной строке не повлияет на последующие команды, потому что они выполняются в отдельных подоболочках.Тем не менее, это действительно то, что нам нужно, чтобы получить make для выполнения действий с очень длинными списками файлов.

Обычно, если вы строите «многострочный» список файлов с регулярным назначением переменной, который использует обратную косую черту, чтобы разбитьоператор в нескольких строках, make удаляет все новые строки:

# The following two statements are equivalent
FILES := a b c

FILES := \
a \
b \
c

Однако, используя директиву define, можно создавать значения переменных, содержащие символы новой строки.Более того, если вы подставите такую ​​переменную в рецепт, каждая строка будет действительно выполняться с использованием отдельной подоболочки, так что, например, запуск make test из /home/jbloggs с использованием make-файла ниже (и при условии, что файла с именем test не существует) выдаст вывод /home/jbloggs, поскольку эффект команды cd .. теряется при завершении ее подоболочки:

define CMDS
cd ..
pwd
endef

test:
        $(CMDS)

Если мы создадим переменную, которая содержит символы новой строки, используя define, она может бытькак обычно, объединяется с другим текстом и обрабатывается с использованием всех обычных функций make.Это, в сочетании с функцией $(foreach), позволяет нам получить то, что мы хотим:

# Just a single newline!  Note 2 blank lines are needed.
define NL


endef

combined.txt: $(INPUTS)
        rm $@
        $(foreach f,$(INPUTS),cat $(f) >> $@$(NL))

Мы просим $(foreach) преобразовать каждое имя файла в команду terminline-terminated , котораябудет выполнен в своей собственной оболочке.Для более сложных задач вы можете вместо этого записать список имен файлов в файл с серией команд echo, а затем использовать xargs.

Примечания

  • Директива define описывается как необязательное получение токена =, := или += в конце первой строки, чтобы определить, какой переменный аромат должен быть создан -- но учтите, что это работает только на версиях GNU make 3.82 и выше!Возможно, вы, как и я, работаете с популярной версией 3.81, которая ничего не назначает переменной, если вы добавляете один из этих токенов, что приводит к большому разочарованию.Подробнее см. здесь .
  • Все строки рецепта должны начинаться с буквенного символа табуляции, а не с 8 пробелов, которые я здесь использовал.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...