Principle

Path: docs/Principle
Last Update: Mon Jun 04 14:34:35 +0400 2007

Принципы работы RuCodeGen

Типовой сценарий использования RuCodeGen

RuCodeGen расчитан на использования языка Ruby в качестве DSL (Domain Specific Language) для описания какой-либо кодогенерации. Это означает, что тип и параметры кодогенерации задаются в виде Ruby-скрипта. Этот скрипт затем запускается на выполнение (как обычный Ruby-скрипт) и в результате получается один или несколько результирующих файлов со сгенерированным кодом.

Ниже приведен сценарий использования шаблона RuCodeGen::ValueIncapsulator.

Создание скрипта с описанием кодогенерации

В скрипте для генерации C++ класса с использованием RuCodeGen::ValueIncapsulator необходимо сделать две вещи:

  1. Подгрузить Gem RuCodeGen.
  2. Создать описание для генерации C++ класса.

Например:

        # Загрузка Gem RuCodeGen.
        require 'rubygems'
        require_gem 'RuCodeGen'

        # Описание для кодогенерации.
        cpp_value_incapsulator :host_config_t do |c|
                c.decl_file :script_relative => "host_config.impl.hpp"
                c.impl_file :script_relative => "host_config.impl.cpp"

                c.attr_prefix "m_"

                c.attr :host, "std::string", :default => "localhost"
                c.attr :post, "short", :default => 8080
        end

Пусть приведенный код размещается в файле src/cg-host_config.rb.

Запуск скрипта на выполнение

Приведенный выше скрипт будет генерировать два файла: src/host_config.impl.hpp и src/host_config.impl.cpp. Для этого всего лишь необходимо запустить скрипт src/cg-host_config.rb:

        $ruby src/cg-host_config.rb

Далее полученные файлы нужно включить в С++ проект.

Режимы работы RuCodeGen

По умолчанию, скрипт с описанием кодогенерации выполняет кодогенерацию. Но это всего лишь один из возможных режимов его работы. Чтобы узнать остальные возможные режимы, нужно запустить скрипт с аргументом —help:

        $ruby src/cg-host_config.rb --help
        Usage: cg-host_config [options]
                 -m, --mode MODE                  run mode
                                                  (build, rebuild, clean, dry-run)
                 -h, --help                       show this screen

Если аргумент —mode не задан, то подразумевается, что скрипт запускается с параметром —mode build. Но, указав аргумент —mode в командной строке явно, можно выбрать любой из поддерживаемых режимов:

build
выполнить кодогенерацию. Реально кодогенерация производится только с момента последней генерации изменился скрипт с описанием или был удален какой-то из генерированных ранее файлов.
rebuild
выполнить принудительную перегенерацию, даже если с момента последней генерации ничего не удалялось и не изменялось.
clean
удалить все результаты предыдущей кодогенерации.
dry-run
ничего не генерировать. Может быть полезен для проверки синтаксической корректности скрипта с описанием параметров кодогенерации.

Внутренняя кухня

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

Идея RuCodeGen состоит в том, чтобы для каждого типа кодогенерации был создан свой шаблон (как, например, RuCodeGen::ValueIncapsulator). Скрипты с описанием параметров кодогенерации использовали бы возможности конкретного шаблона. Роль RuCodeGen в таком случае заключалась бы в следующем:

  1. Предоставить шаблонам готовые механизмы для выполнения элементарных операций (таких как проверка существования результатов предыдущей кодогенерации, инициирование новой кодогенерации, обработка аргументов командной стороки и пр.).
  2. Быть хранилищем наиболее частоиспользуемых и универсальных кодогенераторов.

В качестве базового фреймворка для поддержки кодогенерации RuCodeGen требует, чтобы каждый шаблон в результате своей работы формировал один или несколько объектов-генераторов и регистрировал их в классе RuCodeGen::Generators. Пока каждый генератор должен быть связан с уникальным именем результирующего файла (т.е. на данный момент нельзя сделать так, чтобы несколько генераторов перезаписывали один и тот же файл).

Генераторы должны поддерживать следующий интерфейс:

        def generate(to) ... end

где в качестве аргумента to будет передаваться объек-поток, в который нужно записывать результаты генерации.

Примерами готовых генераторов являются классы RuCodeGen::ValueIncapsulator::Generation::DeclGenerator и RuCodeGen::ValueIncapsulator::Generation::ImplGenerator.

Инициирование кодогенерации

Если скрипт с описанием кодогенерации запускается в режиме build, то генерация выполняется при завершении работы скрипта следующим образом:

  1. Проверяется наличие файла с контрольной суммой описания. Например, если описание кодогенерации находится в файле src/cg-host_config.rb, то его контрольная сумма должна находится в файле src/.cg-host_config.rb.md5.
  2. Если файла с контрольной суммой нет, то выполняется полная перегенерация. Даже если само описание не изменялось и существуют все результаты предыдущей кодогенерации.
  3. Если файл с контрольной суммой есть, но его содержимое не совпадает с контрольной суммой скрипта с описанием кодогенерации, то выполняется полная перегенерация (т.к. считается, что изменились параметры генерации).
  4. Если контрольная сумма скрипта с описанием кодогенерации осталась неизменной, то проверяется наличие файлов-результатов кодогенерации. В этом случае генерация запускается только для отсутствующих файлов.

Если скрипт с описанием кодогенерации запускается в режиме clean, то удаляются все файлы с результатами кодогенерации и файл с контрольной суммой.

Расположение результатов кодогенерации

В принципе, каждый кодогенерирующий шаблон может применять собственные правила расположения сгенерированных им файлов. RuCodeGen предоставляет класс RuCodeGen::FilenameProducer, который помогает формировать имена для трех типовых ситуаций:

  • когда в качестве результирующего имени указывается абсолютное имя файла. В этом случае никаких преобразований над указанным именем выполнять не нужно;
  • когда указывается имя, заданное относительно текущего каталога. В этом случае формируется полное имя, в которое добавляется имя текущего каталога;
  • когда указывается имя, заданое относительно скрипта с описанием кодогенерации. В этом случае формируется полное имя с учетом расположения исходного скрипта.

Например, в приведенном выше примере с src/cg-host_config.rb, имена результирущих файлов заданы относительно скрипта cg-host_config.rb. Поэтому, скажем, если скрипт находится в каталоге ~/sandboxes/some_prj/src, а текущим каталогом является ~/sandboxes, то запуск кодогенерации:

        ruby some_prj/src/cg-host_config.rb

приведет к созданию файлов ~/sandboxes/some_prj/src/host_config.impl.hpp и ~/sandboxes/some_prj/src/host_config.impl.cpp.

[Validate]

Hosted by uCoz