Qt QML 杂记

使用 CMake 将 Qt 项目部署到嵌入式设备

2020-06-22  本文已影响0人  赵者也

运行 Qt 项目时,Qt Creator 首先通过 ssh 将项目部署到远程嵌入式 Linux 设备上,然后在远程设备上运行可执行文件。这个特性几乎可以即时反馈 Qt 应用程序如何在嵌入式设备上工作。

部署工作可以很好地使用 qmake 的 INSTALLS 变量 来实现。不过 CMake 不具备类似 qmake 的 INSTALLS 变量的功能。幸运的是,Qt 提供了一个解决方案。下面将通过一个示例 CMakeLists.txt 文件来演示这个解决方案。

我们希望在嵌入式设备的 /opt/mycompany/bin 中安装 Qt 项目的可执行文件,在 /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
)

上述设置本地部署到 Linux PC 与 make install 都工作良好。但是,嵌入式 Linux 设备的远程部署不能正常工作。

Qt Creator 在 项目>运行设置>部署>Files to deploy 设置中显示了从本地文件路径到远程文件路径的映射。对于上面的安装功能,Qt Creator 将只显示一个条目。可执行应用程序从其构建位置 ${CMAKE_BINARY_DIR}/src/app 映射到 src。这显然是不对的。

解决这个问题的思想 是让 CMake 将从本地文件路径到远程文件路径的映射写入一个被命名为 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
...

第一行给出的是复制文件到远程计算机上的(绝对)路径前缀。第二行和后面所有行的格式是。

relative/local/file:relative/remote/dir

本地目录相对于 ${CMAKE_SOURCE_DIR} 目录,而远程目录相对于第一行中给出的安装前缀 /opt/mycompany 目录。因此,上面这一行在概念上等同于下面的远程复制命令:

scp ${CMAKE_SOURCE_DIR}/relative/local/file \
      developer@192.168.100.100:/opt/mycompany/relative/remote/dir/file

与往常一样,Qt 让我们的远程部署工作变得轻松了一些,它提供了两个 CMake 宏来向 QtCreatorDeployment.txt 添加文件和目录树。第一个宏 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()

第一个文件命令计算从源文件的根目录 ${CMAKE_SOURCE_DIR} 到当前处理的源目录 ${CMAKE_CURRENT_SOURCE_DIR} 的相对路径。第二个文件命令将行“${path}/${SRC}:${DEST}\n”追加到文件 QtCreatorDeployment.txt

示例,调用:

add_deployment_file("lib/other/libMagic.so" "lib")

被解析的结果类似于:

SRC = "lib/other/libMagic.so", "DEST = lib"
path = "src"
Append: "src/lib/other/libMagic.so:lib\n"

宏将映射文件 QtCreatorDeployment.txt 写到目录 ${CMAKE_SOURCE_DIR},该目录是源树的根目录。但是,在构建过程中生成的所有文件都应该写入到构建树中。幸运的是,Qt Creator 不仅在 ${CMAKE_SOURCE_DIR} 目录(源码根目录,例如:/home/toby/test/TestEmptyCMakeProject)中查找映射文件,而且在构建树的根目录(构建目录,例如:/home/toby/test/build-TestEmptyCMakeProject-target_qt5_12_5_syberos_5_0-Debug{CMAKE_BINARY_DIR} 中查找映射文件。因此,我们也可以将第一个宏中的第二条 file 命令更改为:

file(APPEND "${CMAKE_BINARY_DIR}/QtCreatorDeployment.txt"
     "${path}/${SRC}:${DEST}\n")

第二个宏 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 变量中存储与通配符表达式 ${SRC}/* 匹配的所有文件。

foreach 循环遍历 file 命令找到的所有文件。对于每个文件,它使用相对本地文件路径和远程目录路径 ${DEST}/${path} 调用 add_deployment_file 宏,其中 ${path} 是本地文件的相对目录路径。

示例,调用:

add_deployment_directory("cad" ".")

的解析结果示例如下:

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"

现在,我们可以将所有部分放在一起并重写 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 是一个 CMake 选项,其定义为:

option(DEPLOYED_REMOTELY "Turn on for remote deployment" OFF)

Qt Creator 在 项目>构建设置> CMake 设置页面中将此选项显示为复选框。如果希望远程部署应用程序,只需勾选复选框并按下 Apply Configuration Changes 按钮。

当我们使用 Ctrl+R 运行应用程序时,Qt Creator 将根据 QtCreatorDeployment.txt 中的映射将文件复制到嵌入式设备,并在嵌入式设备上运行应用程序。

本文非原创,原文链接:《
Deploying Qt Projects to Embedded Devices with CMake

上一篇下一篇

猜你喜欢

热点阅读