Суть шестой из "вечных истин" - в извечном противопоставлении и неразрывном единстве дедукции и индукции, анализа и синтеза. И здесь отчетливо проступает академическое происхождение POSIX-совместимых систем: если пользователь Windows в своей повседневной деятельности руководствуется набором готовых рецептов, более или менее обширным, то эффективное использование Linux или FreeBSD начинается с постижения некоторых общих принципов. Подобно тому, как любая наука начинается, вопреки утверждениям марксистской философии, не с анализа фактов, а с некоторого первичного их обобщения.
В последнее время общим местом в околокомпьютерной прессе стало сопоставление компьютеров с бытовой техникой по простоте использования. Мне такое сопоставление всегда казалось некорректным, и ниже я постараюсь обосновать свое мнение.
Действительно, что такое видеомагнитофон (магнитофон просто, телевизор, радиоприемник - нужное подчеркнуть)? Это - исключительно инструмент для потребления. Потребления продукции, созданной кем-то другим (случай продукции собственной рассмотрим чуть ниже). Причем продукции, в отличие от продуктов питания, не жизненно важной. То есть потребляемой исключительно с целью развлечения (случай профессиональных теле- или радио-озирателей здесь не рассматривается).
А потому развлекаемый, если так можно выразиться, пользователь-потребитель вправе требовать от такого инструмента минимума сложностей в использовании. Не желает он понимать принципы работы привода видака или передачи сигнала на телевизор. Потому как иначе это превратится в работу, а его задача - как раз отдохнуть от своих (в том числе и производственных) проблем за любимой "Великолепной семеркой" или "Тремя мушкетерами" (нужное вписать).
Более того, пользователь-потребитель видеомагнитофона имеет полную возможность обойтись без всяких знаний о его устройстве. Потому как его потребность - не забивать голову техническими подробностями, - сполна удовлетворяется производителями такого рода техники. Иначе ее просто не стали бы массово покупать - вспомним тех же радиолюбителей с паяльниками, много ли их было в процентом отношении? Чай, не кусок хлеба, обходились Маяком на фабричной Спидоле (за исключением убежденных диссидентов, конечно, у которых "был обычай на Руси - ночью слушать BBC").
Так что развлекаемый пользователь вполне может обойтись минимумом самых простых рецептов, как то: вставить кассету, нажать кнопку "Вперед", после просмотра нажать кнопку Eject. Хотя и тут требуется некий минимум подготовки - например, какой стороной кассету засовывать. Иначе возникнет нештатная ситуация, требующая уже дополнительных рецептов (типа - использовать деревянную линейку) и дополнительных знаний (на что этой линейкой давить).
Однако все это - лишь до тех пор, пока происходит пассивное потребление кем-то созданной продукции. Если же пользователь видика/телевизора, насмотревшись передач типа "Сам себе режиссер" или там крутой порнографии, решит создать собственный шедевр в том же духе, ситуация тут же меняется.
Во-первых, ему потребуется инструмент для созидания, а не потребления. Сиречь - кинокамера. Во-вторых - умение ею пользоваться. Под которым нужно понимать не только владение ее интерфейсом (грубо говоря, знания тех же кнопок запуска-останова), но и умение снимать. То есть - понимание перспективы, освещенности, навыки создания какого-никакого сюжета. Думаю, все согласятся со мной, что мало что может быть страшнее видеоролика, снятого по методу "что увидел, то и снимаю". Помнится, меня всегда доставал просмотр экспедиционных слайдов - обычно именно таким образом он и осуществлялся.
А уж если такой пользователь в итоге изберет видеосъемку своей профессией - тут ему потребуется и многое другое. В том числе не лишними окажутся знания о физических принципах фото- и видеосъемки. Не случайно лучший из лично известных мне фотографов по образованию - физик-оптик с неслабым опытом инженерной работы в очень нестандартных условиях (см. www.rwpbb.ru).
Да и на видеомагнитофон такой пользователь (а он ведь по прежнему - пользователь видеомагнитофона по определению, не так ли? - ибо производством оных не занимается) будет смотреть совершенно иначе. Для него это будет уже не инструмент потребления, а аппарат, способный подчеркнуть или затушевать собственное мастерство (или - отсутствие такового). То есть превратится в деталь производственного цикла, в орудие производства.
И тут требования к удобству интерфейса даже видеомагнитофона отступают на второй план. Мало горя будет в том, что на конкретной модели кнопка запуска расположена не совсем там, где хотелось бы, если это - единственный (или единственный доступный финансово) инструмент, способный обеспечить требуемую функциональность.
Вернемся, однако, к рецептам и принципам. Пока пользователь видеотехники остается чистым потребителем, он вполне может обходиться минимумом рецептов. Однако первые же попытки креативного характера приводят к резкому возрастанию потребности в HOW TO: куда встать, под каким углом держать, откуда направить свет, и так далее. И здесь перед ним два пути: экстенсивный или интенсивный. Первый - копить все те же практические рецепты, наработанные эмпирически. Однако скоро а) их становится очень много и б) все рецепты по определению охватывают только стандартные ситуации, ничего нетленно-непреходящего с их помощью не создашь. И приходится нашему пользователю волей-неволей обращаться к истокам - то есть базовым принципам. Бия себя по голове за то, что в школе не читал внимательно "Физику" Перышкина...
Теперь обратимся к компьютерам. В отличие от видеомагнитофонов, они изначально создавались не для потребления, а для креатива (чего бы то ни было). И многими, как ни странно, и по сей день используются главным образом для работы:-). В том числе, а то и в первую очередь - для работы дома. Помнится, для меня первая "персональная персоналка" обеспечила именно возможностью не ходить на службу.
Конечно, компьютер имеет и потребительски-развлекательную функцию. Каждый пользователь, профессионально с компьютером связанный (не в смысле - профессиональный компьютерщик, а - выполняющий свою профессиональную работу главным образом на компьютере), отнюдь не прочь послушать музыку или посмотреть киношку без отрыва "от станка". Однако функция эта не просто вторична по времени, она не критична и по значению. Думаю, если просмотр видеоролика будет мешать работе профессионально критичной программы, любой профессионал предпочтет смотреть его по видику (тому самому, развлекательно-потребительскому).
Очевидно, что для профессиональной работы важнее функциональность, а не удобство использования (мне безразлично, насколько удобно я не могу выполнить свою задачу). А развлекательная составляющая - вроде бесплатного приложения.
Говорят, что есть и чисто развлекаемые пользователи-потребители компьютеров, покупающие навороченные P-4 вместо музыкальных центров или домашних видеотеатров. Хотя мне таковых видеть и не доводилось. Однако рискну предположить, что это в основном - именно люди, профессионально с компьютерами связанные (или энтузиасты цифрового контента), иначе не вижу тут ни практической, ни финансовой целесообразности. Может, я и отстал от жизни, но мне кажется, что нормального качества телевизор стоит дешевле, чем высококлассный монитор (а только на таком просмотр фильма и доставит удовольствие истинному ценителю).
Это я все к тому, что даже и в развлекательном аспекте компьютер остается где-то инструментом креативным, со всеми вытекающими последствиями. Кроме того, он при этом практически не теряет своего универсализма. Если на видеомагнитофоне слушать Баха, скажем так, несколько затруднительно, а на CD-плейере, напротив, фильмы Бессона обычно не смотрят, то тот же мультимедиа-компьютер призван выступать в обоих качествах (а зачастую, повторюсь, на нем даже еще и работают). И потому ожидать, что он будет так же прост в обращении, как монофункциональный развлекатель - по меньшей мере излишне оптимистично.
Конечно, чисто развлекательную функцию компьютера тоже можно упростить до состояния видеомагнитофона. Замечательным примером чему служит Linux-дистрибутив под названием MoviX. Это - один из т.н. LiveCD, то есть система на компакте, способная с оного не только запускаться, но и полноценно функционировать. Функции MoviX'а, правда, весьма ограничены. А именно, он умеет только крутить мультимедийные файлы (видео и аудио). Но зато умеет это - очень хорошо. И, главное, ничуть не сложнее, чем бытовой агрегат соответствующего назначения.
Так что достаточно легкого движения рук - вставки диска MoviX в привод и комбинации из трех пальцев, - чтобы волшебным образом превратить тысячебаксовый компьютер в элегантный видеомагнитофон или CD-плейер красной ценой в пару сотен. Благо, и обратное превращение ничуть не сложнее...
Но упростить производственную-то функцию компьютера - все равно не удастся (работать вообще довольно трудно, как говорил, если не ошибаюсь, Антон Палыч Чехов). И потому лозунги типа "С выходом Windows 3.0 (3.1, 95, 98, ME, XP etc.), обращаться с компьютером наконец-то (выделение мое - А.Ф.) стало также просто, как с бытовой техникой", которые я лично слышу уже более 10 лет, - лукавы, как минимум, вдвойне. Во-первых, это самая обычная подмена понятий - если под обращением понимать не только развлекательную сторону, но и производственную (см. вышеуказанный тезис А.П. - создавать видеофильмы никогда не будет также просто, как их просматривать). А во-вторых, сама регулярность появления таких лозунгов (я не случайно выделил слова наконец-то) вызывает подозрения, что и с развлекательной строной компьютеров все еще сохраняется некоторая напряженка. В частности, за что я не люблю Windows, - за то, что она, обещая избавление от всех и всяческих проблем хотя бы в потребительском аспекте, своих обещаний не выполняет.
Однако опять вернемся к рецептам и принципам. Худо-бедно, но с развлечениями на компьютерах можно справиться посредством первых. А вот с производством любого рода? Рассмотрим это на примере более-менее близкой мне области - работы с текстами, претендующими на оригинальность (то есть выдумываемыми из головы).
Подобно нашему видеолюбителю, профессиональный текстовик, пересев с пишущей машинки (или с письменного стола со стопой бумаги и паркеровской ручкой) за компьютер, быстро узнает множество простых рецептов, как то: нажав клавишу Insert, он может забить неправильно введенный текст (как забивочные листки на машинке, только проще), клавишами Delete или Backspace можно уничтожить лишнюю букву (в отличие от замазки, без следов), и так далее.
Жить нашему текстовику становится лучше, становится веселей. Но еще не до конца. Потому как он узнает об управляющих последовательностях, с помощью которых может мгновенно переместиться в требуемое место текста и продолжить набор-редактирование, о глобальном поиске и замене, об автоматической проверке правильнописания, и о многом, многом другом.
Однако суть работы текстовика при этом не меняется, как не меняется и стиль мышления. И то, и другое по прежнему линейно, оригинальный текст создается от начала и до конца, как и за пишущей машинкой, расширяются только возможности возврата к написанному и внесения в него корректив. И потому следующая мысль - а не структурировать ли текст изначально, внеся соответствующую разметку рубрик, подрубрик, параграфов? И вот это - уже скачок качественный, ведь для структуризации (ненаписанного еще) текста последний весь, целиком, уже должен быть вот здесь, в ... (ну, сами знаете где).
К слову сказать, современные ворд-процессоры WYSIWYG-типа пользователя к такой структуризации отнюдь не стимулируют. За ненужностью народу, вероятно. В одной книжке про Word мне как-то встретилась фраза, что стилевая разметка - это штука очень сложная, которая по силам только высоким профессионалам. Простым людям, видимо, проще вручную придавать заголовку каждой главы кегль и начертание (и при этом помнить, какое оформление было придано Главе 1, какое - Главе 2, и так далее).
Впрочем, я опять отвлекся, перестройка мышления текстовика - тема совершенно отдельная. Вернемся к принципам. С созданными и отредактированными текстами подчас приходится продолжать работать - делить на фрагменты, соединять, извлекать части одного документа и вставлять в другой. И вот тут-то и обнаруживается неэффективность рецептов.
Возьмем простую задачу - создание единого документа из нескольких существующих, причем - включенных в определенной последовательности. Мне этот пример кажется очень показательной, и я не устаю его повторять. Можно: открыть документ 1, перейти в его конец, щелкая мышью по контекстному меню, (или даже воспользовавшись существующим рецептом - макрокомандами с привязанными к ним горячими клавишами) вставить туда документ 2, и так далее. Быстрее, конечно, чем подклеивать бумажные листы силикатным клеем, но все ли это, что может компьютер?
Нет, если узнать (или вспомнить) несколько принципов более общего порядка. Любой документ - есть файл. Любой файл может быть выведен на устройство вывода (экран или, скажем, принтер). А любой вывод может быть перенаправлен с одного устройства вывода на другое. Но ведь любое устройство вывода - тоже файл. Значит, вывод любого файла может быть перенаправлен не в файл устройства, а в другой, например, текстовый, файл. Остается только отыскать команду, которая это сделает. И такая команда легко находится - это cat
. В результате конструкция
$ cat file1 file2 file3 > file-all
создаст нам результирующий документ в один присест. Причем составные части его расположатся в той последовательности, в какой нам нужно - в соответствие с порядком аргументов команды cat
. И, если мы заранее озаботились тем, чтобы структура наших рабочих файлов хоть как-то коррелировала (не обязательно плоско-линейно, но по какому-либо принципу) со структурой итоговой работы (а это та самая структуризация мозгов, о которых я говорил чуть раньше) - результирующий документ будет структурирован должным образом, причем без всяких дополнительных усилий.
Задача обратная - поделить наш правильно (!) структурированный документ на отдельные части в соответствие с его внутренней структурой (например, разбить книгу на главы, как это обычно требуется для представления в издательство). Для автоматизации процесса нам достаточно знать о другом относительно общем понятии - регулярных выражений, причем лишь в той его части, которая описывается термином шаблон (pattern). И задача сводится к тому, чтобы отыскать в нашем файле строки, начинающиеся последовательностью символов
Глава
и каждую последовательность символов в промежутке записать (то есть вывести) в самостоятельный файл. Что можно сделать многими способами, но один из них - штатен и элементарен, это команда split
(во FreeBSD) или csplit
(в Linux).
Последний пример показывает, что, хотя понимание принципов и не избавляет уж совсем от обращения к рецептам, но зато позволяет вычислить последние при их незнании. Дело в том, что во FreeBSD команда split
универсальна, и служит для разделения файла по любому параметру - размеру, номеру линии или шаблону. Одноименная же команда в Linux выполняет только первые две функции, опции -p
(--pattern
) в ней не предусмотрено. Что поначалу может расстроить. Однако если понимать, что в принципе разделение файлов по шаблону почти ничем не отличается от такового по номеру линии или размеру (и то, и другое суть действия, основанные на анализе последовательности символов), остается только изыскать соответствующий рецепт. Что можно сделать просто в лоб - поискать слово split
в каталоге с man
-страницами командой grep
(строго говоря, не слово, а последовательность символов, и командой zgrep
, так как страницы эти обычно в gzip
-сжатом виде). Чем и обнаруживается man-страница с описанием команды csplit
, прочитать которую - уже вопрос элементарной грамотности.
Следующий пример соотношения рецептов и принципов касается установки пакетов. Пользователь любого прекомпилированного дистрибутива быстро усваивает простые рецепты управления оными. Таким рецептом в rpm-based дистрибутивах Linux является команда вида
$ rpm -ihv package_name.rpm
прекрасно работающая в штатных ситуациях. Однако что делать, если пакет таким образом не устанавливается (например, вследствие нарушения зависимостей), работает не так, как ожидалось, или просто отсутствует? На помощь приходит понимание принципов распространения свободных программ - в исходных текстах, - и принципов их сборки, универсальных и не зависящих ни от дистрибутива, ни даже от операционной системы. А понимание принципов сборки, в свою очередь, способствует проникновению в суть дистрибутив-специфичного пакетного менеджмента...
Но особенно явно превосходство принципов над рецептами выступает при конфигурировании всего и вся - от общесистемных опций до шрифта меню конкретного приложения. Рецептурный подход - использование специализированных средств настройки, оформленных в виде самостоятельных утилит или встроенных в прикладные программы. Самостоятельные утилиты такие многочисленны и разнообразны, не зря же Владимир Попов как-то заметил, что число утилит конфигурирования давно превзошло количество конфигурируемых параметров. Интерфейс у них разный в разных дистрибутивах, да к тому же еще и может меняться от версии к версии. Так что доскональное знание какого-либо DrakX из Mandrake ничем не поможет при работе с sysinstall
из FreeBSD, и наоборот.
Если же не "поступаться принципами" - достаточно раз и навсегда понять, что все параметры настройки системы описываются в соответствующих конфигурационных файлах, которые суть обычные тексты, могущие быть открытыми в любом текстовом редакторе и там модифицированными надлежащим образом. Что и проделывают, только в завуалированном виде, все настроечные утилиты.
Что касается объектов конфигурирования, то есть соответствующих файлов, и субъектов оного - параметров настройки, - то они определяются стандартными утилитами работы с текстами, например, командой find
- для поиска файлов по маске, и командой grep
- для изыскания в них подходящих по смыслу фрагментов. Ну и, разумеется, осмысленного анализа результатов того и другого...
Вопреки сложившемуся убеждению, для этого не обязательно быть Unix-гуру или к таковому обращаться. Во-первых, тот же гуру даст, скорее всего, именно конкретный рецепт на злобу дня. Во-вторых, многие вещи в системе настраиваются один раз в жизни, и вполне возможно, что наш гуру благополучно забыл о том, как именно он это делал. Так что своей просьбой вы просто вынуждаете его вторично проделать ту самую цепочку логических рассуждений, отталкивающихся от общих принципов, которую вы легко (и с пользой для духовного самосовершенствования) могли бы проделать сами.
И еще к слову - с успехом (надеюсь) применяя принципиальный подход к жизненно важным для работы настройкам, можно не поступаться принципами и при настройке вещей развлекательного свойства. Например: звуковая карта определена и в ядре настроена правильно, соответствующий софт установлен и работает, но mpeg-файлы, скажем, воспроизводиться не желают.
Вероятно, в user-ориентированных дистрибутивах существуют какие-нибудь специальные утилиты для настройки этого хозяйства, и можно обратиться к ним. А можно просто вспомнить, что звук воспроизводится устройством, устройство есть файл, а файл имеет определенные атрибуты принадлежности (хозяину, то есть пользователю имя_рек, группе и всем прочим) и атрибуты доступа (в просторечии именуемые правом чтения, исполнения и изменения). И остается только проверить, а имеет ли данный пользователь должные права доступа к этому файлу? И если выясняется, что файл /dev/audio
открыт для всеобщего использования во всех отношениях - посмотреть, а не есть ли его имя лишь символическая ссылка на файл реального устройства, отвечающего за воспроизведение звука, и проверить права доступа к нему.
И опять к слову: понятие атрибутов принадлежности и доступа, одно из краеугольных в Unix-системах, существует и в тех Windows, которые можно назвать всамделишними (то есть NT/2000/XP, даже в ME вроде бы есть зачатки - семейный доступ в систему и прочее). Да вот только пользователи их об этом часто не подозревают. Не потому, что чайники, а потому, что их от этого знания тщательно оберегают.
В результате к нештатным ситуациям (например, потере пароля - кто от этого застрахован, все мы люди, все мы человеки) пользователи Windows оказываются просто морально не готовы: нужно дергаться, звать админа, даже (страшно подумать) лезть в книги (не для того ли мы отказывались от man
- и info
-страниц?) для поиска рецептов, соответствующих ситуации.
А в Unix (вернее, Linux/*BSD, за прочие не скажу по незнанию) - все просто, если помнить о файле (файлах) паролей, однопользовательском режиме (или возможности загрузки с внешнего носителя) и о том, что дисковые устройства нужно монтировать (насколько я знаю, в NT сотоварищи диски тоже как бы монтируются, только "дружелюбно" и "прозрачно" для пользователя; в итоге ему остается только удивляться сообщениям об ошибках, выдаваемых при неправильном извлечении USB-драйва).
В общем, подведу итог. Рецептурный подход вполне приемлем при потребительско-развлекательных задачах. И оказывается, мягко говоря, не самым эффективным при задачах производственно-креативных. А отработав принципиальный подход на них, становится уже в лом искать, какая кнопочка отвечает за масштабирование окна данной программы воспроизводства видео... Проще задать сиюминутную геометрию в командной строке или (раз навсегда) в соответствующем файле ресурсов.
Далее в этой главе я продемонстрирую соотношения рецептов и принципов на трех примерах: установке программ, управлении пакетами и конфигурировании системы и пользовательских приложений.
Как уже говорилось ранее, дистрибутивы Linux организованы по пакетному принципу. Точно также, в виде пакетов, распространяются и любые программы, создаваемые независимыми разработчиками (из которых в основном и собираются дистрибутивы Linux). А в BSD-системы попакетно включаются все приложения, не входящие в состав базового комплекта. И потому одна из важных задач пользователя - это интеграция пакетов в свою систему.
В большинстве случаев эта задача решается за пользователя разработчиками его операционки: системы управления пакетами (менеджеры пакетов) составляют неотъемлемую часть любого дистрибутива Linux, Free- и прочих BSD. Однако в ряде случаев пользователю приходится устанавливать пакеты и самостоятельно. К тому же понимание сути этого процесса зело способствует уяснению того, что же делают пакетные менеджеры, и помогает принять правильное решение в нештатных ситуациях. Так что вопрос этот заслуживает подробного рассмотрения.
Само по себе выражение "сборка программы" часто повергает начинающего пользователя в некий священный трепет - по себе помню. Однако в этой главе я постараюсь показать, что ничего сверхъестественного в этом процессе нет, и выполнение его по силам любому пользователю, вне зависимости от чисто программистской квалификации - собственно говоря, никаких навыков программирования он не требует, а только - некоторых предварительных знаний.
Однако сначала - маленькое введение для тех, что компьютерное образование начиналось не с книжек Брябрина и Фигурнова, а с руководств типа "Word за 5 минут" или "Quark Press для полных идиотов" (это - не в обиду читателям, но в упрек писателям). Все прочие могут смело пропустить нижеследующие элементарные рассуждения.
Так вот, программы, то есть наборы инструкций, предписывающие машине выполнить то или иное действие (от самых элементарных, типа - скопировать файл, до предельно сложных), сочиняются программистами на языках программирования:-). И представляют они собой самые обычные тексты (называемые исходными текстами, исходниками или, уж совсем жаргонно, сырцами - sources), в которых необходимые инструкции описываются в соответствие с принятыми в данном языке правилами (синтаксисом языка). Собственно говоря, именно это мы и проделывали в разделе о сценариях из предыдущей главы.
Однако не следует через чур уж очеловечивать компьютеры (говорят, что они этого не любят:-)), и ожидать от них способности понимать какой-либо "человеческий" язык, даже такой формализованный, как язык программирования. Нет, они способны воспринять только собственные инструкции (зависящие от центрального процессора), представляемые в бинарном виде - то есть последовательностей нулей и единиц. Собственно, и сами эти цифры - выше их понимания, каковое не выходит за пределы наличия/отсутствия электрического сигнала, но в такие глубины мы лезть уже не будем.
Так что для выполнения программы она в конечном счете должны быть транслирована в соответствующие, зависящие от архитектуры процессора, машинные инструкции. И выполняется этот процесс двояко - посредством интерпретации или компиляции.
Интерпретация - это последовательный перевод языковых конструкций из символов латинского алфавита (и прочих, специальных) в машинные инструкции по мере их ввода. Простейший процесс интерпретации - это ввод директив в командной оболочке (почему она часто называется также командным интерпретатором), а также обработка ее сценариев - наборов элементарных команд. Сколь бы ни был сложен такой сценарий, выполняется он последовательно: сначала интерпретируется команда 1, потом - команда 2, и так далее.
При интерпретации никакого изменения исходного текстового файла не происходит. А сам он отличается от простого списка команд только тем, что имеет бит исполнения. Однако интерпретируемая программа не может исполняться сама по себе, для ее запуска требуется соответствующая среда - программа-интерпретатор (например, та же командная оболочка).
Процесс интерпретации выполняется каждый раз при исполнении программы. И, соответственно, каждый раз время затрачивается на выполнение одних и тех же процедур. Так что возникает резонный вопрос - а нельзя ли оттранслировать исходник программы в машинные инструкции раз и навсегда, и в дальнейшем запускать на исполнение уже набор таких инструкций, не затрачивая время на их преобразование?
Ответ - столь же резонен: ну конечно же, можно. И процедура эта называется компиляцией, а выполняющие ее программы - компиляторами. В ходе этой процедуры из исходного текстового файла по определенным правилам образуется файл бинарный, образованный, если просмотреть его в текстовом редакторе, последовательностью неудобопонятных символов. И - пригодный для автономного исполнения, для чего необходимости в породившем его компиляторе уже нет.
Предварительно оттранслированные (прекомпилированные) программы, по вполне очевидным причинам, выполняются много быстрее, чем программы интерпретированные, причем разница в скорости нарастает с объемом. И потому все масштабные программы, как правило, пишутся в рассчете на использование в откомпилированном виде. Хотя и роль интерпретируемых программ - сценариев разного рода - в POSIX-системах не стоит недооценивать.
В соответствие с дальнейшим предназначением программ при их написании выбираются инструментальные средства для этого, то есть в первую очередь языки программирования. Которые, таким образом, подразделяются на интерпретируемые и компилируемые.
К первым принадлежат постоянно упоминавшиеся ранее языки командных оболочек. Однако ими список интерпретируемых языков не исчерпывается. В POSIX-системах широко используются такие мощные средства, как Perl, Python, Ruby, Tcl/Tk. Они предоставляют большие возможности, вплоть до создания графических пользовательских инструментов. Однако принципиально написанные на них программы ничем не отличаются от сценариев командной оболочки.
Компилируемых языков - также великое множество, начиная с пресловутого Basic'а и заканчивая специализированными средствами типа Fortran. Однако в POSIX-системах наибольшее значение имеют программы на языке C. Который, собственно, и создавался для разработки первозданного Unix. И на котором написана большая часть ядра всех POSIX-совместимых систем (а ядро ОС - это почти такая же компилируемая программа, как и любая другая), а также большая часть их приложений.
Так что далее речь пойдет о сборке преимущественно C-программ. Однако для нас это существенного значения не имеет - ведь собственно программированием заниматься мы не будем, а принципы сборки практически не зависят от используемого языка. В частности, точно по той же схеме собираются и графические приложения, в которых широко используется язык C++.
Кроме собственно программ, предназначенных для непосредственного исполнения, существуют еще так называемые разделяемые библиотеки, или библиотеки функций (соответствующего языка программирования). Что это такое - проще пояснить на примере.
Все программы, вне зависимости от их назначения, неизбежно должны выполнять некоторые однотипные действия, как то: открыть файл, закрыть его, вывести на экран и так далее. Сущность их не меняется, что бы программа не делала. И потому нет никакого смысла программировать такие манипуляции каждый раз заново.
Вот их, как правило, и не программируют. А объединяют соответствующие директивы в отдельные программные комплексы, именуемые библиотеками. Сами по себе они к автономному исполнению не пригодны. Однако любая программа, при необходимости совершить одно из типовых действий, вызывает из такой библиотеки некий фрагмент кода, содержащий требуемую последовательность директив.
Библиотеки обычно привязаны к определенным языкам программирования, синтаксису которого подчиняются описания директив (т.н. функции - о них вкратце говорилось в заключении главы о командных оболочках). Поскольку наиболее употребимым в POSIX-системах и их приложениях является язык C, то его функции и требуются чаще всего. Они собираются в главную системную библиотеку, которая именуется обычно libc (Library C), хотя реально это разные комплексы, отличающиеся полнотой функций и их описанием и зависящие от конкретной операционной системы.
В подавляющем большинстве дистрибутивов Linux используется реализация главной системной библиотеки, именуемая glibc
(GNU Library C); специализированные дистрибутивы могут использовать и другие библиотеки, например, uclibc
, менее функциональную, но более компактную. Главная системная библиотека FreeBSD называется просто - libc
, и функционально близка к glibc
, хотя и не идентична ей.
Однако libc
(glibc
) список библиотек не исчерпывается. В POSIX-системах используются библиотеки свойств терминала (например, ncurces
) для консольных программ и библиотеки, описывающие процедуры управления окнами - для графических программ системы X (xlib), библиотеки интерфейсных элементов и графических примитивов (Motif, Qt, Gtk), библиотеки описания графических и мультимедийных форматов. Короче говоря, существует тенденция к вынесению в разделяемые библиотеки всех повторяющихся действий и элементов. И в этом - одна из причин компактности большинства классических Unix-программ, в том числе и предназначенных для работы в графическом режиме.
Ну все, с элементарным введением покончено. Переходим собственно к пакетам и их сборке.
Как явствует из названия (и из вводной главы), все открытые и свободные программы и разделяемые библиотеки распространяются их разработчиками в исходных текстах. Конечно, никто не запрещает им создавать и прекомпилированные версии своих творений, и многие создатели программ так и поступают, предлагая один или несколько вариантов таковых, рассчитанные обычно на наиболее распространенные дистрибутивы Linux, иногда - FreeBSD, реже - другие BSD-системы (по причинам, которые станут ясными в последующем, прекомпилированные версии зависят от множества факторов, из которых целевая ОС - не последний). Однако это - скорее исключение чем правило.
Наборы исходников объединяются разработчиками в т.н. пакеты, о которых уже упоминалось на протяжении всего этого повествования. Пакет - понятие очень широкое и многогранное. Это может быть и простая монофункциональная утилита (например, строчный текстовый редактор ed
или архиватор tar
), более или менее обширный набор функционально связанных программ (скажем, coreutils
) или огромный программный комплекс (примером чему - XFree86 или Xorg).
Следует оговориться, что термин пакет (английское package) постоянно употребляется в двух смыслах: как набор исходных текстов и как комплект скомпилированных из него программ и всех их служебных файлов. Обычно различие между ними ясно из контекста, в случаях же неоднозначности тот или иной смысл будет оговариваться явно.
Пакеты принято распространять в виде компрессированных архивов - файлов вида *.tar.gz
(*.tgz
) или *.tar.bz2
(*.tbz2
, *.tbz
), так называемых тарбаллов. Обычно действует правило: один тарбалл - один пакет. Очень большие пакеты могут быть поделены на несколько тарбаллов (примером чему те же XFree86 или Xorg), но делается это исключительно для удобства скачивания, все равно такой набор тарбаллов исходников сохраняет свою целостность.
Прекомпилированные пакеты подчас также распространяются разработчиками в виде точно таких же тарбаллов. Но тут уже корреляции пакет - тарбалл может и не быть. Так, 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 эквивалентны, однако это правило имеет многочисленные исключения. Так, при статической сборке (что это такое - будет говориться чуть позднее) библиотеки, которые использует данный пакет, требуются только в момент компиляции - в дальнейшем необходимости в них не возникает.
С другой стороны, следует различать зависимости жесткие и "мягкие". Удовлетворение первых абсолютно необходимо для сборки данного пакета. Так, практически любая программа использует (статически или динамически) главную системную библиотеку glibc
(или libc
), любое приложение для системы X - главную Иксовую библиотеку xlib
, все приложения для интегрированной среды KDE - библиотеки qt
и kdelibc
.
"Мягкие" зависимости данного пакета не критичны для его функционирования - удовлетворение их лишь добавляет ему дополнительные функции (которые могут оказаться и лишними).
Понятие зависимостей пронизывает насквозь POSIX-совместимые системы, и особенно важно для свободных их представителей. В то же время пользователи Windows с ним сталкиваются очень редко, и потому постижение его вызывает определенные трудности у недавнего подоконника. Это связано с двумя факторами.
Во-первых, традиционная модель разработки Unix-программ (то, что задумчиво именуют Unix Way) характеризуется ярко выраженным стремлением не множить сущности без крайней необходимости. Или, говоря попросту, не изобретать велосипеды. То есть: если требуемая разработчику данной программы функция уже реализована и включена в какую-либо распространенную библиотеку, то наш разработчик скорее всего этой библиотекой и воспользуется, а не будет переписывать ее с нуля. Благо, поскольку все распространенные и общеупотребимые библиотеки открыты, он имеет полную возможность это сделать (вспомним о смертном грехе лености:-)).
Возможно, разработчик Windows-программы с удовольствием последовал бы примеру братьев-POSIX'ивистов. Однако исходники Windows-библиотек в большинстве своем закрыты и защищаются всякого рода проприетарными лицензиями, препятствующими их свободному использованию. И потому Windows-разработчику волей-неволей приходится реализовывать требуемые ему функции самостоятельно. В результате чего программа, хотя и приобретает определенную самодостаточность, но зато разбухает в размерах: вспомним, сколько файлов вида *.dll
устанавливает элементарный графический вьювер, идущий в комплекте со сканером или цифровой камерой.
За конфигурирование обычно отвечает сценарий, расположенный в корне дерева исходников данной программы и, по соглашению, носящий имя configure
. Что именно делает конкретный конфигурационный скрипт - сугубо на совести разработчика программы. Как минимум, он обязан проверять жесткие зависимости устанавливаемого пакета и, при их нарушении, выдавать соответствующие сообщения. Кроме того, он может обеспечивать также подключение дополнительных функций - при наличии определенных условий, то есть удовлетворении зависимостей "мягких".
Так, для консольных программ в Linux существует возможность использования мыши в качестве указательно-позиционирующего устройства (а не только для выделения/вставки экранных фрагментов, как это имеет место в BSD-системах). Обеспечивается эта функция специальным сервисом - gpm
. И во многих программах (таких, как файловый менеджер Midnight Commander или текстовый браузер links
) конфигурационный скрипт проверяет, установлен ли пакет gpm
, и при наличии его - автоматически задействует использование мыши.
Если процесс конфигурирования завершается успешно, в корне дерева каталогов создается специальный файл - Makefile
, в котором и фиксируются все предусмотренные разработчиком настройки, выступающие в качестве директив на следующем этапе.
Если конфигурирование прошло с ошибками, выдается соответствующее сообщение, форма которого также целиком определяется разработчиком. Ошибки эти могут быть связаны с нарушением жестких зависимостей пакета, и в этом случае никакие дальнейшие действия, до их разрешения, невозможны. Если же конфигурационный сценарий выявил нарушение "мягких" зависимостей, то пользователь обычно может отказаться от них, просто потеряв некоторую дополнительную функциональность. Которая, к тому же, вполне может быть ему не нужной. Так, например, я всегда отказываюсь от поддержки мыши (через gpm
) в консольных Linux-программах. Правда, это может потребовать указания некоторых опций конфигурирования (о чем я скажу чуть ниже). Правда, обычно это требует указания некоторых дополнительных опций исполнения скрипта configure
, о которых будет сказано ниже.
Образцово-показательный отчет о выполнении сценария configure
выдают, по моему мнению, пакеты, штатно входящие в состав интегрированной среды KDE. Во-первых, их конфигурирование не обрывается сразу же после нахождения первой ошибки (первого нарушения зависимостей - например, отсутствия библиотеки qt
), как это бывает в большинстве других программ, а в любом случае доводится до конца. После чего сообщается, что такие-то компоненты необходимы для сборки данного пакета (то есть связаны с ним жесткими зависимостями), другие же - требуются для получения определенных функций (например, наличие пакета cups
- для обеспечения печати на принтере, пакета sane
- для сканирования, и так далее). И пользователю вольно решить - устанавливать ли ему "мягко-зависимые" пакеты, или он, за отсутствием сканера или принтера, вполне может обойтись без них.
Наконец, конфигурирование завершилось успешно. Наступает время следующего этапа - собственно сборки, то есть претворения исходных текстов программы в исполняемый машинный код. Этап этот распадается на несколько стадий.
Первая стадия - собственно компиляция исходного текста в бинарный код, завершающаяся формированием т.н. объектного модуля. Это - как правило, еще не готовая к запуску программа. Почему? Да потому, что в его скомпилированном коде может не быть многих стандартных функций - тех самых, которые разработчик предполагал заимствовать из разделяемых библиотек.
И потому вторая стадия - это связывание (linking, в просторечии именуемое линковкой) сгенерированного кода с необходимыми библиотечными фрагментами. Линковка может быть двух видов - статическая и динамическая. В первом случае требуемый код из библиотеки встраивается внутрь собираемой программы, после чего получается готовый к исполнению бинарный файл, более в библиотеке не нуждающийся. Это - именно тот случай, когда понятия depends и rdepends приобретают разное значение: первое оказывается шире.
Второй случай - динамической линковки, - предполагает, что библиотечный код не встраивается в программу: вместо него устанавливается только ссылка на файл библиотеки и требуемую функцию (имя которой извлекается из так называемого заголовочного файла - header-файла). И в дальнейшем, при запуске исполняемого модуля программы, соответствующие библиотечные фрагменты извлекаются с диска и присоединяются к коду программы уже только в оперативной памяти. При этом сущности depends и rdepends оказываются идентичными: библиотека, с которой программа связывается при сборке, столь же необходима и для ее запуска.
Динамическая линковка приводит как к сокращению размера исполняемого файла, так и уменьшению объема оперативной памяти, задействуемого при запуске программ (особенно если они используют одни и те же библиотечные функции - а в большинстве случаев так оно и есть). И потому именно она преимущественно используется при сборке программ для свободных POSIX-систем (повторю еще раз, что разделяемые библиотеки в них открыты и могу применяться без ограничений).
Однако бывают ситуации, когда приходится прибегать к линковке статической. Так, во FreeBSD статически линкуются с главной системной библиотекой жизненно важные для запуска и восстановления системы утилиты (в предыдущих версиях этой ОС они располагались в каталогах /bin
и /sbin
, во FreeBSD 5-й ветки для них отведен специальный каталог /restore
). В результате они оказываются доступными (и пригодными к исполнению) даже в случае аварийной загрузки, когда все файловые системы, кроме корневой, не монтируются (а разделяемые библиотеки вполне могут располагаться на самостоятельных физических носителях со своими файловыми системами).
Третий этап процесса сборки - инсталляция. Это - инкорпорация всех компонентов программы в структуру файловой системы данной машины. Или, по простому, по бразильскому - их копирование в соответствующие каталоги файлового древа (по завершении сборки они могут находиться в самых разных местах, обычно - в подкаталогах дерева исходников). Как правило, для разных компонентов пакета существуют традиционно предопределенные имена каталогов, в которых они должны размещаться: bin
или sbin
- для исполняемых модулей, lib
- для библиотек, etc
- для конфигурационных файлов, share
- для всякого рода документации и примеров, и так далее.
Предопределенные имена каталогов не обязательно будут ветвями корня файловой системы (типа bin
, /sbin
и так далее). Точнее, в общем случае, не будут: более вероятно, что соответствующие компоненты собираемого пакета помещаются в каталоги /usr/bin
, /usr/local/bin
и так далее. Впрочем, к обсуждению этого вопроса мы скоро вернемся.
Так вот, процесс инсталляции и сводится к тому, что исполняемый файл (файлы) собранного пакета копируется в файл ~/bin
(или, для программ системного назначения, в ~/sbin
), ее конфиг - в ~/etc
, страницы документации - в ~/man
, и так далее (~/
в данном случае символизирует не домашний каталог пользователя, а некий условный префикс - см. далее).
По завершении всего сборочного цикла из пакета исходников должна получиться полнофункциональная, готовая к употреблению, программа, требующая лишь некоторой пользовательской настройки.
Теперь, разобравшись с принципами, посмотрим, как сборка пакетов осуществляется на практике.
Понятное дело, что перво-наперво тарбалл исходников следует декомпрессировать и развернуть в каком-либо подходящем каталоге. Для самостоятельно собираемых исходников я использую обычно каталоги вроде $HOME/src
или, в некоторых случаях, /usr/local/src
(разумеется, для этого нужно обладать правами на запись в тот или иной). Как станет ясным из дальнейшего, удаление развернутого дерева исходников установленных программ очень нежелательно, поэтому следует озаботиться наличием достаточного количества свободного места в той файловой системе, в которой выполняется распаковка.
Сама по себе распаковка делается обычным образом, например, командой tar
:
$ tar xzpvf /path_to_src/tarball.tar.gz
или
$ tar xjpvf /path_to_src/tarball.tar.bz2
в зависимости от использовавшейся для компрессии программы (gzip
или bzip2
, соответственно). Кратко остановлюсь на смысле опций (команда tar
будет предметом отдельного рассмотрения впоследствие).
Опция x
(от eXtract) предписывает развертывание архива. Однако поскольку он был ранее сжат утилитой компрессии, его предварительно нужно декомпрессировать - этому служит опция z
при gzip
или j
при bzip2
. Опция f
имеет своим значением имя подвергающегося развертыванию/декомпрессии файла - в примере tarball.tar.*
. Опция v
не обязательна - она заставляет выводить на экран сообщения о ходе распаковки.
А вот опция p
может быть важной: она предписывает сохранять атрибуты доступа и принадлежности теми же, что были у оригинальных файлов до их упаковки в тарбалл. Без нее хозяином всех новораспакованных файлов оказался бы пользователь, выполняющий процедуру распаковки. Обычно это не имеет значения но в некоторых случаях - нежелательно, или просто не должно быть (например, при распаковке прекомпилированных тарбаллов stage1-3 в Gentoo). Так что лучше взять себе за правило не забывать про эту опцию никогда.
Если пакет состоит из нескольких тарбаллов, все они должны быть распакованы. Повторять несколько раз какую-либо из приведенных выше команд было бы скучно - однако эту процедуру можно выполнить в один присест. ИМХО, самый простой способ для этого - прибегнуть к универсальной утилите find
, что в данном случае будет выглядеть примерно так:
$ find /path_to_src -name *.tar.gz -exec tar xzpvf {} \;
В результате любой из описанных процедур в текущем каталоге должен образоваться подкаталог вида package_name-version
, то есть соответствующий имени пакета с указанием номера его версии и, иногда, реализации (реже - просто имени пакета). Но это - только в том случае, если исходный тарбалл был сформирован корректно, с включением корня дерева исходников. Редко, но бывает так, что разработчик забывает о такой мелочи. И потому, дабы не получить в текущем каталоге неудобопонятной мешанины файлов, перед собственно распаковкой лучше выполнить проверку на вшивость, командой
$ tar -tzvf tarball.tar.gz
где опция t
(от lisT) и предписывает вывести список файлов тарбалла вместо его развертывания.
Дальнейшие действия по сборке пакета в большинстве случаев осуществляются путем последовательной отдачи трех команд - ./configure
, make
, make install
.
Первую из этих трех команд следует давать, перейдя предварительно в корень дерева исходников нужного пакета:
$ cd /path_to_srcpkg $ ./configure
Это - запуск того самого конфигурационного скрипта, о котором давеча говорилось. Обращаю внимание на ./
- эти символы являются указателями на текущий каталог (/path_to_srcpkg
), в котором расположен файл сценария (а мы помним, что текущий каталог, как правило, не включается в число значений переменной $PATH
).
Сценарий configure
имеет некоторое количество опций. Число их и назначение определяются разработчиком, однако некоторые - встречаются практически всегда. И важнейшая из них - это опция --help
, выводящая полный список всех других опций. Строго говоря, именно с команды
$ ./configure --help
и следует начинать самостоятельную сборку любого пакета, особенно - не знакомого. Прочитав предварительно файлы README
и INSTALL
- минимум один их таковых, скорее всего, имеется в корне дерева исходников, - которые содержат более или менее подробную информацию о программу, в том числе - и о порядке ее сборки. Однако полного перечня опций конфигурирования там не будет - так что ознакомимся с наиболее обычными из них посредством вышеуказанной команды.
В аккуратно написанных программах вывод команды ./configure --help
обычно распадается на несколько секций. Первой, как правили, идет секция
Installation directories:
в которой указываются каталоги, куда в дальнейшем будут устанавливаться отдельные компоненты собранного пакета. Важнейшей опцией здесь является --prefix=PREFIX
. Значением PREFIX
будет выступать ветвь корневого каталога, в подкаталоги которого запишутся исполняемые файлы, конфиги, библиотеки и т.д. По умолчанию эта опция в большинстве случаев имеет значение /usr/local.
То есть в случае, если значение опции --prefix
при запуске скрипта ./configure
не задано, то исполнимые файлы пакета будут инсталлированы в /usr/local/bin
, конфиги - в >/usr/local/etc
, и так далее.
Так что если желательно размещение компонентов собираемого пакета в каталоге, отличном от умолчального (например, в /usr
), значение перфикса следует задать в явном виде, скажем, так:
./configure --prefix=/usr
В последнее время некоторые пакеты предполагают установку по умолчанию в подкаталоги каталога /opt
- /opt/bin
, /opt/lib
, и так далее. А для KDE-приложений последних версий умолчальное значение перфикса - /opt/kde
(/opt/kde/bin
, /opt/kde/lib
и так далее). Для таких программных комплексов лучше его не менять - во избежание осложнений при поиске библиотек.
А вообще, значение опции --prefix
может быть любым. В частности, если предполагается сборка пакета для дальнейшего автономного его распространения в бинарном виде, целесообразно сосредоточить все его компоненты в отдельном подкаталоге, например, вида $HOME/my_pkg/pkg_name
. Аналогично следует поступать и при тестировании пакета, предшествующем его инсталляции.
Далее в той же секции обычно имеет место быть опция --bindir=DIR
. И здесь значением DIR
выступает обычно PREFIX/bin
. Однако в некоторых случаях исполнимые файлы пакета целесообразно поместить в иные ветви файловой системы. Например, если вручную собирается командная оболочка, которая будет выступать в дальнейшем как login shell
, или любимый (=общесистемный) текстовый редактор, очень желательно, чтобы их исполняемые бинарники находились непосредственно в каталоге корневой файловой системы (иначе они могут быть недоступны в аварийных случаях или при старте в однопользовательском режиме). И тут, вне зависимости от того, задано ли значение опции --prefix
или нет, конфигурационный скрипт следует запускать в такой форме:
$ ./configure --bindir=/bin
или, для программ административного назначения, -
$ ./configure --bindir=/sbin
Нередко в секции Installation directories
можно обнаружить и другие опции, предписывающие размещение библиотек, страниц документации и тому подобных компонентов пакета.
Следующая почти непременная секция вывода помощи конфигурационного скрипта -
Optional Features:
Именно в ней, как легко понять из названия, перечисляются опции, позволяющие подключить/отключить дополнительные возможности собираемого пакета. Они имеют вид
--enable-FEATURE
и
--disable-FEATURE
Первая, естественно, подключает возможность с именем FEATURE, а вторая - отключает оную. Причем одна из этих опций может быть принята по умолчанию, Например, большинство свободных программ ныне собирается с поддержкой национальных языков (National Labguage Support - NLS), обеспечивающих вывод сообщений, пунктов меню, помощи т т.д. на языках, отличных от американского (при наличии соответствующих ресурсов, разумеется - если систему помощи некоего пакета никто не удосужился перевести на русский язык, то включай NLS, не включай - все едино, русского хелпа от нее не получишь). Однако в ряде случаев это может показаться нежелательным - и тогда при конфигурировании программы нужно задать соответствующею опцию:
$ ./configure --disable-nls
Обычно допустима и иная форма этой опции:
$ ./configure --enable-nls=no
Функции, подключаемые (или отключаемые) посредством опции --enable(disable)-FEATURE
, берутся из библиотечных пакетов. В частности, за поддержку NLS отвечает библиотека gettext
(подчеркну, что сама по себе эта библиотека не дает возможности волшебным образом получать сообщения на русском или там эскимосском языке, а только обеспечивает принципиальную возможность таковых).
Сходный смысл имеют опции, входящие в секцию
Optional Packages:
общий вид которых таков:
with-PACKAGE
или
without-PACKAGE
Отличие опций этой секции от тех, что перечислены в секции Optional Features
- в том, что в качестве значений PACKAGE
выступаю не некие абстрактные функции, а имена конкретных пакетов, возможности которых добавляются к собираемому (или отнимаются от оного).
Опции вида with-PACKAGE
могут также иметь значения - yes
, no
или auto
. Последняя обычно принята по умолчанию. То есть, если в ходе выполнения конфигурационного скрипта пакет с именем PACKAGE будет обнаружен в данной системе, его возможности будут подключены автоматически, если нет - проигнорированы.
Так, в приводимом ранее примере с поддержкой указующе-позиционирующих свойств мыши при сборке консольных программ типа links
или mc
они будут автоматически задействованы по умолчанию, если в Linux-системе (к BSD это не относится) обнаружится установленный пакет gpm
. Если же он имеется, но поддержка мыши для данного пакета представляется нежелательной, это следует указать в явном виде:
$ ./configure --without-gpm
И последняя секция, практически всегда присутствующая в выводе помощи конфигурационного скрипта -
Some influential environment variables:
Как следует из названия, здесь перечисляются различные переменные окружения, могущие оказывать влияние на процесс компиляции. Наиболее часто в качестве таких переменных предусматриваются флаги компилятора gcc
типа CFLAGS
и CXXFLAGS
(для программ на языке Си и Си++, соответственно). Обычное употребление таких флагов - задание всякого рода оптимизаций - общего ее уровня, архитектуры целевого процессора, конкретных наборов его команд, и так далее. Например, оказание опции
$ ./configure CFLAGS="-O3 -march=pentium4"
обеспечит максимальный уровень оптимизации (значение -O3
) для процессора Pentium4 (значение -march=pentium4
) - опять же заостри внимание на кавычках, в которые эти значения заключены (дабы восприниматься как единый аргумент). Впрочем, тема оптимизации будет предметом особого рассмотрения.
Ранее я говорил, что при сборке пакеты могут связываться с библиотечными функциями как статически, так и динамически. Так вот, характер связи также определяется на стадии начального конфигурирования. По умолчанию во всех известных мне случаях используется динамическая линковка. Что связать исполняемый модуль с библиотекой статически, требуется специальный флаг - LDFLAGS
. Значениями его в данном случае будут
$ ./configure LDFLAGS="-static"
или, в случае линковки с несколькими библиотеками -
$ ./configure LDFLAGS="-all-static"
Вообще говоря, опции конфигурирования пакета могут быть очень разнообразны - я перечислил лишь наиболее часто встречающиеся. Общие рецепты эффектвиного их использования - а) внимательное чтение вывода помощи конфигурационного скрипта, б) знание особенностей своей собственной системы (в первую очередь - мест локации разделяемых библиотек) и в) конечно же, здравый смысл.
Надо отметить, что некоторые пакеты не имеют конфигурационного сценария в дереве исходников. Это не обязательно - следствие лени разработчика: может быть, что программа настолько проста, что в предварительном конфигурировании не нуждается (например, не использует функций никакой разделяемой библиотеки).
Другая возможная причина отсутствия скрипта configure
- то, что предварительное конфигурирование уже выполнено разработчиком. В этом случае в дереве исходников можно обнаружить уже готовый Makefile
, рассчитанный на некоторые типовые ситуации, или несколько его вариантов - для разных архитектур, операционных систем и так далее. Если же ни один из предложенных автором вариантов не отвечает в полной мере реалиям пользователя - у него остается последний выход: ручная правка Make-файла (обычно такие случаи документируются в файлах типа README
или INSTALL
./p>
Предварительное конфигурирование пакета - очень важный момент в его сборке: можно сказать, что успех ее на 90% определяется именно в результате исполнения скрипта configure. Однако рано или поздно оно завершается удачно (о случаях фатального невезения я скажу несколько позже). И наступает время собственно сборки, для чего предназначено второе из наших магических заклинаний - команда make
.
Сама по себе команда make
не выполняет ни компиляции (это - дело компилятора gcc
, ни линковки (с этой ролью справляется редактор связей ld
), ни каких-либо иных действий по превращению исходного текста в машинный код. Задача ее - интеграция всех требуемых средств (а в процессе сборки могут задействоваться и т.н. препроцессоры, и языковые анализаторы, возможно, и иные инструменты), чтобы автоматически получить (почти) готовые к употреблению бинарные компоненты пакета. Собственно говоря, от пользователя требуется только дать директивное указание - набрать в командной строке make
и нажать Enter - все остальное произойдет как бы само собой. С другой стороны, у него нет и возможности вмешаться в процесс сборки (разве что прервать его комбинацией клавиш Control+C:-)).
Конечно, и команда make
способна воспринимать многие параметры командной строки. Правда, в большинстве случаев они дублируют опции, заданные при конфигурировании. Так, при отдаче директивы make
можно задать флаги оптимизации
$ make CFLAGS="-O3 -march=pentium4"
предписать статическую линковку с разделяемыми библиотеками
$ make LDFLAGS="-static"
или предписать последующую установку компонентов пакета в каталог, отличный от умолчального. Однако - и все: далее остается только дожидаться успешного (надеюсь) окончания сборки.
Однако у команды make
есть еще один важный вид аргументов командной строки - так называемые цели (target). Собственно для сборки по умолчанию они обычно не требуются. Хотя некоторые пакеты требуют их задания в явном виде. Так, оконная система X штатным образом собирается с указанием цели world
:
$ make world
В других случаях для достижения того же результата может применяться и иная цель, например
$ make all
или указание на конкретную архитектуру, операционную систему, и т.д. Все такого рода исключения, как правило, описаны в сопроводительной документации. На худой конец (если таковой не имеется), допустимые для данного пакета цели команды make
можно подсмотреть в Makefile
.
Однако есть у команды make
и практически обязательная цель - install
, предписывающая выполнить установку компонентов пакета в надлежащие места файловой системы (умолчальные или определенные на этапе конфигурирования или сборки). И это - третье, и последнее, из наших шаманских заклинаний:
$ make install
После чего мы наконец получаем готовую к употреблению программу.
В некоторых программах цель install
разработчиком не предусматривается. И тут приходится вручную скопировать скомпилированные модули в подходящие каталоги. Правда, такое бывает очень редко и, как правило, для простых по устройству программ.
В принципе команду make install
можно было бы дать и сразу по исполнении сценария ./configure
. В этом случае сначала будет исполнена умолчальная цель make
- компиляция и линковка пакета, а затем его инсталляция. Однако делать это не всегда целесообразно. Во-первых, такое совмещение целей затрудняет отслеживание ошибок. Во-вторых, их совместному исполнению может помешать отсутствие должных прав доступа.
Дело в том, что стадии конфигурирования и сборки обычно могут быть выполнены от имени обычного пользователя - это определяется правами доступа к каталогу, в который было распаковано дерево исходных текстов пакета. А вот установка собранных компонентов почти наверняка потребует административных привилегий. Ведь исполняемые файлы пакета после директивы make install
копируются (если придерживаться умолчальной схемы) в каталог /usr/local/bin
, документация - в /usr/local/share
, и так далее. А все они закрыты для изменения кем бы то ни было, кроме root'а.
Так что, прежде чем начинать установку собранного пакета, следует озаботиться получением соответствующих полномочий командой
$ su
или, в некоторых случаях, даже
$ su -
которая приведет среду исполнения команды в соответствие с конфигурацией суперпользователя.
Впрочем, иногда полномочия администратора могут оказаться необходимыми и при сборке программы или ее конфигурировании. Типичный тому пример - сборка ядра Linux штатными средствами, в ходе которой задействуются скрипты, требующие root-доступа. Впрочем, это - тема отдельной беседы.
Вернемся к сборке "обычных", если так можно выразиться, пакетов. Все вышесказанное относилось к случаю сборки без ошибок на любом этапе. От каковых, однако же, никто не гарантирован. И что делать, если ошибки появляются?
В случае ошибки при сборке пакета перед пользователем, как обычно, появляется два выхода: а) бросить это занятие, попробовав отыскать и установить прекомпилированный вариант пакета, и б) разобраться в причинах ошибки и попытаться ее устранить.
Наиболее часто сообщение об ошибке возникает в ходе предварительного конфигурирования. И, в большинстве случаев, связано с нарушением зависимостей, жестких или "мягких" - обычно это можно понять, внимательно читая экранный вывод.
С нарушением жестких зависимостей все ясно - нужно установить пакет, от которого зависит собираемый, и все - вариантов тут не предлагается. Нарушения же "мягких", но тем не менее принятых разработчиком по умолчанию, зависимостей обычно можно избежать посредством явного указания опций конфигурирования - типа disable-FEATURE
и --without-PACKAGE
.
Встречаются и ситуации кажущегося нарушения зависимостей, когда в ходе конфигурирования следует сообщение об отсутствии пакета имя рек, хотя пользователь точно знает, что таковой был установлен. В одних случаях это может быть связано с тем, что собираемый пакет ссылается не просто на некую библиотеку, но на конкретную ее версию. При этом, даже если в системе установлена более новая ее реализация, заведомо перекрывающая функциональность предыдущей (а совместимость сверху вниз - один из краеугольных камней программирования вообще и POSIX-программирования - в особенности), в ходе конфигурирования будет отмечено нарушение зависимостей. Разрешение такой коллизии - очень простое: создание символической ссылки вида
$ ln -s libname.xxx libname.yyy
где xxx
- номер старой версии библиотеки, а yyy
- актуальный ее вариант.
Другой случай - когда сценарий конфигурирования пакета ищет библиотеку, от которой он зависит, не в том каталоге, где она реально располагается. Так, старые приложения KDE могут ожидать требуемых им библиотек в каталогах типа /usr/local/qt
и /usr/local/kde
, тогда как ныне они, скорее всего, будут располагаться в ветвях каталога /opt
.
И тут выход из положения не сложен. Во-первых, можно задать переменную окружения
LDPATH="/opt/qt:/opt/kde"
значения которой точно определяют каталоги соответствующих программных комплексов.
Во-вторых, эти значения можно просто указать в качестве опций конфигурационного скрипта:
$ ./configure --with-qt-dir=/opt/qt \ --with-kde-dir=/opt/kde
Реже бывают ошибки при исполнении команды make
. Однако бороться с ними труднее. Общий рецепт тут дать очень трудно. Можно только посоветовать внимательно читать вывод сообщений о ходе компиляции, непосредственно предшествующих ее обрыву.
Ошибки при инсталляции связаны, почти всегда, с отсутствием прав на запись в каталоги, в которые помещаются устанавливаемые компоненты пакета. Иногда же они возникают вследствие того, что целевой каталог просто не существует. Например, в таких дистрибутивах Linux, как CRUX и Archlinux, из пакетов штатного комплекта изъята вся документация, кроме man-страниц. И, соответственно, отсутствуют каталоги для помещения документации в форматах info и html. А поскольку практически любая программа проекта GNU сопровождается info-документацией, попытка инсталляции ее вызывает ошибку. Побороть которую очень просто: нужно только уничтожить в дереве исходников соответствующие подкаталоги.
На какой бы стадии ни возникла ошибка, перед повторной сборкой пакета дерево исходников следует очистить от побочных продуктов сборки предыдущей. Резонные люди рекомендуют просто стереть каталог с исходными текстами и развернуть его из тарбалла по новой. Это, конечно, самый радикальный способ, но не всегда приемлемый. Обычно можно обойтись и терапевтическими мерами. Потому что в большинстве пакетов предусматриваются специальные цели для таких процедур. Первая из них
$ make clean
Она вычистит дерево исходников от объектных модулей, собранных при предыдущей компиляции. Сохранив, тем не менее, созданные при конфигурировании Make-файлы. Если нужно избавиться и от них - существует цель
S make distclean
по исполнении которой дерево исходников теоретически должно приобрести первозданный вид.
Пакеты, как правило, устанавливаются для того, чтобы запускать входящие в них программы:-). Однако не исключено, что первый же запуск новой программы показывает, что она делает что-то не то или не так, нежели это нужно пользователю. Или просто ему не нравится. И возникает вопрос - а как удалить такой неподходящий пакет?
Можно, конечно, последовательно пройтись по всем каталогам, в которые записывались компоненты пакета, выявить их по каким-либо признакам (например, по атрибуту ctime
, в данном случае отвечающему времени создания файла) и удалить вручную. Однако способ этот - трудоемкий и чреват ошибками.
К счастью, большинство разработчиков, не страдающих манией величия, предусматривают такую ситуацию, определяя специальную цель для удаления программы. Обычно это -
$ make uninstall
реже -
$ make deinstall
Любую из этих команд нужно дать в корне дерева исходников - именно поэтому его желательно сохранять и после сборки, не смотря на непроизводительный расход дискового пространства: иначе единственным способом удаления пакета останется ручной. При деинсталляции команда make
отыскивает установленные компоненты пакета в дереве файловой системы и удаляет их.
Важно, что make uninstall
не затрагивает пользовательских настроечных файлов - т.н. dot-файлов в его домашнем каталоге, которые часто генерируются автоматически при первом запуске программы. Такие файлы при необходимости в любом случае придется удалять вручную. Казалось бы - неудобство, однако сейчас мы увидим, что это не лишено резонов.
Дело в том, что самостоятельная сборка пакетов из исходников не предусматривает никакого механизма обновления их версий. Правда, на самом деле обладателю настольной машины обычно нет надобности гнаться за самыми актуальными версиями большинства пользовательских программ: это целесообразно делать только при существенном расширении их функциональности или обнаружении серьезных ошибок. Однако администратор системы вынужден отслеживать все обновления пакетов, связанные исправлением ошибок в безопасности системы. А поскольку, как я все время повторяю, каждый пользователь - это немного админ своего десктопа, необходимость в обновлении версий возникает достаточно часто.
Так вот, единственный способ обновления собственноручно собранной программы - это собрать и инсталлировать ее по новой. При этом теоретически старые компоненты должны затереться новыми. Однако на практике от старой версии могу остаться хвосты, не только бесполезные, но, возможно, и конфликтующие с файлами новой версии.
И потому при обновлении версий вручную обычно рекомендуется сначала удалить старую версию, и лишь затем инсталлировать новую. Предварительно убедившись, конечно, в успешности сборки ее (то есть выполнив стадии ./configure
и make
- это еще одна причина для обособления цели make install
) и работоспособности (для чего можно пробно запустить исполняемый файл пакета прямо из каталога исходников).
В то же время при смене версий, как правило, желательно сохранить все выполненные ранее настройки пакета - подчас это весьма трудоемкая процедура. И вот тут-то и оказывается, что пользовательские настройки удаленной версии остались в неприкосновенности в домашнем каталоге./p>
Ядро свободной POSIX-системы - это (почти) такая же программа, как и любая другая, распространяемая в исходных текстах. И потому оно также должно быть приведено в пригодное к употреблению состояние посредством сборки. Однако ядро все же - не пользовательское приложение и даже не системная утилита, и потому процесс сборки ядра имеет свою специфику.
Кроме того, ядро - это тот компонент, который и отличает в первую очередь одну POSIX-систему от другой - например, Linux от FreeBSD. Поэтому процесс сборки ядра для каждой операционки имеет свою специфику. Тогда как все сказанное выше касаемо сборки пакетов имеет силу (с очень незначительными оговорками) для любой POSIX-системы - причем даже не обязательно свободной: открытые приложения и утилиты для проприетарной Solaris или AIX в принципе собираются точно так же, как для Linux или какой-либо BSD.
Тем не менее, сборка ядра любой из рассматриваемых ОС включает те же стадии, что и сборка иных приложений - предварительное конфигурирование, собственно сборку и инсталляцию.
Конфигурирование ядра - это включение или выключение поддержки различных устройств (т.н. драйверов, хотя здесь это понятие существенно отличается от принятого в Windows), файловых систем, сетевых протоколов и т.д. Включение поддержки какой-либо опции подразумевает, что в ядро будет встроен код, обеспечивающий работу с неким устройством, файловой системой и проч. Кроме того, многие опции могут быть включены как модули. То есть соответствующий им код компилируется в бинарный вид, но непосредственно в ядро не встраивается, а подгружается в виде отдельной программы по мере необходимости - вручную, соответствующими командами, или автоматически.
Заметим, что, когда речь заходит о драйверах устройств, распространяемых отдельно от ядра операционной системы (например, производителями оборудования - некоторые из них признали ныне факт существования операционок, отличных от Microsoft Windows), имеются ввиду именно загружаемые модули ядра. Подобно другим программам, они могут существовать в виде исходников или в бинарном виде. В первом случае их теоретически можно собрать для любой версии ядра (или, по крайней мере, для диапазона близких версий), разумеется, только данной ОС (драйверы для Linux, как можно догадаться, не будут работать во FreeBSD, и наоборот). Бинарные, прекомпилированные, драйверы обычно жестко привязаны к версии ядра, хотя иногда могут работать и при смене оной.
Процесс конфигурирования является наиболее ОС-специфичным во всей процедуре сборки ядра. Само собой разумеется, что ядра разных операционок имеют разные функции и, соответственно, разные опции конфигурирования - еще бы, ведь это все-таки разные программы:-). Но даже чисто внешне, на пользовательском уровне, процесс конфигурирования ядра Linux и, скажем, FreeBSD существенно отличается.
Во FreeBSD традиционным инструментом конфигурирования ядра выступает обычный текстовый редактор. С его помощью конфиг умолчального ядра (т.н. ядра GENERIC
, устанавливаемого при инсталляции системы) приводится в соответствие с потребностями пользователя - одни опции отключаются, другие - включаются.
С отключаемыми опциями все понятно - они просто изымаются из умолчального конфига (путем установки символа комментария на соответствующих строках). А вот откуда взять опции недостающие? Они отыскиваются в некоем образцово-показательном конфигурационном файле. Во FreeBSD 4-й ветки (и, насколько мне известно, более ранних) этот файл носил имя LINT
(во FreeBSD для конфигов ядра принято употреблять символы верхнего регистра - этим подчеркивается величие сей программы) и был похож на настоящий - хотя и не работал (в смысле - скомпилировать из него работоспособное ядро было невозможно). В 5-й ветке на смену ему пришел файл NOTES
- уже без претензий на всамделишность, это просто список всех теоретически доступных опций конфигурирования, снабженных достаточно подробными комментариями.
Специального включения/выключения модулей в конфигурационном файле ядра FreeBSD не предусмотрено - в качестве таковых по умолчанию собираются все выключенные опции, для которых модульная поддержка в принципе возможна (а она реализована еще не для всех опций). Правда, это положение можно изменить правкой соответствующих конфигурационных файлов.
В первозданном (или каноническом - том, что скачивается с kernel.org) ядре Linux никакого умолчального конфигурационного файла не предусмотрено: он генерируется при первом же запуске штатного инструмента для ядерной настройки, базируемого все на той же утилите make
- то есть представляющего собой одну из обычных ее целей.
Точнее, не одну - для изначального конфигурирования ядра предусмотрено ажно четыре цели: make config
, make menuconfig
, make xconfig
и make gconfig
. Все они делают одно дело - но каждая по своему:-).
Команда make config
вызывает текстовый конфигуратор ядра, работающий в диалоговом режиме. То есть он требует ответа на множество вопросов, для которых предусмотрены варианты ответов - Yes
и No
, а для многих опций - еще и M
(Module), который и обеспечивает подключение модульной поддержки (как и во FreeBSD, таковая возможна не для всех опций).
make config
представляется не очень удобным - в случае малейшей ошибки имеется только одна возможность для ее исправления - оборвать программу (например, через Control+C
) и начать все заново. И вообще, этот метод конфигурирования считается устаревшим, и в ядрах ветки 2.6.X цель make config
штатно не документирована. Хотя и может использоваться при желании - ее преимущество (ИМХО, несколько сомнительное) в том, что, в отличие от прочих средств конфигурирования, она не требует прав суперпользователя.
Команда make menuconfig
, напротив, загружает весьма наглядный меню-ориентированный конфигуратор, богато оформленный псевдографикой. В каждом из подпунктов меню достаточно отметить нужные опции для их включения и, напротив, снять отметки для отключения. Опции, для которых реализована модульная поддержка, могут быть отмечены соответствующим образом.
Использование make menuconfig
- пожалуй, наиболее употребимый способ конфигурирования ядра Linux. Нужно только помнить, что запуск соответствующей команды требует полномочий root'а: не то чтобы при этом происходит что-то особо брутальное, просто таковы атрибуты доступа к используемым целью скриптам.
Команды make xconfig
и make gconfig
вызывают конфигураторы ядра, работающие в графическом режиме и, соответственно, могут быть запущены только в терминальном окне сеанса Иксов. В версиях ядра до 2.4.X включительно предусматривалась только первая из указанных целей, которая вызывала достаточно простую меню-ориентированную программу. В ядрах ветки 2.6.X цель make xconfig
претерпела существенные изменения: теперь ею вызывается весьма "тяжелая", основанная на библиотеке Qt, программа, требующая также наличия в системе установленной среды KDE и пакета разработки kdevelop
, что делает ее практически недоступной для многих пользователей. Ну а make gconfig
влечет загрузку аналогичного по функциональности конфигуратора, но основанного на библиотеке Gtk. По причине стойкой нелюбви к последней я этим методом никогда не пользовался и ничего сказать о нем не могу.
Есть в Linux и еще несколько целей, обеспечивающих конфигурирование ядра. Так, командой make defconfig
можно сгенерировать некий умолчальный ядерный конфиг - в нем будут включены именно те опции и модули, которые отмечены по умолчанию в меню make menuconfig
.
При смене версии ядра можно прибегнуть к цели make oldconfig
. Она восстановит конфигурацию ядра прежнего, запросив ответы только на вопросы, касающиеся появившихся опций.
И, наконец, (почти) полный список доступных для конфигурирования ядра и его сборки можно получить посредством
$ make help
Собственно сборка ядра и в Linux, и в BSD-системах осуществляется командой make
- возможно, с указанием неких конкретных целей. Останавливаться на их описании я сейчас не буду - тому придет свое время. Замечу только, что в Linux дополнительно нужно собрать и модули - командой
make modules
тогда как во FreeBSD это произойдет по умолчанию одновременно с компиляцией ядра.
В результате по завершении сборки у нас в подкаталогах дерева исходников ядра образуется собственно образ ядра - (почти) обычный исполняемый файл, - и набор скомпилированных модулей. Это такие же объектные файлы, которые возникают на промежуточной стадии компиляции других программ. Так как к самостоятельному использованию они не предназначены, то и в линковке они не нуждаются.
Файл образа ядра во FreeBSD всегда носит имя kernel
. В Linux же существует несколько видов образов, за которыми традициями закреплены фиксированные имена - типа vmlinuz
, bzImage
и еще несколько, о чем мы подробно поговорим тогда, когда до этого дойдет дело.
Как и компоненты любого прикладного пакета, образ ядра и сопровождающие его модули должны быть инсталлированы в должное место - в те подкаталоги, где система ожидает обнаружить их при загрузке. В Linux таких мест традиционно два - корень файловой системы или каталог /boot
, тогда как для модулей предназначается каталог /lib/modules/X.Y.Z
, где X.Y.Z
- номер версии ядра (например, 2.6.7
). Во FreeBSD, начиная с 5-й ветки, под ядро и модули отведен каталог /boot/kernel
.
Так вот, процесс инсталляции и сводится к тому, что файл образа ядра вместе с объектными модулями (а это ныне файлы вида *.ko
- для отличия от обычных объектных файлов) копируется в надлежащие каталоги. Для этого предназначены специальные цели команды make
. Во FreeBSD это make install
или make kernelinstall
. В Linux для установки модулей предназначена цель make modules_install
, а само ядро может быть инсталлировано различными способами (в том числе - и просто ручным копированием).
В разделе о системных загрузчиках мы увидим, что в принципе ядро может быть размещено чуть ли не в любой части файловой системы. Нужно только позаботиться о том, чтобы программа, выступающая в данной ОС в качестве загрузчика, знала о его расположении. Однако нестандартное размещение ядра может создать некоторые проблемы и в любом случае потребует лишних телодвижений, так что лучше придерживаться традиционной схемы.