test primer juego google c++ cmake doxygen googletest

c++ - primer - google test juego



Organización de proyectos C++(con gtest, cmake y doxygen) (4)

Soy nuevo en la programación en general, así que decidí comenzar haciendo una simple clase vectorial en C ++. Sin embargo, me gustaría tener buenos hábitos desde el principio en lugar de tratar de modificar mi flujo de trabajo más adelante.

Actualmente tengo solo dos archivos vector3.hpp y vector3.cpp . Este proyecto comenzará a crecer lentamente (convirtiéndolo en una biblioteca de álgebra lineal general) a medida que me familiarice con todo, por lo que me gustaría adoptar un diseño de proyecto "estándar" para hacer la vida más fácil más adelante. Entonces, después de mirar a mi alrededor, he encontrado dos formas de organizar los archivos hpp y cpp, siendo el primero:

project └── src ├── vector3.hpp └── vector3.cpp

y el segundo ser:

project ├── inc │ └── project │ └── vector3.hpp └── src └── vector3.cpp

¿Cuál recomendarías y por qué?

En segundo lugar, me gustaría utilizar el marco de prueba de Google C ++ para probar mi código unitaria, ya que parece bastante fácil de usar. ¿Sugiere agrupar esto con mi código, por ejemplo en una carpeta inc/gtest o contrib/gtest ? Si está incluido, ¿sugiere utilizar el script fuse_gtest_files.py para reducir el número o los archivos, o dejarlos como están? Si no está incluido, ¿cómo se maneja esta dependencia?

Cuando se trata de escribir pruebas, ¿cómo se organizan generalmente? Estaba pensando en tener un archivo cpp para cada clase ( test_vector3.cpp por ejemplo) pero todo compilado en un archivo binario para que todos puedan ejecutarse juntos fácilmente.

Dado que la biblioteca gtest generalmente se construye utilizando cmake y make, ¿estaba pensando que tendría sentido que mi proyecto también se construyera así? Si decidí usar el siguiente diseño de proyecto:

├── CMakeLists.txt ├── contrib │ └── gtest │ ├── gtest-all.cc │ └── gtest.h ├── docs │ └── Doxyfile ├── inc │ └── project │ └── vector3.cpp ├── src │ └── vector3.cpp └── test └── test_vector3.cpp

¿Cómo debería CMakeLists.txt el CMakeLists.txt para que pueda compilar solo la biblioteca o la biblioteca y las pruebas? También he visto bastantes proyectos que tienen un directorio build y un bin . ¿La construcción ocurre en el directorio de compilación y luego los archivos binarios se trasladaron al directorio bin? ¿Vivirían los binarios de las pruebas y la biblioteca en el mismo lugar? ¿O tendría más sentido estructurarlo de la siguiente manera?

test ├── bin ├── build └── src └── test_vector3.cpp

También me gustaría usar doxygen para documentar mi código. ¿Es posible hacer que esto se ejecute automáticamente con cmake y make?

Perdón por tantas preguntas, pero no he encontrado un libro en C ++ que responda satisfactoriamente este tipo de preguntas.


Estructurando el proyecto

En general, preferiría lo siguiente:

├── CMakeLists.txt | ├── docs/ │ └── Doxyfile | ├── include/ │ └── project/ │ └── vector3.hpp | ├── src/ └── project/ └── vector3.cpp └── test/ └── test_vector3.cpp

Esto significa que tiene un conjunto de archivos API definidos muy claramente para su biblioteca, y la estructura significa que los clientes de su biblioteca harían

#include "project/vector3.hpp"

en lugar de lo menos explícito

#include "vector3.hpp"


Me gusta la estructura del árbol / src para que coincida con la del árbol / include, pero eso es realmente una preferencia personal. Sin embargo, si su proyecto se expande para contener subdirectorios dentro de / include / project, generalmente ayudaría a coincidir con aquellos dentro del árbol / src.

Para las pruebas, prefiero mantenerlos "cerca" de los archivos que prueban, y si terminas con subdirectorios dentro de / src, es un paradigma bastante fácil de seguir para otros si quieren encontrar el código de prueba de un archivo dado.

Pruebas

En segundo lugar, me gustaría utilizar el marco de prueba de Google C ++ para probar mi código unitaria, ya que parece bastante fácil de usar.

Gtest es simple de usar y bastante completo en términos de sus capacidades. Se puede utilizar junto con gmock mucha facilidad para ampliar sus capacidades, pero mis propias experiencias con gmock han sido menos favorables. Estoy bastante dispuesto a aceptar que esto puede deberse a mis propias deficiencias, pero las pruebas de gmock tienden a ser más difíciles de crear y mucho más frágiles / difíciles de mantener. Un gran clavo en el ataúd gmock es que realmente no funciona bien con punteros inteligentes.

Esta es una respuesta muy trivial y subjetiva a una gran pregunta (que probablemente no pertenece realmente a SO)

¿Sugiere agrupar esto con mi código, por ejemplo, en una carpeta "inc / gtest" o "contrib / gtest"? Si está incluido, ¿sugiere utilizar el script fuse_gtest_files.py para reducir el número o los archivos, o dejarlos como están? Si no está incluido, ¿cómo se maneja esta dependencia?

Prefiero usar el módulo ExternalProject_Add de CMake. Esto evita tener que mantener el código fuente de Gtest en su repositorio o instalarlo en cualquier lugar. Se descarga y se construye en su árbol de compilación de forma automática.

Vea mi respuesta que trata con los detalles aquí .

Cuando se trata de escribir pruebas, ¿cómo se organizan generalmente? Estaba pensando en tener un archivo cpp para cada clase (test_vector3.cpp, por ejemplo) pero todo compilado en un archivo binario para que todos puedan ejecutarse juntos fácilmente.

Buen plan.

edificio

Soy fanático de CMake, pero al igual que con sus preguntas relacionadas con la prueba, es probable que SO no sea el mejor lugar para pedir opiniones sobre un tema tan subjetivo.

¿Cómo debería verse el CMakeLists.txt para que pueda compilar solo la biblioteca o la biblioteca y las pruebas?

add_library(ProjectLibrary <All library sources and headers>) add_executable(ProjectTest <All test files>) target_link_libraries(ProjectTest ProjectLibrary)

La biblioteca aparecerá como un objetivo "ProjectLibrary" y el banco de pruebas como un objetivo "ProjectTest". Al especificar la biblioteca como una dependencia del exe de prueba, crear el exe de prueba hará que la biblioteca se reconstruya automáticamente si está desactualizada.

También he visto bastantes proyectos que tienen un directorio de construcción y un directorio bin. ¿La construcción ocurre en el directorio de compilación y luego los archivos binarios se trasladaron al directorio bin? ¿Vivirían los binarios de las pruebas y la biblioteca en el mismo lugar?

CMake recomienda compilaciones "fuera de la fuente", es decir, crea su propio directorio de compilación fuera del proyecto y ejecuta CMake desde allí. Esto evita "contaminar" tu árbol fuente con archivos de compilación, y es muy conveniente si estás usando vcs.

Puede especificar que los archivos binarios se muevan o se copien en un directorio diferente una vez creado, o que se creen de manera predeterminada en otro directorio, pero en general no es necesario. CMake proporciona formas completas de instalar su proyecto si lo desea, o facilita que otros proyectos de CMake "encuentren" los archivos relevantes de su proyecto.

Con respecto al propio soporte de CMake para encontrar y ejecutar las pruebas de Gtest , esto sería en gran medida inapropiado si construyes gtest como parte de tu proyecto. El módulo FindGtest está diseñado para usarse en el caso en que gtest se haya construido por separado fuera de su proyecto.

CMake proporciona su propio marco de prueba (CTest), e idealmente, cada caso de Gtest se agregaría como un caso de CTest.

Sin embargo, la macro GTEST_ADD_TESTS proporcionada por FindGtest para permitir la fácil adición de casos de prueba de Gtest como casos de prueba individuales es algo que falta, ya que no funciona para las macros de TEST_F que no sean TEST y TEST_F . Type-parameterised pruebas con TEST_P o Type-parameterised usando TEST_P , TYPED_TEST_P , etc. no se tratan en absoluto.

El problema no tiene una solución fácil que yo sepa. La manera más robusta de obtener una lista de casos de prueba es ejecutar el exe de prueba con la --gtest_list_tests . Sin embargo, esto solo se puede hacer una vez que se construya el exe, por lo que CMake no puede hacer uso de esto. Lo cual te deja con dos opciones; CMake debe intentar analizar el código C ++ para deducir los nombres de las pruebas (no triviales en extremo si desea tener en cuenta todas las macros gtest, las pruebas comentadas, las pruebas desactivadas), o los casos de prueba se agregan a mano al Archivo CMakeLists.txt.

También me gustaría usar doxygen para documentar mi código. ¿Es posible hacer que esto se ejecute automáticamente con cmake y make?

Sí, aunque no tengo experiencia en este frente. CMake proporciona FindDoxygen para este propósito.


Además de las otras (excelentes) respuestas, voy a describir una estructura que he estado usando para proyectos relativamente a gran escala .
No voy a abordar la pregunta sobre Doxygen, ya que simplemente repetiría lo que se dice en las otras respuestas.

Razón fundamental

Para modularidad y mantenibilidad, el proyecto está organizado como un conjunto de unidades pequeñas. Para mayor claridad, vamos a llamarlos UnitX, con X = A, B, C, ... (pero pueden tener cualquier nombre general). La estructura del directorio se organiza para reflejar esta elección, con la posibilidad de agrupar unidades si es necesario.

Solución

El diseño del directorio básico es el siguiente (el contenido de las unidades se detalla más adelante):

project ├── CMakeLists.txt ├── UnitA ├── UnitB ├── GroupA │ └── CMakeLists.txt │ └── GroupB │ └── CMakeLists.txt │ └── UnitC │ └── UnitD │ └── UnitE

project/CMakeLists.txt podría contener lo siguiente:

cmake_minimum_required(VERSION 3.0.2) project(project) enable_testing() # This will be necessary for testing (details below) add_subdirectory(UnitA) add_subdirectory(UnitB) add_subdirectory(GroupA)

y project/GroupA/CMakeLists.txt :

add_subdirectory(GroupB) add_subdirectory(UnitE)

y project/GroupB/CMakeLists.txt :

add_subdirectory(UnitC) add_subdirectory(UnitD)

Ahora a la estructura de las diferentes unidades (tomemos, por ejemplo, UnitD)

project/GroupA/GroupB/UnitD ├── README.md ├── CMakeLists.txt ├── lib │ └── CMakeLists.txt │ └── UnitD │ └── ClassA.h │ └── ClassA.cpp │ └── ClassB.h │ └── ClassB.cpp ├── test │ └── CMakeLists.txt │ └── ClassATest.cpp │ └── ClassBTest.cpp │ └── [main.cpp]

Para los diferentes componentes:

  • Me gusta tener el origen ( .cpp ) y los encabezados ( .h ) en la misma carpeta. Esto evita una jerarquía duplicada de directorios, facilita el mantenimiento. Para la instalación, no es ningún problema (especialmente con CMake) simplemente filtrar los archivos de encabezado.
  • La función del directorio UnitD es permitir más adelante incluir archivos con #include <UnitD/ClassA.h> . Además, al instalar esta unidad, puede simplemente copiar la estructura del directorio como está. Tenga en cuenta que también puede organizar sus archivos fuente en subdirectorios.
  • Me gusta un archivo README para resumir de qué se trata la unidad y especificar información útil al respecto.
  • CMakeLists.txt podría contener simplemente:

    add_subdirectory(lib) add_subdirectory(test)

  • lib/CMakeLists.txt :

    project(UnitD) set(headers UnitD/ClassA.h UnitD/ClassB.h ) set(sources UnitD/ClassA.cpp UnitD/ClassB.cpp ) add_library(${TARGET_NAME} STATIC ${headers} ${sources}) # INSTALL_INTERFACE: folder to which you will install a directory UnitD containing the headers target_include_directories(UnitD PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> PUBLIC $<INSTALL_INTERFACE:include/SomeDir> ) target_link_libraries(UnitD PUBLIC UnitA PRIVATE UnitC )

    Aquí, tenga en cuenta que no es necesario decirle a CMake que queremos los directorios de UnitA para UnitA y UnitC , ya que esto ya se especificó al configurar esas unidades. Además, PUBLIC le dirá a todos los objetivos que dependen de UnitD que deberían incluir automáticamente la dependencia UnitA , mientras que UnitC no será necesario entonces ( PRIVATE ).

  • test/CMakeLists.txt (consulte más abajo si desea usar GTest para ello):

    project(UnitDTests) add_executable(UnitDTests ClassATest.cpp ClassBTest.cpp [main.cpp] ) target_link_libraries(UnitDTests PUBLIC UnitD ) add_test( NAME UnitDTests COMMAND UnitDTests )

Usando GoogleTest

Para Google Test, lo más fácil es si su fuente está presente en algún lugar del directorio de origen, pero no es necesario que la agregue allí. He estado utilizando este proyecto para descargarlo automáticamente, y envuelvo su uso en una función para asegurarme de que se descargue solo una vez, aunque tengamos varios objetivos de prueba.

Esta función CMake es la siguiente:

function(import_gtest) include (DownloadProject) if (NOT TARGET gmock_main) include(DownloadProject) download_project(PROJ googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG release-1.8.0 UPDATE_DISCONNECTED 1 ) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # Prevent GoogleTest from overriding our compiler/linker options when building with Visual Studio add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} EXCLUDE_FROM_ALL) endif() endfunction()

y luego, cuando quiera usarlo dentro de uno de mis objetivos de prueba, CMakeLists.txt las siguientes líneas a CMakeLists.txt (esto es para el ejemplo anterior, test/CMakeLists.txt ):

import_gtest() target_link_libraries(UnitDTests gtest_main gmock_main)


Como iniciador, hay algunos nombres convencionales para directorios que no puede ignorar, estos se basan en la larga tradición del sistema de archivos Unix. Estos son:

trunk ├── bin : for all executables (applications) ├── lib : for all other binaries (static and shared libraries (.so or .dll)) ├── include : for all header files ├── src : for source files └── doc : for documentation

Probablemente sea una buena idea mantener este diseño básico, al menos en el nivel superior.

Acerca de la división de los archivos de cabecera y los archivos de origen (cpp), ambos esquemas son bastante comunes. Sin embargo, tiendo a preferir mantenerlos juntos, es más práctico en las tareas diarias tener los archivos juntos. Además, cuando todo el código está debajo de una carpeta de nivel superior, es decir, la carpeta trunk/src/ , puede observar que todas las demás carpetas (bin, lib, include, doc y quizás alguna carpeta de prueba) en el nivel superior , además del directorio "compilar" para una compilación fuera de la fuente, son todas las carpetas que contienen nada más que los archivos que se generan en el proceso de compilación. Y por lo tanto, solo la carpeta src necesita una copia de seguridad, o mucho mejor, mantenida bajo un sistema / servidor de control de versiones (como Git o SVN).

Y cuando se trata de instalar sus archivos de encabezado en el sistema de destino (si desea distribuir su biblioteca), bueno, CMake tiene un comando para instalar archivos (implícitamente crea un destino de "instalación", para hacer "hacer instalación") que puede usar para poner todos los encabezados en el directorio /usr/include/ . Solo uso la siguiente macro cmake para este propósito:

# custom macro to register some headers as target for installation: # setup_headers("/path/to/header/something.h" "/relative/install/path") macro(setup_headers HEADER_FILES HEADER_PATH) foreach(CURRENT_HEADER_FILE ${HEADER_FILES}) install(FILES "${SRCROOT}${CURRENT_HEADER_FILE}" DESTINATION "${INCLUDEROOT}${HEADER_PATH}") endforeach(CURRENT_HEADER_FILE) endmacro(setup_headers)

Donde SRCROOT es una variable cmake que establecí en la carpeta src, e INCLUDEROOT es la variable cmake que configuro para donde los encabezados deben ir. Por supuesto, hay muchas otras formas de hacerlo, y estoy seguro de que mi camino no es el mejor. El punto es que no hay ninguna razón para dividir los encabezados y las fuentes solo porque los encabezados deben instalarse en el sistema de destino, porque es muy fácil, especialmente con CMake (o CPack), seleccionar y configurar los encabezados para ser instalado sin tener que tenerlos en un directorio separado. Y esto es lo que he visto en la mayoría de las bibliotecas.

Cita: En segundo lugar, me gustaría utilizar el Marco de prueba de Google C ++ para probar mi código unitaria, ya que parece bastante fácil de usar. ¿Sugiere agrupar esto con mi código, por ejemplo, en una carpeta "inc / gtest" o "contrib / gtest"? Si está incluido, ¿sugiere utilizar el script fuse_gtest_files.py para reducir el número o los archivos, o dejarlos como están? Si no está incluido, ¿cómo se maneja esta dependencia?

No agrupe las dependencias con su biblioteca. En general, esta es una idea bastante horrible, y siempre la odio cuando estoy atascado tratando de construir una biblioteca que hiciera eso. Debería ser su último recurso, y tenga cuidado con las trampas. A menudo, las personas combinan dependencias con su biblioteca, ya sea porque se dirigen a un entorno de desarrollo terrible (por ejemplo, Windows) o porque solo admiten una versión anterior (obsoleta) de la biblioteca (dependencia) en cuestión. El principal inconveniente es que su dependencia puede chocar con las versiones ya instaladas de la misma biblioteca / aplicación (por ejemplo, usted incluyó gtest, pero la persona que intenta construir su biblioteca ya tiene una versión más nueva (o anterior) de gtest ya instalada, los dos pueden chocar y darle a esa persona un dolor de cabeza muy desagradable). Entonces, como dije, hágalo bajo su propio riesgo, y solo lo diría como último recurso. Pedir a las personas que instalen algunas dependencias antes de poder compilar su biblioteca es mucho menos malo que intentar resolver los conflictos entre las dependencias incluidas y las instalaciones existentes.

Cita: Cuando se trata de escribir pruebas, ¿cómo se organizan generalmente? Estaba pensando en tener un archivo cpp para cada clase (test_vector3.cpp, por ejemplo) pero todo compilado en un archivo binario para que todos puedan ejecutarse juntos fácilmente.

Un archivo cpp por clase (o un pequeño grupo cohesivo de clases y funciones) es más habitual y práctico en mi opinión. Sin embargo, definitivamente, no compilarlos todos en un solo binario solo para que "todos puedan ejecutarse juntos". Esa es una muy mala idea. Generalmente, cuando se trata de codificación, quiere dividir las cosas tanto como sea razonable hacerlo. En el caso de las pruebas unitarias, no desea que un binario ejecute todas las pruebas, porque eso significa que cualquier pequeño cambio que realice en cualquier elemento de su biblioteca probablemente ocasione una recompilación casi total de ese programa de prueba unitaria. , y eso son solo minutos / horas perdidos esperando la recompilación. Solo adhiérase a un esquema simple: 1 unidad = 1 programa de prueba de unidad. Luego, use un script o un marco de pruebas unitarias (como gtest y / o CTest) para ejecutar todos los programas de prueba e informar sobre las tasas de falla / éxito.

Cita: Dado que la biblioteca gtest generalmente se construye usando cmake y make, ¿estaba pensando que tendría sentido que mi proyecto también se construyera así? Si decidí usar el siguiente diseño de proyecto:

Prefiero sugerir este diseño:

trunk ├── bin ├── lib │ └── project │ └── libvector3.so │ └── libvector3.a products of installation / building ├── docs │ └── Doxyfile ├── include │ └── project │ └── vector3.hpp │_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ │ ├── src │ └── CMakeLists.txt │ └── Doxyfile.in │ └── project part of version-control / source-distribution │ └── CMakeLists.txt │ └── vector3.hpp │ └── vector3.cpp │ └── test │ └── test_vector3.cpp │_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ │ ├── build └── test working directories for building / testing └── test_vector3

Algunas cosas para notar aquí. Primero, los subdirectorios de su directorio src deben reflejar los subdirectorios de su directorio de inclusión, esto es solo para mantener las cosas intuitivas (también, trate de mantener su estructura de subdirectorios razonablemente plana (poco profunda), porque la anidación profunda de las carpetas a menudo es más una molestia que cualquier otra cosa). Segundo, el directorio "incluir" es solo un directorio de instalación, sus contenidos son solo los encabezados que se eligen del directorio src.

En tercer lugar, el sistema CMake está destinado a distribuirse a través de los subdirectorios de origen, no como un archivo CMakeLists.txt en el nivel superior. Esto mantiene las cosas locales, y eso es bueno (en el espíritu de dividir las cosas en piezas independientes). Si agrega una nueva fuente, un nuevo encabezado o un nuevo programa de prueba, todo lo que necesita es editar un archivo CMakeLists.txt pequeño y simple en el subdirectorio en cuestión, sin afectar a nada más. Esto también le permite reestructurar los directorios con facilidad (CMakeLists son locales y están contenidos en los subdirectorios que se mueven). Los CMakeLists de nivel superior deberían contener la mayoría de las configuraciones de nivel superior, como la configuración de directorios de destino, comandos personalizados (o macros) y la búsqueda de paquetes instalados en el sistema. Los CMakeLists de nivel inferior deben contener solo listas simples de encabezados, fuentes y orígenes de pruebas unitarias, y los comandos cmake que los registran en los objetivos de compilación.

Cita: ¿Cómo debería CMakeLists.txt buscar para que pueda compilar solo la biblioteca o la biblioteca y las pruebas?

La respuesta básica es que CMake le permite excluir específicamente ciertos objetivos de "todos" (que es lo que se crea al escribir "make"), y también puede crear grupos específicos de objetivos. No puedo hacer un tutorial de CMake aquí, pero es bastante sencillo de averiguar por ti mismo. En este caso específico, sin embargo, la solución recomendada es, por supuesto, utilizar CTest, que es simplemente un conjunto adicional de comandos que puede usar en los archivos CMakeLists para registrar una serie de objetivos (programas) que están marcados como unidades- pruebas. Entonces, CMake colocará todas las pruebas en una categoría especial de compilaciones, y eso es exactamente lo que usted solicitó, entonces, el problema fue resuelto.

Cita: También he visto bastantes proyectos que tienen un directorio de construcción y un directorio bin. ¿La construcción ocurre en el directorio de compilación y luego los archivos binarios se trasladaron al directorio bin? ¿Vivirían los binarios de las pruebas y la biblioteca en el mismo lugar? ¿O tendría más sentido estructurarlo de la siguiente manera?

Tener un directorio de compilación fuera de la fuente (compilación "fuera de la fuente") es realmente lo único sensato que puede hacer, es el estándar de facto en estos días. Así que, definitivamente, tengo un directorio "build" separado, fuera del directorio fuente, tal como lo recomiendan las personas de CMake, y como lo hace cada programador que he conocido. En cuanto al directorio bin, bueno, eso es una convención, y probablemente sea una buena idea mantenerlo, como dije al comienzo de esta publicación.

Cita: también me gustaría usar doxygen para documentar mi código. ¿Es posible hacer que esto se ejecute automáticamente con cmake y make?

Sí. Es más que posible, es increíble. Dependiendo de qué tan elegante desee obtener, hay varias posibilidades. CMake tiene un módulo para Doxygen (es decir, find_package(Doxygen) ) que le permite registrar objetivos que ejecutarán Doxygen en algunos archivos. Si desea hacer cosas más sofisticadas, como actualizar el número de versión en el archivo Doxy, o ingresar automáticamente una fecha / autor de sellos para los archivos fuente, etc., todo es posible con un poco de CMake kung-fu. En general, hacer esto implicará que guardes un archivo Doxyfile de origen (por ejemplo, el "Doxyfile.in" que puse en el diseño de la carpeta anterior) que tiene tokens para ser encontrados y reemplazados por los comandos de análisis de CMake. En mi archivo CMakeLists de nivel superior , encontrarás una pieza de CMake kung-fu que hace algunas cosas sofisticadas con cmake-doxygen.


Los sistemas de compilación de C ++ son un poco de arte negro y cuanto más antiguo sea el proyecto, más cosas raras puedes encontrar, así que no es sorprendente que surjan muchas preguntas. Trataré de revisar las preguntas una por una y mencionaré algunas cuestiones generales relacionadas con la creación de bibliotecas C ++.

Separación de encabezados y archivos cpp en directorios. Esto solo es esencial si está creando un componente que se supone debe usarse como una biblioteca en lugar de una aplicación real. Tus encabezados son la base para que los usuarios interactúen con lo que ofreces y deben instalarse. Esto significa que tienen que estar en un subdirectorio (nadie quiere muchos encabezados que terminen en el nivel superior /usr/include/ ) y sus encabezados deben poder incluirse con dicha configuración.

└── prj ├── include │   └── prj │   ├── header2.h │   └── header.h └── src └── x.cpp

funciona bien, porque las rutas de inclusión funcionan y puede usar englobamiento fácil para los objetivos de instalación.

Compilación de dependencias: creo que esto depende en gran medida de la capacidad del sistema de compilación para ubicar y configurar las dependencias y cuán dependiente es el código en una sola versión. También depende de qué tan capaces sean sus usuarios y cuán fácil es la dependencia para instalar en su plataforma. CMake viene con un script find_package para Google Test. Esto hace las cosas mucho más fáciles. Iba con agrupar solo cuando era necesario y evitarlo de otra manera.

Cómo construir: evite compilaciones en origen. CMake hace fácil la creación de fuentes y hace la vida mucho más fácil.

Supongo que también quiere usar CTest para ejecutar pruebas para su sistema (también viene con soporte incorporado para GTest). Una decisión importante para el diseño del directorio y la organización de la prueba será: ¿termina con subproyectos? Si es así, necesita un poco más de trabajo al configurar CMakeLists y debe dividir sus subproyectos en subdirectorios, cada uno con sus propios archivos include y src . Tal vez incluso sus propias ejecuciones y salidas doxygen (la combinación de múltiples proyectos doxygen es posible, pero no es fácil ni bonita).

Terminarás con algo como esto:

└── prj ├── CMakeLists.txt <-- (1) ├── include │   └── prj │   ├── header2.hpp │   └── header.hpp ├── src │   ├── CMakeLists.txt <-- (2) │   └── x.cpp └── test ├── CMakeLists.txt <-- (3) ├── data │   └── testdata.yyy └── testcase.cpp

dónde

  • (1) configura las dependencias, los detalles de la plataforma y las rutas de salida
  • (2) configura la biblioteca que vas a construir
  • (3) configura los ejecutables de prueba y los casos de prueba

En caso de que tenga subcomponentes, le sugiero agregar otra jerarquía y utilizar el árbol de arriba para cada subproyecto. Entonces las cosas se complican, porque debe decidir si los subcomponentes buscan y configuran sus dependencias o si lo hace en el nivel superior. Esto debe decidirse caso por caso.

Doxygen: después de que lograste pasar por el baile de configuración de doxygen, es trivial usar CMake add_custom_command para agregar un objetivo de doc.

Así es como terminan mis proyectos y he visto algunos proyectos muy similares, pero por supuesto esto no es la panacea.

Adición En algún momento deseará generar un archivo config.hpp que contenga una versión definida y tal vez una definición de algún identificador de control de versión (un hash Git o número de revisión SVN). CMake tiene módulos para automatizar la búsqueda de esa información y generar archivos. Puede usar el archivo de configure_file de CMake para reemplazar variables en un archivo de plantilla con variables definidas dentro de CMakeLists.txt .

Si está creando bibliotecas, también necesitará una definición de exportación para obtener la diferencia entre compiladores correctos, por ejemplo, __declspec en __declspec y atributos de visibility en GCC / clang.