QGC4.3.0源码学习 - 阅读顶层CMakeLists.txt(4)

QGroundControl Options

关于stable/daily选项

option(QGC_STABLE_BUILD "Stable build option" OFF)
if(NOT QGC_STABLE_BUILD)
    add_compile_definitions(DAILY_BUILD)
endif()

 此版本顶层CMakeLists.txt默认QGC_STABLE_BUILD选项为OFF,如此将会向编译器添加一个定义符号DAILY_BUILD,使用vs code搜索DAILY_BUILD发现有几处源文件使用到了DAILY_BUILD作为一个开关,以QGCApplication.cc为例:

#ifdef DAILY_BUILD
        // This gives daily builds their own separate settings space. Allowing you to use daily and stable builds
        // side by side without daily screwing up your stable settings.
        applicationName = QStringLiteral("%1 Daily").arg(QGC_APPLICATION_NAME);
#else
        applicationName = QGC_APPLICATION_NAME;
#endif

如果QGC_STABLE_BUILD是ON,那么applicationName将设置为QGroundControl,否则applicationName为 QGroundControl Daily

关于Test选项

cmake_dependent_option(QGC_BUILD_TESTING "Enable testing" ON "CMAKE_BUILD_TYPE STREQUAL Debug" OFF)
if(QGC_BUILD_TESTING)
    add_compile_definitions(UNITTEST_BUILD) # TODO: QGC_UNITTEST_BUILD
endif()

提供一个QGC_BUILD_TESTING选项,此选项在选择构建类型为Debug时设为ON,否则为OFF。含义是只在Debug时提供Unit Test。

如果QGC_BUILD_TESTING为ON,向编译器添加UNITTEST_BUILD宏定义,使用vs code搜索UNITTEST_BUILD,很多处源文件使用到此宏定义,以main.cc为例:

#ifdef UNITTEST_BUILD
    if (runUnitTests) {
        for (int i=0; i < (stressUnitTests ? 20 : 1); i++) {
            if (!app->_initForUnitTests()) {
                return -1;
            }

            // Run the test
            int failures = UnitTest::run(unitTestOptions);
            if (failures == 0) {
                qDebug() << "ALL TESTS PASSED";
                exitCode = 0;
            } else {
                qDebug() << failures << " TESTS FAILED!";
                exitCode = -failures;
                break;
            }
        }
    } else
#endif

此处在app->exec()之前进行单元测试,对每个测试结果打印输出。

关于QML debug选项

cmake_dependent_option(QGC_DEBUG_QML "Build QGroundControl with QML debugging/profiling support." OFF "CMAKE_BUILD_TYPE STREQUAL Debug" OFF)
if(QGC_DEBUG_QML)
    message(STATUS "To enable the QML debugger/profiler, run with: '-qmljsdebugger=port:1234'")
    add_compile_definitions(QT_QML_DEBUG)
endif()

 提供QGC_DEBUG_QML选项,用于QML在debug/profile时的支持,CMakeLists.txt这里无论构建方式是否为Debug都设置为OFF,Qt的QML debug参考官方文档:Debugging QML Applications | Qt 6.7

当Qt quick程序使用CMake构建方式,可以向编译器添加以下宏定义,实现QML debug:

QT_DECLARATIVE_DEBUG
QT_QML_DEBUG

 QGroundControl Resources

设置QGC_RESOURCES变量,代表QGC使用到的资源文件:

set(QGC_RESOURCES
    ${CMAKE_SOURCE_DIR}/qgcimages.qrc
    ${CMAKE_SOURCE_DIR}/qgcresources.qrc
    ${CMAKE_SOURCE_DIR}/qgroundcontrol.qrc
    ${CMAKE_SOURCE_DIR}/resources/InstrumentValueIcons/InstrumentValueIcons.qrc
    ${CMAKE_SOURCE_DIR}/src/FirmwarePlugin/APM/APMResources.qrc
    ${CMAKE_SOURCE_DIR}/src/FirmwarePlugin/PX4/PX4Resources.qrc
)

 关于UTMSP

if(CONFIG_UTM_ADAPTER)
    list(APPEND QGC_RESOURCES ${CMAKE_SOURCE_DIR}/src/UTMSP/utmsp.qrc)
else()
    list(APPEND QGC_RESOURCES ${CMAKE_SOURCE_DIR}/src/UTMSP/dummy/utmsp_dummy.qrc)
endif()

 

这里UTM是指无人机交通管理Unmanned Traffic Management,UTMSP是指Unmanned Traffic Management Service Providers。

在src/UTMSP/CMakeLists.txt中,定义QGC_UTM_ADAPTER选项:

option(QGC_UTM_ADAPTER "Enable UTM Adapter" OFF)

 如果QGC_BUILD_TESTING为ON,将向QGC_RESOURCES这个list变量添加UnitTest.qrc:

if(QGC_BUILD_TESTING)
    list(APPEND QGC_RESOURCES ${CMAKE_SOURCE_DIR}/test/UnitTest.qrc)
endif()

 QGroundControl Target

创建可执行目标

qt_add_executable(${PROJECT_NAME}
    src/main.cc
    ${QGC_RESOURCES}
)

qt_add_executable命令参考Qt官方文档:qt_add_executable | Qt Core 6.7.1

特别声明,此命令在除Android 以外的所有平台上,都将创建可执行目标。所有参数都将传递到标准 CMake add_executable() 命令,但MANUAL_FINALIZATION(qt_add_executable可选参数)除外。如果设置了MANUAL_FINALIZATION参数,则应该最后由qt_finalize_target确定target。

Qt6 LinguistTools翻译工具

if(Qt6LinguistTools_FOUND)
    file(GLOB TS_SOURCES RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/translations/qgc_*.ts)
    qt_add_translations(${PROJECT_NAME} TS_FILES ${TS_SOURCES}) # TODO: Update to new qt_add_translations form in Qt6.7
endif()

前面find_package(Qt6 REQUIRED COMPONENTS LinguistTools)

如果找到了Q6::LinguistTools,cmake将会产生一个BOOL变量Qt6LinguistTools_FOUND为TRUE,此时调用file(GLOB)命令,将translations目录下,所有符合qgc_开头,以.ts结尾的文件记录在TS_SOURCES变量中:

调用qt_add_translations命令,将这些.ts文件(xml格式),使用LinguistTools转换为.qm文件(二进制),最后应用程序将会使用installTranslator调用.qm文件,完成翻译工作。

关于qt_add_translations命令详细介绍,参考官方文档:qt_add_translations | Qt Linguist Manual

QGC这里有一个TODO标记,因为这是Qt6.2版本引入的的qt_add_translations命令,QGC4.3.0规定最低版本为Qt6.6.0,下一个版本将会使用Qt6.7定义新的qt_add_translations命令。

关于Qt国际化,.ts与.qm文件等内容,参考一篇知乎文章:

超越 C++ 的 Qt 翻译 - 知乎 (zhihu.com)

在src/QGCApplication.cc内有QGCApplication::setLanguage函数,其中部分代码如下:

if (_locale.name() != "en_US") {
        QLocale::setDefault(_locale);
        if(_qgcTranslatorQtLibs.load("qt_" + _locale.name(), QLibraryInfo::path(QLibraryInfo::TranslationsPath))) {
            _app->installTranslator(&_qgcTranslatorQtLibs);
        } else {
            qCWarning(LocalizationLog) << "Qt lib localization for" << _locale.name() << "is not present";
        }
        if(_qgcTranslatorSourceCode.load(_locale, QLatin1String("qgc_source_"), "", ":/i18n")) {
            _app->installTranslator(&_qgcTranslatorSourceCode);
        } else {
            qCWarning(LocalizationLog) << "Error loading source localization for" << _locale.name();
        }
        if(_qgcTranslatorJSON.load(_locale, QLatin1String("qgc_json_"), "", ":/i18n")) {
            _app->installTranslator(&_qgcTranslatorJSON);
        } else {
            qCWarning(LocalizationLog) << "Error loading json localization for" << _locale.name();
        }
    }

根据机器所在的地区,确定本地应该使用的语言,随后调用installTranslator函数加载.qm文件。

预编译头文件

target_precompile_headers(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src/pch.h)

对于很少更改的头文件,如Qt提供的库头文件、C++标准库头文件等,可以将其放在pch.h中进行预编译,更改源码后,保证编译速度不会像第一次那么慢。

关于target_precompile_headers命令,参考官方文档:

target_precompile_headers — CMake 3.29.2 Documentation

Qt资源

set_target_properties(${PROJECT_NAME} PROPERTIES QT_RESOURCE_PREFIX "/qgc")

 关于QT_RESOURCE_PREFIX属性,参考QT官方文档:QT_RESOURCE_PREFIX | Qt Core 6.7.1

当使用qt_add_resources命令时,没有指明PREFIX参数,且QT_RESOURCE_PREFIX已被设置,则将会在资源文件路径前,自动加上${QT_RESOURCE_PREFIX},使用vs code搜索qt_add_resources,发现只有:

qt_add_resources(Joystick "gamecontrollerdb.txt"
		    PREFIX "/db/mapping/joystick"
		    FILES ${sdl_db_SOURCE_DIR}/gamecontrollerdb.txt
	    )

 给出了PREFIX,似乎此set_target_properties并没有起到作用?

进行git submodule update

include(Git)命令将会调用cmake/Git.cmake模块,打开此模块有几行代码需要注意:

find_package(Git)

查找Git package,准备进行Git操作。

if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
    option(GIT_SUBMODULE "Check submodules during build" ON)
    if(GIT_SUBMODULE)
        message(STATUS "Submodule update")
        execute_process(
            COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
            WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
            RESULT_VARIABLE GIT_SUBMODULE_RESULT
            OUTPUT_VARIABLE GIT_SUBMODULE_OUTPUT
            ERROR_VARIABLE GIT_SUBMODULE_ERROR
            OUTPUT_STRIP_TRAILING_WHITESPACE
        )
        if(NOT GIT_SUBMODULE_RESULT EQUAL 0)
            cmake_print_variables(GIT_SUBMODULE_RESULT GIT_SUBMODULE_OUTPUT GIT_SUBMODULE_ERROR)
            message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMODULE_RESULT}, please checkout submodules")
        endif()
    endif()
endif()

以上主要功能是:如果查找到Git包,且在顶层CMakeLists.txt同级目录存在.git文件(.gitmodules也算),执行git submodules,将子模块update。如果以下命令失败,将会输出日志信息:

git submodule update --init --recursive 

 添加编译器定义

add_compile_definitions(
    QGC_APPLICATION_NAME="QGroundControl"
    QGC_ORG_NAME="QGroundControl.org"
    QGC_ORG_DOMAIN="org.qgroundcontrol"
    APP_VERSION_STR="${APP_VERSION_STR}"
)

主要作用是向编译器添加几个STRING类型的宏定义,在源码中均有用到,使用vs code搜索QGC_APPLICATION_NAME,在main.cc有:

QObject::tr("A second instance of %1 is already running. Please close the other instance and try again.").arg(QGC_APPLICATION_NAME)

 添加src子目录到构建树

add_subdirectory(src)

 src目录包含除test外几乎所有的源文件,此命令将src内源文件添加到构建树,src有CMakeLists.txt,通过add_subdirectory继续添加子目录到构建树:

add_subdirectory(ADSB)
add_subdirectory(AirLink)
add_subdirectory(AnalyzeView)
add_subdirectory(api)
add_subdirectory(Audio)
add_subdirectory(AutoPilotPlugins)
add_subdirectory(Camera)
add_subdirectory(comm)
add_subdirectory(Compression)
add_subdirectory(FactSystem)
add_subdirectory(FirmwarePlugin)
add_subdirectory(FlightDisplay)
add_subdirectory(FlightMap)
add_subdirectory(FollowMe)
add_subdirectory(Geo)
add_subdirectory(GPS)
add_subdirectory(Joystick)
add_subdirectory(MissionManager)
add_subdirectory(PlanView)
add_subdirectory(PositionManager)
add_subdirectory(QmlControls)
add_subdirectory(QtLocationPlugin)
add_subdirectory(Settings)
add_subdirectory(Terrain)
add_subdirectory(ui)
add_subdirectory(Utilities)
add_subdirectory(UTMSP)
add_subdirectory(Vehicle)
add_subdirectory(VehicleSetup)
add_subdirectory(VideoManager)
add_subdirectory(VideoReceiver)
add_subdirectory(Viewer3D)

 添加链接库

target_link_libraries(${PROJECT_NAME}
    PRIVATE
        Qt6::Core
        Qt6::Gui
        Qt6::Qml
        Qt6::Quick
        Qt6::QuickControls2
        Qt6::Svg # Used to import QSvgPlugin
        qgc
)

 target_link_libraries作用是向可执行文件/库文件指定链接库,详细内容参考cmake官方文档:target_link_libraries — CMake 3.29.2 Documentation

关于Qt6::Core中::的作用,官方文档有一段描述:

Items containing ::, such as Foo::Bar, are assumed to be IMPORTED or ALIAS library target names and will cause an error if no such target exists. See policy CMP0028.

通过阅读cmake对于CMP0028的介绍:CMP0028 — CMake 3.29.2 Documentation

使用::是用于命名 IMPORTED 目标和 ALIAS 目标的常见模式。

此处尝试对Qt链接库文件的流程理解,以Core为例:

第一步调用find_package
find_package(Qt6
    REQUIRED
COMPONENTS
Core …)

找到本机Qt安装目录下的.cmake文件并调用:

F:\QT\6.7.0\mingw_64\lib\cmake\Qt6Core\Qt6CoreConfig.cmake

查看此.cmake文件,当Qt6Core_FOUND为TRUE时有以下一行代码:

include("${CMAKE_CURRENT_LIST_DIR}/Qt6CoreTargets.cmake")
 第二步调用Qt6CoreTargets.cmake

查看Qt6CoreTargets.cmake有一行代码:

# Create imported target Qt6::Core
add_library(Qt6::Core SHARED IMPORTED)

 添加本机上的Qt6::core,根据这个动态库信息,可以得知其将会链接到Qt6Core.dll,但是一个很重要的问题是:怎么知道要链接的Qt6Core.dll在Qt安装目录下的bin目录呢?

第三步定位库路径

在Qt6CoreTargets.cmake有一行代码:

include("${CMAKE_CURRENT_LIST_DIR}/Qt6CoreAdditionalTargetInfo.cmake")

 打开Qt6CoreAdditionalTargetInfo.cmake:

if(_qt_imported_location_default)
    set_property(TARGET Qt6::Core PROPERTY IMPORTED_LOCATION "${_qt_imported_location_default}")
endif()
if(_qt_imported_implib_default)
    set_property(TARGET Qt6::Core PROPERTY IMPORTED_IMPLIB "${_qt_imported_implib_default}")
endif()

 设置IMPORTED_LOCATION为${_qt_imported_location_default},搜索_qt_imported_location_default,发现:Qt6CoreTargets.cmake调用include(Qt6CoreTargets-relwithdebinfo.cmake)

Qt6CoreTargets-relwithdebinfo.cmake设置了变量IMPORTED_LOCATION_RELWITHDEBINFO如下:

set_target_properties(Qt6::Core PROPERTIES
  IMPORTED_IMPLIB_RELWITHDEBINFO "${_IMPORT_PREFIX}/lib/libQt6Core.a"
  IMPORTED_LOCATION_RELWITHDEBINFO "${_IMPORT_PREFIX}/bin/Qt6Core.dll"
  )
最后调用target_link_libraries

link Qt6::Core,将Qt6Core.dll添加为链接库文件。

注意观察target_link_libraries链接一个库为qgc,使用vs code搜索add_library,发现在src/CMakeLists.txt有:

qt_add_library(qgc STATIC
	CmdLineOptParser.cc
	CmdLineOptParser.h
	QGCApplication.cc
	QGCApplication.h
	QGCConfig.h
	QGCToolbox.cc
	QGCToolbox.h
)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值