CMake comparte biblioteca con múltiples ejecutables

Mi proyecto contiene varios ejecutables que comparten un código común. Me gustaría poner el código común en una biblioteca estática a la que los ejecutables se pueden vincular. (El código común es bastante pequeño y prefiero no tratar con bibliotecas compartidas).

El árbol fuente se ve así:

  • proyecto
    • CMakeLists.txt
    • común
      • CMakeLists.txt
      • src
      • incluir
    • app1
      • src
      • CMakeLists.txt
    • app2
      • src
      • CMakeLists.txt

app1 y app2 dependen del código en común.

Este código común es muy específico de la aplicación y nunca necesitará ser utilizado por otro proyecto fuera de este árbol de directorios. Por esa razón, preferiría no instalar la biblioteca en ningún tipo de ubicación global.

El archivo CMakeLists.txt de nivel superior simplemente agrega los subdirectorios:

project(toplevel) cmake_minimum_required(VERSION 3.1) add_subdirectory(common) add_subdirectory(app1) add_subdirectory(app2) 

El archivo CMakeLists.txt de la biblioteca común crea la biblioteca estática y los conjuntos incluyen directorios:

 add_library(common STATIC common.cpp) target_include_directories(common PUBLIC "${CMAKE_CURRENT_LIST_DIR}/include") 

Y el archivo para los ejecutables se ve así:

 project(app1) cmake_minimum_required(VERSION 3.1) add_executable(${PROJECT_NAME} main.cpp) target_link_libraries(${PROJECT_NAME} common) 

Ahora para mi pregunta. Si ejecuto CMake desde el directorio del proyecto de nivel superior, puedo comstackr app1 y app2 y se comstackn correctamente. Sin embargo, si quiero construir uno solo de estos proyectos (ejecutando CMake desde la aplicación 1, por ejemplo) en lugar de comstackr desde el directorio de nivel superior, aparece un error porque common/include no se agrega a la ruta de búsqueda del encabezado.

Puedo ver por qué sucede esto. No hay nada en el archivo CMakeLists.txt para la aplicación1 o la aplicación2 que sea “común”. Esto solo se hace en el nivel superior.

¿Hay alguna forma de evitar esto, o este comportamiento generalmente se considera aceptable? ¿Hay algo sobre mi configuración subóptima? Solo pienso que sería bueno poder construir los proyectos de forma individual en lugar de desde el nivel superior en el caso de que comencemos a desarrollar más y más ejecutables que usen esta biblioteca común, pero quizás esto es algo que no debería estar preocupado por.

Debe usar el comando project() en subdirectorios solo si este subproyecto está pensado para ser construido tanto como independiente como como parte de un proyecto de alto nivel. Este es el caso de LLVM y Clang, por ejemplo: Clang se puede comstackr por separado, pero cuando el sistema de comstackción LLVM detecta la fuente Clang, también incluye sus objectives.

En su caso, no necesita subproyectos. Para comstackr solo el problema del objective app1 o app2 , make app1 / make app2 en los proyectos compile dir.

Cuando configura su entorno de comstackción, debe pensar un poco en los siguientes tres temas (al lado de otros, pero para esta discusión / respuesta lo reduje a los tres que considero relevantes aquí):

  1. Dependencias / Acoplamiento
  2. Despliegue
  3. Equipos

“Acoplamiento fuerte”

Llegué a pensar que el add_subdirectory() admite “acoplamiento fuerte” y su configuración actual admite implícitamente:

  • Una biblioteca common cambia common
  • Una única implementación (proceso y tiempo) para todas sus aplicaciones
  • Un solo equipo trabajando en la base de la fuente completa
    • Un IDE lo mostraría todo en una solución
    • Generas un entorno de construcción para todo

“Bajo acoplamiento”

Si desea un “acoplamiento flexible” más , puede utilizar scripts externos en otros idiomas o el uso de la macro ExternalProject_Add() de CMake. Entonces, si configura la biblioteca common (tal vez incluso incluye la “entrega binaria”) y cada app como un proyecto separado, usted admite:

  • Una biblioteca common cambia menos a menudo
    • Probablemente con sus propios ciclos de lanzamiento
  • Un ciclo de desarrollo / despliegue independiente para cada aplicación
  • Un equipo de desarrolladores diferentes trabajando en cada app

Una mezcla de ambos

Entonces, como puede ver, hay muchas cosas que considerar y CMake puede brindarle soporte para todo tipo de enfoques. Teniendo en cuenta que su proyecto podría estar en las primeras etapas, es probable que adopte un enfoque mixto (no desacople de inmediato la biblioteca common ):

CMakeLists.txt

 project(toplevel) cmake_minimum_required(VERSION 3.1) include(ExternalProject) ExternalProject_Add( app1 SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/app1" PREFIX app1 INSTALL_COMMAND "" ) ExternalProject_Add( app2 SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/app2" PREFIX app2 INSTALL_COMMAND "" ) 

app1 / CMakeLists.txt

 project(app1) cmake_minimum_required(VERSION 3.1) add_subdirectory(../common common) add_executable(${PROJECT_NAME} src/main.cpp) target_link_libraries(${PROJECT_NAME} common) 

Esto realmente generará tres entornos de construcción. Uno directamente en el directorio de salida binaria y otro en los app1 y app2 .

Y en tales enfoques es posible que desee pensar en los archivos CMake toolchain comunes.

Referencias

  • Use las bibliotecas habilitadas para CMake en su proyecto CMake (II)
  • CMake: ¿Cómo configurar las dependencias Source, Library y CMakeLists.txt?