[О блоге] [наверх] [пред] [2020-11-20 19:40:50+03:00] [fbfbf62ce9cb0a3fcb97e02d6fc05c249bb4e162]
Темы: [go][redo]

I redo redo

http://git.cypherpunks.ru/cgit.cgi/goredo.git/tree/README
Психанул и написал свою redo реализацию на Go!

Python apenwarr/redo всем хорош, но Python зависимость не очень приятна.
Плюс при большом уровне параллелизма у него иногда что-то сбоит и он
падает. Не критично, ибо всё равно потом всё недособранное можно
пересобрать (это ж не Make, где с чистого листа только безопасно будет),
но не приятно.

redo-c не имеет redo-stamp команды. С ней просто приятнее и удобнее
жить. Плюс он не уважает umask, ибо просто используется mkstemp(). И
мозолят глаза его .dep. файлы в каждой директории.

Решил было просто переписать его на Go. И в основу он полностью и лёг.
Но на Go так приятно и легко пишется, что я по сути почти все фичи
apenwarr/redo реализации засунул. Ровно один день чтобы написать рабочую
(текущие проекты полностью мочь без проблем *пере*собирать) redo
реализацию. Тестами ничего не покрыто, но у себя всё что в голову
приходило и на нетривиальном проекте с default/redo-stamp разнообразием
оно отрабатывает идеально. Сейчас я даже Python реализацию удалил из
путей.

Что добавил, чего не было в redo-c:

* всегда захватывать stdout. Как и в apenwarr/redo, явно проверяется не
  записал ли пользователь И в stdout И в $3. Я не раз по неосторожности
  так делал и это сильно помогало
* явно проверять что собранный файл не изменился пользователем, вне
  контекста сборки redo, опять же, как это делает apenwarr/redo -- это
  очень удобно, ибо позволяет временно вносить правки и смотреть что
  получится, но файлы не будут перезатёрты
* исполняемые файлы запускаются как есть, содержащие shebang --
  запускаются с ним. Ну а оставшиеся с /bin/sh -e (или -x в дополнении)
* redo -x покажет trace (sh -x) только для текущих указанных целей, а не
  всех -- оказалось это очень удобно (но можно через env выставить и
  показ вообще всего для всех)
* уважение umask
* redo-stamp, redo-whichdo (мне не надо, но сделать легко)
* в .dep файле сохраняется и UUID сборки, по которому можно понять а
  была ли цель уже собрана, ведь от неё могут зависеть многие, а на ней
  стоит redo-always или redo-stamp какой-нибудь. Позволяет избежать
  возможных повторных ненужных сборок. В apenwarr/redo тоже хранится
  информация о конкретной сборки, поэтому в нём тоже избежали эту
  проблему
* lock-и и зависимости для каждой цели у меня аналогично в отдельных
  файлах, но все они собраны в .redo поддиректории (каждой директории
  проекта), что просто разгружает глаз. Там же хранятся и .log файлы

А теперь отличия от навороченного apenwarr/redo:

* apenwarr/redo проверяет размеры, mtime и ещё несколько параметров
  файла для определения его свежести. Насколько понимаю, ctime он не
  использует потому что он меняется с изменением link count-а. Я
  использовал подход redo-c: проверка ctime, если не совпал, то проверка
  хэша файла. Проверка хэша убирает false positive срабатывание. Если
  хочется иметь "вечную" цель, то, как и redo-c, как и apenwarr/redo,
  отсутствие файла воспринимают как тухлость. Последние правки в redo-c
  не удаляют файл, если stdout был пуст -- это ломает подобный подход. Я
  поэтому делал revert коммита. Ибо это добавляет гибкости и отсутствие
  проблем из-за хэширования пустоты. В итоге имеем и гибкость и false
  positive защиту хэшом. Если покажется кому-то медленным, то добавил
  возможность отключения хэширования (как apenwarr/redo будет). Для хэша
  использую BLAKE2b
* я явно делаю sync для созданных целей и файлов зависимостей, а также
  директорий после переименования файлов. Можно отключить. apenwarr/redo
  не делает sync ни для целей, ни для SQLite3 БД, явно отключая
  синхронность
* распараллеливание работы делается через jobserver-like протокол, но с
  возможностью infinite работ. У меня на C проекте это позволяет сверх
  быстро собрать его
* у меня разукрашенный вывод, с удобными indent-ами, возможностью показа
  когда цель завершена и сколько это заняло времени, возможностью
  показа PID-ов. stderr задач можно тоже показывать с PID-ом самого
  shell-скрипта выполняющегося. У меня сверх verbose debug вывод, весь
  из себя разукрашенный, показывающий все принимаемые решения и шаги
* stderr каждой цели можно сохранить на диск автоматом, но в нём каждая
  строка будет ещё и с TAI64N timestamp-ом, совместимым с tai64nlocal
  утилитой. redo-log команда позволит просмотреть его
* состояние хранится в .dep файлах которые являются recfile-ами
  (8249370437018ad186c7946f22242731fba52035, d47bdefa40f41b81435565029c035f62614fe0da)
  Особо надобности в этом нет, oneline формат redo-c не проблематичен,
  но так красивее, мне кажется
* в процессе работы создаются временные файлы, которые при убийстве
  процесса не подчищаются. Сделал отдельную redo-cleanup команду для их
  подчистки и возможности вообще удаления .redo отовсюду, для начала с
  чистого листа
* apenwarr/redo даже в FAQ говорит что скорее всего начнёт создавать
  .redo поддиректории в каждой директории, ибо не понятно как выяснять
  что является верхним уровнем проекта. Я сделал подход redo-c: вплоть
  до корня ФС он будет искать .do файлы. Но, с возможностью ограничения
  "сверху" через REDO_TOP_DIR переменную или наличию .redo/top файла.
  И оптимизация и безопасность если проект и подпроект используют redo,
  но должны быть независимы друг от друга. Насколько понимаю,
  apenwarr/redo тут будет иметь проблемы

Наконец, goredo имеет "gore" корень, что не может меня, любителя
goregrind, не радовать. Это один исполняемый файл, на который нужно
создать символические ссылки. Точнее запустить с -symlinks опцией, чтобы
он это сделал сам.

goredo в итоге работает и собирает проект даже на глаз ОЩУТИМО быстрее.
Всё же, как ни крути, но Python это Python и быстро запуститься
программа на нём не может. Чтобы просто перезапустить rebuild, который
пройдёт по целям делающим redo-stamp, apenwarr/redo нужно время заметное
на глаз, тогда как goredo отрабатывает вмиг. В общем, пока для меня это
идеальная redo реализация!

Смотрел ли я ещё на какие-нибудь?

* Haskell реализация поддерживает только redo-ifchange. Да, это основное
  и главное, но нет, хочется большего
* Есть 2-3 реализации на shell (не считая работающего apenwarr/do, но не
  делающего rebuild), но либо они требуют либо GNU утилит, либо вообще
  bash (идёт сразу в жопу)
* redux на Go я даже пробовал запускать. Он создавал какое-то дичайшее
  количество (многие мегабайты) JSON файлов, но .do вроде запускал.
  Только с безумно низкой скоростью это всё работало и я даже через
  десятки секунд (минуты?) не дождался конца и нажал Ctrl-C. Просто
  неюзабельно
* C++ не смотрел, ибо нафиг этот язык, тем более для выполнения redo задачи
* Отпадают не совместимые с redo(-ifchange) реализации

И только с написанием goredo я наконец-то понял надобность в
redo-ifcreate! Например есть цель "foo", но нет "foo.do", а есть
"default.do" (где-нибудь). Если в зависимости будет прописано только
"foo" и "default.do", то появление "foo.do" не заставить цель протухнуть!
Поэтому пути поиска default.do файлов автоматически прописываются в
зависимости, как-будто был вызван redo-ifcreate. Очень красиво, на мой
взгляд. Как пользователь, я redo-ifcreate так и не знаю где вызвать, но
внутри без него вижу что нельзя. Ведь и зависимость от .do файла явно
никто не прописывает -- оно автоматически.

    [оставить комментарий]
    комментарий 0:
    From: kmeaw
    Date: 2020-12-02 16:08:53Z
    
    Похоже, то ли что-то случилось со ссылкой, то ли cgit обновился, но я
    получаю 404. Вот работающая ссылка:
    
    http://git.cypherpunks.ru/?p=goredo.git;a=blob;f=README;h=1c9e59be4026be59a0999487ff49602a700fd5da;hb=HEAD
    
    комментарий 1:
    From: Sergey Matveev
    Date: 2020-12-02 16:11:29Z
    
    *** kmeaw [2020-12-02 19:03]:
    >Похоже, то ли что-то случилось со ссылкой, то ли cgit обновился
    
    Я буквально сегодня заменил cgit на gitweb, который из коробки с git-ом
    идёт. Всё равно находится в системе да и проще его собирать. Сейчас
    более корректная ссылка это http://www.goredo.cypherpunks.ru/