2009-11-26 17 views
11

Estoy escribiendo el código C para un sistema integrado (plataforma dsPIC33), y estoy considerando construir una biblioteca de códigos reutilizable para usar en múltiples proyectos.¿Mejores prácticas para C incrustado reutilizable?

¿Cuáles son las mejores prácticas para vincular la biblioteca a cada proyecto?

Obviamente, la biblioteca tendrá algunas dependencias específicas del hardware (y por lo tanto, proyectos específicos), por lo que es razonable suponer que se compilará con cada proyecto (en lugar de enlazarse en forma binaria).

Lo que he encontrado hasta ahora es mantener la biblioteca en una ubicación central, pero requiere una libraryConfig.h específica del proyecto que incluya definiciones de funciones, macros, etc. Esto requiere que la biblioteca incluya el encabezado en sí mismo código, lo que significa que el directorio fuente del proyecto deberá estar en la ruta de inclusión (no solo en el directorio de origen de la biblioteca). Ese tipo de errores en la distinción entre #include "" y #include <>, ¿no?

¿Es así como se hace normalmente?

Respuesta

7

Una muy buena pregunta y la respuesta no es simple. Varias cosas para considerar Aquí hay algunas opiniones de mi experiencia hasta el momento.

Código Común vs Proyecto-Local Copia

Una decisión importante es si utilizar código de la biblioteca "común" que se actualiza automáticamente desde una ubicación central ("biblioteca de reutilización" de su empresa), o si se debe mantener una copia del proyecto local.

Esto se discute en detalle en this SO question.

El beneficio de una biblioteca central es que el trabajo realizado una vez puede beneficiar a muchos proyectos. La dificultad con una copia de proyecto local es que las correcciones de errores y mejoras no se envían a la biblioteca central, y las correcciones de errores en la biblioteca central no se pueden incorporar a su proyecto.

Pero una dificultad potencial con el uso de una biblioteca central es si las personas en su particular lo modifican de forma descontrolada para adecuarse a su proyecto, y rompe involuntariamente otros proyectos. Lo he visto personalmente, en un código "común" que se llenó de #ifdefs y regularmente rompió otros proyectos.

Para obtener un buen valor de código común biblioteca también conocido como el centro de reutilización:

La biblioteca:

  • deben tener requisitos bien definidos, la API y las pruebas unitarias
  • deben evitar específica del proyecto código; debe ser de uso general
  • debe tener un mecanismo para establecer claramente la configuración específica del proyecto (esto se puede ver como parte de la API, efectivamente)
  • debe tener un proceso formal de publicación, con números de versión y arreglos, problemas debe ser rastreado.

proyectos individuales:

  • no deben recibir de forma automática y ciegamente "la última", sino que debe ser capaz de obtener una "liberación" en particular con un número de versión especificado. Entonces los proyectos deberían tener control sobre si/cuándo se actualizan a una versión más nueva. El proyecto debe poder seguir claramente, "estamos usando la versión 1.2.3 de la biblioteca xyz".
  • debe evitar "bifurcando" el código de la biblioteca si es posible. P.ej. evite agregar "características" específicas del proyecto al código de la biblioteca.
  • debe rastrear cualquier modificación local al código de la biblioteca
  • debe considerar los errores como errores de la biblioteca, para ser reparados en la biblioteca central si es posible. La empresa debería tener procesos para arreglarlos en la biblioteca central, probar la biblioteca con su propio conjunto de pruebas unitarias (probablemente mejorando las pruebas unitarias para detectar el error en el futuro). A continuación, libere una nueva versión de la biblioteca central, según sea necesario, e impleméntela en otros proyectos si estos proyectos lo consideran oportuno.

Si una empresa no tiene un proceso así, un proyecto debe hacer una copia local de un código (digamos, copiado de un proyecto anterior) y luego asumir la responsabilidad total del proyecto local a partir de entonces. Todavía estás obteniendo algún beneficio de la reutilización en esa situación, porque no estás reescribiéndolo desde cero.

proyecto referenciado configuración

Si el código necesita configuración específica del proyecto, lo ideal es que debe mantenerse a una parte tan pequeña del código como sea posible - no se dispersa a través de un montón de archivos de origen. Idealmente, un solo archivo de encabezado. Pero posiblemente también un archivo .C (por ejemplo, si necesita definir algunas tablas de búsqueda). La biblioteca debe proporcionar una plantilla, con las opciones bien comentadas.

Para obtener un buen ejemplo de cómo se puede hacer esto, consulte el µC/OS-II RTOS (book) de Jean Labrosse, en Micrium.

1

No ensucia la distinción, que de todos modos está casi completamente definida en la plataforma. El único comportamiento definido es que si un incluir utilizando "" no puede encontrar el archivo, entonces busca de nuevo como si dijera <>.

Creo que estás haciendo lo correcto. La forma normal de manejar un encabezado específico de la plataforma, en mi experiencia, es que le das un nombre con la mayor confianza posible para que nunca colisione con otra cosa, y #includeselo con "". Luego, le dice al portero de la plataforma que haga lo que sea necesario para compilar específicamente para asegurarse de que se encuentre. Normalmente eso solo significa especificar algún argumento del compilador como -I, para donde quiera guardar el archivo. Entonces sí, uno de los directorios de su proyecto. Pero si todo lo demás falla, siempre puede copiar su archivo en algún lugar donde se verá su compilador. Incluso podría copiarlo en su copia local de la fuente de su biblioteca, si su compilador es irrazonablemente difícil sobre todo el asunto.

Otra manera es tener un archivo en la biblioteca, selectplatform.h, con este aspecto:

// obviously WIN32 isn't an embedded platform, and GCC is too broad 
// to be supported by a single header file. Replace with whatever platforms 
// it is you do support out of the box. 
#if _WIN32 
    #include "platforms/msvc32.h" 
#elif __GNUC__ 
    #include "platforms/gcc.h" 
#else 
    #error "You must add a new clause to selectplatform.h for your platform" 
#endif 

Esto evita la necesidad de configuración del compilador, pero tiene la desventaja de que cada nueva plataforma puerto tiene que modificar el archivo. Si eres el único que hace porteos, eso definitivamente no es un problema. De lo contrario, un archivo será bifurcado por terceros. Entonces tal vez añadan un nuevo archivo al platforms/ en su biblioteca, o tal vez pongan su archivo en otro lugar. Entonces con terceros, es solo probablemente no hay problema. Pueden contribuir con sus cambios (posiblemente incluyendo el encabezado de su plataforma) nuevamente si ambos lo desean.

1

Normalmente se define una ruta al directorio de lib's includes mediante un indicador de comando en el compilador (por lo general, es -I indicador).

Es decir, si usted está empleando el compilador GCC, y los archivos de cabecera de su biblioteca está en el

/usr/local/include/mylibheaders 

entonces usted debe llamar compilador con opción siguiente:

-I/usr/local/include/mylibheader/mycurrentplatform 

donde mycurrentplatform directorio es diferente para cada proyecto y contiene proyectos específicos libraryConfig.h

Por lo tanto, puede usar #include<libraryConfig.h> en cada proyecto.

1

Esto es realmente más una cuestión de gestión de configuración que una pregunta en C. En mi experiencia, usar un buen programa de control de versiones es de gran ayuda. Encuentre uno que le permita definir un "proyecto" tirando del código fuente de varias ubicaciones diferentes. Tenga en cuenta que la definición de "proyecto" del programa de control de versiones se convertirá en un elemento esencial para la construcción del proyecto.

También es importante poder hacer cambios en el código de su biblioteca para un proyecto y registrarlos en su sistema de control de versiones varias veces sin tener que verificar los cambios en la ubicación de la biblioteca principal hasta que los cambios sean probados pueden afectar muchos proyectos diferentes.

Sus módulos de biblioteca también pueden terminar con un archivo que define las opciones de biblioteca para cada proyecto específico. Una práctica que he adoptado es nombrar estos archivos de interfaz _PAL.h donde _PAL indica un archivo de Capa de abstracción de proyecto.