Преимущества и недостатки redo redo это предложенная => DJB альтернатива Make системе сборки. Своё видение такой системы он просто описал => тремя небольшими страницами Никакого кода не опубликовал. Alan Grosskurth написал работу в институте на => эту тему А десятки других людей позже написали свои реализации этой системы сборки на самых разных языках. Очень хорошее пояснение и введение с примерами есть в документации к => apenwarr/redo => Ещё одно хорошее введение Программ/реализаций Make -- десятки. Систем сборки несовместимых с Make -- с полсотни минимум. Десяток систем генерирующих Makefile-ы. Несовместимых между собой диалектов Make-ов тоже десятки (например GNU Make и BSD Make). Так чем же примечательна ещё одна система? Почему нельзя использовать POSIX Make подмножество, которое, по идее, должно поддерживаться и одинаково работать на широком круге Make реализаций? Чуть ли не каждый заявляет что он лучше Make или как минимум удобнее. Начну с единственного, что можно причислить к недостатку: redo из коробки не часто стоит в ОС. Впрочем, как и почти все системы сборки, кроме какого-то одного из Make, часто вас всё равно не удовлетворяющего (сколько вам приходится дополнительно ставить GNU Make в вашу систему чтобы хоть как-то собиралась масса софта)? Преимущества: * Не смотря на POSIX, всё равно будут не регламентированные отличия даже в мелочах, существенно затрудняющие разработку Makefile-ов. Например, одни Make реализации переходят в директорию при исполнении "make -C dir", а другие нет. * Полная реализация redo, с возможностью распараллеливания заданий, на чистом C, без зависимостей, с встроенной полной реализацией SHA256, занимает менее 1k SLOC. Реализация на чистом POSIX shell запросто умещается в 100 SLOC. Поэтому, даже если redo не установлен в системе, то достаточно скопировать небольшой shell скрипт (например https://github.com/apenwarr/redo/blob/main/minimal/do занимает одну треть от размера GPLv3 лицензии) или один бинарный исполняемый файл в пару десятков килобайт. Реализации имеются и на других shell, Python, Haskell, Go, C++, Inferno Shell. * redo позволяет делать абсолютно *все* те же самые задачи что и Make. В отличии от Make, в нём нет ни предположений о том, что вы собираете (.SUFFIXES), ни специфичного для языка (часто это C/C++) hard-code. * *Минимальный* порог входа -- не требуется изучение ни нового языка, ни нового синтаксиса. По умолчанию, используется POSIX shell, но вы вольны использовать *любой* язык по вашему усмотрению (или даже смешивать их). Тогда как в Make нельзя удобно записывать shell в несколько строчек. Лично у меня, на практике, redo описания получаются меньше по размеру чем Makefile-ы. * Задание зависимостей более простое и чёткое. Описание сборки цели автоматом является зависимостью (если делать зависимость от Makefile, то любое его изменение означает пересборку всех целей). Тривиально делать динамические зависимости (например на основе #include из .[ch]), что в Makefile невозможно. Зависимости можно задать даже после сборки цели. Зависимости от изменённых переменных окружения или переменных Make, аналогично, делаются тривиально. На практике, мало кто делает подобное для Make из-за сложности, зачастую просто вручную выполняя make clean, с последующей полной пересборкой. Более того, в redo можно задавать зависимости уже *после* сборки цели! * Отсутствие проблем с атомарностью сборки целей: redo все цели собирает атомарно, через fsync и переименование временного файла. Make ничем не помогает в этом -- вы вынуждены вручную эмулировать атомарность сборки, чего, на практике, мало кто делает из-за сложности и неудобства. Частично скопированные/скачанные файлы или упавшая команда не будут более считаться успешно выполненной целью. * Распараллеливание сборок целей и понимание изменения зависимостей делается на основе сохраняемого на диске состояния, поэтому не возникает проблем с "слепотой" Make к изменению файлов, например из-за NFS, mmap, VCS, FUSE (со всеми ними => нет гарантий об изменениях в mtime), небольшой гранулярностью времени mtime, нестабильных часов. Многие реализации используют криптографические хэши для выяснения изменённости файлов. * По умолчанию, описания правил сборки целей изолированы друг от друга. redo позволяет делать зависимости на цели в других директориях, с полным централизованным глобальным ведением информации о зависимостях, с state-ом и lock-ами, без каких-либо недостатков описанных в => Recursive Make Considered Harmful * Возможность задания зависимостей на несуществующие цели, например на флаговые файлы, новые описания правил сборки. * Никаких проблем с именами файлов содержащие пробелы. Как же пользоваться этой системой сборки? Приведу её полное описание с точки зрения пользователя. Для конкретных примеров советую ссылки в начале этой статьи. * Целью является единственный файл. Результат работы цели может и не создать файл. * Правила сборки цели target прописываются в target.do файле. По умолчанию он запускается как "/bin/sh -e" скрипт. Но это может зависеть от конкретных redo реализаций: некоторые, если файл является исполняемым, запускают его как полноценную программу; некоторые, позволяют запускать иные интерпретаторы при указании shebang-а. Некоторые реализации при задании отладочного флага, добавляют -x к вызову /bin/sh. * Цели можно указывать и в других директориях. Соответствующие .do файлы будут искаться в них для начала. * Если dir/base.a.b.do не найден, то ищутся: dir/default.a.b.do dir/default.b.do dir/default.do default.a.b.do default.b.do default.do А для цели ../a/b/xtarget.y ищутся: ./../a/b/xtarget.y.do ./../a/b/default.y.do ./../a/b/default.do ./../a/default.y.do ./../a/default.do ./../default.y.do ./../default.do В принципе, единственный default.do можно использовать как аналог единственного Makefile. * .do запускается всегда в той же директории где и сам .do. * .do запускается с тремя аргументами: $1 -- имя цели $2 -- базовое имя цели. Совпадает с $1 если это не default файл. В противном случае оно будет без расширения, например для цели a.b.c: a.b.c.do -> $2=a.b.c default.do -> $2=a.b.c default.c.do -> $2=a.b default.b.c.do -> $2=a $3 -- имя временного выходного файла, который будет переименован в "цель" * stdout .do скрипта записывается во временный файл и становится результатом выполнения цели. Вместо stdout можно использовать $3 файл. * Некоторые реализации не создают пустой файл цели, если вывода в stdout/$3 не было, для удобства. * Если цель не существует, то она считается устаревшей. * Если цель не задана, то многие реализации по умолчанию собирают all. * Зависимость в .do файле прописывается как вызов команды redo-ifchange с передачей в качестве аргументов множества зависимых целей. Если файла перечисленной зависимости нет, то она собирается и запоминается в state как зависимость. Если файл зависимости не изменился, то цель зависимости не пересобирается. Зависимость от текущего .do автоматическая. * Несуществующие зависимости в .do файле прописываются как вызов redo-ifcreate с аргументами несуществующих файлов. В state запоминается отсутствие файла. Его появление вызывает пересборку текущей цели. * Явный вызов redo target форсированно вызывает пересборку цели, даже если зависимости не изменились (.PHONY-like цель). Многие реализации дополнительно предлагают ещё такую команду как redo-always: всегда пересобирать указанные зависимости. Некоторые реализации используют ctime для понимания изменился ли файл, а некоторые -- множество различных атрибутов файлов (mtime, size, inode number, file mode, UID/GID, и т.д.). Некоторые просто считают криптографический хэш от файла -- плюсом этого решения является уменьшение false-positive сборок. Проверенные мной работающие реализации: => apenwarr/redo Python apenwarr/do -- из состава apenwarr/redo POSIX shell => redo-c C, есть проблема с пустыми целями => goredo Go => baredo C, проверяет только mtime