Компиляция, сборка и установка программ


Введение

Разные способы установки программ

При установке ПО в Unix используется один из трех вариантов:

  1. Из готового пакета (в RedHat Linux -- при помощи rpm).
  2. Разворачивание из файла .tar.gz или .tgz, содержащего готовые исполняемые файлы.
  3. Компиляция из исходных текстов.

Первый вариант -- самый удобный и предпочтительный. Но при этом надо, чтобы нужное ПО существовало именно в виде .rpm-пакета.

Второй вариант очень часто используется с программами "от третьих поставщиков". К примеру, самая последняя версия Netscape обычно становится доступна в виде .tar.gz-архива (и лишь несколько позже появляется .rpm-пакет).

Третий вариант до недавнего времени был самым распространенным, да и до сих пор множество программ научного и развлекательного назначения распространяются именно в виде исходных текстов. Достоинство этого способа -- то, что собрать программу из исходных текстов можно практически под любым Unix, даже если сам автор этого не сделал. К недостаткам же относится то, что "сходу" программа может и не откомпилироваться (или даже просто не заработать), так что могут потребоваться навыки программиста.

Если дистрибутив берется из .tar.gz-архива, то чаще всего его может установить не только "root", но и любой пользователь (естественно, в свою личную директорию, а не в системную). В случае же .rpm-пакета обычному пользователю придется повозиться, чтобы извлечь оттуда файлы для "ручной" установки.

В какие директории следует устанавливать ПО

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

Основные директории, где располагается ПО
ДиректорияНазначение
/usr/Стандартное ПО, поставляемое с системой.
/usr/X11R6/Система X-Window и разнообразное ПО для нее.
/usr/local/Локальное ПО (обычно от третьих поставщиков), установленное администратором отдельно после инсталляции системы. Здесь может располагаться также отдельно установленное ПО под X-Window.

Как очевидно, "вручную" программы надо устанавливать именно в /usr/local/.

В каждой из этих иерархий имеются поддиректории bin/, lib/, man/ и doc/, служащие для исполняемых файлов, библиотек, man-страниц и документации соответственно.

/usr/
    |
    +--bin/
    +--doc/
    +--lib/
    +--man/
    |
    +--X11R6/
    |       |
    |       +--bin/
    |       +--doc/
    |       +--lib/
    |       +--man/
    |
    +--local/
            |
            +--bin/
            +--doc/
            +--lib/
            +--man/

В переменной окружения PATH есть ссылки на /usr/bin, /usr/X11R6/bin и /usr/local/bin, так что все программы запускаются просто по имени. Команда man же, соответственно, ищет man-страницы также во всех трех man-директориях.

Естественно, весь дистрибутив следует ставить внутрь одной иерархии. Так, если исполняемые файлы располагаются в /usr/local/bin/, то man-страницы -- в /usr/local/man/, а библиотечные файлы -- в /usr/local/lib/.

Некоторые пакеты (например, Wabi -- эмулятор Windows 3.x) устанавливаются в директорию /opt/. В этом случае создается директория вида /opt/имя-программы/ (например, /opt/wabi/), и в ней размещаются все файлы пакета. Такие пакеты обычно не предлагают выбор директории, а просто сами ставятся в /opt/.

Подробно про то, какие директории в файловой системе для чего предназначены, можно прочитать в "Filesystem Hierarchy Standard" (прежнее название -- "Linux FSSTND") по адресу

http://www.pathname.com/fhs/

Меры предосторожности

Установку ПО из .rpm-пакетов может выполнять только "root".

При установке же из бинарного дистрибутива или из исходных текстов следует придерживаться следующих правил:

  1. Разворачивать архивы и выполнять компиляцию надо не как "root", а как обычный пользователь. Для этого стоит выделить отдельную директорию. А еще лучше -- создать для этих целей специального пользователя и компилировать ПО только из-под него.
  2. Все файлы, устанавливаемые в общее пользование (в общесистемные директории) должны иметь права доступа "rw-r--r--" для обычных файлов и "rwxr-xr-x" для исполняемых файлов и директорий, и их владельцем должен быть пользователь "root" и группа "root", если в инструкциях не оговорено иное.
  3. Если пакет требует установки каких-либо исполняемых файлов с флажком смены идентификатора пользователя (setuid, suid) или группы (setgid, sgid), то следует десять раз проверить, действительно ли это необходимо пакету для работы, и если да -- то лучше отказаться от его использования.


Подробно про rpm

Зачем нужен rpm

Как уже упоминалось в разделе "Добавление и удаление пакетов", rpm (Redhat Package Manager) служит для работы с пакетами -- установка, удаление, проверка и т.д.

При установке пакета rpm записывает информацию о нем в свою базу данных, что и позволяет в дальнейшем удалять пакет, просматривать информацию о нем и т.д.

Такой подход к установке ПО имеет несколько достоинств, в частности:

  • Унифицированная работа с разными пакетами (в частности, не надо помнить, куда какая-либо программа положила при инсталляции свои файлы -- постоянная головная боль в Dos/Windows).
  • Отслеживание зависимостей между пакетами выполняется автоматически (не надо помнить, что программа такая-то требует некоей библиотеки с версией не ниже какой-то -- сравните с вечными проблемами, к примеру, с DirectX в Windows).
  • Непротиворечивость между разными пакетами -- в частности, корректно "разводится" ситуация, когда несколько пакетов содержат один и тот же файл (например, в /etc/).

Режимы работы rpm

Если вызвать rpm без параметров, то он покажет "краткий" список ключей. Обычно же формат вызова rpm такой:

rpm -КлючРежима [дополнительные ключи] [параметры]

КлючРежима, указываемый первым, определяет режим работы. Самые частоиспользуемые режимы перечислены в таблице.

Основные варианты вызова rpm
КомандаНазначение
rpm -i файл-пакета.rpmУстановка пакета (install)
rpm -U файл-пакета.rpmОбновление пакета (Upgrage)
rpm -e пакетУдаление пакета (erase)
rpm -q пакетПолучение информации (query)
rpm -y пакетПроверка пакета (verify)
rpm -bСоздание пакета .rpm из .src.rpm (build); здесь не рассматривается

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

Ключи и параметры, общие для разных режимов

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

Имя-файла-пакета.rpm для режимов -i и -U -- это полное (с директорией) имя файла. Например, ~/RPMS/apache-1.3.3-1.i386.rpm. В принципе, rpm понимает имена файлов в виде ftp-URL, т.е. ftp://сервер/директория/файл.rpm, но ими имеет смысл пользоваться только в быстрой и надежной сети (в нашей стране -- в локальной). Впрочем, в локальной сети (и любой быстрой) удобнее пользоваться NFS-сервером, если он есть.

Пакет -- это имя уже установленного пакета для режимов -e, -q и -y. Оно может указываться как с номером версии, так и без него. Примеры: acroread-3.01-4, acroread. Если вместо списка пакетов указать ключ "-a" (all), то это будет означать "все пакеты". Кроме того, ключ "-f" позволяет вместо имени пакета указать какой-либо файл, принадлежащий этому пакету (см. ниже).

Можно указывать не один файл-пакета или пакет, а сразу несколько, разделяя их пробелами.

Получение информации

Команда rpm -q позволяет получать следующую информацию о пакете:

  • Версию пакета.
  • Список файлов.
  • Чего требует пакет.
  • Можно узнать, какому пакету принадлежит указанный файл.

Просто "rpm -qимя-пакета" выдает полное название пакета, вместе с версией:

bobby:~% rpm -q gcc
gcc-2.7.2.3-14
bobby:~% _

Но чаще всего команда "rpm -q" используется для получения списка файлов пакета.

Краткая информация о пакете -- rpm -qi


Команда "rpm -qi" (info) выдает сводку информации о пакете -- название, версия, объем и т.д., плюс краткую аннотацию:

bobby:~% rpm -qi xjewel
Name        : xjewel                      Distribution: Manhattan 
Version     : 1.6                               Vendor: Red Hat Software
Release     : 9                             Build Date: Thu Aug 13 05:51:53 1998
Install date: Wed Dec  9 21:00:59 1998      Build Host: porky.redhat.com
Group       : X11/Games/Video               Source RPM: xjewel-1.6-9.src.rpm
Size        : 50289                            License: MIT
Packager    : Red Hat Software <bugs@redhat.com>
Summary     : Game like Sega's columns
Description :
Jewel is a game much like Domain/Jewelbox which is a puzzle game like
Tetris.

It is played by controling the motion of blocks which continue to fall from
the top of the screen.  One can move them left and right, as well as
rotate the jewel segements.  The object is to get the most points before
the grim reaper ends the fun.
bobby:~% _

Список файлов пакета -- rpm -ql


Для получения списка файлов используется ключ "-l" (list):

bobby:~% rpm -ql xjewel
/etc/X11/wmconfig/xjewel
/usr/X11R6/bin/xjewel
/usr/X11R6/man/man1/xjewel.1x
/var/lib/games/xjewel.scores
bobby:~% _

Поскольку некоторые пакеты содержат очень большое количество файлов, то стоит отправлять вывод от rpm -ql команде less:

rpm -ql gcc | less

Для получения "полной" информации о пакете (аннотации и списка файлов) можно указать ключи "-i" и "-l" одновременно:

bobby:~% rpm -qil xjewel
Name        : xjewel                      Distribution: Manhattan 
Version     : 1.6                               Vendor: Red Hat Software
Release     : 9                             Build Date: Thu Aug 13 05:51:53 1998
Install date: Wed Dec  9 21:00:59 1998      Build Host: porky.redhat.com
Group       : X11/Games/Video               Source RPM: xjewel-1.6-9.src.rpm
Size        : 50289                            License: MIT
Packager    : Red Hat Software <bugs@redhat.com>
Summary     : Game like Sega's columns
Description :
Jewel is a game much like Domain/Jewelbox which is a puzzle game like
Tetris.

It is played by controling the motion of blocks which continue to fall from
the top of the screen.  One can move them left and right, as well as
rotate the jewel segements.  The object is to get the most points before
the grim reaper ends the fun.
/etc/X11/wmconfig/xjewel
/usr/X11R6/bin/xjewel
/usr/X11R6/man/man1/xjewel.1x
/var/lib/games/xjewel.scores
bobby:~% _

Какому пакету принадлежит файл


Часто возникает необходимость узнать, какому пакету принадлежит какой-то файл (например, чтобы знать, где искать к нему документацию). Для этого можно воспользоваться ключом "-f" (file):

bobby:~% rpm -qf /etc/dosemu.users
dosemu-0.98.1-2
bobby:~% _

При этом надо указывать полное имя файла -- с директорией. Кроме того, если к файлу есть "несколько путей" (из-за символьных линков на директории), то следует указывать "основной" (обычно тот, который без символьных линков), иначе rpm не сможет дать ответ:

bobby:~% ls -ld /usr/X11 /usr/X11R6
lrwxrwxrwx   1 root     root            5 Dec  9 20:44 /usr/X11 -> X11R6
drwxr-xr-x   8 root     root         1024 Jan 19 05:14 /usr/X11R6
bobby:~% rpm -qf /usr/X11/lib/X11/app-defaults/GV
file /usr/X11/lib/X11/app-defaults/GV is not owned by any package
bobby:~% rpm -qf /usr/X11R6/lib/X11/app-defaults/GV
gv-3.5.8-5
bobby:~% _

Вообще-то действие ключа "-f" не ограничивается простым запросом "скажи кому принадлежит файл". Этот ключ позволяет другим способом сослаться на пакет, т.е. вместо имени пакета указать один из принадлежащих ему файлов. Так, команды

rpm -qif /etc/zshenv
и

rpm -qi zsh
эквивалентны.

А как там назывался пакет...


Иногда возникает такая ситуация: примерно помнишь, как назывался некий пакет, но только примерно (а мало ли где в имени были заглавные буквы, где маленькие, где дефисы...). В этой ситуации можно заставить rpm выдать список всех пакетов (ключ "-a") и найти нужное при помощи grep. Пример ("как назывались пакеты, содержащие netscape?"):

bobby:~% rpm -qa | grep netscape
netscape-communicator-4.08-1
netscape-common-4.08-1
bobby:~% _

Другой пример ("к чему там относится afterstep?"):

bobby:~% rpm -qa | grep -i after
AfterStep-1.5-0.7
AfterStep-APPS-1.5-0.3
bobby:~% _

Где же был этот файл...


Аналогично иногда возникает необходимость найти некий файл, имя которого помнишь весьма приблизительно, а уж в какой он лежит директории...

Вместо того, чтобы делать поиск по всему диску (что очень долго), можно заставить rpm выдать список файлов всех пакетов (ключ "-al") и отфильтровать нужное при помощи grep. Пример ("где там был файл с параметрами разных мониторов?"):

bobby:~% rpm -qal | grep -i monitors
/usr/X11R6/share/Xconfigurator/MonitorsDB
/usr/X11R6/lib/X11/doc/Monitors
bobby:~% _

Искомый файл в данном примере -- второй.

Информация о неинсталлированном пакете


Перед установкой нового пакета обычно имеет смысл посмотреть информацию о нем и/или список содержащихся в нем файлов.

Получить информацию о содержимом .rpm-файла можно, если вместо имени пакета указать ключ "-p" (package) и полное имя .rpm-файла, содержащего пакет. Пример:

bobby:~% rpm -qip /net/rdist/dist/contrib/libc6/i386/x11amp-0.65r2-1.i386.rpm
Name        : x11amp                      Distribution: (none)
Version     : 0.65r2                            Vendor: (none)
Release     : 1                             Build Date: Fri Mar 13 16:09:25 1998
Install date: (not installed)               Build Host: fallenAngel.intrak.tuke.sk
Group       : X11/Applications              Source RPM: x11amp-0.65r2-1.src.rpm
Size        : 349571                           License: Not GPL, please read http:
//www.x11amp.ml.org/source.html
Summary     : mp3 player for X windows
Description :
This player will have the same features as the original winamp from Windows
95 but it will of course feature some specials only available for the linux
version. Some features that are already included are: Nice file requester,
Seeking in files, Volume/Balance, Shuffle play, Repeat play, Playlist
editor, Spectrum Analyzer, Realtime Playing.
bobby:~% _

Чего требует пакет -- rpm -qR


Ключ "-R" (Requirements) позволяет узнать, какие пакеты и библиотеки требуются пакету. Особенно часто это требуется перед установкой пакета. Пример:

bobby:~% rpm -qRp /net/rdist/dist/contrib/libc6/i386/x11amp-0.9-alpha2-2.i386.rpm
gtk+ >= 1.1.13
ld-linux.so.2  
libX11.so.6  
libXext.so.6  
libc.so.6  
libdl.so.2  
libgdk-1.1.so.13  
libglib-1.1.so.13  
libgmodule-1.1.so.13  
libgtk-1.1.so.13  
libm.so.6  
libpthread.so.0  
libx11amp.so.0  
bobby:~% rpm -q gtk+
gtk+-1.0.6-3
bobby:~% _

В вышеприведенном примере видно, что данный пакет установить не удастся, как минимум потому, что установленная версия пакета gtk+ слишком старая.

Проверка

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

При нахождении различий печатается ключевая строка, с обозначением отличий и имя файла, в котором они найдены.

Сравниваются следующие параметры:

5
Контрольная сумма (подсчитанная по алгоритму MD5)
S
Размер файла
L
Символьный линк (мне это не разу не встречалось, так что что это...)
T
Время модификации
D
Устройство (раздел), на котором расположен файл
U
Владелец
G
Группа-владелец
M
Права доступа

Проверку лучше выполнять как "root", так как некоторые файлы (например, /usr/X11R6/bin/xterm) могут быть недоступны на чтение другим пользователям и для них всегда будет выдаваться несовпадение по контрольной сумме.

Пример:

bobby:~# rpm -y setup
S.5....T c /etc/exports
S.5....T c /etc/printcap
S.5....T c /etc/securetty
S.5....T c /etc/services
bobby:~# _

Как видно из этого примера, в некоторых файлах обязательно будут отличия, поскольку тот же /etc/passwd изменяется при создании и изменении пользователей.

Аналогично команде rpm -q, rpm -y можно вместо имени пакета указывать "-f файл" или "-a".

Команда rpm -ya полезна для проверки всей системы, но ее исполнение занимает много времени.

Замечание
Иногда в документации вместо "-y" указывается "-V" (Verify) -- эти два ключа эквивалентны.

Где еще брать информацию про rpm

Основные сведения содержатся в man-странице по rpm. Кроме того, с системой поставляется "HOWTO" документация --

/usr/doc/HOWTO/other-formats/html/RPM-HOWTO.html

Самое лучшее справочное пособие по rpm -- книга Ed Bailey "Maximum RPM". Она есть в электронном виде на сайте rpm:

http://www.rpm.org/


Установка программ из бинарного дистрибутива

Предварительные действия

Первым делом надо развернуть архив. Это следует делать, находясь в какой-нибудь специальной директории (например, ~/soft/), поскольку файлы в архиве могут быть заархивированы без директорий.

В дистрибутивах всегда в верхней директории есть файлы с именами типа README, INSTALL или подобные (чаще всего -- README). Они содержат описание программы и инструкции по установке.

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

Рассмотрим оба варианта на примере программ X11Amp (версия 0.7) и Adobe Acrobat Reader (версия 4.0).

Установка "руками"

X11Amp -- это программа-проигрыватель файлов .mp3, очень похожая на широко известный WinAmp. Ее домашняя страница --

http://www.x11amp.bz.nu/

мы же воспользуемся локальной копией версии 0.7.

Сначала развернем архив:

bobby:~/soft# tar xvzf ~/x11amp-0.7-glibc.tgz
x11amp0-7/
x11amp0-7/README
x11amp0-7/x11amp
bobby:~/soft# _

Это оказался самый простой случай -- один бинарный файл и файл README.

Прочитав README, узнаем, что для минимальной установки достаточно просто скопировать исполняемый файл в какую-нибудь директорию с программами (ссылка на которую есть в переменной окружения PATH). Стандартным местом для программ от третьих поставщиков является директория /usr/local/bin/.

Копируем и устанавливаем права доступа:

bobby:~/soft% cp x11amp0-7/x11amp /usr/local/bin/
bobby:~/soft% chown root:root /usr/local/bin/x11amp
bobby:~/soft% chmod 755 /usr/local/bin/x11amp
bobby:~/soft% _

Вот и все, программой можно пользоваться -- достаточно набрать "x11amp" и появится окно программы.

Окно программы X11Amp

Впрочем, X11Amp -- не такая простая программа, в README содержатся еще и инструкции как сделать лучше внешний вид, чтобы window manager не декорировал это окно (в случае AnotherLevel все и так работает нормально), а также о том, как заставить X11Amp потреблять меньше процессорного времени и играть качественнее.

Установка при помощи инсталлятора

Adobe Acrobat Reader -- это программа для просмотра файлов .pdf, которая может работать как самостоятельно, так и в качестве plugin для Netscape. Версия 4.0 для Linux имеется на ftp-сервере фирмы Adobe в директории

ftp://ftp.adobe.com/pub/adobe/acrobatreader/unix/4.x/

Опять первым делом развернем архив:

bobby:~/soft% tar xvzf ~/linux-ar-40.tar.gz
ILINXR.install/
ILINXR.install/INSTALL
ILINXR.install/INSTGUID.TXT
ILINXR.install/LICREAD.TXT
ILINXR.install/ILINXR.TAR
ILINXR.install/READ.TAR
bobby:~/soft% ls -l ILINXR.install/
total 16297
-rw-r-----   1 ivanov   lab5     13755392 Feb 26 05:17 ILINXR.TAR
-rwxr-x---   1 ivanov   lab5        39573 Feb 26 05:17 INSTALL
-rw-r-----   1 ivanov   lab5        20106 Feb 26 05:17 INSTGUID.TXT
-rw-r-----   1 ivanov   lab5         6076 Feb 26 05:17 LICREAD.TXT
-rw-r-----   1 ivanov   lab5      2796544 Feb 26 05:17 READ.TAR
bobby:~/soft% _

Итак, мы видим исполняемый файл INSTALL, пару больших .tar-файлов (содержащих собственно файлы программы) и два .txt-файла. Из названий ясно, что инструкции по установке -- в INSTGUID.TXT.

Внимательно прочитав его, мы узнаем, что надо перейти в директорию с дистрибутивом и запустить скрипт INSTALL. Итак,

bobby:~/soft% su
Password:
bobby:~/soft# cd ILINXR.install/
bobby:~/soft# ./INSTALL

После выдачи на экран лицензионного соглашения программа просит набрать "accept", если мы согласны с его положениями.

Затем она укажет требуемое свободное место на диске и спросит, в какую директорию надо ставить программу. По умолчанию предлагается /usr/local/Acrobat4, на что мы и согласимся, нажав <Enter>. После вопроса, надо ли создать эту директорию (поскольку она не существует) программа проведет собственно инсталляцию.

Please type "accept" to accept the terms and conditions license agreement;
Type "decline" to exit. accept
 
This installation requires 16MB of free disk space.
 
Enter installation directory for Acrobat 4.0 [/usr/local/Acrobat4] <Enter>
 
Directory "/usr/local/Acrobat4" does not exist.
Do you want to create it now? [y] y
 
Installing platform independent files ... Done
 
Installing platform dependent files ... Done
 
bobby:~/soft/ILINXR.install# _

Как и следовало ожидать, в директории /usr/local/Acrobat4 появились файлы программы:

bobby:~/soft/ILINXR.install# ls -l /usr/local/Acrobat4/
total 4
drwxr-xr-x   3 1436     users        1024 May  8 22:14 Browsers
drwxr-xr-x   5 root     root         1024 May  8 22:13 Reader
drwxr-xr-x   3 1436     users        1024 May  8 22:13 Resource
drwxr-xr-x   2 1436     users        1024 May  8 22:14 bin
bobby:~/soft/ILINXR.install# _

Но вот незадача -- их владельцем является несуществующий пользователь с uid=1436 и группа users. Но владельцем всех программ (за исключением так называемых "сюидных" (setuid, suid)) должны быть пользователь "root" и группа "root". Для исправления ситуации достаточно команды

chown -R root:root /usr/local/Acrobat4/

Далее, программа acroread в директории Acrobat4/bin/ имеет права доступа "-rwxr-x--x", что не позволяет запускать ее обычным пользователям (поскольку это скрипт, то кроме "x" требуется еще и "r"). Исправляем:

chmod a+r /usr/local/Acrobat4/bin/acroread

Теперь запустив как обычный пользователь файл /usr/local/Acrobat4/bin/acroread, после некоторой паузы видим окно программы.

Окно программы Acroread

Последнее что осталось сделать -- настроить запуск программы по короткой команде, без /usr/local/... Для этого достаточно сделать в /usr/local/bin/ символьный линк, указывающий на исполняемый файл:

ln -s /usr/local/Acrobat4/bin/acroread /usr/local/bin/acroread

Заключение

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

Многие программы кроме собственно исполняемого файла (а чаще файлов) содержат еще некоторое количество файлов конфигурации и библиотек, которые следует поместить в строго определенные места (обычно внутри директории lib/) и man-страниц.

Кроме того, часто после установки требуется выполнить "руками" некоторые дополнительные действия -- например, добавить в стартовые файлы shell установку некоторых переменных окружения, модифицировать конфигурацию window manager'а (добавить программу в меню или настроить ее автоматический запуск).

Именно поэтому желательно перед установкой программы подробно изучить ее документацию, особенно файлы README, INSTALL или их аналоги. Там почти наверняка будут отражены эти особенности.


Установка программ из исходных текстов

Процесс компиляции и сборки программ в Unix

Создание исполняемого файла из одиночного файла .c выглядит довольно просто. Сначала .c-файл компилируется в объектный код (файл .o -- "object"), который затем при помощи сборщика (loader; его еще называют линкером -- linker) с добавлением системных библиотек превращается собственно в исполняемый файл.

Создание программы из одного исходного файла

Большинство же программ состоят из нескольких исходных файлов (крупные программы содержат по несколько сотен и даже тысяч файлов). Эти файлы также компилируются в объектный код, а потом сборщик делает из них исполняемый файл.

Создание крупной программы

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

Компиляторы языка Си в Unix обычно называются cc (C Compiler), а зачастую (особенно в Linux) используется т.н. GNU C Compiler -- gcc. Компиляторы Си++ аналогично называются c++ и g++. Сборщики именуются ld, хотя можно для этих целей пользоваться и gcc. Библиотекарь-архиватор -- программа ar.

Замечание
Практически все современные клоны Unix поддерживают также разделяемые библиотеки (именуемые .so (shared object) или .sa (shared archive)), которые подключаются к программе непосредственно перед исполнением (аналогично .dll в Windows). Собственно, большинство библиотек являются именно разделяемыми. Здесь мы разделяемые библиотеки не затрагиваем во-первых для краткости, а во-вторых потому, что обычно при компиляции никакого внешнего отличия между .a и .so нет.

Утилита make

Для чего нужен make


Приведенная в предыдущем разделе картина способна повергнуть в уныние -- неужели все эти действия надо выполнять "вручную"?

Естественно, нет -- для этого существует утилита make, которая все и делает ("make" -- дословно "делать").

Утилита make считывает из специального файла с именем Makefile или makefile в текущей директории инструкции о том, как (при помощи каких команд) компилировать и собирать программы, а также информацию, из каких файлов состоит программа, которую надо "сделать".

Одним из главных достоинств make (чрезвычайно полезным при создании больших программ) является то, что он сравнивает времена модификации файлов, и если, к примеру, файл file1.c новее, чем получаемый из него file1.o, то make поймет, что перекомпилировать надо только его, а остальные -- не нужно (если они не изменились).

Формат Makefile


Makefile содержат три основных компонента:

  • Правила, как из одних файлов создавать другие (например, .o из .c).
  • Так называемые "зависимости", которые указывают, что, например, исполняемый файл proggie собирается из файлов prg_main.o и prg_funcs.o, а те, в свою очередь, получаются из файлов prg_main.c и prg_funcs.c.
  • Определения переменных, позволяющие делать Makefile более гибкими.

Ниже приведен пример простейшего Makefile (он и используемые файлы .c доступны здесь):

CC=             gcc
CFLAGS=         -c -W -Wall

.c.o:
                $(CC) $(CFLAGS) -o $@ $< 

all:            proggie

proggie:        prg_main.o prg_funcs.o
                $(CC) -o proggie prg_main.o prg_funcs.o

Определения переменных. Строки вида "ИМЯ=значение" -- это определения переменных. Для получения значения переменной используется запись "$(ИМЯ)" (знак доллара, а за ним имя переменной в скобках). Переменная может определяться через значения других переменных, например:

CFLAGS= -c $(WARNINGOPTIONS)

Правила. Запись ".c.o:" с последующей командой означает: "для любого файла .c, чтобы из него получить одноименный файл .o, надо выполнить такую-то команду"; в данном случае --

gcc -c -W -Wall -o $@ $<

В правилах всегда используются специальные переменные "$@" и "$<". Переменная "$@" обозначает "тот файл, который надо получить" (в данном случае .o), а "$<" -- "исходный файл" (в данном случае .c). Такие переменные называются автоматическими.

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

Это довольно странное правило является основным источником ошибок и запутанности Makefile'ов -- ведь визуально отличить символ табуляции от цепочки пробелов невозможно. Поэтому, к примеру, редактор Midnight Commander автоматически цветом выделяет строки, начинающиеся с символа табуляции.

Makefile в редакторе MC

Зависимости. Запись вида

proggie: prg_main.o prg_funcs.o
означает, что файл proggie зависит от файлов prg_main.o и prg_funcs.o. Файл proggie называется целью (target), а файлы .o -- зависимостями (dependencies).

Несмотря на то, что зависимости по внешнему виду похожи на правила, make их различает.

В принципе в данном Makefile не помешали бы и строки

prog_main.o:    prog_main.c
prog_funcs.o:   prog_funcs.c
но у make хватит интеллекта догадаться об этом самому (исходя из правила ".c.o").

Более длинные правила и зависимости.

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

Аналогично после зависимости на следующих строках можно указать команды, при помощи которых ее надо "делать". Например,

prog_main.o:    prog_main.c
                $(CC) $(CFLAGS) -D_GNU_SOURCE -o prog_main.o prog_main.c

При этом команды, определяемые в правиле ".c.o" использоваться не будут.

Замечание
В Linux используется GNU-версия программы make (GNU Make), имеющий более богатый синтаксис (он включает условные конструкции, директиву include для "вставки" других файлов, более гибкий формат определения правил (вида %.c: %.o)). Поэтому многие программы пользуются Makefile'ами именно под GNU Make. В других ОС для вызова GNU Make обычно служит команда "gmake".

Запуск make


При запуске без параметров make пытается сделать самую первую цель из перечисленных в Makefile. Обычно в качестве первой ставят дополнительную цель "all", зависящую от всех файлов, которые надо сделать для изготовления программы.

Если же указать в командной строке имя цели, то make выполнит команды, необходимые для этой цели (и, при надобности, команды для изготовления промежуточных целей).

Например, команда

make prog_main.o
выполнит только команду компиляции файла prog_main.c в prog_main.o, да и то только если файл .c новее чем .o.

Если запустить make с ключом "-n", то он лишь напечатает на экране команды, которые следует выполнить, не не станет реально их запускать. (Мнемоника для запоминания: "-n" -- "do Nothing" -- "Nичего не делай".)

Ключ "-f" позволяет заставить make читать инструкции из указанного файла, вместо Makefile или makefile, используемых по умолчанию. Пример:

make -f Makefile.irix

Если при компиляции или сборке возникает ошибка, то make прекращает процесс, не выполняя последующие команды.


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

Кроме того, очень подробная документация (включая введение для начинающих) есть в info-документации на GNU Make, вызываемой командой "info make", -- там содержится буквально "все, что вы хотели знать о Make, но боялись спросить".

Конфигурация, компиляция и установка программ из исходных текстов

При разработке программ под Unix авторы обычно стараются сделать так, чтобы их продукт можно было использовать с любым клоном Unix.

Но, поскольку разные клоны довольно сильно отличаются (особенно BSD-системы от SystemV), то написать код, который компилировался и работал бы без изменений на большом количестве систем, практически невозможно. Причем, чем больше программа, тем эта задача сложнее.

Поэтому перед компиляцией надо сначала произвести настройку.

Для этого обычно используется один из трех способов:

  1. Если к дистрибутиву прилагается скрипт configure, то надо его запустить (командой "./configure").
  2. Возможно, в Makefile имеется специальная цель "config" -- в таком случае для конфигурирования служит команда "make config".
  3. В Makefile могут быть разные цели для разных ОС, в таком случае для компиляции под Irix надо будет дать команду типа "make irix", а под Linux -- "make linux".

Первые два способа создают файлы настроек, специфичные для данной ОС (обычно это или include-файлы (.h), или же сразу генерируется нужный Makefile). В третьем случае сразу запускается компиляция с параметрами, специфичными для данной системы.

Какой из способов используется в конкретной программе -- надо смотреть в прилагаемой документации (т.е. в файлах типа README и INSTALL).

Непосредственно для инсталляции же практически всегда используется специальная цель "install" в Makefile. Т.е. для того, чтобы после компиляции и сборки установить программу, надо дать команду

make install

Для примера рассмотрим компиляцию двух программ -- Wget, использующей первый способ, и NetCat, использующей третий.

Замечание
В принципе, обе программы (вследствие своей популярности) существуют и в виде .rpm-пакетов, но они являют собой очень простые и "чистые" примеры обоих вариантов конфигурирования, поэтому мы ими и воспользуемся.

Компиляция и установка программы Wget


Wget -- это утилита командной строки, служащая для скачивания файлов с WWW- и FTP-серверов. Она имеет множество достоинств, в частности, рекуррентную перекачку и автоматическую докачку после обрыва соединения. (Более подробно Wget описан в разделе "Программа wget").

Возьмем локальную копию дистрибутива.

Развернув архив, мы увидим в нем файл INSTALL и скрипт configure, а вот Makefile там нет!

bobby:~/soft% tar xfz ~/wget-1.5.3.tar.gz
bobby:~/soft% cd wget-1.5.3/
bobby:~/soft/wget-1.5.3% ls
AUTHORS         MAILING-LIST    aclocal.m4      configure.in    src/
COPYING         Makefile.in     config.guess*   doc/            stamp-h.in
ChangeLog       NEWS            config.sub*     install-sh*     util/
INSTALL         README          configure*      mkinstalldirs*  windows/
MACHINES        TODO            configure.bat   po/
bobby:~/soft/wget-1.5.3% _

Как и следовало ожидать, в INSTALL рекомендуют запустить configure, а затем набрать "make".

В ответ на команду "./configure" компьютер сообщает, что конфигурирует программу GNU Wget 1.5.3, а затем около минуты печатает список свойств системы, которые он проверяет. В конце он уведомляет, что создает несколько Makefile (в разных поддиректориях).

Набрав теперь "make", запускаем процесс компиляции и сборки, который занимает некоторое время.

По его окончании можно набрать "make install". Вот и все!

Компиляция программы NetCat


NetCat -- это, вообще говоря, хакерская программа для "отладки" сети, позволяющая передавать данные в сеть по протоколам TCP и UDP и принимать их из сети. Как обычно, воспользуемся локальной копией дистрибутива версии 1.10.

Первым делом развернем архив:

bobby:~/soft% mkdir netcat
bobby:~/soft% cd netcat
bobby:~/soft/netcat% tar xfz ~/nc110.tgz
bobby:~/soft/netcat% ls
Changelog     README        generic.h     netcat.c      stupidh*
Makefile      data/         netcat.blurb  scripts/
bobby:~/soft/netcat% _

Из файла README узнаем, что для компиляции надо указать команде make тип ОС. То же увидим, и запустив make без параметров:

bobby:~/soft/netcat% make
Usage:  make  <systype>  [options]
bobby:~/soft/netcat% _

Беглый просмотр Makefile показывает, что в нем есть цель под названием "linux". Итак,

bobby:~/soft/netcat% make linux
make -e nc  XFLAGS='-DLINUX' STATIC=-static
make[1]: Entering directory `/export/bobby/ivanov/soft/netcat'
cc -O -s          -DLINUX -static -o nc netcat.c 
make[1]: Leaving directory `/export/bobby/ivanov/soft/netcat'
bobby:~/soft/netcat% ls
Changelog     README        generic.h     netcat.blurb  scripts/
Makefile      data/         nc*           netcat.c      stupidh*
bobby:~/soft/netcat% ls -l nc
-rwxr-xr-x   1 ivanov   lab5       142668 May 10 18:21 nc*
bobby:~/soft/netcat% _

Как мы видим, после компиляции появился исполняемый файл nc. Поскольку цель "install" в Makefile отсутствует, то для установки надо просто скопировать этот файл в общесистемную директорию:

bobby:~/soft/netcat% su
Password:
bobby:~/soft/netcat# cp nc /usr/local/bin/
bobby:~/soft/netcat# _

Особенности компиляции программ под X-Window

Imakefile и программа xmkmf


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

Идея заключалась в том, чтобы перед компиляцией Makefile автоматически генерировался специальной утилитой xmkmf, "знающей" про специфику конкретной системы, из другого файла, под названием Imakefile. В Imakefile же на некоем специальном языке записывается примерно та же информация, что в Makefile.

Замечание
Аббревиатура "xmkmf" расшифровывается очень просто -- "MaKe MakeFile" -- "сделай Makefile", а "x" -- префикс, обозначающий принадлежность программы к X-Window.

Хотя замысел был очень хороший, реализация оставляет желать лучшего. Во-первых, язык Imakefile'ов -- не менее "птичий", чем у Makefile, так что людей, умеющих их создавать, еще меньше. Во-вторых, xmkmf берет описание системы из нескольких файлов конфигурации (в глубине директории /usr/X11R6/), а они зачастую оказываются несовместимы с конкретным Imakefile, и xmkmf просто завершается с каким-нибудь маловразумительным сообщением об ошибке.

Тем не менее, большинство программ под X-Window поставляются именно с Imakefile.

Пример сборки и установки программы под X-Window


В качестве примера рассмотрим сборку и установку программы XRoach (той самой, что пускает бегать по экрану тараканов). Воспользуемся локальной копией дистрибутива.

Сначала развернем дистрибутив:

bobby:~/soft% tar xfz ~/xroach.tar.gz
bobby:~/soft% cd xroach/
viper:~/soft/xroach% ls
Imakefile     roach060.xbm  roach165.xbm  roach270.xbm  squish.xbm
README.linux  roach075.xbm  roach180.xbm  roach285.xbm  xroach.a
patchlevel.h  roach090.xbm  roach195.xbm  roach300.xbm  xroach.c
roach000.xbm  roach105.xbm  roach210.xbm  roach315.xbm  xroach.man
roach015.xbm  roach120.xbm  roach225.xbm  roach330.xbm
roach030.xbm  roach135.xbm  roach240.xbm  roach345.xbm
roach045.xbm  roach150.xbm  roach255.xbm  roachmap.h
bobby:~/soft/xroach% _

Прочтя файл README.linux, мы узнаем лишь, что при компиляции должно быть предупреждение (warning) в строке 373.

Запускаем xmkmf и затем make:

bobby:~/soft/xroach% xmkmf
imake -DUseInstalled -I/usr/X11R6/lib/X11/config
bobby:~/soft/xroach% make
gcc -O2 -fno-strength-reduce     -I/usr/X11R6/include   -Dlinux -D__i3
86__ -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -D_XOPEN_SOURCE=500L -D
_BSD_SOURCE -D_SVID_SOURCE   -DFUNCPROTO=15 -DNARROWPROTO     -c xroac
h.c -o xroach.o
xroach.c: In function `FindRootWindow':
xroach.c:373: warning: passing arg 9 of `XGetWindowProperty' from inco
mpatible pointer type
xroach.c:373: warning: passing arg 12 of `XGetWindowProperty' from in
compatible pointer type
rm -f xroach
gcc -o xroach -O2 -fno-strength-reduce      -L/usr/X11R6/lib xroach.o
 -lXext -lX11   -lm   
bobby:~/soft/xroach% _

Теперь, аналогично обычным программам, делаем "make install":

bobby:~/soft/xroach% su
Password:
bobby:~/soft/xroach# make install
install -c -s  xroach /usr/X11R6/bin/xroach
install in . done
bobby:~/soft/xroach# _

Единственно что, автор поленился сделать автоматическую установку man-страницы, хотя она и есть. Что ж, не беда -- скопируем ее в нужное место "руками":

bobby:~/soft/xroach# cp xroach.man /usr/X11R6/man/man1/
bobby:~/soft/xroach# _

(Поскольку "make install" установил программу в /usr/X11R6/bin/, то и man-страницу надо положить "рядом" -- внутри /usr/X11R6/. А поскольку xroach -- пользовательская программа, то ее man-страница должна лежать в разделе 1 (поддиректория man1/.)


Практические задания
  1. Просмотреть информацию и список файлов пакета AnotherLevel. Найти в списке файлов, какие к этому пакету прилагаются man-страницы.
  2. Выдать на экран список всех пакетов, в имени которых есть "ftp".
  3. Посмотреть, от каких пакетов зависит пакет AnotherLevel.
  4. Выполнить проверку пакета xterm-color, сначала как обычный пользователь, а затем как "root". Объяснить полученные результаты.
  5. Установить из бинарного дистрибутива программу NEdit (не забыв про man!), дистрибутив лежит по адресу

    /net/class/home/teachers/bolkhov/11/nedit_linux.tar.gz
  6. Собрать и установить программу lftp, дистрибутив лежит по адресу

    /net/class/home/teachers/bolkhov/11/lftp-1.2.4.tar.gz
  7. Собрать и установить программу xtetris, дистрибутив лежит по адресу

    /net/class/home/teachers/bolkhov/11/xtetris-2.6.tar.Z
    Для этого надо предварительно установить пакет XFree86-devel. Его .-rpm-файл расположен в директории

    /net/class/mnt/rdist/redhat-5.2/i386/RedHat/RPMS/

----------------------------------------

© 1999 Дмитрий Болховитянов