因为项目需要,原运行在windows下的一个c++程序,要移植到linux(ubuntu)去。该c++程序在windows下,IDE是visual studio 2022,使用cmake作为构建工具。移植到ubuntu后,IDE是vscode,仍然使用cmake作为构建工具。
移植过程,由于该程序使用了一些windows的库,代码需要修改以外,cmake的脚本CMakeLists.txt也需要做一定调整。
一、CMakeLists.txt的调整
1、windows下的CMakeLists.txt
该 CMakeLists.txt 用于配置 UnderwtConn 项目构建:
1)要求 CMake 最低版本 3.8
2)配置 Vcpkg 工具链。
3)处理 MSVC 调试信息策略。
4)设置 VISA 库和自定义库路径。
5)把多个源文件编译为 UnderwtConn 可执行文件。
6)查找 Drogon、hiredis 等依赖库并链接。
7)若 CMake 版本够高,设 C++ 标准为 20。
8)构建后将 jbh 目录文件复制到可执行文件目录。
cpp
cmake_minimum_required (VERSION 3.8)
set(VCPKG_ROOT "E:/vcpkg" CACHE PATH "")
set(CMAKE_TOOLCHAIN_FILE "${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake")
if (POLICY CMP0141)
cmake_policy(SET CMP0141 NEW)
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
endif()
project ("UnderwtConn")
set(VISA_PATH "C:\\Program Files (x86)\\IVI Foundation\\VISA\\WinNT")
set(VISA_LIB_PATH ${VISA_PATH}\\lib\\msc)
include_directories("${VISA_PATH}\\include")
link_directories(${VISA_LIB_PATH})
link_directories(${CMAKE_SOURCE_DIR}/jbh)
# 将源代码添加到此项目的可执行文件
add_executable (UnderwtConn "UnderWtConn.cpp"
"util/redis_util.cpp" "util/conf_util.cpp" "controller/DeviceController.cpp"
"util/path_util.cpp" "util/time_util.cpp" "util/json_util.cpp" "util/hex_util.cpp"
"util/mysqlconn.cpp" "util/file_util.cpp" "entity/ConnBoxSIIM.cpp"
"util/cbx_operate.cpp" "entity/Gateway.cpp" "util/general.cpp"
"entity/LIMClass.cpp" "entity/HangYuPower.h" "entity/HangYuPower.cpp" "entity/StepAttr.h" "config/ConfigManager.h" "config/ConfigManager.cpp")
find_package(Drogon CONFIG REQUIRED)
find_package(hiredis CONFIG REQUIRED)
find_package(Boost REQUIRED COMPONENTS system thread)
find_package(unofficial-mysql-connector-cpp CONFIG REQUIRED)
target_link_libraries(UnderwtConn
PUBLIC visa32.lib
Boost::system Boost::thread
Drogon::Drogon
AnaGateDllCan
hiredis::hiredis
unofficial::mysql-connector-cpp::connector
)
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET UnderwtConn PROPERTY CXX_STANDARD 20)
endif()
file(GLOB DLL_FILES "${CMAKE_SOURCE_DIR}/jbh/*")
add_custom_command(TARGET UnderwtConn POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${DLL_FILES}
$<TARGET_FILE_DIR:UnderwtConn>)
2、linux下的CMakeLists.txt
1)去掉vcpkg
移植到linux之后,不需要vcpkg了。Vcpkg 是微软开发的一款开源的跨平台 C++ 库管理器,它能帮助开发者在不同的操作系统上更便捷地管理和使用第三方库。windows下,我们那个vcpkg非常庞大,有10G之多。我们项目肯定是用不了这么多,可能是之前开发的同事从别的地方拷贝过来的。
这个库不是必须的。就算是在windows下,我们项目也没有用到里面多少库,所以移植到linux之后,就索性去掉了。
2)不再链接外部库
第二个就是把"8)构建后将 jbh 目录文件复制到可执行文件目录"去掉了。原先用的是第三方提供的库,现在改为自己实现,所以就无须再将动态库拷贝到发布目录。
windows下:
linux:
将实现直接写在了头文件里,所以不需要再链接外部库了。
3)将配置文件拷贝到发布目录
项目有个配置文件,改进了原先的代码,发布时将配置文件拷贝到发布目录。
cpp
# 将配置文件复制到构建目录
add_custom_command(TARGET UnderwtConn POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/config/sys.conf
${CMAKE_CURRENT_BINARY_DIR}/config/sys.conf
COMMENT "Copying config file to build directory"
VERBATIM)

移植后的CMakeLists.txt
cpp
cmake_minimum_required (VERSION 3.28)
cmake_policy(SET CMP0153 OLD)
project ("UnderwtConn")
# 将源代码添加到此项目的可执行文件
add_executable (UnderwtConn
"UnderWtConn.cpp"
"controller/DeviceController.cpp"
"util/conf_util.cpp"
"util/redis_util.cpp"
"util/path_util.cpp"
"util/time_util.cpp"
"util/json_util.cpp"
"util/hex_util.cpp"
"util/mysqlconn.cpp"
"util/file_util.cpp"
"util/general.cpp"
"util/cbx_operate.cpp"
"entity/ConnBoxSIIM.cpp"
"entity/Gateway.cpp"
"entity/LIMClass.cpp"
"entity/HangYuPower.h"
"entity/HangYuPower.cpp"
"entity/StepAttr.h"
"config/ConfigManager.h"
"config/ConfigManager.cpp")
set(VISA_PATH "/usr")
set(VISA_LIB_PATH "/usr/lib/x86_64-linux-gnu")
include_directories("${VISA_PATH}/include")
link_directories(${VISA_LIB_PATH})
find_package(Drogon CONFIG REQUIRED)
find_package(Boost REQUIRED COMPONENTS system thread)
#告诉 CMake 尝试找到版本至少为 1.2.0 的 Hiredis 库,并且要求必须找到这个包才能继续执行后续的构建过程
#CONFIG:指示 CMake 使用"配置"模式来查找包。这意味着 CMake 期望找到由该软件包提供的配置文件
#REQUIRED:表示这是一个必需的依赖项,如果找不到就抛出错误
find_package(Hiredis 1.2.0 CONFIG REQUIRED) # 指定 hiredis 版本为 1.2.0
# 如果不存在 hiredis::hiredis 目标,则创建一个
if(NOT TARGET hiredis::hiredis)
add_library(hiredis::hiredis UNKNOWN IMPORTED)
set_target_properties(hiredis::hiredis PROPERTIES
IMPORTED_LOCATION "${HIREDIS_LIBRARIES}"
INTERFACE_INCLUDE_DIRECTORIES "${HIREDIS_INCLUDE_DIRS}"
)
endif()
# 指定额外的头文件搜索路径。
# 具体来说,它告诉编译器在编译过程中除了标准的头文件目录外,还需要检查 /usr/include/mysql 目录下的头文件
# 类似于 target_include_directories,但后者更精细。目前推荐使用 target_include_directories
include_directories(/usr/include/mysql)
# 查找 MySQL Connector/C++ 头文件目录
find_path(MYSQL_CONNECTOR_CPP_INCLUDE_DIR
NAMES mysqlx/xdevapi.h
PATHS /usr/include /usr/local/include /usr/include/mysql-cppconn)
find_library(MYSQL_CONNECTOR_CPP_LIBRARY
NAMES mysqlcppconnx # 直接指定具体版本
PATHS /usr/lib/x86_64-linux-gnu)
if(NOT MYSQL_CONNECTOR_CPP_INCLUDE_DIR OR NOT MYSQL_CONNECTOR_CPP_LIBRARY)
message(FATAL_ERROR "MySQL Connector/C++ not found! Please install libmysqlcppconn-dev.")
endif()
#类似于include_directories
target_include_directories(${PROJECT_NAME} PRIVATE ${MYSQL_CONNECTOR_CPP_INCLUDE_DIR})
# 编译过程分为4个阶段,预处理、编译、汇编、链接。
# 所谓链接,是指将多个目标文件以及所需的库文件合并成最终的可执行文件或库
target_link_libraries(UnderwtConn
PUBLIC visa
Boost::system Boost::thread
Drogon::Drogon
hiredis::hiredis
${MYSQL_CONNECTOR_CPP_LIBRARY}
)
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET UnderwtConn PROPERTY CXX_STANDARD 20)
endif()
# 将配置文件复制到构建目录
add_custom_command(TARGET UnderwtConn POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/config/sys.conf
${CMAKE_CURRENT_BINARY_DIR}/config/sys.conf
COMMENT "Copying config file to build directory"
VERBATIM)
二、CMakeLists.txt的一些理解
对于之前没怎么搞过C++的人来说,刚开始移植时真的是无所适从,觉得所有的地方都是那么的神秘。调试得多了,慢慢的也看出一些门道。记录如下:
1、find_package
查找并加载外部项目或库的配置信息。当需要使用一个已经使用 CMake 进行构建和安装的第三方库时,使用 find_package 是最方便的。许多知名的开源库(如 Boost、Drogon 等)都提供了自己的 CMake 配置文件,通过 find_package 可以轻松地将这些库集成到项目中。
cpp
#告诉 CMake 尝试找到版本至少为 1.2.0 的 Hiredis 库,并且要求必须找到这个包才能继续执行后续的构建过程
#CONFIG:指示 CMake 使用"配置"模式来查找包。这意味着 CMake 期望找到由该软件包提供的配置文件
#REQUIRED:表示这是一个必需的依赖项,如果找不到就抛出错误
find_package(Hiredis 1.2.0 CONFIG REQUIRED) # 指定 hiredis 版本为 1.2.0
2、find_path
于查找指定文件所在的目录。它会在指定的搜索路径中查找具有指定名称的文件,并将找到该文件的目录路径存储在一个变量中。当只需要知道某个头文件所在的目录,而不需要加载整个库的配置信息时,可以使用 find_path。
cpp
find_path(MYSQL_CONNECTOR_CPP_INCLUDE_DIR
NAMES mysqlx/xdevapi.h
PATHS /usr/include /usr/local/include /usr/include/mysql-cppconn)
3、find_library
用于查找指定名称的库文件。它会在指定的搜索路径中查找具有指定名称的库文件,并将找到的库文件的完整路径存储在一个变量中。当只需要知道某个库文件(上面的是头文件)的路径,而不需要加载整个库的配置信息时,可以使用 find_library。
cpp
ind_library(MYSQL_CONNECTOR_CPP_LIBRARY
NAMES mysqlcppconnx # 直接指定具体版本
PATHS /usr/lib/x86_64-linux-gnu)
4、链接
编译过程分为4个阶段,预处理、编译、汇编、链接。
所谓链接,是指将多个目标文件以及所需的库文件合并成最终的可执行文件或库。
1)include_directories 。
include_directories 指令用于向 CMake 项目全局添加头文件的搜索路径。在编译过程中,编译器会在这些指定的路径中查找 #include 指令引用的头文件。当项目中多个目标(如可执行文件、库)都需要使用相同的头文件路径时,可以使用 include_directories 进行全局设置。例如,项目依赖的第三方库的头文件路径通常是固定的,就可以使用该指令添加。
2)target_include_directories
类似于include_directories,但比include_directories精细。
3)target_link_libraries
用于将指定的库文件链接到目标(如可执行文件或库)上。在链接阶段,链接器会将这些库文件与目标文件合并,生成最终的可执行文件或库。当项目需要使用外部库来实现某些功能时,就需要使用 target_link_libraries 将这些库链接到目标上。例如,项目使用了 SQLite 数据库,就需要将 SQLite 库链接到可执行文件中。
cpp
#类似于include_directories
target_include_directories(${PROJECT_NAME} PRIVATE ${MYSQL_CONNECTOR_CPP_INCLUDE_DIR})
# 编译过程分为4个阶段,预处理、编译、汇编、链接。
# 所谓链接,是指将多个目标文件以及所需的库文件合并成最终的可执行文件或库
target_link_libraries(UnderwtConn
PUBLIC visa
Boost::system Boost::thread
Drogon::Drogon
hiredis::hiredis
${MYSQL_CONNECTOR_CPP_LIBRARY}
)
5、自定义命令(add_custom_command)
在 CMake 里,add_custom_command 是一个很实用的指令,能在构建过程里添加自定义的命令。
以本文中的复制配置文件命令为例:
cpp
# 将配置文件复制到构建目录
add_custom_command(TARGET UnderwtConn POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/config/sys.conf
${CMAKE_CURRENT_BINARY_DIR}/config/sys.conf
COMMENT "Copying config file to build directory"
VERBATIM)
TARGET UnderwtConn
指定该自定义命令所关联的目标为 UnderwtConn。UnderwtConn 通常是通过 add_executable 或者 add_library 命令创建的可执行文件或库目标。这意味着该自定义命令会和 UnderwtConn 这个目标的构建过程相关联。
POST_BUILD
表明自定义命令的执行时机,即该命令会在 UnderwtConn 目标构建完成之后执行。与之对应的还有 PRE_BUILD(在构建开始前执行)和 PRE_LINK(在链接操作前执行)。
COMMAND ${CMAKE_COMMAND} -E copy_if_different
add_custom_command 里用于指定要执行操作的关键部分,仅在源文件和目标文件内容不同时,把源文件复制到目标位置。
VERBATIM
确保命令参数被原封不动地传递给底层的构建系统,避免在传递过程中出现 shell 转义等问题,保证命令的正确执行。
三、小结
移植过程中,在CMakeLists.txt里耗费的时间是最多的。其中又以mysql最为麻烦。由于没有什么基础,反反复复地问AI,无脑遵照其指示,安装了许多包,光mysql就有多个版本。但看起来,多个版本并不影响mysql的使用。