Чи краще вказати вихідні файли за допомогою GLOB або кожного файлу окремо в CMake?


157

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

FILE(GLOB MY_SRCS dir/*)

Ще один спосіб - вказати кожен файл окремо.

Який спосіб є кращим? Глобінг здається легким, але я чув, що у нього є деякі недоліки.

Відповіді:


185

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

Оригінальна відповідь:


Перевагами глобалізації є:

  • Додавати нові файли легко, оскільки вони перераховані лише в одному місці: на диску. Не глобулювання створює дублювання.

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

Перевагами використання твердо кодованих списків файлів є:

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

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

Щоб вирішити перший випуск, ви можете просто "торкнутися" CMakeLists.txt, який виконує глобус, або за допомогою сенсорної команди, або записавши файл без змін. Це змусить CMake повторно запустити і забрати новий файл.

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

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

Єдина реальна ситуація, коли це може вас вкусити - це якщо ви використовуєте щось подібне до git-bisect, щоб спробувати більш старі версії свого коду в одному каталозі збірки. У такому випадку вам, можливо, доведеться очищати та збирати більше, ніж потрібно, щоб забезпечити отримання потрібних файлів у списку. Це такий кутовий випадок, і той, де ви вже на ногах, це насправді не проблема.


1
Також погано з глобулюванням: файли difftool git зберігаються як $ basename. $ Ext. $ Type. $ Pid. $ Ext, які можуть викликати веселі помилки при спробі компіляції після єдиного дозволу злиття.
mathstuf

9
Я думаю, що ця відповідь висвітлює недоліки cmake відсутніх нових файлів. Simply "touch" the CMakeLists.txtДобре, якщо ви розробник, але для інших, хто будує ваше програмне забезпечення, це справді може бути больовим моментом, що ваша збірка не вдасться після оновлення, і на них лежить тягар розслідувати. чому.
ideaman42

36
Знаєш, що? З часу написання цієї відповіді 6 років тому я трохи передумав і тепер віддаю перевагу явно перелічити файли. Єдиним справжнім недоліком є ​​"трохи більше роботи над тим, щоб додати файл", але це рятує від вас всілякі головні болі. І багато в чому явне краще, ніж неявне.
richq

1
@richq Чи змусив би вас цей гак-гук переглянути своє поточне становище? :)
Антоніо

8
Як каже Антоніо, голоси були віддані за відстоювання "глобального" підходу. Зміна характеру відповіді - це важлива річ для цих виборців. В якості компромісу я додав редагування, щоб відобразити мою змінену думку. Прошу вибачення в Інтернеті за те, що спричинив таку бурю в чашці :-P
richq

113

Найкращий спосіб вказати вихідні файли в CMake - це чітко вказати їх .

Самі творці CMake радять не використовувати глобінг.

Дивіться: https://cmake.org/cmake/help/v3.15/command/file.html?highlight=glob#file

(Ми не рекомендуємо використовувати GLOB для збору списку вихідних файлів з вашого вихідного дерева. Якщо жоден файл CMakeLists.txt не змінюється, коли джерело додається або видаляється, то створена система збірки не може знати, коли просити CMake відновити.)

Звичайно, можливо, ви захочете дізнатися, які недоліки - читайте далі!


Коли глобус не вдається:

Великим недоліком глобалізації є те, що створення / видалення файлів не оновлює автоматично збірку системи.

Якщо ви є особою, яка додає файли, це може здатися прийнятним компромісом, однак це спричиняє проблеми іншим людям, які будують ваш код, вони оновлюють проект із контролю версій, запускають збірку, а потім зв'язуються з вами, скаржившись, що
"збірка зламаний ».

Що ще гірше, помилка, як правило, дає деяку помилку посилання, яка не дає жодних підказів щодо причини проблеми, і час на її усунення втрачається.

У проекті, над яким я працював, ми розпочали глобалізацію, але отримали так багато скарг, коли були додані нові файли, що це було достатньою підставою для явного переліку файлів замість глобалізації.

Це також порушує загальні робочі потоки git
( git bisectта перемикання між функціональними гілками).

Тому я не міг рекомендувати це, проблеми, які він викликає, значно перевищують зручність, коли хтось не може створити ваше програмне забезпечення через це, вони можуть втратити багато часу, щоб відстежити проблему або просто відмовитись.

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

Винятки з правила:

Бувають випадки, коли глобус кращий:

  • Налаштування CMakeLists.txtфайлів для існуючих проектів, які не використовують CMake.
    Це швидкий спосіб отримати все посилання на джерело (після запуску системи збирання - замініть глобалізацію явними списками файлів).
  • Якщо CMake не використовується в якості основної системи збирання, якщо, наприклад, ви використовуєте проект, який не використовує CMake, і ви хочете підтримувати власну систему збирання для нього.
  • У будь-якій ситуації, коли список файлів змінюється так часто, що підтримувати їх недоцільно. У цьому випадку це може бути корисним, але тоді вам доведеться щоразу приймати запуск cmakeдля створення файлів збірки, щоб отримати надійну / правильну збірку (що суперечить наміру CMake - можливості розділити конфігурацію від будівлі) .

* Так, я міг би написати код для порівняння дерева файлів на диску до та після оновлення, але це не такий приємний спосіб вирішення, і щось краще залишається в системі збирання.


9
"Великим недоліком глобалізації є те, що створення нових файлів не оновлює автоматично збірку системи." Але чи не правда, що якщо ви не глобус, вам все одно доведеться вручну оновлювати CMakeLists.txt, тобто cmake все ще не автоматично оновлює систему збирання? Здається, як у будь-якому випадку, ви повинні пам'ятати, щоб зробити щось вручну для того, щоб нові файли збиралися. Дотик до CMakeLists.txt здається простішим, ніж відкривати його та редагувати, щоб додати новий файл.
Дан

17
@Dan, для вашої системи - впевнено, якщо ви розвиваєтеся тільки в одиночку, це добре, але як бути з усіма, хто будує ваш проект? Ви збираєтесь надіслати їх електронною поштою, щоб перейти та вручну торкнутися файлу CMake? кожного разу, коли файл додається чи видаляється? - Зберігання списку файлів у CMake гарантує, що збірка завжди використовує ті самі файли, про які знає vcs. Повірте - це не лише якась тонка деталь. Коли ваша збірка не працює для багатьох розробників, вони надсилають списки і запитують в IRC, що код порушений. Примітка: (Навіть у власній системі ви можете повернутися в історію git, наприклад, і не думаєте заходити та торкатися файлів CMake)
ideaman42

2
Ах, я не думав про цю справу. Це найкраща причина, яку я чув проти глобалізації. Я б хотів, щоб документи cmake розширилися, чому вони рекомендують людям уникати глобалізації.
Дан

1
Я думав над рішенням написання часової позначки останнього виконання cmake у файл. Єдині проблеми полягають у тому, що: 1) це, мабуть, повинно бути виконано cmake, щоб бути кросплатформою, і тому нам потрібно уникати того, щоб cmake запускалася сама вдруге. 2) Можливо, більше конфліктів злиття (які все ще трапляються зі списком файлів btw) У цьому випадку вони фактично можуть бути вирішені тривіально, взявши пізніші позначки часу.
Предельник

2
@ tim-mb, "Але було б непогано, якби CMake створив файл filetree_update, який ви могли б зареєструвати, який він автоматично мінятиме щоразу, коли глобус файлів оновлюється." - ви точно точно описали, що робить моя відповідь.
Глен Ноулз

21

У 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надійно працює, все одно варто перевірити кожну перебудову.

Особисто я вважаю переваги того, що не потрібно вручну керувати списком вихідних файлів, щоб переважати можливі недоліки. Якщо вам доведеться перейти назад до файлів, що перераховуються вручну, цього можна легко досягти, просто надрукувавши глобальний список джерел і вставивши його назад.


Якщо ваша система збирання виконує повний цикл cmake і build (видаліть каталог збирання, запустіть cmake звідти і потім попросіть makefile), за умови, що вони не втягують непотрібні файли, напевно немає недоліків у використанні джерел GLOBbed? На мій досвід, частина cmake працює набагато швидше, ніж збірка, так що це не так вже й великі накладні витрати
Den-Jason

9

Ви можете сміливо глобувати (і, мабуть, повинні) ціною додаткового файлу, щоб утримати залежності.

Десь додайте такі функції:

# 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 і перевстановити, якщо ви почнете глобально, запускаєте cmake явно - або просто торкаєтесь CMakeLists.txt.


Спочатку я думав, що це інструмент, який автоматично оновлює Makefiles, коли додається вихідний файл, але тепер я бачу, яке його значення. Приємно! Це вирішує занепокоєння того, що хтось оновлюється з сховища та має makeдивні помилки в посиланнях.
Кріс Луенго

1
Я вважаю, що це може бути хорошим методом. Звичайно, слід пам’ятати, щоб запустити cmake після додавання або видалення файлу, а також потрібно ввести цей файл залежності, тому необхідна певна освіта з боку користувача. Основним недоліком може бути те, що цей файл залежності може породжувати неприємні конфлікти злиття, які може бути важко вирішити, не вимагаючи від розробника певного розуміння механізму.
Антоніо

1
Це не спрацює, якщо ваш проект умовно включає файли (наприклад, деякі файли, які використовуються лише тоді, коли функція включена або використовується лише для певної операційної системи). Для портативного програмного забезпечення досить поширено, що деякі файли використовуються лише для особливих платформ.
ideaman42

0

Вкажіть кожен файл окремо!

Для оновлення я використовую звичайний CMakeLists.txt та сценарій python. Я запускаю скрипт python вручну після додавання файлів.

Дивіться мою відповідь тут: https://stackoverflow.com/a/48318388/3929196

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.