NGINX — это HTTP-сервер и обратный прокси-сервер, почтовый прокси-сервер, а также TCP/UDP прокси-сервер общего назначения, изначально написанный Игорем Сысоевым. Уже длительное время он обслуживает серверы многих высоконагруженных сайтов.
Однако кросс-компиляция NGINX практически невозможна, поскольку разработанные Игорем Сысоевым скрипты конфигурирования исходного кода в большинстве случаев используют так называемую процедуру «Try Run».
Те кто знаком с утилитами Autoconf, Automake знают, что проверки необходимых параметров системы и кросс-компилятора осуществляются различными процедурами, которые, в свою очередь, могут применять попытки сборки исходного кода (Try compile), линковки объектных файлов (Try link) и, наконец, попытки запуска тестовых программ (Try Run).
Естественно, если речь идет о кросс-сборке, операции «Try Run» недопустимы, ведь мы не можем запустить программу, собранную под целевую архитектуру отличающуюся от архитектуры машины сборки на самой машине сборки.
В Autotools проблемы, связанные с невозможностью запуска целевых программ на сборочной машине в некоторых случаях решаютcя кешированием переменных, которые могу быть переопределены пользователем в файле --cache-file или заданы в командной строке вызова скрипта ./configure.
Скрипты Игоря Сысоева не предусматривают такого переопределения машинно-зависимых величин. Однако величин, которые необходимо задать во время конфигурирования исходных кодов NGINX достаточно много. К ним, в первую очередь, относятся размеры типов данных. Именно с них мы и начнем.
Размеры переменных различных типов проверяются с помощью скрипта auto/types/sizeof. Тестовые процедуры возвращают размер в байтах и предаются с помощью переменной ngx_size. В случае использования gcc для вычисления основных размеров данных, мы можем воспользоваться предопределенными macro С-компилятора, входящего в поставку GCC. Если мы добавим в каталог auto/types скрипт gcc-sizeof:
#!/bin/sh
if [ "x$NGX_CC_NAME" = "x" -o "x$ngx_type" = "x" ] ; then
echo "unknown"
fi
if [ "$ngx_type" = "int" ] ; then
size_from_cpp=`${CC} -dM -E - < /dev/null | grep __SIZEOF_INT__ | cut -f3 -d' ' | sed 's,^[ \t]*,,' | sed 's,[ \t]*$,,'`
echo "$size_from_cpp"
elif [ "$ngx_type" = "long" ] ; then
size_from_cpp=`${CC} -dM -E - < /dev/null | grep __SIZEOF_LONG__ | cut -f3 -d' ' | sed 's,^[ \t]*,,' | sed 's,[ \t]*$,,'`
echo "$size_from_cpp"
elif [ "$ngx_type" = "long long" ] ; then
size_from_cpp=`${CC} -dM -E - < /dev/null | grep __SIZEOF_LONG_LONG__ | cut -f3 -d' ' | sed 's,^[ \t]*,,' | sed 's,[ \t]*$,,'`
echo "$size_from_cpp"
elif [ "$ngx_type" = "size_t" ] ; then
size_from_cpp=`${CC} -dM -E - < /dev/null | grep __SIZEOF_SIZE_T__ | cut -f3 -d' ' | sed 's,^[ \t]*,,' | sed 's,[ \t]*$,,'`
echo "$size_from_cpp"
elif [ "$ngx_type" = "sig_atomic_t" ] ; then
size_from_cpp=`${CC} -dM -E - < /dev/null | grep __SIZEOF_INT__ | cut -f3 -d' ' | sed 's,^[ \t]*,,' | sed 's,[ \t]*$,,'`
echo "$size_from_cpp"
elif [ "$ngx_type" = "void *" ] ; then
size_from_cpp=`${CC} -dM -E - < /dev/null | grep __SIZEOF_POINTER__ | cut -f3 -d' ' | sed 's,^[ \t]*,,' | sed 's,[ \t]*$,,'`
echo "$size_from_cpp"
elif [ "$ngx_type" = "off_t" ] ; then
size_from_cpp=`${CC} -dM -E - < /dev/null | grep __SIZEOF_PTRDIFF_T__ | cut -f3 -d' ' | sed 's,^[ \t]*,,' | sed 's,[ \t]*$,,'`
echo "$size_from_cpp"
elif [ "$ngx_type" = "time_t" ] ; then
size_from_cpp=`${CC} -dM -E - < /dev/null | grep __SIZEOF_PTRDIFF_T__ | cut -f3 -d' ' | sed 's,^[ \t]*,,' | sed 's,[ \t]*$,,'`
echo "$size_from_cpp"
fi
и поправим скрипт auto/types/sizeof так, чтобы в случае GCC, для вычисления размеров вызывался скрипт auto/types/gcc-sizeof, то на данном этапе конфигурирования мы сможем обеспечить правильную работу без запуска тестовых исполняемых файлов:
diff -b --unified -Nr nginx-1.20.2-orig/auto/types/sizeof nginx-1.20.2/auto/types/sizeof
--- nginx-1.20.2-orig/auto/types/sizeof 2021-11-16 17:44:02.000000000 +0300
+++ nginx-1.20.2/auto/types/sizeof 2022-02-13 19:50:26.816530942 +0300
@@ -14,7 +14,8 @@
ngx_size=
-cat << END > $NGX_AUTOTEST.c
+if [ "$NGX_CC_NAME" != "gcc" ] ; then
+ cat << END > $NGX_AUTOTEST.c
#include <sys/types.h>
#include <sys/time.h>
@@ -33,18 +34,21 @@
END
-ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS \
+ ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS \
-o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_LD_OPT $ngx_feature_libs"
-eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1"
+ eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1"
-if [ -x $NGX_AUTOTEST ]; then
+ if [ -x $NGX_AUTOTEST ]; then
ngx_size=`$NGX_AUTOTEST`
echo " $ngx_size bytes"
+ fi
+else
+ ngx_size=`ngx_type="$ngx_type" . auto/types/gcc-sizeof`
+ echo " $ngx_size bytes"
fi
-
case $ngx_size in
4)
ngx_max_value=2147483647
Для проверки big/little endian мы также можем воспользоваться предопределенными macro, добавив собственный скрипт gcc-endianness в каталог auto:
#!/bin/sh
if [ "x$NGX_CC_NAME" = "x" ] ; then
exit 0
fi
if `${CC} -dM -E - < /dev/null | grep " __BYTE_ORDER__ " | cut -f3 -d' ' | grep -q "_BIG_"` ; then
exit 1
fi
и немного поправив оригинальный скрипт endianness:
diff -b --unified -Nr nginx-1.20.2-orig/auto/endianness nginx-1.20.2/auto/endianness
--- nginx-1.20.2-orig/auto/endianness 2021-11-16 17:44:02.000000000 +0300
+++ nginx-1.20.2/auto/endianness 2022-02-13 19:50:26.816530942 +0300
@@ -13,7 +13,8 @@
END
-cat << END > $NGX_AUTOTEST.c
+if [ "$NGX_CC_NAME" != "gcc" ] ; then
+ cat << END > $NGX_AUTOTEST.c
int main(void) {
int i = 0x11223344;
@@ -26,12 +27,12 @@
END
-ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS \
+ ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS \
-o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_LD_OPT $ngx_feature_libs"
-eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1"
+ eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1"
-if [ -x $NGX_AUTOTEST ]; then
+ if [ -x $NGX_AUTOTEST ]; then
if $NGX_AUTOTEST >/dev/null 2>&1; then
echo " little endian"
have=NGX_HAVE_LITTLE_ENDIAN . auto/have
@@ -41,10 +42,18 @@
rm -rf $NGX_AUTOTEST*
-else
+ else
rm -rf $NGX_AUTOTEST*
echo
echo "$0: error: cannot detect system byte ordering"
exit 1
+ fi
+else
+ if `. auto/gcc-endianness` ; then
+ echo " little endian"
+ have=NGX_HAVE_LITTLE_ENDIAN . auto/have
+ else
+ echo " big endian"
+ fi
fi
Здесь надо учитывать то, что скрипт endianness ожидает нормального выхода из скрипта gcc-endianness в случае little-endian и аварийного завершения в случае big-endian.
Покончив с основными типами данных и ориентацией байтов, мы можем отменить проверку самого cross-компилятора, просто запретив выполнение тестовой процедуры в файле auto/cc/name:
diff -b --unified -Nr nginx-1.20.2-orig/auto/cc/name nginx-1.20.2/auto/cc/name
--- nginx-1.20.2-orig/auto/cc/name 2021-11-16 17:44:02.000000000 +0300
+++ nginx-1.20.2/auto/cc/name 2022-02-13 19:50:26.816530942 +0300
@@ -7,7 +7,7 @@
ngx_feature="C compiler"
ngx_feature_name=
- ngx_feature_run=yes
+ ngx_feature_run=
ngx_feature_incs=
ngx_feature_path=
ngx_feature_libs=
Однако это еще не все. Далее нам необходимо обеспечить проверки, осуществляемые с помощью скрипта auto/feature. Здесь вам будет необходимо, самостоятельно собрав и выполнив код некоторых тестовых программ, разобраться в том, какие из проверок вы сможете заменить на заготовленные вами ответы. Ответы передаются посредством переменной ngx_found. Если речь идет об обычном дистрибутиве на базе ядра Linux и GNU Libc, то вам будет достаточно следующих изменений файла auto/feature:
diff -b --unified -Nr nginx-1.20.2-orig/auto/feature nginx-1.20.2/auto/feature
--- nginx-1.20.2-orig/auto/feature 2021-11-16 17:44:02.000000000 +0300
+++ nginx-1.20.2/auto/feature 2022-02-13 19:50:26.816530942 +0300
@@ -52,6 +52,85 @@
case "$ngx_feature_run" in
yes)
+
+ if [ "$ngx_feature_name" = "NGX_HAVE_GCC_ATOMIC" ] ; then
+ echo " found"
+ ngx_found=yes
+
+ if test -n "$ngx_feature_name"; then
+ have=$ngx_have_feature . auto/have
+ fi
+ elif [ "$ngx_feature_name" = "NGX_HAVE_C99_VARIADIC_MACROS" ] ; then
+ echo " found"
+ ngx_found=yes
+
+ if test -n "$ngx_feature_name"; then
+ have=$ngx_have_feature . auto/have
+ fi
+ elif [ "$ngx_feature_name" = "NGX_HAVE_GCC_VARIADIC_MACROS" ] ; then
+ echo " found"
+ ngx_found=yes
+
+ if test -n "$ngx_feature_name"; then
+ have=$ngx_have_feature . auto/have
+ fi
+ elif [ "$ngx_feature_name" = "NGX_HAVE_EPOLL" ] ; then
+ echo " found"
+ ngx_found=yes
+
+ if test -n "$ngx_feature_name"; then
+ have=$ngx_have_feature . auto/have
+ fi
+ elif [ "$ngx_feature_name" = "NGX_HAVE_SENDFILE" ] ; then
+ echo " found"
+ ngx_found=yes
+
+ if test -n "$ngx_feature_name"; then
+ have=$ngx_have_feature . auto/have
+ fi
+ elif [ "$ngx_feature_name" = "NGX_HAVE_SENDFILE64" ] ; then
+ echo " found"
+ ngx_found=yes
+
+ if test -n "$ngx_feature_name"; then
+ have=$ngx_have_feature . auto/have
+ fi
+ elif [ "$ngx_feature_name" = "NGX_HAVE_PR_SET_DUMPABLE" ] ; then
+ echo " found"
+ ngx_found=yes
+
+ if test -n "$ngx_feature_name"; then
+ have=$ngx_have_feature . auto/have
+ fi
+ elif [ "$ngx_feature_name" = "NGX_HAVE_PR_SET_KEEPCAPS" ] ; then
+ echo " found"
+ ngx_found=yes
+
+ if test -n "$ngx_feature_name"; then
+ have=$ngx_have_feature . auto/have
+ fi
+ elif [ "$ngx_feature_name" = "NGX_HAVE_MAP_ANON" ] ; then
+ echo " found"
+ ngx_found=yes
+
+ if test -n "$ngx_feature_name"; then
+ have=$ngx_have_feature . auto/have
+ fi
+ elif [ "$ngx_feature_name" = "NGX_HAVE_MAP_DEVZERO" ] ; then
+ echo " found"
+ ngx_found=yes
+
+ if test -n "$ngx_feature_name"; then
+ have=$ngx_have_feature . auto/have
+ fi
+ elif [ "$ngx_feature_name" = "NGX_HAVE_SYSVSHM" ] ; then
+ echo " found"
+ ngx_found=yes
+
+ if test -n "$ngx_feature_name"; then
+ have=$ngx_have_feature . auto/have
+ fi
+ else
# /bin/sh is used to intercept "Killed" or "Abort trap" messages
if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then
echo " found"
@@ -64,6 +143,8 @@
else
echo " found but is not working"
fi
+ fi
+
;;
value)
Готовый patch для NGINX версии 1.20.2 можно получить следующим образом. Необходимо получить исходный код Radix cross Linux с помощью команды:
svn checkout svn://radix.pro/platform/branches/radix-1.8
Сменить каталог на radix-1.8/sources/packages/n/nginx и выполнить команду make:
NO_CCACHE=1 make
Таким образом вы получите исходный архив NGINX и необходимый для кросс-сборки patch в каталоге patches.
Весть процесс сборки NGINX можно видеть в каталоге radix-1.8/net/nginx/1.20.2. Здесь в Make-файле представлены основные параметры конфигурирования для различных архитектур.
Мы постарались максимально использовать возможности NGINX для создания простого HTTP-сервера. Вы можете задать другие настройки, например отключив использование Legacy библиотеки GeoIP и/или ассинхронных файловых операций (--with-file-aio), о нюансах которых, в системе на базе ядра Linux, можно почитать в статье Делаем асинхронность асинхронной, разбираемся в планировщике Go, ругаем Linux.