Autoconf - Написание тестов

Go to the first, previous, next, last section, table of contents.


Написание тестов

@anchor{Writing Tests}

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

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

Эти макросы проверяют вывод системного компилятора C. Они не кэшируют результаты тестов для последующего использования (see section Кэширование результатов), поскольку для генерации имени переменной кэша они не имеют достаточно информации о том, что они проверяют. Также по некоторым причинам они не печатают никаких сообщений. Проверки отдельных свойств компилятора С вызывают эти макросы и кэшируют свои результаты, а также выводят сообщения о том, что они проверяют.

Когда вы пишете тест свойства, который может быть применим для более чем одного пакета программного обеспечения, то лучше всего будет описать его как новый макрос. Для того, чтобы узнать, как это делается See section Создание макросов.

Исследование деклараций

@anchor{Examining Declarations}

Макрос AC_TRY_CPP используется для проверки существования конкретных заголовочных файлов. You can check for one at a time, or more than one if you need several header files to all exist for some purpose.

Macro: AC_TRY_CPP (includes, [action-if-true [, action-if-false]])
includes содержит директивы #include языков C или C++, а также объявления, над которыми выполняются подстановки переменных командного процессора, обратных кавычек и обратных слэшей. (В действительности, includes может быть любой программой на C, но другие выражения, вероятно, бесполезны). Если препроцессор не выдает сообщений об ошибках в течении обработки директивы, то выполняется код командного процессора action-if-true. В противном случае выполняется код action-if-false.

Этот макрос использует переменную CPPFLAGS, а не CFLAGS, поскольку `-g', `-O' и т. п. не являются правильными ключами для многих препроцессоров C.

Вот как узнать, содержит ли конкретный заголовочный файл определенное объявление, например, объявление типа, структуры, члена структуры или функции. Используйте макрос AC_EGREP_HEADER вместо прямого запуска команды grep для заголовочного файла; в некоторых системах символ может быть объявлен в другом заголовочном файле, а не в том, который вы проверяете в `#include'.

Macro: AC_EGREP_HEADER (pattern, header-file, action-if-found [, action-if-not-found])
Если вывод препроцессора, запущенного для системного заголовочного файла header-file соответствует регулярному выражению egrep pattern, то выполняются команды командного процессора action-if-found, в противном случае выполняются команды action-if-not-found.

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

AC_EGREP_CPP(yes,
[#ifdef _AIX
  yes
#endif
], is_aix=yes, is_aix=no)

Macro: AC_EGREP_CPP (pattern, program, [action-if-found [, action-if-not-found]])
program является текстом программы на C или C++, для которой выполняются подстановки переменных командного процессора, обратных кавычек и обратных слэшей. Если вывод препроцессора, обрабатывавшего program, соответствует регулярному выражению команды egrep pattern, то выполняется код командного процессора action-if-found, иначе выполняется action-if-not-found.

Этот макрос вызывает AC_PROG_CPP или AC_PROG_CXXCPP (в зависимости от того, какой из языков является текущим, see section Выбор языка), если эти макросы еще не вызывались.

Проверка синтаксиса

@anchor{Examining Syntax}

Для проверки синтаксических возможностей компиляторов C, C++ или Fortran 77, например, распознавания определенных ключевых слов, используется макрос AC_TRY_COMPILE, который пробует откомпилировать маленькую программу, которая использует заданную возможность. Вы также можете использовать этот макрос для проверки структур и полей структур, которые присутствуют не во всех системах.

Macro: AC_TRY_COMPILE (includes, function-body, [action-if-found [, action-if-not-found]])
Создает тестовую программу на C, C++ или Fortran 77 (в зависимости от того, какой язык является текущим, see section Выбор языка), для того, чтобы убедиться, что функция, чье тело состоит из function-body может быть скомпилирована.

Для C и C++, includes является любыми директивами #include, в которых нуждается код в function-body (параметр includes будет проигнорирован, если текущим языком является Fortran 77). Этот макрос при компиляции помимо переменной CPPFLAGS также использует переменные CFLAGS или CXXFLAGS, если текущим языком является C или C++. Переменная FFLAGS будет использована при компиляции, если текущим языком является Fortran 77.

Если файл компилируется нормально, то выполняются команды action-if-found, иначе выполняется action-if-not-found.

Этот макрос не пытается выполнить компоновку программы -- для этого вам придется использовать макрос AC_TRY_LINK (see section Проверка библиотек).

Проверка библиотек

@anchor{Examining Libraries}

Для проверки библиотеки, функции или глобальной переменной скрипт configure попытается скомпилировать и скомпоновать небольшую программу, которая использует тестируемые возможности. Этим Autoconf отличается от Metaconfig, который обрабатывает файлы библиотеки C, используя nm или ar, чтобы определить, какие функции доступны. Попытка скомпоновать программу с функцией -- более надежный вариант, поскольку он избавляет от необходимости обрабатывать различные ключи командной строки и форматы выдачи результатов программ nm и ar, а также выяснять расположение стандартных библиотек. Этот подход также позволяет конфигурировать кросс-компиляцию, а также проверять поведение функции во время выполнения. С другой стороны, этот подход может оказаться значительно более медленным, чем однократное сканирование библиотек.

Компоновщики в нескольких существующих системах не возвращают статус ошибки, если не могут найти какие-либо символы при компоновке. Эта ошибка делает невозможным использование на таких системах скриптов настройки, созданных Autoconf. Однако, некоторым из них могут быть заданы ключи, которые позволяют получить правильный статус завершения работы. Эту проблему в настоящий момент Autoconf не может обработать автоматически. Если пользователь столкнется с таким, то он может решить эту проблему установкой переменной среды LDFLAGS, передавая компоновщику необходимые ключи командной строки (например, `-Wl,-dn' на MIPS RISC/OS).

Макрос AC_TRY_LINK используется для компиляции тестовой программы для проверки функций и глобальных переменных. Он также используется макросом AC_CHECK_LIB для проверки библиотек (see section Файлы библиотек), временно добавляя проверяемую библиотеку в переменную LIBS и пытаясь скомпоновать маленькую программу.

Macro: AC_TRY_LINK (includes, function-body, [action-if-found [, action-if-not-found]])
В зависимости от текущего языка (see section Выбор языка), создается тестовая программа, для того чтобы выяснить, может ли быть скомпилирована и скомпонована функция, чье тело состоит из аргумента function-body.

Для C и C++, includes является любыми директивами #include, в которых нуждается код в function-body (параметр includes будет проигнорирован, если текущим языком является Fortran 77). Этот макрос при компиляции помимо переменной CPPFLAGS также использует переменные CFLAGS или CXXFLAGS, если текущим языком является C или C++. Переменная FFLAGS будет использована при компиляции, если текущим языком является Fortran 77. Однако в любом случае при компоновке будут использованы переменные LDFLAGS и LIBS.

Если файл компилируется и компонуется, то выполняются команды action-if-found, в противном случае --- action-if-not-found.

Macro: AC_TRY_LINK_FUNC (function, [action-if-found [, action-if-not-found]])
В зависимости от текущего языка see section Выбор языка), создается тестовая программа для того, чтобы убедиться, что программа, чье тело состоит их прототипа и вызова function, может быть скомпилирована и скомпонована.

Если файл компилируется и компонуется без ошибок, то выполняется код action-if-found, в противном случае выполняется action-if-not-found.

Macro: AC_TRY_LINK_FUNC (function, [action-if-found [, action-if-not-found]])
Этот макрос пробует скомпилировать и скомпоновать маленькую программу, которая компонуется с function. Если файл компилируется и компонуется без ошибок, то запускается код командного процессора action-if-found, в противном случае выполняется action-if-not-found.

Macro: AC_COMPILE_CHECK (echo-text, includes, function-body, action-if-found [, action-if-not-found])
Этот макрос является устаревшей версией AC_TRY_LINK. Он отличается тем, что выдает сообщение `checking for echo-text' в поток стандартного вывода, в том случае, если аргумент echo-text не является пустым. Вместо этого макроса для выдачи сообщений используйте AC_MSG_CHECKING и AC_MSG_RESULT (see section Выдача сообщений).

Проверка поведения во время выполнения

@anchor{Run Time}

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

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

Запуск тестовых программ

@anchor{Test Programs}

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

Macro: AC_TRY_RUN (program, [action-if-true [, action-if-false [, action-if-cross-compiling]]])
Аргумент program является текстом программы на языке C, для которой выполняются подстановки переменных командного процессора, а также обратных кавычек и обратных слэшей. Если она компилируется и компонуется, и при выполнении возвращает код завершения 0, то выполняется код командного процессора action-if-true. В противном случае выполняются команды action-if-false; код завершения тестовой программы доступен в переменной командного процессора `$?'. При компиляции этот макрос использует переменные CFLAGS или CXXFLAGS, CPPFLAGS, LDFLAGS и LIBS.

Если используемый компилятор C не создает исполняемых файлов, которые запускаются на той же системе, где выполняется скрипт configure, то тестовая программа не запускается. Если задан аргумент action-if-cross-compiling, то вместо программы запускается код, заданный в этом аргументе. В противном случае configure выдает сообщение об ошибке и прекращает работу.

Постарайтесь сделать значения по умолчанию пессимистическими, если кросс-компиляция не позволяет проверить поведение времени выполнения. Это можно сделать, передав макросу AC_TRY_RUN необязательный последний аргумент. autoconf выдает предупреждающее сообщение при создании configure каждый раз, когда встречается вызов макроса AC_TRY_RUN с незаданным аргументом action-if-cross-compiling. Вы можете игнорировать это предупреждение, хотя пользователи не смогут настроить ваш пакет для кросс-компиляции. Несколько макросов, поставляемых в составе Autoconf, выдают это предупреждающее сообщение.

Для конфигурирования для кросс-компиляции вы также можете выбрать значения параметров, основываясь на каноническом имени системы (see section Ручная настройка). В качестве альтернативы, вы можете установить правильное значение для целевой системы в кэш-файле с результатами тестов (see section Кэширование результатов).

Для задания значений по умолчанию для вызовов макроса AC_TRY_RUN, которые включены в другие макросы (включая те, которые поставляются с Autoconf), вы можете вызвать макрос AC_PROG_CC до их вызова. Затем, если переменная командного процессора cross_compiling имеет значение `yes', то используется альтернативный метод для получения результатов, вместо вызова макросов.

Macro: AC_C_CROSS
Этот макрос является устаревшим; он ничего не делает.

Рекомендации по написанию тестовых программ

@anchor{Guidelines}

Тестовые программы не должны выдавать никаких сообщений на поток стандартного вывода. Они должны возвращать значение 0 в случае удачи и ненулевое значение --- в противном случае, так что удачное выполнение можно легко отличить от выдачи дампа при крахе программы или другого неудачного выполнения; нарушение доступа к памяти и другие сбои возвращают ненулевой статус завершения. Тестовые программы должны завершать работу с помощью вызова функции exit, а не с помощью оператора return из подпрограммы main, поскольку на некоторых системах (по крайней мере, на старых машинах Sun) в подпрограмме main игнорируется аргумент оператора return.

Тестовые программы могут использовать директивы #if или #ifdef для проверки значений макросов препроцессора, определенных уже проведенными тестами. Например, если вы вызовете AC_HEADER_STDC, то далее в `configure.in' можно использовать тестовую программу, которая в зависимости от условия включает заголовочные файлы ANSI C:

#if STDC_HEADERS
# include <stdlib.h>
#endif

Если тестовой программе нужно использовать или создать файл данных, то задавайте этому файлу имя, которое начинаются с `conftest', например, `conftestdata'. Скрипт configure после выполнения тестовых программ а также в случае прерывания работы скрипта удаляет эти файлы с помощью команды `rm -rf conftest*'.

Тестовые функции

@anchor{Test Functions}

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

#ifdef __cplusplus
foo(int i)
#else
foo(i) int i;
#endif

Функции, которые объявляются в тестовых программах, должны быть также объявлены с применением прототипов `extern "C"', для использования с компиляторами C++. Убедитесь, что вы не включаете заголовочные файлы, содержащие конфликтующие прототипы.

#ifdef __cplusplus
extern "C" void *malloc(size_t);
#else
char *malloc();
#endif

Если тестовая программа вызывает функцию с неправильными параметрами (просто чтобы убедиться, что такая существует), то организуйте программу таким образом, чтобы эта функция никогда не была вызвана. Это можно сделать путем вызова ее в другой функции, которая никогда не вызывается. Вы не можете сделать это, поместив вызов функции после вызова функции exit, поскольку GCC версии 2 знает о том, что функция exit никогда не возвращается в точку вызова, и оптимизирует любой код, который следует за ней в том же блоке.

Если вы включаете какой-либо заголовочный файл, то убедитесь, что функции, находящиеся в этих файлах, вызываются с правильным числом параметров, даже если все эти параметры равны нулю. Это нужно, чтобы избежать ошибок компиляции из-за несоответствия прототипов. GCC версии 2 имеет внутренние прототипы нескольких функций, которые он встраивает в код автоматически; например, к таким относится memcpy. Для того, чтобы избежать ошибок при их проверке, либо передавайте этим функциям правильное количество аргументов, либо повторно объявите эти функции с другим типом возвращаемого значения (например, как char).

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

@anchor{Portable Shell}

Есть определенные техники программирования скриптов командного процессора, которых вам следует избегать, чтобы ваш код был переносим. Bourne shell и совместимые с ним процессора, такие как Bash и Korn, развивались в течении многих лет, но для того, чтобы избежать трудностей, не используйте возможностей, которые были добавлены после выпуска UNIX версии 7, примерно в 1977 году. Вы не должны использовать функции командного процессора, псевдонимы (aliases), отрицательные классы символов и другие возможности, которые присутствуют не во всех версиях командных процессоров, совместимых с процессором Bourne; ограничьте себя общим знаменателем. Даже unset не поддерживается всеми командными процессорами! При указании интерпретатора ставьте пробел после символов `#!', например,

#! /usr/bin/perl

Если вы уберете пробел перед путевым именем, то системы типа 4.2BSD, такие как Sequent DYNIX, будут просто игнорировать эту строку, поскольку они интерпретируют `#! /' как 4-х байтовое магическое число.

Набор внешних программ, которые можно запускать из скрипта configure, довольно мал. See section `Utilities in Makefiles' in GNU Coding Standards, ниже приведен список этих программ. Это ограничение позволяет пользователям начать с небольшого количества программ, постепенно компилируя остальные, и избежать слишком большого числа зависимостей между пакетами.

Многие такие внешние утилиты обладают общим подмножеством переносимых возможностей; например, не полагайтесь на то, что команда ln имеет ключ `-f', а cat вообще имеет какие-либо ключи. Скрипты sed не должны содержать комментариев или использовать метки длиннее 8 символов. Не используйте `grep -s' для запрещения вывода, поскольку `grep -s' на System V не запрещает вывод, а запрещает только сообщения об ошибках. Вместо этого ключа лучше перенаправьте стандартные потоки вывода и сообщений об ошибках (сообщения о несуществующих файлах) программы grep на устройство `/dev/null'. Проверяйте код возврата grep, чтобы узнать, произошло ли совпадение.

Тестирование значений и файлов

@anchor{Testing Values and Files}

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

Программа test используется для выполнения многих проверок файлов и строк. Она часто запускается альтернативным способом, через имя `[', но использование этого имени в коде Autoconf приведет к ошибкам, потому что этот символ является символом кавычек в m4.

Если вам необходимо выполнить несколько проверок, используя команду test, то объединяйте их с помощью операторов командного процессора `&&' и `||', а не используйте операторы программы test `-a' и `-o'. На System V приоритеты операторов `-a' и `-o' неправильно соотносятся с приоритетами унарных операторов; из-за этого POSIX не определяет эти операторы, так что их использование приводит к непереносимому коду. Если вы в одном выражении используете как `&&', так и `||', то помните, что они имеют одинаковый приоритет.

Скрипты configure, поддерживающие кросс-компиляцию, не должны не делать ничего, что тестирует свойства системы, на которой выполняется скрипт. Но иногда вам может понадобиться проверить, существует ли определенный файл. Чтобы сделать это используйте команды `test -f' или `test -r'. Не используйте команду `test -x', поскольку 4.3BSD не поддерживает ее.

Другой непереносимой конструкцией программирования командного процессора является

var=${var:-value}

Она предназначена для установки значения переменной var равным value, но только в тех случаях, когда переменная еще не имеет значения. Если var уже было присвоено значение, даже равное пустой строке, то оно остается неизменным. Старые командные процессоры BSD, включая Ultrix-версию sh, не воспринимают символ двоеточия, выдают ошибку и прекращают работу. Переносимым эквивалентом данной конструкции является

: ${var=value}

Множество вариантов

@anchor{Multiple Cases}

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

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

AC_MSG_CHECKING(как получить тип файловой системы)
fstype=no
# Порядок этих действий является важным.
AC_TRY_CPP([#include <sys/statvfs.h>
#include <sys/fstyp.h>], AC_DEFINE(FSTYPE_STATVFS) fstype=SVR4)
if test $fstype = no; then
AC_TRY_CPP([#include <sys/statfs.h>
#include <sys/fstyp.h>], AC_DEFINE(FSTYPE_USG_STATFS) fstype=SVR3)
fi
if test $fstype = no; then
AC_TRY_CPP([#include <sys/statfs.h>
#include <sys/vmount.h>], AC_DEFINE(FSTYPE_AIX_STATFS) fstype=AIX)
fi
# (остальные варианты пропущены в этом примере)
AC_MSG_RESULT($fstype)

Выбор языка

@anchor{Language Choice}

Пакеты, использующие одновременно и C, и C++, нуждаются в проверке возможностей обоих компиляторов. Созданные Autoconf скрипты configure по умолчанию выполняют проверку возможностей компилятора C. Нижеописанные макросы определяют, компилятор какого языка будет использоваться в тестах, которые последуют за вызовом этого макроса в `configure.in'.

Macro: AC_LANG_C
Выполняет тесты компиляции, используя переменные CC и CPP, а также используя расширение `.c' для тестовых программ. Устанавливает переменную командного процессора cross_compiling в значение, вычисленное макросом AC_PROG_CC, если он был запущен, и в пустое значение в противном случае.

Macro: AC_LANG_CPLUSPLUS
Выполняет тесты компиляции, используя переменные CXX и CXXPP, а также используя расширение `.C' для тестовых программ. Устанавливает переменную командного процессора cross_compiling в значение, вычисленное макросом AC_PROG_CXX, если он был запущен, и в пустое значение в противном случае.

Macro: AC_LANG_FORTRAN77
Выполняет тесты компиляции, используя переменную F77, а также используя расширение `.f' для тестовых программ. Устанавливает переменную командного процессора cross_compiling в значение, вычисленное макросом AC_PROG_F77, если он был запущен, и в пустое значение в противном случае.

Macro: AC_LANG_SAVE
Запоминает в стеке значение текущего языка (установленное макросами AC_LANG_C, AC_LANG_CPLUSPLUS или AC_LANG_FORTRAN77). Не изменяет значение текущего языка. Используйте этот макрос и AC_LANG_RESTORE в макросах, которым необходимо временно переключиться на конкретный язык.

Macro: AC_LANG_RESTORE
Выбирает язык, который был сохранен на вершине стека, где он был сохранен макросом AC_LANG_SAVE, и удаляет его со стека. Этот макрос эквивалентен вызову AC_LANG_C, AC_LANG_CPLUSPLUS или AC_LANG_FORTRAN77, в зависимости от того, который из них действовал во время последнего вызова макроса AC_LANG_SAVE.

Не вызывайте этот макрос больше раз, чем было вызовов AC_LANG_SAVE.

Macro: AC_REQUIRE_CPP
Убеждается, что препроцессор, который должен сейчас использоваться, был найден. Вызывает макрос AC_REQUIRE (see section Требуемые макросы) с аргументом, равным либо AC_PROG_CPP, либо AC_PROG_CXXCPP, в зависимости от того, какой язык был выбран.


Go to the first, previous, next, last section, table of contents.