Одной из крайне интересных фич, внедрённых в gcc 4.5 стала возможность создания плагинов для компилятора. К сожалению, в сети пока очень мало информации, описывающей данную возможность, а на русском языке её нет совсем. Что ж, постараюсь исправить эту ситуацию.
Перед началом хотелось бы указать небольшой (больше просто не нашёл) список статей, касающихся данной темы:
Для начала уточним, где на конкретной системе находятся заголовочные файлы с плагинами:
круто, в нашей системе у gcc реально есть плагины ;) Каждый плагин должен подключать заголовочный файл gcc-plugin.h. В нём описаны структуры, содержащие информацию о плагине, а также объявлены все основные функции.
Обязательной для любого плагина является функция plugin_init. Она вызывается сразу после загрузки плагина. Эта функция отвечает за инициализацию данных плагина, а также за регистрацию всех callback вызовов, которые будут применены в ходе его работы.
Ещё одним обязательным условием работы плагина является его совместимость с лицензией GPL. Да, да, именно обязательным условием, без этого gcc просто откажется с ним работать. Чтобы подтвердить свою приверженность идеям свободного ПО, нужно в плагине объявить глобальную переменную plugin_is_GPL_compatible. Теперь у нас есть всё что необходимо для создания минимального плагина. Вот его код:
Как видим, всё не так страшно. Осталось скомпилировать и применить. Делается это очень просто. Сначала создаём разделяемую библиотеку:
Затем при компиляции программы показываем gcc, что стоит использовать данный плагин:
Программа main.c вполне корректно компилируется и работает. Но всё таки осталось ощущение неуверенности. А вдруг плагин не отработал? Что ж, справедливый вопрос. Чтобы развеять эти сомнения, надо добавить вывод какой-нибудь информации. Как Вы могли заметить, на вход инициализирующей функции подаётся 2 аргумента с информацией о плагине и о самом gcc. Как раз то, что нужно. Добавим в нашу функцию следущие строки:
Перекомпиливаем:
Да, оно действительно работает! Осталось только нанести последние штрихи. А именно заполнить информацию о версии и справку:
Теперь, если перекомпилировать плагин, он будет выводить всё корректно. Вот его итоговый код:
Перед началом хотелось бы указать небольшой (больше просто не нашёл) список статей, касающихся данной темы:
- документация gcc, описывающая построение плагинов
- цикл статей по парсингу C++ при помощи плагинов gcc
- введение в создание плагинов gcc
Для начала уточним, где на конкретной системе находятся заголовочные файлы с плагинами:
$ gcc -print-file-name=plugin
/usr/lib/gcc/i686-pc-linux-gnu/4.5.3/plugin
круто, в нашей системе у gcc реально есть плагины ;) Каждый плагин должен подключать заголовочный файл gcc-plugin.h. В нём описаны структуры, содержащие информацию о плагине, а также объявлены все основные функции.
Обязательной для любого плагина является функция plugin_init. Она вызывается сразу после загрузки плагина. Эта функция отвечает за инициализацию данных плагина, а также за регистрацию всех callback вызовов, которые будут применены в ходе его работы.
Ещё одним обязательным условием работы плагина является его совместимость с лицензией GPL. Да, да, именно обязательным условием, без этого gcc просто откажется с ним работать. Чтобы подтвердить свою приверженность идеям свободного ПО, нужно в плагине объявить глобальную переменную plugin_is_GPL_compatible. Теперь у нас есть всё что необходимо для создания минимального плагина. Вот его код:
#include "gcc-plugin.h"
int plugin_is_GPL_compatible;
int plugin_init (struct plugin_name_args *plugin_info,
struct plugin_gcc_version *version )
{
return 0;
}
Как видим, всё не так страшно. Осталось скомпилировать и применить. Делается это очень просто. Сначала создаём разделяемую библиотеку:
gcc -I`gcc -print-file-name=plugin`/include -fPIC -shared plugin.c -o plugin.so
Затем при компиляции программы показываем gcc, что стоит использовать данный плагин:
gcc -fplugin=./plugin.so main.c
$ ./a.out
Hello, world
Программа main.c вполне корректно компилируется и работает. Но всё таки осталось ощущение неуверенности. А вдруг плагин не отработал? Что ж, справедливый вопрос. Чтобы развеять эти сомнения, надо добавить вывод какой-нибудь информации. Как Вы могли заметить, на вход инициализирующей функции подаётся 2 аргумента с информацией о плагине и о самом gcc. Как раз то, что нужно. Добавим в нашу функцию следущие строки:
printf("Plugin info:\n");
printf("Plugin short name (filename without .so suffix): %s\n", plugin_info->base_name);
printf("Path to the plugin as specified with -fplugin=: %s\n", plugin_info->full_name);
printf("Number of arguments specified with -fplugin-arg-...: %d\n", plugin_info->argc);
printf("Version string provided by plugin: %s\n", plugin_info->version);
printf("Help string provided by plugin: %s\n", plugin_info->help);
printf("\nGCC info:\n");
printf("Base version: %s\n", version->basever);
printf("Datestamp: %s\n", version->datestamp);
printf("Devphase: %s\n", version->devphase);
printf("Revision: %s\n", version->revision);
printf("Configuration arguments: %s\n", version->configuration_arguments);
Перекомпиливаем:
$ gcc -I`gcc -print-file-name=plugin`/include -fPIC -shared plugin.c -o plugin.so
$ gcc -fplugin=./plugin.so main.cpp
Plugin info:
Plugin short name (filename without .so suffix): plugin
Path to the plugin as specified with -fplugin=: ./plugin.so
Number of arguments specified with -fplugin-arg-...: 0
Version string provided by plugin: (null)
Help string provided by plugin: (null)
GCC info:
Base version: 4.5.3
Datestamp: 20110428
Devphase:
Revision:
Configuration arguments: /var/tmp/portage/sys-devel/gcc-4.5.3-r2/work/gcc-4.5.3/configure --prefix=/usr --bindir=/usr/i686-pc-linux-gnu/gcc-bin/4.5.3 --includedir=/usr/lib/gcc/i686-pc-linux-gnu/4.5.3/include --datadir=/usr/share/gcc-data/i686-pc-linux-gnu/4.5.3 --mandir=/usr/share/gcc-data/i686-pc-linux-gnu/4.5.3/man --infodir=/usr/share/gcc-data/i686-pc-linux-gnu/4.5.3/info --with-gxx-include-dir=/usr/lib/gcc/i686-pc-linux-gnu/4.5.3/include/g++-v4 --host=i686-pc-linux-gnu --build=i686-pc-linux-gnu --disable-altivec --disable-fixed-point --with-ppl --with-cloog --disable-ppl-version-check --with-cloog-include=/usr/include/cloog-ppl --disable-lto --enable-nls --without-included-gettext --with-system-zlib --disable-werror --enable-secureplt --disable-multilib --enable-libmudflap --disable-libssp --enable-libgomp --with-python-dir=/share/gcc-data/i686-pc-linux-gnu/4.5.3/python --enable-checking=release --disable-libgcj --with-arch=i686 --enable-languages=c,c++,fortran --enable-shared --enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu --enable-targets=all --with-bugurl=http://bugs.gentoo.org/ --with-pkgversion='Gentoo 4.5.3-r2 p1.0, pie-0.4.6'
Да, оно действительно работает! Осталось только нанести последние штрихи. А именно заполнить информацию о версии и справку:
plugin_info->version = "0.01";
plugin_info->help = "This plugin does nothing\n";
Теперь, если перекомпилировать плагин, он будет выводить всё корректно. Вот его итоговый код:
#include "gcc-plugin.h"
#include <stdio.h>
int plugin_is_GPL_compatible;
int plugin_init (struct plugin_name_args *plugin_info,
struct plugin_gcc_version *version )
{
plugin_info->version = "0.01";
plugin_info->help = "This plugin does nothing\n";
printf("Plugin info:\n");
printf("Plugin short name (filename without .so suffix): %s\n", plugin_info->base_name);
printf("Path to the plugin as specified with -fplugin=: %s\n", plugin_info->full_name);
printf("Number of arguments specified with -fplugin-arg-...: %d\n", plugin_info->argc);
printf("Version string provided by plugin: %s\n", plugin_info->version);
printf("Help string provided by plugin: %s\n", plugin_info->help);
printf("\nGCC info:\n");
printf("Base version: %s\n", version->basever);
printf("Datestamp: %s\n", version->datestamp);
printf("Devphase: %s\n", version->devphase);
printf("Revision: %s\n", version->revision);
printf("Configuration arguments: %s\n", version->configuration_arguments);
return 0;
}