Pages

Monday, November 21, 2011

Problemas técnicos con CMake y Bison++

En bison++ y flex++ no acaba la cosa. Con cmake también he tenido mis problemas, aunque esta vez relativos al nuevo bison++. Bison++ es en realidad una extensión de bison, y cuando se instala el primero, se sobreescribe el segundo. Como prueba, si se ejecuta la órden bison --version te devuelve el mismo resultado que bison++ --version, que no es ni más ni menos que la versión de bison++.

La diferencia es que si se usa bison++ para traducir un .y, se genera una macro, llamada YY_USE_CLASS que permite al preprocesador de C generar todo el código correspondiente a una clase, al contrario que si se usa bison, que no la genera y se selecciona el código correspondiente a funciones y variables globales, como de costumbre.

El juego de conflictos proviene del hecho de querer aprovechar el módulo de CMake, FindBISON.cmake, para no tener que hacer una búsqueda manual de paquetes. Así que me propuse trabajar con bison añadiendo manualmente la macro YY_USE_CLASS de igual forma que en el caso del post anterior:

%header{
// Defines para stype, véase post anterior.
#ifndef yy_CodeBisonParser_stype
#define yy_CodeBisonParser_stype YY_CodeBisonParser_stype
#endif

#ifndef YY_USE_CLASS_
#define YY_USE_CLASS_
#endif
%}

De esta forma todo funcionaba a la perfección. Pero había un problema que estaba metiendo las castañas en el fuego, y era que, al ejecutar "$cmake ." por segunda vez, o "$make", me daba un aviso indicando que en el fichero CMakeCache.txt había un error de una entrada inválida, que era precisamente la de la versión de bison++.

El origen del problema proviende del siguiente hecho, lo que requiere una pequeña explicación:

Cuando en tu CMakeFiles.txt incluyes una órden del tipo FIND_PACKAGE(<NOMBRE_PACKETE>), bison busca en su directorio de módulos (/usr/share/cmake-2.8/Modules en mi caso) el fichero Find<NOMBRE_PACKETE>.cmake y ejecuta el código que incluye, que busca el packete deseado, genera una serie de macros para que el usuario pueda usar a continuación (como BISON_TARGET) y actualiza las entradas de CMakeCache con los resultados de la búsqueda del paquete, y si ésta fue o no satisfactoria. Luego, cualquier nueva llamada a cmake o a make antes consulta CMakeCache para ver si todo es correcto antes de continuar.

Una de las tareas que realiza el módulo FindBISON.cmake es reconocer la versión actual del packete mediante una expresión regular. Resulta que la expresión regular contenida en FindBISON.cmake para detectar la versión está diseñada para detectar la versión de bison, y como bison++ sobreescribe al fichero de bison, la expresión regular no coincide con la devuelta por "$bison --version". Por tanto, introduce la salida completa de ésta instrucción y la copia en CMakeCache.txt.

Resulta, además, que CMakeCache.txt no soporta todavía valores multilinea en sus entradas, y la versión de bison --version (cuando se ha instalado bison++), arroja una salida multilinea, y de ahí el error producido.

Para solucionarlo, no he tenido más remedio que copiar el módulo FindBISON.cmake a una copia local, renombrarlo por FindBISONPP.cmake y hacer algunos cambios:

// FindBISONPP.cmake

// Comentarios iniciales.

// Sustituir en esta línea bison por bison++
FIND_PROGRAM(BISON_EXECUTABLE bison++ DOC "path to the bison++ executable")

IF(BISON_EXECUTABLE)
   // Dos intrucciones SET, una EXECUTE_PROCESS, otro SET, y un bloque IF-ELSE.
   // En la parte else, se ha sustituido la expresión regular de esta forma:
   STRING(REGEX REPLACE "^bison\\+\\+ Version ([^,]+).*" "\\1"
          BISON_VERSION "${BISON_version_output})

   // En la macro BISON_TARGET, casi al final, hay una sección ADD_CUSTOM_COMMAND.
   // Ahí, en el atributo COMMENT:
   COMMENT "[BISON++][${Name}] Building parser with bison++ ${BISON_VERSION}"
ENDIF(BISON_EXECUTABLE)

// Esto se cambia ya que ahora hemos cambiado de directorio y no 
// encontraría el directorio. Se modifica la línea a continuación.
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(BISON++ REQUIRED_VARS BISON_EXECUTABLE
       VERSION_VAR BISON_VERSION) 

Y así se arregla definitivamente el problema. Con el bison original también existe este problema. La órden "$bison --version" tiene una salida de unas cuantas líneas de versión + créditos. Si tienes la versión inglesa del paquete no hay ningún problema, ya que la expresión regular de FindBISON.cmake está configurada para detectar correctamente la versión. Pero con la versión española del paquete, en la salida se ha sustituido Bison por bison, y ya la expresión regular no encaja, repitiéndose el problema anterior. La solución consiste en realizar un cambio en su expresión regular. La versión original contiene:

"^bison \\(GNU Bison\\) ([^\n]+)\n.*" "\\1" 
Y hay que cambiarla, en tu copia local, por:
"^bison \\(GNU [Bb]ison\\) ([^\n]+)\n.*" "\\1"
Y nuevamente, se soluciona el problema. Por último, cuando en CMake se añade una órden FIND_PACKAGE, CMake busca módulos primero en su directorio de módulos (prsente en la variable CMAKE_MODULE_PATH), y si ahí no lo encuentra, busca en su directorio de isntalación, pero nunca en el directorio local. Para obligarle a buscar en el directorio local hay que hacer lo siguiente en CMake:
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR})

# Pongo como ejemplo mi módulo para bison++
FIND_PACKAGE(BISONPP REQUIRED)
Y definitivamente se arregla el problema. También puede pasar que el módulo de flex no reconozca bien la versión y capture la salida entera (aunque no da error porque la salida de flex --version no ocupa más de una línea).

El problema puede provenir del hecho que, sin que hayas instalado flex manualmente, la tengas instalada en tu sistema. Posiblemente algún paquete la ha instalado como dependencia, pero también puede haber instalado, en vez de flex, el paquete flex-old, cuya funcionalidad es casi idéntica pero la salida de la versión es distinta, de modo que la expresión regular del módulo FindFLEX.cmake no coincide con ella. Con instalar el paquete flex es suficiente, ya que se desistala automáticamente flex-old (al menos en ubuntu) y no hay conflictos por dependencias.

No comments:

Post a Comment