J'écris une bibliothèque C++ (en-tête uniquement) et j'utilise CMake pour générer mes fichiers de projet et de solution (Visual Studio). J'écris également une suite de tests, qui fait partie du même projet CMake.
Mon problème se produit lorsque j'appelle target_include_directories () sur la cible qui représente ma bibliothèque d'en-tête uniquement, afin que les consommateurs de ma bibliothèque puissent trouver ses fichiers d'en-tête. J'obtiens le message d'erreur suivant (même si la génération n'est PAS interrompue).
CMake Error in CMakeLists.txt:
Target "Fonts" INTERFACE_INCLUDE_DIRECTORIES property contains path:
"D:/Projects/GPC/fonts/include"
which is prefixed in the source directory.
(D:/Projects/GPC/Fonts étant le répertoire de niveau supérieur de mon projet de bibliothèque. En fait, le problème persiste si je déplace mes fichiers d'en-tête vers le répertoire supérieur.)
La ligne incriminée dans mon CMakeLists.txt est la suivante (adaptée pour plus de simplicité):
target_include_directories(Fonts INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
Je ne comprends pas ce que je fais mal. Sans target_include_directories (), le code des projets grand public ne peut tout simplement pas inclure mes fichiers d'en-tête (sauf sous forme installée, mais je n'y suis pas encore arrivé, et en tout cas je veux pouvoir utiliser ma bibliothèque à partir de son arborescence de construction , sans installation.)
J'ai l'impression de manquer quelque chose de basique ici; pourtant j'ai cherché pendant des heures sans trouver de solution ou d'explication.
L'origine du problème n'est pas la commande target_include_directories
Elle-même, mais la tentative de install
une cible qui a un répertoire d'inclusion public ou d'interface préfixé dans le chemin source.
Bien qu'il soit parfaitement correct et souhaitable d'utiliser des chemins absolus lors de la construction de la bibliothèque à partir de zéro, une bibliothèque tierce qui extrait une version précompilée de cette bibliothèque voudra probablement utiliser un chemin d'inclusion différent. Après tout, vous ne voulez pas que tous vos utilisateurs reflètent la structure de répertoires de votre machine de génération, juste pour se retrouver dans le bon chemin d'inclusion.
Le mécanisme d'empaquetage de CMake prend en charge ces deux cas d'utilisation: vous pouvez extraire une bibliothèque directement à partir de l'arborescence de construction (c'est-à-dire, extraire la source, la construire et pointer find_package()
dans le répertoire), ou à partir d'un répertoire d'installation (exécutez make INSTALL
pour copier les éléments construits dans le répertoire d'installation et pointez find_package()
vers que répertoire). Cette dernière approche doit être déplaçable (c'est-à-dire que je construis et installe sur ma machine, je vous envoie le répertoire résultant et vous pourrez l'utiliser sur votre machine à partir d'une structure de répertoire différente), tandis que la première ne l'est pas.
Il s'agit d'une fonctionnalité très intéressante, mais vous devez en tenir compte lors de la configuration des répertoires include. Citant le manuel pour target_include_directories
:
Les exigences d'utilisation des répertoires d'inclusion diffèrent généralement entre l'arborescence de construction et l'arborescence d'installation. Les expressions de générateur
BUILD_INTERFACE
EtINSTALL_INTERFACE
Peuvent être utilisées pour décrire des exigences d'utilisation distinctes en fonction de l'emplacement d'utilisation. Les chemins relatifs sont autorisés dans l'expressionINSTALL_INTERFACE
Et sont interprétés par rapport au préfixe d'installation. Par exemple:target_include_directories(mylib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/mylib> $<INSTALL_INTERFACE:include/mylib> # <prefix>/include/mylib )
Les expressions de générateur BUILD_INTERFACE
Et INSTALL_INTERFACE
font toute la magie:
$<INSTALL_INTERFACE:...>
Contenu de
...
Lorsque la propriété est exportée à l'aide deinstall(EXPORT)
, et videz sinon.
$<BUILD_INTERFACE:...>
Contenu de
...
Lorsque la propriété est exportée à l'aide deexport()
, ou lorsque la cible est utilisée par une autre cible dans le même système de build. Se développe dans la chaîne vide sinon.