Path: | docs/CmdLineHandler |
Last Update: | Mon Jun 04 14:34:35 +0400 2007 |
Изначально планировалось, что все необходимые для кодогенерации параметры будут задаваться в DSL-скриптах. Поэтому RuCodeGen сам брал на себя разбор аргументов командной строки. Но со временем выяснились ситуации, когда результат работы генератора кода может зависеть от дополнительных условий. Например, можно либо сгенерировать C++ код с отладочными печатями или без них. Управлять такими признаками в DSL-скриптах не очень удобно. Гораздо удобнее указывать подобные условия в командной строке. Но для этого генераторам кода нужно иметь возможность вмешиваться в работу с аргументами командной строки.
Такая возможность была добавлена в версии 0.3.0 с помощью класса RuCodeGen::OptionHandler и методов RuCodeGen::RunMode::add_option_handler и RuCodeGen::RunMode::options.
Описывающий кодогенерацию DSL запускается обычным образом. В конце работы DSL запускается код RuCodeGen, который разбирает аргументы командной строки. После чего RuCodeGen запускает все зарегистрированные DSL-скриптом генераторы.
Во время своей работы DSL-скрипт может зарегистрировать в RuCodeGen набор собственных объектов, отвечающих за обработку необходимых генераторам аргументов командной строки. Каждый такой объект должен реализовывать интерфейс RuCodeGen::OptionHandler.
Когда RuCodeGen приступает к разбору аргументов командной строки, он создает объект OptionParser для работы с командной строкой и объект OpenStruct для накопления результатов разбора. Каждому объекту OptionHandler дается возможность зарегистрировать в OptionParser необходимые ему аргументы и сохранить в OpenStruct значения по умолчанию. После чего RuCodeGen выполняет разбор командной строки. После разбора каждому OptionHandler предоставляется возможность проверить корректность полученных значений в объекте OpenStruct.
После этого, если не произошло каких-либо ошибок, результирующий объект OpenStruct становится доступен генераторам с помощью метода RuCodeGen::RunMode::options.
Для создания собственного OptionHandler достаточно унаследоваться от класса RuCodeGen::OptionHandler и переопределить метод OptionHandler#setup.
Например, пусть необходимо обрабатывать дополнительные аргументы —debug-logs и —skip-comments. Для этого создается следующий OptionHandler:
module ComplexGenerator class OptionHandler < RuCodeGen::OptionHandler def setup( parser, result ) # По умолчанию не нужно добавлять отладочные печати. result.complexgen_debug_logs = false # По умолчанию не нужно удалять комментарии. result.complexgen_skip_comments = false parser.on( '--debug-logs', 'generate gebug output' ) do result.complexgen_debug_logs = true end parser.on( '--skip-comments', 'skip comments in the result code' ) do result.complexgen_skip_comments = true end end end end RuCodeGen::RunMode.add_option_handler( ComplexGenerator.OptionHandler.new )
Такого объекта OptionHandler уже достаточно. При запуске DSL-скрипта с ключем —help будет выдано описание дополнительных аргументов —debug-logs и —skip-comments.
Если же по ключу —help следует выдавать пользователю дополнительную информацию, то в OptionHandler необходимо переопределить метод banner:
module ComplexGenerator class OptionHandler < RuCodeGen::OptionHandler def banner "Complex generator.\n" + "Copyright (C) 2007 ...\n" + "For addition information please visit http://...\n" end ... end end RuCodeGen::RunMode.add_option_handler( ComplexGenerator.OptionHandler.new )
RuCodeGen аккумулирует все банеры из всех зарегистрированных объектов OptionHandler и отображает затем один общий банер перед выводом информации о доступных аргументах командной строки.
Если аргументы нуждаются в дополнительной проверке, то необходимо переопределить метод check. Например, пусть есть аргументы —gen-high-speed-code и —debug-logs, которые нельзя использовать совместно. Диагностировать такую ситуацию и прервать генерацию можно следующим образом:
module ComplexGenerator class OptionHandler < RuCodeGen::OptionHandler def setup( parser, result ) # По умолчанию генерируется обычный код. result.complexgen_gen_high_speed_code = false # По умолчанию не нужно добавлять отладочные печати. result.complexgen_debug_logs = false ... parser.on( '--gen-high-speed-code', 'generate processor dependent, high speed code' ) do result.complexgen_gen_high_speed_code = true end parser.on( '--debug-logs', 'generate gebug output' ) do result.complexgen_debug_logs = true end ... end def check( result ) fail '--gen-high-speed-code and --debug-logs cannot ' + 'be used together' if result.complexgen_gen_high_speed_code && result.complexgen_debug_logs end end end RuCodeGen::RunMode.add_option_handler( ComplexGenerator.OptionHandler.new )
Собственный OptionHandler должен быть зарегистрирован до того, как RuCodeGen получит управление. Поэтому лучше всего делать это сразу после определения всех специфических для генерации классов и объектов:
module ComplexGenerator class OptionHandler < RuCodeGen::OptionHandler ... end class Description ... end class Generator ... end end RuCodeGen::RunMode.add_option_handler( ComplexGenerator.OptionHandler.new )
Таким образом, ComplexGenerator::OptionHandler будет зарегистрирован в RuCodeGen сразу же, как только в DSL-скрипте будет загружено описание модуля ComplexGenerator.
Получение доступа к собственным аргументам командной строки возможно через метод RuCodeGen::RunMode.options. Но здесь есть одна важная особенность: этот метод будет порождать исключения, если он вызывается до того, как RuCodeGen сможет разобрать аргументы командной строки. Т.е. нельзя обращаться к RuCodeGen::RunMode.options во время работы DSL-скрипта.
Это означает, что в конце работы DSL-скрипта, когда производится создание и регистрация в RuCodeGen генераторов, опции командной строки недоступны. Следовательно, приведенный ниже код работать не будет:
def complexgen_dsl( &blk ) data = ComplexGenerator::Description.new blk[ data ] RuCodeGen::Generators.add( RuCodeGen::FilenameProducer.produce( $0, data.get_file ), # Ошибка!!! Опции командной строки еще недоступны. ComplexGenerator.Generator.new( data, RuCodeGen::RunMode.options.complexgen_gen_high_speed_code, RuCodeGen::RunMode.options.complexgen_debug_logs, RuCodeGen::RunMode.options.complexgen_skip_comments ) ) end
Вместо этого доступ к аргументам командной строки нужно осуществлять внутри метода generate:
module ComplexGenerator ... class Generator def generate( to ) if RuCodeGen::RunMode::options.complexgen_gen_high_speed_code ... elsif RuCodeGen::RuCodeGen::options.complexgen_debug_logs ... end end end end
Пример определения и использования собственного OptionHandler можно увидеть в файле cmd_line_handler/generator