简介

Qbs 是一个构建自动化工具,旨在方便地管理跨多平台软件项目的构建过程。

特性

Qbs 提供以下优点

  • 声明性范式
  • 定义清晰的语言
  • 平台和编程语言独立性
  • 正确且快速的增量构建
  • 可扩展的架构
  • 易于集成到 IDE

声明性范式

在编写项目时,描述构建任务及其之间的依赖关系比指定构建顺序更重要。在复杂项目中,尤其是在并行构建期间,确定正确的构建顺序比较困难。构建工具应该承担这个负担,而不是开发者。

使用声明性语言,Qbs 可以让您表达意图,而不是指定单个构建步骤。这为构建系统提供了适当的抽象级别。例如,可以创建 产品 之间的 依赖关系,使得依赖关系的目标 成果 可以作为依赖于该产品的构建 规则 的输入。此外,您还可以将依赖关系和 属性 导出给其他产品。

Qbs 是模块化的,模块之间具有清晰的接口。一个 模块 是一组属性和 语言元素 的集合,如果产品依赖于该模块,则用于构建该产品。可以设置的模块属性用于控制用于构建模块的工具链的行为。

Qbs 对文件类型或扩展名一无所知,因此一个产品中的所有源文件都平等地处理。但是,您可以给一个成果分配 文件标签,用作标记或指定文件类型。Qbs 将规则应用于项目的源文件,并选择匹配规则指定的输入文件标签的文件。然后,它在构建图中创建具有其他文件名和文件标签的成果。

产品和项目可以包含在构建之前运行的 探测,例如用于定位项目目录外的依赖头文件、库和其他文件。

定义清晰的语言

Qbs 项目使用 QML 方言指定。QML 是一种简洁、易学且直观的语言,已经在 Qt 项目中成功使用。其核心是声明性的,但可以通过 JavaScript 片段扩展以提供额外的灵活性。

Qbs 根据项目文件中的信息构建应用程序。每个项目文件指定一个 项目,该文件可以包含多个 产品。您指定产品的类型,例如 应用程序,以及产品对其他产品的依赖关系。

产品类型决定了 Qbs 应用到输入文件的 规则 集合,以从输入文件生成成果。输入文件可以根据其类型或目的进行分组,例如。组还可以用于将 属性附加到产品上。

以下是一个指定产品类型、应用程序名称、源文件以及依赖于 cpp 模块的至少限项目文件示例:

Application {
    name: "helloworld"
    files: "main.cpp"
    Depends { name: "cpp" }
}

有关更多信息,请参阅 语言简介

平台和编程语言独立性

Qbs 可用于任何软件项目,无论使用哪种编程语言、工具包或库。Qbs 内置了对Windows、Linux、macOS、Android、iOS、tvOS、watchOS、QNX 和 FreeBSD 的应用程序构建支持,以及交叉编译。它可以很容易地扩展以支持更多平台。

从命令行调用 qbs build 会自动使用最佳工具链和设置构建当前宿主平台的 dự án,除非设置了默认配置文件。您可以配置要使用的每个工具链的附加配置文件,并在构建时选择要使用的配置文件。

例如,要为 Android 设备构建应用程序,您需要设置 Android 工具链配置文件,并在构建应用程序时选择它。如果将配置文件命名为 Android,则输入以下命令

qbs build profile:Android

有关更多信息,请参阅 构建应用程序

平台和编程语言支持作为一系列依赖的产品 模块 实现。在上面的语言示例中,对 cpp 模块的依赖确定 C++ 源代码被编译并链接到二进制文件中。

或者,您可以使用CppApplication 推便工具,它假定对 cpp 模块的依赖

CppApplication {
    name: "helloworld"
    files: "main.cpp"
}

此外,如果源代码使用 Qt,则需要依赖 Qt.core 模块,等等。

除了构建项目外,Qbs 还可以将构建工件安装到运行在桌面上或设备上的位置。可以使用 Qbs 模块来为最终用户创建应用程序的安装程序。例如,dmg 模块包含用于构建 Apple 磁盘镜像的属性和规则,通常用于在 macOS 上分发应用程序和安装程序。《a href="qml-qbsmodules-innosetup.html" translate="no">innosetup,nsiswix 模块包含用于在 Windows 平台上构建安装程序的属性和规则。

正确且快速的增量构建

Qbs 是一个一体化的工具,它从高级项目描述(如 qmake 或 CMake)生成构建图,并且还承担执行低级构建图(如 make)中指令的任务。

Qbs 可以自动利用多处理器和多核心系统以实现最大的构建并行化。默认情况下,以无任何参数运行 qbs 大约等同于运行 make -j<n>,其中 n 是 CPU 核心数。同样,Qbs 允许显式指定其自己的 -j 选项来指定并发作业的数量。

Qbs 了解整个项目,因此即使在构建子项目时也能保持构建的正确性,因为 Qbs 确保所有依赖项都得到构建。这几乎消除了需要干净构建的需求。

Qbs 使用动态构建图,具有可以生成可变数量文件的构建规则,并且仅在需要时执行。在确定要执行哪些规则时,Qbs 从产品类型开始,然后查找从源文件产生匹配文件标签的工件的方式,使用连接到它们各自的输入和输出标签的规则链。有关在构建产品时如何应用规则的示例,请参阅 规则和产品类型

Qbs的构建规则可以产生可变数量的输出。如果输入发生变化,则仅在实际构建时间应用所需的规则。如果应用了规则,则所有依赖的规则也将应用,但仅限于这些规则。此功能确保在源代码更改后构建图的正确性,而无需重新配置整个项目。

由于规则没有使用,更改不影响构建的属性将不会导致重新构建项目。属性的使用情况将被跟踪。删除不再存在的生成工件以避免选择过时的生成工件并无限期地增加构建目录的大小。

快速增量构建对于快速编辑-构建-运行周期至关重要。Qbs不检索生成文件的最后修改时间戳,而是使用存储在构建图中的时间戳。这对于在文件系统操作缓慢的Windows上尤其重要。

如果项目文件没有更改,则从磁盘加载构建图。它以二进制格式存储,其加载速度要比真实的项目文件快得多。只有当项目文件更改时,才会解析项目文件。

可扩展的架构

您可以创建自己的自定义模块元素,并使Qbs了解它们。

您可以将自定义模块和元素存储在项目目录的子目录中,并将子目录的路径指定为qbsSearchPaths属性的值。例如,如果自定义模块位于my-modules/modules/modulename/modulename.qbs,您可以在项目文件中指定它如下

Project {
    qbsSearchPaths: "my-modules"

有关更多信息,请参阅自定义模块和元素

IDE集成

Qbs不仅可以从命令行使用,还可以与IDE结合使用,例如Qt Creator或Visual Studio Code。这些IDE直接支持使用新的Qbs 会话功能使用Qbs项目。因此,这些IDE可以通过会话的JSON协议API检索构建单个文件或项目所需的所有信息。

此外,Qbs可以为Visual Studio、IAR EW和Keil uVision生成项目,但这仍然是一个实验性选项。有关更多信息,请参阅生成器

Qt Creator

Qt Creator可以提供关于构建进度的准确信息,并显示反映项目逻辑结构的项目树,而不是展示底层信息,例如文件系统结构。添加或删除源文件将保持现有项目文件结构的完整性。

有关使用Qbs从Qt Creator构建项目的更多信息,请参阅设置Qbs

Visual Studio Code

Visual Studio Code提供了一个qbs-community插件,该插件可以提供关于构建进度的准确信息,并显示反映项目逻辑结构的项目树。它还可以提供有关文件系统结构等信息。

有关使用Qbs从Visual Studio Code构建项目的更多信息,请参阅如何操作

构建过程

产品的构建过程首先检查产品的 类型 属性。它包含一个类似 MIME 类型的 文件标签 列表。

以下示例产品包含一个文件标签,应用

Product {
    Depends { name: "cpp" }
    type: ["application"]
    files: ["main.cpp", "class.cpp", "class.h"]
}

Qbs 会搜索上下文中所有可用的 规则,这意味着定义在项目中的规则,或者通过模块依赖提供的规则,如示例中的从 cpp 依赖项中拉取的编译器和链接器规则。

当 Qbs 找到一个产生与相关文件标签相关的一个或多个工件(artifact)的规则时,它查看此规则的所有依赖项,并发现它产生标记为 obj 的工件。然后它找到一个接受 .cpp 工件作为输入并产生 obj 工件的规则。

Module {
    // ...
    Rule {
        inputs: ["cpp"]
        Artifact {
            filePath: input.fileName + ".o"
            fileTags: ["obj"]
        }
        prepare: {
            // g++ -c main.cpp -o main.o ...
        }
    }
    //...
}

当前上下文中没有规则产生 .cpp 文件,但我们已经将这些文件定义为产品的输入。当我们添加了对 cpp 模块的依赖时,这个依赖项还引入了另一个 Qbs 原语,即名为file tagger 的文件标签器。文件标签器搜索匹配 *.cpp 模式的文件,并给这些输入文件应用 cpp 标签。

Module {
    // ...
    FileTagger {
        patterns: "*.cpp"
        fileTags: ["cpp"]
    }
    //...
}

由于 .cpp 文件是输入文件,根据定义,它们没有其他依赖项,我们可以从上面描述的编译器规则开始逆序遍历树。

这种设计非常适合生成文件。.cpp 工件可能来自另一个通过处理其他输入生产它们的规则,无论是在产品中列出的原始文件列表之外,还是与这些文件一起。

编译器规则将被调用两次,一次针对每个 .cpp 文件,为每个文件生成一个单独的对象文件。然后调用链接器规则。其 multiplex 属性设置为 true,这意味着在每次调用规则多个次以产生一个输出之前,将会收集所有输入并在调用一次规则后产生最终的应用程序对象。

标准规则与多路复用规则很好地映射到编译器和链接器过程。编译器从一个输入文件产生一个输出文件,而链接器接收多个输入文件以产生一个输出文件。

最后,链接器规则被调用后,产生一个标记为 application 的工件。由于产品的类型属性不包含其他文件标签,构建过程现在已经完成。

©2023 Qt 公司 Ltd. 本文档中包含的贡献是各自所有者的版权。提供的文档根据自由软件基金会发布的 GNU自由文档许可证版本1.3 的条款进行许可。Qt 以及相应的标志是芬兰的 Qt 公司及其在全世界各国的商标。所有其他商标都是其各自所有者的财产。