|
| ||||||||||||
| ||||||||||||
Введение в POSIX'ивизм(C) Алексей Федорчук, 2005Опубликовано на сайте LinuxCenter Глава 14. Принципы сборки и установки пакетовКак уже говорилось в главе 2, дистрибутивы Linux организованы по пакетному принципу. Точно также, в виде пакетов, распространяются и любые программы, создаваемые независимыми разработчиками (из которых в основном и собираются дистрибутивы Linux). А в BSD-системы попакетно включаются все приложения, не входящие в состав базового комплекта. И потому одна из важных задач пользователя - это интеграция пакетов в свою систему. Содержание
Очень элементарное введениеВ большинстве случаев эта задача решается за пользователя разработчиками его операционки: системы управления пакетами (менеджеры пакетов) составляют неотъемлемую часть любого дистрибутива Linux, Free- и прочих BSD. Однако в ряде случаев пользователю приходится устанавливать пакеты и самостоятельно. К тому же понимание сути этого процесса зело способствует уяснению того, что же делают пакетные менеджеры, и помогает принять правильное решение в нештатных ситуациях. Так что вопрос этот заслуживает подробного рассмотрения. Само по себе выражение "сборка программы" часто повергает начинающего пользователя в некий священный трепет - по себе помню. Однако в этой главе я постараюсь показать, что ничего сверхъестественного в этом процессе нет, и выполнение его по силам любому пользователю, вне зависимости от чисто программистской квалификации - собственно говоря, никаких навыков программирования он не требует, а только - некоторых предварительных знаний. Однако сначала - маленькое введение для тех, что компьютерное образование начиналось не с книжек Брябрина и Фигурнова, а с руководств типа "Word за 5 минут" или "Quark Press для полных идиотов" (это - не в обиду читателям, но в упрек писателям). Все прочие могут смело пропустить нижеследующие элементарные рассуждения. Так вот, программы, то есть наборы инструкций, предписывающие машине выполнить то или иное действие (от самых элементарных, типа - скопировать файл, до предельно сложных), сочиняются программистами на языках программирования:-). И представляют они собой самые обычные тексты (называемые исходными текстами, исходниками или, уж совсем жаргонно, сырцами - sources), в которых необходимые инструкции описываются в соответствие с принятыми в данном языке правилами (синтаксисом языка). Собственно говоря, именно это мы и проделывали в параграфе о сценариях из главы 12. Однако не следует через чур уж очеловечивать компьютеры и ожидать от них способности понимать какой-либо "человеческий" язык, даже такой формализованный, как язык программирования. Нет, они способны воспринять только собственные инструкции (зависящие от центрального процессора), представляемые в бинарном виде - то есть последовательностей нулей и единиц. Собственно, и сами эти цифры - выше их понимания, каковое не выходит за пределы наличия/отсутствия электрического сигнала, но в такие глубины мы лезть уже не будем. Так что для выполнения программы она в конечном счете должны быть транслирована в соответствующие, зависящие от архитектуры процессора, машинные инструкции. И выполняется этот процесс двояко - посредством интерпретации или компиляции. Интерпретация - это последовательный перевод языковых конструкций из символов латинского алфавита (и прочих, специальных) в машинные инструкции по мере их ввода. Простейший процесс интерпретации - это ввод директив в командной оболочке (почему она часто называется также командным интерпретатором), а также обработка ее сценариев - наборов элементарных команд. Сколь бы ни был сложен такой сценарий, выполняется он последовательно: сначала интерпретируется команда 1, потом - команда 2, и так далее. При интерпретации никакого изменения исходного текстового файла не происходит. А сам он отличается от простого списка команд только тем, что имеет бит исполнения. Однако интерпретируемая программа не может исполняться сама по себе, для ее запуска требуется соответствующая среда - программа-интерпретатор (например, та же командная оболочка). Процесс интерпретации выполняется каждый раз при исполнении программы. И, соответственно, каждый раз время затрачивается на выполнение одних и тех же процедур. Так что возникает резонный вопрос - а нельзя ли оттранслировать исходник программы в машинные инструкции раз и навсегда, и в дальнейшем запускать на исполнение уже набор таких инструкций, не затрачивая время на их преобразование? Ответ - столь же резонен: ну конечно же, можно. И процедура эта называется компиляцией, а выполняющие ее программы - компиляторами. В ходе этой процедуры из исходного текстового файла по определенным правилам образуется файл бинарный, образованный, если просмотреть его в текстовом редакторе, последовательностью неудобопонятных символов. И - пригодный для автономного исполнения, для чего необходимости в породившем его компиляторе уже нет. Предварительно оттранслированные (прекомпилированные) программы, по вполне очевидным причинам, выполняются много быстрее, чем программы интерпретированные, причем разница в скорости нарастает с объемом. И потому все масштабные программы, как правило, пишутся в рассчете на использование в откомпилированном виде. Хотя и роль интерпретируемых программ - сценариев разного рода - в POSIX-системах не стоит недооценивать. В соответствие с дальнейшим предназначением программ при их написании выбираются инструментальные средства для этого, то есть в первую очередь языки программирования. Которые, таким образом, подразделяются на интерпретируемые и компилируемые. К первым принадлежат постоянно упоминавшиеся ранее языки командных оболочек. Однако ими список интерпретируемых языков не исчерпывается. В POSIX-системах широко используются такие мощные средства, как Perl, Python, Ruby, Tcl/Tk. Они предоставляют большие возможности, вплоть до создания графических пользовательских инструментов. Однако принципиально написанные на них программы ничем не отличаются от сценариев командной оболочки. Компилируемых языков - также великое множество, начиная с пресловутого Basic'а и заканчивая специализированными средствами типа Fortran. Однако в POSIX-системах наибольшее значение имеют программы на языке C. Который, собственно, и создавался для разработки первозданного Unix. И на котором написана большая часть ядра всех POSIX-совместимых систем (а ядро ОС - это почти такая же компилируемая программа, как и любая другая), а также большая часть их приложений. Так что далее речь пойдет о сборке преимущественно C-программ. Однако для нас это существенного значения не имеет - ведь собственно программированием заниматься мы не будем, а принципы сборки практически не зависят от используемого языка. В частности, точно по той же схеме собираются и графические приложения, в которых широко используется язык C++. Кроме собственно программ, предназначенных для непосредственного исполнения, существуют еще так называемые разделяемые библиотеки, или библиотеки функций (соответствующего языка программирования). Что это такое - проще пояснить на примере. Все программы, вне зависимости от их назначения, неизбежно должны выполнять некоторые однотипные действия, как то: открыть файл, закрыть его, вывести на экран и так далее. Сущность их не меняется, что бы программа не делала. И потому нет никакого смысла программировать такие манипуляции каждый раз заново. Вот их, как правило, и не программируют. А объединяют соответствующие директивы в отдельные программные комплексы, именуемые библиотеками. Сами по себе они к автономному исполнению не пригодны. Однако любая программа, при необходимости совершить одно из типовых действий, вызывает из такой библиотеки некий фрагмент кода, содержащий требуемую последовательность директив. Библиотеки обычно привязаны к определенным языкам программирования, синтаксису которого подчиняются описания директив (т.н. функции - о них вкратце говорилось в заключении главы 12). Поскольку наиболее употребимым в POSIX-системах и их приложениях является язык C, то его функции и требуются чаще всего. Они собираются в главную системную библиотеку, которая именуется обычно libc (Library C), хотя реально это разные комплексы, отличающиеся полнотой функций и их описанием и зависящие от конкретной операционной системы. В подавляющем большинстве дистрибутивов Linux используется реализация главной системной библиотеки, именуемая Однако Правила сборкиНу все, с элементарным введением покончено. Переходим собственно к пакетам и их сборке. Как явствует из названия (и из главы 1), все открытые и свободные программы и разделяемые библиотеки распространяются их разработчиками в исходных текстах. Конечно, никто не запрещает им создавать и прекомпилированные версии своих творений, и многие создатели программ так и поступают, предлагая один или несколько вариантов таковых, рассчитанные обычно на наиболее распространенные дистрибутивы Linux, иногда - FreeBSD, реже - другие BSD-системы (по причинам, которые станут ясными в последующем, прекомпилированные версии зависят от множества факторов, из которых целевая ОС - не последний). Однако это - скорее исключение чем правило. Наборы исходников объединяются разработчиками в т.н. пакеты, о которых уже упоминалось на протяжении всего этого повествования. Пакет - понятие очень широкое и многогранное. Это может быть и простая монофункциональная утилита (например, строчный текстовый редактор Следует оговориться, что термин пакет (английское package) постоянно употребляется в двух смыслах: как набор исходных текстов и как комплект скомпилированных из него программ и всех их служебных файлов. Обычно различие между ними ясно из контекста, в случаях же неоднозначности тот или иной смысл будет оговариваться явно. Пакеты принято распространять в виде компрессированных архивов - файлов вида Прекомпилированные пакеты подчас также распространяются разработчиками в виде точно таких же тарбаллов. Но тут уже корреляции пакет - тарбалл может и не быть. Так, XFree86, кроме исходников, доступен также в виде серии скомпилированных пакетов для нескольких дистрибутивов Linux, Free- и OpenBSD. Но это уже - именно самостоятельные пакеты, и не все они обязательны к установке. А сборщики дистрибутивов могут и далее дробить изначально единый пакет, как это обычно делается с теми же Иксами. И еще. В предыдущих главах я говорил, что BSD-системы, в отличие от Linux, не имеют пакетной организации. Это не совсем точно. Конечно, например, из FreeBSD Distributions нельзя выделить ядро системы или наборы базовых утилит в виде отдельных пакетов. Однако сам по себе он - в сущности единый пакет, только очень большой. А то, что перед пользователем (на CD ли диске, или на ftp-сервере) он предстает перед пользователем в виде кучи мелких (по 1,44 Мбайт) пакетиков - просто наследие тех времен, когда системы еще устанавливались с дискет. В NetBSD и OpenBSD же базовая система собрана в виде единого тарбалла (так и называемого - base.tgz), и уже совсем ничем не отличается от обычных пакетов. В последнее время и в некоторых дистрибутивах Linux прослеживается тенденция отказа от "квантования" базовой системы. Так, в Gentoo она вся собрана в три тарбалла (stage1, stage2, stage3), и может быть развернута (с различной полнотой, в зависимости от схемы инсталляции) из любого из них. А в Sorcerer и его клонах базовый тарбалл вообще единственный. Однако я отвлекся, вернемся к нашим исходниками и посмотрим, что с ними нужно делать - ведь ясно, что в том виде, в каком они распространяются, использование их невозможно. Для того, чтобы программа, распространяемая в исходниках, могла выполнять свои функции, она должна быть собрана. Процесс этот в общем случае разбивается на три стадии:
Конфигурирование - это приведение программы в соответствие с реалиями конкретной системы. Как неоднократно говорилось, подавляющее большинство свободного софта пишется в рассчете на некую абстрактную POSIX-совместимую ОС. Конкретные же их представители отличаются друг от друга многими деталями, в частности - функциональностью библиотек, их названиями и расположением. Что и проверяется в процессе конфигурирования. То есть основное назначение его - проверка так называемых зависимостей пакетов. Понятие зависимостей - одно из основных при сборке программ. Суть его в том, что пакет pkgname1 для установки и (или) функционирования требует наличия в системе пакета pkgname2, тот, в свою очередь, может потребовать пакета pkgname3, и так далее. В качестве зависимостей выступают часто (хотя и не всегда) те самые системные библиотеки, о которых говорилось ранее. Зависимости пакетов бывают разными. С одной стороны, различают зависимости при сборке (обычно называемые просто зависимостями - depends) и зависимости при запуске (по английски именуемые run depends). Как следует из названий, при зависимости пакеты, от которых зависит данный, необходимы только на стадии сборки пакета, тогда как зависимости второго рода действуют постоянно. В большинстве случаев depends и run depends эквивалентны, однако это правило имеет многочисленные исключения. Так, при статической сборке (что это такое - будет говориться чуть позднее) библиотеки, которые использует данный пакет, требуются только в момент компиляции - в дальнейшем необходимости в них не возникает. С другой стороны, следует различать зависимости жесткие и "мягкие". Удовлетворение первых абсолютно необходимо для сборки данного пакета. Так, практически любая программа использует (статически или динамически) главную системную библиотеку "Мягкие" зависимости данного пакета не критичны для его функционирования - удовлетворение их лишь добавляет ему дополнительные функции (которые могут оказаться и лишними). Понятие зависимостей пронизывает насквозь POSIX-совместимые системы, и особенно важно для свободных их представителей. В то же время пользователи Windows с ним сталкиваются очень редко, и потому постижение его вызывает определенные трудности у недавнего подоконника. Это связано с двумя факторами. Во-первых, традиционная модель разработки Unix-программ (то, что задумчиво именуют Unix Way) характеризуется ярко выраженным стремлением не множить сущности без крайней необходимости. Или, говоря попросту, не изобретать велосипеды. То есть: если требуемая разработчику данной программы функция уже реализована и включена в какую-либо распространенную библиотеку, то наш разработчик скорее всего этой библиотекой и воспользуется, а не будет переписывать ее с нуля. Благо, поскольку все распространенные и общеупотребимые библиотеки открыты, он имеет полную возможность это сделать (вспомним о смертном грехе лености). Возможно, разработчик Windows-программы с удовольствием последовал бы примеру братьев-POSIX'ивистов. Однако исходники Windows-библиотек в большинстве своем закрыты и защищаются всякого рода проприетарными лицензиями, препятствующими их свободному использованию. И потому Windows-разработчику волей-неволей приходится реализовывать требуемые ему функции самостоятельно. В результате чего программа, хотя и приобретает определенную самодостаточность, но зато разбухает в размерах: вспомним, сколько файлов вида За конфигурирование обычно отвечает сценарий, расположенный в корне дерева исходников данной программы и, по соглашению, носящий имя Так, для консольных программ в Linux существует возможность использования мыши в качестве указательно-позиционирующего устройства (а не только для выделения/вставки экранных фрагментов, как это имеет место в BSD-системах). Обеспечивается эта функция специальным сервисом - Если процесс конфигурирования завершается успешно, в корне дерева каталогов создается специальный файл - Если конфигурирование прошло с ошибками, выдается соответствующее сообщение, форма которого также целиком определяется разработчиком. Ошибки эти могут быть связаны с нарушением жестких зависимостей пакета, и в этом случае никакие дальнейшие действия, до их разрешения, невозможны. Если же конфигурационный сценарий выявил нарушение "мягких" зависимостей, то пользователь обычно может отказаться от них, просто потеряв некоторую дополнительную функциональность. Которая, к тому же, вполне может быть ему не нужной. Так, например, я всегда отказываюсь от поддержки мыши (через Образцово-показательный отчет о выполнении сценария Наконец, конфигурирование завершилось успешно. Наступает время следующего этапа - собственно сборки, то есть претворения исходных текстов программы в исполняемый машинный код. Этап этот распадается на несколько стадий. Первая стадия - собственно компиляция исходного текста в бинарный код, завершающаяся формированием т.н. объектного модуля. Это - как правило, еще не готовая к запуску программа. Почему? Да потому, что в его скомпилированном коде может не быть многих стандартных функций - тех самых, которые разработчик предполагал заимствовать из разделяемых библиотек. И потому вторая стадия - это связывание (linking, в просторечии именуемое линковкой) сгенерированного кода с необходимыми библиотечными фрагментами. Линковка может быть двух видов - статическая и динамическая. В первом случае требуемый код из библиотеки встраивается внутрь собираемой программы, после чего получается готовый к исполнению бинарный файл, более в библиотеке не нуждающийся. Это - именно тот случай, когда понятия depends и rdepends приобретают разное значение: первое оказывается шире. Второй случай - динамической линковки, - предполагает, что библиотечный код не встраивается в программу: вместо него устанавливается только ссылка на файл библиотеки и требуемую функцию (имя которой извлекается из так называемого заголовочного файла - header-файла). И в дальнейшем, при запуске исполняемого модуля программы, соответствующие библиотечные фрагменты извлекаются с диска и присоединяются к коду программы уже только в оперативной памяти. При этом сущности depends и rdepends оказываются идентичными: библиотека, с которой программа связывается при сборке, столь же необходима и для ее запуска. Динамическая линковка приводит как к сокращению размера исполняемого файла, так и уменьшению объема оперативной памяти, задействуемого при запуске программ (особенно если они используют одни и те же библиотечные функции - а в большинстве случаев так оно и есть). И потому именно она преимущественно используется при сборке программ для свободных POSIX-систем (повторю еще раз, что разделяемые библиотеки в них открыты и могу применяться без ограничений). Однако бывают ситуации, когда приходится прибегать к линковке статической. Так, во FreeBSD статически линкуются с главной системной библиотекой жизненно важные для запуска и восстановления системы утилиты (в предыдущих версиях этой ОС они располагались в каталогах Третий этап процесса сборки - инсталляция. Это - инкорпорация всех компонентов программы в структуру файловой системы данной машины. Или, по простому, по бразильскому - их копирование в соответствующие каталоги файлового древа (по завершении сборки они могут находиться в самых разных местах, обычно - в подкаталогах дерева исходников). Как правило, для разных компонентов пакета существуют традиционно предопределенные имена каталогов, в которых они должны размещаться: Предопределенные имена каталогов не обязательно будут ветвями корня файловой системы (типа Так вот, процесс инсталляции и сводится к тому, что исполняемый файл (файлы) собранного пакета копируется в файл По завершении всего сборочного цикла из пакета исходников должна получиться полнофункциональная, готовая к употреблению, программа, требующая лишь некоторой пользовательской настройки. Три волшебных словаТеперь, разобравшись с принципами, посмотрим, как сборка пакетов осуществляется на практике. Понятное дело, что перво-наперво тарбалл исходников следует декомпрессировать и развернуть в каком-либо подходящем каталоге. Для самостоятельно собираемых исходников я использую обычно каталоги вроде Сама по себе распаковка делается обычным образом, например, командой $ tar xzpvf /path_to_src/tarball.tar.gz или $ tar xjpvf /path_to_src/tarball.tar.bz2 в зависимости от использовавшейся для компрессии программы ( Опция А вот опция Если пакет состоит из нескольких тарбаллов, все они должны быть распакованы. Повторять несколько раз какую-либо из приведенных выше команд было бы скучно - однако эту процедуру можно выполнить в один присест. ИМХО, самый простой способ для этого - прибегнуть к универсальной утилите
$ find /path_to_src -name *.tar.gz -exec tar xzpvf {} \;
В результате любой из описанных процедур в текущем каталоге должен образоваться подкаталог вида $ tar -tzvf tarball.tar.gz где опция Дальнейшие действия по сборке пакета в большинстве случаев осуществляются путем последовательной отдачи трех команд - Первую из этих трех команд следует давать, перейдя предварительно в корень дерева исходников нужного пакета: $ cd /path2srcpkg $ ./configure Это - запуск того самого конфигурационного скрипта, о котором давеча говорилось. Обращаю внимание на Сценарий $ ./configure --help и следует начинать самостоятельную сборку любого пакета, особенно - не знакомого. Прочитав предварительно файлы В аккуратно написанных программах вывод команды Installation directories: в которой указываются каталоги, куда в дальнейшем будут устанавливаться отдельные компоненты собранного пакета. Важнейшей опцией здесь является Так что если желательно размещение компонентов собираемого пакета в каталоге, отличном от умолчального (например, в ./configure --prefix=/usr В последнее время некоторые пакеты предполагают установку по умолчанию в подкаталоги каталога А вообще, значение опции Далее в той же секции обычно имеет место быть опция $ ./configure --bindir=/bin или, для программ административного назначения, - $ ./configure --bindir=/sbin Нередко в секции Следующая почти непременная секция вывода помощи конфигурационного скрипта - Optional Features: Именно в ней, как легко понять из названия, перечисляются опции, позволяющие подключить/отключить дополнительные возможности собираемого пакета. Они имеют вид --enable-FEATURE и --disable-FEATURE Первая, естественно, подключает возможность с именем FEATURE, а вторая - отключает оную. Причем одна из этих опций может быть принята по умолчанию. Например, большинство свободных программ ныне собирается с поддержкой национальных языков (National Labguage Support - NLS), обеспечивающих вывод сообщений, пунктов меню, помощи т т.д. на языках, отличных от американского (при наличии соответствующих ресурсов, разумеется - если систему помощи некоего пакета никто не удосужился перевести на русский язык, то включай NLS, не включай - все едино, русского хелпа от нее не получишь). Однако в ряде случаев это может показаться нежелательным - и тогда при конфигурировании программы нужно задать соответствующею опцию: $ ./configure --disable-nls Обычно допустима и иная форма этой опции: $ ./configure --enable-nls=no Функции, подключаемые (или отключаемые) посредством опции Сходный смысл имеют опции, входящие в секцию Optional Packages: общий вид которых таков: with-PACKAGE или without-PACKAGE Отличие опций этой секции от тех, что перечислены в секции Опции вида Так, в приводимом ранее примере с поддержкой указующе-позиционирующих свойств мыши при сборке консольных программ типа $ ./configure --without-gpm И последняя секция, практически всегда присутствующая в выводе помощи конфигурационного скрипта - Some influential environment variables: Как следует из названия, здесь перечисляются различные переменные окружения, могущие оказывать влияние на процесс компиляции. Наиболее часто в качестве таких переменных предусматриваются флаги компилятора $ ./configure CFLAGS="-O3 -march=pentium4" обеспечит максимальный уровень оптимизации (значение Ранее я говорил, что при сборке пакеты могут связываться с библиотечными функциями как статически, так и динамически. Так вот, характер связи также определяется на стадии начального конфигурирования. По умолчанию во всех известных мне случаях используется динамическая линковка. Что связать исполняемый модуль с библиотекой статически, требуется специальный флаг - $ ./configure LDFLAGS="-static" или, в случае линковки с несколькими библиотеками - $ ./configure LDFLAGS="-all-static" Вообще говоря, опции конфигурирования пакета могут быть очень разнообразны - я перечислил лишь наиболее часто встречающиеся. Общие рецепты эффектвиного их использования - а) внимательное чтение вывода помощи конфигурационного скрипта, б) знание особенностей своей собственной системы (в первую очередь - мест размещения разделяемых библиотек) и в) конечно же, здравый смысл. Надо отметить, что некоторые пакеты не имеют конфигурационного сценария в дереве исходников. Это не обязательно следствие лени разработчика: может быть, что программа настолько проста, что в предварительном конфигурировании не нуждается (например, не использует функций никакой разделяемой библиотеки). Другая возможная причина отсутствия скрипта Предварительное конфигурирование пакета - очень важный момент в его сборке: можно сказать, что успех ее на 90% определяется именно в результате исполнения скрипта Сама по себе команда Конечно, и команда $ make CFLAGS="-O3 -march=pentium4" предписать статическую линковку с разделяемыми библиотеками $ make LDFLAGS="-static" или предписать последующую установку компонентов пакета в каталог, отличный от умолчального. Однако - и все: далее остается только дожидаться успешного (надеюсь) окончания сборки. Однако у команды $ make world В других случаях для достижения того же результата может применяться и иная цель, например $ make all или указание на конкретную архитектуру, операционную систему, и т.д. Все такого рода исключения, как правило, описаны в сопроводительной документации. На худой конец (если таковой не имеется), допустимые для данного пакета цели команды Однако есть у команды $ make install После чего мы наконец получаем готовую к употреблению программу. В некоторых программах цель В принципе команду Дело в том, что стадии конфигурирования и сборки обычно могут быть выполнены от имени обычного пользователя - это определяется правами доступа к каталогу, в который было распаковано дерево исходных текстов пакета. А вот установка собранных компонентов почти наверняка потребует административных привилегий. Ведь исполняемые файлы пакета после директивы Так что, прежде чем начинать установку собранного пакета, следует озаботиться получением соответствующих полномочий командой $ su или, в некоторых случаях, даже $ su - которая приведет среду исполнения команды в соответствие с конфигурацией суперпользователя. Впрочем, иногда полномочия администратора могут оказаться необходимыми и при сборке программы или ее конфигурировании. Типичный тому пример - сборка ядра Linux штатными средствами, в ходе которой задействуются скрипты, требующие root-доступа. Впрочем, это - тема отдельной беседы. Вернемся к сборке "обычных", если так можно выразиться, пакетов. Все вышесказанное относилось к случаю сборки без ошибок на любом этапе. От каковых, однако же, никто не гарантирован. И что делать, если ошибки появляются? В случае ошибки при сборке пакета перед пользователем, как обычно, появляется два выхода: а) бросить это занятие, попробовав отыскать и установить прекомпилированный вариант пакета, и б) разобраться в причинах ошибки и попытаться ее устранить. Наиболее часто сообщение об ошибке возникает в ходе предварительного конфигурирования. И, в большинстве случаев, связано с нарушением зависимостей, жестких или "мягких" - обычно это можно понять, внимательно читая экранный вывод. С нарушением жестких зависимостей все ясно - нужно установить пакет, от которого зависит собираемый, и все - вариантов тут не предлагается. Нарушения же "мягких", но тем не менее принятых разработчиком по умолчанию, зависимостей обычно можно избежать посредством явного указания опций конфигурирования - типа Встречаются и ситуации кажущегося нарушения зависимостей, когда в ходе конфигурирования следует сообщение об отсутствии пакета имя рек, хотя пользователь точно знает, что таковой был установлен. В одних случаях это может быть связано с тем, что собираемый пакет ссылается не просто на некую библиотеку, но на конкретную ее версию. При этом, даже если в системе установлена более новая ее реализация, заведомо перекрывающая функциональность предыдущей (а совместимость сверху вниз - один из краеугольных камней программирования вообще и POSIX-программирования - в особенности), в ходе конфигурирования будет отмечено нарушение зависимостей. Разрешение такой коллизии - очень простое: создание символической ссылки вида $ ln -s libname.xxx libname.yyy где Другой случай - когда сценарий конфигурирования пакета ищет библиотеку, от которой он зависит, не в том каталоге, где она реально располагается. Так, старые приложения KDE могут ожидать требуемых им библиотек в каталогах типа И тут выход из положения не сложен. Во-первых, можно задать переменную окружения LDPATH="/opt/qt:/opt/kde" значения которой точно определяют каталоги соответствующих программных комплексов. Во-вторых, эти значения можно просто указать в качестве опций конфигурационного скрипта: $ ./configure --with-qt-dir=/opt/qt \ --with-kde-dir=/opt/kde Реже бывают ошибки при исполнении команды Ошибки при инсталляции связаны, почти всегда, с отсутствием прав на запись в каталоги, в которые помещаются устанавливаемые компоненты пакета. Иногда же они возникают вследствие того, что целевой каталог просто не существует. Например, в таких дистрибутивах Linux, как CRUX и Archlinux, из пакетов штатного комплекта изъята вся документация, кроме man-страниц. И, соответственно, отсутствуют каталоги для помещения документации в форматах info и html. А поскольку практически любая программа проекта GNU сопровождается info-документацией, попытка инсталляции ее вызывает ошибку. Побороть которую очень просто: нужно только уничтожить в дереве исходников соответствующие подкаталоги. На какой бы стадии ни возникла ошибка, перед повторной сборкой пакета дерево исходников следует очистить от побочных продуктов сборки предыдущей. Резонные люди рекомендуют просто стереть каталог с исходными текстами и развернуть его из тарбалла по новой. Это, конечно, самый радикальный способ, но не всегда приемлемый. Обычно можно обойтись и терапевтическими мерами. Потому что в большинстве пакетов предусматриваются специальные цели для таких процедур. Первая из них $ make clean Она вычистит дерево исходников от объектных модулей, собранных при предыдущей компиляции. Сохранив, тем не менее, созданные при конфигурировании Make-файлы. Если нужно избавиться и от них - существует цель S make distclean по исполнении которой дерево исходников теоретически должно приобрести первозданный вид. Пакеты, как правило, устанавливаются для того, чтобы запускать входящие в них программы. Однако не исключено, что первый же запуск новой программы показывает, что она делает что-то не то или не так, нежели это нужно пользователю. Или просто ему не нравится. И возникает вопрос - а как удалить такой неподходящий пакет? Можно, конечно, последовательно пройтись по всем каталогам, в которые записывались компоненты пакета, выявить их по каким-либо признакам (например, по атрибуту К счастью, большинство разработчиков, не страдающих манией величия, предусматривают такую ситуацию, определяя специальную цель для удаления программы. Обычно это - $ make uninstall реже - $ make deinstall Любую из этих команд нужно дать в корне дерева исходников - именно поэтому его желательно сохранять и после сборки, несмотря на непроизводительный расход дискового пространства: иначе единственным способом удаления пакета останется ручной. При деинсталляции команда Важно, что Дело в том, что самостоятельная сборка пакетов из исходников не предусматривает никакого механизма обновления их версий. Правда, на самом деле обладателю настольной машины обычно нет надобности гнаться за самыми актуальными версиями большинства пользовательских программ: это целесообразно делать только при существенном расширении их функциональности или обнаружении серьезных ошибок. Однако администратор системы вынужден отслеживать все обновления пакетов, связанные исправлением ошибок в безопасности системы. А поскольку, как я все время повторяю, каждый пользователь - это немного админ своего десктопа, необходимость в обновлении версий возникает достаточно часто. Так вот, единственный способ обновления собственноручно собранной программы - это собрать и инсталлировать ее по новой. При этом теоретически старые компоненты должны затереться новыми. Однако на практике от старой версии могу остаться хвосты, не только бесполезные, но, возможно, и конфликтующие с файлами новой версии. И потому при обновлении версий вручную обычно рекомендуется сначала удалить старую версию, и лишь затем инсталлировать новую. Предварительно убедившись, конечно, в успешности сборки ее (то есть выполнив стадии В то же время при смене версий, как правило, желательно сохранить все выполненные ранее настройки пакета - подчас это весьма трудоемкая процедура. И вот тут-то и оказывается, что пользовательские настройки удаленной версии остались в неприкосновенности в домашнем каталоге./p> Особенности сборки ядраЯдро свободной POSIX-системы - это (почти) такая же программа, как и любая другая, распространяемая в исходных текстах. И потому оно также должно быть приведено в пригодное к употреблению состояние посредством сборки. Однако ядро все же - не пользовательское приложение и даже не системная утилита, и потому процесс сборки ядра имеет свою специфику. Кроме того, ядро - это тот компонент, который и отличает в первую очередь одну POSIX-систему от другой - например, Linux от FreeBSD. Поэтому процесс сборки ядра для каждой операционки имеет свою специфику. Тогда как все сказанное выше касаемо сборки пакетов имеет силу (с очень незначительными оговорками) для любой POSIX-системы - причем даже не обязательно свободной: открытые приложения и утилиты для проприетарной Solaris или AIX в принципе собираются точно так же, как для Linux или какой-либо BSD. Тем не менее, сборка ядра любой из рассматриваемых ОС включает те же стадии, что и сборка иных приложений - предварительное конфигурирование, собственно сборку и инсталляцию. Конфигурирование ядра - это включение или выключение поддержки различных устройств (т.н. драйверов, хотя здесь это понятие существенно отличается от принятого в Windows), файловых систем, сетевых протоколов и т.д. Включение поддержки какой-либо опции подразумевает, что в ядро будет встроен код, обеспечивающий работу с неким устройством, файловой системой и проч. Кроме того, многие опции могут быть включены как модули. То есть соответствующий им код компилируется в бинарный вид, но непосредственно в ядро не встраивается, а подгружается в виде отдельной программы по мере необходимости - вручную, соответствующими командами, или автоматически. Заметим, что, когда речь заходит о драйверах устройств, распространяемых отдельно от ядра операционной системы (например, производителями оборудования - некоторые из них признали ныне факт существования операционок, отличных от Microsoft Windows), имеются ввиду именно загружаемые модули ядра. Подобно другим программам, они могут существовать в виде исходников или в бинарном виде. В первом случае их теоретически можно собрать для любой версии ядра (или, по крайней мере, для диапазона близких версий), разумеется, только данной ОС (драйверы для Linux, как можно догадаться, не будут работать во FreeBSD, и наоборот). Бинарные же, прекомпилированные, драйверы обычно жестко привязаны к версии ядра, хотя иногда могут как-то работать и при смене оной. Процесс конфигурирования является наиболее ОС-специфичным во всей процедуре сборки ядра. Само собой разумеется, что ядра разных операционок имеют разные функции и, соответственно, разные опции конфигурирования - еще бы, ведь это все-таки разные программы. Но даже чисто внешне, на пользовательском уровне, процесс конфигурирования ядра Linux и, скажем, FreeBSD (или DragonFlyBSD) существенно отличается. Во FreeBSD традиционным инструментом конфигурирования ядра выступает обычный текстовый редактор. С его помощью конфиг умолчального ядра (т.н. ядра С отключаемыми опциями все понятно - они просто изымаются из умолчального конфига (путем установки символа комментария на соответствующих строках). А вот откуда взять опции недостающие? Они отыскиваются в некоем образцово-показательном конфигурационном файле. Во FreeBSD 4-й ветки (и это унаследовано в DragonFlyBSD) такой файл носит имя Специального включения/выключения модулей в конфигурационном файле ядра FreeBSD не предусмотрено - в качестве таковых по умолчанию собираются все выключенные опции, для которых модульная поддержка в принципе возможна (а она реализована еще не для всех опций). Правда, это положение можно изменить правкой соответствующих конфигурационных файлов. В первозданном (или каноническом - том, что скачивается с http://www.kernel.org) ядре Linux никакого умолчального конфигурационного файла не предусмотрено: он генерируется при первом же запуске штатного инструмента для ядерной настройки, базируемого все на той же утилите Точнее, не одну - для изначального конфигурирования ядра предусмотрено ажно четыре цели: Команда make config представляется не очень удобным - в случае малейшей ошибки имеется только одна возможность для ее исправления - оборвать программу (например, через Control+C) и начать все заново. И вообще, этот метод конфигурирования считается устаревшим, и в ядрах ветки 2.6.X цель make config штатно не документирована. Хотя и может использоваться при желании - ее преимущество (по моему, несколько сомнительное) в том, что, в отличие от прочих средств конфигурирования, она не требует прав суперпользователя.
Команда Использование Команды Есть в Linux и еще несколько целей, обеспечивающих конфигурирование ядра. Так, командой При смене версии ядра можно прибегнуть к цели И, наконец, (почти) полный список доступных для конфигурирования ядра и его сборки целей можно получить посредством $ make help Собственно сборка ядра и в Linux, и в BSD-системах осуществляется командой make modules тогда как во FreeBSD это произойдет по умолчанию одновременно с компиляцией ядра. В результате по завершении сборки у нас в подкаталогах дерева исходников ядра образуется собственно образ ядра - (почти) обычный исполняемый файл, - и набор скомпилированных модулей. Это такие же объектные файлы, которые возникают на промежуточной стадии компиляции других программ. Так как к самостоятельному использованию они не предназначены, то и в линковке они не нуждаются. Файл образа ядра во FreeBSD всегда носит имя Как и компоненты любого прикладного пакета, образ ядра и сопровождающие его модули должны быть инсталлированы в должное место - в те подкаталоги, где система ожидает обнаружить их при загрузке. В Linux таких мест традиционно два - каталог Так вот, процесс инсталляции и сводится к тому, что файл образа ядра вместе с объектными модулями (а это ныне файлы вида В разделе о системных загрузчиках мы увидим, что в принципе ядро может быть размещено чуть ли не в любой части файловой системы. Нужно только позаботиться о том, чтобы программа, выступающая в данной ОС в качестве загрузчика, знала о его расположении. Однако нестандартное размещение ядра может создать некоторые проблемы и в любом случае потребует лишних телодвижений, так что лучше придерживаться традиционной схемы. Вопросы оптимизацииСказавши "а", логично было бы добавить "б". А именно, после пересборки ядра - выполнить оптимизацию приложений под имеющееся "железо", по крайней мере - критически важных для производительности системы в данных условиях. Тем более что современные версии компилятора Выбор версии компилятора Положение изменилось с выходом Оптимизация приложений достигается заданием соответствующих флагов - параметров компилятора Выбор уровня оптимизации - не столь однозначный вопрос, как может показаться. И максимально высокий уровень Воообще говоря, мои (и не только мои) наблюдения показывают, что самый большой скачек в быстродействии происходит при переходе от уровня Следующие флаги, весьма влияющие на производительность, задают конкретный процессор для целевой машины: Допустимые значения флагов Особенно богатые возможности для оптимизации под современные процессоры предсотавляет Следующие процессорно-специфичные флаги оптимизации позволяют задействовать специальные наборы инструкций современных "камней" - от MMX до 3DNow и SSE всех номеров. Для этого сначала определяем, куда в этих случаях должен обращаться процессор - к стандартному сопроцессору или соответствующим блокам специализированных инструкций. Делается это с помощью флага Насколько я понимаю, при компиляции под Athlon'ы более оправданным будет флаг Впрочем, флаги для задйствования специальных наборов инструкций имеют смысл только для программ, которые в принципе способны их использовать. По имеющимся у меня сведениям, они дают хорошие результаты для некоторых видеоприложений и вообще всякого рода multimedia. Ожидать же от них выигрыша в быстродействии того же компилятора вряд ли оправданно. Процессорно-привязанными флагами возможности оптимизации не исчерпываются. Есть еще флаги, специфическим образом обрабатывающие код для достижения максимальной производительности вне зависимости от типа процессора (вплоть до нарушений стандартов POSIX/ANSI, типа Как уже говорилось, при использовании флагов оптимизации, особенно достаточно жестких (типа Я достаточно много экспериментировал с различными флагами оптимизации, в том числе и весьма жесткими, при разных версиях компилятора, и на разных системах (Gentoo, CRUX, Archlinux, FreeBSD 5-й ветви, DragonFlyBSD). И в конце концов пришел к весьма умеренному решению: CFLAGS="-O2 -march=pentium4" CXXFLAGS="$CFLAGS" Вероятно, рекордных результатов при нем не добиться, но оно дает гарантированно стабильный результат. По крайней мере, и ядро, и "мир" DragonFlyBSD (при gcc-3.4.X) собираются без проблем, как и большинство используемых мной портов. Тем не менее, любители экстремальной сборки могут попробовать нечто вроде этого:CFLAGS="-O3 -march=pentium4 \ -fomit-frame-pointer \ -funroll-loops -pipe \ -mfpmath=sse -mmmx -msse2" CXXFLAGS="$CFLAGS" А при желании еще и подобрать специальные флаги оптимизации для программ Си++, чем я никогда не озадачивался вообще. К слову сказать, флаги оптимизации задаются различными способами:
CFLAGS="значения" ./configure
export CFLAGS="значения"
в sh-совместимы оболочках и
setenv CFLAGS "значения"
в csh и tcsh;/etc/profile или /etc/cshrc);Осталось определить, что дает столь жесткая оптимизация? Компилятор Средства управления пакетамиВопросу ручной сборки программ выше было уделено чень много места. Однако на самом деле пользователю не так уж и часто приходится заниматься этим делом. Потому что любая уважающая себя система, как было сказано в главе 2, обладает тем или иным способом автоматизации установки и удаления программ, избавляющей не только от отслеживания зависимостей, но обычно даже и от компиляции, то есть системой управления пакетами. Средства управления пакетами чрезвычайно разнообразны. В первом приближении их можно разделить на два класса - системы портов и собственно системы пакетного менеджмента. Системы портов пришли из мира BSD, где впервые появились во FreeBSD. Собственно, порты - это родовое имя системы пакетного менеджмента этой операционки. Все остальные портообразные системы возникли и развивались под сильным ее влиянием. Наибольшее распространение и известность получили Первая система возникла первоначально в NetBSD, постепенно превратившись в кроссс-платформенное средство управления пакетами: существуют ее версии для всех BSD-систем (включая DragonFly), ряда дистрибутивов Linux и даже для таких проприетарных Unix'ов, как Solaris и IRIX. Система портежей была разработана для дистрибутива Gentoo Linux. Однако и она быстро обрела черты кросс-платформенности: более или менее активно разрабатываются ее варианты для FreeBSD и NetBSD, поговаривают о переносе ее на Hurd и другие дистрибутивы Linux. Тем не менее, практически каждый дистрибутив Linux, относимый к классу Source Based, обладает собственной, более или менее оригинальной, портообразной системой (почему я и полагаю, что их следует называть скорее портируемыми дистрибутивами). Тут следует упомянуть и ABS - Archlinux Building System из одноименного дистрибутива, и порты CRUX, и Sorcery из Sorcerer Linux и его потомков (Source Mage и Linar). И примеры портообразных систем множатся с каждым днем: только за истекший год появились дистрибутивы Rubyx и MyGeOS - каждый со своей портообразной системой. Не смотря на такое разнообразие конкретных реализаций, общие черты любой портообразной системы вычленить очень легко: это набор правил для отыскания в Сети исходников устанавливаемых программ, их получения на локальную машину, развертывания тарбаллов, конфигурирования, сборки (компиляции и линковки), инсталляции в файловую систему и, возможно, постинсталляционного конфигурирования. То есть по этим правилам выполняется весь тот цикл действий, которые мы проделываем при ручной сборке любой программы (вспомним три волшебных заклинания Собственно системы пакетного менеджмента - это инструменты для установки и удаления ранее скопмилированных бинарных пакетов. Что такое бинарный пакет - легко представить себе, если вернуться к процессу сборки программ, описанному выше. Как мы помним, собственно стадия сборки (исполнение процедуры По умолчанию каталог для промежуточных продуктов сборки - то же самый, что и для исходных текстов. Однако, если на стадии конфигурирования задать какой-либо иной адрес сборки, например $ ./configure --prefix=/path_to/pkgname то бинарные компоненты пакета будут собраны в отдельном каталоге с подкаталогами По формату пакеты в первом приближении можно разделить на две группы - те, что содержат внутри себя метаинформацию (в частности, информацию о зависимостях пакетов), и таковой лишенные. К первой группе относятся широко распространенные форматы пакетов Пакеты без метаданных - это обычные тарбаллы, то есть компрессированные tar-архивы типа Еще более существенно то, что отсутствие в составе пакета информации о его зависимостях отнюдь не препятствует контролю над ними: он может осуществляться за счет внешних баз данных репозиториев пакетов и локальных баз данных пакетов установленных. А функции удовлетворения зависимостей в этом случае целиком ложатся на программы, осуществляющие пакетный менеджмент. И надо отметить, что управление "чистыми" тарбаллами подчас оказывается более гибким, чем пакетами с информацией об их заивисимостях. Средства пакетного менеджмента жестко привязаны к формату пакетов - для установки rpm-пакетов служит одноименная утилита (rpm), пакеты deb устанавливаются посредством утилиты dpkg, для пакетных тарбаллов предусмотрены собственные средства, обычно - дистрибутив-специфичные, не смотря на похожие, и даже подчас одинаковые, имена. Конечно, существуют средства взаимной трансформации пакетов разных форматов (типа rpm2cpio, rpm2tgz или почти универсальной утилиты alien), однако возможности их применения ограничены - очевидно, что из rpm-пакета (и тем более "чистого" трабалла) получить полноценный deb-пакет невозможно.
Однако существуют еще и средства пакетного мета-менеджмента, если так можно выразиться (собственно, только они-то и заслуживают названия систем управления пакетами). Наиболее известное и распространенное из них - Под явным влиянием Следует помнить, что резкую границу между системами портов и средствами пакетного менеджмента провести нелегко. Все портообразные системы позволяют выполнить сборку автономных бинарных пакетов и, соответственно, включают средства их установки, регистрации и удаления. А в системах пакетного менеджмента можно обнаружить инструментарий для автоматической компиляции программ из исходников, хотя они и играют сугубо вспомогательную роль. |
|
CITForum © 1997–2025