Qt
8 March 2012

Вставляем генератор кода в сборку qmake

Лень — двигатель прогресса. Работая в программировании уже второй десяток лет, я до сих пор согласен с этим тезисом. Но в каждой шутке, как известно, есть доля шутки.
В данной статье речь пойдет о том, как заставить компьютер писать рутинный код за вас. Причём максимально автоматизировать этот процесс и интегрировать со сборкой проекта. Во всём этом нам поможет qmake

Зачем это нужно

В текущем моем проекте возникла необходимость применить объектный подход при работе с данными, физически хранящимися в реляционной БД. Стало быть, ORM. Так уж вышло, что данный проект корпоративного сегмента разрабатывается на Qt. Да, хоть это и прекраснейший фреймворк, но не вполне подходящий под задачи программирования сложных корпоративных приложений. Тем не менее, выбор в пользу Qt был сделан по ряду весьма веских причин.
Итак, есть небольшая реляционная БД из примерно 100 таблиц. Необходимо написать тонкий пока слой бизнес-логики, который в перспективе будет обрастать жирком. Имеется описание структуры БД в XML.
Можно засучить рукава и за неделю написать 100 однотипных классов бизнес-логики. Написать многочисленные тесты, сравнивающие эти классы, XML-описание метаданных и саму структуру БД. Но, это не подход настоящего джедая! Действительно, ведь у нас уже есть все необходимое для описания прототипов классов бизнес-логики, просто нужно превратить .xml в .h.

Генератор кода

Для начала разберёмся с XML. Описание таблиц выглядит примерно так:
    &lttable name="TestTable" &gt
        &ltfield name="id" type="INTEGER" /&gt
        &ltfield name="name" type="VARCHAR(100)" /&gt
    &lt/table&gt

Можно конечно при помощи того же QDomDocument довольно быстро написать собственный препроцессор-генератор, но правильнее будет использовать XSL. Для XSLT-преобразований удобно использовать какой-нибудь консольный процессор. Я использовал xsltproc. Эта простая утилита берёт на вход XML, XSL и выводит получившийся текст куда скажут. Можно использовать другой инструмент, можно написать самому, это не принципиально. Используя нехитрый шаблон получаем
class TestTable
{
public:
    int id();
    QString name();
};

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

Автоматизация

Я работаю в QtCreator и хочу включить свои XML-ки с описанием структуры данных прямо в проект. Для файлов, не участвующих в стандартном процессе сборки есть раздел проекта «Другие файлы». В .pro-файле он, что характерно, называется OTHER_FILES. Но не будем мешать мух с котлетами, заведём сразу свою секцию. Добавим туда наше описание, а потом добавим свою секцию к «Другим файлам»:
ORM_FILES += classes.qoc
OTHER_FILES += $$ORM_FILES

Теперь начинается самое интересное. QMake — это невероятно навороченная штука. Одних ключевых слов описания pro-файлов там больше сотни. Одна из возможностей — QMAKE_EXTRA_COMPILERS. Это механизм указания в pro-файле правил запуска дополнительных компиляторов, препроцессоров и прочих интерпретаторов кода. Именно таким образом вызываются moc и uic. Оригинальное описание можно почерпнуть тут. Правда, оно довольно лаконичное. В нашем профайле должно быть дописано примерно следующее:
orm.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_IN_BASE}_qoc.h    #выходной файл
orm.input = ORM_FILES         #список входных файлов
orm.commands = xsltproc -o ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN}    #команда 
orm.variable_out = HEADERS    # куда добавить выходные файлы
orm.name = ORM    # имя(для внутренних целей qmake)

QMAKE_EXTRA_COMPILERS += orm

Можно попробовать собрать, на данном этапе всё уже должно работать.

Наводим порядок

Вышеприведенный кусок на самом деле напоминает .prf-файлы конфигурируации qmake, которые располагаются в /usr/share/qt4/mkspecs/features если вы используете Линукс. Когда мы пишем
CONFIG += xml 
например, подключается конфиг xml.prf. Таким образом можно вынести эти инструкции в файл orm.prf и положить его в указанную папку. Тогда для обработки наших XML-ек достаточно будет написать
CONFIG += orm


Вместо заключения

Теперь пару слов о возможных подводных камнях. Я практически сразу написал данный конфиг по мануалу, однако ничего не происходило. В консоли сборки не было запуска xsltproc и хедер не генерировался. Перепробовав разнобразнейшие варианты обнаружил, что xsltproc по крайней мере вызывается если написать orm.variable_out = SOURCES. Дальше обнаружил, что забыл в XSL-шаблоне написать #define'ы защиты от повторного включения хедера и после корректировки шаблона ВНЕЗАПНО конфиг заработал и стал исправно выдавать сгенерированный хедер. Я в чудеса не верю, списал это на четвертый час ночи. Но, если обнаружите нечто подобное — я вас предупредил.

+20
5.1k 46
Comments 16
Top of the day