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

в 21:00, , рубрики: 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 TestClass { 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'ы защиты от повторного включения хедера и после корректировки шаблона ВНЕЗАПНО конфиг заработал и стал исправно выдавать сгенерированный хедер. Я в чудеса не верю, списал это на четвертый час ночи. Но, если обнаружите нечто подобное — я вас предупредил.

Автор: ncix

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js