Comment ajouter correctement des répertoires d'inclusion avec CMake

243

Il y a environ un an, j'ai posé des questions sur les dépendances d'en-tête dans CMake .

J'ai réalisé récemment que le problème semblait être que CMake considérait ces fichiers d'en-tête comme externes au projet. Au moins, lors de la génération d'un projet Code :: Blocks, les fichiers d'en-tête n'apparaissent pas dans le projet (les fichiers source le font). Il me semble donc que CMake considère ces en-têtes comme externes au projet et ne les suit pas dans les dépendances.

Une recherche rapide dans le tutoriel CMake ne fait que pointer vers include_directoriesce qui ne semble pas faire ce que je souhaite ...

Quelle est la bonne façon de signaler à CMake qu'un répertoire particulier contient des en-têtes à inclure et que ces en-têtes doivent être suivis par le Makefile généré?

Matthieu M.
la source
Les modifications apportées à cette question la rendent déroutante. La question et les réponses d'origine étaient de savoir comment suivre les fichiers d'en-tête dans un IDE. C'est assez différent des dépendances d'un fichier d'en-tête Makefile manquant et comment résoudre ce problème.
fdk1342
@Fred: Je n'ai aucune idée de quoi tu parles. Comme le montre clairement la révision d'édition, la dernière phrase a toujours été là. Seules des modifications cosmétiques ont été apportées à cette question, et aucun mot n'a été introduit (ni supprimé).
Matthieu M.
Alors c'est mon malentendu. Il m'a semblé qu'un paragraphe entier avait été ajouté. stackoverflow.com/questions/13703647/… dit que la compréhension commune était de savoir comment lister le fichier d'en-tête dans l'IDE. Cela aurait fait référence au .cbpdossier de projet. Maintenant, si le scanner de dépendances cmake ne parvient pas à identifier correctement un fichier d'en-tête en tant que dépendance d'un Makefile, il existe des moyens de résoudre ce problème, mais dans certains cas, il se trompe car il n'inclut pas un préprocesseur complet.
fdk1342

Réponses:

267

Il faut faire deux choses.

Ajoutez d'abord le répertoire à inclure:

target_include_directories(test PRIVATE ${YOUR_DIRECTORY})

Dans le cas où vous êtes bloqué avec une très ancienne version de CMake (2.8.10 ou plus ancienne) sans prise en charge de target_include_directories, vous pouvez également utiliser l'héritage à la include_directoriesplace:

include_directories(${YOUR_DIRECTORY})

Ensuite, vous devez également ajouter les fichiers d'en-tête à la liste de vos fichiers source pour la cible actuelle, par exemple:

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

De cette façon, les fichiers d'en-tête apparaîtront comme des dépendances dans le Makefile, et aussi par exemple dans le projet Visual Studio généré, si vous en générez un.

Comment utiliser ces fichiers d'en-tête pour plusieurs cibles:

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
la source
Ah! Je savais que ça devait être quelque chose de stupide. En effet, je n'ai pas répertorié les en-têtes ... Dois-je lister uniquement les en-têtes de cette bibliothèque, ou aussi tous les en-têtes dont elle pourrait dépendre (en plus de déclarer la dépendance sur la bibliothèque)? C'est un projet en pleine croissance et je redoute l'idée d'ajouter un en-tête à toutes les dépendances lorsque j'en ajoute un dans la bibliothèque racine.
Matthieu M.
Pour permettre un meilleur suivi des dépendances (par exemple pour vous assurer que la modification d'un fichier d'en-tête déclenche la compilation pour toutes les cibles affectées), oui. Cependant, vous pouvez utiliser les variables cmake pour répertorier les fichiers d'en-tête une seule fois et les utiliser à plusieurs endroits, voir ma modification.
SirDarius
1
Ma question était plus en ce sens que j'ai plusieurs bibliothèques qui dépendent les unes des autres: libroot, liba dépend de libroot, libb dépend de libroot. Puis - je utiliser la LIBROOT_HEADER_FILESvariable liba/CMakefileet libb/CMakefilealors?
Matthieu M.
2
C'est faux, vous ne devriez jamais utiliser include_directoriesplus target_include_directories. Le premier le définit récursivement pour toutes les cibles de ce répertoire; alors que ce dernier le fixe comme objectif. Faire le premier casse la notion de graphique cible dans CMake et s'appuie à la place sur les effets secondaires de votre hiérarchie de fichiers.
Andy
1
J'ai édité la réponse pour refléter la notion actuelle de préférence target_include_directoriespour le code CMake moderne. N'hésitez pas à m'inviter à un chat si vous n'êtes pas d'accord avec les changements.
ComicSansMS
74

Tout d'abord, vous utilisez include_directories()pour dire à CMake d'ajouter le répertoire -Ià la ligne de commande de compilation. Deuxièmement, vous répertoriez les en-têtes dans votre add_executable()ou add_library()appelez.

Par exemple, si les sources de votre projet sont dedans srcet que vous avez besoin d'en-têtes include, vous pouvez le faire comme ceci:

include_directories(include)

add_executable(MyExec
  src/main.c
  src/other_source.c
  include/header1.h
  include/header2.h
)
Angew n'est plus fier de SO
la source
19
Avez-vous vraiment besoin d'ajouter des en-têtes add_executable? Je pensais que CMake avait compris automatiquement les dépendances des fichiers inclus.
Colin D Bennett
57
@ColinDBennett Vous n'avez pas à les répertorier pour des raisons de dépendance - CMake comprend très bien les dépendances de construction si vous ne le faites pas. Mais si vous les listez, ils sont considérés comme faisant partie du projet et seront répertoriés comme tels dans les IDE (qui était le sujet de la question).
Angew n'est plus fier de SO
Au moins pour QtCreator, il n'est pas nécessaire d'ajouter class.h au cas où un class.cpp existe. Seul lonely.h doit être ajouté à la source. Voir le tutoriel sur www.th-thielemann.de/cmake
Th. Thielemann
19

CMake ressemble plus à un langage de script si on le compare avec d'autres façons de créer un Makefile (par exemple make ou qmake). Ce n'est pas très cool comme Python, mais quand même.

Il n'y a rien de tel qu'une " bonne façon " si l'on regarde dans divers projets open source comment les gens incluent les répertoires. Mais il y a deux façons de procéder.

  1. Les include_directories bruts ajouteront un répertoire au projet actuel et à tous les autres projets descendants que vous ajouterez via une série de commandes add_subdirectory . Parfois, les gens disent qu'une telle approche est un héritage.

  2. Une manière plus élégante est avec target_include_directories . Il permet d'ajouter un répertoire pour un projet / cible spécifique sans (peut-être) héritage inutile ni conflit de divers répertoires d'inclusion. Permet également d'effectuer une configuration même subtile et d'ajouter l'un des marqueurs suivants pour cette commande.

PRIVÉ - à utiliser uniquement pour cette cible de génération spécifiée

PUBLIC - utilisez-le pour la cible spécifiée et pour les cibles liées à ce projet

INTERFACE - utilisez-le uniquement pour les cibles liées au projet en cours

PS:

  1. Les deux commandes permettent de marquer un répertoire en tant que SYSTEM pour donner un indice que ce n'est pas votre entreprise que les répertoires spécifiés contiendront des avertissements.

  2. Une réponse similaire est avec d'autres paires de commandes target_compile_definitions / add_definitions , target_compile_options / CMAKE_C_FLAGS

bruziuz
la source
13

Ajouter include_directories("/your/path/here").

Cela sera similaire à l'appel gccavec -I/your/path/here/option.

Assurez-vous de mettre des guillemets doubles autour du chemin. D'autres personnes n'ont pas mentionné cela et cela m'a fait coincé pendant 2 jours. Cette réponse s'adresse donc aux personnes qui sont très nouvelles à CMake et très confuses.

off99555
la source
7

J'ai eu le même problème.

Mon répertoire de projet était comme ceci:

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

Et ce que j'avais l'habitude d'inclure les fichiers dans tous ces dossiers:

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

    add_executable(Server ${source_files})

Et cela a totalement fonctionné.

Seyed Hussein Mirzaki
la source
Se souvenir que cmake est un «générateur de système de génération» et non un «système de génération» utilisant glob de fichier n'est pas une bonne idée dans cmake moderne (CMake avec les versions 3.0 et supérieures) parce que les globs de fichiers sont évalués au moment de la «construction» et non de la «construction» temps de génération du système. Voir le lien: gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1
ggulgulia