CmdLineHandler

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

Для создания собственного 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

Собственный 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

[Validate]

Hosted by uCoz