CMake'de GLOB veya her bir dosyayı ayrı ayrı belirtmek daha mı iyidir?


157

CMake, bir hedefin kaynak dosyalarını belirtmek için çeşitli yollar sunar. Bunlardan biri globbing ( dokümantasyon ) kullanmaktır , örneğin:

FILE(GLOB MY_SRCS dir/*)

Başka bir yöntem, her dosyayı ayrı ayrı belirtmektir.

Hangi yol tercih edilir? Globbing kolay görünüyor, ama bunun bazı dezavantajları olduğunu duydum.

Yanıtlar:


185

Tam açıklama: Başlangıçta basitliği için globbing yaklaşımını tercih ettim, ancak yıllar boyunca dosyaları açıkça listelemenin büyük, çok geliştirici projeleri için daha az hataya meyilli olduğunu fark ettim.

Orijinal cevap:


Globbingin avantajları:

  • Yeni dosyaları yalnızca tek bir yerde listelendikleri için eklemek kolaydır: diskte. Globbing değil çoğaltma oluşturur.

  • CMakeLists.txt dosyanız daha kısa olacaktır. Çok fazla dosyanız varsa bu büyük bir artı. Globbing değil, büyük dosya listeleri arasında CMake mantığını kaybetmenize neden olur.

Sabit kodlu dosya listelerini kullanmanın avantajları şunlardır:

  • CMake, diskteki yeni bir dosyanın bağımlılıklarını doğru bir şekilde izleyecektir - glob kullanırsak, CMake'i çalıştırdığınızda ilk kez globbed edilmeyen dosyalar alınmaz

  • Yalnızca istediğiniz dosyaların eklenmesini sağlarsınız. Globbing istemediğiniz başıboş dosyaları alabilir.

İlk soruna geçici bir çözüm bulmak için, dokunma komutunu kullanarak veya dosyayı değişiklik yapmadan yazarak, glob'u yapan CMakeLists.txt dosyasına "dokunabilirsiniz". Bu, CMake'i yeniden çalıştırmaya ve yeni dosyayı almaya zorlar.

İkinci sorunu düzeltmek için kodunuzu dikkatlice dizinler halinde düzenleyebilirsiniz. En kötü durumda, list(REMOVE_ITEM)globbed dosya listesini temizlemek için komutu kullanabilirsiniz :

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

Bunun sizi ısırdığı tek gerçek durum , kodunuzun eski sürümlerini aynı derleme dizininde denemek için git-bisect gibi bir şey kullanıyorsanız . Bu durumda, listede doğru dosyaları aldığınızdan emin olmak için gerekenden fazlasını temizlemeniz ve derlemeniz gerekebilir. Bu böyle bir köşe davası ve zaten ayak parmaklarınızın üzerinde olduğunuz bir dava, gerçekten bir sorun değil.


1
Ayrıca globbing ile kötü: git'in difftool dosyaları $ basename. $ Ext. $ Type. $ Pid. $ Ext olarak kaydedildi ve tek bir birleştirme çözünürlüğünden sonra derlemeye çalışırken eğlenceli hatalara neden olabilir.
mathstuf

9
Bence bu cevap, yeni dosyalar eksik olan cmake dezavantajları üzerinde parlıyor Simply "touch" the CMakeLists.txt, geliştiriciyseniz sorun değil, ancak yazılımınızı oluşturan diğerleri için, güncellemenizden sonra yapınızın başarısız olduğu ve yükü araştırmak için üzerlerinde bir nokta olabilir. neden.
ideasman42

36
Biliyor musun? Bu yanıtı 6 yıl önce yazdığımdan beri , fikrimi biraz değiştirdim ve şimdi dosyaları açıkça listelemeyi tercih ediyorum. Bu sadece gerçek bir dezavantaj "bir dosya eklemek için biraz daha fazla iş", ama size her türlü baş ağrısı kaydeder. Ve birçok açıdan açık, örtük olmaktan daha iyidir.
richq

1
@richq Bu git kancası mevcut konumunuzu yeniden düşünmenizi sağlar mı? :)
Antonio

8
Antonio'nun dediği gibi, "globbing" yaklaşımını savunmak için oylar verildi. Cevabın doğasını değiştirmek, bu seçmenler için yapılacak bir yem ve değişim işidir. Uzlaşma olarak, değiştirilmiş fikrimi yansıtacak bir düzenleme ekledim. Bir çay bardağında böyle bir fırtınaya neden olduğu için internetten özür dilerim :-P
richq

113

Kaynak dosyaları CMake'de belirtmenin en iyi yolu, bunları açıkça listelemektir .

CMake yaratıcıları kendileri tavsiyede değil globbing kullanımına.

Bkz. Https://cmake.org/cmake/help/v3.15/command/file.html?highlight=glob#file

(Kaynak ağacınızdan kaynak dosyalarının bir listesini toplamak için GLOB kullanılmasını önermiyoruz. Bir kaynak eklendiğinde veya kaldırıldığında CMakeLists.txt dosyası değişmezse, oluşturulan derleme sistemi CMake'den ne zaman yeniden oluşturulmasını isteyeceğini bilemez.)

Tabii ki, dezavantajların ne olduğunu bilmek isteyebilirsiniz - okumaya devam edin!


Globbing Başarısız Olduğunda:

Globbingin en büyük dezavantajı, dosya oluşturmanın / silmenin derleme sistemini otomatik olarak güncellememesidir.

Dosyaları ekleyen kişi sizseniz, bu kabul edilebilir bir değiş tokuş gibi görünebilir, ancak bu kodunuzu oluşturan diğer kişiler için sorunlara neden olur, projeyi sürüm kontrolünden günceller, derlemeyi çalıştırır, sonra sizinle iletişime
geçerek "derlemenin kırık".

Daha da kötüsü, başarısızlık genellikle sorunun nedenine dair herhangi bir ipucu vermeyen bazı bağlantı hataları verir ve sorun giderme sırasında zaman kaybedilir.

Üzerinde çalıştığım bir projede globbing yapmaya başladık, ancak yeni dosyalar eklendiğinde çok fazla şikayet aldık, dosyaları globbing yerine açıkça listelemek için yeterli sebepti.

Bu aynı zamanda yaygın git iş akışlarını
( git bisectve unsur dalları arasında geçiş yapmayı) keser .

Bu yüzden bunu tavsiye edemedim, neden olduğu problemler kolaylıktan çok daha ağır basar, birisi bu nedenle yazılımınızı oluşturamazsa, sorunu izlemek veya sadece vazgeçmek için çok zaman kaybedebilirler.

Ve başka bir not, Sadece dokunmayı hatırlamak CMakeLists.txther zaman yeterli değildir, globbing kullanan otomatik derlemelerle, dosyalar son binadan beri eklenmiş / kaldırılmış olabileceğinden her derlemeden cmakeönce çalıştırmak zorunda kaldım *.

Kural istisnaları:

Globbingin tercih edildiği zamanlar vardır:

  • CMakeLists.txtCMake kullanmayan mevcut projeler için bir dosya ayarlamak için .
    Başvurulan tüm kaynağı almanın hızlı bir yolu (derleme sistemi çalıştığında - globbing'i açık dosya listeleriyle değiştirin).
  • CMake birincil derleme sistemi olarak kullanılmadığında , örneğin CMake kullanmayan bir proje kullanıyorsanız ve bunun için kendi derleme sisteminizi korumak istiyorsanız.
  • Dosya listesinin o kadar sık ​​değiştiği her durumda, bakımı pratik olmaz. Bu durumda yararlı olabilir , ancak daha sonra güvenilir / doğru bir yapı oluşturmak için her zamancmake yapı dosyaları oluşturmak için çalışmayı kabul etmelisiniz (CMake'in amacına aykırı - yapılandırmayı binadan ayırma yeteneği) .

* Evet, bir güncellemeden önce ve sonra diskteki dosya ağacını karşılaştırmak için bir kod yazabilirdim, ancak bu böyle güzel bir geçici çözüm değil ve yapı sistemine bırakılan daha iyi bir şey değil.


9
"Globbing'in en büyük dezavantajı, yeni dosyalar oluşturmanın yapı sistemini otomatik olarak güncellememesidir." Ancak, glob yapmazsanız, CMakeLists.txt dosyasını el ile güncellemeniz gerektiği doğru değil mi, yani cmake hala inşa sistemini otomatik olarak güncellemiyor mu? Her iki şekilde de yeni dosyaların oluşturulması için manuel olarak bir şeyler yapmayı hatırlamanız gerekir. CMakeLists.txt'ye dokunmak, dosyayı açmaktan ve yeni dosyayı eklemek için düzenlemekten daha kolay görünüyor.
Dan

17
@Dan için senin sistemin - Eğer sadece tek başına geliştirmek emin, bu iyi, ama ne projeyi inşa kalan herkes? CMake dosyasına dokunup manuel olarak dokunmaları için onlara e-posta gönderecek misiniz? her dosya eklendiğinde veya kaldırıldığında? - CMake'de dosya listesini saklamak, yapının her zaman vcs'nin bildiği aynı dosyaları kullanmasını sağlar. İnanın - bu sadece ince bir ayrıntı değil - Derlemeniz birçok geliştirici için başarısız olduğunda - listeler postalar ve IRC'de kodun bozuk olduğunu sorarlar. Not: (Kendi sisteminizde bile, örneğin git geçmişine geri dönebilir ve içeri
girmeyi

2
Ah bu davayı düşünmemiştim. Globbinge karşı duymamın en iyi nedeni bu. Keşke cmake belgelerinin neden insanların kürek çekmekten kaçınmasını önlediklerine dair genişlemesini dilerim.
Dan

1
Ben son cmake yürütme zaman damgası dosyaya yazma çözümü düşünüyorum. Tek problemler şunlardır: 1) muhtemelen cmake tarafından çapraz platform olması gerekiyor ve bu nedenle cmake'nin bir şekilde ikinci kez çalışmasından kaçınmamız gerekiyor. 2) Muhtemelen daha fazla birleştirme çakışması (hala btw dosya listesi ile olur) Bu durumda daha sonra zaman damgası alarak önemsiz bir şekilde çözülebilirler.
Predelnik

2
@ tim-mb, "Ancak CMake, kontrol edebileceğiniz bir filetree_updated dosyası oluştursa iyi olur, bu da her dosya güncellendiğinde otomatik olarak değişir." - cevabımın ne yaptığını tam olarak açıkladınız.
Glen Knowles

22

CMake 3.12'de file(GLOB ...)vefile(GLOB_RECURSE ...) komutları CONFIGURE_DEPENDS, glob'ın değeri değişirse cmake'yi yeniden çalıştıran bir seçenek kazandı . Kaynak dosyalar için globbingin birincil dezavantajı olduğu için, şimdi bunu yapmakta sorun yok:

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

Bununla birlikte, bazı insanlar hala kaynaklar için globbingden kaçınmayı öneriyor. Gerçekten de, belgeler şunları belirtmektedir:

Kaynak ağacınızdan bir kaynak dosya listesi toplamak için GLOB kullanılmasını önermiyoruz. ... CONFIGURE_DEPENDSBayrak tüm jeneratörlerde güvenilir bir şekilde çalışmayabilir veya gelecekte destekleyemeyecek yeni bir jeneratör eklenirse, onu kullanan projeler takılır. CONFIGURE_DEPENDSGüvenilir bir şekilde çalışsa bile , her yeniden oluşturmada kontrolü gerçekleştirmenin bir maliyeti vardır.

Şahsen, olası dezavantajlardan daha ağır basmak için kaynak dosya listesini manuel olarak yönetmemenin faydalarını düşünüyorum. Manuel olarak listelenen dosyalara geri dönmek zorunda kalırsanız, bu sadece globbed kaynak listesini yazdırarak ve tekrar yapıştırarak kolayca elde edilebilir.


Oluşturma sisteminiz tam bir cmake ve build döngüsü gerçekleştirirse (build dizinini silin, cmake'yi oradan çalıştırın ve makefile'ı çağırın), istenmeyen dosyaları çekmedikleri sürece, kesinlikle GLOBbed kaynaklarını kullanmanın herhangi bir dezavantajı yoktur? Benim tecrübelerime göre, cmake kısmı yapıdan çok daha hızlı çalışıyor, bu yüzden yine de bir ek yük değil
Den-Jason

9

Bağımlılıkları korumak için ek bir dosya pahasına güvenli bir şekilde glob (ve muhtemelen gerekir).

Bir yere aşağıdaki gibi işlevler ekleyin:

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

Ve sonra globbing'e gidin:

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

Hala daha önce olduğu gibi açık bağımlılıklar etrafında toplanıyorsunuz (ve tüm otomatik yapıları tetikliyorsunuz!), Sadece bir dosya yerine iki dosyada.

Yordamdaki tek değişiklik, yeni bir dosya oluşturduktan sonra yapılır. İş akışı glob değilse, Visual Studio içinden CMakeLists.txt değiştirmek ve yeniden oluşturmak, glob yaparsanız cmake açıkça çalıştırın - ya da sadece CMakeLists.txt dokunun.


İlk başta bunun bir kaynak dosyası eklendiğinde Makefiles'i otomatik olarak güncelleyecek bir araç olduğunu düşündüm, ancak şimdi değerinin ne olduğunu görüyorum. Güzel! Bu, depodan güncellenen ve makegarip linker hataları veren birinin endişesini çözer .
Cris Luengo

1
Bunun iyi bir yöntem olabileceğine inanıyorum. Tabii ki, bir dosya ekledikten veya kaldırdıktan sonra cmake'yi tetiklemeyi hala hatırlamak zorundadır ve bu bağımlılık dosyasını da işlemek gerekir, bu nedenle kullanıcı tarafında bir miktar eğitim gereklidir. En büyük dezavantajı, bu bağımlılık dosyasının, geliştiricinin mekanizmayı biraz anlamasını gerektirmeden çözülmesi zor olabilecek kötü birleştirme çakışmaları oluşturabilmesidir.
Antonio

1
Projeniz koşullu olarak dosyalar içeriyorsa (örneğin, yalnızca bir özellik etkinleştirildiğinde kullanılan veya yalnızca belirli bir işletim sistemi için kullanılan bazı dosyalar) bu işe yaramaz. Taşınabilir yazılımlar için bazı dosyaların yalnızca belirli platformlarda kullanılması yeterince yaygındır.
ideasman42

0

Her dosyayı ayrı ayrı belirtin!

Güncellemek için geleneksel bir CMakeLists.txt ve bir python komut dosyası kullanıyorum. Dosyaları ekledikten sonra python komut dosyasını elle çalıştırıyorum.

Cevabımı buraya bakın: https://stackoverflow.com/a/48318388/3929196

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.