目錄
- qtcreator.pri
- 判斷重復包含
- 定義版本資訊
- VERSION
- 定義IDE名稱
- 啟用C++14
- CONFIG
- 自定義函式
- Replace Functions
- Test Functions
- _PRO_FILE_PWD_
- _PRO_FILE_
- 設定macOS最小版本
- 設定QTEST模塊
- 設定源目錄和構建目錄
- re_escape(string)
- clean_path(path)
- 設定IDE和INSTALLS相關路徑
- 設定字串宏
- shell_quote
- qmake定義字串宏
- 設定INCLUDEPATH
- INCLUDEPATH
- Whitespace
- 設值庫鏈接路徑和編譯選項
- 解決插件和庫依賴
qtcreator.pri
前面我們介紹了qtcreator.pro,下面我們開始介紹qtcreator.pri,來看看pro中include的pri到底是干什么用的,
注意,許多函式/變數/關鍵字的含義,某些基礎用法,在qtcreator.pro中進行了介紹,
判斷重復包含
qtcreator.pri第一部分是
!isEmpty(QTCREATOR_PRI_INCLUDED):error("qtcreator.pri already included")
QTCREATOR_PRI_INCLUDED = 1
很明顯,isEmpty()為false,則呼叫error報錯退出編譯,那么只能是為true,即要求QTCREATOR_PRI_INCLUDED為空,并在下一行立即定義為1,
那么這個是在干什么呢?我們看變數的名稱就能略窺一二,INCLUDED就是已包含的意思,那么這里就是為了避免在其他地方重復包含qtcreator.pri檔案,類似于C/C++頭檔案中的
# ifndef XXX_H
# define XXX_H
#endif
定義版本資訊
接下來是
QTCREATOR_VERSION = 4.6.2
QTCREATOR_COMPAT_VERSION = 4.6.0
VERSION = $$QTCREATOR_VERSION
QTCREATOR_DISPLAY_VERSION = 4.6.2
QTCREATOR_COPYRIGHT_YEAR = 2018
BINARY_ARTIFACTS_BRANCH = 4.6
VERSION
如果TEMPLATE值為app,則指定應用程式的版本號;如果TEMPLATE值為lib,則指定庫的版本號,
在Windows上,如果未設定RC_FILE和RES_FILE變數,則自動生成.rc檔案, 生成的.rc檔案將具有FILEVERSION和PRODUCTVERSION條目,并用主,次,補丁和構建版本號填充, 每個數字的范圍必須在0到65535之間,有關.rc檔案生成的更多詳細資訊,請參見Platform Notes,
示例:
win32:VERSION = 1.2.3.4 # major.minor.patch.build else:VERSION = 1.2.3 # major.minor.patch
很明顯,是在定義QtCreator的版本,兼容性版本,著作權,以及git分支,
定義IDE名稱
接下來是
isEmpty(IDE_DISPLAY_NAME): IDE_DISPLAY_NAME = Qt Creator
isEmpty(IDE_ID): IDE_ID = qtcreator
isEmpty(IDE_CASED_ID): IDE_CASED_ID = QtCreator
isEmpty(PRODUCT_BUNDLE_IDENTIFIER): PRODUCT_BUNDLE_IDENTIFIER = org.qt-project.$$IDE_ID
我們在qtcreator.pro中已經介紹過isEmpty這種用法,這里在給相關變數設定默認值,
啟用C++14
接下來是
CONFIG += c++14
CONFIG
指定專案配置和編譯器選項, 這些值由qmake內部識別,并具有特殊含義,
以下CONFIG值控制編譯標志:
選項 描述 release 該專案將以release模式構建, 如果還指定了debug,則最后那個生效, debug 該專案將以debug模式構建, debug_and_release 該專案將同時構建debug和release模式, debug_and_release_target 默認情況下設定此選項, 如果還設定了debug_and_release,則debug和release版本最終將放置在單獨的debug和release目錄中, build_all 如果指定了debug_and_release,則默認情況下專案同時構建debug和release模式, autogen_precompile_source 自動生成一個.cpp檔案,其中包含.pro檔案中指定的預編譯頭檔案, ordered 當TEMPLATE為subdirs時,此選項指定應按給出的順序處理列出的目錄,
注意:不建議使用此選項, 如SUBDIRS變數檔案中所述指定依賴項,precompile_header 使能支持在專案中使用precompiled headers, precompile_header_c (MSVC only) 使能支持在C檔案中使用precompiled headers, warn_on 編譯器應盡可能多的輸出警告, 如果還指定了warn_off,則最后那個生效, warn_off 編譯器應盡可能少的輸出警告, exceptions 使能例外支持,默認設定該選項, exceptions_off 禁用例外支持, rtti 使能RTTI支持,默認情況下,使用編譯器默認值, rtti_off 禁用RTTI支持,默認情況下,使用編譯器默認值, stl 使能STL支持,默認情況下,使用編譯器默認值, stl_off 禁用STL支持,默認情況下,使用編譯器默認值, thread 使能Thread支持,當CONFIG包含qt(默認設定)時,將使能此功能, c99 使能C99支持, 如果編譯器不支持C99或無法選擇C標準,則此選項無效, 默認情況下,使用編譯器默認值, c11 使能C11支持, 如果編譯器不支持C11或無法選擇C標準,則此選項無效, 默認情況下,使用編譯器默認值, strict_c 禁用對C編譯器擴展的支持, 默認情況下,它們是使能的, c++11 使能C++11支持, 如果編譯器不支持C++11或無法選擇C++標準,則此選項無效, 默認情況下,使用編譯器默認值, c++14 使能C++14支持, 如果編譯器不支持C++14或無法選擇C++標準,則此選項無效, 默認情況下,使用編譯器默認值, c++1z 使能C++17支持, 如果編譯器不支持C++17或無法選擇C++標準,則此選項無效, 默認情況下,使用編譯器默認值, c++17 同c++1z c++2a 使能C++2a支持, 如果編譯器不支持C++2a或無法選擇C++標準,則此選項無效, 默認情況下,使用編譯器默認值, c++latest 如果編譯器支持,使能最新C++語言標準的支持, 默認情況下,此選項是禁用的, strict_c++ 禁用對C++編譯器擴展的支持, 默認情況下,它們是使能的, depend_includepath 使能將INCLUDEPATH的值附加到DEPENDPATH, 默認設定此選項, lrelease 對TRANSLATIONS 和EXTRA_TRANSLATIONS中列出的所有檔案運行lrelease, 如果未設定embed_translations,則將生成的.qm檔案安裝到QM_FILES_INSTALL_PATH中, 使用QMAKE_LRELEASE_FLAGS向lrelease呼叫添加引數選項, 默認情況下未設定此選項, embed_translations 將lrelease生成的翻譯內容嵌入QM_FILES_RESOURCE_PREFIX下的可執行檔案中, 也需要同時設定lrelease, 默認情況下未設定此選項, create_libtool 為當前構建的庫創建一個libtool.la檔案, create_pc 為當前構建的庫創建一個pkg-config .pc檔案, no_batch 僅限NMake:關閉NMake批處理規則或推斷規則的生成, skip_target_version_ext 在Windows上禁止附加自動版本號到DLL檔案名, suppress_vcproj_warnings 禁止VS專案生成器的警告, windeployqt 鏈接后自動呼叫windeployqt,并將輸出添加為部署項, dont_recurse 禁止對當前子專案的qmake遞回, no_include_pwd 不要將當前目錄添加到INCLUDEPATHS, 當您使用debug_and_release選項(在Windows下是默認設定)時,專案將被處理三次:一次生成元Makefile,再兩次生成Makefile.Debug和Makefile.Release,
在后面的程序中,將build_pass和相應的debug或release選項附加到CONFIG, 這樣就可以執行特定構建任務, 例如:
build_pass:CONFIG(debug, debug|release) { unix: TARGET = $$join(TARGET,,,_debug) else: TARGET = $$join(TARGET,,,d) }作為手動撰寫構建型別條件的替代方法,除了常規QMAKE_LFLAGS外,某些變數還提供特定構建變數,例如 QMAKE_LFLAGS_RELEASE, 這些應在可用時使用,
元Makefile通過debug和release目標進行子構建呼叫,并可通過all目標進行聯合構建呼叫, 使用build_all選項時,聯合構建為默認設定, 否則,CONFIG中最后指定的來自集合(debug,release)的選項會變為默認選項, 在這種情況下,您可以顯式呼叫all以一次構建兩個配置:
make all注意:在生成Visual Studio和Xcode專案時,詳細資訊略有不同,
鏈接庫時,qmake依賴基礎平臺,來了解該庫應該鏈接的其他庫, 但是,如果是靜態鏈接,qmake不會獲取此資訊,除非使用以下CONFIG選項:
選項 描述 create_prl 此選項使qmake可以跟蹤這些依賴性, 使能此選項后,qmake將創建擴展名為.prl的檔案,該檔案將保存有關庫的元資訊(有關更多資訊,請參見Library Dependencies), link_prl 使能此選項后,qmake將處理該應用程式鏈接的所有庫并找到其元資訊(有關更多資訊,請參見Library Dependencies), no_install_prl 此選項禁用創建.prl檔案的安裝規則的生成, 注意:構建靜態庫時,需要create_prl選項,而使用靜態庫時,則需要link_prl選項,
以下選項定義應用程式或庫的型別:
選項 描述 qt 目標是Qt應用程式或庫,并且需要Qt庫和頭檔案, Qt庫正確的包含和庫路徑將自動添加到專案中, 這是默認定義的,可以使用\l{#qt}{QT}變數進行微調, x11 目標是X11應用程式或庫, 正確的包含路徑和庫將自動添加到專案中, testcase 目標是一個自動測驗, 一個檢查目標將被添加到生成的Makefile中,以運行測驗, 僅在生成Makefile時相關, insignificant_test 自動測驗的退出代碼將被忽略, 僅當還設定了testcase時才相關, windows 目標是Win32視窗應用程式(僅適用于TEMPLATE為app), 正確的包含路徑,編譯器標志和庫將自動添加到專案中, console 目標是Win32控制臺應用程式(僅適用于TEMPLATE為app), 正確的包含路徑,編譯器標志和庫將自動添加到專案中,考慮將選項cmdline用于跨平臺應用程式, cmdline 目標是跨平臺的命令列應用程式, 在Windows上,這意味著CONFIG += console, 在macOS上,這意味著CONFIG -= app_bundle, shared 目標是共享物件/DLL, 正確的包含路徑,編譯器標志和庫將自動添加到專案中, 請注意,dll也可以在所有平臺上使用, 將創建帶有目標平臺的適當后綴(.dll或.so)的共享庫檔案, dll 同上, static 目標是靜態庫(僅lib), 正確的編譯器標志將自動添加到專案中, staticlib 同上, plugin 目標是插件(僅lib), 這也會使能dll, designer 目標是Qt Designer的插件, no_lflags_merge 確保存盤在LIBS變數中的庫串列在使用前不減少為值唯一(去除了重復的)串列, 這些選項僅在Windows上定義特定功能:
選項 描述 flat 使用vcapp模板時,這會將所有源檔案置于源組中,并將頭檔案置于頭組中,而不管它們位于哪個目錄中,關閉此選項,將根據檔案所在目錄歸類, 默認情況下是打開的, embed_manifest_dll 將清單檔案嵌入到作為庫專案一部分的DLL中, embed_manifest_exe 將清單檔案嵌入到作為應用程式專案一部分的EXE中, 有關嵌入清單檔案的選項的更多資訊,請參見Platform Notes,
以下選項僅在macOS上有效:
選項 描述 app_bundle 將可執行檔案放入捆綁包(這是默認設定), lib_bundle 將庫放入庫包(這是默認設定), plugin_bundle 將插件放入插件包中, Xcode專案生成器不支持此值, 捆綁軟體的構建程序也受QMAKE_BUNDLE_DATA變數內容的影響,
以下選項僅在Linux / Unix平臺上有效:
選項 描述 largefile 支持大檔案的包含 separate_debug_info 把庫的除錯資訊放到單獨的檔案中 決議作用域時,將檢查CONFIG變數, 您可以為該變數分配任何內容,
例如:
CONFIG += console newstuff ... newstuff { SOURCES += new.cpp HEADERS += new.h }
自定義函式
接下來是
defineReplace(qtLibraryTargetName) {
unset(LIBRARY_NAME)
LIBRARY_NAME = $$1
CONFIG(debug, debug|release) {
!debug_and_release|build_pass {
mac:RET = $$member(LIBRARY_NAME, 0)_debug
else:win32:RET = $$member(LIBRARY_NAME, 0)d
}
}
isEmpty(RET):RET = $$LIBRARY_NAME
return($$RET)
}
defineReplace(qtLibraryName) {
RET = $$qtLibraryTargetName($$1)
win32 {
VERSION_LIST = $$split(QTCREATOR_VERSION, .)
RET = $$RET$$first(VERSION_LIST)
}
return($$RET)
}
defineTest(minQtVersion) {
maj = $$1
min = $$2
patch = $$3
isEqual(QT_MAJOR_VERSION, $$maj) {
isEqual(QT_MINOR_VERSION, $$min) {
isEqual(QT_PATCH_VERSION, $$patch) {
return(true)
}
greaterThan(QT_PATCH_VERSION, $$patch) {
return(true)
}
}
greaterThan(QT_MINOR_VERSION, $$min) {
return(true)
}
}
greaterThan(QT_MAJOR_VERSION, $$maj) {
return(true)
}
return(false)
}
# For use in custom compilers which just copy files
defineReplace(stripSrcDir) {
return($$relative_path($$absolute_path($$1, $$OUT_PWD), $$_PRO_FILE_PWD_))
}
Replace Functions
qmake提供了一些內置函式,以允許處理變數的內容, 這些函式處理提供給它們的引數,并回傳一個值或值串列,
要將結果分配給變數,可以將$$運算子與此類函式一起使用,就像將一個變數的內容分配給另一個一樣:HEADERS = model.h HEADERS += $$OTHER_HEADERS HEADERS = $$unique(HEADERS)此類函式應在賦值的右側(即,作為運算元),
您可以定義自己的函式來處理變數的內容,如下所示:
defineReplace(functionName){ #function code }以下示例函式將變數名作為唯一引數,使用內置函式eval()從變數中提取值串列,并編譯檔案串列:
defineReplace(headersAndSources) { variable = $$1 names = $$eval($$variable) headers = sources = for(name, names) { header = $${name}.h exists($$header) { headers += $$header } source = $${name}.cpp exists($$source) { sources += $$source } } return($$headers $$sources) }引數$$1
Test Functions
qmake提供了內置函式,可以在撰寫作用域時用作條件, 這些函式不回傳值,而是指示成功或失敗:
count(options, 2) { message(Both release and debug specified.) }此類函式應僅在條件運算式中使用,
可以定義自己的函式以提供作用域條件, 以下示例測驗串列中的每個檔案是否存在,如果全部存在,則回傳true,否則回傳false:
defineTest(allFiles) { files = $$ARGS for(file, files) { !exists($$file) { return(false) } } return(true) }引數串列$$ARGS
_PRO_FILE_PWD_
包含正在使用的專案檔案的目錄的路徑,(即使該變數出現在 .pri 檔案,也是指包含該 .pri 檔案的 .pro 檔案所在目錄的路徑,)
例如,以下行導致包含專案檔案的目錄的位置寫入控制臺:
message($$_PRO_FILE_PWD_)注意:請勿嘗試覆寫此變數的值,
_PRO_FILE_
包含正在使用的專案檔案的路徑,
例如,以下行導致專案檔案的位置寫入控制臺:
message($$_PRO_FILE_)注意:請勿嘗試覆寫此變數的值,
現在我們來分析pri中定義的三個函式,
因為這兩個函式在 Qt Creator 中使用了多次,并且完全可以拷貝復制到其它專案繼續使用,
自定義替換函式qtLibraryTargetName,
- 取消LIBRARY_NAME的定義,設定LIBRARY_NAME為
$$1,即函式的第一個引數, - CONFIG測驗函式判斷debug或release模式,
- 如果是debug模式,再次進行判斷,
- 如果CONFIG沒有設定debug_and_release或者是構建程序build_pass,則設定RET變數,對于 mac,LIBRARY_NAME值后面添加_debug賦給RET,對于win,LIBRARY_NAME值后面添加d賦給RET,
- 如果RET為空,則把LIBRARY_NAME值賦給RET,
- 回傳RET,
簡單來說,該函式實作功能:在debug環境下,在庫名后面添加_debug或_d尾綴,來跟release模式進行區分,當然,也有其他方式來實作上述功能,譬如使用join()函式,見CONFIG小節,
自定義替換函式qtLibraryName,
- 使用qtLibraryTargetName()函式,對輸入的第一個引數進行替換,并賦值給RET,
- 如果 是win32 系統,
- 使用split()函式,將前面定義的QTCREATOR_VERSION(即4.6.2),使用'.'進行分隔得到串列,賦值給VERSION_LIST
- 使用first()函式,獲取VERSION_LIST的第一個元素(即4),與RET拼接,并賦值給RET,
- 回傳RET,
簡單來說,該函式實作功能:為了在win32系統中避免出現 dll hell,在win32系統下,在庫名后面添加主版本號,
自定義測驗函式minQtVersion,
- 獲取三個引數(即,主/次/補丁),并賦值給maj,min,patch,
- maj <= QT_MAJOR_VERSION,min <= QT_MINOR_VERSION和patch <= QT_PATCH_VERSION,則回傳true,其他回傳false,
簡單來說,該函式實作功能:函式引數的版本號小于等于當前Qt的版本號,
自定義替換函式stripSrcDir,
- 獲取絕對路徑,absolute_path回傳$$OUT_PWD/$$1,
- 獲取相對路徑,步驟1中獲取的絕對路徑相對與$$_PRO_FILE_PWD_的相對路徑,
簡單來說,該函式實作功能(見自帶的注釋):用于自定義編譯器拷貝檔案,
設定macOS最小版本
接下來是:
darwin:!minQtVersion(5, 7, 0) {
# Qt 5.6 still sets deployment target 10.7, which does not work
# with all C++11/14 features (e.g. std::future)
QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.8
}
如果是基于Darwin作業系統的,并且Qt 的版本低于5.7.0時,設定應用程式支持的macOs最小版本,可以從注釋看出,10.7不支持C++11/14特性,
設定QTEST模塊
接下來是
QTC_BUILD_TESTS = $$(QTC_BUILD_TESTS)
!isEmpty(QTC_BUILD_TESTS):TEST = $$QTC_BUILD_TESTS
!isEmpty(BUILD_TESTS):TEST = 1
isEmpty(TEST):CONFIG(debug, debug|release) {
!debug_and_release|build_pass {
TEST = 1
}
}
isEmpty(IDE_LIBRARY_BASENAME) {
IDE_LIBRARY_BASENAME = lib
}
equals(TEST, 1) {
QT +=testlib
DEFINES += WITH_TESTS
}
- 如果設定了QTC_BUILD_TESTS,則賦值給TEST,
- 如果設定了BUILD_TESTS,則給TEST賦值1,
- 如果TEST沒有值,且為debug模式,并且沒有設定debug_and_release,則在構建程序中,設定TEST為1,
- 如果IDE_LIBRARY_BASENAME為空,則為庫賦值基礎名為lib,
- 如果TEST等于1,則添加QTEST模塊功能,
設定源目錄和構建目錄
接下來是
IDE_SOURCE_TREE = $$PWD
isEmpty(IDE_BUILD_TREE) {
sub_dir = $$_PRO_FILE_PWD_
sub_dir ~= s,^$$re_escape($$PWD),,
IDE_BUILD_TREE = $$clean_path($$OUT_PWD)
IDE_BUILD_TREE ~= s,$$re_escape($$sub_dir)$,,
}
re_escape(string)
對每個string中的特殊正則運算式字符,使用反斜杠轉義,回傳轉義后的字串, 該函式是QRegExp::escape的包裝,
例如:
s1 = QRegExp::escape("bingo"); // s1 == "bingo" s2 = QRegExp::escape("f(x)"); // s2 == "f\\(x\\)"
clean_path(path)
處理path,對目錄分隔符進行規范化(轉換為"/"),洗掉了多余的目錄分隔符,并且決議"."和".."(盡可能), 該函式是QDir::cleanPath的包裝,
另請閱absolute_path(), relative_path(), shell_path(), system_path().
我們在代碼后面插樁輸出陳述句
build_pass:message($$PWD) # 當前pri檔案所在目錄
build_pass:message($$OUT_PWD) # 生成makefile所在目錄
build_pass:message($$_PRO_FILE_) # 包含當前pri的pro所在路徑
build_pass:message($$_PRO_FILE_PWD_) # 包含當前pri的pro所在目錄
現在,我們來看一下部分輸出,
Project MESSAGE: F:/plugin/qt_creator/qt-creator-opensource-src-4.6.2
Project MESSAGE: F:/plugin/qt_creator/build-qtcreator-Desktop_Qt_5_11_1_MinGW_32bit-Debug-splt-debug-info/bin
Project MESSAGE: F:/plugin/qt_creator/qt-creator-opensource-src-4.6.2/bin/bin.pro
Project MESSAGE: F:/plugin/qt_creator/qt-creator-opensource-src-4.6.2/bin
Project MESSAGE: F:/plugin/qt_creator/qt-creator-opensource-src-4.6.2
Project MESSAGE: F:/plugin/qt_creator/build-qtcreator-Desktop_Qt_5_11_1_MinGW_32bit-Debug-splt-debug-info/src/app
Project MESSAGE: F:/plugin/qt_creator/qt-creator-opensource-src-4.6.2/src/app/app.pro
Project MESSAGE: F:/plugin/qt_creator/qt-creator-opensource-src-4.6.2/src/app
我們可以發現
- PWD沒有發生變化,
- 對比OUT_PWD和_PRO_FILE_PWD_,輸出目錄和源目錄的子檔案夾組織架構一樣,
下面我們分析pri中的陳述句
-
設定源目錄IDE_SOURCE_TREE,
-
如果構建目錄IDE_BUILD_TREE為空,
- 設定sub_dir,并進行替換,可以認為是從_PRO_FILE_PWD_減去PWD,剩下子檔案夾相對路徑,如bin/和app/,
- 初始化IDE_BUILD_TREE,并進行替換,可以認為是從OUT_PWD減去相對路徑,剩下相同的根目錄,
大家可以用我們上面的message輸出結果來簡單的計算下即可,
IDE_SOURCE_TREE為F:/plugin/qt_creator/qt-creator-opensource-src-4.6.2,
IDE_BUILD_TREE為F:/plugin/qt_creator/build-qtcreator-Desktop_Qt_5_11_1_MinGW_32bit-Debug-splt-debug-info,
設定IDE和INSTALLS相關路徑
接下來是
IDE_APP_PATH = $$IDE_BUILD_TREE/bin
osx {
IDE_APP_TARGET = "$$IDE_DISPLAY_NAME"
# check if IDE_BUILD_TREE is actually an existing Qt Creator.app,
# for building against a binary package
exists($$IDE_BUILD_TREE/Contents/MacOS/$$IDE_APP_TARGET): IDE_APP_BUNDLE = $$IDE_BUILD_TREE
else: IDE_APP_BUNDLE = $$IDE_APP_PATH/$${IDE_APP_TARGET}.app
# set output path if not set manually
isEmpty(IDE_OUTPUT_PATH): IDE_OUTPUT_PATH = $$IDE_APP_BUNDLE/Contents
IDE_LIBRARY_PATH = $$IDE_OUTPUT_PATH/Frameworks
IDE_PLUGIN_PATH = $$IDE_OUTPUT_PATH/PlugIns
IDE_LIBEXEC_PATH = $$IDE_OUTPUT_PATH/Resources
IDE_DATA_PATH = $$IDE_OUTPUT_PATH/Resources
IDE_DOC_PATH = $$IDE_DATA_PATH/doc
IDE_BIN_PATH = $$IDE_OUTPUT_PATH/MacOS
copydata = https://www.cnblogs.com/codeForFamily/p/1
LINK_LIBRARY_PATH = $$IDE_APP_BUNDLE/Contents/Frameworks
LINK_PLUGIN_PATH = $$IDE_APP_BUNDLE/Contents/PlugIns
INSTALL_LIBRARY_PATH = $$QTC_PREFIX/$${IDE_APP_TARGET}.app/Contents/Frameworks
INSTALL_PLUGIN_PATH = $$QTC_PREFIX/$${IDE_APP_TARGET}.app/Contents/PlugIns
INSTALL_LIBEXEC_PATH = $$QTC_PREFIX/$${IDE_APP_TARGET}.app/Contents/Resources
INSTALL_DATA_PATH = $$QTC_PREFIX/$${IDE_APP_TARGET}.app/Contents/Resources
INSTALL_DOC_PATH = $$INSTALL_DATA_PATH/doc
INSTALL_BIN_PATH = $$QTC_PREFIX/$${IDE_APP_TARGET}.app/Contents/MacOS
INSTALL_APP_PATH = $$QTC_PREFIX/
} else {
contains(TEMPLATE, vc.*):vcproj = 1
IDE_APP_TARGET = $$IDE_ID
# target output path if not set manually
isEmpty(IDE_OUTPUT_PATH): IDE_OUTPUT_PATH = $$IDE_BUILD_TREE
IDE_LIBRARY_PATH = $$IDE_OUTPUT_PATH/$$IDE_LIBRARY_BASENAME/qtcreator
IDE_PLUGIN_PATH = $$IDE_LIBRARY_PATH/plugins
IDE_DATA_PATH = $$IDE_OUTPUT_PATH/share/qtcreator
IDE_DOC_PATH = $$IDE_OUTPUT_PATH/share/doc/qtcreator
IDE_BIN_PATH = $$IDE_OUTPUT_PATH/bin
win32: /
IDE_LIBEXEC_PATH = $$IDE_OUTPUT_PATH/bin
else: /
IDE_LIBEXEC_PATH = $$IDE_OUTPUT_PATH/libexec/qtcreator
!isEqual(IDE_SOURCE_TREE, $$IDE_OUTPUT_PATH):copydata = 1
LINK_LIBRARY_PATH = $$IDE_BUILD_TREE/$$IDE_LIBRARY_BASENAME/qtcreator
LINK_PLUGIN_PATH = $$LINK_LIBRARY_PATH/plugins
INSTALL_LIBRARY_PATH = $$QTC_PREFIX/$$IDE_LIBRARY_BASENAME/qtcreator
INSTALL_PLUGIN_PATH = $$INSTALL_LIBRARY_PATH/plugins
win32: /
INSTALL_LIBEXEC_PATH = $$QTC_PREFIX/bin
else: /
INSTALL_LIBEXEC_PATH = $$QTC_PREFIX/libexec/qtcreator
INSTALL_DATA_PATH = $$QTC_PREFIX/share/qtcreator
INSTALL_DOC_PATH = $$QTC_PREFIX/share/doc/qtcreator
INSTALL_BIN_PATH = $$QTC_PREFIX/bin
INSTALL_APP_PATH = $$QTC_PREFIX/bin
}
我們可以發現上面的內容大部分是基于IDE_BUILD_TREE和QTC_PREFIX的,
代碼首先設定了可執行程式的目錄,
接下來,我們重點分析else分支的內容,
-
如果TEMPLATE包含vc.*,其實就是vcapp或vclib,設定vcproj為1,表示是vs工程,
-
設定可執行程式檔案名為IDE_ID(默認為qtcreator),
-
設定輸出路徑IDE_OUTPUT_PATH默認為IDE_BUILD_TREE,
-
設定IDE相關子檔案夾路徑,可以發現都是相對于IDE_OUTPUT_PATH的,
-
如果輸出路徑不是源目錄,則設定copydata為1,表示需要拷貝資料,
-
考慮到IDE_BUILD_TREE與IDE_OUTPUT_PATH可能不一樣,設定IDE庫和插件的鏈接路徑,
-
設定INSTALLS用的相關子檔案夾路徑,
設定字串宏
接下來是
RELATIVE_PLUGIN_PATH = $$relative_path($$IDE_PLUGIN_PATH, $$IDE_BIN_PATH)
RELATIVE_LIBEXEC_PATH = $$relative_path($$IDE_LIBEXEC_PATH, $$IDE_BIN_PATH)
RELATIVE_DATA_PATH = $$relative_path($$IDE_DATA_PATH, $$IDE_BIN_PATH)
RELATIVE_DOC_PATH = $$relative_path($$IDE_DOC_PATH, $$IDE_BIN_PATH)
DEFINES += $$shell_quote(RELATIVE_PLUGIN_PATH=\"$$RELATIVE_PLUGIN_PATH\")
DEFINES += $$shell_quote(RELATIVE_LIBEXEC_PATH=\"$$RELATIVE_LIBEXEC_PATH\")
DEFINES += $$shell_quote(RELATIVE_DATA_PATH=\"$$RELATIVE_DATA_PATH\")
DEFINES += $$shell_quote(RELATIVE_DOC_PATH=\"$$RELATIVE_DOC_PATH\")
shell_quote
在qmake中的介紹很簡單:為shell對arg加引號,當構建構建專案時,
在linux man page中的介紹:可讓您通過shell傳遞任意字串,shell不會更改它們, 這使您可以安全地處理帶有
嵌入式空格或shell globbing字符的命令或檔案,
qmake定義字串宏
有時候,我們想定義字串宏,并在源代碼中進行使用,假設你想在qmake中定義字串宏,這里有三種途徑
![]()
我們先來看一下qmake編譯得到的Makefile.Debug,符合makefile語法的形式
![]()
現在我們來介紹下DEFINES中的含義:
NAME1中第一個"對,告訴qmake引導里面的是字串,里面的\"對,是對引號的轉義,在makefile中變為",再里面的\\對,也是轉義,在makefile中變為\,在里面的\"同樣,最終變為",最終得到我們想要的字串,
NAME2使用shell_quote()函式,該函式對引數加引號,
NAME0對比NAME1,少了最外面的"對,這導致NAME0只能定義沒有空格的字串,如果存在空格,這會導致內容發生變化,中間多了個-D,
qmake: DEFINES += NAME0=\"\\\"app1 .0\\\"\" makefile: -DNAME0="\"app1 -D.0\""此外,對于沒有空格的字串宏定義,我們甚至可以不需要最外層的引號轉義,
qmake: DEFINES += NAME0=\\\"app1\\\" makefile: -DNAME0=\"app1\"
分析代碼:
- 設定了PLUGIN,LIBEXEC,DATA和DOC相對于BIN的相對路徑,譬如PLUGIN的為../lib/qtcreator/plugins,
- 使步驟1中的變數稱為字串,添加到DEFINES中,變為宏,
設定INCLUDEPATH
接下來是
INCLUDEPATH += \
$$IDE_BUILD_TREE/src \ # for <app/app_version.h> in case of actual build directory
$$IDE_SOURCE_TREE/src \ # for <app/app_version.h> in case of binary package with dev package
$$IDE_SOURCE_TREE/src/libs \
$$IDE_SOURCE_TREE/tools
win32:exists($$IDE_SOURCE_TREE/lib/qtcreator) {
# for .lib in case of binary package with dev package
LIBS *= -L$$IDE_SOURCE_TREE/lib/qtcreator
LIBS *= -L$$IDE_SOURCE_TREE/lib/qtcreator/plugins
}
QTC_PLUGIN_DIRS_FROM_ENVIRONMENT = $$(QTC_PLUGIN_DIRS)
QTC_PLUGIN_DIRS += $$split(QTC_PLUGIN_DIRS_FROM_ENVIRONMENT, $$QMAKE_DIRLIST_SEP)
QTC_PLUGIN_DIRS += $$IDE_SOURCE_TREE/src/plugins
for(dir, QTC_PLUGIN_DIRS) {
INCLUDEPATH += $$dir
}
QTC_LIB_DIRS_FROM_ENVIRONMENT = $$(QTC_LIB_DIRS)
QTC_LIB_DIRS += $$split(QTC_LIB_DIRS_FROM_ENVIRONMENT, $$QMAKE_DIRLIST_SEP)
QTC_LIB_DIRS += $$IDE_SOURCE_TREE/src/libs
for(dir, QTC_LIB_DIRS) {
INCLUDEPATH += $$dir
}
CONFIG += \
depend_includepath \
no_include_pwd
INCLUDEPATH
指定編譯專案時應搜索的#include目錄,
例如:
INCLUDEPATH = c:/msdev/include d:/stl/include要指定包含空格的路徑,請使用Whitespace中所述的技術對路徑添加引號,
win32:INCLUDEPATH += "C:/mylibs/extra headers" unix:INCLUDEPATH += "/home/user/extra headers"
Whitespace
通常,空格在變數賦值是分隔值, 要指定包含空格的值,必須將值用雙引號引起來:
DEST = "Program Files"引號引起來的文本在變數所保存的值串列中被視為單個條目,使用類似的方法可處理包含空格的路徑,尤其是在為Windows平臺定義INCLUDEPATH 和LIBS變數時:
win32:INCLUDEPATH += "C:/mylibs/extra headers" unix:INCLUDEPATH += "/home/user/extra headers"
<app/app_version.h>
我們在原始碼中搜索app_version.h,可以在src/app/app.pro中發現定義:
# an hidden functionality in qmake that take a file with '.in' suffix # and creates a copy in the build directory without the suffix in which # variables have been expanded QMAKE_SUBSTITUTES += $$PWD/app_version.h.in注釋很明白,qmake中的隱藏功能,對于后綴為".in"的檔案,在構建目錄中創建一個沒有后綴的副本,并對其中變數進行擴展,
那么app_version.h.in就變為了app_version.h,現在我們簡單看下該檔案
... const char IDE_DISPLAY_NAME[] = \"$${IDE_DISPLAY_NAME}\"; const char IDE_ID[] = \"$${IDE_ID}\"; const char IDE_CASED_ID[] = \"$${IDE_CASED_ID}\"; #define IDE_VERSION $${QTCREATOR_VERSION} ...上面截取的內容,就是對qtcreator.pri中定義的變數,進行了展開,賦值給同名的char []變數,
其實app_version.h.in中包含了Qt Creator的相關IDE版本資訊,
代碼首先添加#include搜索路徑,特別說明<app/app_version.h>,為了參考構建目錄中創建的app_version.h,需要在INCLUDEPATH中添加$$IDE_BUILD_TREE/src,
展開的app_version.h的內容如下
接下來的判斷win32系統下,是否在源目錄中存在lib/qtcreator子目錄,存在就過濾重復的鏈接路徑,
接下來是對插件檔案夾QTC_PLUGIN_DIRS按照分隔符QMAKE_DIRLIST_SEP進行分隔,得到插件檔案夾串列,上述可能為空,所以又添加了源目錄下的src/plugins子目錄,現在QTC_PLUGIN_DIRS至少為src/plugins,
for陳述句和c++11中的新增的for陳述句很像,不再展開,意思也很明確,遍歷插件檔案夾串列中的每個條目,添加進#include搜索路徑,
對于QTC_LIB_DIRS,同QTC_PLUGIN_DIRS,
CONFIG的depend_includepath和no_include_pwd含義,見上面的CONFIG子小節,
這么操作后,對于每個包含qtcreator.pri的子專案,可能很方便的統一添加相同的plugins和libs中的頭檔案,
設值庫鏈接路徑和編譯選項
接下來是
LIBS *= -L$$LINK_LIBRARY_PATH # Qt Creator libraries
exists($$IDE_LIBRARY_PATH): LIBS *= -L$$IDE_LIBRARY_PATH # library path from output path
!isEmpty(vcproj) {
DEFINES += IDE_LIBRARY_BASENAME=\"$$IDE_LIBRARY_BASENAME\"
} else {
DEFINES += IDE_LIBRARY_BASENAME=\\\"$$IDE_LIBRARY_BASENAME\\\"
}
DEFINES += \
QT_CREATOR \
QT_NO_CAST_TO_ASCII \
QT_RESTRICTED_CAST_FROM_ASCII \
QT_DISABLE_DEPRECATED_BEFORE=0x050600 \
QT_USE_FAST_OPERATOR_PLUS \
QT_USE_FAST_CONCATENATION
unix {
CONFIG(debug, debug|release):OBJECTS_DIR = $${OUT_PWD}/.obj/debug-shared
CONFIG(release, debug|release):OBJECTS_DIR = $${OUT_PWD}/.obj/release-shared
CONFIG(debug, debug|release):MOC_DIR = $${OUT_PWD}/.moc/debug-shared
CONFIG(release, debug|release):MOC_DIR = $${OUT_PWD}/.moc/release-shared
RCC_DIR = $${OUT_PWD}/.rcc
UI_DIR = $${OUT_PWD}/.uic
}
msvc {
#Don't warn about sprintf, fopen etc being 'unsafe'
DEFINES += _CRT_SECURE_NO_WARNINGS
QMAKE_CXXFLAGS_WARN_ON *= -w44996
# Speed up startup time when debugging with cdb
QMAKE_LFLAGS_DEBUG += /INCREMENTAL:NO
}
qt {
contains(QT, core): QT += concurrent
contains(QT, gui): QT += widgets
}
QBSFILE = $$replace(_PRO_FILE_, \\.pro$, .qbs)
exists($$QBSFILE):DISTFILES += $$QBSFILE
!isEmpty(QTC_PLUGIN_DEPENDS) {
LIBS *= -L$$IDE_PLUGIN_PATH # plugin path from output directory
LIBS *= -L$$LINK_PLUGIN_PATH # when output path is different from Qt Creator build directory
}
我們依次分析:
-
LIBS中添加qt creator的lib庫鏈接路徑,并去除重復項,
-
DEFINES添加字串宏,看樣子vcproj下宏的定義不需要對引號進行轉義哈,
-
DEFINES添加其他相關宏,看樣子就是用來設定編譯選項,
-
unix系統下,首先分別對debug和release模式設定對應的obj檔案夾,然后設定qt資源編譯輸出檔案RCC和用戶介面檔案UI的檔案夾,
-
msvc平臺下,設定相關編譯指令,
-
目標是Qt應用程式或庫,并且包含core核心模塊或gui影像用戶界面模塊,則添加concurrent并發模塊和widget視窗模塊,
-
對于每個pro檔案,替換擴展名為qbs,如果同一目錄下存在該qbs檔案,則添加到dist目標檔案串列中,
-
最后,如果插件依賴QTC_PLUGIN_DEPENDS有值,則LIBS添加插件依賴路徑,并去除重復項,
這么操作后,對于每個包含qtcreator.pri的子專案,可能很方便的統一添加相同的庫鏈接路徑和編譯選項,
解決插件和庫依賴
接下來是
# recursively resolve plugin deps
done_plugins =
for(ever) {
isEmpty(QTC_PLUGIN_DEPENDS): \
break()
done_plugins += $$QTC_PLUGIN_DEPENDS
for(dep, QTC_PLUGIN_DEPENDS) {
dependencies_file =
for(dir, QTC_PLUGIN_DIRS) {
exists($$dir/$$dep/$${dep}_dependencies.pri) {
dependencies_file = $$dir/$$dep/$${dep}_dependencies.pri
break()
}
}
isEmpty(dependencies_file): \
error("Plugin dependency $$dep not found")
include($$dependencies_file)
LIBS += -l$$qtLibraryName($$QTC_PLUGIN_NAME)
}
QTC_PLUGIN_DEPENDS = $$unique(QTC_PLUGIN_DEPENDS)
QTC_PLUGIN_DEPENDS -= $$unique(done_plugins)
}
# recursively resolve library deps
done_libs =
for(ever) {
isEmpty(QTC_LIB_DEPENDS): \
break()
done_libs += $$QTC_LIB_DEPENDS
for(dep, QTC_LIB_DEPENDS) {
dependencies_file =
for(dir, QTC_LIB_DIRS) {
exists($$dir/$$dep/$${dep}_dependencies.pri) {
dependencies_file = $$dir/$$dep/$${dep}_dependencies.pri
break()
}
}
isEmpty(dependencies_file): \
error("Library dependency $$dep not found")
include($$dependencies_file)
LIBS += -l$$qtLibraryName($$QTC_LIB_NAME)
}
QTC_LIB_DEPENDS = $$unique(QTC_LIB_DEPENDS)
QTC_LIB_DEPENDS -= $$unique(done_libs)
}
注釋說的很明白,一個遞回解決插件依賴,一個遞回解決庫依賴,
首先我們來看下依賴檔案示例,源目錄src\plugins\cppeditor\cppeditor_dependencies.pri,
QTC_PLUGIN_NAME = CppEditor
QTC_LIB_DEPENDS += \
extensionsystem \
utils \
cplusplus
QTC_PLUGIN_DEPENDS += \
texteditor \
coreplugin \
cpptools \
projectexplorer
QTC_TEST_DEPENDS += \
qmakeprojectmanager
內容顯而易見,首先指定插件名稱(同檔案夾名,用于查找),然后在依賴項指定其他插件,
首先,我們分析插件依賴,for(ever)顧名思義是死回圈,只能通過break()或者error()退出,
-
如果QTC_PLUGIN_DEPENDS為空,則退出回圈,
-
遍歷QTC_PLUGIN_DEPENDS中的每一個依賴dep,
-
對于dep,遍歷QTC_PLUGIN_DIRS中的每一個插件檔案夾dir,譬如源目錄中的src/plugins,如果{dep}子檔案夾中存在{dep}_dependencies.pri檔案,則找到需要的依賴,
-
如果遍歷完畢都沒有找到,則報錯退出,
-
如果找到了,則include加載之,并從依賴檔案中獲取QTC_PLUGIN_NAME,添加到LIBS用于鏈接,
-
-
由于include時,會加入該插件的依賴插件,可能重復,所以需要去除重復項,
-
去除已經解決依賴的插件,
-
回到步驟1,重復,直到每一個依賴都被解決,
這段代碼,允許用戶在編譯時直接通過QTC_PLUGIN_DEPENDS指定插件依賴,
庫依賴函式同上,
原創造福大家,共享改變世界
獻出一片愛心,溫暖作者心靈
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/14321.html
標籤:其他
