Создание программного обеспечения для нескольких UNIX-платформ

Мартин Браун

Перед началом работы

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

О данном руководстве

Разработка программного обеспечения, которое компилируется и компонуется на нескольких платформах, может быть сложной задачей. Существуют незначительные и существенные различия между разными вариантами UNIX, которые могут быть причинами проблем: от отсутствия программ и библиотек, до различий в заголовочных файлах, используемых для компоновки вашего кода. Учет этих различий делает ваш код более переносимым, что и является предметом рассмотрения данного руководства. Вы также рассмотрите использование программы GNU autotools, которая может сделать процесс миграции и разработки менее болезненным и сложным.

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

Вы должны иметь доступ к компилятору C для работы с некоторыми примерами. Для работы с программой autotools необходим доступ к пакету Autoconf/autotools (см. Web-сайт GNU).

Несовместимость UNIX

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

Предпосылки

Существует несколько различных вариантов UNIX, в том числе и свободно распространяемых, например, разные BSD-варианты (Berkeley Systems Division) - FreeBSD, OpenBSD, NetBSD и Linux. Различия проистекают первоначально из исходного UNIX-дистрибутива, используемого конкретным вариантом UNIX. Существует две версии: AT&T (развившейся, в конечном счете, в систему, известную под названием SVR4) и версия, разработанная в University of California Berkeley (UCB) и известная под названием BSD-версия.

Различные компании основали свои операционные системы UNIX на одной из этих базовых редакций и добавили впоследствии собственные расширения и улучшения. Некоторые компании, в частности Sun, изменили исходный вариант, лежащий в основе их UNIX-системы. Например, компания Sun заменила BSD-ядро в SunOS на SVR4-ядро в Solaris.

System V Release 4 в настоящее время является наиболее распространенной в коммерческих дистрибутивах и содержит ядро, основанное на оригинальном ядре AT&T с некоторыми дополнительными BSD-элементами.

В результате, несмотря на то, что различные версии UNIX (AIX, HP-UX, Solaris и другие) технически представляют собой операционную систему UNIX, различия между системами, а именно, различия в библиотеках, заголовочных файлах и даже инструментах для их компоновки, приводят к тому, что исходный код на С нельзя просто переносить с одной платформы на другую.

Реальные функции неизменны; просто отличается месторасположение или определение функций.

POSIX-совместимость

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

Для UNIX первичным POSIX-стандартом является 1003.1, который определяет интерфейс между приложением и операционной системой.

POSIX-стандарты приняты в операционных системах многих компаний, в том числе Sun Microsystems, IBM, Digital и Hewlett Packard. Даже Microsoft предоставляет уровень POSIX-совместимости в Microsoft Windows.

Стандарт POSIX 1003.1 (известный также как POSIX.1) определяет названия функций, используемых для выполнения конкретных операций в OS, включая аргументы, их формат и порядок вызова. Стандарт также указывает ожидаемый результат и все ошибки (и содержит стандарты для номеров ошибок).

В двух словах, любой вызов функции, которая является POSIX-совместимой, должен работать на различных операционных системах без каких-либо модификаций. Например, функция chdir(), определенная в POSIX-стандарте как int chdir(const char *path); должна быть доступна во всех версиях UNIX, являющихся POSIX-совместимыми, и значение аргумента (строка), возвращаемое значение (целое) и сообщения об ошибках в различных системах должны быть одинаковыми.

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

Оценка текущей задачи

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

Элементами, которые вы должны исследовать, являются:

  • C-код ядра - Большая часть C-кода является переносимой. Основная несовместимость исходного кода на С заключается в библиотеках и заголовках, от которых различные элементы зависят.
  • Необходимые библиотеки - Различные версии UNIX содержат широкий диапазон различных библиотек и инструментальных средств, и некоторые могут поставляться даже со библиотеками сторонних производителей в качестве стандартных.
  • Среда компоновки - Makefile является стандартной средой компоновки для большинства приложений.
  • Необходимые инструментальные программы - Для некоторых приложений могут понадобиться специфические программы, например, yacc, lex, rpcgen и другие, которые должны быть доступны на всех платформах, на которых вы собираетесь выполнять компоновку приложения.

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

Общие проблемы

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

Программы компоновки и среда

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

Например, должны быть доступны компилятор C, препроцессор C и такие программы, как yacc, lex и другие. Вы также должны учитывать параметры командной строки и процессы для различных инструментальных средств.

Например, на большинстве версий UNIX доступен компилятор C - cc, а также отдельный препроцессор C - cpp. Однако для некоторых предпочтительно запускать препроцессор с использованием параметра командной строки -E для cc.

Вы можете упростить среду и процесс, используя свободно распространяемые программы, такие как gcc, bison и flex, но опять же, учитывайте ограничения и различия.

Стандартная UNIX-программа yacc, например, создает файлы y.tab.c и y.tab.h (по запросу), а bison создает файлы, основанные на именах исходных файлов (например, генерирует parser.tab.c из parser.y). Вы должны учитывать эти различия.

Кроме того, учитывайте, что различные программы расположены в разных местах на разных платформах. Например, Solaris содержит make как стандартную программу (но не компилятор C), но она расположена в каталоге /usr/ccs/bin/make, а не в каталоге по умолчанию, как на многих других платформах.

Самым простым способом учета этих различий на уровне Makefile (без использования программы GNU autotools) является создание отдельного Makefile для каждой среды, изменяя каждый Makefile соответственно платформе. Затем вы можете использовать соответствующий Makefile в make:

$ make -f Makefile.solaris
 

Вам все равно придется учитывать различия между файлами, но процесс должен быть значительно легче.

Использование препроцессора C

Файлы заголовков и большинство архитектурных и функциональных различий обычно могут быть учтены в C при помощи использования директив препроцессора C для выбора различных участков кода.

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

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

#define SOLARISX86
 

Вы можете указать это определение либо в заголовочном файле, либо включить определение в командную строку для компилятора:

$ cc -DSOLARISX86
 

Внутри исходного кода вы могли бы тогда указать корректный фрагмент для использования, проверяя это определение. Например, в листинге 1 , выбирается вариант для вашего определения.

Листинг 1. Выбор исключения

#ifdef SOLARISX86
/* do solaris specific code */
#else
/* do other Unix code */
#endif	

Вы можете также использовать #ifndef для проверки того, что директива не была определена, и #elif для проверки дополнительных директив в секции if..else..endif.

Вы можете использовать аналогичную систему по всему исходному коду. Например, вы можете применить ее для включения различных заголовочных файлов (листинг 2) и учесть различия, с которыми столкнулись ранее для функции localtime().

Листинг 2. Выбор файла заголовка

#ifdef SOLARISX86
#include <iso/time_iso.h>
#else
#include <time.h>
#endif		

Эти же принципы вы должны использовать по всему вашему коду, предполагая, что вы знаете, что ищете.

Различия в заголовках

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

Некоторые заголовочные файлы размещаются в различных местах. Например, содержимое файла limits.h более или менее стандартно в разных UNIX-платформах, но он располагается в разных каталогах. В AIX, например, вы импортируете файл так:

#include <sys/limits.h>
 

Но в Solaris:

#include <limits.h>
 

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

В указанных ситуациях вы должны найти месторасположение определения функции, структуры или переменной, которую вы ищете. Лучшим способом поиска месторасположения является использование программы grep, как показано в следующем коде, в котором ищется определение PIPE_MAX на системе Solaris.

$ find /usr/include -exec grep -il PIPE_MAX {} \;
/usr/include/sys/param.h
/usr/include/limits.h

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

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

Различия в библиотеках

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

Однако в Solaris вы должны явно добавить эти библиотеки, для того чтобы они могли быть связаны с вашим приложением:

$ cc -o server server.c -lnsl -lsocket -lresolv
 

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

Например, если вы не включили библиотеку math, то можете получить ошибки, аналогичные показанным в листинге 3.

Листинг 3. Ошибки отсутствующих символов

calcparse.tab.o: In function `yyparse':
calcparse.tab.c:(.text+0x53a): undefined reference to 'pow'
calcparse.tab.c:(.text+0x573): undefined reference to 'fmod'
calcparse.tab.c:(.text+0x5bb): undefined reference to 'log'
calcparse.tab.c:(.text+0x818): undefined reference to 'cos'
calcparse.tab.c:(.text+0x829): undefined reference to 'tan'
calcparse.tab.c:(.text+0x83c): undefined reference to 'cos'
calcparse.tab.c:(.text+0x849): undefined reference to 'tan'
calcparse.tab.c:(.text+0x856): undefined reference to 'asin'
calcparse.tab.c:(.text+0x863): undefined reference to 'acos'
calcparse.tab.c:(.text+0x870): undefined reference to 'atan'
collect2: ld returned 1 exit status	

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

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

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

Различия в порядке байт

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

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

CPU с обратным порядком байтов (big endian) (SPARC, PA-RISC и PowerPC) обращаются к информации по порядку, начиная с наиболее значимого байта. CPU с прямым порядком байтов (little endian) (intel) обращаются к информации по порядку, начиная с наименее значимого байта. Это приводит к полному реверсированию адреса, например, ABCD в DCBA.

Это не влияет на строки, поскольку они основаны на отдельных байтах, но влияет на многобайтовые значения, например на 32-битные адреса, где порядок байтов меняет значение, которое будет отличаться в разных типах CPU.

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

Процессы, сигналы и IPC

Существуют незначительные различия между реализациями fork() и потоков в разных версиях UNIX. Например, не все они поддерживают все функции fork*(). IRIX поддерживает функцию m_fork(), которая фактически больше похожа на одну из различных потоковых библиотек. Однако даже в области потоков различные реализации и стандарты не распространены широко.

Различные версии UNIX поддерживают различные сигналы. Многие из сигналов ядра остаются неизменными, например, SIGHUP равен 1, SIGKILL равен NINE; но не полагайтесь на доступность на всех платформах сигналов, относящихся к операционной системе, таких как SIGJVM1 (Solaris). Обычно только сигналы со значениями до 15 одинаковы для большинства платформ. После 15 (SIGTERM) значения сигналов полностью зависят от OS.

Также учитывайте, что методы Interprocess Communication (IPC) не стандартизированы. И хотя большинство версий UNIX поддерживает SVR4 IPC, знайте, что нет полного уровня поддержки. Вы должны обычно привязываться к более открытым IPC-стандартам, таким как именованные каналы (named pipe).

Использование программы GNU autotools

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

Обзор программы GNU autotools

Пакет GNU autotools представляет собой систему, формирующую набор скелетов (skeleton) правил конфигурации и файлов. Когда конфигурационный сценарий выполняется на новой системе, он проверяет операционную систему и требования к исходному коду и формирует подходящую конфигурацию заголовков и среды компоновки (на основе стандартного Makefile), которая компонует приложение на системе.

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

$ ./configure
$ make
$ make install	

Сценарий configure является частью дистрибутива исходного кода. Главным преимуществом системы autotools является то, что она может в сценарии определить конфигурацию и доступность компонентов в системе, на которой выполняется сценарий, сравнить с необходимыми элементами исходного кода и сформировать рабочую среду компоновки.

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

Для многих приложений вы не должны делать изменения в вашем коде вручную для того, чтобы сделать его переносимым. Также, вы даже не должны беспокоиться о том, работает ли на целевой системе сценарий configure. Я разрабатывал приложения, использующие систему autotools, которые успешно компилировались и компоновались без каких-либо изменений в коде на HP-UX, Solaris (SPARC and x86), Linux, Mac OS X и BSD.

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

Настройка структуры проекта

Первым действием при использовании autotools является создание подходящей структуры для вашего приложения. Для данного примера вы будете использовать простое приложение calculator (см. раздел «Загрузка»), которое базируется на компоненте лексического анализа (требующего наличия lex) и грамматическом компоненте (требующего наличия yacc ), а также на паре других файлов для поддержки определенных операций. Список файлов приведен в листинге 4 .

Листинг 4. Исходные файлы для приложения calculator

total 28
-rwxrwx--x 1 mc mcslp 458 Jan 25 17:19 Makefile*
-rwxrwx--x 1 mc mcslp 129 Apr 16 1997 calc.h*
-rwxrwx--x 1 mc mcslp 4216 Jun 11 1997 calcparse.y*
-rwxrwx--x 1 mc mcslp 136 Jun 8 1997 const.c*
-rwxrwx--x 1 mc mcslp 643 Apr 16 1997 fmath.c*
-rwxrwx--x 1 mc mcslp 1095 Jan 25 17:16 lex.l*	

Записанный вручную Makefile для компоновки приложения calculator приведен в листинге 5.

Листинг 5. Записанный вручную Makefile

YFLAGS  = -d

PROGRAM = calc

OBJS    = calcparse.tab.o lex.yy.o fmath.o const.o

SRCS    = calcparse.tab.c lex.yy.c fmath.c const.c

CC      = gcc #C compiler

all:      $(PROGRAM)

.c.o:     $(SRCS)
          $(CC) -c $*.c -o $@ -O

calcparse.tab.c: calcparse.y
          bison $(YFLAGS) calcparse.y

lex.yy.c: lex.l
          flex lex.l

calc:     $(OBJS)
          $(CC) $(OBJS) -o $@ -lfl -lm

clean:;   rm -f $(OBJS) core *~ \#* *.o $(PROGRAM)           y.* lex.yy.* calcparse.tab.*	

Для настройки приложения на использование с программой autotools удалите Makefile, создайте новый каталог (назовем его calc) и подкаталог (src), в который скопируйте все исходные файлы. Схему расположения файлов (готовую для autotools) вы можете увидеть в листинге 6.

Листинг 6. Исходные файлы, готовые для компоновки с autotools

./calc
./calc/src
./calc/src/calc.h
./calc/src/calcparser.c
./calc/src/calcparser.h
./calc/src/calcparser.y
./calc/src/const.c
./calc/src/fmath.c
./calc/src/lex.c
./calc/src/lex.l

Теперь вы готовы начать настройку исходных файлов для autotools.

Создание основы Makefiles

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

Итак, для вашего примера файл Makefile.am в каталоге calc будет выглядеть следующим образом:

SUBDIRS=src
 

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

Файл Makefile.am в вашем главном каталоге src показан в листинге 7.

Листинг 7. Скелет src/Makefile.am для компоновки на целевой системе по умолчанию

calcprgdir=../
calcprg_PROGRAMS=calc
calc_SOURCES=lex.l calcparser.y fmath.c const.c
AM_YFLAGS=-d
calc_LDADD=-lfl -lm

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

Первая строка в листинге 7 указывает конечный каталог приложения для его установки программой make. При компоновке данного демонстрационного примера мы будем использовать родительский каталог в качестве целевого для установки.

Вторая строка указывает название генерируемого приложения. Третья перечисляет исходные файлы, требуемые для компоновки приложения. Обратите внимание на то, что хотя ваше приложение основано на С-файлах, автоматически сгенерированных различными программами (в частности lex или flex и yacc или bison), вам не нужно явно указывать эту стадию. Это является следствием того, что пакет autotools уже знает, как скомпоновать исходные файлы на языке С из этих исходных файлов, основываясь на их расширении.

Четвертая строка устанавливает общий параметр для добавления параметра командной строки -d при вызове синтаксического анализатора yacc. Кстати, применение yacc/bison - это еще один пример, где используется предварительно настроенная осведомленность системы autotools. Autotools знает, что на платформах UNIX может быть установлен yacc, и если это не так, значит установлена программа GNU bison. Эти две программы генерируют аналогичную информацию, но в различных файлах; для удобства в программе bison есть режим совместимости с yacc. Когда на системе выполняется сценарий configure, который генерирует система autotools для вашего кода, он будет определять, доступен yacc или bison, и автоматически формировать команду для данного этапа процесса.

Последняя строка листинга 4 указывает дополнительные библиотеки, требуемые для компоновки приложения. Обратите внимание на то, что вы указываете только названия библиотек так, как они указывались бы для cc; сценарий configure определяет месторасположение этих библиотек и необходимость добавления определенных каталогов в команду во время компоновки приложения.

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

Сканирование исходного кода

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

Вы не должны создавать этот файл самостоятельно; можно использовать функцию autoscan для автоматического генерирования скелета этого файла.

$ autoscan
autom4te-2.59: configure.ac: no such file or directory
autoscan-2.59: /usr/bin/autom4te-2.59 failed with exit status: 1

Не обращайте внимания на ошибку - она просто указывает на то, что не был найден файл configure.ac, на котором основывалась операция сканирования. При первом запуске этой инструментальной программы должен сформироваться файл под названием configure.scan. Содержимое этого файла приведено в листинге 8.

Листинг 8. Конфигурация autoscan по умолчанию

# 					-*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ(2.59)
AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
AC_CONFIG_SRCDIR([src/calc.h])
AC_CONFIG_HEADER([config.h])

# Checks for programs.
AC_PROG_CC

# Checks for libraries.
# FIXME: Replace `main' with a function in `-lfl':
AC_CHECK_LIB([l], [main])
# FIXME: Replace `main' with a function in `-lm':
AC_CHECK_LIB([m], [main])

# Checks for header files.
AC_FUNC_ALLOCA
AC_HEADER_STDC
AC_CHECK_HEADERS([stddef.h stdlib.h])

# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST

# Checks for library functions.
AC_CHECK_FUNCS([pow])

AC_CONFIG_FILES([src/Makefile])
AC_OUTPUT

Элементы FIXME удобно указывают на то, что именно вы должны исправить. Прежде всего, переименуйте файл с configure.scan на configure.ac.

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

Затем добавьте строку, конфигурирующую систему для automake:

AM_INIT_AUTOMAKE
 

Необходимо также обновить строки AC_CHECK_LIB для проверки конкретных функций в используемых вами библиотеках. Для данного приложения вам нужно просто проверить доступность библиотеки math ( -lm). Наконец, вы должны добавить две строки, включающие определение конфигурации для запуска lex и yacc, а также файлы, которые от них зависят.

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

Listing 9. Файл configure.ac

AC_INIT(calc, 1.0, mc at mcslp.com)
AC_CONFIG_SRCDIR([src/calc.h])
AM_INIT_AUTOMAKE
AC_CONFIG_HEADER([config.h])

# Checks for programs.
AC_PROG_CC
AM_PROG_LEX(src/lex.l)
AC_PROG_YACC(src/calc.y)

# Checks for libraries.
AC_CHECK_LIB([m], [sin])

# Checks for header files.
AC_FUNC_ALLOCA
AC_HEADER_STDC
AC_CHECK_HEADERS([stddef.h stdlib.h])

# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST

# Checks for library functions.
AC_CHECK_FUNCS([pow])

AC_CONFIG_FILES([src/Makefile])
AC_OUTPUT

Этот файл будет использоваться программой autoconf для определения функций, которые нужно найти для компоновки приложения.

Изменение исходного кода

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

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

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

Создание конфигурационного сценария

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

  • Скелет заголовочного файла (config.h.in), хранящего специфичную информацию по совместимости для вашего проекта.
  • Файл aclocal.m4 - макрос в формате m4, который компонует необходимые макросы для преобразования вашего конфигурационного файла.
  • Makefile.in - шаблон файла Makefile для каждого из ваших каталогов и подкаталогов.
  • Конфигурационный сценарий (configure), который учитывает все вышесказанное при определении среды компоновки и создает необходимые заголовочные файлы и файлы Makefile для компоновки вашего приложения.

Этот процесс выполняется в четыре стадии. Во-первых, сгенерируйте m4-макрос:

$ aclocal
 

Вы можете проигнорировать любые предупреждения, выдаваемые этой командой, но обратите внимание на все ошибки; они, возможно, укажут на опечатки в вашем файле configure.ac. Затем создайте шаблон заголовочного файла:

$ autoheader
 

Теперь вы готовы к формированию шаблона файлов Makefile. Но сначала вы должны создать несколько файлов, являющихся частью стандартной конфигурации autotools. Такими файлами являются:

  • COPYING - Правила для копирования вашего исходного кода - по умолчанию ссылаются на GPL.
  • AUTHORS - Список авторов.
  • ChangeLog - История изменений исходных файлов и версий.
  • INSTALL - Инструкции по установке приложения.
  • NEWS - Новости, относящиеся к приложению.
  • README - Простой файл README о приложении.

Пока вы можете просто создать пустые файлы:

$ touch INSTALL NEWS README AUTHORS ChangeLog COPYING
 

Теперь можно создать шаблоны Makefile:

$ automake -ac
 

Параметр -a указывает программе automake добавить отсутствующие файлы, а параметр -c - скопировать эти файлы в каталог.

Наконец, вы должны сгенерировать конфигурационный сценарий, который фактически выполнит конфигурирование на целевой системе:

$ autoconf
 

Полная последовательность действий (и пример отображения результатов работы) приведена в листинге 10.

Листинг 10. Последовательность компоновки конфигурации

$ aclocal
/usr/share/aclocal/pth.m4:43: warning: underquoted definition of _AC_PTH_ERROR
  run info '(automake)Extending aclocal'
  or see http://sources.redhat.com/automake/automake.html#Extending-aclocal
/usr/share/aclocal/pth.m4:55: warning: underquoted definition of _AC_PTH_VERBOSE
/usr/share/aclocal/pth.m4:61: warning: underquoted definition of AC_CHECK_PTH
/usr/share/aclocal/glib.m4:8: warning: underquoted definition of AM_PATH_GLIB
$ autoheader
$ automake -ac
$ autoconf

Если вы проверите ваш корневой каталог, то должны заметить основные компоненты, такие как сценарий configure, файлы config.h.in и Makefile.in (листинг 11 ).

Листинг 11. Пример структуры каталога

-rw-rw-rw- 1 root mcslp 0 Jan 25 15:45 AUTHORS
-rw-r--r-- 1 root mcslp 18002 Jan 25 15:45 COPYING
-rw-rw-rw- 1 root mcslp 0 Jan 25 15:45 ChangeLog
-rw-r--r-- 1 root mcslp 9498 Jan 25 15:45 INSTALL
-rw-rw-rw- 1 root mcslp 12 Jan 25 15:44 Makefile.am
-rw-rw-rw- 1 root mcslp 18101 Jan 25 16:06 Makefile.in
-rw-rw-rw- 1 root mcslp 0 Jan 25 15:45 NEWS
-rw-rw-rw- 1 root mcslp 0 Jan 25 15:45 README
-rw-rw-rw- 1 root mcslp 31731 Jan 25 15:52 aclocal.m4
drwxr-xr-x 7 root mcslp 238 Jan 25 15:52 autom4te.cache/
-rw-rw-rw- 1 root mcslp 2563 Jan 25 15:52 config.h.in
-rw-rw-rw- 1 root mcslp 2634 Jan 25 15:51 config.h.in~
-rwxrwxrwx 1 root mcslp 174254 Jan 25 15:52 configure*
-rw-rw-rw- 1 root mcslp 690 Jan 25 15:52 configure.ac
-rw-rw-rw- 1 root mcslp 718 Jan 25 15:50 configure.ac~
-rwxr-xr-x 1 root mcslp 15936 Jan 25 15:45 depcomp*
-rwxr-xr-x 1 root mcslp 9233 Jan 25 15:45 install-sh*
-rwxr-xr-x 1 root mcslp 11014 Jan 25 15:45 missing*
drwxrwxrwx 16 root mcslp 544 Jan 25 16:08 src/

Теперь вы можете попытаться сконфигурировать ваше приложение.

Тестирование новой конфигурации

Для проверки конфигурации просто выполните команду configure. Вы должны увидеть определение сценарием различных необходимых файлов библиотек и заголовков (листинг 12).

Листинг 12. Выполнение примера конфигурационного сценария

$ ./configure
checking for a BSD-compatible install... /bin/install -c
checking whether build environment is sane... yes
/bin/sh: /mnt/mc/Active: No such file or directory
configure: WARNING: `missing' script is too old or missing
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking for gcc... gcc
checking for C compiler default output file name... a.out
checking whether the C compiler works... yes
checking whether we are cross compiling... no
checking for suffix of executables...
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ANSI C... none needed
checking for style of include used by make... GNU
checking dependency style of gcc... gcc3
checking for flex... flex
checking for yywrap in -lfl... yes
checking lex output file root... lex.yy
checking whether yytext is a pointer... yes
checking for bison... bison -y
checking for sin in -lm... yes
checking how to run the C preprocessor... gcc -E
checking for egrep... grep -E
checking for working alloca.h... yes
checking for alloca... yes
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking stddef.h usability... yes
checking stddef.h presence... yes
checking for stddef.h... yes
checking for stdlib.h... (cached) yes
checking for an ANSI C-conforming const... yes
checking for pow... yes
configure: creating ./config.status
config.status: creating Makefile
config.status: creating src/Makefile
config.status: creating config.h
config.status: executing depfiles commands

Затем вы можете выполнить команду make для компоновки вашего приложения (листинг 13).

Листинг 13. Выполнение make

$ make
make all-recursive
make[1]: Entering directory `/export/data/calc'
Making all in src
make[2]: Entering directory `/export/data/calc/src'
bison -y -d calcparser.y
flex lex.l
if gcc -DHAVE_CONFIG_H -I. -I. -I..
-g -O2 -MT fmath.o -MD -MP -MF ".deps/fmath.Tpo" -c -o fmath.o fmath.c; 
then mv -f ".deps/fmath.Tpo" ".deps/fmath.Po"; else rm -f ".deps/fmath.Tpo";
  exit 1; fi
sed '/^#/ s/lex.yy\.c/lex.c/' lex.yy.c >lex.c
rm -f lex.yy.c
if gcc -DHAVE_CONFIG_H -I. -I. -I..
-g -O2 -MT tmath.o -MD -MP -MF ".deps/tmath.Tpo" -c -o tmath.o tmath.c; 
then mv -f ".deps/tmath.Tpo" ".deps/tmath.Po"; else rm -f ".deps/tmath.Tpo";
  exit 1; fi
if gcc -DHAVE_CONFIG_H -I. -I. -I..
-g -O2 -MT const.o -MD -MP -MF ".deps/const.Tpo" -c -o const.o const.c; 
then mv -f ".deps/const.Tpo" ".deps/const.Po"; else rm -f ".deps/const.Tpo";
  exit 1; fi
if test -f y.tab.h; then   to=`echo "calcparser_H" / sed -e
'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`;
sed -e "/^#/!b" -e "s/Y_TAB_H/$to/g" -e "s/y\.tab\.h/calcparser.h/" y.tab.h >calcparser.ht;
rm -f y.tab.h;   
if cmp -s calcparser.ht calcparser.h; then     rm -f calcparser.ht ; 
else     mv calcparser.ht calcparser.h;   fi; fi
if test -f y.output; then   mv y.output calcparser.output; fi
sed '/^#/ s/y\.tab\.c/calcparser.c/' y.tab.c
  >calcparser.ct && mv calcparser.ct calcparser.c
if gcc -DHAVE_CONFIG_H -I. -I. -I.. -
  g -O2 -MT lex.o -MD -MP -MF ".deps/lex.Tpo" -c -o lex.o lex.c; 
  then mv -f ".deps/lex.Tpo" ".deps/lex.Po";
  else rm -f ".deps/lex.Tpo"; exit 1; fi
rm -f y.tab.c
if gcc -DHAVE_CONFIG_H -I. -I. -I..
-g -O2 -MT calcparser.o -MD -MP -MF ".deps/calcparser.Tpo"
-c -o calcparser.o calcparser.c; then mv -f ".deps/calcparser.Tpo" ".deps/calcparser.Po";
else rm -f ".deps/calcparser.Tpo"; exit 1; fi
gcc -g -O2 -o calc calcparser.o lex.o fmath.o tmath.o const.o -lfl -lm -lm
make[2]: Leaving directory `/export/data/calc/src'
make[2]: Entering directory `/export/data/calc'
make[2]: Nothing to be done for `all-am'.
make[2]: Leaving directory `/export/data/calc'
make[1]: Leaving directory `/export/data/calc'
You can try out the application just to make it has been built properly:
$ ./src/calc
45*69
3105

Вы можете вернуться в первоначальное состояние (до компоновки и конфигурирования), используя указатель distclean:

$ make distclean
 

Используя файлы совместно по NFS-соединению и применяя этот метод для «сброса» проекта в исходное состояние после каждой компоновки, вы можете легко протестировать процесс на нескольких платформах без необходимости копирования дистрибутива на каждую систему.

Ограничения программы autotools

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

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

autotools также не может учесть любые используемые вами элементы, уникальные для конкретной OS. Если используемая вами функция или библиотека является уникальной для Solaris (например, уникальная для интерфейса ядра Solaris), autotools не сможет решить эту проблему.

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

Это не означает, что autotools нельзя использовать; это возможно самая эффективная инструментальная программа среды компоновки для переноса программного обеспечения, но не ждите от нее чудес. Для наиболее эффективного использования autotools вы должны быть готовы комбинировать ее использование с применением некоторых из рассмотренных ранее в данном руководстве приемов и методик для обеспечения переносимости.

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

Резюме

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

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

Хотя программа autotools может упростить процесс компоновки, она не предназначена для учета всех особенностей платформ. Она не может, например, учесть отсутствующие функции, библиотеки и различия в ядре OS, но будет учитывать значительную долю различий.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=2848