Запускать Cog вручную для отдельного файла можно просто из командной строки.
Но если в вашем проекте будет несколько файлов, которые требуют Cog-обработки,
то целесообразнее создать в Makefile специальную цель с именем cog.
Действие этой цели будет заключаться в безусловной cog-обработке всех необходимых
файлов:
cog:
cog.py -r foo.c
cog.py -r bar.c
Тогда набрав просто
make cog
вы запустите cog-обработку для файлов foo.c и bar.c.
Если главная цель носит имя all, то командой
make cog all
вы запустите обработку и сборку главной цели.
Подобный метод хорош для однократных генераторов, а также для ситуаций, когда
год генератора содержится в самом исходном файле.
Для многократных генераторов запуск вручную будет не слишком хорошей идеей.
С одной стороны, потому что это утомительно, а с другой -- потому что
существует опасность не запустить cog-обработку, когда она действительно
будет нужна и в результате получить шанс долго и нудно искать логическую ошибку
в работе проекта, связанную с тем, что некоторые ваши файлы не были обновлены.
Для того, чтобы cog-обработка запускалась автоматически каждый раз, когда она
нужна, необходимо иметь способ построить зависимости, которыми будет
руководиться make при сборке проекта. Т.е. в результате cog-обработки
должен получаться некий файл, который будет использоваться в качестве make-цели.
Если обработке подвергается, например, файл foo.c, то использовать его
в качестве цели будет плохой идеей. Отчасти потому, что содержимое этого файла
может изменяться как в результате cog-обработки, так и при ручной правке.
Можно использовать некоторый вспомогательный (dummy) файл, содержимое
которого не имеет значения (для make важна лишь дата модификации файла).
Такой вспомогательный файл можно было бы сформировать следующим образом:
foo.cog: foo.c
cog.py -r foo.c
echo done > foo.cog
Если при генерации используются данные из внешних файлов, например, bar.ext,
то правило сборки можно было бы переписать следующим образом:
foo.cog: foo.c bar.ext
cog.py -r foo.c
echo done > foo.cog
Описанный метод имеет один огромный недостаток: он не будет работать.
Видно, что cog-обработка будет запускаться каждый раз
при изменении даты файла foo.c (или bar.ext). А поскольку дата foo.c
будет меняться в результате cog-обработки, то make просто зациклится.
Что же делать?
Я решил для себя эту проблему простым способом: выделил тело cog-генератора
в отдельный питоновый файл (например, foo.py для рассматриваемого примера).
А в теле foo.c генератор вырождается в импорт этого файла (и, возможно,
вызов функции из этого файла):
[[[cog import foo ]]]
Побочным эффектом от импорта является создание файла содержащего скомпилированный
байт-код для файла foo.py -- он будет носить название foo.pyc. Этот файл
можно использовать как имя цели, вместо foo.cog.
В таком случае правило cog-обработки можно записать так:
foo.pyc: foo.py bar.ext
cog.py -r foo.c
echo done > foo.pyc
Список имен cog-целей удобно присвоить некоторой make-переменной:
cog_targets := foo.pyc zzz.pyc
И затем использовать в списке зависимостей для построения главной цели проекта.
Например:
program.exe: $(cog_targets) $(obj_files)
$(LINK) $(obj_files)
Указывая список cog-целей перед списком объектных файлов, мы тем самым заставляем
make сначала провести всю необходимую cog-обработку, а затем уже компилировать
исходные файлы.