Panduan Penting untuk Qmake
Diterbitkan: 2022-03-11pengantar
qmake adalah alat sistem pembangunan yang dikirimkan bersama pustaka Qt yang menyederhanakan proses pembangunan di berbagai platform. Tidak seperti CMake dan Qbs , qmake adalah bagian dari Qt sejak awal dan harus dianggap sebagai alat "asli". Tak perlu dikatakan, IDE default Qt— Qt Creator —memiliki dukungan terbaik dari qmake. Ya, Anda juga dapat memilih sistem build CMake dan Qbs untuk proyek baru di sana, tetapi ini tidak terintegrasi dengan baik. Sepertinya dukungan CMake di Qt Creator akan ditingkatkan dari waktu ke waktu, dan ini akan menjadi alasan yang baik untuk menerbitkan edisi kedua panduan ini, yang ditujukan khusus untuk CMake. Bahkan jika Anda tidak bermaksud menggunakan Qt Creator, Anda mungkin masih ingin mempertimbangkan qmake sebagai sistem pembangunan kedua jika Anda sedang membangun perpustakaan umum atau plugin. Hampir semua perpustakaan atau plugin berbasis Qt pihak ketiga menyediakan file qmake yang digunakan untuk berintegrasi ke dalam proyek berbasis qmake dengan mulus. Hanya sedikit dari mereka yang menyediakan konfigurasi ganda, misalnya qmake dan CMake. Anda mungkin lebih suka menggunakan qmake jika hal berikut berlaku untuk Anda:
- Anda sedang membangun proyek berbasis Qt lintas platform
- Anda menggunakan Qt Creator IDE dan sebagian besar fiturnya
- Anda sedang membangun perpustakaan/plugin mandiri untuk digunakan oleh proyek qmake lainnya
Panduan ini menjelaskan fitur qmake yang paling berguna dan memberikan contoh nyata untuk masing-masing fitur tersebut. Pembaca yang baru mengenal Qt dapat menggunakan panduan ini sebagai tutorial untuk membangun sistem Qt. Pengembang Qt dapat memperlakukan ini sebagai buku masak saat memulai proyek baru atau dapat secara selektif menerapkan beberapa fitur ke salah satu proyek yang ada dengan dampak rendah.
Penggunaan Qmake Dasar
Spesifikasi qmake ditulis dalam file .pro
(“project”). Ini adalah contoh file .pro
yang paling sederhana:
SOURCES = hello.cpp
Secara default, ini akan membuat Makefile
yang akan membuat file yang dapat dieksekusi dari file kode sumber tunggal hello.cpp
.
Untuk membangun biner (dapat dieksekusi dalam kasus ini), Anda harus menjalankan qmake terlebih dahulu untuk menghasilkan Makefile dan kemudian make
(atau nmake
, atau mingw32-make
tergantung pada rantai alat Anda) untuk membangun target.
Singkatnya, spesifikasi qmake tidak lebih dari daftar definisi variabel yang dicampur dengan pernyataan aliran kontrol opsional. Setiap variabel, secara umum, menyimpan daftar string. Pernyataan aliran kontrol memungkinkan Anda untuk menyertakan file spesifikasi qmake lainnya, mengontrol bagian kondisional, dan bahkan memanggil fungsi.
Memahami Sintaks Variabel
Saat mempelajari proyek qmake yang ada, Anda mungkin terkejut bagaimana variabel yang berbeda dapat dirujuk: \(VAR,\){VAR} atau $$(VAR) …
Gunakan lembar contekan mini ini sambil menerapkan aturan:
-
VAR = value
Tetapkan nilai ke VAR -
VAR += value
Tambahkan nilai ke daftar VAR -
VAR -= value
Hapus nilai dari daftar VAR -
$$VAR
or$${VAR}
Mendapatkan nilai VAR pada saat qmake dijalankan -
$(VAR)
Isi dari VAR Lingkungan pada saat Makefile (bukan qmake) sedang berjalan -
$$(VAR)
Isi dari VAR Lingkungan pada saat qmake (bukan Makefile) sedang berjalan
Template Umum
Daftar lengkap variabel qmake dapat ditemukan di spesifikasi: http://doc.qt.io/qt-5/qmake-variable-reference.html
Mari kita tinjau beberapa template umum untuk proyek:
# Windows application TEMPLATE = app CONFIG += windows # Shared library (.so or .dll) TEMPLATE = lib CONFIG += shared # Static library (.a or .lib) TEMPLATE = lib CONFIG += static # Console application TEMPLATE = app CONFIG += console
Cukup tambahkan SOURCES += … dan HEADERS += … untuk membuat daftar semua file kode sumber Anda, dan selesai.
Sejauh ini, kami telah meninjau template yang sangat mendasar. Proyek yang lebih kompleks biasanya mencakup beberapa sub-proyek dengan ketergantungan satu sama lain. Mari kita lihat bagaimana mengelola ini dengan qmake.
Sub-proyek
Kasus penggunaan yang paling umum adalah aplikasi yang dikirimkan dengan satu atau beberapa perpustakaan dan proyek pengujian. Perhatikan struktur berikut:
/project ../library ..../include ../library-tests ../application
Jelas, kami ingin dapat membangun semuanya sekaligus, seperti ini:
cd project qmake && make
Untuk mencapai tujuan ini, kita memerlukan file proyek qmake di bawah folder /project
:
TEMPLATE = subdirs SUBDIRS = library library-tests application library-tests.depends = library application.depends = library
CATATAN: menggunakan CONFIG += ordered
dianggap sebagai praktik yang buruk—lebih suka menggunakan .depends
sebagai gantinya.
Spesifikasi ini menginstruksikan qmake untuk membangun sub-proyek perpustakaan terlebih dahulu karena target lain bergantung padanya. Kemudian ia dapat membangun library-tests
dan aplikasi dalam urutan arbitrer karena keduanya saling bergantung.
Menghubungkan Perpustakaan
Dalam contoh di atas, kami memiliki perpustakaan yang perlu ditautkan ke aplikasi. Di C/C++, ini berarti kita perlu mengkonfigurasi beberapa hal lagi:
- Tentukan
-I
untuk menyediakan jalur pencarian untuk arahan #include. - Tentukan
-L
untuk menyediakan jalur pencarian untuk linker. - Tentukan
-l
untuk menyediakan perpustakaan apa yang perlu ditautkan.
Karena kami ingin semua sub-proyek dapat dipindahkan, kami tidak dapat menggunakan jalur absolut atau relatif. Misalnya, kita tidak boleh melakukan ini: INCLUDEPATH += ../library/include dan tentu saja kita tidak dapat mereferensikan biner perpustakaan (.a file) dari folder build sementara. Mengikuti prinsip "pemisahan masalah", kita dapat dengan cepat menyadari bahwa file proyek aplikasi harus abstrak dari detail perpustakaan. Sebaliknya, perpustakaan bertanggung jawab untuk memberi tahu di mana menemukan file header, dll.
Mari manfaatkan direktif include()
qmake untuk menyelesaikan masalah ini. Dalam proyek perpustakaan, kami akan menambahkan spesifikasi qmake lain dalam file baru dengan ekstensi .pri
(ekstensi bisa apa saja, tapi di sini i
singkatan dari include). Jadi, perpustakaan akan memiliki dua spesifikasi: library.pro
dan library.pri
. Yang pertama digunakan untuk membangun perpustakaan, yang kedua digunakan untuk menyediakan semua detail yang dibutuhkan oleh proyek yang sedang dikonsumsi.
Isi file library.pri adalah sebagai berikut:
LIBTARGET = library BASEDIR = $${PWD} INCLUDEPATH *= $${BASEDIR}/include LIBS += -L$${DESTDIR} -llibrary
BASEDIR
menentukan folder proyek perpustakaan (tepatnya, lokasi file spesifikasi qmake saat ini, yaitu library.pri
dalam kasus kami). Seperti yang Anda duga, INCLUDEPATH
akan dievaluasi ke /project/library/include
. DESTDIR
adalah direktori tempat sistem build menempatkan artefak keluaran, seperti (file .o .a .so .dll atau .exe). Ini biasanya dikonfigurasi di IDE Anda, jadi Anda tidak boleh berasumsi di mana file output berada.
Di file application.pro
cukup tambahkan include(../library/library.pri)
dan selesai.
Mari kita tinjau bagaimana proyek aplikasi dibangun dalam kasus ini:
- Project.pro teratas adalah
project.pro
subdir. Ini memberitahu kita bahwa proyek perpustakaan perlu dibangun terlebih dahulu. Jadi qmake masuk ke folder library dan membangunnya menggunakanlibrary.pro
. Pada tahap ini,library.a
diproduksi dan ditempatkan ke dalam folderDESTDIR
. - Kemudian qmake masuk ke subfolder aplikasi dan mem-parsing file
application.pro
. Ia menemukan direktifinclude(../library/library.pri)
, yang memerintahkan qmake untuk membaca dan menafsirkannya segera. Ini menambahkan definisi baru ke variabelINCLUDEPATH
danLIBS
, jadi sekarang kompiler dan tautan tahu di mana harus mencari file yang disertakan, binari perpustakaan, dan perpustakaan apa yang akan ditautkan.
Kami melewatkan pembangunan proyek pengujian perpustakaan, tetapi itu identik dengan proyek aplikasi. Jelas, proyek pengujian kami juga perlu menautkan perpustakaan yang seharusnya diuji.

Dengan pengaturan ini, Anda dapat dengan mudah memindahkan proyek perpustakaan ke proyek qmake lain dan memasukkannya, dengan demikian merujuk ke file .pri
. Ini persis bagaimana perpustakaan pihak ketiga didistribusikan oleh komunitas.
config.pri
Sangat umum untuk proyek yang kompleks memiliki beberapa parameter konfigurasi bersama yang digunakan oleh banyak sub-proyek. Untuk menghindari duplikasi, Anda dapat kembali memanfaatkan direktif include()
dan membuat config.pri
di folder tingkat atas. Anda mungkin juga memiliki "utilitas" qmake umum yang dibagikan ke sub-proyek Anda, mirip dengan apa yang akan kita bahas selanjutnya dalam panduan ini.
Menyalin Artefak ke DESTDIR
Seringkali, proyek memiliki beberapa file "lain" yang perlu didistribusikan bersama dengan perpustakaan atau aplikasi. Kami hanya perlu dapat menyalin semua file tersebut ke DESTDIR
selama proses pembuatan. Perhatikan cuplikan berikut:
defineTest(copyToDestDir) { files = $$1 for(FILE, files) { DDIR = $$DESTDIR FILE = $$absolute_path($$FILE) # Replace slashes in paths with backslashes for Windows win32:FILE ~= s,/,\\,g win32:DDIR ~= s,/,\\,g QMAKE_POST_LINK += $$QMAKE_COPY $$quote($$FILE) $$quote($$DDIR) $$escape_expand(\\n\\t) } export(QMAKE_POST_LINK) }
Catatan: Dengan menggunakan pola ini, Anda dapat menentukan fungsi Anda sendiri yang dapat digunakan kembali yang berfungsi pada file.
Tempatkan kode ini ke /project/copyToDestDir.pri
sehingga Anda dapat include()
dalam menuntut sub-proyek sebagai berikut:
include(../copyToDestDir.pri) MYFILES += \ parameters.conf \ testdata.db ## this is copying all files listed in MYFILES variable copyToDestDir($$MYFILES) ## this is copying a single file, a required DLL in this example copyToDestDir($${3RDPARTY}/openssl/bin/crypto.dll)
Catatan: DISTFILES diperkenalkan untuk tujuan yang sama, tetapi hanya berfungsi di Unix.
Pembuatan Kode
Contoh yang bagus dari pembuatan kode sebagai langkah yang dibuat sebelumnya adalah ketika proyek C++ menggunakan protobuf Google. Mari kita lihat bagaimana kita bisa menyuntikkan eksekusi protoc
ke dalam proses build.
Anda dapat dengan mudah mencari solusi yang sesuai di Google, tetapi Anda harus menyadari satu kasus sudut penting. Bayangkan Anda memiliki dua kontrak, di mana A mereferensikan B.
A.proto <= B.proto
Jika kita akan membuat kode untuk A.proto
terlebih dahulu (untuk menghasilkan A.pb.h
dan A.pb.cxx
) dan memasukkannya ke kompilator, itu hanya akan gagal karena ketergantungan B.pb.h
belum ada. Untuk mengatasi ini, kita harus melewati semua tahap pembuatan kode proto sebelum membangun kode sumber yang dihasilkan.
Saya menemukan cuplikan yang bagus untuk tugas ini di sini: https://github.com/jmesmon/qmake-protobuf-example/blob/master/protobuf.pri
Ini adalah skrip yang cukup besar, tetapi Anda harus sudah tahu cara menggunakannya:
PROTOS = A.proto B.proto include(protobuf.pri)
Saat melihat ke protobuf.pri
, Anda mungkin melihat pola umum yang dapat dengan mudah diterapkan ke kompilasi khusus atau pembuatan kode:
my_custom_compiler.name = my custom compiler name my_custom_compiler.input = input variable (list) my_custom_compiler.output = output file path + pattern my_custom_compiler.commands = custom compilation command my_custom_compiler.variable_out = output variable (list) QMAKE_EXTRA_COMPILERS += my_custom_compiler
Lingkup dan Ketentuan
Seringkali, kita perlu mendefinisikan deklarasi khusus untuk platform tertentu, seperti Windows atau MacOS. Qmake menawarkan tiga indikator platform yang telah ditentukan: win32, macx, dan unix. Berikut sintaksnya:
win32 { # add Windows application icon, not applicable to unix/macx platform RC_ICONS += icon.ico }
Lingkup dapat bersarang, dapat menggunakan operator !
, |
dan bahkan wildcard:
macx:debug { # include only on Mac and only for debug build HEADERS += debugging.h } win32|macx { HEADERS += windows_or_macx.h } win32-msvc* { # same as win32-msvc|win32-mscv.net }
Catatan: Unix didefinisikan pada Mac OS! Jika Anda ingin menguji Mac OS (bukan Unix generik), gunakan kondisi unix:!macx
.
Di Qt Creator, kondisi cakupan debug
dan release
tidak berfungsi seperti yang diharapkan. Untuk membuatnya bekerja dengan benar, gunakan pola berikut:
CONFIG(debug, debug|release) { LIBS += ... } CONFIG(release, debug|release) { LIBS += ... }
Fungsi yang Berguna
Qmake memiliki sejumlah fungsi tertanam yang menambahkan lebih banyak otomatisasi.
Contoh pertama adalah fungsi files()
. Dengan asumsi Anda memiliki langkah pembuatan kode yang menghasilkan sejumlah variabel file sumber. Berikut adalah bagaimana Anda dapat memasukkan semuanya ke dalam SOURCES
:
SOURCES += $$files(generated/*.c)
Ini akan menemukan semua file dengan ekstensi .c
di sub-folder yang generated
dan menambahkannya ke variabel SOURCES
.
Contoh kedua mirip dengan yang sebelumnya, tetapi sekarang pembuatan kode menghasilkan file teks yang berisi nama file keluaran (daftar file):
SOURCES += $$cat(generated/filelist, lines)
Ini hanya akan membaca konten file dan memperlakukan setiap baris sebagai entri untuk SOURCES
.
Catatan: Daftar lengkap fungsi yang disematkan dapat ditemukan di sini: http://doc.qt.io/qt-5/qmake-function-reference.html
Memperlakukan Peringatan sebagai Kesalahan
Cuplikan berikut menggunakan fitur cakupan bersyarat yang dijelaskan sebelumnya:
*g++*: QMAKE_CXXFLAGS += -Werror *msvc*: QMAKE_CXXFLAGS += /WX
Alasan komplikasi ini adalah karena MSVC memiliki flag yang berbeda untuk mengaktifkan opsi ini.
Menghasilkan Versi Git
Cuplikan berikut berguna saat Anda perlu membuat definisi praprosesor yang berisi versi SW saat ini yang diperoleh dari Git:
DEFINES += SW_VERSION=\\\"$$system(git describe --always --abbrev=0)\\\"
Ini berfungsi pada platform apa pun selama perintah git
tersedia. Jika Anda menggunakan tag Git, maka ini akan mengintip tag terbaru, meskipun cabangnya duluan. Ubah perintah git describe
untuk mendapatkan output pilihan Anda.
Kesimpulan
Qmake adalah alat hebat yang berfokus untuk membangun proyek berbasis Qt lintas platform Anda. Dalam panduan ini, kami meninjau penggunaan alat dasar dan pola yang paling umum digunakan yang akan membuat struktur proyek Anda fleksibel dan spesifikasi build mudah dibaca dan dipelihara.
Ingin mempelajari cara membuat aplikasi Qt Anda terlihat lebih baik? Coba: Cara Mendapatkan Bentuk Sudut Bulat Dalam C++ Menggunakan Kurva Bezier dan QPainter: Panduan Langkah demi Langkah