Est-il préférable de spécifier les fichiers source avec GLOB ou chaque fichier individuellement dans CMake?

StackOverflow https://stackoverflow.com/questions/1027247

  •  06-07-2019
  •  | 
  •  

Question

CMake offre plusieurs façons de spécifier les fichiers source pour une cible. L’une consiste à utiliser globbing ( documentation ), par exemple:

FILE (GLOB dir/*)

Un autre consiste à spécifier chaque fichier individuellement.

Quel est le meilleur choix? Globbing semble facile, mais j'ai entendu dire qu'il a quelques inconvénients.

Était-ce utile?

La solution

Divulgation complète: au départ, j’avais préféré l’approche Globbing pour sa simplicité, mais au fil des années, j’ai reconnu que l’énumération explicite des fichiers était moins sujette aux erreurs pour les grands projets multi-développeurs.

Réponse originale:

Les avantages de globbing sont les suivants:

  • Il est facile d'ajouter de nouveaux fichiers car ils ne sont listés qu’à un seul endroit: sur disque. Ne pas créer crée la duplication.

  • Votre fichier CMakeLists.txt sera plus court. C'est un gros plus si vous avoir beaucoup de fichiers. Pas globbing vous fait perdre la logique CMake parmi d'énormes listes de fichiers.

Les avantages de l'utilisation de listes de fichiers codés en dur sont les suivants:

  • CMake suivra correctement les dépendances d’un nouveau fichier sur le disque - si nous utilisons Glob alors les fichiers non globbed première fois lorsque vous avez exécuté CMake ne sera pas ramassé

  • Vous vous assurez que seuls les fichiers de votre choix sont ajoutés. Globbing peut attraper errant les fichiers que vous ne voulez pas.

Pour contourner le premier problème, vous pouvez simplement "toucher" et 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 cela peut vous toucher est si vous utilisez quelque chose comme git-bisect pour essayer les anciennes versions 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. C’est un cas de ce genre, et vous êtes déjà sur vos gardes, ce n’est pas vraiment un problème.

Autres conseils

Le meilleur moyen de spécifier les fichiers source dans CMake consiste à les lister explicitement .

Les créateurs de CM se conseillent eux-mêmes de ne pas utiliser l’utilisation de globbing.

Voir: http: // www.cmake.org/cmake/help/v3.3/command/file.html?highlight=glob#file

  

(Nous déconseillons à GLOB de collecter une liste de fichiers source à partir de votre arborescence source. Si aucun fichier CMakeLists.txt ne change lorsque la source est ajoutée ou supprimée, le système de génération généré ne peut pas savoir quand demander à CMake de se régénérer. )

Bien sûr, vous voudrez peut-être connaître les inconvénients - lisez la suite!

Lorsque la globalisation échoue:

Le gros inconvénient 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 être un compromis acceptable. Toutefois, les autres personnes qui créent votre code doivent mettre à jour le projet. Elles mettent à jour le projet à partir du contrôle de version, exécutez build, puis vous contactent en vous plaignant.
"la construction est endommagé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 fait perdre du temps à le résoudre.

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.

Ceci rompt également les flux de travaux git courants
( la coupe de git et le passage d'une branche à une autre).

Je ne pouvais donc pas le recommander, les problèmes que cela pose l'emportent largement sur la commodité. Lorsque quelqu'un ne peut pas construire votre logiciel pour cette raison, il risque de perdre beaucoup de temps pour analyser le problème ou tout simplement pour abandonner. / p>

Et une autre remarque, se rappeler de toucher CMakeLists.txt ne suffit pas toujours, avec les versions automatisées qui utilisent le globbing, je devais exécuter cmake avant chaque build depuis que les fichiers ont peut-être été ajoutés / supprimés depuis le dernier build *.

Exceptions à la règle:

Il est parfois préférable de "balayer":

  • Pour configurer un fichier CMakeLists.txt pour des projets existants qui n'utilisent pas CMake.
    C'est un moyen rapide d'obtenir toutes les sources référencées (une fois que le système de construction est en cours d'exécution - remplacez globbing avec des listes de fichiers explicites).
  • Lorsque CMake n'est pas utilisé comme système de construction primaire , si, par exemple, vous utilisez un projet qui n'utilise pas CMake, et que vous souhaitez conserver votre propre système de construction pour cela.
  • Pour toute situation dans laquelle la liste de fichiers change si souvent qu'il devient difficile de la gérer. Dans ce cas, cela pourrait être utile, mais vous devez alors accepter de lancer cmake pour générer des fichiers de construction à chaque fois afin d'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 cette solution de contournement n'est pas si 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 ensuite, baladez-vous:

file(GLOB_RECURSE sources LIST_DIRECTORIES false *.h *.cpp)
update_deps_file("${sources}")
add_executable(test ${sources})

Vous continuez à utiliser les dépendances explicites (et à déclencher toutes les versions automatisées!) comme auparavant, mais uniquement dans deux fichiers au lieu d'un.

Le seul changement de procédure est après la création d'un nouveau fichier. Si vous ne globalisez pas le flux de travail, vous devez modifier CMakeLists.txt depuis Visual Studio et la reconstruire. Si vous globissez, vous exécutez cmake explicitement - ou vous touchez simplement CMakeLists.txt.

Dans CMake 3.12, le fichier (GLOB ...) et le fichier (GLOB_RECURSE ...) a obtenu une option CONFIGURE_DEPENDS qui réexécute cmake si la valeur du glob change. Comme c'était le principal inconvénient de la recherche de fichiers source, vous pouvez maintenant 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 la documentation indique:

  

Nous déconseillons d’utiliser GLOB pour collecter une liste de fichiers source à partir de votre arborescence. ... L’indicateur CONFIGURE_DEPENDS peut ne pas fonctionner de manière fiable sur tous les générateurs. Si les nouveaux générateurs qui ne le prennent pas en charge sont ajoutés, les projets l’utilisant seront bloqués. Même si CONFIGURE_DEPENDS fonctionne de manière fiable, la vérification à chaque reconstruction est payante.

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.

Spécifiez chaque fichier individuellement!

J'utilise un CMakeLists.txt conventionnel et un script python pour le mettre à jour. J'exécute le script python manuellement après avoir ajouté des fichiers.

Voir ma réponse ici: https://stackoverflow.com/a/48318388/3929196

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top