Лень — двигатель прогресса. Да, работая в программировании уже второй десяток лет, я до сих пор согласен с этим тезисом. Но в каждой шутке, как известно, есть доля шутки.
В данной статье речь пойдет о том, как заставить компьютер писать рутинный код за вас. Причём максимально автоматизировать этот процесс и интегрировать со сборкой проекта. Во всём этом нам поможет qmake
Зачем это нужно
В текущем моем проекте возникла необходимость применить объектный подход при работе с данными, физически хранящимися в реляционной БД. Стало быть ORM. Так уж вышло, что данный проект корпоративного сегмента разрабатывается на Qt. Да, хоть это и прекраснейший фреймворк, но не вполне подходящий под задачи программирования сложных корпоративных приложений. Тем не менее, выбор в пользу Qt был сделан по ряду весьма веских причин.
Итак, есть небольшая реляционная БД из примерно 100 таблиц. Необходимо написать тонкий пока слой бизнес-логики, который в перспективе будет обрастать жирком. Имеется описание структуры БД в XML.
Да, можно засучить рукава и за неделю написать 100 однотипных классов бизнес-логики. Написать многочисленные тесты, сравнивающие эти классы, XML-описание метаданных и саму структуру БД. Но, это не подход настоящего джедая! Действительно, ведь у нас уже есть все необходимое для описания прототипов классов бизнес-логики, просто нужно превратить .XML в .h.
Генератор кода
Для начала разберёмся с XML. Описание таблиц выглядит примерно так:
<table name="TestTable" > <field name="id" type="integer" /> <field name="name" type="VARCHAR(100)" /> </table>
Можно конечно при помощи того же 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