Генерация документации для расширений PHP с использованием Doxygen
Любимые разработчиками PHP макросы и их уровень вложенности зачастую оказываются плохо совместимыми с системой документирования исходных текстов Doxygen.
В зависимости от настроек препроцессора Doxygen (в частности, директивы SKIP_FUNCTION_MACROS
) отдельные блоки кода могут быть вообще пропущены; например, в коде:
STD_PHP_INI_BOOLEAN("chuid.disable_posix_setuid_family", "1", PHP_INI_SYSTEM, OnUpdateBool, disable_setuid, zend_chuid_globals, chuid_globals)
STD_PHP_INI_BOOLEAN("chuid.never_root", "1", PHP_INI_SYSTEM, OnUpdateBool, never_root, zend_chuid_globals, chuid_globals)
STD_PHP_INI_BOOLEAN("chuid.cli_disable", "1", PHP_INI_SYSTEM, OnUpdateBool, cli_disable, zend_chuid_globals, chuid_globals)
STD_PHP_INI_BOOLEAN("chuid.be_secure", "1", PHP_INI_SYSTEM, OnUpdateBool, be_secure, zend_chuid_globals, chuid_globals)
STD_PHP_INI_ENTRY("chuid.default_uid", "65534", PHP_INI_SYSTEM, OnUpdateLong, default_uid, zend_chuid_globals, chuid_globals)
STD_PHP_INI_ENTRY("chuid.default_gid", "65534", PHP_INI_SYSTEM, OnUpdateLong, default_gid, zend_chuid_globals, chuid_globals)
STD_PHP_INI_ENTRY("chuid.global_chroot", NULL, PHP_INI_SYSTEM, OnUpdateString, global_chroot, zend_chuid_globals, chuid_globals)
PHP_INI_END()
Блок PHP_INI_BEGIN()
…PHP_INI_END()
может быть рассмотрен как функциональный макрос и проигнорироваться Doxygen. Либо, если директива SKIP_FUNCTION_MACROS
установлена в No
, распознать декларации PHP_INI_BEGIN()
и ZEND_DECLARE_MODULE_GLOBALS()
как функции.
У меня не получилось никаким настройками (кроме ручного задания соответствия макросов) заставить Doxygen развернуть макросы из zend_module_entry
или всякие PHP_MINIT_FUNCTION
.
Решение, которое я нашел, не идеально, но всё же лучше, чем ничего.
Применительно к расширениям PHP требуются следующие модификации:
- В
config.m4
добавляется строка[-]View Code TextPHP_ADD_MAKEFILE_FRAGMENT - Создаётся
Makefile.frag
следующего содержания:[-]View Code (Unknown Language)htmldocs: docs/html/index.html Doxyfile
docs/html/index.html: caps.h compatibility.h config.h helpers.h php_chuid.h caps.c chuid.c compatibility.c helpers.c Doxyfile macros.h
doxygen Doxyfile
macros.h: caps.c chuid.c compatibility.c helpers.c
$(CPP) $(COMMON_FLAGS) -dM $^ -o $@Вместо
docs/html/index.html
подставляется имя одного из генерируемых файлов документации (я предпочитаю HTML). Этот файл должен зависеть от всех исходных файлов проекта (по крайней мере тех, по которым генерируется документация),Doxyfile
(файл конфигурации Doxygen) и специального файлаmacros.h
.macros.h
должен иметь в зависимостях все файлы проекта, исключая заголовочные.Вся магия заключается в параметре
В результате Doxygen будет брать определения всех макросов из этого файла, и на выходе получится более-менее нормальный результат.-dM
препроцессора (работатьэто будет, видимо, только сgcc
), которая генерирует список объявлений#define
для всех макросов, определённых во время выполнения препроцессора; список включает также предопределённые макросы. - В директиве
INPUT
Doxygen я явно перечисляю все файлы проекта, при этом важно, чтобыmacros.h
шел первым. - Использование файла
macros.h
имеет один побочный эффект: в файле определяются все константы, защищающие заголовочные файлы от повторного включения. К счастью, проблема решается просто:
Такая конфигурация позволила сгенерировать вполне читаемую документацию.
Единственное «но»: всё же не все макросы раскрываются успешно: например, для самого первого примера получился такой код:
{ 0, (1<<2) , "chuid.disable_posix_setuid_family" , sizeof( "chuid.disable_posix_setuid_family" ), OnUpdateBool , (void *) ((long) (((char *) (&((( zend_chuid_globals * ) ((void *)0) )-> disable_setuid ))) - ((char *) ((void *)0) ))) , (void *) & chuid_globals , ((void *)0) , "1" , sizeof( "1" )-1, ((void *)0) , 0, 0, zend_ini_boolean_displayer_cb },
{ 0, (1<<2) , "chuid.never_root" , sizeof( "chuid.never_root" ), OnUpdateBool , (void *) ((long) (((char *) (&((( zend_chuid_globals * ) ((void *)0) )-> never_root ))) - ((char *) ((void *)0) ))) , (void *) & chuid_globals , ((void *)0) , "1" , sizeof( "1" )-1, ((void *)0) , 0, 0, zend_ini_boolean_displayer_cb },
{ 0, (1<<2) , "chuid.cli_disable" , sizeof( "chuid.cli_disable" ), OnUpdateBool , (void *) ((long) (((char *) (&((( zend_chuid_globals * ) ((void *)0) )-> cli_disable ))) - ((char *) ((void *)0) ))) , (void *) & chuid_globals , ((void *)0) , "1" , sizeof( "1" )-1, ((void *)0) , 0, 0, zend_ini_boolean_displayer_cb },
{ 0, (1<<2) , "chuid.be_secure" , sizeof( "chuid.be_secure" ), OnUpdateBool , (void *) ((long) (((char *) (&((( zend_chuid_globals * ) ((void *)0) )-> be_secure ))) - ((char *) ((void *)0) ))) , (void *) & chuid_globals , ((void *)0) , "1" , sizeof( "1" )-1, ((void *)0) , 0, 0, zend_ini_boolean_displayer_cb },
STD_PHP_INI_ENTRY("chuid.default_uid", "65534", (1<<2) , OnUpdateLong, default_uid, zend_chuid_globals, chuid_globals)
STD_PHP_INI_ENTRY("chuid.default_gid", "65534", (1<<2) , OnUpdateLong, default_gid, zend_chuid_globals, chuid_globals)
STD_PHP_INI_ENTRY("chuid.global_chroot", ((void *)0) , (1<<2) , OnUpdateString, global_chroot, zend_chuid_globals, chuid_globals)
{ 0, 0, ((void *)0) , 0, ((void *)0) , ((void *)0) , ((void *)0) , ((void *)0) , ((void *)0) , 0, ((void *)0) , 0, 0, ((void *)0) } }
Увы, Doxygen не смог раскрыть макрос STD_PHP_INI_ENTRY
. Пожалуй, это один недочёт во всей документации.
UPDATE: я только что нашёл другое решение: в Makefile.frag
вместо $(CPP) $(COMMON_FLAGS) -dM $^ -o $@
нужно поставить:
В результате получается файл меньшего размера, и этот файл Doxygen’у использовать проще, в результате чего макросы раскрываются лучше:
{ 0, (1<<2) , "chuid.disable_posix_setuid_family" , sizeof( "chuid.disable_posix_setuid_family" ), OnUpdateBool , (void *) ((long) (((char *) (&((( zend_chuid_globals * ) ((void *)0) )-> disable_setuid ))) - ((char *) ((void *)0) ))) , (void *) & chuid_globals , ((void *)0) , "1" , sizeof( "1" )-1, ((void *)0) , 0, 0, zend_ini_boolean_displayer_cb },
{ 0, (1<<2) , "chuid.never_root" , sizeof( "chuid.never_root" ), OnUpdateBool , (void *) ((long) (((char *) (&((( zend_chuid_globals * ) ((void *)0) )-> never_root ))) - ((char *) ((void *)0) ))) , (void *) & chuid_globals , ((void *)0) , "1" , sizeof( "1" )-1, ((void *)0) , 0, 0, zend_ini_boolean_displayer_cb },
{ 0, (1<<2) , "chuid.cli_disable" , sizeof( "chuid.cli_disable" ), OnUpdateBool , (void *) ((long) (((char *) (&((( zend_chuid_globals * ) ((void *)0) )-> cli_disable ))) - ((char *) ((void *)0) ))) , (void *) & chuid_globals , ((void *)0) , "1" , sizeof( "1" )-1, ((void *)0) , 0, 0, zend_ini_boolean_displayer_cb },
{ 0, (1<<2) , "chuid.be_secure" , sizeof( "chuid.be_secure" ), OnUpdateBool , (void *) ((long) (((char *) (&((( zend_chuid_globals * ) ((void *)0) )-> be_secure ))) - ((char *) ((void *)0) ))) , (void *) & chuid_globals , ((void *)0) , "1" , sizeof( "1" )-1, ((void *)0) , 0, 0, zend_ini_boolean_displayer_cb },
{ 0, (1<<2) , "chuid.default_uid" , sizeof( "chuid.default_uid" ), OnUpdateLong , (void *) ((long) (((char *) (&((( zend_chuid_globals * ) ((void *)0) )-> default_uid ))) - ((char *) ((void *)0) ))) , (void *) & chuid_globals , ((void *)0) , "65534" , sizeof( "65534" )-1, ((void *)0) , 0, 0, ((void *)0) },
{ 0, (1<<2) , "chuid.default_gid" , sizeof( "chuid.default_gid" ), OnUpdateLong , (void *) ((long) (((char *) (&((( zend_chuid_globals * ) ((void *)0) )-> default_gid ))) - ((char *) ((void *)0) ))) , (void *) & chuid_globals , ((void *)0) , "65534" , sizeof( "65534" )-1, ((void *)0) , 0, 0, ((void *)0) },
{ 0, (1<<2) , "chuid.global_chroot" , sizeof( "chuid.global_chroot" ), OnUpdateString , (void *) ((long) (((char *) (&((( zend_chuid_globals * ) ((void *)0) )-> global_chroot ))) - ((char *) ((void *)0) ))) , (void *) & chuid_globals , ((void *)0) , ((void *)0) , sizeof( ((void *)0) )-1, ((void *)0) , 0, 0, ((void *)0) },
{ 0, 0, ((void *)0) , 0, ((void *)0) , ((void *)0) , ((void *)0) , ((void *)0) , ((void *)0) , 0, ((void *)0) , 0, 0, ((void *)0) } }
Если читабельность кода приоритетнее, чем точность подстановок, то вместо -dD
следует использовать -dU
. Опять же, это рекомендация, полученная из экспериментов.