descarga - CMake y encontrar otros proyectos y sus dependencias
cmake tutorial (3)
Imagine el siguiente escenario: el Proyecto A es una biblioteca compartida que tiene varias dependencias (LibA, LibB, LibC). El Proyecto B es un ejecutable que tiene una dependencia en el proyecto A, y por lo tanto requiere todas las dependencias del Proyecto A también para construir.
Además, ambos proyectos se crean utilizando CMake, y el Proyecto A no debería ser instalado (a través del objetivo de "instalación") para que el Proyecto B lo use, ya que esto puede ser una molestia para los desarrolladores.
Entonces la pregunta es, ¿cuál es la mejor manera de resolver estas dependencias usando CMake? La solución ideal sería tan simple como sea posible (aunque no más simple) y requerirá un mantenimiento mínimo.
Alexander Shukaev tuvo un gran comienzo, pero hay varias cosas que se podrían hacer mejor:
- No use include_directories, como mínimo, use target_include_directories. Sin embargo, es probable que ni siquiera necesite hacer eso si usa los objetivos importados.
Usa los objetivos importados. Ejemplo de impulso:
find_package(Boost 1.56 REQUIRED COMPONENTS date_time filesystem iostreams) add_executable(foo foo.cc) target_link_libraries(foo PRIVATE Boost::date_time Boost::filesystem Boost::iostreams )
Esto se ocupa de los directorios de inclusión, bibliotecas, etc. Si utilizó boost en sus encabezados en B, entonces, en lugar de PRIVATE, use PUBLIC, y estas dependencias se agregarán transitoriamente a lo que dependa de B.
- No use globing de archivos (a menos que use 3.12). Hasta hace muy poco, el globbing de archivos solo funciona durante el tiempo de configuración, por lo que si agrega archivos y compila, no podrá detectar los cambios hasta que regenere explícitamente el proyecto. Sin embargo, si enumera los archivos directamente e intenta compilar, debe reconocer que la configuración está desactualizada y regenerarse automáticamente en el paso de compilación.
Hay una buena conversación aquí: https://www.youtube.com/watch?v=bsXLMQ6WgIk
Sin embargo, he tratado de adoptar la ideología de esto y estoy teniendo grandes problemas con el uso de find_package para todo y teniendo una estructura de directorios como la tuya.
Fácil. Aquí está el ejemplo desde lo más alto de mi cabeza:
El nivel superior CMakeLists.txt
:
cmake_minimum_required(VERSION 2.8.10)
# You can tweak some common (for all subprojects) stuff here. For example:
set(CMAKE_DISABLE_IN_SOURCE_BUILD ON)
set(CMAKE_DISABLE_SOURCE_CHANGES ON)
if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
message(SEND_ERROR "In-source builds are not allowed.")
endif ()
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_COLOR_MAKEFILE ON)
# Remove ''lib'' prefix for shared libraries on Windows
if (WIN32)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
endif ()
# When done tweaking common stuff, configure the components (subprojects).
# NOTE: The order matters! The most independent ones should go first.
add_subdirectory(components/B) # B is a static library (depends on Boost)
add_subdirectory(components/C) # C is a shared library (depends on B and external XXX)
add_subdirectory(components/A) # A is a shared library (depends on C and B)
add_subdirectory(components/Executable) # Executable (depends on A and C)
CMakeLists.txt
en components/B
:
cmake_minimum_required(VERSION 2.8.10)
project(B C CXX)
find_package(Boost
1.50.0
REQUIRED)
file(GLOB CPP_FILES source/*.cpp)
include_directories(${Boost_INCLUDE_DIRS})
add_library(${PROJECT_NAME} STATIC ${CPP_FILES})
# Required on Unix OS family to be able to be linked into shared libraries.
set_target_properties(${PROJECT_NAME}
PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_link_libraries(${PROJECT_NAME})
# Expose B''s public includes (including Boost transitively) to other
# subprojects through cache variable.
set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include
${Boost_INCLUDE_DIRS}
CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE)
CMakeLists.txt
en components/C
:
cmake_minimum_required(VERSION 2.8.10)
project(C C CXX)
find_package(XXX REQUIRED)
file(GLOB CPP_FILES source/*.cpp)
add_definitions(${XXX_DEFINITIONS})
# NOTE: Boost''s includes are transitively added through B_INCLUDE_DIRS.
include_directories(${B_INCLUDE_DIRS}
${XXX_INCLUDE_DIRS})
add_library(${PROJECT_NAME} SHARED ${CPP_FILES})
target_link_libraries(${PROJECT_NAME} B
${XXX_LIBRARIES})
# Expose C''s definitions (in this case only the ones of XXX transitively)
# to other subprojects through cache variable.
set(${PROJECT_NAME}_DEFINITIONS ${XXX_DEFINITIONS}
CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE)
# Expose C''s public includes (including the ones of C''s dependencies transitively)
# to other subprojects through cache variable.
set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include
${B_INCLUDE_DIRS}
${XXX_INCLUDE_DIRS}
CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE)
CMakeLists.txt
en components/A
:
cmake_minimum_required(VERSION 2.8.10)
project(A C CXX)
file(GLOB CPP_FILES source/*.cpp)
# XXX''s definitions are transitively added through C_DEFINITIONS.
add_definitions(${C_DEFINITIONS})
# NOTE: B''s and Boost''s includes are transitively added through C_INCLUDE_DIRS.
include_directories(${C_INCLUDE_DIRS})
add_library(${PROJECT_NAME} SHARED ${CPP_FILES})
# You could need `${XXX_LIBRARIES}` here too, in case if the dependency
# of A on C is not purely transitive in terms of XXX, but A explicitly requires
# some additional symbols from XXX. However, in this example, I assumed that
# this is not the case, therefore A is only linked against B and C.
target_link_libraries(${PROJECT_NAME} B
C)
# Expose A''s definitions (in this case only the ones of C transitively)
# to other subprojects through cache variable.
set(${PROJECT_NAME}_DEFINITIONS ${C_DEFINITIONS}
CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE)
# Expose A''s public includes (including the ones of A''s dependencies
# transitively) to other subprojects through cache variable.
set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include
${C_INCLUDE_DIRS}
CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE)
CMakeLists.txt
en components/Executable
:
cmake_minimum_required(VERSION 2.8.10)
project(Executable C CXX)
file(GLOB CPP_FILES source/*.cpp)
add_definitions(${A_DEFINITIONS})
include_directories(${A_INCLUDE_DIRS})
add_executable(${PROJECT_NAME} ${CPP_FILES})
target_link_libraries(${PROJECT_NAME} A C)
Para que quede claro, aquí está la estructura de árbol fuente correspondiente:
Root of the project
├───components
│ ├───Executable
│ │ ├───resource
│ │ │ └───icons
│ │ ├───source
| | └───CMakeLists.txt
│ ├───A
│ │ ├───include
│ │ │ └───A
│ │ ├───source
| | └───CMakeLists.txt
│ ├───B
│ │ ├───include
│ │ │ └───B
│ │ ├───source
| | └───CMakeLists.txt
│ └───C
│ ├───include
│ │ └───C
│ ├───source
| └───CMakeLists.txt
└───CMakeLists.txt
Hay muchos puntos en los que esto podría modificarse o modificarse para satisfacer ciertas necesidades, pero al menos esto debería ayudarlo a comenzar.
NOTA: he empleado con éxito esta estructura en varios proyectos grandes y medianos.
También se puede hacer, utilizando el mecanismo de Cache
CMake para lograr lo mismo (es decir, el intercambio de variables específicas del proyecto).
set (VAR "value"
CACHE INTERNAL "")
Consulte - https://.com/a/19859882/4361073