Qmake için Hayati Bir Kılavuz

Yayınlanan: 2022-03-11

Tanıtım

qmake , farklı platformlarda oluşturma sürecini basitleştiren, Qt kitaplığıyla birlikte gönderilen bir yapı sistemi aracıdır. CMake ve Qbs'den farklı olarak, qmake en başından beri Qt'nin bir parçasıydı ve “yerel” bir araç olarak kabul edilecektir. Söylemeye gerek yok, Qt'nin varsayılan IDE'si — Qt Creator — kutudan çıktığı haliyle en iyi qmake desteğine sahiptir. Evet, orada yeni bir proje için CMake ve Qbs derleme sistemlerini de seçebilirsiniz, ancak bunlar o kadar iyi entegre değil. Qt Creator'daki CMake desteğinin zaman içinde iyileştirilmesi muhtemeldir ve bu, bu kılavuzun özellikle CMake'i hedefleyen ikinci baskısını yayınlamak için iyi bir neden olacaktır. Qt Creator kullanmayı düşünmüyorsanız bile, halk kütüphaneleri veya eklentiler oluşturuyorsanız qmake'i ikinci bir derleme sistemi olarak düşünebilirsiniz. Neredeyse tüm üçüncü taraf Qt tabanlı kitaplıklar veya eklentiler, qmake tabanlı projelere sorunsuz bir şekilde entegre olmak için kullanılan qmake dosyalarını sağlar. Yalnızca birkaçı ikili yapılandırma sağlar, örneğin qmake ve CMake. Aşağıdakiler sizin için geçerliyse qmake kullanmayı tercih edebilirsiniz:

  • Platformlar arası Qt tabanlı bir proje oluşturuyorsunuz
  • Qt Creator IDE ve özelliklerinin çoğunu kullanıyorsunuz
  • Diğer qmake projeleri tarafından kullanılacak bağımsız bir kitaplık/eklenti oluşturuyorsunuz

Bu kılavuz, en kullanışlı qmake özelliklerini açıklar ve her biri için gerçek dünyadan örnekler sunar. Qt'de yeni olan okuyucular bu kılavuzu Qt'nin inşa sistemi için bir öğretici olarak kullanabilirler. Qt geliştiricileri, yeni bir projeye başlarken bunu bir yemek kitabı olarak değerlendirebilir veya bazı özellikleri düşük etki ile mevcut projelerden herhangi birine seçerek uygulayabilir.

qmake oluşturma sürecinin bir örneği

Temel Qmake Kullanımı

qmake belirtimi .pro (“proje”) dosyalarına yazılır. Bu, mümkün olan en basit .pro dosyasına bir örnektir:

 SOURCES = hello.cpp

Varsayılan olarak bu, hello.cpp tek kaynak kod dosyasından bir yürütülebilir dosya oluşturacak bir Makefile oluşturacaktır.

İkiliyi oluşturmak için (bu durumda yürütülebilir), önce bir Makefile oluşturmak için qmake'i çalıştırmanız ve ardından hedefi oluşturmak için make (veya nmake veya araç zincirinize bağlı olarak mingw32-make ) yapmanız gerekir.

Özetle, bir qmake belirtimi, isteğe bağlı kontrol akışı ifadeleriyle karıştırılmış bir değişken tanımları listesinden başka bir şey değildir. Her değişken genel olarak bir dizi dizi içerir. Kontrol akışı deyimleri, diğer qmake belirtim dosyalarını, kontrol koşullu bölümlerini ve hatta çağrı fonksiyonlarını dahil etmenize izin verir.

Değişkenlerin Sözdizimini Anlamak

Mevcut qmake projelerini öğrenirken, farklı değişkenlere nasıl başvurulabileceğine şaşırabilirsiniz: \(VAR,\){VAR} veya $$(VAR)

Kuralları benimserken bu mini hile sayfasını kullanın:

  • VAR = value ata
  • VAR += value VAR listesine değer ekle
  • VAR -= value kaldır
  • $$VAR veya $${VAR} qmake çalışırken VAR değerini alır
  • $(VAR) Makefile (qmake değil) çalışırken Ortam VAR'ın içeriği
  • $$(VAR) qmake (Makefile değil) çalışırken Ortam VAR'ın içeriği

Ortak Şablonlar

qmake değişkenlerinin tam listesi spesifikasyonda bulunabilir: http://doc.qt.io/qt-5/qmake-variable-reference.html

Projeler için birkaç yaygın şablonu gözden geçirelim:

 # 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

Tüm kaynak kod dosyalarınızı listelemek için SOURCES += … ve HEADERS += … ekleyin ve işiniz bitti.

Şimdiye kadar çok temel şablonları inceledik. Daha karmaşık projeler genellikle birbirine bağımlı birkaç alt proje içerir. Bunu qmake ile nasıl yöneteceğimizi görelim.

alt projeler

En yaygın kullanım durumu, bir veya birkaç kitaplık ve test projesiyle birlikte gönderilen bir uygulamadır. Aşağıdaki yapıyı göz önünde bulundurun:

 /project ../library ..../include ../library-tests ../application

Açıkçası, her şeyi bir kerede inşa edebilmek istiyoruz, bunun gibi:

 cd project qmake && make

Bu amaca ulaşmak için /project klasörü altında bir qmake proje dosyasına ihtiyacımız var:

 TEMPLATE = subdirs SUBDIRS = library library-tests application library-tests.depends = library application.depends = library

NOT: CONFIG += ordered order öğesinin kullanılması kötü bir uygulama olarak kabul edilir; bunun yerine .depends kullanmayı tercih edin.

Bu belirtim, diğer hedefler buna bağlı olduğundan, önce qmake'e bir kitaplık alt projesi oluşturma talimatı verir. Ardından, bu ikisi bağımlı olduğundan, library-tests ve uygulamayı keyfi bir sırada oluşturabilir.

Proje dizin yapısı

Kitaplıkları Bağlama

Yukarıdaki örnekte, uygulamaya bağlanması gereken bir kitaplığımız var. C/C++'da bu, birkaç şeyi daha yapılandırmamız gerektiği anlamına gelir:

  1. #include yönergeleri için arama yolları sağlamak için -I belirtin.
  2. Bağlayıcı için arama yolları sağlamak için -L belirtin.
  3. Hangi kitaplığın bağlanması gerektiğini sağlamak için -l belirtin.

Tüm alt projelerin hareketli olmasını istediğimiz için mutlak veya göreceli yollar kullanamayız. Örneğin şunu yapmayacağız: INCLUDEPATH += ../library/include ve elbette geçici bir derleme klasöründen kitaplık ikili dosyasına (.a dosyası) başvuramayız. “Endişelerin ayrılması” ilkesini takiben, uygulama proje dosyasının kütüphane detaylarından soyutlanacağını hemen anlayabiliriz. Bunun yerine, başlık dosyalarının vb. nerede bulunacağını söylemek kitaplığın sorumluluğundadır.

Bu sorunu çözmek için qmake'in include() yönergesinden yararlanalım. Kitaplık projesinde, .pri uzantılı yeni bir dosyaya başka bir qmake belirtimi ekleyeceğiz (uzantı herhangi bir şey olabilir, ancak burada i içerme anlamına gelir). Bu nedenle, kitaplığın iki özelliği olacaktır: library.pro ve library.pri . Birincisi kütüphaneyi oluşturmak için, ikincisi ise tüketen bir projenin ihtiyaç duyduğu tüm detayları sağlamak için kullanılır.

library.pri dosyasının içeriği aşağıdaki gibi olacaktır:

 LIBTARGET = library BASEDIR = $${PWD} INCLUDEPATH *= $${BASEDIR}/include LIBS += -L$${DESTDIR} -llibrary

BASEDIR , kitaplık projesinin klasörünü belirtir (tam olarak, bizim durumumuzda library.pri olan mevcut qmake belirtim dosyasının konumu). Tahmin edebileceğiniz gibi, INCLUDEPATH /project/library/include olarak değerlendirilecektir. DESTDIR , derleme sisteminin (.o .a .so .dll veya .exe dosyaları) gibi çıktı yapılarını yerleştirdiği dizindir. Bu genellikle IDE'nizde yapılandırılır, bu nedenle çıktı dosyalarının nerede bulunduğuna dair hiçbir varsayımda bulunmamalısınız.

application.pro dosyasına yalnızca include(../library/library.pri) ekleyin ve işiniz bitti.

Bu durumda uygulama projesinin nasıl oluşturulduğunu gözden geçirelim:

  1. Topmost project.pro bir alt dizin projesidir. Önce kütüphane projesinin inşa edilmesi gerektiğini söylüyor. Böylece qmake, kitaplığın klasörüne girer ve onu library.pro kullanarak oluşturur. Bu aşamada library.a üretilir ve DESTDIR klasörüne yerleştirilir.
  2. Ardından qmake, uygulama alt klasörüne girer ve application.pro dosyasını ayrıştırır. qmake'e onu hemen okuması ve yorumlaması talimatını veren include(../library/library.pri) yönergesini bulur. Bu, INCLUDEPATH ve LIBS değişkenlerine yeni tanımlar ekler, böylece artık derleyici ve bağlayıcı, ekleme dosyalarını, kitaplık ikili dosyalarını ve hangi kitaplığın bağlanacağını nerede arayacağını bilir.

Kütüphane testleri projesinin oluşturulmasını atladık, ancak uygulama projesiyle aynı. Açıkçası, test projemizin test etmesi gereken kitaplığı da bağlaması gerekir.

Bu kurulumla, kitaplık projesini kolayca başka bir qmake projesine taşıyabilir ve onu dahil edebilir, böylece .pri dosyasına referans verebilirsiniz. Üçüncü taraf kitaplıkları topluluk tarafından tam olarak bu şekilde dağıtılır.

config.pri

Karmaşık bir projede, birçok alt proje tarafından kullanılan bazı paylaşılan konfigürasyon parametrelerine sahip olmak çok yaygındır. Yinelemeyi önlemek için, include() yönergesinden tekrar yararlanabilir ve üst düzey klasörde config.pri oluşturabilirsiniz. Ayrıca, bu kılavuzda daha sonra tartışacağımıza benzer şekilde, alt projelerinizle paylaşılan ortak qmake “yardımcı programlarınız” olabilir.

Yapıları DESTDIR'a Kopyalama

Çoğu zaman, projelerin bir kitaplık veya uygulama ile birlikte dağıtılması gereken bazı "başka" dosyaları vardır. Oluşturma işlemi sırasında tüm bu dosyaları DESTDIR kopyalayabilmemiz yeterlidir. Aşağıdaki parçacığı göz önünde bulundurun:

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

Not: Bu kalıbı kullanarak, dosyalar üzerinde çalışan kendi yeniden kullanılabilir işlevlerinizi tanımlayabilirsiniz.

Bu kodu /project/copyToDestDir.pri içine yerleştirin, böylece aşağıdaki gibi zorlu alt projelere include() :

 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)

Not: DISTFILES aynı amaç için tanıtıldı, ancak yalnızca Unix'te çalışıyor.

Kod Oluşturma

Önceden oluşturulmuş bir adım olarak kod oluşturmanın harika bir örneği, bir C++ projesinin Google protobuf kullanmasıdır. Şimdi inşa sürecine protoc yürütmeyi nasıl enjekte edebileceğimizi görelim.

Google'da kolayca uygun bir çözüm bulabilirsiniz, ancak önemli bir köşe vakasının farkında olmanız gerekir. A'nın B'ye atıfta bulunduğu iki sözleşmeniz olduğunu hayal edin.

 A.proto <= B.proto

Önce A.proto için kod üretecek olursak ( A.pb.h ve A.pb.cxx üretmek için) ve bunu derleyiciye beslersek, B.pb.h bağımlılığı henüz mevcut olmadığından başarısız olur. Bunu çözmek için, ortaya çıkan kaynak kodunu oluşturmadan önce tüm proto kod oluşturma aşamasını geçmemiz gerekiyor.

Burada bu görev için harika bir pasaj buldum: https://github.com/jmesmon/qmake-protobuf-example/blob/master/protobuf.pri

Oldukça büyük bir komut dosyasıdır, ancak onu nasıl kullanacağınızı zaten bilmelisiniz:

 PROTOS = A.proto B.proto include(protobuf.pri)

protobuf.pri dosyasına bakarken, herhangi bir özel derlemeye veya kod oluşturmaya kolayca uygulanabilen genel kalıbı fark edebilirsiniz:

 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

Kapsamlar ve Koşullar

Genellikle, Windows veya MacOS gibi belirli bir platform için özel olarak bildirimler tanımlamamız gerekir. Qmake, önceden tanımlanmış üç platform göstergesi sunar: win32, macx ve unix. İşte sözdizimi:

 win32 { # add Windows application icon, not applicable to unix/macx platform RC_ICONS += icon.ico }

Kapsamlar iç içe yerleştirilebilir, operatörleri kullanabilir ! , | ve hatta joker karakterler:

 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 }

Not: Unix, Mac OS'de tanımlanmıştır! Mac OS için test etmek istiyorsanız (genel Unix değil), unix:!macx koşulunu kullanın.

Qt Creator'da kapsam koşulları debug ve release beklendiği gibi çalışmıyor. Bunların düzgün çalışmasını sağlamak için aşağıdaki kalıbı kullanın:

 CONFIG(debug, debug|release) { LIBS += ... } CONFIG(release, debug|release) { LIBS += ... }

Faydalı Fonksiyonlar

Qmake, daha fazla otomasyon ekleyen bir dizi gömülü işleve sahiptir.

İlk örnek, files() işlevidir. Değişken sayıda kaynak dosya üreten bir kod oluşturma adımınız olduğunu varsayarsak. Hepsini SOURCES şu şekilde dahil edebilirsiniz:

 SOURCES += $$files(generated/*.c)

Bu, generated alt klasördeki .c uzantılı tüm dosyaları bulur ve bunları SOURCES değişkenine ekler.

İkinci örnek öncekine benzer, ancak şimdi kod oluşturma, çıktı dosyası adlarını (dosya listesi) içeren bir metin dosyası üretti:

 SOURCES += $$cat(generated/filelist, lines)

Bu sadece dosya içeriğini okuyacak ve her satırı SOURCES için bir giriş olarak değerlendirecektir.

Not: Gömülü işlevlerin tam listesi burada bulunabilir: http://doc.qt.io/qt-5/qmake-function-reference.html

Uyarıları Hata Olarak Değerlendirme

Aşağıdaki kod parçası, daha önce açıklanan koşullu kapsam özelliğini kullanır:

 *g++*: QMAKE_CXXFLAGS += -Werror *msvc*: QMAKE_CXXFLAGS += /WX

Bu karmaşıklığın nedeni, MSVC'nin bu seçeneği etkinleştirmek için farklı bir bayrağa sahip olmasıdır.

Git Sürümü Oluşturma

Aşağıdaki pasaj, Git'ten alınan mevcut SW sürümünü içeren bir önişlemci tanımı oluşturmanız gerektiğinde kullanışlıdır:

 DEFINES += SW_VERSION=\\\"$$system(git describe --always --abbrev=0)\\\"

Bu, git komutu mevcut olduğu sürece herhangi bir platformda çalışır. Git etiketlerini kullanırsanız, dal ilerlemiş olsa bile bu en son etikete göz atacaktır. Seçtiğiniz çıktıyı almak için git describe komutunu değiştirin.

Çözüm

Qmake, platformlar arası Qt tabanlı projelerinizi oluşturmaya odaklanan harika bir araçtır. Bu kılavuzda, proje yapınızı esnek tutacak ve yapı belirtimlerinin okunması ve bakımının kolay olmasını sağlayacak temel araç kullanımını ve en sık kullanılan kalıpları inceledik.

Qt uygulamanızın nasıl daha iyi görüneceğini öğrenmek ister misiniz? Deneyin: Bezier Eğrileri ve QPainter Kullanarak C++'da Yuvarlak Köşe Şekilleri Nasıl Elde Edilir: Adım Adım Kılavuz