Создание программного обеспечения для нескольких UNIX-платформИсточник: IBM developerWorks Россия Мартин Браун
Перед началом работыДанное руководство посвящено проблемам совместимости при создании и миграции ваших приложений между различными 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-совместимой, должен работать на различных операционных системах без каких-либо модификаций. Например, функция Если создаваемое вами приложение основано на конкретной версии UNIX, значит, миграция вашей системы на POSIX-стандарты является первым шагом на пути к совместимости, хотя это и не устраняет все проблемы. Оценка текущей задачиВашей первой задачей является определение сложности приложения, которое вы собираетесь переносить. Обычное приложение содержит много различных элементов. Вы должны определить, какие элементы должны быть разработаны заново, а какие должны быть сделаны более совместимыми с различными платформами. Элементами, которые вы должны исследовать, являются:
Естественно, основная часть C-кода является переносимой. Большинство современных компиляторов С основаны на стандартах, следовательно компилирование кода не представляет проблем. Большая часть проблем при переносе связана с библиотеками, заголовками и ограничениями среды компоновки. Общие проблемыВ данном разделе исследуются некоторые основные проблемы, связанные с работой с заголовками, библиотеками, программами компоновки и средой. Программы компоновки и средаОсновными элементами в процессе переноса приложений являются доступные инструментальные средства компоновки и среда. Если вы используете стандартную программу Makefile, ситуация значительно упрощается, но вы все равно должны принимать во внимание некоторые критические различия. Например, должны быть доступны компилятор C, препроцессор C и такие программы, как Например, на большинстве версий UNIX доступен компилятор C - Вы можете упростить среду и процесс, используя свободно распространяемые программы, такие как Стандартная UNIX-программа Кроме того, учитывайте, что различные программы расположены в разных местах на разных платформах. Например, Solaris содержит Самым простым способом учета этих различий на уровне Makefile (без использования программы GNU autotools) является создание отдельного Makefile для каждой среды, изменяя каждый Makefile соответственно платформе. Затем вы можете использовать соответствующий Makefile в
Вам все равно придется учитывать различия между файлами, но процесс должен быть значительно легче. Использование препроцессора CФайлы заголовков и большинство архитектурных и функциональных различий обычно могут быть учтены в C при помощи использования директив препроцессора C для выбора различных участков кода. Система работает, потому что C-код компилируется перед проходом через препроцессор С ( Вы наверняка уже использовали директивы для включения отладочного кода. Например, вы могли бы указать следующее определение при компоновке приложения в среде Solaris x86:
Вы можете указать это определение либо в заголовочном файле, либо включить определение в командную строку для компилятора:
Внутри исходного кода вы могли бы тогда указать корректный фрагмент для использования, проверяя это определение. Например, в листинге 1 , выбирается вариант для вашего определения.
Вы можете также использовать Вы можете использовать аналогичную систему по всему исходному коду. Например, вы можете применить ее для включения различных заголовочных файлов (листинг 2) и учесть различия, с которыми столкнулись ранее для функции Листинг 2. Выбор файла заголовка
Эти же принципы вы должны использовать по всему вашему коду, предполагая, что вы знаете, что ищете. Различия в заголовкахНаиболее общей проблемой при переносе приложений между различными версиями UNIX являются доступные в операционной системе заголовочные файлы, используемые для определения структур, типов переменных и функций. Некоторые заголовочные файлы размещаются в различных местах. Например, содержимое файла limits.h более или менее стандартно в разных UNIX-платформах, но он располагается в разных каталогах. В AIX, например, вы импортируете файл так:
Но в Solaris:
В некоторых ситуациях, требуемая информация находится не в том файле, отличается от используемого вами определения, либо вовсе отсутствует. В указанных ситуациях вы должны найти месторасположение определения функции, структуры или переменной, которую вы ищете. Лучшим способом поиска месторасположения является использование программы grep, как показано в следующем коде, в котором ищется определение
Если искомое определение не соответствует определению на исходной платформе, вы можете иногда вставить альтернативное определение. Однако будьте внимательны и не измените смысл вызова системной или библиотечной функции; изменение определения forward-функции изменит соответствующую библиотечную функцию. Если определение не существует, это может указывать на отсутствие библиотеки или интерфейса, на которых оно базируется. Различия в библиотекахРазличные версии UNIX размещают компоненты в различных библиотеках и могут потребовать включения нескольких библиотек, не нужных на другой платформе. Классическим примером являются сетевые библиотеки. На многих UNIX-платформах необходимые для создания сетевых приложений библиотеки автоматически включаются в ваши приложения при компоновке Однако в Solaris вы должны явно добавить эти библиотеки, для того чтобы они могли быть связаны с вашим приложением:
Не существует простого способа перечислить все различные варианты и потенциальные отличия для разных версий UNIX. Единственным способом решения этой проблемы является индивидуальный подход к каждому приложению. Пробуйте компилировать приложение и следите за предупреждениями об отсутствующих символах. Например, если вы не включили библиотеку math, то можете получить ошибки, аналогичные показанным в листинге 3. Листинг 3. Ошибки отсутствующих символов
После получения списка отсутствующих символов вы можете просмотреть справочное руководство (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), которая компонует приложение на системе. Если вы когда-либо компоновали приложение с открытым исходным кодом на вашей системе, то возможно помните следующую последовательность:
Сценарий Весь процесс работает, поскольку система autotools основывается на комбинации заранее известных значений (например, она знает доступные на целевой системе типы, библиотеки и заголовочные файлы) для конкретной платформы, объединенной с информацией о системе времени исполнения, определяющей доступность конкретных функций и заголовочных файлов. Для многих приложений вы не должны делать изменения в вашем коде вручную для того, чтобы сделать его переносимым. Также, вы даже не должны беспокоиться о том, работает ли на целевой системе сценарий configure. Я разрабатывал приложения, использующие систему autotools, которые успешно компилировались и компоновались без каких-либо изменений в коде на HP-UX, Solaris (SPARC and x86), Linux, Mac OS X и BSD. Очевидно, что для эффективного использования преимуществ системы вы, прежде всего, должны предоставить среде autotools обычный сценарий компоновки вашего приложения, а затем использовать autotools для сканирования вашего исходного кода и определения функций, библиотек и других компонентов, необходимых для работы приложения. Настройка структуры проектаПервым действием при использовании autotools является создание подходящей структуры для вашего приложения. Для данного примера вы будете использовать простое приложение calculator (см. раздел «Загрузка»), которое базируется на компоненте лексического анализа (требующего наличия lex) и грамматическом компоненте (требующего наличия Листинг 4. Исходные файлы для приложения calculator
Записанный вручную Makefile для компоновки приложения calculator приведен в листинге 5. Листинг 5. Записанный вручную Makefile
Для настройки приложения на использование с программой autotools удалите Makefile, создайте новый каталог (назовем его calc) и подкаталог (src), в который скопируйте все исходные файлы. Схему расположения файлов (готовую для autotools) вы можете увидеть в листинге 6. Листинг 6. Исходные файлы, готовые для компоновки с autotools
Теперь вы готовы начать настройку исходных файлов для autotools. Создание основы MakefilesВы должны создать файл Makefile.am в каждом каталоге с вашими исходными кодами, в том числе и в корневом каталоге проекта. Makefile.am в корневом каталоге используется для ссылки на содержимое остальных каталогов. Файлы Makefile.am, расположенные в каждом из каталогов с исходными кодами, используются для определения процесса компоновки и требований, относящихся только к данному каталогу. Итак, для вашего примера файл Makefile.am в каталоге calc будет выглядеть следующим образом:
Эта строка просто указывает список подкаталогов, содержащих файлы, которые будут настраиваться (и компоноваться) при помощи системы конфигурирования. Файл Makefile.am в вашем главном каталоге src показан в листинге 7. Листинг 7. Скелет src/Makefile.am для компоновки на целевой системе по умолчанию
Префикс индивидуальных строк является важной составляющей: он используется для идентификации объекта, к которому применяется параметр. Первая строка в листинге 7 указывает конечный каталог приложения для его установки программой Вторая строка указывает название генерируемого приложения. Третья перечисляет исходные файлы, требуемые для компоновки приложения. Обратите внимание на то, что хотя ваше приложение основано на С-файлах, автоматически сгенерированных различными программами (в частности Четвертая строка устанавливает общий параметр для добавления параметра командной строки Последняя строка листинга 4 указывает дополнительные библиотеки, требуемые для компоновки приложения. Обратите внимание на то, что вы указываете только названия библиотек так, как они указывались бы для После настройки основного процесса компоновки вы должны дать возможность системе autotools просканировать ваш исходный код и определить функции и другие элементы, которые, возможно, могут иметь различные источники и определения в разных системах. Сканирование исходного кодаФайл configure.ac определяет зависящие от конфигурации элементы вашего исходного кода. Содержимое этого файла используется для генерирования необходимых конфигурационных сценариев, которые окончательно настраивают среду компоновки для целевой хост-системы. Вы не должны создавать этот файл самостоятельно; можно использовать функцию
Не обращайте внимания на ошибку - она просто указывает на то, что не был найден файл configure.ac, на котором основывалась операция сканирования. При первом запуске этой инструментальной программы должен сформироваться файл под названием configure.scan. Содержимое этого файла приведено в листинге 8. Листинг 8. Конфигурация autoscan по умолчанию
Элементы Здесь необходимо перенастроить несколько элементов. Начните с Затем добавьте строку, конфигурирующую систему для automake:
Необходимо также обновить строки Получившийся файл должен выглядеть примерно так, как показано в листинге 9.
Этот файл будет использоваться программой autoconf для определения функций, которые нужно найти для компоновки приложения. Изменение исходного кодаСистема autotools генерирует конфигурационные файлы и, что более важно, заголовочный файл config.h, который используется для хранения всех необходимых элементов определения для компоновки сценария. Вы должны добавить заголовочный файл config.h в ваши исходные коды на С, для того чтобы они загружали информацию о конфигурации во время компилирования. Возможно, еще понадобится произвести незначительные изменения в вашем коде. Например, сценарий конфигурирования предполагает, что yacc-файл calc.y будет формировать два файла, calc.h и calc.c. Проверьте, что при этом не перезапишутся какие-либо существующие файлы, либо просто измените название исходного файла для Создание конфигурационного сценарияТеперь у вас есть скелет Makefile и скелет конфигурации для проекта. Вы должны преобразовать их в четыре различных компонента:
Этот процесс выполняется в четыре стадии. Во-первых, сгенерируйте m4-макрос:
Вы можете проигнорировать любые предупреждения, выдаваемые этой командой, но обратите внимание на все ошибки; они, возможно, укажут на опечатки в вашем файле configure.ac. Затем создайте шаблон заголовочного файла:
Теперь вы готовы к формированию шаблона файлов Makefile. Но сначала вы должны создать несколько файлов, являющихся частью стандартной конфигурации autotools. Такими файлами являются:
Пока вы можете просто создать пустые файлы:
Теперь можно создать шаблоны Makefile:
Параметр -a указывает программе automake добавить отсутствующие файлы, а параметр -c - скопировать эти файлы в каталог. Наконец, вы должны сгенерировать конфигурационный сценарий, который фактически выполнит конфигурирование на целевой системе:
Полная последовательность действий (и пример отображения результатов работы) приведена в листинге 10. Листинг 10. Последовательность компоновки конфигурации
Если вы проверите ваш корневой каталог, то должны заметить основные компоненты, такие как сценарий configure, файлы config.h.in и Makefile.in (листинг 11 ). Листинг 11. Пример структуры каталога
Теперь вы можете попытаться сконфигурировать ваше приложение. Тестирование новой конфигурацииДля проверки конфигурации просто выполните команду Листинг 12. Выполнение примера конфигурационного сценария
Затем вы можете выполнить команду
Вы можете вернуться в первоначальное состояние (до компоновки и конфигурирования), используя указатель
Используя файлы совместно по NFS-соединению и применяя этот метод для «сброса» проекта в исходное состояние после каждой компоновки, вы можете легко протестировать процесс на нескольких платформах без необходимости копирования дистрибутива на каждую систему. Ограничения программы autotoolsСистема autotools берет на себя значительную долю сложности компоновки совместимых с несколькими платформами приложений, но она не является окончательным решением. autotools не может учесть специализированные библиотеки, сторонних разработчиков или ваши собственные, не являющиеся частью дистрибутивных файлов (хотя ничто не мешает вам добавить их или создать специализированные конфигурации autotool). autotools также не может учесть любые используемые вами элементы, уникальные для конкретной OS. Если используемая вами функция или библиотека является уникальной для Solaris (например, уникальная для интерфейса ядра Solaris), autotools не сможет решить эту проблему. Система autotools решает только основные проблемы различий в версиях UNIX и C-библиотеках, зависимости их заголовков, и среды компоновки, необходимых для компилирования связей элементов. Она не может эмулировать альтернативную библиотеку и среду ядра для переноса программного обеспечения. Это не означает, что autotools нельзя использовать; это возможно самая эффективная инструментальная программа среды компоновки для переноса программного обеспечения, но не ждите от нее чудес. Для наиболее эффективного использования autotools вы должны быть готовы комбинировать ее использование с применением некоторых из рассмотренных ранее в данном руководстве приемов и методик для обеспечения переносимости. Наконец, независимо от объема выполняемой вами работы, нет реальной замены тестированию вашего приложения на целевой платформе. Программа autotools может упростить процесс компоновки, но не ждите от нее исключения необходимости отладки и тестирования. РезюмеРазработка приложений, работающих на нескольких UNIX-платформах, требует понимания существующих проблем, которые влияют на процессы компилирования и связывания. Почти для всех ситуаций проблемы возникают в двух областях: в заголовочных файлах, поддерживающих стандартную функциональность системы и ядра, и библиотеках, используемых для поддержки специализированных расширений и функциональности. За этими различиями скрываются сложные проблемы ограничений конкретной операционной системы. Например, проблемы могут вызвать различия в сигналах, поддерживаемых разными версиями UNIX. Некоторые из этих различий могут быть смягчены использованием приемов для обхода этих различий и проблем. Для упрощения проблем в заголовках, библиотеках и средах компоновки вы можете использовать пакет autotools/autoconf, производимый GNU. Он использует комбинацию известных различий и сценарий определения особенностей среды целевой системы во время процесса компоновки, и, затем, формирует подходящий сценарий компоновки и заголовочный файл для учета платформо-зависимых вопросов. Хотя программа autotools может упростить процесс компоновки, она не предназначена для учета всех особенностей платформ. Она не может, например, учесть отсутствующие функции, библиотеки и различия в ядре OS, но будет учитывать значительную долю различий. |