CMake offre plusieurs façons de spécifier les fichiers source pour une cible. La première consiste à utiliser globbing ( documentation ), par exemple:
FILE (GLOB dir/*)
Une autre consiste à spécifier chaque fichier individuellement.
De quelle façon préférer? Globbing semble facile, mais j'ai entendu dire qu'il a quelques inconvénients.
Divulgation complète: à l'origine, je préférais l'approche globbing pour sa simplicité, mais au fil des ans, j'ai fini par reconnaître que lister explicitement les fichiers était moins sujet aux erreurs pour les grands projets multi-développeurs.
Réponse originale:
Les avantages de globbing sont:
Il est facile d'ajouter de nouveaux fichiers car ils ne sont répertoriés qu'à un seul endroit: sur le disque. Le fait de ne pas déplacer crée de la duplication.
Votre fichier CMakeLists.txt sera plus court. C'est un gros plus si vous avez beaucoup de fichiers. Le fait de ne pas effectuer de déplacement entraîne la perte de la logique CMake au sein de vastes listes de fichiers.
L'utilisation des listes de fichiers en dur présente les avantages suivants:
CMake suivra correctement les dépendances d'un nouveau fichier sur le disque - si nous utilisons glob, les fichiers qui ne sont pas globalisés pour la première fois lorsque vous avez exécuté CMake ne seront pas récupérés.
Vous vous assurez que seuls les fichiers de votre choix sont ajoutés. Globbing peut récupérer des fichiers parasites que vous ne voulez pas.
Afin de contourner le premier problème, vous pouvez simplement "toucher" le CMakeLists.txt qui effectue le glob, soit en utilisant la commande touch, soit en écrivant le fichier sans modification. Cela forcera cmake à réexécuter et à récupérer le nouveau fichier.
Pour résoudre le deuxième problème, vous pouvez organiser votre code avec soin dans des répertoires, ce que vous ferez probablement de toute façon. Dans le pire des cas, vous pouvez utiliser la commande list (REMOVE_ITEM) pour nettoyer la liste de fichiers globale:
file(GLOB to_remove file_to_remove.cpp)
list(REMOVE_ITEM list ${to_remove})
La seule situation réelle dans laquelle ceci peut vous mordre est si vous utilisez quelque chose comme git-bisect pour essayer des versions plus anciennes de votre code dans le même répertoire de construction. Dans ce cas, vous devrez peut-être nettoyer et compiler plus de ressources que nécessaire pour vous assurer que les fichiers appropriés figurent dans la liste. Il s’agit là d’un cas d’angle où vous êtes déjà sur le qui-vive. Ce n’est pas vraiment un problème.
Le meilleur moyen de spécifier les fichiers source dans CMake consiste à à les lister explicitement .
Les créateurs de CM se conseillent de ne pas utiliser la suppression.
Voir: https://cmake.org/cmake/help/v3.15/command/file.html?highlight=glob#file
(Nous ne recommandons pas d'utiliser GLOB pour collecter une liste de fichiers source à partir de votre arborescence source. Si aucun fichier CMakeLists.txt ne change à la suite de l'ajout ou de la suppression d'une source, le système de génération généré ne sait pas quand demander à CMake de se régénérer.)
Bien sûr, vous voudrez peut-être savoir quels sont les inconvénients - lisez la suite !
L'inconvénient majeur de la navigation est que la création/suppression de fichiers ne met pas automatiquement à jour le système de construction.
Si vous êtes la personne qui ajoute les fichiers, cela peut sembler un compromis acceptable. Cependant, cela pose des problèmes à d'autres personnes qui construisent votre code. Elles mettent à jour le projet à partir du contrôle de version, exécutez build, puis vous contactent, en vous plaignant.
"la construction est cassée".
Pour aggraver les choses, l’échec donne généralement une erreur de liaison qui ne donne aucune indication sur la cause du problème et qui fait perdre du temps.
Dans un projet sur lequel nous avons travaillé, nous avons commencé à globaliser, mais nous avons reçu tellement de plaintes lorsque de nouveaux fichiers ont été ajoutés que c'était une raison suffisante pour répertorier explicitement les fichiers au lieu de globuler.
Cela rompt également les flux de travail git courants
(git bisect
et basculement entre les branches d’entités).
Je ne peux donc pas le recommander, car les problèmes qu’il pose dépassent de loin la commodité. Lorsque quelqu'un ne peut pas construire votre logiciel pour cette raison, il risque de perdre beaucoup de temps pour détecter le problème ou tout simplement abandonner.
Et une autre note, rappelant juste de toucher CMakeLists.txt
_ ne suffit pas toujours, avec les versions automatisées qui utilisent la méthode Globbing, je devais exécuter cmake
avant chaque version depuis les fichiers might ont été ajoutés/supprimés depuis le dernier bâtiment *.
Il y a des moments où le gloutonnage est préférable:
CMakeLists.txt
fichiers pour les projets existants qui n'utilisent pas CMake.cmake
pour générer des fichiers de construction à chaque fois pour obtenir un fichier fiable./correct build (ce qui va à l’encontre de l’intention de CMake - la possibilité de séparer la configuration du bâtiment).* Oui, j'aurais pu écrire un code pour comparer l'arborescence des fichiers sur le disque avant et après une mise à jour, mais ce n'est pas une solution de contournement aussi agréable et il vaut mieux laisser quelque chose au système de construction. = =
Vous pouvez sans risque glob (et probablement le faire) au prix d'un fichier supplémentaire contenant les dépendances.
Ajoutez des fonctions comme celles-ci quelque part:
# Compare the new contents with the existing file, if it exists and is the
# same we don't want to trigger a make by changing its timestamp.
function(update_file path content)
set(old_content "")
if(EXISTS "${path}")
file(READ "${path}" old_content)
endif()
if(NOT old_content STREQUAL content)
file(WRITE "${path}" "${content}")
endif()
endfunction(update_file)
# Creates a file called CMakeDeps.cmake next to your CMakeLists.txt with
# the list of dependencies in it - this file should be treated as part of
# CMakeLists.txt (source controlled, etc.).
function(update_deps_file deps)
set(deps_file "CMakeDeps.cmake")
# Normalize the list so it's the same on every machine
list(REMOVE_DUPLICATES deps)
foreach(dep IN LISTS deps)
file(RELATIVE_PATH rel_dep ${CMAKE_CURRENT_SOURCE_DIR} ${dep})
list(APPEND rel_deps ${rel_dep})
endforeach(dep)
list(SORT rel_deps)
# Update the deps file
set(content "# generated by make process\nset(sources ${rel_deps})\n")
update_file(${deps_file} "${content}")
# Include the file so it's tracked as a generation dependency we don't
# need the content.
include(${deps_file})
endfunction(update_deps_file)
Et puis allez-y:
file(GLOB_RECURSE sources LIST_DIRECTORIES false *.h *.cpp)
update_deps_file("${sources}")
add_executable(test ${sources})
Vous continuez de manipuler les dépendances explicites (et de déclencher toutes les versions automatisées!), Comme auparavant, mais uniquement dans deux fichiers au lieu d'un.
Le seul changement de procédure survient après la création d'un nouveau fichier. Si vous ne globalisez pas le flux de travail, vous devez modifier CMakeLists.txt à partir de Visual Studio et la reconstruire. Si vous globissez, vous exécutez cmake explicitement - ou vous touchez simplement CMakeLists.txt.
Dans CMake 3.12, les commandes file(GLOB ...)
et file(GLOB_RECURSE ...)
ont obtenu un CONFIGURE_DEPENDS
option qui relit cmake si la valeur du glob change. Comme c'était le principal inconvénient de la recherche de fichiers source, il est maintenant possible de le faire:
# Whenever this glob's value changes, cmake will rerun and update the build with the
# new/removed files.
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.cpp")
add_executable(my_target ${sources})
Cependant, certaines personnes recommandent encore d'éviter de déplacer les sources. En effet, la documentation dit:
Nous vous déconseillons d’utiliser GLOB pour collecter une liste de fichiers source à partir de votre arborescence. ... Le
CONFIGURE_DEPENDS
drapeau peut ne pas fonctionner de manière fiable sur tous les générateurs, ou si un nouveau générateur est ajouté à l'avenir et ne peut pas le prendre en charge, les projets utilisant ce générateur seront bloqués. Même siCONFIGURE_DEPENDS
fonctionne de manière fiable, il y a toujours un coût pour effectuer le contrôle à chaque reconstruction.
Personnellement, j’estime que l’avantage de ne pas avoir à gérer manuellement la liste des fichiers source est plus important que ses inconvénients. Si vous devez revenir à des fichiers répertoriés manuellement, vous pouvez facilement y parvenir simplement en imprimant la liste de sources globalisée et en la collant.