Apakah lebih baik untuk menentukan file sumber dengan GLOB atau setiap file secara individual di CMake?

157

CMake menawarkan beberapa cara untuk menentukan file sumber untuk target. Salah satunya adalah menggunakan globbing ( dokumentasi ), misalnya:

FILE(GLOB MY_SRCS dir/*)

Metode lain adalah menentukan setiap file secara individual.

Cara mana yang lebih disukai? Globbing sepertinya mudah, tetapi saya dengar ada beberapa kelemahan.

Marenz
sumber

Jawaban:

185

Pengungkapan penuh: Saya awalnya lebih suka pendekatan globbing karena kesederhanaannya, tetapi selama bertahun-tahun saya menyadari bahwa secara eksplisit daftar file tidak terlalu rentan kesalahan untuk proyek-proyek besar, multi-developer.

Jawaban asli:


Keuntungan dari globbing adalah:

  • Sangat mudah untuk menambahkan file baru karena hanya terdaftar di satu tempat: pada disk. Tidak globbing menciptakan duplikasi.

  • File CMakeLists.txt Anda akan lebih pendek. Ini merupakan nilai tambah besar jika Anda memiliki banyak file. Tidak menggumpal menyebabkan Anda kehilangan logika CMake di antara daftar besar file.

Keuntungan menggunakan daftar file hardcoded adalah:

  • CMake akan melacak dependensi file baru pada disk dengan benar - jika kita menggunakan glob maka file tidak menggumpal pertama kali ketika Anda menjalankan CMake tidak akan diambil

  • Anda memastikan bahwa hanya file yang Anda inginkan yang ditambahkan. Globbing dapat mengambil file liar yang tidak Anda inginkan.

Untuk mengatasi masalah pertama, Anda cukup "menyentuh" ​​CMakeLists.txt yang melakukan glob, baik dengan menggunakan perintah sentuh atau dengan menulis file tanpa perubahan. Ini akan memaksa CMake untuk menjalankan kembali dan mengambil file baru.

Untuk memperbaiki masalah kedua, Anda dapat mengatur kode Anda dengan hati-hati ke dalam direktori, yang mungkin Anda lakukan. Dalam kasus terburuk, Anda dapat menggunakan list(REMOVE_ITEM)perintah untuk membersihkan daftar berkas globbed:

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

Satu-satunya situasi nyata di mana ini dapat menggigit Anda adalah jika Anda menggunakan sesuatu seperti git-bisect untuk mencoba versi kode Anda yang lebih lama di direktori build yang sama. Dalam hal ini, Anda mungkin harus membersihkan dan mengkompilasi lebih dari yang diperlukan untuk memastikan Anda mendapatkan file yang tepat dalam daftar. Ini adalah kasus sudut, dan di mana Anda sudah berada di kaki Anda, bahwa itu tidak benar-benar masalah.

richq
sumber
1
Juga buruk dengan globbing: file gt's difftool disimpan sebagai $ basename. $ Ext. $ Type. $ Pid. $ Ext yang dapat menyebabkan kesalahan yang menyenangkan ketika mencoba mengkompilasi setelah resolusi gabungan tunggal.
mathstuf
9
Saya pikir jawaban ini menyoroti kelemahan cmake kehilangan file baru, Simply "touch" the CMakeLists.txttidak apa-apa jika Anda adalah pengembang, tetapi bagi orang lain membangun perangkat lunak Anda itu benar-benar dapat menjadi titik sakit yang membangun Anda gagal setelah memperbarui dan beban ada pada mereka untuk menyelidiki Mengapa.
ideasman42
36
Kamu tahu apa? Sejak menulis jawaban ini 6 tahun yang lalu , saya sedikit berubah pikiran dan sekarang lebih suka untuk secara eksplisit mendaftarkan file. Satu-satunya kelemahan sebenarnya adalah "ini sedikit lebih banyak pekerjaan untuk menambahkan file", tetapi itu menghemat semua jenis sakit kepala. Dan dalam banyak hal, eksplisit lebih baik daripada implisit.
richq
1
@ Richq Akankah git hook ini membuat Anda mempertimbangkan kembali posisi Anda saat ini? :)
Antonio
8
Seperti yang dikatakan Antonio, suara diberikan untuk mendukung pendekatan "globbing". Mengubah sifat jawaban adalah hal umpan-dan-beralih yang harus dilakukan untuk para pemilih. Sebagai kompromi, saya telah menambahkan suntingan untuk mencerminkan pendapat saya yang berubah. Saya minta maaf ke internet karena menyebabkan badai seperti itu dalam cangkir teh :-P
richq
113

Cara terbaik untuk menentukan sourcefile di CMake adalah dengan mendaftar secara eksplisit .

Pembuat CMake sendiri menyarankan untuk tidak menggunakan globbing.

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

(Kami tidak menyarankan menggunakan GLOB untuk mengumpulkan daftar file sumber dari hierarki sumber Anda. Jika tidak ada file CMakeLists.txt yang berubah ketika sumber ditambahkan atau dihapus, sistem build yang dihasilkan tidak tahu kapan harus meminta CMake untuk membuat ulang.)

Tentu saja, Anda mungkin ingin tahu apa kerugiannya - baca terus!


Ketika Globbing Gagal:

Kerugian besar dari globbing adalah membuat / menghapus file tidak akan secara otomatis memperbarui sistem build.

Jika Anda adalah orang yang menambahkan file, ini mungkin merupakan trade-off yang dapat diterima, namun hal ini menyebabkan masalah bagi orang lain yang membangun kode Anda, mereka memperbarui proyek dari kontrol versi, menjalankan build, kemudian menghubungi Anda, mengeluh bahwa
"build's rusak".

Untuk membuat keadaan menjadi lebih buruk, kegagalan biasanya memberikan beberapa kesalahan penghubung yang tidak memberikan petunjuk apa pun tentang penyebab masalah dan waktu hilang pemecahan masalah itu.

Dalam sebuah proyek yang saya kerjakan, kami memulai globbing tetapi mendapat begitu banyak keluhan ketika file baru ditambahkan, bahwa itu adalah alasan yang cukup untuk secara eksplisit mendaftar file daripada globbing.

Ini juga memecah alur kerja git yang umum
( git bisectdan beralih di antara cabang fitur).

Jadi saya tidak bisa merekomendasikan ini, masalah yang disebabkannya jauh melebihi kenyamanan, ketika seseorang tidak dapat membangun perangkat lunak Anda karena ini, mereka mungkin kehilangan banyak waktu untuk melacak masalah atau menyerah begitu saja.

Dan catatan lain, Hanya ingat untuk menyentuh CMakeLists.txttidak selalu cukup, dengan build otomatis yang menggunakan globbing, saya harus menjalankan cmakesebelum setiap build karena file mungkin telah ditambahkan / dihapus sejak bangunan terakhir *.

Pengecualian terhadap aturan:

Ada saat-saat globbing lebih disukai:

  • Untuk mengatur CMakeLists.txtfile untuk proyek yang ada yang tidak menggunakan CMake.
    Ini cara cepat untuk mendapatkan semua sumber yang direferensikan (setelah sistem build berjalan - ganti globbing dengan daftar file yang eksplisit).
  • Ketika CMake tidak digunakan sebagai sistem build utama , jika misalnya Anda menggunakan proyek yang tidak menggunakan CMake, dan Anda ingin mempertahankan sistem build Anda sendiri untuk itu.
  • Untuk setiap situasi di mana daftar file berubah begitu sering sehingga menjadi tidak praktis untuk dipelihara. Dalam hal ini bisa berguna, tetapi kemudian Anda harus menerima menjalankan cmakeuntuk menghasilkan file-file build setiap kali untuk mendapatkan build yang andal / benar (yang bertentangan dengan niat CMake - kemampuan untuk memisahkan konfigurasi dari gedung) .

* Ya, saya bisa menulis kode untuk membandingkan pohon file pada disk sebelum dan sesudah pembaruan, tetapi ini bukan solusi yang baik dan sesuatu yang lebih baik diserahkan kepada sistem build.

gagasanman42
sumber
9
"Kelemahan utama dari globbing adalah membuat file baru tidak akan secara otomatis memperbarui sistem build." Tetapi bukankah benar bahwa jika Anda tidak glob, Anda masih harus memperbarui CMakeLists.txt secara manual, artinya cmake masih belum secara otomatis memperbarui sistem build? Sepertinya Anda harus ingat untuk melakukan sesuatu secara manual agar file baru dapat dibuat. Menyentuh CMakeLists.txt tampaknya lebih mudah daripada membukanya dan mengeditnya untuk menambahkan file baru.
Dan
17
@Dan, untuk sistem Anda - tentu saja, jika Anda hanya mengembangkan sendiri ini baik-baik saja, tetapi bagaimana dengan orang lain yang membangun proyek Anda? Anda akan mengirim email kepada mereka untuk pergi dan menyentuh file CMake secara manual? setiap kali file ditambahkan atau dihapus? - Menyimpan daftar file di CMake memastikan build selalu menggunakan file yang sama yang diketahui vcs. Percayalah - ini bukan hanya detail halus - Ketika bangunan Anda gagal untuk banyak pengembang - mereka mengirimkan daftar dan bertanya pada IRC bahwa kode tersebut rusak. Catatan: (Bahkan pada sistem Anda sendiri, Anda dapat kembali dalam sejarah git misalnya, dan tidak berpikir untuk masuk dan menyentuh file CMake)
ideasman42
2
Ah saya belum memikirkan hal itu. Itulah alasan terbaik yang saya dengar menentang globbing. Saya berharap dokumen cmake diperluas tentang mengapa mereka merekomendasikan orang menghindari globbing.
Dan
1
Saya telah memikirkan solusi penulisan stempel waktu pelaksanaan cmake terakhir ke dalam file. Satu-satunya masalah adalah: 1) itu mungkin harus dilakukan oleh cmake untuk menjadi crossplatform dan oleh karena itu kita perlu menghindari cmake menjalankan dirinya sendiri untuk kedua kalinya. 2) Kemungkinan lebih banyak menggabungkan konflik (yang masih terjadi dengan daftar file btw) Mereka sebenarnya bisa diselesaikan secara sepele dalam kasus ini dengan mengambil cap waktu kemudian.
Predelnik
2
@ tim-mb, "Tapi alangkah baiknya jika CMake membuat file filetree_updated yang bisa Anda periksa, yang secara otomatis akan berubah setiap kali gumpalan file diperbarui." - Anda baru saja menjelaskan apa jawaban saya.
Glen Knowles
22

Dalam CMake 3.12, perintah file(GLOB ...)danfile(GLOB_RECURSE ...) memperoleh CONFIGURE_DEPENDSopsi yang menjalankan kembali cmake jika nilai glob berubah. Karena itu adalah kelemahan utama dari globbing untuk file sumber, sekarang boleh saja melakukannya:

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

Namun, beberapa orang masih merekomendasikan menghindari globbing untuk sumber. Memang, dokumentasi menyatakan:

Kami tidak menyarankan menggunakan GLOB untuk mengumpulkan daftar file sumber dari pohon sumber Anda. ... CONFIGURE_DEPENDSBendera mungkin tidak berfungsi dengan baik pada semua generator, atau jika generator baru ditambahkan di masa depan yang tidak dapat mendukungnya, proyek yang menggunakannya akan macet. Bahkan jika CONFIGURE_DEPENDSbekerja dengan andal, masih ada biaya untuk melakukan pemeriksaan pada setiap pembangunan kembali.

Secara pribadi, saya menganggap manfaat dari tidak harus mengelola secara manual daftar file sumber untuk lebih besar daripada kemungkinan kelemahannya. Jika Anda harus beralih kembali ke file yang terdaftar secara manual, ini dapat dengan mudah dicapai dengan hanya mencetak daftar sumber globbed dan menempelkannya kembali.

Justin
sumber
Jika sistem build Anda menjalankan siklus cmake dan build lengkap (hapus direktori build, jalankan cmake dari sana dan aktifkan makefile), asalkan mereka tidak menarik file yang tidak diinginkan, tentunya tidak ada kekurangan untuk menggunakan sumber GLOBbed? Dalam pengalaman saya, bagian cmake berjalan jauh lebih cepat daripada build, jadi toh itu tidak terlalu mahal
Den-Jason
9

Anda dapat dengan aman glob (dan mungkin harus) dengan mengorbankan file tambahan untuk menahan dependensi.

Tambahkan fungsi seperti ini di suatu tempat:

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

Dan kemudian pergi globbing:

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

Anda masih menggunakan dependensi eksplisit (dan memicu semua build otomatis!) Seperti sebelumnya, hanya saja dalam dua file, bukan satu.

Satu-satunya perubahan dalam prosedur adalah setelah Anda membuat file baru. Jika Anda tidak glob, alur kerjanya adalah memodifikasi CMakeLists.txt dari dalam Visual Studio dan membangun kembali, jika Anda melakukan glob, Anda menjalankan cmake secara eksplisit - atau cukup sentuh CMakeLists.txt.

Glen Knowles
sumber
Pada awalnya saya pikir ini adalah alat yang secara otomatis akan memperbarui Makefiles ketika file sumber ditambahkan, tetapi sekarang saya melihat nilainya. Bagus! Ini menyelesaikan masalah seseorang memperbarui dari repositori dan telah makememberikan kesalahan linker aneh.
Cris Luengo
1
Saya percaya ini bisa menjadi metode yang baik. Salah satu tentu saja masih ingat untuk memicu cmake setelah menambahkan atau menghapus file, dan itu juga memerlukan melakukan file dependensi ini, sehingga beberapa pendidikan di sisi pengguna diperlukan. Kelemahan utama adalah bahwa file ketergantungan ini dapat berasal dari konflik gabungan yang tidak menyenangkan yang mungkin sulit untuk dipecahkan tanpa lagi mengharuskan pengembang untuk memiliki pemahaman tentang mekanisme ini.
Antonio
1
Ini tidak akan berfungsi jika proyek Anda menyertakan file-file bersyarat (mis., Beberapa file yang hanya digunakan ketika fitur diaktifkan, atau hanya digunakan untuk sistem operasi tertentu). Ini cukup umum untuk perangkat lunak portabel sehingga beberapa file hanya digunakan untuk platform tertentu.
ideasman42
0

Tentukan setiap file secara individual!

Saya menggunakan CMakeLists.txt konvensional dan skrip python untuk memperbaruinya. Saya menjalankan skrip python secara manual setelah menambahkan file.

Lihat jawaban saya di sini: https://stackoverflow.com/a/48318388/3929196

palfi
sumber