[software development] 在这个时代,以Spark构建为名讲一点360天前与 CMake 的故事
Tofloor
poster avatar
魔法师
deepin
2023-12-09 11:22
Author

以 Spark 构建为名讲一点故事

这是一个源于 360 天之前的故事

  • 引用于星火应用商店 CMake 化构建补丁中的 Spark 构建 与 CMake 构建系统预览 文档

    image.png
    (源始分支在很久之前就已经被删除,但此文档仍旧存在于补丁中,并永远保留了这个故事)


  • 前言

# 在 v4.0 之前,我们一直使用 `qmake` 来进行构建星火应用商店。
# 在 v4.0 之后,我们加入了 cmake 构建,并使用 spark 构建为扩展进行开展项目的构建处理。
# 当然,这对于星火应用商店传统的 qmake 构建风格,我们表示还是可以继续保留。
  • 有关 CMakeSpark 之间的关系

    在进行 CMake 化构建时,我们摒弃了传统 CMake 语法,使用以 Spark 为代号进行一种可扩展的 CMake 构建模块设计。

    以下是使用传统 CMake 进行构建一个简单的 Qt 应用程序:

    cmake_minimum_required(VERSION 3.5.1)
    
    project(template LANGUAGES CXX VERSION 0.0.1)
    
    set(CMAKE_INCLUDE_CURRENT_DIR ON)
    set(CMAKE_AUTOMOC ON)
    set(CMAKE_AUTOUIC ON)
    set(CMAKE_AUTORCC ON)
    
    find_package(Qt5 COMPONENTS Core Widgets Network)
    
    # 头文件目录
    include_directories()
    
    # 资源文件路径
    set(QRC_SOURCES "")
    
    add_executable(${PROJECT_NAME} "main.cpp"
        "mainwindow.cpp" "mainwindow.h" 
        ${QRC_SOURCES}
    )
    target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Widgets Qt5::Network)
    

    在传统的 CMake 项目中,它保留了基于 Makefile 构建项目的风格设计,在每一个构建点都会有一个 CMakeLists.txt 存在,它就是所谓的 Makefile 构建的超集。

    终于,我们在编写了大量 CMakeLists.txt 之后,觉得需要一个更快的构建方式,最起码是移除原有的 类C 写法,通过包装不同的目的来完成构建工作,而不是在构建脚本中出现一大堆 CMake 传统语法。

    通过初的设计,我们仅保留了最顶层的 CMakeLists.txt,并将其作为一个唯一构建点。

    cmake_minimum_required(VERSION 3.5.1)
    
    project(spark-store LANGUAGES CXX VERSION 0.0.1)
    
    # 构建 spark-stop 的配置流程
    include(cmake/SparkEnvConfig.cmake)           # 设置一些有关QT构建的开关
    include(cmake/SparkMacrosConfig.cmake)        # 声明了一些 spark_ 开头的 macro 宏
    include(cmake/SparkFindQt5Config.cmake)       # 提供了 target_link_qt5 用于目标链接 qt5 的库
    include(cmake/SparkFindDtkConfig.cmake)       # 提供了 target_link_dtk 用于目标链接 dtk 的库
    include(cmake/SparkThirdLibraryConfig.cmake)  # 提供了 third-party 下对应的 target_link_ 用于目标链接  的库
    include(cmake/SparkFindLibraries.cmake)       # 提供了基于 spark_ 宏生成的 target_link_ 用于目标链接  的库
    include(cmake/SparkTranslatorConfig.cmake)    # 提供了 qt5 ts转qm 的操作,最终生成 SPARK_QM_TRANSLATIONS 变量用于构建可执行文件时参与编译
    include(cmake/SparkBuild.cmake)               # 使用了 spark_ 宏基于已提供的宏参数自动展开构建可执行目标文件
    
    # 构建 spark-store 可执行文件 (用于显式展开 SparkBuild.cmake 内容,如果使用 SparkBuild.cmake 此处将需要注释)
    # spark_add_executable_path(${PROJECT_NAME} src ${SPARK_SOURCES} ${SPARK_QM_TRANSLATIONS})
    # target_link_qt5(${PROJECT_NAME})                 # 构建的目标需要使用 qt5 库
    # target_link_dtk(${PROJECT_NAME})                 # 构建的目标需要使用 dtk 库
    # target_link_notify(${PROJECT_NAME})              # 构建的目标需要使用 notify 库
    # target_link_QtNetworkService(${PROJECT_NAME})    # 构建的目标需要使用 third-part 库
    
    # 子构建 spark-dstore-patch
    # add_subdirectory(src/spark-dstore-patch) # 传统构建方式,但已经可使用 spark_ 宏构建目标
    spark_add_executable_path(spark-dstore-patch src/spark-dstore-patch)
    target_link_qt5(spark-dstore-patch)        # 构建的目标需要使用 qt5 库
    
    include(cmake/SparkInstall.cmake)          # 使用了 DebPackage 提供的安装模式
    include(cmake/SparkBuildGraphviz.cmake)    # 添加了 builddeps 目标构建,make builddeps 将会生成依赖图
    

    这样一写,我们觉得这是一种非常独特的构建工作,旨在为一些 Linux Qt 项目进行构建时,苦于没有一个较好的构建模板设计,每次在编写一个新的项目时,只能从头开始写构建脚本的一种解决方式。

    我们并不打算发明构建工具,只不过在研究打破 CMake 传统构建风格时,我发现了 XMake,当时这是我当时安装一个 XMake 版本。

    $ xmake --version
    xmake v2.6.2+202201121245, A cross-platform build utility based on Lua
    Copyright (C) 2015-present Ruki Wang, tboox.org, xmake.io
                            _
        __  ___ __  __  __ _| | ______      
        \ \/ / |  \/  |/ _  | |/ / __ \     
        >  <  | \__/ | /_| |   <  ___/      
        /_/\_\_|_|  |_|\__ \|_|\_\____|     
                            by ruki, xmake.io  
    
        👉  Manual: https://xmake.io/#/getting_started
        🙏  Donate: https://xmake.io/#/sponsor
    

    在准备尝试使用最适用于 Linux Qt 项目的构建方式,也为更快构建一个 Linux 应用项目来进行扩展构建。

    我们最开始完成了简单的封装一个 spark_ 开头的函数来定义简单的构建库目标、构建可执行目标。

    当时使用的是 function,并没有使用宏 macro,起初认为是无太大区别,后来都转用 macro 来定义了。

    # SparkMacrosConfig.cmake
    
    cmake_minimum_required(VERSION 3.5.1)
    
    # 定义一些 macro 用于自动生成构建结构
    
    # spark_add_library  [files]...
    # 构建一个库,基于指定的源文件
        # 并根据库名生成 target_link_ 函数
    macro(spark_add_library _lib_name)
        message("================ ${_lib_name} Library ================")
        add_library(${_lib_name} ${ARGN})
    
        set(SRCS ${ARGN})
        foreach(item IN LISTS SRCS)
            message(" -> ${item}")
        endforeach(item IN LISTS SRCS)
    
        function(target_link_${_lib_name} TARGET)
            message("${_lib_name}")
            target_link_libraries(${TARGET} ${_lib_name})
        endfunction(target_link_${_lib_name} TARGET)
    
    endmacro(spark_add_library _lib_name)
    
    
    # spark_add_executable  [files]...
    # 构建一个可执行文件,基于指定的源文件
        # Qt编译时源文件包括很多类型,需要指定 *.h/*.cpp/*.qrc/*.qm/... 等
    macro(spark_add_executable _exec_name)
    
        message("================ ${_exec_name} Executable ================")
        add_executable(${_exec_name} ${ARGN})
    
    endmacro(spark_add_executable _exec_name)
    

    这样,我们就完成了一个简单的构建目标的方式,通过包装一个 add_library 我们可以达到相同的目的。

    并为其创建一个 target_link_ 开头的function来明确声明这个库目标被使用者给依赖。

    # 例如构建一个 hellworld 目标,并链接到到 `qt5` 的基础库
    # target_link_qt5 并不是由 spark_add_library 产生的。
    # 只是为了更方便使用 Qt 库, 我们对其进行了一次简单的定义,
    # 而 target_link_ 可以在使用 macro 宏时生成。
    
    # target_link_qt5 中只定义了有限的几个核心组件: Qt5::Core Qt5::Widgets Qt5::Network
    
    spark_add_executable(helloworld 
        main.cpp)
    
    target_link_qt5(helloworld)     # 表示 helloworld 可执行目标依赖于 Qt5
    

    当然也可以这样

    # 构建一个库目标 Say ,它依赖于 qt5 核心库进行构建
    spark_add_library(Say say.h say.cpp)
    target_link_qt5(Say)
    
    # 构建一个可执行目标 helloworld,它将依赖于 Say 库
    spark_add_executable(helloworld main.cpp)
    target_link_Say(helloworld)
    
  • 来到 Spark 构建的世界

    这个 Spark 构建,最主要的方向就是追求扩展与应用到现有 Linux Qt 项目,并替换现有使用传统 CMake 构建的 Linux Qt 项目。

    Spark 一直在追求新的构建风格,新的扩展模块,从最开始的封装简单的构建库与可执行文件,到生成 desktop 文件的模块,构建 deb 软件包的模块,构建新的 install 安装方案。

    其中,从基于指定的源代码构建库与可执行文件,发展到使用指定的路径来构建为一个模块。

    # 构建一个 bigimage 库,它将依赖于 qt5 
    spark_add_libraries_path(bigimage src/spark-widgets/bigimage)
    target_link_qt5(bigimage)
    
    
    # 构建一个 imageshow 库,它将依赖于 bigimage 
    spark_add_libraries_path(imageshow src/spark-widgets/imageshow)
    target_link_bigimage(imageshow)
    
    ...
    

    后来,这种方式也基本上被认为最繁琐的构建方式,我们开始了"一行一库"的构建时代,以上的构建内容可以被认为只有两行构建。

    一是构建 bigimage 库,二是构建 imageshow 库,三是 imageshow 依赖了 bigimage,当然,依赖列表就用'+'来进行表示吧。

    # 基于传入的项进行构建
    # 可接受的值为: 路径列表
    # 可接受的值为: 路径列表+依赖库A+依赖库B
    spark_add_library_realpaths(
        src/spark-widgets/bigimage
        src/spark-widgets/imageshow+bigimage)
    
  • Spark 构建与 DTK

    我们在为基于 Deepin Tool Kit(DTK) 的应用程序添加了简单的扩展,使用以下内容即可使你的程序依赖于 DTK

    # 引入 SparkFindDtk 模块
    include(cmake/SparkFindDtkConfig.cmake)
    
    # 构建一个 bigimage 库,它将自动依赖于 qt5
    spark_add_library_realpaths(
        src/spark-widgets/bigimage)
    
    # 为 bigimage 库目标进行链接 DTK
    target_link_dtk(bigimage)
    
  • Spark 构建与 deb 打包

    我们在为基于 CMakeLists.txt 中使用的 install 指令进行了 CPack 打包扩展,因为我们不喜欢类似 Makefile 这种 make install 安装的方式。

    所以我们也增加了一个扩展模块 DebPackageConfig.cmake,因为它是早于 Spark 构建出现,所以并不为它进行 Spark 命名,它拥有一个模板配置,可以通过简单的填充包描述信息即可实现打包。

    注意,它的最开始三行即是使用方式说明,通过(cv)复制粘贴到您的顶层构建脚本中,即可完成打包功能,更多的软件包打包设定功能仍在 DebPackageConfig.cmake 中预留被注释的部分。

    例如您想生成软件包依赖列表等,在其中 SHLIBDEPS 字样的部分已预留注释。

    例如您想为软件包增加 pre[inst|rm]、post[inst|rm] 等脚本,在其中 CONTROL 字样的部分已预留注释。

    描述文件还为您专门提供了可选的自动化填充软件包名称、软件包版本、软件包架构等,而无需要每次更新描述文件。

    
    
  • 写在后面,有关 Spark 构建的起源与未来

    Spark 构建真正意义上只是一个有趣的想法,并且为它付诸一定的实现。

    我们拥抱过 qmake,我们也拥抱过 cmake。我们是混乱的 IDE 或是代码编辑器的忠实用户,就像是在 IDE 与 编辑器之间的战争从未停止过。

    在着手 Spark 构建之前,它就是一个想法,目的是为了尝试将星火应用商店从 qmake 构建转为 cmake 构建,它就像星星之火中的野火,它有自己的想法。而这个想法就是打破传统的构建方式,或尝试改造现有的构建模式。

    而这并没有为星火商店付出什么,甚至没有提交过任何 bug fix,只是一个因为喜欢安份但又不守已的试图破坏(改变)星火应用商店传统构建的疯狂的 VSCode 用户,事实上是一个 CMake 用户,因为他无法在 VSCode 中使用 qmake 增强 VSCode 的代码能力。

    只能试图在一个已经发展了多年了项目上开始进行破坏(改造),将其转化为以 cmake 为主的构建,并在其它开源项目中寻找 Spark 的构建瓶颈以及拓展它疯狂的可扩展模块。

    在很久之后,这个想法终于在星火商店的 4.0 计划下开始正式实施,此时 Spark 构建已经为很多 Linux Qt 项目进行构建,包括非常复杂的构建探索,打破了一个又一个构建方式,最终完善了基本的构建模板。

    现在,Spark 构建在强大的 CMake 扩展下增强了 VSCode 的代码编写能力,在绕了一大圈之后,终于回到了起源的地方,并开始了它的构建使命,为星火应用商店构建 4.0 以及未来的版本。


(以上是源文)

Reply Favorite View the author
All Replies
魔法师
deepin
2023-12-09 11:38
#1

前排提示:

  1. 有关构建

    • 星火应用商店目前仍是 qmake 项目,cmake 未被采用
    • 星火投稿器除外 - 出道即是 cmake
  2. 有关打包

    • 星火应用商店目前仍是 debian 构建模式打包
    • 星火投稿器除外 - 出道即是 cmake/deb/Appimage

目前整体Spark构建模式正随着投稿器一起准备进入 next 阶段

shamed

Reply View the author