Cmake vs membuat kode sampel?

118

Saya bertanya-tanya apakah ada kode sampel untuk Makefiles ( make) dan CMakeLists.txt( cmake) yang keduanya melakukan hal yang sama (satu-satunya perbedaan adalah yang satu ditulis makedan yang lainnya di cmake).

Saya mencoba mencari 'cmake vs make', tetapi saya tidak pernah menemukan perbandingan kode apa pun. Akan sangat membantu untuk memahami perbedaannya, meskipun hanya untuk kasus sederhana.

jlo
sumber
20
+1 Ini adalah pertanyaan yang bagus; ketika saya mulai dengan cmakesaya menginginkan ini juga. Tapi saya ragu Anda akan menemukannya karena kemampuannya tidak memetakan dengan baik satu sama lain. Jika Anda mencoba membuat cmaketindakan seperti itu make, Anda akan membuat diri Anda gila, sungguh. Sebaiknya mulai dari awal. Hal-hal yang sepele makecukup terlibat cmake, begitu pula sebaliknya.
Ernest Friedman-Hill
1
@ ErnestFriedman-Hill apakah Anda memiliki detail lebih lanjut tentang itu? Begitu makedan cmakebegitu berbeda sehingga mereka harus dilihat lebih sebagai alat yang saling melengkapi daripada bersaing?
Ehtesh Choudhury
2
@ Shurane - cmake tidak membangun apapun itu sendiri; itu membuat Makefiles (dan lainnya, skrip build serupa), yang kemudian Anda jalankan. Oleh karena itu, setiap kali Anda menulis file cmake, Anda harus memikirkan apakah sebuah perintah harus diterapkan selama waktu pembuatan, atau selama waktu pembuatan. Beberapa tindakan - menyalin sekumpulan file wildcard pada waktu pembuatan, misalnya - cukup rumit, dibandingkan dengan " cp *.x $(OUTDIR)" yang Anda tulis di Makefile. Mungkin bagian yang paling menjengkelkan bagi saya adalah bahwa Makefile yang dihasilkan dirancang dengan benar-benar tidak dapat dibawa dan tidak fleksibel (lanjutan)
Ernest Friedman-Hill
1
(lanjutan) Anda bahkan tidak dapat memindahkan direktori sumber pada mesin yang sama tanpa menjalankan ulang cmake untuk meregenerasi Makefile! Jadi pilihannya bukan antara cmake dan make, melainkan antara menulis Makefile portabel sendiri, atau menggunakan cmake untuk menghasilkan yang tidak dapat dibawa pada setiap mesin build (dan mengingat bahwa Anda dapat menggunakan Cygwin atau mingw di Windows, saya biasanya menganggap yang pertama lebih mudah. )
Ernest Friedman-Hill
1
pertanyaan yang bagus, tetapi tidak ada jawaban yang spesifik, karena kedua alat mencoba menyelesaikan masalah yang berbeda. cmake mengambil informasi tentang bagaimana membangun program menghasilkan makefiles yang membangun program. Karenanya cmake adalah bahasa dengan aturan build abstrak dan gnu make adalah penyelesaian ketergantungan yang mengeksekusi program pada traversal grafik asiklik yang diarahkan.
Alex

Jawaban:

118

Makefile berikut membuat executable yang dinamai progdari sumber prog1.c, prog2.c, prog3.c and main.c. progditautkan libmystatlib.a dan libmydynlib.sokeduanya juga dibangun dari sumber. Selain itu, progmenggunakan pustaka libstuff.adi stuff/libdan headernya di stuff/include. Makefile secara default membangun target rilis, tetapi juga menawarkan target debug:

#Makefile    
CC = gcc
CPP = g++
RANLIB = ar rcs
RELEASE = -c -O3 
DEBUG = -c -g -D_DEBUG
INCDIR = -I./stuff/include
LIBDIR = -L./stuff/lib -L.
LIBS = -lstuff -lmystatlib -lmydynlib
CFLAGS = $(RELEASE)

PROGOBJS = prog1.o prog2.o prog3.o

prog: main.o $(PROGOBJS) mystatlib mydynlib
    $(CC) main.o $(PROGOBJS) $(LIBDIR) $(LIBS) -o prog 
debug: CFLAGS=$(DEBUG)
debug: prog

mystatlib: mystatlib.o
    $(RANLIB) libmystatlib.a mystatlib.o
mydynlib: mydynlib.o
    $(CPP) -shared mydynlib.o -o libmydynlib.so

%.o: %.c
    $(CC) $(CFLAGS) $(INCDIR) $< -o $@ 
%.o: %.cpp
    $(CPP) $(CFLAGS) $(INCDIR) -fPIC  $< -o $@ 

Berikut ini adalah CMakeLists.txtyang (hampir) persis sama, dengan beberapa komentar untuk menggarisbawahi kesamaan dengan Makefile:

#CMakeLists.txt     
cmake_minimum_required(VERSION 2.8)                    # stuff not directly
project(example)                                       # related to building

include_directories(${CMAKE_SOURCE_DIR}/stuff/include) # -I flags for compiler
link_directories(${CMAKE_SOURCE_DIR}/stuff/lib)        # -L flags for linker

set(PROGSRC prog1.c prog2.c prog3.c)                   # define variable 

add_executable(prog main.c ${PROGSRC})                 # define executable target prog, specify sources
target_link_libraries(prog mystatlib mydynlib stuff)   # -l flags for linking prog target

add_library(mystatlib STATIC mystatlib.c)              # define static library target mystatlib, specify sources

add_library(mydynlib SHARED mydynlib.cpp)              # define shared library target mydynlib, specify sources
#extra flags for linking mydynlib
set_target_properties(mydynlib PROPERTIES POSITION_INDEPENDENT_CODE TRUE) 
#alternatively:
#set_target_properties(mydynlib PROPERTIES COMPILE_FLAGS "-fPIC")

Dalam contoh sederhana ini, perbedaan terpenting adalah:

  • CMake mengenali kompiler mana yang akan digunakan untuk jenis sumber apa. Juga, itu memanggil urutan perintah yang benar untuk setiap jenis target. Oleh karena itu, tidak ada spesifikasi eksplisit dari perintah seperti $(CC) ..., $(RANLIB) ...dan sebagainya.

  • Semua flag compiler / linker biasa yang menangani penyertaan file header, library, dll. Diganti dengan perintah platform independen / sistem build independen.

  • Bendera debugging termasuk oleh salah satu pengaturan variabel CMAKE_BUILD_TYPEuntuk "Debug", atau dengan melewatkan ke CMake saat menjalankan program: cmake -DCMAKE_BUILD_TYPE:STRING=Debug.

  • CMake juga menawarkan penyertaan bendera '-fPIC' secara independen pada platform (melalui POSITION_INDEPENDENT_CODEproperti) dan banyak lainnya. Namun, pengaturan yang lebih tidak jelas dapat diimplementasikan dengan tangan di CMake serta di Makefile (dengan menggunakan COMPILE_FLAGS dan properti serupa). Tentu saja CMake benar-benar mulai bersinar saat pustaka pihak ketiga (seperti OpenGL) disertakan secara portabel.

  • Proses build memiliki satu langkah jika menggunakan Makefile yaitu mengetik makepada baris perintah. Untuk CMake, ada dua langkah: Pertama, Anda perlu menyiapkan lingkungan build Anda (baik dengan mengetik cmake <source_dir>di direktori build Anda atau dengan menjalankan beberapa klien GUI). Ini membuat Makefile atau sesuatu yang setara, tergantung pada sistem build pilihan Anda (misalnya make di Unixes atau VC ++ atau MinGW + Msys di Windows). Sistem build dapat diteruskan ke CMake sebagai parameter; namun, CMake membuat pilihan default yang wajar bergantung pada konfigurasi sistem Anda. Kedua, Anda menjalankan build sebenarnya dalam sistem build yang dipilih.

Sumber dan instruksi pembuatan tersedia di https://github.com/rhoelzel/make_cmake .

Roberto
sumber
3
Bukankah Makefile terlalu rumit? Dengan menggunakan, CPPFLAGSbukan INCDIRseseorang dapat menggunakan aturan built-in dan pemanggilan eksplisit dari compiler akan menjadi redundan. Demikian pula untuk penanganan ar, aturan bawaan juga bisa mencakup itu. Juga, mengapa ditetapkan CPPdan CCsecara eksplisit? Mereka sudah diatur ke nilai yang baik dengan make, mereka adalah variabel yang telah ditentukan sebelumnya. makejuga mengenali kompiler mana yang akan digunakan untuk jenis sumber apa, aturan bawaannya banyak.
Christian Hujer
1
Dan banyak dari variabel ini harus ditetapkan dengan, :=bukan =.
Christian Hujer
1
Melihat deskripsi, cmakelebih sebanding dengan automakedari make.
ivan_pozdeev
Makefile yang disediakan dapat dikurangi menjadi 3/4 baris. Anda HARUS menentukan, INCLUDESbukan INCDIR. Anda tidak membutuhkan aturan% .o:%. C.
shuva
6

Ambil beberapa perangkat lunak yang menggunakan CMake sebagai sistem pembuatannya (ada banyak proyek sumber terbuka untuk dipilih sebagai contoh). Dapatkan kode sumber dan konfigurasikan menggunakan CMake. Baca makefile yang dihasilkan dan nikmati.

Satu hal yang perlu diingat bahwa alat tersebut tidak memetakan satu-ke-satu. Perbedaan yang paling jelas adalah CMake memindai dependensi antara file yang berbeda (mis. Header C dan file sumber), sementara meninggalkannya ke pembuat makefile.

Tadeusz A. Kadłubowski
sumber
4

Jika pertanyaan ini tentang contoh Makefilekeluaran CMakeList.txtfile, silakan periksa sumber cmake-backend dan buat satu seperti itu Makefile. Jika tidak, maka menambahkan balasan @Roberto saya mencoba membuatnya sederhana dengan menyembunyikan detailnya.

Fungsi CMake

Sedangkan Makealat fleksibel untuk aturan dan resep, CMakemerupakan lapisan abstraksi yang juga menambahkan fitur konfigurasi.

Dataran saya CMakeLists.txtakan terlihat seperti berikut ini,

cmake_minimum_required(VERSION 2.8)
project(example)
file(GLOB testapp_SOURCES *.cc)
add_executable(testapp ${testapp_SOURCES})

Perhatikan, bahwa CMakemenyembunyikan howbuild bisa dilakukan. Kami hanya menentukan whatadalah input dan output.

The CMakeLists.txtberisi daftar fungsi-panggilan yang ditentukan oleh cmake.

(Fungsi CMake) Vs Buat aturan

Dalam Makefilesatu rules and recipesdigunakan sebagai pengganti functions. Selain functionfitur -like, rules and recipessediakan chaining. Minimalis saya Makefileakan terlihat seperti berikut,

-include "executable.mk"
TARGETS=testapp.bin
all:${TARGETS}

Sedangkan tampilannya executable.mkakan seperti berikut ini,

SOURCES=$(wildcard *.cpp)
OBJECTS=$(SOURCES:.cpp=.o)
DEPS=$(SOURCES:.cpp=.d)

%.bin:$(OBJECTS)
    $(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(LIBS)

.PHONY: all clean

clean:
    $(RM) $(OBJECTS) $(DEPS) $(TARGETS)

-include $(DEPS)

Mulai dari awal saya akan mulai dengan Makefileyang berikut ini,

all: testapp.bin

testapp.bin:sourcea.o sourcb.o
    $(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(LIBS)

.PHONY: all clean

clean:
    $(RM) $(OBJECTS) testapp.bin

Saya mendapatkan potongan ini dari sini dan memodifikasinya. Perhatikan bahwa beberapa implisit-rules ditambahkan ke file ini yang dapat ditemukan di dokumentasi makefile. Beberapa variabel implisit juga relevan di sini.

Catatan, itu Makefilememberikan detail yang recipemenunjukkan howbahwa pembangunan bisa dilakukan. Dimungkinkan untuk menulis executable.mkuntuk menjaga detail yang ditentukan dalam satu file. Dengan cara itu makefile bisa dikurangi seperti yang saya tunjukkan tadi.

Variabel Internal dalam CMakedanMake

Sekarang sedikit lebih maju, di CMakekita dapat mengatur bendera kompilator seperti berikut,

set(CMAKE_C_FLAGS "-Wall")

Cari tahu lebih lanjut tentang CMakevariabel default dalam CMakeCache.txtfile. The CMakekode di atas akan setara dengan Makekode di bawah ini,

CFLAGS = -Wall

Perhatikan bahwa CFLAGSvariabel internal dalam Make, dengan cara yang sama, CMAKE_C_FLAGSadalah variabel internal dalam CMake.

menambahkan penyertaan dan jalur perpustakaan di CMake

Kita bisa melakukannya dengan cmakemenggunakan fungsi.

target_include_directories(testapp PRIVATE "myincludes")
list(APPEND testapp_LIBRARIES
    mytest mylibrarypath
)
target_link_libraries(testapp ${testapp_LIBRARIES})

Vs menambahkan penyertaan dan jalur pustaka di Make

Kita dapat menambahkan penyertaan dan pustaka dengan menambahkan baris seperti berikut,

INCLUDES += -Imyincludes
LIBS += -Lmylibrarypath -lmytest

Perhatikan bahwa baris di atas dapat dibuat dari alat auto-gen atau pkg-config. (meskipun Makefile tidak bergantung pada alat konfigurasi otomatis)

Konfigurasi CMake / tweek

Biasanya dimungkinkan untuk menghasilkan beberapa config.hfile seperti auto-configalat dengan menggunakan configure_filefungsi. Dimungkinkan untuk melakukan lebih banyak trik menulis fungsi kustom. Dan terakhir kita bisa memilih konfigurasi seperti berikut,

cmake --build . --config "Release"

Dimungkinkan untuk menambahkan beberapa opsi yang dapat dikonfigurasi menggunakan optionfungsi tersebut.

Konfigurasi / tweak Makefile

Jika entah bagaimana kita perlu mengkompilasinya dengan beberapa flag debug, kita bisa memanggil makelike,

make CXXFLAGS=NDEBUG

Saya pikir variabel internal, Makefile-rulesdan CMake-functionsawal yang baik untuk perbandingan, semoga sukses dengan lebih banyak menggali.

shuva
sumber