Qmake 配置自定義編譯過程
需求:動態更換資源文件
在 Windows10 下編寫 Qt 項目時,有這樣的需求:
- 程序用到的資源文件可以動態更換而不需要重新編譯整個項目
解決方案 0.1
將所有的資源文件全部放到 qrc 文件中,由 Qt 負責管理資源文件。
但是這種方法在每次更改了 res 中的文件后都需要重新編譯程序。比較麻煩。
新的需求
於是需求變成了:
- 將源代碼目錄下的
res
文件夾下的 xml 文件全部復制到編譯好的程序同級目錄下
解決方案 1.0
最初的解決方案是在 res 目錄下編寫一個批處理文件:update.bat
,然后每次更新了 xml 文件后,
手動執行這個批處理文件。由於 xml 文件寫好后基本上不需要怎么修改,所以寫完代碼后在自己的環境中
運行起來沒有問題,不會出現程序運行時找不到 xml 文件的情況。
該批處理文件的內容如下:
mkdir ../../build-DEMO-Desktop_Qt_5_6_3_MSVC2015_64bit-Debug/debug/res
copy /y *.xml ../../build-DEMO-Desktop_Qt_5_6_3_MSVC2015_64bit-Debug/debug/res
mkdir ../../build-DEMO-Desktop_Qt_5_6_3_MSVC2015_64bit-Release/realse/res
copy /y *.xml ../../build-DEMO-Desktop_Qt_5_6_3_MSVC2015_64bit-Release/realse/res
思路是這樣的:
- 首先創建相應的目錄
- 將項目下面所有的 xml 文件復制到指定的目錄
在我的電腦環境中運行正常,但是問題很明顯,如果我換了編譯器,或者更換了構建目錄,我就得去批處理文件中添加兩行代碼,
而且還得把所有的文件復制到好幾個目錄下面。非常麻煩!
我在把源代碼拷給別人的時候,由於別人用的編譯器是 MinGW 編譯器,結果我的批處理文件沒有辦法直接使用。
所以在別人的編譯環境中生成的程序由於缺少資源文件,程序運行不正常。
那么我需要找到一種方法,得到 Qt 構建項目的目錄,並將需要的資源文件復制到相應目錄下。
解決方案 2.0
用 QtCreator
編譯項目時,IDE 會調用 qmake
根據項目 .pro
文件自動生成一個 Makefile
。
然后 IDE 就能根據這個 Makefile 對項目進行編譯處理。
經過一番搜索,在 stackoverflow 上搜到了可用的內容,然后我在 .pro
文件末尾添加了這些內容(修改了部分內容):
DESTDIR = $$OUT_PWD
defineTest(resToDestdir) {
files = $$1
for(FILE, files) {
DDIR = $$RAWDDIR
# Replace slashes in paths with backslashes for Windows
win32:DDIR ~= s,/,\\,g
win32:FILE ~= s,/,\\,g
QMAKE_POST_LINK += $$QMAKE_COPY $$quote($$FILE) $$quote($$DDIR) $$escape_expand(\\n\\t)
}
export(QMAKE_POST_LINK)
return(true)
}
# update xml files into proper folder
XML_FILES = $$PWD/res/*.xml
## note, if there is something wrong when compiling, annote following sentence
resToDestdir($$XML_FILES)
Qt 官方文檔 里對這些用到的變量都有說明。
這里簡單說一下。
DESTDIR
: 指定放置 target 文件的位置QMAKE_POST_LINK
: 在鏈接 TARGET 后指定要執行的命令export(variablename)
: 將 variablename 在函數的局域上下文中的值導出到全局上下文defineTest
:定義一個測試函數,以便復用代碼(盡管這里並沒有復用代碼:))
經過幾次嘗試后,發現要運行 .pro
文件中的這段代碼需要每次都重新編譯。因為直接在 QtCreator 中點擊編譯會先比較
文件,如果沒有更改源文件(或者說被依賴文件沒有更新,那么編譯就可以跳過)。而且每次更改了 .pro
文件都需要先運行
一次 qmake,確保 Makefile 是最新的。
解決方案 2.9
我對代碼進行了修改,添加了創建目錄的語句,並用 exists
函數判斷是否存在目錄,如果不存在就創建。
在 Windows 上,目錄分隔符是反斜杠 "" ,因此直接利用 win32:DDIR
進行判斷是否存在相應目錄, exists
函數返回的都是 false。
官方文檔 對此已經有說明了:
Note: "/" should be used as a directory separator, regardless of the platform in use.
即:
注意: 目錄分隔符應該用 “/” ,不論你用的是什么平台。
所以判斷目錄是否已經存在需要用最開始的斜杠分隔符,但是創建目錄的命令還是要用反斜杠(調用的是 Windows 的命令行)。
最終修改后的 qmake 代碼:
#-----------------------------------
# @author BriFuture
# @details this is used for automated build
#
# Copies the given files to the destination directory
#-----------------------------------
## set destdir
DESTDIR = $$OUT_PWD
defineTest(resToDestdir) {
files = $$1
ddir = $$2
for(FILE, files) {
# Replace slashes in paths with backslashes for Windows
DDIR = $$DESTDIR/$$ddir
## if res folder is created, then no need to exec mkdir
## note, whatever the platform you are using( Windows, Linux, Mac),
## the directory seperator must be slash "/"
!exists($$DDIR) {
log($$DDIR not exists)
mkpath($$DDIR)
}
win32:DDIR ~= s,/,\\,g
win32:FILE ~= s,/,\\,g
QMAKE_POST_LINK += $$QMAKE_COPY $$quote($$FILE) $$quote($$DDIR) $$escape_expand(\\n\\t)
# QMAKE_POST_LINK += echo $$FILE $$escape_expand(\\n\\t)
# message(FILE $$FILE)
}
# QMAKE_POST_LINK += echo $$quote($$PWD == $$DESTDIR)
# message(LINK: $$QMAKE_POST_LINK)
export(QMAKE_POST_LINK)
return(true)
}
# update xml files into proper folder
XML_FILES = $$PWD/res/*.xml
## note, if there is something wrong when compiling, annote following sentence
resToDestdir($$XML_FILES, res)
總結
最終這個代碼可以完美的復制需要的資源文件到指定的目錄下,但是還有一些小 bug,偶爾編譯時會報錯:
mkdir ..\debug\res 目錄已經存在
,遇到這種情況,重新執行一遍 qmake
,再重新編譯就好了。
雖然並不完美,但再深入就太累了。
當然還有一種思路,在 qmake 中執行已經寫好的 bat 批處理文件,然后將構建目錄作為參數傳遞給批處理文件。
時間有限,沒有實現。