Tabla de Contenidos
Generador de archivo OIL
Qué es OIL? (Por si no está en otro lado)
*) xxx El propósito del generador es producir archivos de encabezados .h y .c a partir de unos templates de C con php embebido que se ejectua según unos archivos configuración .oil y .oilx.
Arquitectura
Diagrama de clases
+---+ |Log| +---+ ^ | +------------+ +------+ |OilGenerator|--------->|Helper| +------------+ +------+ | | | v | +---------+ +---------+ | |OilConfig|------>|OilParser| | +---------+ +---------+ | | +------------+ +-->|OutputWriter| +------------+ A (A = Herencia) | (^v<> = Uso o composición) +------+-------+ | | +----------+ +------------+ |FileWriter| |StdoutWriter| +----------+ +------------+
Diagrama de componentes
+-----------+ +----------+ +-------------+ |+------------+ |+-----------+ |+--------------+ ||+-------------+ ||+------------+ ||+---------------+ +|| configs | +|| helpers | +|| templates | +|(.oil|.olix) | +| (.php) | +|(.c.php|.h.php)| +-------------+ +------------+ +---------------+ | | | +----------------+ | +-----------------+ | | | v v v +--------------+ +------------+ | command line |------->| generator | | options and | +------------+ | defines | | +--------------+ v +----------+ |+-----------+ +|+------------+ +| salidas | | (.h|.c) | +------------+
Detalles
generator.php contruye el objeto OilGenerator dándole un tipo de salida.
OilGenerator procesa los argumentos, verifica la existencia de las entradas y salidas, carga los helpers declarados, parsea los oil/oilx e incluye cada template, que utilizando los métodos disponibles genera cada archivo objetivo, según estas reglas de nombres (completar)
Interrelación de templates
La implementación original permitía la comunicación directa entre templates, o sea, estos se cargaban en el mismo scope.
clase OilGenerator metodo run() \ para cada template en templates | activar buffer de salida + scope original evaluar template | salvar buffer de salida /
Según #386 y la cadena “Consulta acerca de aislación de templates de OilGenerator”, la nueva implementación encapsulará las llamadas impidiendo efectos colaterales entre templates.
clase OilGenerator metodo run() para cada template en templates activar buffer de salida llamada a metodo de objeto encapsulador evaluar template } scope actual salvar buffer de salida
Si hubiera necesidad concreta de que haya alguna comunicación, implementaremos algún mecanismo explícito.
Con respecto a la invocación de helpers hay permanentes y efímeros. Los primeros son los cargados por linea de comando disponibles para todos los templates. Los efímeros los que se cargan en cada template.
Carga efímera de Helper con invocación en el template:
$this->loadHelper("modules/rtos/gen/ginc/HelperX.php");
Carga permanente por linea de comando:
-H modules/rtos/gen/ginc/HelperX.php
La inclusión de helpers brinda acceso a los métodos de un objeto Helper:
$this->helper->multicore->getLocalList("/OSEK", "TASK");
Aún no está definido el comportamiento ante múltiples cargas del mismo Helper. El resultado es que cada carga pisa a la anterior y podría ser el comportamiento final deseado. Veremos.
Para la migración hay que estar atentos a la aparición en el log de mensajes de warning o error referidos a variables indefinidas como $os, $priority y $tasks. Se soluciona copiando del template anterior que funcione y tenga las llamadas tipo
$tasks = $this->helper->multicore->getLocalList("/OSEK", "TASK");
Por ahora hay que repetir en cada template todas las llamadas. Si surge un patrón de uso, veremos de refactorizar.
Por el momento no nos preocuparemos por la falta de performance de esta solución, ya que no es una operación frecuente.
Ciclo de vida de un Helper
El ciclo sería primero definir las funciones propias de un template en el mismo. Si hay oportunidad de reutilización, va a un Helper local al módulo o proyecto. Si su utilidad es transversal, pasaría a … la carpeta de Helpers del generador o alguna ubicación común a definir.
TODO: los Helpers deben proveer un método de ayuda e implementar en algún lado que ante … –help-helpers se exploren los Helpers y se muestre esa ayuda. Esto implica que debe existir la clase AbstractHelper.
Opciones
Las opciones se pueden consultar con
php modules/rtos/generator/generator.php --help php generator.php [-l] [-h] [--cmdline] \ [-b path]\ [-Ddef[=definition]] \ -c <CONFIG_1> [<CONFIG_N>] \ -o <OUTPUTDIR> \ -t <TEMPLATE_1> [<TEMPLATE_N>] \ -[ -H <HELPER_1> [HELPER_N]] -c indicate the configuration input files -o output directory -t indicates the templates to be processed optional parameters: -b relative base path (default "/templates/") -H load helpers -h display this help -l displays a short license overview -D defines --cmdline print the command line
StdoutWriter existe a fines de testeo y si mal no recuerdo, no funciona bien por el hecho de estar usando ob_*.
Elección del lenguaje
Se ha elegido php por que aunque es un lenguaje completo, está pensado para templating. Se pudo haber usado django, twig o similares, pero en esos casos había que incorporar un lenguaje y un sistema de templating.
Los programas php estaban pensados originalmente para intercalar código con el formato html, al igual que jsp (no?). Esto viene muy bien pues permite escribir archivos .h o .c con partes estáticas y resolver mediante código las partes dinámicas, provenientes de los .oil con ayuda de los helpers.
Una particularidad muy especial es que existen funciones de buffering (ob_*) para manipular el stdout, lo que permite intercalar código C estático con el código embebido en php con extrema sencillez.
Estas piezas de código, se ejecutan en el contexto de una llamada a un método de un objeto OilGenerator, que previamente ha cargado la configuración y mediante una serie de métodos disponibles xxxxx
Testing
Se está utilizando test unitario con phpunit y funcional con shunit…
¿Cómo hago para...
... hacer un template?
Copiar el ejemplo para tener la licencia
El template consta de dos partes intercaladas, las estáticas y las dinámicas
Las estáticas son las que van de modo incondicional y fijo, como la licencia.
Las dinámicas son las que dependen de distintos factores variables como:
- Configuración OIL
- Defines pasados por linea de comando
Lo contenidos dinámicos pueden ser tan sencillos como mostrar un define:
<?
la … de un componente
…
o la …
…
Los loops tienen contadores implícitos que se pueden aprovechar para simplificar el código.
foreach ($coleccion as $contador=>$elemento) { print("elemento: " . $elemento->nombre . " posición: " . $contador); }
producirá:
elemento nombreUno posición: 0 elemento nombreDos posición: 1 elemento nombreTres posición: 2
y es equivalente a:
$contador = 0; foreach ($coleccion as $elemento) { print("elemento: " . $elemento->nombre . " posición: " . $contador++); }
o tambien a
for($contador = 0; $contador < $coleccion.size(); $contador++) { print("elemento: " . $elemento->nombre . " posición: " . $contador++); }
Los recursos disponibles en el contexto del template son
- log
- config
- helper
y se acceden mediante
$this->
en particular, la clase Log provée:
$this->log->info($msg); $this->log->warn($msg); $this->log->error($msg);
En caso de cancelar, por ahora hay que tirar una excepción usando la OilGeneratorException
throw new OilGeneratorException($msg, $exitCode);