Всеобщий исходняк, или зачем собирать программы

Алексей Федорчук, 2.06.2003, UNIX4all

Сколь бы могучим и всеобъемлющим ни был дистрибутив Linux'а, на котором остановил свой выбор конечный пользователь, перед последним рано или поздно встает задача установки дополнительных пакетов или обновления существующих. Какие варианты выбора перед ним возникают?

С точки зрения "пакетчика"

Для пользователя пакетного дистрибутива (назовем его "пакетчиком)", типа Red Hat (или любого другого, базируемого на rpm), направшивающееся решение - поиск нужного пакета в прекомпилированном виде и соответствующем формате, по завершении чего следует простая команда

$ rpm -ihv имя_пакета.rpm

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

Да простят меня апологеты Red Hat и знатоки rpm, но сообщение это, на мой взгляд, излишней внятностью вряд ли будет отличаться. Скорее всего, смысл его сведется к тому, что для установки нашего пакета не хватает файла типа имя_рек.so.6 или чего-то подобного. Интуитивно ясно - не хватает некоего библиотечного файла. Однако определить, в каком пакете этот файл следует искать, из полученного сообщения не так просто - имя недостающего файла в общем случае отнюдь не обязано совпадать (или даже хоть как-то коррелировать) с именем пакета, в состав которого он входит. То есть с большой степенью вероятности пользователь столкнется с проблемой зависимостей и их разрешения.

Опять же, коллеги-rpm'щики, не бросайте в меня комнями под названием RTFM'иты (собакит - это горная порода, которой только в собак бросать, RTFM'ит - то, чем бросают в незадачливых юзеров). Теоретически я знаю, что существует штатный способ решения этой проблемы, и даже не один (включая самый радикальный - опцию --nodeps). Однако вот сейчас, почти через два года после последнего плотного общения с rpm-based дистрибутивами, указать такие способы на память для меня было бы затруднительно. И в любом случае, методы решения проблемы зависимостей будут специфичны для дистрибутива (точнее, для принятого в нем формата пакетов). И навыки, наработанные в Red Hat, мало помогут при работе в Debian или Slackware.

Во избежание недоразумений замечу, что о методе apt, предусматривающем автоматическое получение пакетов и их установку, причем с обеспечением контроля зависимостей, мне также известно. Более того, я знаю, что метод этот имеет все шансы стать универсальным для пакетных дистрибутивов, вне зависимости от формата пакетов. Однако при использовании apt возникает другая проблема - проблема зависимостей избыточных. Что это такое - я подробно рассмотрю ниже. Пока же замечу только, что пользователь оказывается в зависимости от зависимостей (пардон за тавтологию), предопределенных майнтайнером пакета или составителем дистрибутива. А выливается это в то, что установка консольного файлового менеджера или текстового редактора тянет за собой Иксы, Gnome и... да чего только не тянет. Причем - из Сети, по модему или выделенке с прогрессивной оплатой трафика. Главное же - пользователь лишен возможности влиять на процесс установки. Поэтому, если его потребности не укладываются в рамки, установленные сборщиками дистрибутива или манйтайнерами пакетов, перед ним остается единственный путь - ручная сборка программ из исходников.

Можно возразить, что в состав любого клона Red Hat наряду с бинарными пакетами входят также исходники в формате rpm (*.src.rpm), работа с которыми возможна средствами все той же программы управления пакетами. Однако это - не панацея на все случаи жизни. Построение пакета из *.src.rpm осуществляется по правилам, описанным в соответствующем spec-файле - то есть опять же по произволу разработчика, а не нашего конкретного пользователя. Конечно, последнему вольно править spec-файл в соответствие со своими потребностями. Однако скажите на милость, чем это легче, чем просто ручная сборка по универсальной схеме ./configure -> make -> make install? И еще - я не силен в rpm-билдинге, но (ИМХО) вряд ли достижимая последним способом гибкость настроек будет превзойдена сколь угодно изощренным конструированием spec-файла...

Возможности "портовика"

По другому поведет себя пользователь Source Based дистрибутива Linux или, скажем, чего-нибудь из клана BSD (коего можно назвать "портовиком"). В любой из этих систем к его услугам - целостный комплекс управления пакетами, известный под названием портов (в BSD-производных или CRUX Linux), портежей (в Gentoo Linux), sorcery (в Sorcerer Linux и его потомках). Конечно, и здесь исходники тащатся из Сети, а автоматическая система контроля зависимостей также полностью на совести майнтайнера порта (портежа). Однако а) эта система контроля может быть более (в Gentoo Linux) или менее (во всех прочих случаях) настроена глобально и б) от автоматической системы контроля всегда можно отказаться в пользу полуручного определения переменных или просто ручного конфигурирования.

Тем не менее, и перед пользователем портированных систем может встать необходимость ручной сборки по крайней мере отдельных пакетов. Например, тех, что еще не охвачены системой портов его дистрибутива, или просто очень новых версий. Да и в итоге гибкость ручной сборки все равно остается непревзойденной. Показательны слова Андрея Лаврентьева применительно к FreeBSD (цитирую по памяти, но за сохранение смысла ручаюсь): "любой уважающий себя сисадмин все равно пересоберет критически важные для него пакеты со своими настройками" (то есть фактически вручную - в портах FreeBSD нет средства, аналогичного переменной USE из Gentoo). А поскольку на настольной персоналке юзер - сам себе сисадмин, сказанное можно распростарнить и на любого (уважающего себя) пользователя.

Поговорим о зависимостях

Что же необходимо пользователю для сборки пакетов из исходников? Во-первых, набор программ, именуемых обычно инструментарием разработчика (хотя в данном случае они выступают как инструменты сборки). Что ж, инструменты эти есть в любом нормальном дистрибутиве (за исключением уж совсем пионерских). Вопрос только в том, были ли они установлены на стадии первичной инсталляции, или пользователь опустил их, убоявшись сложностей или посчитав ненужными.

Далее, необходимы основные системные библиотеки, такие, как glibc. Однако они заведомо имеются в любом полнофункциональном дистрибутиве - без них невозможна работа ни одной программы.

И наконец, не выскажу великой премудрости, заметив, что для сборки пакетов из исходников необходимы исходники пакетов. А вот тут-то начинаются сложности. Конечно, свежий тарбалл дерева исходных текстов легко скачать из Сети. Хотя легко ли - для таких монстров, как XFree86, OpenOffice, KDE или GNOME речь может идти о сотнях мегабайт, что выдержит не каждый канал (и не каждый кошелек).

Однако будем считать это трудностями практической реализации. Но есть и принципиальная сложность - это все те же пресловутые зависимости пакетов. Если системы типа apt, порты или портежи пользователя от этой проблемы освобождают, то при полностью ручной сборке он остается с ней один на один. Не говоря уж о том, что за счет зависимостей необходимость в объемах скачивания существенно возрастает: достаточно посмотреть на debian.org (а это одно из лучших мест для изучения зависимостей пакетов) список софта, необходимого для сборки и функционирования какого-либо не очень сложного пакета, типа Midnight Commander.

А уж если речь пойдет о XFree86... Дав в Gentoo Linux команду

$ emerge --pretend --fetchonly xfree

мы обнаружим в списке необходимого и модули для поддержки планшетов wacom, и драйверы для видеокарт 3Dfx, и какие-то шрифтовые файлы, и многое, многое другое (причем - вне зависимости от того, есть ли у нас в системе графический планшет и нужны ли нам на самом деле дополнительные шрифты).

Однако не так страшен черт, как его малюют. И в роли святой воды здесь выступит понимание простой истины: зависимость зависимости lupus est. Иначе говоря, есть пакеты, абсолютно необходимые для сборки и работы данной программы, и есть пакеты, лишь придающие нашей программе некоторые дополнительные свойства.

Во всех описаниях зависимостей, какие мне доводилось видеть, различий между абсолютными (или жесткими) зависимостями, и зависимостями опциональными (можно сказать, мягкими) в явном виде не проводится. Единственное исключение - это документация Gentoo.org по работе с портежами (все, имеющее отношение к портежам, переменной USE и связанных с этим материях, доступно и в русском переводе на Gentoo.ru). Конечно, в ней не следует искать рецептов на все случаи жизни, но пониманию принципиальных различий между абсолютными и опциональными зависимостями чтение ее очень поспособствует - различе это там проводится очень последовательно. И вообще, знакомство с системой портежей, желательно практическое, - второй эффективный способ изучения взаимозависимостей пакетов.

Очевидно, что без абсолютных зависимостей данный пакет не удастся ни собрать, ни запустить. Любая программа для Linux требует каких-либо функций из главной системной библиотеки glibc, практически все консольные приложения используют какую-либо терминальную библиотеку типа ncurses, для Иксовых программ потребуется библиотека xlib, KDE-приложения не могут существовать без библиотеки Qt, и так далее.

Это - очевидные примеры. Почти столь же ясно, что программы для работы с графикой, от простенькой fbgrab до GIMP, немыслимы без библиотек графических форматов (png, gif, tiff, jpeg), а программы работы со звуком - без мультимедийных библиотек для соответствующих форматов (mpeg там, или ogg-vorbis). Менее очевидно, почему для сборки (и работы) Mozilla, не имеющей никакого отношения к GNOME, необходима библиотека Gtk - однако случаи такого рода на первых порах можно просто запомнить.

Легко видеть, что в качестве абсолютных зависимостей обычно выступают разного рода библиотеки. И основная (и чуть ли не единственная) сложность здесь - в соответствии версий. Так, одни из приложений, базирующихся на библиотеке Gtk, требуют непременно второй ее версии, тогда как другие - удовлетворяются первой (а со второй, к слову сказать, просто не собираются). Однако во многих случаях жесткая зависимость существует только для версий некоторого диапазона (или, скажем, для данного пакета требуется некая библиотека версии не ниже ##).

Однако не всегда в качестве абсолютных зависимостей выступают библиотеки. Как правило, в списке зависимостей практически любой программы, выводимой командой emerge (в Gentoo Linux) можно увидеть такие утилиты для работы с текстами, как grep, gawk, sed. Казалось бы, кокое отношение они могут иметь к программе, скажем, обработки растровых изображений? Действительно, никакого, однако утилиты эти учавствуют в работе установочных сценариев, и потому необходимы на стадии конфигурирования и сборки пакета.

Из предыдущего абзаца ясно, что некоторые из абсолютных зависимостей требуются только на стадии установки пакета, тогда как другие необходимы и для его запуска и функционирования. Последние называются часто run dependes, и список их обычно короче зависимостей для установки (величаемых просто denendes). В Gentoo Linux переменная DEPEND определяется обычно через значение переменной $RDEPEND плюс необходимые для сборки пакеты.

Однако без абсолютных зависимостей при сборке пакета обойтись невозможно, не важно, лежит ли связь его с какими-либо библиотеками или приложениями) на поверхности или глубоко опосредована. А вот опциональные зависимости, строго говоря, для сборки и функционирования пакета не являются необходимыми. И хотя многие из них часто определяются как умолчальные разработчиками или сборщиками, - именно в этом причина упомянутой выше избыточности зависимостей, столь типичной для бинарных пакетов, - отделаться от них при сборке из исходников можно, причем различными способами.

Так, практически любая программа под абстрактный Unix в списке своих зависимостей обнаружит groff, а программы проекта GNU - еще и Texinfo. Не потому, что эти системы обработки текста сами по себе нужны, скажем, аудиоплейеру, а лишь вследствие использования их системами экранной документации man и info, соответственно. А вот без этих систем экранной документации ни одна из уважающих себя программ не обходится. И потому отсутствие в системе, скажем, Texinfo вызовет ошибку при компиляции или установке практически любого GNU-приложения.

Конечно, совсем без экранной документации жить не очень интересно. Однако я, например, практически не использую info-страницы (по мне, это одно из самых GNU'тых и мрачных изобретений мрачных бородатых GNU-хакеров). И потому в моей домашней самостройной системе Texinfo не установлен. А дабы предотвратить появление ошибок на стадии make или make install, я после выполнения конфигурирования просто удаляю вручную каталог с соответствующей документацией.

Однако это пример правки даже не напильником, а скорее топором. Потому что сценарий конфигурирования именно для того и предназначен, что бы отключить опциональные зависимости. Ну и конечно, включить, буде такая потребность возникнет. Только вот что-то не припомню, чтобы мне требовалось что-то включать...

В одних случаях такое отключение происходит само собой: сценарий конфигурирования, выполняя проверку системы, обнаруживает наличие отстутсвия какой-либо библиотеки и просто игнорирует соответствующую функцию в собираемом пакете. В других случаях, это приходится делать в явном виде с помощью опций --disable или --without в командной строке скрипта ./configure.

К слову сказать, отключение ненужных зависимостей не только способствует уменьшению входящего трафика (да и экономии места на диске, хотя это при нынешних их объемах не столь актуально: важнее, на мой взгляд, то, что система освобождается от компонентов непонятного происхождения, сплошь и рядом по мановению руки майнтайнера возникающих во всех почти пакетных дистрибутивах). Оно еще и повышает удобство использования некоторых программ. В качестве примера приведу свой любимый gpm - по мне, так MC и особенно links, собранные без его поддержки, много более "юзабильны". Хотя от самой по себе консольной мыши я отказываться отнюдь не намерен - нет более удобного средства перекинуть URL из консоли с links в консоль с текстовым редактором...

Наконец, отключение ненужных зависимостей в некоторых случаях оказывает благотворное влияние и на быстродействие программ. Однако тут уже от проблемы зависимостей мы плавно переходим к вопросам оптимизации, а это - несколько другая история.

Кое-что об оптимизации

Ибо главная опцимизация происходит на стадии собственно компиляции, в ходе выполнения программы make, и задается она специальными флагами компилятора. Главные из них - задание уровня оптимизации (от -O0, то есть без оптимизации вообще, до -O3) и оптимизация под конкретный процессор (флаги -mcpu= и -march=, в качестве значений которых выступают имена процессоров, подерживаемых текущей версией gcc - а версия его 3.3 поддерживает весь зоопарк x86-совместимых процессоров). Исходя из общих соображений, можно предположить, что для графических и мультимедийных приложений действенной окажется оптимизация под классический сопроцессор или специальные наборы инструкций типа SSE или 3Dnow (флаг -mfpmath= со значениями 387 или sse, соответственно, и однин из флагов -msse, -msse2 или -m3dnow, в зависимости от типа процессора), однако практически этот вопрос я еще не исследовал.

Вообще флагов у компилятора gcc много, и служит они не только оптимизации, но и сохранению отладочной информации (или, напротив, очистке от оной), повышению быстродйствия самого процесса, да и многим другим целям (за деталями следует обратиться к man gcc текущей версии). В итоге командная строка make может стать весьма длинной, и вводить ее каждый раз вручную покажется скучновато. Избежать этого просто - достаточно определить в профильном файле (общесистемном или пользователя, выполняющего сборку) переменную

CFLAGS="флаги и значения, разделенные пробелами"

для программ на Си, и переменную

CXXFLAGS="$CFLAGS"

для программ на Си++, не забыв их экспортировать (любителям tcsh - определив через setenv). Только нужно иметь ввиду: говорят, что отдельные программы могут отказаться собираться с флагами типа -O3 и -march, хотя мне такие не встречались (с gcc версии 3.1 и выше).

К слову сказать, можно пересобрать и сам компилятор gcc - в дальнейшем это будет весьма способствовать ускорению процесса. Особенно эффективен в этом отношении т.н. bootstrapping - пересборка gcc самим собой, что выполняется командой

$ make bootstrap

а флаги оптимизации определяются в переменной типа

BOOT_CFLAGS="-O3 -march=pentium4 -mfpmath=sse -msse2"

(нужное вписать). По существующим оценкам, это может дать до 30-процентного выиграша в скорости компиляции, например, ядра системы. Я количественных замеров не делал, но по субъективным оценкам - это близко к истине.

Еще один способ увеличить быстроту компиляции (а для пакетов типа Xfree86, не говоря уж об OpenOffice, этот процесс займет не один час даже на весьма мощной машине - у меня перед глазами пример Pentium-4/2,53 Ghz) можно реализовать при большом количестве оперативной памяти. Для этого требуется также поддержка виртуальной файловой системы tmpfs, что задается при конфигурировании ядра, и монтирование ее в какой-либо каталог типа /tmp, /dev/shm (штатное для нее место) или /var/tmp. И все действия по распаковке тарбаллов, конфигурированию, сборке следует выпаолнять именно в этом каталоге. Правда, для больших пакетов выигрышь во времени оказывается не очень большим (процентов 10, однако, будет). Зато мелкие - собираются просто с песнями.

Оценка выигрыша в итоговом быстродействии собранных пакетов также очень субъективна. Как, впрочем, и все оценки быстродействия Linux вообще. Ибо "умолчальный" Linux по определению нонсенс (в отличие от "умолчальных" Windows) - пользователь, не настраивающий его под себя, подлежит товарищескому суду Линча с принудительным переводом в "подоконники" (копирайт Владимира Игнатова). А какие настройки оказалались более весомыми для итогового быстродействия - определить веьсма трудно.

Из собственной практики. Завершив свой скорбный труд по сборке самостройной системы (метод Pure Linux с некоторыми модификациями, комплектация пакетами - в объеме сильно разгруженного CRUX'а), я был просто потрясен скоростью ее работы. Однако что при этом оказалось более весомым - оптимизация ли под Pentiun-4 (примерно с приведенными выше флагами), просто превосходство текущего gcc (версия 3.3) над его предшественниками, разгрузка ли стартовых сервисов и демонов или оптимизация ядра, - судить не возьмусь. А повторять процесс поэтапно с количественными замерами - потенции в себе пока не чую. Что, впрочем, можно рассматривать как довод в пользу комплексного подхода при самострое.

Заключительные замечания

Однако затеял я этот разговор не в контексте самостроя, а с несколько иными целями. Во-первых, дабы привлечь внимание пользователяей пакетных дистрибутивов (для пользователей Source Based я, вероятно, не сказал ничего нового) к реальной альтернативе их родным системам управления пакетами - ручной сборке. По крайней мере, для критически важных приложений.

Вторая цель - еще раз подчеркнуть: каким бы дистрибутивом Linux (или даже BSD) вы ни пользовались, база приложений в них едина. И представляет собой порождение движения за открытые исходники в самом широком смысле этого слова. А из самого названия движения ясно, что любое из этих приложений всегда доступно в исходных текстах, которые могут быть собраны самостоятельно по универсальной для всех открытых и свободных систем схеме.

Ну и третья цель - чисто практическая. Дело в том, что Linuxshop в ближайшее время планирует начать распространение тематических сборников программ в исходных текстах. Первый из таких сборников будет комплектоваться в меру нашего скромного разумения о том, что нужно народу в его повседневной деятельности. А в дальнейшем мы рассчитываем на обратную связь с этим самым народом. В частности, комплектация таких сборников будет одним из вопросов, планируемых к обсуждению на грядущей выезной сессии Linuxshop-форума.

В заключение не могу не выразить признательность Владимиру Попову, постоянному автору UNIX4all: все изложенное выше было окончательно осмыслено в процессе нашей переписки и личного общения.





Источник - LinuxBegin.ru
http://linuxbegin.ru

Адрес этой статьи:
http://linuxshop.ru/linuxbegin/article349.html