В ходе любой крупной(да и не очень) разработки вам придется задуматься о том как организовать процессы так, что бы ваш проект не разваливался от любого неосторожного коммита. В этом вам помогут системы непрерывной интеграции и деплоя, которые включают в себя сборку, тестирование и релизные действия.
Сегодня мы с вами поговорим о том как устроены системы сборки, что такое система зависимостей и какие бывают способы тестирования. Так же мы начнем разговор о CD, но не закончим его, так как ближе к концу нашего курса вернемся к теме релизов и описания полного пайплайна вашей работы.
— основа любого проекта. Будь то однострочный запуск команды gcc -O3 main.cpp -o main или запуск распределенного процесса сборки на кластере вашей компании, все сведется к тому, что на выходе у вас получится бинарный, исполняемый файл, который будет решать вашу задачу. И хоть эта тема может казаться не сложной и достаточно прямолинейной, на самом деле в ней скрыто множество сложных задач, многие из которых еще даже не решены в общем случае.
Для того, что бы работать с любой сложной системой(коей и является сборка), нам требуется абстрагироваться от “лишней” информации и понять, что из себя представляет объект в удаленном варианте.
Любая система сборки должна в том или ином виде давать вам возможность задать объект сборки и объекты от которых этот объект зависит. Таким образом можно понять, что любая сборка это ориентированный направленный граф, где узел это исходник(e.g. модуль и/или файл), а зависимость одного файла от другого — ребро. Таким образом задача сборки представляет из себя не что иное как поиск “правильного” обхода этого графа.
Под правильностью здесь может пониматься много разных параметров, но основной вытекает из самой постановки задачи — в момент когда мы собираем какой-то объект все его зависимости должны быть “обработаны”(так как не всегда работа с зависимостями является сборкой). В базовом сценарии(однопоточная сборка) эта задача решится топологической сортировкой графа и последовательным выполнением выданных задач. Но однопоточная постановка задачи нас мало интересует — любой достаточно крупный проект будет собираться десятки часов на одном потоке. Куда интереснее обсудить этот вопрос в контексте многопоточного(или даже распределенного) выполнения. В такой постановке наша задача превращается в алгоритм планирования DAG(Directed Acyclic Graph). Эта задача до сих пор является активной областью изучения и универсального решения никто не придумал(на мой взгляд его даже нет), потому что появляется множество нюансов, которые надо учитывать. Эти нюансы включают в себя(но не ограничиваются)
Если на минуту отойти от темы лекции и подумать, является ли описанная нами модель уникальной задачей сборки, то окажется, что это не так — мы только что описали пайплайны любых распределенных вычислений(будь то обучения моделей машинного обучения, выполнение сложных запросов в базу данных или вычисление математических задач).
Перед тем как поговорить о настоящих системах сборки обсудим еще два важных свойства этих самих систем.
Любая система сборки претендующая на то, что бы быть хоть сколько-то полезной и помогать в разработке, должна в той или иной форме уметь кэшировать промежуточные артефакты своей работы, а не собирать их с нуля каждый раз.
В действительно крупных и качественных системах сборки, этот кэш будет распределенным и доступным всем разработчикам, что минимизирует колличество пересборок, что в свою очередь помогает более грамотно утилизировать CPU вашего рабочего пространства(хоть и несколько нагружает сеть).
Как мы обсудили в Абстракция часто задача сборки будет характеризоваться высокой параллельностью, так как обычно сборка представляет из себя тысячи мелких задач, нежели несколько крупных. Таким образом скорость сборки(при нормальном планировании) прямо пропорциональна доступным потокам в вашей системе.
Важно — следите, что бы граф вашей сборки был как можно более разряженным, таким образом ваша сборка будет
Для примера — так выглядит граф сборки одного из крупнейших проектов Яндекса

Карта сборки движка БК