Bagaimana CMake digunakan? [Tutup]

95

Sangat sulit untuk mendapatkan informasi yang berguna tentang CMake sebagai pemula. Sejauh ini, saya telah melihat beberapa tutorial tentang cara menyiapkan beberapa proyek yang sangat mendasar atau lainnya. Namun, tidak satupun dari ini menjelaskan alasan di balik apa pun yang ditampilkan di dalamnya, selalu meninggalkan banyak lubang untuk diisi.

Apa memanggil CMake pada CMakeLists berarti ? Apakah itu seharusnya dipanggil sekali per membangun pohon atau apa? Bagaimana cara menggunakan pengaturan berbeda untuk setiap build jika semuanya menggunakan file CMakeLists.txt yang sama dari sumber yang sama? Mengapa setiap subdirektori membutuhkan file CMakeListsnya sendiri? Apakah masuk akal untuk menggunakan CMake di CMakeLists.txt selain yang ada di root proyek? Jika ya, dalam kasus apa? Apa perbedaan antara menentukan cara membuat file yang dapat dieksekusi atau pustaka dari file CMakeLists di subdirektori mereka sendiri dengan melakukannya di file CMakeLists di root semua sumber? Dapatkah saya membuat proyek untuk Eclipse dan yang lainnya untuk Visual Studio, hanya mengubah -Gopsi saat memanggil CMake? Apakah itu cara penggunaannya?

Tak satu pun dari tutorial, halaman dokumentasi, atau pertanyaan / jawaban yang sejauh ini saya lihat memberikan wawasan yang berguna untuk memahami cara menggunakan CMake. Contoh-contohnya tidak menyeluruh. Tidak peduli tutorial apa yang saya baca, saya merasa seperti kehilangan sesuatu yang penting.

Ada banyak pertanyaan yang diajukan oleh pemula CMake seperti saya yang tidak menanyakan hal ini secara eksplisit, tetapi itu menjelaskan fakta bahwa, sebagai newbs, kami tidak tahu bagaimana menangani CMake atau apa yang membuatnya.

leinaD_natipaC
sumber
8
Mungkin Anda harus menulis posting blog sebagai gantinya.
steveire
2
Saya tergoda untuk menutup suara "terlalu luas" ... Ini terasa lebih seperti esai pribadi di CMake daripada cocok untuk stackoverflow. Mengapa Anda tidak memasukkan semua pertanyaan ini ke dalam pertanyaan terpisah? Dan bagaimana pertanyaan + jawaban Anda berbeda dari banyak pertanyaan lainnya, misalnya stackoverflow.com/q/20884380/417197 , stackoverflow.com/q/4745996/417197 , stackoverflow.com/q/4506193/417197 ?
André
13
@Andre Mengapa pertanyaan ini: Saya menghabiskan seminggu mencoba memahami CMake dan tidak ada tutorial yang saya temukan selesai. Tautan yang Anda tunjukkan itu bagus, tetapi bagi seseorang yang menemukan CMake karena mereka mendapat proyek yang dilemparkan kepada mereka dengan sekelompok CMakeList di dalamnya, mereka tidak menjelaskan banyak tentang cara kerja CMake. Sejujurnya, saya mencoba menulis jawaban yang saya harap dapat saya kirimkan kembali pada diri saya sendiri ketika saya mulai mencoba belajar CMake. Dan sub-pertanyaan dimaksudkan untuk menunjukkan bahwa saya sebenarnya mencoba untuk menanyakan apa yang harus saya ketahui agar tidak memiliki keraguan itu. Mengajukan pertanyaan yang tepat itu SULIT.
leinaD_natipaC
2
Bukan jawaban, tapi saya mengundang Anda untuk melihat jaws.rootdirectory.de . Sementara dokumentasi CMake (dan jawaban yang diterima ...) menunjukkan kepada Anda banyak potongan kecil tentang apa yang dapat Anda lakukan, saya menulis penyiapan dasar (tidak berarti "lengkap" atau "sempurna"), termasuk komentar di seluruh, yang IMHO menampilkan apa yang dapat dilakukan CMake (dan CTest dan CPack dan CDash ...) untuk Anda. Itu dibangun di luar kotak, dan Anda dapat dengan mudah menyesuaikan / memperluasnya dengan kebutuhan proyek Anda.
DevSolar
9
Sayang sekali pertanyaan seperti itu tertutup sekarang. Saya pikir pertanyaan luas yang membutuhkan tutorial seperti jawaban tetapi tentang topik yang didokumentasikan dengan buruk / tidak jelas (contoh lain adalah api Torch7 C) dan pertanyaan tingkat penelitian harus tetap terbuka. Itu jauh lebih menarik daripada tipikal "hei, saya punya segfault saat membuat proyek mainan" yang mengganggu situs.
Ash

Jawaban:

139

Untuk apa CMake?

Menurut Wikipedia:

CMake adalah perangkat lunak [...] untuk mengelola proses pembangunan perangkat lunak menggunakan metode compiler-independent. Ini dirancang untuk mendukung hierarki direktori dan aplikasi yang bergantung pada banyak pustaka. Ini digunakan bersama dengan lingkungan build asli seperti make, Apple's Xcode, dan Microsoft Visual Studio.

Dengan CMake, Anda tidak perlu lagi mempertahankan setelan terpisah khusus untuk lingkungan kompiler / build Anda. Anda memiliki satu konfigurasi, dan itu berfungsi untuk banyak lingkungan.

CMake dapat membuat solusi Microsoft Visual Studio, proyek Eclipse, atau labirin Makefile dari file yang sama tanpa mengubah apa pun di dalamnya.

Dengan adanya sekumpulan direktori dengan kode di dalamnya, CMake mengelola semua dependensi, perintah pembuatan, dan tugas lain yang perlu diselesaikan oleh proyek Anda sebelum dapat dikompilasi. Itu TIDAK benar-benar mengkompilasi apa pun. Untuk menggunakan CMake, Anda harus memberitahukannya (menggunakan file konfigurasi yang disebut CMakeLists.txt) executable apa yang perlu Anda kompilasi, library apa yang mereka tautkan, direktori apa yang ada di proyek Anda dan apa yang ada di dalamnya, serta detail apa pun seperti flag. atau apa pun yang Anda butuhkan (CMake cukup kuat).

Jika ini sudah diatur dengan benar, Anda kemudian menggunakan CMake untuk membuat semua file yang "lingkungan build asli" pilihan Anda perlu melakukan tugasnya. Di Linux, secara default, ini berarti Makefiles. Jadi begitu Anda menjalankan CMake, itu akan membuat banyak file untuk digunakan sendiri ditambah beberapa Makefile. Yang perlu Anda lakukan selanjutnya adalah mengetikkan "make" di konsol dari folder root setiap kali Anda selesai mengedit kode Anda, dan bam, file executable yang dikompilasi dan ditautkan dibuat.

Bagaimana cara kerja CMake? Apa fungsinya?

Berikut adalah contoh pengaturan proyek yang akan saya gunakan di seluruh:

simple/
  CMakeLists.txt
  src/
    tutorial.cxx
    CMakeLists.txt
  lib/
    TestLib.cxx
    TestLib.h
    CMakeLists.txt
  build/

Isi setiap file akan ditampilkan dan dibahas nanti.

CMake mengatur proyek Anda sesuai dengan root CMakeLists.txt proyek Anda, dan melakukannya di direktori apa pun yang Anda jalankan cmakedari dalam konsol. Melakukan ini dari folder yang bukan root proyek Anda menghasilkan apa yang disebut build out-of-source , yang berarti file yang dibuat selama kompilasi (file obj, file lib, executable, Anda tahu) akan ditempatkan di folder tersebut , disimpan terpisah dari kode sebenarnya. Ini membantu mengurangi kekacauan dan lebih disukai untuk alasan lain juga, yang tidak akan saya bahas.

Saya tidak tahu apa yang terjadi jika Anda mengeksekusi cmakeselain root CMakeLists.txt.

Dalam contoh ini, karena saya ingin semuanya ditempatkan di dalam build/folder, pertama-tama saya harus menavigasi ke sana, lalu meneruskan CMake direktori tempat root CMakeLists.txtberada.

cd build
cmake ..

Secara default, ini mengatur semuanya menggunakan Makefiles seperti yang saya katakan. Berikut tampilan folder build sekarang:

simple/build/
  CMakeCache.txt
  cmake_install.cmake
  Makefile
  CMakeFiles/
    (...)
  src/
    CMakeFiles/
      (...)
    cmake_install.cmake
    Makefile
  lib/
    CMakeFiles/
      (...)
    cmake_install.cmake
    Makefile

Apa semua file ini? Satu-satunya hal yang perlu Anda khawatirkan adalah Makefile dan folder proyek .

Perhatikan folder src/dan lib/. Ini telah dibuat karena simple/CMakeLists.txtmenunjuk ke sana menggunakan perintah add_subdirectory(<folder>). Perintah ini memberitahu CMake untuk melihat di folder mengatakan selama CMakeLists.txtberkas dan mengeksekusi bahwa naskah, sehingga setiap subdirektori menambahkan cara ini harus memiliki CMakeLists.txtberkas dalam. Dalam proyek ini, simple/src/CMakeLists.txtmenjelaskan cara membuat file yang dapat dieksekusi dan simple/lib/CMakeLists.txtmenjelaskan cara membuat pustaka. Setiap target yang CMakeLists.txtdideskripsikan akan ditempatkan secara default di subdirektori di dalam pohon build. Jadi, setelah cepat

make

di konsol selesai dari build/, beberapa file ditambahkan:

simple/build/
  (...)
  lib/
    libTestLib.a
    (...)
  src/
    Tutorial
    (...)

Proyek dibangun, dan dapat dieksekusi siap untuk dieksekusi. Apa yang Anda lakukan jika Anda ingin file yang dapat dieksekusi diletakkan di folder tertentu? Tetapkan variabel CMake yang sesuai, atau ubah properti target tertentu . Lebih lanjut tentang variabel CMake nanti.

Bagaimana cara memberi tahu CMake cara membangun proyek saya?

Berikut adalah isi dari setiap file di direktori sumber:

simple/CMakeLists.txt:

cmake_minimum_required(VERSION 2.6)

project(Tutorial)

# Add all subdirectories in this project
add_subdirectory(lib)
add_subdirectory(src)

Versi minimum yang diperlukan harus selalu disetel, sesuai dengan peringatan yang diberikan CMake jika Anda tidak melakukannya. Gunakan apa pun versi CMake Anda.

Nama proyek Anda dapat digunakan nanti, dan menunjukkan fakta bahwa Anda dapat mengelola lebih dari satu proyek dari file CMake yang sama. Saya tidak akan membahasnya.

Seperti yang disebutkan sebelumnya, add_subdirectory()menambahkan folder ke proyek, yang berarti CMake mengharapkannya memiliki di CMakeLists.txtdalam, yang kemudian akan dijalankan sebelum melanjutkan. Ngomong-ngomong, jika Anda kebetulan memiliki fungsi CMake yang ditentukan, Anda dapat menggunakannya dari CMakeLists.txtsubdirektori lain, tetapi Anda harus menentukannya sebelum Anda menggunakan add_subdirectory()atau tidak akan menemukannya. CMake lebih pintar tentang pustaka, jadi ini kemungkinan satu-satunya saat Anda akan mengalami masalah seperti ini.

simple/lib/CMakeLists.txt:

add_library(TestLib TestLib.cxx)

Untuk membuat perpustakaan Anda sendiri, Anda memberinya nama dan kemudian daftar semua file yang dibuat. Mudah. Jika diperlukan file lain foo.cxx,, untuk dikompilasi, Anda akan menulis add_library(TestLib TestLib.cxx foo.cxx). Ini juga berfungsi untuk file di direktori lain, misalnya add_library(TestLib TestLib.cxx ${CMAKE_SOURCE_DIR}/foo.cxx). Lebih lanjut tentang variabel CMAKE_SOURCE_DIR nanti.

Hal lain yang dapat Anda lakukan dengan ini adalah menentukan bahwa Anda menginginkan perpustakaan bersama. Contoh: add_library(TestLib SHARED TestLib.cxx). Jangan takut, di sinilah CMake mulai membuat hidup Anda lebih mudah. Baik itu dibagikan atau tidak, sekarang yang perlu Anda tangani untuk menggunakan pustaka yang dibuat dengan cara ini adalah nama yang Anda berikan di sini. Nama library ini sekarang TestLib, dan Anda dapat mereferensikannya dari mana saja dalam project. CMake akan menemukannya.

Apakah ada cara yang lebih baik untuk membuat daftar dependensi? Pasti ya . Lihat di bawah untuk lebih lanjut tentang ini.

simple/lib/TestLib.cxx:

#include <stdio.h>

void test() {
  printf("testing...\n");
}

simple/lib/TestLib.h:

#ifndef TestLib
#define TestLib

void test();

#endif

simple/src/CMakeLists.txt:

# Name the executable and all resources it depends on directly
add_executable(Tutorial tutorial.cxx)

# Link to needed libraries
target_link_libraries(Tutorial TestLib)

# Tell CMake where to look for the .h files
target_include_directories(Tutorial PUBLIC ${CMAKE_SOURCE_DIR}/lib)

Perintah ini add_executable()bekerja persis sama add_library(), kecuali, tentu saja, ini akan menghasilkan file yang dapat dieksekusi. Eksekusi ini sekarang dapat dirujuk sebagai target untuk hal-hal seperti target_link_libraries(). Karena tutorial.cxx menggunakan kode yang ditemukan di pustaka TestLib, Anda menunjukkan ini ke CMake seperti yang ditunjukkan.

Demikian pula, file .h apa pun yang # disertakan oleh sumber apa pun add_executable()yang tidak ada dalam direktori yang sama dengan sumber harus ditambahkan. Jika bukan karena target_include_directories()perintah, lib/TestLib.htidak akan ditemukan saat menyusun Tutorial, sehingga seluruh lib/folder ditambahkan ke direktori include yang akan dicari #includes. Anda mungkin juga melihat perintah include_directories()yang bertindak dengan cara yang sama, kecuali bahwa Anda tidak perlu menentukan target karena langsung menetapkannya secara global, untuk semua file yang dapat dieksekusi. Sekali lagi, saya akan menjelaskan CMAKE_SOURCE_DIR nanti.

simple/src/tutorial.cxx:

#include <stdio.h>
#include "TestLib.h"
int main (int argc, char *argv[])
{
  test();
  fprintf(stdout, "Main\n");
  return 0;
}

Perhatikan bagaimana file "TestLib.h" disertakan. Tidak perlu menyertakan jalur lengkap: CMake menangani semua itu di balik layar berkat target_include_directories().

Secara teknis, di pohon sumber yang sederhana seperti ini dapat Anda lakukan tanpa CMakeLists.txts bawah lib/dan src/dan hanya menambahkan sesuatu seperti add_executable(Tutorial src/tutorial.cxx)untuk simple/CMakeLists.txt. Terserah Anda dan kebutuhan proyek Anda.

Apa lagi yang harus saya ketahui untuk menggunakan CMake dengan benar?

(Topik AKA relevan dengan pemahaman Anda)

Menemukan dan menggunakan paket : Jawaban atas pertanyaan ini menjelaskannya lebih baik daripada yang pernah saya bisa.

Mendeklarasikan variabel dan fungsi, menggunakan aliran kontrol, dll .: Lihat tutorial ini yang menjelaskan dasar-dasar dari apa yang ditawarkan CMake, serta menjadi pengantar yang baik secara umum.

Variabel CMake : ada banyak, jadi yang berikut ini adalah kursus kilat untuk membawa Anda ke jalur yang benar. Wiki CMake adalah tempat yang baik untuk mendapatkan informasi yang lebih mendalam tentang variabel dan juga hal-hal lain.

Anda mungkin ingin mengedit beberapa variabel tanpa membangun kembali pohon build. Gunakan ccmake untuk ini (ini mengedit CMakeCache.txtfile). Ingatlah untuk melakukan ckonfigurasi setelah selesai dengan perubahan dan kemudian generate makefiles dengan konfigurasi yang diperbarui.

Baca tutorial yang direferensikan sebelumnya untuk mempelajari tentang menggunakan variabel, tetapi cerita singkatnya: set(<variable name> value)mengubah atau membuat variabel. ${<variable name>}untuk menggunakannya.

  • CMAKE_SOURCE_DIR: Direktori root sumber. Dalam contoh sebelumnya, ini selalu sama dengan/simple
  • CMAKE_BINARY_DIR: Direktori root build. Dalam contoh sebelumnya, ini sama dengan simple/build/, tetapi jika Anda menjalankan cmake simple/dari folder seperti foo/bar/etc/, semua referensi ke CMAKE_BINARY_DIRdalam build tree itu akan menjadi /foo/bar/etc.
  • CMAKE_CURRENT_SOURCE_DIR: Direktori tempat arus CMakeLists.txtmasuk. Artinya, ia berubah seluruhnya: mencetak ini dari simple/CMakeLists.txthasil /simple, dan mencetaknya dari simple/src/CMakeLists.txthasil /simple/src.
  • CMAKE_CURRENT_BINARY_DIR: Anda mendapatkan idenya. Jalur ini tidak hanya bergantung pada folder tempat build berada, tetapi juga pada lokasi CMakeLists.txtskrip saat ini.

Mengapa ini penting? File sumber jelas tidak akan ada di pohon build. Jika Anda mencoba sesuatu seperti target_include_directories(Tutorial PUBLIC ../lib)pada contoh sebelumnya, jalur tersebut akan relatif terhadap pohon build, artinya akan seperti menulis ${CMAKE_BINARY_DIR}/lib, yang akan melihat ke dalam simple/build/lib/. Tidak ada file .h di sana; paling banyak yang akan Anda temukan libTestLib.a. Anda ingin ${CMAKE_SOURCE_DIR}/libsebagai gantinya.

  • CMAKE_CXX_FLAGS: Tanda untuk diteruskan ke kompilator, dalam hal ini kompilator C ++. Juga perlu diperhatikan adalah CMAKE_CXX_FLAGS_DEBUGmana yang akan digunakan sebagai gantinya jika CMAKE_BUILD_TYPEdisetel ke DEBUG. Ada lebih banyak yang seperti ini; lihat wiki CMake .
  • CMAKE_RUNTIME_OUTPUT_DIRECTORY: Beri tahu CMake di mana harus meletakkan semua file executable saat dibuat. Ini adalah pengaturan global. Anda dapat, misalnya, mengaturnya bin/dan meletakkan semuanya dengan rapi di sana. EXECUTABLE_OUTPUT_PATHserupa, tetapi usang, jika Anda tersandung padanya.
  • CMAKE_LIBRARY_OUTPUT_DIRECTORY: Demikian juga, pengaturan global untuk memberi tahu CMake di mana harus meletakkan semua file perpustakaan.

Properti target : Anda dapat menyetel properti yang hanya memengaruhi satu target, baik itu yang dapat dieksekusi atau pustaka (atau arsip ... Anda mendapatkan idenya). Berikut adalah contoh yang bagus tentang cara menggunakannya (dengan set_target_properties().

Apakah ada cara mudah untuk menambahkan sumber ke target secara otomatis? Gunakan GLOB untuk mencantumkan semua yang ada di direktori tertentu di bawah variabel yang sama. Contoh sintaksnya adalah FILE(GLOB <variable name> <directory>/*.cxx).

Bisakah Anda menentukan jenis build yang berbeda? Ya, meskipun saya tidak yakin tentang cara kerjanya atau batasannya. Ini mungkin memerlukan beberapa if / then'ning, tetapi CMake memang menawarkan beberapa dukungan dasar tanpa mengkonfigurasi apa pun, seperti default untuk CMAKE_CXX_FLAGS_DEBUG, misalnya. Anda dapat menyetel jenis build Anda dari dalam CMakeLists.txtfile melalui set(CMAKE_BUILD_TYPE <type>)atau dengan memanggil CMake dari konsol dengan flag yang sesuai, misalnya cmake -DCMAKE_BUILD_TYPE=Debug.

Ada contoh bagus dari proyek yang menggunakan CMake? Wikipedia memiliki daftar proyek sumber terbuka yang menggunakan CMake, jika Anda ingin memeriksanya. Tutorial online sejauh ini mengecewakan saya dalam hal ini, namun pertanyaan Stack Overflow ini memiliki pengaturan CMake yang cukup keren dan mudah dipahami. Layak untuk dilihat.

Menggunakan variabel dari CMake dalam kode Anda : Berikut adalah contoh cepat dan kotor (diadaptasi dari beberapa tutorial lain ):

simple/CMakeLists.txt:

project (Tutorial)

# Setting variables
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 1)

# Configure_file(<input> <output>)
# Copies a file <input> to file <output> and substitutes variable values referenced in the file content.
# So you can pass some CMake variables to the source code (in this case version numbers)
configure_file (
  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
  "${PROJECT_SOURCE_DIR}/src/TutorialConfig.h"
)

simple/TutorialConfig.h.in:

// Configured options and settings
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

File yang dihasilkan dihasilkan oleh CMake, simple/src/TutorialConfig.h:

// Configured options and settings
#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 1

Dengan penggunaan cerdas ini, Anda dapat melakukan hal-hal keren seperti mematikan perpustakaan dan semacamnya. Saya merekomendasikan untuk melihat tutorial itu karena ada beberapa hal yang sedikit lebih maju yang pasti akan sangat berguna pada proyek yang lebih besar, cepat atau lambat.

Untuk yang lainnya, Stack Overflow penuh dengan pertanyaan spesifik dan jawaban singkat, yang bagus untuk semua orang kecuali yang belum tahu.

leinaD_natipaC
sumber
2
Saya menerima jawaban ini tetapi jika orang lain menulis jawaban yang lebih baik dan lebih singkat, saya akan memilih itu. Saya akan melakukannya sendiri tetapi saya sudah cukup menderita karena CMake.
leinaD_natipaC
6
Terima kasih untuk pekerjaan luar biasa ini. Semoga ini mendapat lebih banyak suara! :)
LETs
4
Poin yang agak diremehkan di sana: Dengan CMake, Anda tidak perlu lagi mempertahankan pengaturan terpisah khusus untuk lingkungan kompiler / build Anda. Anda memiliki satu konfigurasi, dan itu berfungsi untuk (berbagai versi) Visual Studio, Code Blocks, Unix Makefiles biasa, dan banyak lingkungan lainnya. Itulah mengapa saya pertama kali melihat CMake: Sangat sulit untuk menjaga konfigurasi Autoconf, MSVC 2005 / 32bit, dan MSVC 2010 / 64bit tetap sinkron. Dan begitu saya memahaminya, saya menambahkan lebih banyak hal seperti pembuatan dokumen, pengujian, pengemasan, dll., Semuanya dengan cara lintas platform.
DevSolar
1
@Devolar Benar. Saya lebih suka baris itu daripada wikipedia jadi saya akan menambahkannya ke jawaban jika Anda tidak keberatan. Saya tidak akan memasukkan apa pun tentang cara menggunakan CMake dengan benar untuk efek itu. Ini lebih dari sekedar mengetahui apa yang sedang terjadi.
leinaD_natipaC
1
@RichieHH Mengikuti contoh, pertama Anda mengkonfigurasi CMake untuk menggunakan Makefiles, kemudian Anda menggunakan CMake untuk menghasilkan file yang perlu dibuat oleh proyek Anda, dan akhirnya Anda berhenti mengkhawatirkan CMake karena tugasnya di sini sudah selesai. Jadi, Anda "khawatir" tentang Makefile dalam arti bahwa mulai sekarang Anda perlu menggunakan makebash untuk membuat proyek Anda.
leinaD_natipaC