пятница, 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. Концепция чистых исходников сохраняется. Кроме того я подумал, что одно из трех собираемых ядер явно лишнее. Релизное ядро вполне подходит и для инсталлятора и нам незачем тратить время на сборку инсталляционного.

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

9 коммент.:

Qehgt комментирует...

Возьмите SCons/CMake и не мучайтесь.

Andrey Valyaev комментирует...

Я не мучаюсь, я наслаждаюсь. :)

нету их во FreeBSD... лишнего не хочется тащить.

С другой стороны любое универсальное решение будет хуже специально заточенного. У нас там embedded FreeBSD, модули собираются в статики...

Есть pmake, он же BSD make. он конечно от GNU make сильно отличается. своеобразный немного. Но вполне позволяет разные штуки интересные делать.

Скриптов на самом деле получается мало. Стараюсь чтобы было мало буков.

Сергей комментирует...

Что-же такое вы такое говорите.
Они присутствуют во FreeBSD ports и самых последних версий.
Использовать FreeBSD без ее репозитория с портами - тонкое извращение.

dmi3s комментирует...

Около года назад использовал CMake для кросс-компиляции под ARM. Правда, под linux. Впечатления остались положительные. Ради эксперимента успешно собрал им boost под девайс (jam не смог).

Запросы у меня были скромные: собиралась одна библиотека (и статическая, и динамическая) и бинарник. Так что сложными зависимостями я не занимался, и поругать/похвалить не могу.

Писать же и, в случае чего, как-то "отлаживать" собственные Makefiles - удовольствие еще то, как мне кажется. Т.е. для меня использование чистого или собственноручно заскритованного make - это что-то около грани добра и зла :)

Andrey Valyaev комментирует...

Любой новый инструмент надо курить...

К тому же у меня нет уверенности, что тот же SCons удовлетворит все мои пожелания.

Тащить же еще один крутой тул в проект - может стать шоком для разработчиков :) надо быть проще.

У нас, помимо собственно FreeBSD из каробки не так уж много сторонних зависимостей. не стоит их множить без нужды...

Вообще вы как-то сгущаете краски... :) Там крайне простая система сборки... в каждом компоненте мейкфайл на 3-5 строк... И один основной мейкфайл... строк 200 наверное :)

Просто BSD System излишне парит мозги. Хочется упростить.

Andrey Valyaev комментирует...

Можно взять крутую систему сборки... или крутую общеизвестную либу... И всегда будут свои муравьи...

Специализированное решение может быть более эффективным и более подконтрольным.

dmi3s комментирует...

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

Я же хотел поделиться скромным опытом крос-компиляции cmake-ом, но во-время не остановился, и заодно уж высказал свое субъективное отношение к make-у.:)

dmi3s комментирует...

Вспомнил о самой "страшной" системе автосборки, с которой доводилось работать.

Это была система qmake скриптов, отвечающая за сборку одного объемного приложения по обработке видео, да еще и с сопутствующими плагинам для AfterEffects и FinalCut. Само приложение компилировалось под Win32/RHEL4/RHEL5, если не путаю, а плагины -- под [Win|OSX][32|64]. Скриптов было около 15и, и написаны они были, вроде как, нормально. Не идеально, конечно: иногда было неясно, где может находится тот или иной параметр; иногда возникали вопросы по поводу логики или порядку присвоения переменных, ну и прочие мелочи.

Почему же я назвал ее "страшной"? Потому как была эта система ну уж очень нежной, да еще и без возможности быстрого тестирования всех конфигураций. Изменение какого-либо маленького "пука" сводилось к 20-40 минутам редактирования и проверки собираемости на своей машине. Я писал плагин под AE на 64х битной винде (из почти тех же исходников он собирался для OSX 64), так что сборка проходила довольно шустро. Затем билдовые скрипты коммитились в репозитарий, и в дело вступал Hudson.

Параллельно я начинал делать другой таск непосредственно в исходниках, но держал пальцы скрещеными. Если на Hudson-е отваливалась сборка моего плагина на OSX, то это еще пол-беды: была возможность зайти по VNC на мак и более-менее быстро поправить. Но если я ломал что-то чужое, то это уже была полноценная беда, и попахивало рабочим днем, убитым на "полу-слепое" ковыряние в билд-системе в попытках выяснить, что, где и как поломалось.

К тому же, в качестве системы контроля версий использовался svn, и ни о каких кошерных вещах вроде git branch/git stash речи не шло. Соответственно, я разок случайно затер изменения в исходниках, которые успел накрапать пока Hudson работал.

Собственно, я так подробно описал все эти "страдания" (не только мои, но и всех программеров в команде) для того чтобы как-то пояснить свое отношение к "простому" make-у, которое я высказывал в предыдущих каментах.

Andrey Valyaev комментирует...

dmi3s, спасибо за поддержку... :)

Еще думаю, что для устоявшегося проекта привнесение нового тула или новой либы - взамен чего-то старого, по сути то же самое велосипедостроение.

Тут нужно все взвесить и сделать так, как кажется удобнее, проще, как знакомее может быть.

Не могу похвастаться опытом по работе с SCons. Лучше всего я дружу с мейкфайлами. :) А переносимость нам на данном этапе не нужна. Наш проект достаточно старый и к сожалению не переносится даже на 64 бита... над этим надо много работать.