пятница, 25 марта 2011 г.

Own build system

Наш проект издавна был прикручен к BSD build system. Это набор скриптов, которые располагаются в /usr/share/mk и BSD make с ними. Что-то я от них устал. Они совершенно не подходят для гибкой разработки.

Например, если попытаться практиковать TDD - для качественной сборки необходимо будет всегда делать make depend, иначе изменения в инклюдах не будут отслеживаться. И один раз сделать make depend - совершенно недостаточно. Всегда надо забивать себе голову мыслями о том, не изменил ли я в коде что-то, что могло повлиять на зависимости...

С другой стороны избыточный синтаксис мейкфайлов. Не достаточно указать библиотеку в LDADD, мне необходимо добавить ее в DPADD, чтобы изменение этой библиотеки так же корректно приводило к пересборке. Явное дублирование.

Следование соглашениям BSD вынуждает все файлы инсталлировать. Нам нет необходимости их инсталлировать, потому что из них еще надлежит собрать архивы и образы для инсталляции.

И самое неудобное, что при смене какой-нибудь опции компилятора все надо сперва очистить make clean, make cleandir два раза, а то все не сотрет, make obj не забыть а то objdir не заюзает, собрать все. Не забыть потом сделать make depend, если есть планы пересобирать далее, который без собранных компонентов предварительно не проработает. Вроде ничего не забыл... Это же жесть...

Всякие наворочанные системы сборки тащить в проект особо не хочется, лучше всего обходиться стандартными FreeBSD-шными средствами.

Пораскинув мозгами пришел к выводу, что многие из вышеуказанных проблем можно легко обойти выбросив нафиг BSD build system и написав свои собственные мейкфайлы.

Для начала нужно явно указывать временный каталог и каталог с результатами сборки. Это позволит переключаться между несколькими альтернативными сборками, которые могут различаться используемыми компиляторами или опциями. Кроме того позволит не замусоривать промежуточными файлами дерево исходных текстов, которое кроме того доступно на моей сборочной машине по NFS. С другой стороны желающие могут временный каталог разместить рядом - нет никаких проблем.

Шаг номер два я подсмотрел в ninja. Зависимости там вычисляются паралельно со сборкой файла (для каждого объектного модуля свои). Это достаточно легко делается через -MMD -MF depfile. И действительно, если объектного модуля нету, то и зависимости никому не нужны. А после первой компиляции зависимости появляются и заставляют файл пересобираться автоматически самообновляясь.

С третей задачей я серьезно застрял. Два дня думал. Очень хотелось, чтобы при смене компилятора или опций все что необходимо пересобиралось само, без дурацких make clean. Основная идея в том, что версия компилятора и флаги сгружаются в файл, который сравнивается с таким же файлом, сохраненным для каждого объектного модуля. Проблема в том, что файл образец создается безусловно и все модульный флаги считаются out-of-date. В результате остановился на следующей схеме: файл образец для каждого компонента свой. И когда мы его формируем - сперва создаем временную копию. Если временная версия не соответствует образцу (через diff), то она заменяет образец и все модульные флаги оутдатятся, утягивая за собой и объектные модули.

Лишнее все из компонентных мейкфайлов выкинул. В каждом компоненте указывается только цель (TARGET), список исходников (SOURCES) и включается специальный инклюд верхнего уровня. Который по TARGET легко определяет как ее собирать - как библиотеку или как бинарник. BSD использовала для этого разные переменные и разные mk инклюды. Для бинарников предусмотрено указание линкуемых библиотек. И в любом случае можно прописать индивидуальные опции компиляции/линкования, если в этом есть нужда.

Отдельно подумал о юниттестах. При написании тестов лучше избегать всяких рутинных операций, и я решил что даже Makefile в тестах будет лишним. Просто каталог с набором файлов, файлы начинающиеся со слова test обнаруживаются автоматически, линкуются в тестраннер и запускаются. Если нужно отключить тест - его можно просто переименовать. Помоему будет очень удобно. Можно будет специально поработать над скоростью компиляции, чтобы работа в стиле TDD проходила быстрее.

Порадовало ядро FreeBSD. Раньше оно чихало на OBJDIR и собиралось в недрах sys/i386/compile/CONFIG. Но оказалось что эту папку легко можно переназначить в TEMPDIR. Концепция чистых исходников сохраняется. Кроме того я подумал, что одно из трех собираемых ядер явно лишнее. Релизное ядро вполне подходит и для инсталлятора и нам незачем тратить время на сборку инсталляционного.

И судя по всему сборка будет проходить значительно быстрее. Работа пока еще не закончена. Наверное на следующей неделе смогу собрать весь проект - поглядим.