Лучше указать исходные файлы с GLOB или каждый файл отдельно в CMake? - PullRequest
137 голосов
/ 22 июня 2009

CMake предлагает несколько способов указать исходные файлы для цели. Один из них - использовать глобирование ( документация ), например:

FILE (GLOB dir/*)

Еще один способ - указать каждый файл отдельно.

Какой способ предпочитать? Шарить кажется легким, но я слышал, что у него есть некоторые недостатки.

Ответы [ 5 ]

170 голосов
/ 29 июня 2009

Полное раскрытие. Первоначально я предпочел глобальный подход из-за его простоты, но с годами я пришел к выводу, что явное перечисление файлов менее подвержено ошибкам для крупных проектов с несколькими разработчиками.

Оригинальный ответ:


Преимущества глобализации:

  • Легко добавлять новые файлы, так как они перечислены только в одном месте: на диск. Не сгущает создает дублирование.

  • Ваш файл CMakeLists.txt будет короче. Это большой плюс, если вы есть много файлов. Не сгущаться заставляет вас терять логику CMake среди огромных списков файлов.

Преимущества использования жестко закодированных списков файлов:

  • CMake будет правильно отслеживать зависимости нового файла на диске - если мы используем glob, то файлы, которые не были скопированы в первый раз, когда вы запустили CMake, не получат поднял

  • Вы гарантируете, что добавляются только те файлы, которые вы хотите. Глупбинг может забрать бродягу файлы, которые вам не нужны.

Чтобы обойти первую проблему, вы можете просто «дотронуться» до CMakeLists.txt, который делает глобус, либо с помощью команды touch, либо написав файл без изменений. Это заставит cmake перезапуститься и забрать новый файл.

Чтобы решить вторую проблему, вы можете аккуратно организовать свой код по каталогам, что вы, вероятно, в любом случае делаете. В худшем случае вы можете использовать команду list (REMOVE_ITEM), чтобы очистить глобализированный список файлов:

file(GLOB to_remove file_to_remove.cpp)
list(REMOVE_ITEM list ${to_remove})

Единственная реальная ситуация, когда это может вас укусить, это если вы используете что-то вроде git-bisect , чтобы попробовать старые версии вашего кода в том же каталоге сборки. В этом случае вам может потребоваться очистить и скомпилировать больше, чем необходимо, чтобы убедиться, что вы получаете нужные файлы в списке. Это такой угловой случай, и тот, где вы уже находитесь в затруднительном положении, что это на самом деле не проблема.

102 голосов
/ 30 августа 2013

Лучший способ указать исходные файлы в CMake - это , перечислив их явно .

Создатели CMake сами советуют не использовать globbing.

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

(Мы не рекомендуем использовать GLOB для сбора списка исходных файлов из дерева исходных текстов. Если файл CMakeLists.txt не изменяется при добавлении или удалении источника, то сгенерированная система сборки не может знать, когда попросить CMake сгенерировать заново. )

Конечно, вы, возможно, захотите узнать, каковы недостатки - читайте дальше!


Когда сбои не удаляются:

Большим недостатком глобализации является то, что создание / удаление файлов не приведет к автоматическому обновлению системы сборки.

Если вы являетесь человеком, добавляющим файлы, это может показаться приемлемым компромиссом, однако это создает проблемы для других людей, создающих ваш код, они обновляют проект из системы контроля версий, запускают сборку, а затем связываются с вами, жалуясь, что
"сборка сломана".

Что еще хуже, сбой, как правило, дает некоторую ошибку компоновки, которая не дает никаких указаний на причину проблемы и теряется время на ее устранение.

В проекте, над которым я работал, мы начинали с глобализации, но получали столько жалоб при добавлении новых файлов, что было достаточно оснований явно перечислять файлы вместо глобализации.

Это также нарушает общие рабочие процессы git
(git bisect и переключение между функциональными ветвями).

Так что я не мог бы порекомендовать это, проблемы, которые это вызывает, намного перевешивают удобство, когда кто-то из-за этого не может создать ваше программное обеспечение, он может потерять много времени, чтобы отследить проблему или просто сдаться.

И еще одно замечание: просто помнить о прикосновении CMakeLists.txt не всегда достаточно, с автоматическими сборками, использующими глобирование, мне пришлось запускать cmake перед каждой сборкой , так как файлы могут были добавлены / удалены со времени последнего строительства *.

Исключения из правила:

Бывают моменты, когда предпочтительнее использовать шатание:

  • Для настройки файлов CMakeLists.txt для существующих проектов, которые не используют CMake.
    Это быстрый способ получить ссылки на все источники (после запуска системы сборки - замените глобализацию явными списками файлов).
  • Когда CMake не используется в качестве основной системы сборки, если, например, вы используете проект, который не использует CMake, и вы хотите сохранить свою собственную систему сборки для это.
  • Для любой ситуации, когда список файлов меняется так часто, что его становится нецелесообразно поддерживать. В этом случае может быть полезным, но тогда вы должны принять запуск cmake для генерации файлов сборки каждый раз для получения надежной / правильной сборки (что происходит против намерения CMake - возможность отделить конфигурацию от сборки) .

* Да, я мог бы написать код для сравнения дерева файлов на диске до и после обновления, но это не очень хороший обходной путь, и что-то лучше оставить для системы сборки.

8 голосов
/ 11 октября 2016

Вы можете безопасно перемещаться (и, вероятно, должны) за счет дополнительного файла для хранения зависимостей.

Добавьте где-нибудь такие функции:

# 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)

А потом иди и проболтайся:

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

Вы по-прежнему перемещаетесь вокруг явных зависимостей (и запускаете все автоматические сборки!), Как и раньше, только в двух файлах вместо одного.

Единственное изменение в процедуре - после того, как вы создали новый файл. Если вы не переключаетесь, рабочий процесс состоит в том, чтобы изменить CMakeLists.txt из Visual Studio и перестроить, если вы делаете glob, вы явно запускаете cmake - или просто нажимаете CMakeLists.txt.

4 голосов
/ 22 марта 2019

В CMake 3.12 команды file(GLOB ...) и file(GLOB_RECURSE ...) получили опцию CONFIGURE_DEPENDS, которая повторно запускает cmake, если значение шара изменяется. Поскольку это было основным недостатком глобализации для исходных файлов, теперь это можно сделать так:

# 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})

Тем не менее, некоторые люди по-прежнему рекомендуют избегать скупых источников. Действительно, документация гласит:

Мы не рекомендуем использовать GLOB для сбора списка исходных файлов из вашего исходного дерева. ... Флаг CONFIGURE_DEPENDS может работать не надежно на всех генераторах, или если в будущем будет добавлен новый генератор, который не сможет его поддерживать, проекты, использующие его, будут заблокированы. Даже если CONFIGURE_DEPENDS работает надежно, проверка при каждой перестройке все равно стоит.

Лично я считаю, что нет необходимости вручную управлять списком исходных файлов, чтобы перевесить возможные недостатки. Если вам действительно нужно переключиться обратно на файлы, перечисленные вручную, это можно легко сделать, просто распечатав свернутый список источников и вставив его обратно.

0 голосов
/ 18 января 2018

Укажите каждый файл отдельно!

Я использую обычный CMakeLists.txt и скрипт Python для его обновления. Я запускаю скрипт python вручную после добавления файлов.

Смотрите мой ответ здесь: https://stackoverflow.com/a/48318388/3929196

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...