Библиотека для кеширования зависящих друг от друга объектов
Постановка задачи всё ещё содержит изрядно элементов её решения, увы ☹.
Дано пространство объектов (изначально пустое). У каждого возможного объекта есть:
Тип
Имя
Родословная — сеть (ограф без циклов) предков, из которых получен объект (
возможно, достаточно множества?)
Контент
Имена могут быть составными: например, могут быть объекты, имена которых имеют одинаковые суффиксы, и с такими группами объектов предполагается работать. Также частью имени является версия (timestamp).
Вариант: можно заранее договориться, что у имени есть синтаксическая структура
Вариант: имя можно оставить в покое и ввести отдельное поле категории с хештегами или чем-то подобным
Вариант: такие категории можно оформить как часть родословной, тогда нового поля не надо (версию, наверное, нельзя выносить в родословную)
Далее в тексте под термином имя предполагается вариант № 1
Объекты могут образовываться двумя путями:
Попадать в систему извне (входные данные)
Генерироваться на основании других объектов (генерат)
Среди типов объектов выделяется особый тип рецепт ( один или несколько?). Рецепт описывает граф генерации объектов (логика работы рецепта может быть любой — это контент объекта типа рецепт).
На вход рецепту подаётся шаблон объектов-исходников
- На выходе порождаются объекты-генераты, имена которых (частично) вычисляются из имён исходников
- Рецепт является предком всех генерируемых им объектов
- Использованные при генерации исходники и родословные добавляются в родословную генерата
- Необходим инструмент, описывающий,
- может ли данный рецепт породить данный генерат, и если да, то какие именно исходники для этого требуются;
- каковы требования к шаблонам этих исходников (например, к версиям).
- Если требования к шаблонам исходников удовлетворены, рецепт считается выполненным
В шаблоне могут встречаться:
- Фильтры на имя
- Фильтры на родословную
Требуется: на основании имени ( шаблона?) объекта-целевого генерата
- вычислить рекурсивно всю его предполагаемую родословную;
- определить, какой подграф родословной следует (пере)генерировать;
- сгенерировать целевой генерат, соблюдая порядок родословной.
Пример «Make»
(синтаксис условный) Собрать объект программа: prog (программа — тип, prog — имя).
Рецепты (два объекта в системе):
компоновка:: generates = программа: «имя»; файл_лог: «имя».log sources = файл_obj: «имя».o; файл_obj: lib.o content = call_gnu_ld
и
компиляция:: generates = файл_obj: «имя».o sources = файл_си: «имя».c content = call_gcc
В этом примере
- нет фильтров на родословную;
действует умолчание для фильтров на имя:
- используется наибольшая версия всех объектов,
- в требование к шаблонам автоматически добавляется условие «версия генерата не меньше версии любого из исходников»
Процедуры call_gnu_ld / call_gcc устанавливают версию генерата равной текущему времени:
Вариант: Сообразно внутренней логике — это не контролируется синтаксисом
Вариант: Сообразно умолчаниям, которые (в иных случаях) можно поменять, например, на «сделать версию равной версии исходника»
Пример «HWorker»
(синтаксис условный) Собрать объект оценка: финал (user: Никифор) (user: Никифор — фильтр на родословную)
Рецепты
финальный_балл:: gerenates = оценка: финал (user: «студент») sources = оценка: за_задачу (user: «студент»; задача: «задача»); оценка: за_посещаемость (user: «студент»; задача: «задача») content = вычислить_формулу
Здесь:
Есть свободная переменная — «задача» (т. к. упоминается только в sources). Это значит, что годится любой объект типа задача, проходящий фильтры
оценка_задачи:: gerenates = оценка: за_задачу (user: «студент»; задача: «задача») sources = решение: «имя_программы» (user: «студент»; задача: «задача») content = оценить_решение
Здесь «решение» — это входные данные, в которых предполагается единственный объект с произвольным именем (отсюда свободная переменная «имя_программы»)
оценка_посещаемости:: gerenates = оценка: за_посещаемость (user: «студент»; задача: «задача») sources = решение: «имя_программы» (user: «студент»; задача: «задача», version=MIN); дедлайн: «неважно_что» (задача: «задача») content = оценить_время
Здесь выбирается самое раннее решение с помощью явного фильтра имени; а имя дедлайна значения не имеет вообще.
TODO очистка неиспользованных генератов
Предыдущий вариант постановки.
Дано
- Задано некоторое (изначально пустое) пространство объектов разных категорий
Часть категорий — это объекты-исходники
Исходник (возможно, несколько, и возможно — различных категорий) появляется или обновляется (увеличивает версию) в результате запуска некоторой процедуры загрузкии
Оставшиеся категории — это объекты-генераты
Генерат (возможно, несколько, и возможно — различных категорий) конструируется из объектов других категорий (как исходников, так и генератов) с помощью процедуры сборки
У каждого объекта и каждой процедуры есть уникальный идентификатор
У каждого объекта и каждой процедуры есть версия («время создания»). Версия не может уменьшаться.
Появление процедуры или объекта с имеющимся идентификатором, но новой (более поздней) версией называется обновлением
- Решение о присвоении объекту определённой версии принимает процедура
- Если идентификатор и версия совпадают, считается, что объект не изменился.
Известны:
- категории объектов
- спецификация (описание входа и выхода) каждой процедуры загрузки
процедура загрузки допускает спецификацию вида «загрузить все объекты таких-то категорий»
- спецификация (описание входа и выхода) каждой процедуры сборки.
Понятие «актуальности» и «недоступности»
Актуальный объект или процедура всегда имеют наибольшую версию.
Дополнительно:
- Исходник актуален, если он получен с помощью актуальной процедуры загрузки, обновлять его не требуется
Процедуру загрузки можно вызвать явно, даже если все её исходники актуальны, потому что это может привести к обновлению исходников и появлению новых
- Генерат актуален, если он получен последовательностью актуальных сборок из актуальных исходников
Недоступным называется объект, который не может быть получен никакой актуальной процедурой.
Хранение
Хранятся как минимум все версии исходников и все доступные генераты.
Требуется
На основании заданных процедур и хранимого пространства объектов получить цель — заданный набор актуальных исходников и/или генератов.
Если по каким-то причинам объект получить нельзя, возвращается т. н. пустой объект, при этом доступен вердикт — обоснование, отчего объект не получен.
Условия
- Если хранимый объект актуален, процедуры не выполняются — выдаётся хранимый объект
- Сборка и загрузка объекта могут окончиться неуспехом — возвращается пустой объект + вердикт
Цель может быть задана неявно: например, «загрузить все возможные исходники, а затем все генераты данной категории, которые можно собрать из имеющихся исходников»
- Должна быть возможность удаления недоступных объектов
- Должна быть возможность удаления всех генератов
- Сборка должна иметь два режима работы с исходниками
- Используются только актуальные исходники
- Используются все версии актуальных исходников
Реализация
TODO Пока только идеи
- Построение графа зависимостей на основании спецификации процедур
- Запихивание под капот всего, что только можно, вплоть до того, что достаточно будет написать
- … и система запомнит, что:
bin_file — это категория генерата
bin_file изготавливается из c_file
c_file берётся последней версии (для работы со всеми версиями будет другой декоратор, например @versioned_build)
Генерат ли c_file или исходник — определится по какому-нибудь
c_compile — это процедура сборки (@build)
- …