運(yùn)行 Qt 項(xiàng)目時(shí),Qt Creator 首先通過 ssh 將項(xiàng)目部署到遠(yuǎn)程嵌入式 Linux 設(shè)備上,然后在遠(yuǎn)程設(shè)備上運(yùn)行可執(zhí)行文件。這個(gè)特性幾乎可以即時(shí)反饋 Qt 應(yīng)用程序如何在嵌入式設(shè)備上工作。
部署工作可以很好地使用 qmake 的 INSTALLS 變量 來實(shí)現(xiàn)。不過 CMake 不具備類似 qmake 的 INSTALLS
變量的功能。幸運(yùn)的是,Qt 提供了一個(gè)解決方案。下面將通過一個(gè)示例 CMakeLists.txt
文件來演示這個(gè)解決方案。
我們希望在嵌入式設(shè)備的 /opt/mycompany/bin
中安裝 Qt 項(xiàng)目的可執(zhí)行文件,在 /opt/mycompany/lib
中存放需要的三方庫,而 /opt/mycompany/cad
是包含 3D CAD 文件的目錄。CMakeLists.txt 的安裝部分(位于 ${CMAKE_SOURCE_DIR}/src
中)與此類似。
set(CMAKE_INSTALL_PREFIX "/opt/mycompany")
install(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
)
install(FILES ./lib/other/libMagic.so
DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
)
install(DIRECTORY ./cad/
DESTINATION ${CMAKE_INSTALL_PREFIX}/cad
)
上述設(shè)置本地部署到 Linux PC 與 make install
都工作良好。但是,嵌入式 Linux 設(shè)備的遠(yuǎn)程部署不能正常工作。
Qt Creator 在 項(xiàng)目>運(yùn)行設(shè)置>部署>Files to deploy
設(shè)置中顯示了從本地文件路徑到遠(yuǎn)程文件路徑的映射。對(duì)于上面的安裝功能,Qt Creator 將只顯示一個(gè)條目。可執(zhí)行應(yīng)用程序從其構(gòu)建位置 ${CMAKE_BINARY_DIR}/src/app
映射到 src
。這顯然是不對(duì)的。
解決這個(gè)問題的思想 是讓 CMake 將從本地文件路徑到遠(yuǎn)程文件路徑的映射寫入一個(gè)被命名為 QtCreatorDeployment.txt
的文件中。正確定義的 QtCreatorDeployment.txt
將包含以下映射。
/opt/mycompany
src/../../build-app-Remote_Qt_5_12_1-Release/src/app:bin
src/lib/other/libMagic.so:lib
src/cad/machine1/part1.step:cad/machine1
src/cad/machine1/part3.step:cad/machine1
src/cad/machine2/part6.step:cad/machine2
...
第一行給出的是復(fù)制文件到遠(yuǎn)程計(jì)算機(jī)上的(絕對(duì))路徑前綴。第二行和后面所有行的格式是。
relative/local/file:relative/remote/dir
本地目錄相對(duì)于 ${CMAKE_SOURCE_DIR}
目錄,而遠(yuǎn)程目錄相對(duì)于第一行中給出的安裝前綴 /opt/mycompany
目錄。因此,上面這一行在概念上等同于下面的遠(yuǎn)程復(fù)制命令:
scp ${CMAKE_SOURCE_DIR}/relative/local/file \
developer@192.168.100.100:/opt/mycompany/relative/remote/dir/file
與往常一樣,Qt 讓我們的遠(yuǎn)程部署工作變得輕松了一些,它提供了兩個(gè) CMake 宏來向 QtCreatorDeployment.txt
添加文件和目錄樹。第一個(gè)宏 add_deployment_file
如下所示:
macro(add_deployment_file SRC DEST)
file(RELATIVE_PATH path ${CMAKE_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR})
file(APPEND "${CMAKE_SOURCE_DIR}/QtCreatorDeployment.txt"
"${path}/${SRC}:${DEST}\n")
endmacro()
第一個(gè)文件命令計(jì)算從源文件的根目錄 ${CMAKE_SOURCE_DIR}
到當(dāng)前處理的源目錄 ${CMAKE_CURRENT_SOURCE_DIR}
的相對(duì)路徑。第二個(gè)文件命令將行“${path}/${SRC}:${DEST}\n
”追加到文件 QtCreatorDeployment.txt
。
示例,調(diào)用:
add_deployment_file("lib/other/libMagic.so" "lib")
被解析的結(jié)果類似于:
SRC = "lib/other/libMagic.so", "DEST = lib"
path = "src"
Append: "src/lib/other/libMagic.so:lib\n"
宏將映射文件 QtCreatorDeployment.txt
寫到目錄 ${CMAKE_SOURCE_DIR}
,該目錄是源樹的根目錄。但是,在構(gòu)建過程中生成的所有文件都應(yīng)該寫入到構(gòu)建樹中。幸運(yùn)的是,Qt Creator 不僅在 ${CMAKE_SOURCE_DIR}
目錄(源碼根目錄,例如:/home/toby/test/TestEmptyCMakeProject
)中查找映射文件,而且在構(gòu)建樹的根目錄(構(gòu)建目錄,例如:/home/toby/test/build-TestEmptyCMakeProject-target_qt5_12_5_syberos_5_0-Debug
) {CMAKE_BINARY_DIR}
中查找映射文件。因此,我們也可以將第一個(gè)宏中的第二條 file 命令更改為:
file(APPEND "${CMAKE_BINARY_DIR}/QtCreatorDeployment.txt"
"${path}/${SRC}:${DEST}\n")
第二個(gè)宏 add_deployment_directory
如下所示:
macro(add_deployment_directory SRC DEST)
file(GLOB_RECURSE files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
"${SRC}/*")
foreach(filename ${files})
get_filename_component(path ${filename} PATH)
add_deployment_file("${filename}" "${DEST}/${path}")
endforeach(filename)
endmacro()
file 命令行遍歷目錄 ${CMAKE_CURRENT_SOURCE_DIR}
處的樹,并在 files
變量中存儲(chǔ)與通配符表達(dá)式 ${SRC}/*
匹配的所有文件。
foreach 循環(huán)遍歷 file 命令找到的所有文件。對(duì)于每個(gè)文件,它使用相對(duì)本地文件路徑和遠(yuǎn)程目錄路徑 ${DEST}/${path}
調(diào)用 add_deployment_file 宏,其中 ${path}
是本地文件的相對(duì)目錄路徑。
示例,調(diào)用:
add_deployment_directory("cad" ".")
的解析結(jié)果示例如下:
SRC = "cad", DEST = "."
files = all file paths relative to "${CMAKE_CURRENT_SOURCE_DIR}" and
matching the pattern "cad/*"
files = "cad/machine1/part1.step" "cad/machine1/part3.step"
"cad/machine2/part6.step"
foreach loop:
add_deployment_file("cad/machine1/part1.step" "./cad/machine1")
Append: "src/cad/machine1/part1.step:cad/machine1\n"
add_deployment_file("cad/machine1/part3.step" "./cad/machine1")
Append: "src/cad/machine1/part3.step:cad/machine1\n"
add_deployment_file("cad/machine2/part6.step" "./cad/machine2")
Append: "src/cad/machine2/part6.step:cad/machine2\n"
現(xiàn)在,我們可以將所有部分放在一起并重寫 CMakeLists.txt 文件的安裝部分,如下所示:
macro(add_deployment_file SRC DEST)
file(RELATIVE_PATH path ${CMAKE_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR})
file(APPEND "${CMAKE_BINARY_DIR}/QtCreatorDeployment.txt"
"${path}/${SRC}:${DEST}\n")
endmacro()
macro(add_deployment_directory SRC DEST)
file(GLOB_RECURSE files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
"${SRC}/*")
foreach(filename ${files})
get_filename_component(path ${filename} PATH)
add_deployment_file("${filename}" "${DEST}/${path}")
endforeach(filename)
endmacro()
set(CMAKE_INSTALL_PREFIX "/opt/mycompany")
if(DEPLOYED_REMOTELY)
# Write base installation path as first line.
file(WRITE "${CMAKE_BINARY_DIR}/QtCreatorDeployment.txt"
"${CMAKE_INSTALL_PREFIX}\n")
# Append mapping for executable.
file(RELATIVE_PATH relative_exe_path
"${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}")
add_deployment_file(relative_exe_path bin)
# Append mapping for single library file.
add_deployment_file("lib/other/libMagic.so" "lib")
# Append all 3D CAD files from local directory "cad".
add_deployment_directory("cad" ".")
else()
# The original install commands go here for local deployment.
...
endif()
上述代碼中 DEPLOYED_REMOTELY 是一個(gè) CMake 選項(xiàng),其定義為:
option(DEPLOYED_REMOTELY "Turn on for remote deployment" OFF)
Qt Creator 在 項(xiàng)目>構(gòu)建設(shè)置> CMake
設(shè)置頁面中將此選項(xiàng)顯示為復(fù)選框。如果希望遠(yuǎn)程部署應(yīng)用程序,只需勾選復(fù)選框并按下 Apply Configuration Changes
按鈕。
當(dāng)我們使用 Ctrl+R
運(yùn)行應(yīng)用程序時(shí),Qt Creator 將根據(jù) QtCreatorDeployment.txt
中的映射將文件復(fù)制到嵌入式設(shè)備,并在嵌入式設(shè)備上運(yùn)行應(yīng)用程序。
本文非原創(chuàng),原文鏈接:《
Deploying Qt Projects to Embedded Devices with CMake》