CMake comparte biblioteca con mĂșltiples ejecutables
(2)
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 puedan vincularse los ejecutables. (El código común es bastante pequeño y prefiero no tratar con bibliotecas compartidas).
El árbol fuente se parece a esto:
-
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 tendrá que 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 solo 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 de proyectos de nivel superior, puedo compilar app1 y app2 y se compilan con éxito.
Sin embargo, si quiero construir uno solo de estos proyectos (ejecutando CMake desde la aplicación1, por ejemplo) en lugar de construir desde el directorio de nivel superior, obtengo 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 app1 o app2 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 en mi configuración subóptima? Solo estoy pensando que sería bueno poder construir los proyectos individualmente en lugar de desde el nivel superior en caso de que empecemos 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.
Cuando configura su entorno de compilación, debe reflexionar sobre los siguientes tres temas (además de otros, pero para esta discusión / respuesta lo reduje a los tres que considero relevantes aquí):
- Dependencias / Acoplamiento
- Despliegue
- Equipos
"Acoplamiento fuerte"
Llegué a pensar en el
add_subdirectory()
como compatible con "acoplamiento fuerte" y su configuración actual admite implícitamente:
-
Una biblioteca
common
cambia con frecuencia - Una implementación única (proceso y tiempo) para todas sus aplicaciones
-
Un solo equipo trabajando en la base fuente completa
- Un IDE lo mostraría todo en una solución
- Generas un entorno de compilación para todo
"Bajo acoplamiento"
Si desea un
"acoplamiento
más
suelto"
, 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 incluyendo la "entrega binaria") y cada
app
como un proyecto separado, usted admite:
-
Una biblioteca
common
cambia con menos frecuencia- Probablemente con sus propios ciclos de lanzamiento.
- Un ciclo de desarrollo / implementación independiente para cada aplicación
-
Un equipo de diferentes desarrolladores trabajando en cada
app
Una mezcla de ambos
Entonces, como puede ver, hay muchas cosas que considerar y CMake puede brindarle apoyo 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 desconecte 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)
En realidad, esto generará tres entornos de compilación.
Uno directamente en su directorio de salida binario y uno en cada uno de los
app1
y
app2
.
Y en estos enfoques, es posible que desee pensar en los archivos comunes de la cadena de herramientas CMake.
Referencias
Debe usar el comando
project()
en los subdirectorios solo si este subproyecto está diseñado para construirse de manera independiente y como parte del proyecto de nivel superior.
Este es el caso de LLVM y Clang, por ejemplo: Clang se puede compilar por separado, pero cuando el sistema de compilación LLVM detecta la fuente de Clang, también incluye sus objetivos.
En su caso no necesita subproyectos.
Para compilar solo el problema objetivo de
app1
o
app2
,
make app1
/
make app2
en el directorio de compilación de proyectos.