Cómo agregar correctamente directorios de inclusión con CMake

243

Hace aproximadamente un año, pregunté sobre las dependencias de encabezado en CMake .

Recientemente me di cuenta de que el problema parecía ser que CMake consideraba que esos archivos de encabezado eran externos al proyecto. Al menos, al generar un proyecto Code :: Blocks, los archivos de encabezado no aparecen dentro del proyecto (los archivos de origen sí). Por lo tanto, me parece que CMake considera que esos encabezados son externos al proyecto y no los rastrea en las dependencias.

Una búsqueda rápida en el tutorial de CMake solo señaló include_directoriesque no parece hacer lo que deseo ...

¿Cuál es la forma correcta de indicarle a CMake que un directorio en particular contiene encabezados para incluir, y que esos encabezados deben ser rastreados por el Makefile generado?

Matthieu M.
fuente
Las modificaciones realizadas a esta pregunta lo hacen confuso. La pregunta y las respuestas originales eran cómo rastrear los archivos de encabezado en un IDE. Esto es bastante diferente de las dependencias de un archivo de encabezado faltante de Makefile generado y cómo resolver ese problema.
fdk1342
@ Fred: No tengo idea de lo que estás hablando. Como la revisión de edición muestra claramente, la última oración siempre ha estado allí. Solo se hicieron ediciones cosméticas sobre esta pregunta, y no se introdujo (o eliminó) ninguna palabra.
Matthieu M.
Entonces ese es mi malentendido. Me gustó que se haya agregado un párrafo completo. stackoverflow.com/questions/13703647/… dice que el entendimiento común era cómo listar el archivo de encabezado en el IDE. Esto habría estado haciendo referencia al .cbparchivo del proyecto. Ahora, si el escáner de dependencia de cmake no identifica correctamente un archivo de encabezado como una dependencia para un Makefile, hay formas de solucionarlo, pero en algunos casos se equivocará porque no incluye un preprocesador completo.
fdk1342

Respuestas:

267

Hay que hacer dos cosas.

Primero agregue el directorio que se incluirá:

target_include_directories(test PRIVATE ${YOUR_DIRECTORY})

En caso de que esté atascado con una versión CMake muy antigua (2.8.10 o anterior) sin soporte target_include_directories, también puede usar el legado en su include_directorieslugar:

include_directories(${YOUR_DIRECTORY})

Luego, también debe agregar los archivos de encabezado a la lista de sus archivos de origen para el destino actual, por ejemplo:

set(SOURCES file.cpp file2.cpp ${YOUR_DIRECTORY}/file1.h ${YOUR_DIRECTORY}/file2.h)
add_executable(test ${SOURCES})

De esta forma, los archivos de encabezado aparecerán como dependencias en el Makefile, y también, por ejemplo, en el proyecto generado de Visual Studio, si genera uno.

Cómo usar esos archivos de encabezado para varios objetivos:

set(HEADER_FILES ${YOUR_DIRECTORY}/file1.h ${YOUR_DIRECTORY}/file2.h)

add_library(mylib libsrc.cpp ${HEADER_FILES})
target_include_directories(mylib PRIVATE ${YOUR_DIRECTORY})
add_executable(myexec execfile.cpp ${HEADER_FILES})
target_include_directories(myexec PRIVATE ${YOUR_DIRECTORY})
SirDarius
fuente
Ah! Sabía que debía ser algo estúpido. De hecho, no enumeré los encabezados ... ¿Debo enumerar los encabezados de solo esta biblioteca, o también todos los encabezados de los que podría depender (además de declarar la dependencia de la biblioteca)? Es un proyecto en crecimiento y me da mucho miedo la idea de agregar un encabezado a todas las dependencias cuando agrego uno en la biblioteca raíz.
Matthieu M.
Para permitir un mejor seguimiento de la dependencia (por ejemplo, para asegurarse de que la modificación de un archivo de encabezado desencadena la compilación para todos los objetivos afectados), sí. Sin embargo, puede usar variables cmake para enumerar los archivos de encabezado solo una vez y usarlos en varios lugares, vea mi edición.
SirDarius
1
Mi pregunta era más en el sentido de que tengo varias bibliotecas que dependen unas de otras: libroot, liba depende de libroot, libb depende de libroot. ¿Puedo usar la LIBROOT_HEADER_FILESvariable in liba/CMakefiley libb/CMakefileluego?
Matthieu M.
2
Esto está mal, nunca debes usar include_directoriesmás target_include_directories. El primero lo establece recursivamente para todos los objetivos en ese directorio; mientras que este último lo establece como objetivo. Hacer lo primero rompe la noción de un gráfico de destino en CMake y, en cambio, depende de los efectos secundarios de la jerarquía de archivos.
Andy
1
Edité la respuesta para reflejar la noción actual de preferir target_include_directoriesel código CMake moderno. No dude en invitarme a un chat si no está de acuerdo con los cambios.
ComicSansMS
74

Primero, usted le include_directories()dice a CMake que agregue el directorio -Ia la línea de comando de compilación. En segundo lugar, enumera los encabezados en su add_executable()o add_library()llamada.

Como ejemplo, si las fuentes de su proyecto están incluidas srcy necesita encabezados include, puede hacerlo así:

include_directories(include)

add_executable(MyExec
  src/main.c
  src/other_source.c
  include/header1.h
  include/header2.h
)
Angew ya no está orgulloso de SO
fuente
19
¿Realmente necesitas agregar encabezados add_executable? Pensé que CMake descubrió las dependencias del archivo de inclusión automáticamente.
Colin D Bennett
57
@ColinDBennett No tiene que enumerarlos por razones de dependencia: CMake descubre que las dependencias de compilación están bien si no lo hace. Pero si los enumera, se consideran parte del proyecto y se incluirán como tales en IDE (que fue el tema de la pregunta).
Angew ya no está orgulloso de SO
Al menos para QtCreator no es necesario agregar class.h en caso de que exista un class.cpp. Solo lonely.h necesita ser agregado a la fuente. Ver tutorial en www.th-thielemann.de/cmake
Th. Thielemann
19

CMake es más como un lenguaje de script si lo compara con otras formas de crear Makefile (por ejemplo, make o qmake). No es tan genial como Python, pero aún así.

No existe una " forma adecuada " si se observa en varios proyectos de código abierto cómo las personas incluyen directorios. Pero hay dos formas de hacerlo.

  1. Los include_directories brutos agregarán un directorio al proyecto actual y a todos los demás proyectos descendientes que agregará a través de una serie de comandos add_subdirectory . A veces la gente dice que ese enfoque es legado.

  2. Una forma más elegante es con target_include_directories . Permite agregar un directorio para un proyecto / objetivo específico sin (tal vez) herencia innecesaria o choque de varios directorios de inclusión. También permita realizar incluso una configuración sutil y agregue uno de los siguientes marcadores para este comando.

PRIVADO : se usa solo para este objetivo de compilación especificado

PÚBLICO : utilícelo para el objetivo especificado y para los objetivos que se vinculan con este proyecto

INTERFAZ : úsela solo para objetivos que se vinculan con el proyecto actual

PD:

  1. Ambos comandos permiten marcar un directorio como SYSTEM para dar una pista de que no es asunto suyo que los directorios especificados contengan advertencias.

  2. Una respuesta similar es con otros pares de comandos target_compile_definitions / add_definitions , target_compile_options / CMAKE_C_FLAGS

bruziuz
fuente
13

Añadir include_directories("/your/path/here").

Esto será similar a llamar gcccon -I/your/path/here/opción.

Asegúrese de poner comillas dobles alrededor del camino. Otras personas no mencionaron eso y me hicieron quedar atrapado durante 2 días. Entonces, esta respuesta es para personas que son muy nuevas en CMake y están muy confundidas.

off99555
fuente
7

Yo tuve el mismo problema.

Mi directorio de proyectos era así:

    --project
    ---Classes
    ----Application
    -----.h and .c files
    ----OtherFolders
    --main.cpp

Y lo que solía incluir los archivos en todas esas carpetas:

    file(GLOB source_files
            "*.h"
            "*.cpp"
            "Classes/*/*.cpp"
            "Classes/*/*.h"
    )

    add_executable(Server ${source_files})

Y funcionó totalmente.

Seyed Hussein Mirzaki
fuente
Recordar que cmake es un 'generador de sistema de compilación' y no un 'sistema de compilación' que utiliza file glob no es una buena idea en cmake moderno (CMake con versiones 3.0 y superiores) porque los globs de archivo se evalúan en el momento de 'compilación' y no 'build tiempo de generación del sistema. Ver enlace: gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1
ggulgulia