跳转到内容

Xmake v3.0.5 预览:多行进度输出、XML 模块、异步 OS API 和 Swift 互操作

新特性介绍

新版本中,我们引入了多个重要特性,显著提升了开发体验。重点包括多行进度输出(支持主题配置,提供更好的构建可见性)、全面的XML 模块(用于解析和编码 XML 数据)、异步 OS API(提升 I/O 性能)以及Swift 互操作支持(实现 Swift 与 C++/Objective-C 代码的无缝集成)。同时,我们也对工具链配置、TTY 处理进行了重大改进,并进行了各种性能优化。

支持多行刷新进度输出

我们改进了进度输出,支持多行刷新,在长时间运行的构建过程中提供显著更好的视觉体验。构建输出现在不再只更新单行进度,而是显示多个并发构建任务及其各自的进度,使得监控并行编译变得更加容易。

输出现在显示并行构建的多行进度,每个编译任务都有实时状态更新:

progress

您可以通过两种方式启用多行进度输出:

  1. 通过主题配置:使用 soong 主题,它默认包含多行进度:

    bash
    $ xmake g --theme=soong
  2. 通过项目策略:在 xmake.lua 中直接启用:

    lua
    set_policy("build.progress_style", "multirow")

这提供了更好的并行构建进度可见性,更容易识别编译缓慢的单元,并为包含大量源文件或具有多个编译单元的并行构建的大型项目改善了整体用户体验。

更多详情,请参考:#6974

添加 Swift 与 C++/Objective-C 互操作支持

我们新增了全面的 Swift 互操作支持,实现了 Swift 与 C++/Objective-C 代码之间的无缝双向互操作性。当设置了 swift.interop 目标值时,swift.interop 规则会自动启用,使得在同一个项目中混合使用 Swift 和 C++ 代码变得非常容易。

Swift 互操作支持包括:

  • Swift-C++ 双向互操作性
  • 自动生成 C++ 头文件,使 C++ 可以调用 Swift 函数
  • 支持 Objective-C 和 C++ 两种互操作模式
  • Swift 静态库归档工具集,增强编译工作流

目标值配置:

您可以使用以下目标值来配置 Swift 互操作:

lua
set_values("swift.modulename", "SwiftFibonacci")           -- 设置 Swift 模块名
set_values("swift.interop", "cxx")                         -- 启用互操作:"objc" 或 "cxx"
set_values("swift.interop.headername", "fibonacci-Swift.h") -- 定义输出头文件名
set_values("swift.interop.cxxmain", true)                  -- 强制 -parse-as-library 以避免重复的 main 符号

完整示例:Swift-C++ 互操作

以下是一个完整的示例,演示 Swift-C++ 双向互操作:

fibonacci.swift:

swift
// fibonacci.swift
public func fibonacciSwift(_ x: CInt) -> CInt {
  print("x [swift]: \(x)")
  if x <= 1 {
    return 1
  }
  return fibonacciSwift(x - 1) + fibonacciSwift(x - 2)
}

main.cpp:

cpp
// main.cpp
#include <fibonacci-Swift.h>
#include <iostream>

int main(int argc, char ** argv) {
  std::cout << SwiftFibonacci::fibonacciSwift(5) << std::endl;
  return 0;
}

xmake.lua:

lua
-- xmake.lua
target("cxx_interop")
    set_kind("binary")
    set_languages("cxx20")
    add_files("lib/**.swift", {public = true})
    add_files("src/**.cpp")
    set_values("swift.modulename", "SwiftFibonacci")
    set_values("swift.interop", "cxx")
    set_values("swift.interop.headername", "fibonacci-Swift.h")
    set_values("swift.interop.cxxmain", true)

构建输出:

bash
$ xmake
checking for platform ... macosx
checking for architecture ... x86_64
checking for Xcode directory ... /Applications/Xcode.app
[  3%]: <cxx_interop> generating.swift.header fibonacci-Swift.h
[ 38%]: cache compiling.release src/fibonacci.cpp
[ 56%]: compiling.release lib/fibonacci/fibonacci.swift
[ 76%]: linking.release cxx_interop
[100%]: build ok, spent 1.785s

$ xmake run
x [swift]: 5
x [swift]: 4
...
8

当设置 swift.interop 时,xmake 会自动生成 C++ 头文件,使 C++ 代码能够调用 Swift 函数。您可以使用 swift.modulename 定义 Swift 模块名,该名称将成为 C++ 中的命名空间。选择 "objc" 进行 Objective-C 互操作,或选择 "cxx" 进行 C++ 互操作。当 Swift 和 C++ 都有 main 函数时,使用 swift.interop.cxxmain 来避免重复的 main 符号。

这个特性特别适用于:

  • 逐步将现有 C++ 项目迁移到 Swift
  • 在 Swift 应用程序中使用高性能 C++ 库
  • 为 C++ API 创建 Swift 包装器
  • 构建利用两种语言的跨平台应用程序
  • 从 C++ 应用程序无缝调用 Swift 代码

更多详情,请参考:#6967

添加 XML 模块支持

我们引入了一个新的 core.base.xml 模块,提供了一个轻量级的 DOM 风格 XML 工具包,可在 Xmake 的沙箱环境中工作。它专注于可预测的数据结构、类似 JSON 的易用性,以及可选的流式解析,使您可以在不构建整个树的情况下解析大型 XML 文档。

XML 模块特性:

  • 使用普通 Lua 表的 DOM 风格节点结构
  • 用于大文件的流式解析器(xml.scan
  • 类似 XPath 的查询(xml.find
  • 便捷的文件 I/O 辅助函数(xml.loadfilexml.savefile
  • 支持注释、CDATA、DOCTYPE 和未引用的属性
  • 可自定义缩进的格式化输出

节点结构:

XML 节点是具有以下结构的普通 Lua 表:

lua
{
    name     = "element-name" | nil,  -- 仅用于元素节点
    kind     = "element" | "text" | "comment" | "cdata" | "doctype" | "document",
    attrs    = { key = value, ... } or nil,
    text     = string or nil,
    children = { child1, child2, ... } or nil,
    prolog   = { comment/doctype nodes before root } or nil
}

基本用法:

lua
import("core.base.xml")

-- 解析 XML 字符串
local doc = assert(xml.decode([[
<?xml version="1.0"?>
<root id="1">
  <item id="foo">hello</item>
</root>
]]))

-- 查找并修改节点
local item = assert(xml.find(doc, "//item[@id='foo']"))
item.attrs.lang = "en"             -- 直接修改属性
item.children = {xml.text("world")} -- 替换现有文本节点

-- 添加注释
table.insert(doc.children, xml.comment("generated by xmake"))

-- 使用格式化输出编码
local pretty = assert(xml.encode(doc, {pretty = true}))
assert(xml.savefile("out.xml", doc, {pretty = true}))

文件操作:

lua
import("core.base.xml")

-- 从文件加载
local plist = assert(xml.loadfile("Info.plist"))

-- 修改并保存
local dict = assert(xml.find(plist, "plist/dict"))
-- ... 修改节点 ...
assert(xml.savefile("Info.plist", plist, {pretty = true, indent = 2}))

用于大文件的流式解析器:

lua
import("core.base.xml")

local found
xml.scan(plist_text, function(node)
    if node.name == "key" and xml.text_of(node) == "NSPrincipalClass" then
        found = node
        return false -- 提前终止
    end
end)

xml.scan 在节点完成时遍历它们;返回 false 会立即停止扫描。这对于只需要几个条目的大文件非常理想。

类似 XPath 的查询:

lua
import("core.base.xml")

local doc = assert(xml.loadfile("config.xml"))

-- 通过路径查找
local element = xml.find(doc, "/root/item")

-- 通过属性查找
local item = xml.find(doc, "//item[@id='foo']")

-- 通过文本内容查找
local node = xml.find(doc, "//string[text()='value']")

-- 获取文本内容
local text = xml.text_of(node)

节点创建辅助函数:

lua
import("core.base.xml")

local textnode   = xml.text("hello")
local empty      = xml.empty("br", {class = "line"})
local comment    = xml.comment("generated by xmake")
local cdata_node = xml.cdata("if (value < 1) {...}")
local doctype    = xml.doctype('plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"')

选项:

选项适用于描述
trim_text = truexml.decode, xml.scan去除文本节点中的前导/尾随空格
keep_whitespace_nodes = truexml.decode, xml.scan保留仅包含空白字符的文本节点
pretty = true / indent / indentcharxml.encode, xml.savefile启用格式化并控制缩进

使用场景:

  • 读取和修改 IDE 项目配置(Info.plist、.vcxproj 等)
  • 生成基于 XML 的项目文件
  • 处理构建元数据和报告
  • 使用流式解析高效解析大型 XML 文件
  • 在 XML 和 Lua 数据结构之间转换

更多详情,请参考:#7025

添加目标信息的 JSON 输出格式

我们为 xmake show 命令添加了 JSON 输出格式支持,使得以编程方式提取目标信息变得更加容易。这个特性实现了与 IDE、构建自动化工具和需要解析 xmake 项目元数据的自定义脚本的无缝集成。

JSON 输出包含全面的目标信息:

  • 目标名称、类型和文件路径
  • 源文件和头文件
  • 编译器标志和宏定义
  • 链接库和链接器标志
  • 包含目录和依赖项
  • 配置选项和规则

您可以使用 --json 获得紧凑输出,或使用 --pretty-json 获得格式化输出:

bash
$ xmake show -t target --json
{"targets":[{"name":"test","kind":"binary","files":["src/main.cpp"],"links":["pthread"],"defines":["DEBUG"]}]}

$ xmake show -t target --pretty-json
{
  "targets": [
    {
      "name": "test",
      "kind": "binary",
      "files": ["src/main.cpp"],
      "links": ["pthread"],
      "defines": ["DEBUG"],
      "includedirs": ["include"],
      "cxxflags": ["-std=c++17"],
      "deps": ["mylib"]
    }
  ]
}

您可以提取目标信息用于 IDE 集成或在脚本中使用:

bash
# 提取目标信息用于 IDE 集成
xmake show -t target --pretty-json > project_info.json

# 在脚本中使用
TARGET_INFO=$(xmake show -t target --json)
TARGET_NAME=$(echo $TARGET_INFO | jq -r '.targets[0].name')

这对于以下场景特别有用:

  • IDE 集成(VS Code、CLion 等)
  • 自动化构建系统和 CI/CD 流水线
  • 自定义项目分析工具
  • 文档生成

更多详情,请参考:#7024

支持指定 CUDA SDK 版本

我们添加了通过 cuda_sdkver 配置选项指定 CUDA SDK 版本的支持,让您对 CUDA 编译有精确的控制。这在处理多个 CUDA 安装或需要针对特定 CUDA 版本以确保兼容性时非常重要。

您可以为目标指定 CUDA SDK 版本:

lua
target("cuda_app")
    set_kind("binary")
    add_files("src/*.cu")
    add_rules("cuda")
    set_values("cuda.sdkver", "12.0")  -- 指定 CUDA SDK 版本

您也可以将其与针对特定 GPU 架构的计算能力设置结合使用:

lua
target("cuda_app")
    set_kind("binary")
    add_files("src/*.cu")
    add_rules("cuda")
    set_values("cuda.sdkver", "12.0")
    set_values("cuda.arch", "sm_75", "sm_80", "sm_86")

不同的目标可以使用不同的 CUDA 版本:

lua
-- 使用 CUDA 11.8 的目标
target("cuda11_app")
    set_kind("binary")
    add_files("src/cuda11/*.cu")
    add_rules("cuda")
    set_values("cuda.sdkver", "11.8")

-- 使用 CUDA 12.0 的目标
target("cuda12_app")
    set_kind("binary")
    add_files("src/cuda12/*.cu")
    add_rules("cuda")
    set_values("cuda.sdkver", "12.0")

这个特性特别适用于:

  • 需要特定 CUDA 版本以确保兼容性的项目
  • 多版本 CUDA 开发环境
  • 确保在不同系统上一致的 CUDA 编译
  • 针对特定 GPU 架构使用相应的 CUDA 特性

更多详情,请参考:#6964

添加 GCC 15 工具链支持

我们添加了对最新 GCC 15 工具链的支持,确保与最新的编译器特性和改进的兼容性。

bash
$ xmake f --toolchain=gcc-15

更多详情,请参考:#6929

添加 os API 异步支持

我们为 os API 添加了异步支持,允许在 xmake 脚本中进行非阻塞的文件和进程操作。这使得能够并发执行 I/O 操作,在处理多个文件操作或长时间运行的进程时显著提高性能。

支持的 API:

以下 API 现在支持异步操作:

  • os.rm - 删除文件
  • os.rmdir - 删除目录
  • os.cp - 复制文件
  • os.files - 查找文件
  • os.filedirs - 查找文件和目录
  • os.dirs - 查找目录
  • os.match - 匹配文件模式
  • lib.detect.find_file - 查找文件
  • lib.detect.find_library - 查找库
  • lib.detect.find_path - 查找路径
  • lib.detect.find_directory - 查找目录

异步模式:

有两种异步模式可用:

  1. async = true(阻塞):操作可以与其他协程任务一起调度。您需要等待返回值。
  2. async = true, detach = true(非阻塞):操作在后台线程中执行,因此您不需要等待返回值。

使用示例:

lua
import("lib.detect.find_file")

function main()
    -- 在空闲的后台线程中删除文件,我们不需要等待它
    os.rm("/tmp/xxx.txt", {async = true, detach = true})

    -- 异步等待并获取返回值
    local files = os.files("src/*.c", {async = true})

    -- 异步等待并查找文件
    local file = find_file("*.txt", "/tmp/", {async = true})
end

这实现了非阻塞 I/O 操作,在并行读取多个配置文件或并发处理大型文件列表时显著提高性能。它对于异步运行外部工具、实现并行构建步骤以及提高插件和规则性能也很有用。

更多详情,请参考:#6989#6868

改进

改进工具链配置语法

我们改进了工具链配置语法,支持内联配置选项,使工具链设置更加简洁和声明式。新语法提供了三种主要的简化格式:

简化的工具链配置格式:

  1. 仅工具链名称clanggcc
  2. 工具链@包clang@llvm-10@muslcczig
  3. 工具链[配置]@包mingw[clang]@llvm-mingwmsvc[vs=2025,..]

快速切换工具链配置:

您现在可以使用内联语法快速切换工具链配置:

lua
-- 等价于:set_toolchains("mingw", {clang = true})
set_toolchains("mingw[clang]")

-- 命令行用法
-- xmake f --toolchain=mingw[clang]

示例:

lua
-- 简单工具链
set_toolchains("clang")

-- 带包的工具链
set_toolchains("clang@llvm-10")
set_toolchains("@muslcc")
set_toolchains("zig")

-- 带配置和包的工具链
set_toolchains("mingw[clang]@llvm-mingw")
set_toolchains("msvc[vs=2025]")

-- 多个配置
set_toolchains("mingw[clang]", {sdk = "/path/to/llvm-mingw"})

其他改进:

  • 为 llvm-mingw 工具链添加了 clang 支持:

    bash
    xmake f --toolchain=mingw[clang] --sdk=/xxx/llvm-mingw
    lua
    set_toolchains("mingw[clang]@llvm-mingw")
  • 为旧版本 NDK 工具链添加了 gcc 支持:

    bash
    xmake f -p android --toolchain=ndk[gcc] --ndk=/xxxx

这使得工具链配置更加简洁和可读,支持声明式工具链设置,并更易于管理多个工具链变体。它对于快速切换工具链、每个目标的工具链配置、CI/CD 流水线设置以及交叉编译工具链规范特别有用。

更多详情,请参考:#6924讨论 #6903讨论 #6879

改进文件读取性能

我们显著改进了文件读取性能,特别是对于大文件和包含大量源文件的项目。

改进包括更好的缓冲策略和优化的 I/O 操作。

更多详情,请参考:#6942

添加 xmake test 实时输出支持

我们为 xmake test 添加了实时输出支持,允许测试输出在测试运行时实时显示,而不是等到测试完成后再缓冲输出。这对于长时间运行的测试或产生连续输出的测试特别有用,因为它提供了即时的测试进度反馈。

要为测试启用实时输出,在测试配置中设置 realtime_output = true

lua
target("test")
    set_kind("binary")
    add_tests("stub_n", {realtime_output = true, files = "tests/stub_n*.cpp", defines = "STUB_N"})

当启用 realtime_output 时,测试输出将在测试运行时直接流式传输到终端,使得实时监控测试进度和调试问题变得更加容易。

更多详情,请参考:#6993

改进 TTY 处理和输出

我们改进了 core.base.tty 模块的 TTY 处理和输出格式。新增了以下接口:

  • tty.cursor_move_up(n) / tty.cursor_move_down(n) - 垂直移动光标
  • tty.cursor_move_left(n) / tty.cursor_move_right(n) - 水平移动光标
  • tty.cursor_move_to_col(n) - 移动到指定列
  • tty.cursor_save() / tty.cursor_restore() - 保存和恢复光标位置
  • tty.cursor_hide() / tty.cursor_show() - 控制光标可见性
  • tty.cr() - 移动到行首(回车)
  • tty.erase_line() - 清除整行
  • tty.erase_line_to_end() - 清除从光标到行尾
  • tty.has_vtansi() - 检查终端是否支持 ANSI 控制码

更多详情,请参考:#6970

添加 Ghostty 终端检测支持

我们添加了对 Ghostty 终端的检测支持,确保在这个现代终端模拟器中具有正确的输出格式和颜色支持。

更多详情,请参考:#6987

改进图模块性能

我们改进了图模块的性能,该模块用于依赖解析和构建图生成。

这些改进使得项目配置和依赖分析速度更快。

更多详情,请参考:#7027

更新日志

新特性

  • #6929: 添加 GCC 15 工具链支持
  • #6967: 添加 Swift 与 C++/Objective-C 互操作支持
  • #6964: 支持通过 cuda_sdkver 指定 CUDA SDK 版本
  • #6963: 添加交叉编译的 libtool 补丁支持
  • #6974: 支持多行刷新进度输出
  • #7024: 为 xmake show -t target 添加 JSON 输出格式
  • #7025: 添加 XML 模块,支持解析和编码
  • #6989: 添加 os API 异步支持

改进

  • #6924: 改进工具链配置,支持 add_toolchains("name[configs]") 语法
  • #6942: 改进文件读取性能
  • #6970: 改进 TTY 处理和输出
  • #6977: 重构 Xcode 工具链,集成到 Apple 设备的 LLVM 工具链中
  • #6987: 添加 Ghostty 终端检测支持
  • #7003: 限制包配置中的构建环境获取
  • #7004: 使用 -r 标志时跳过重建包和 std 模块
  • #7019: 改进 xmake.sh/configure 脚本,添加 Ninja 生成器支持
  • #7022: 使 zig-cc 工具链继承自 clang
  • #7027: 改进图模块性能
  • #7031: 改进 require 解析
  • #7032: 改进符号提取

Bugs 修复

  • #6926: 修复 Windows 上加载 Unicode 主脚本路径的问题
  • #6931: 修复 C++ 模块:当工具链版本未安装时回退到系统范围的 clang-scan-deps
  • #6937: 修复目标作业处理
  • #6954: 修复 vsxmake/vs 生成器的模块支持
  • #6955: 修复包中的构建号排序
  • #6956: 修复使用不支持 depfile 的 zigcc 链接器时的构建失败
  • #6959: 修复使用 zigcc 与 autotools 进行动态链接的问题
  • #6983: 修复模块:为模块重用去除 sanitizer 标志
  • #6984: 修复已安装的 CMake 导入文件中的 libdir 路径
  • #6992: 修复模块:为 clang get_cpp_library_name 添加所有支持的平台
  • #6993: 修复 xmake test 模块
  • #6996: 修复 Nimble find_package 以使用最新的包列表格式
  • #6999: 修复 rootdir 处理
  • #7002: 修复 asn1c:将生成的输出作为系统头文件包含
  • #7012: 修复稀疏检出处理
  • #7013: 修复打包时移除依赖的问题
  • #7016: 修复 vsxmake 中的项目默认配置
  • #7017: 修复 lock_packages 拼写错误
  • #7018: 修复构建顺序:仅在禁用依赖链接继承时禁用