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

2025 年 11 月 28 日
 waruqi

新特性介绍

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

下载地址: GitHub Releases | 源码仓库

支持多行刷新进度输出

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

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

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

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

    $ xmake g --theme=soong
    
  2. 通过项目策略:在 xmake.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 互操作:

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:

// 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:

// 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:

-- 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)

构建输出:

$ 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 符号。

这个特性特别适用于:

更多详情,请参考:#6967

添加 XML 模块支持

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

XML 模块特性:

节点结构:

XML 节点是具有以下结构的普通 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
}

基本用法:

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}))

文件操作:

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}))

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

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 的查询:

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)

节点创建辅助函数:

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 = true xml.decode, xml.scan 去除文本节点中的前导/尾随空格
keep_whitespace_nodes = true xml.decode, xml.scan 保留仅包含空白字符的文本节点
pretty = true / indent / indentchar xml.encode, xml.savefile 启用格式化并控制缩进

使用场景:

更多详情,请参考:#7025

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

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

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

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

$ 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 集成或在脚本中使用:

# 提取目标信息用于 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')

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

更多详情,请参考:#7024

支持指定 CUDA SDK 版本

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

您可以通过命令行参数指定 CUDA SDK 版本:

$ xmake f --cuda_sdkver=11.8
$ xmake

支持的版本值包括:

这个特性特别适用于:

更多详情,请参考:#6964

添加 GCC 15 工具链支持

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

$ xmake f --toolchain=gcc-15

更多详情,请参考:#6929

添加 Solaris 平台支持

我们新增了对 Solaris 平台的支持,包括 i386 和 x86_64 架构。这使得 xmake 可以在 Solaris 系统上进行构建,进一步扩展了跨平台支持。同时,我们也新增了对其他 BSD 系统的支持,包括 NetBSD 、OpenBSD 和 DragonflyBSD 。

$ xmake f -p solaris -a i386
$ xmake f -p solaris -a x86_64

更多详情,请参考:#7055 和 #7054

添加 os API 异步支持

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

支持的 API:

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

异步模式:

有两种异步模式可用:

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

使用示例:

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. 仅工具链名称:clang、gcc
  2. 工具链 @包:clang@llvm-10、@muslcc、zig
  3. 工具链[配置]@包:mingw[clang]@llvm-mingw、msvc[vs=2025,..]

快速切换工具链配置:

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

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

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

示例:

-- 简单工具链
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"})

其他改进:

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

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

改进文件读取性能

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

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

更多详情,请参考:#6942

添加 xmake test 实时输出支持

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

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

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 处理和输出格式。新增了以下接口:

更多详情,请参考:#6970

添加 Ghostty 终端检测支持

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

更多详情,请参考:#6987

改进图模块性能

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

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

更多详情,请参考:#7027

更新日志

新特性

改进

Bugs 修复

2670 次点击
所在节点    程序员
2 条回复
momo1999
2025 年 11 月 28 日
现在 xmake.io 网站不好找版本信息和更新日志了。
waruqi
2025 年 11 月 28 日
@momo1999 在 github 上看,每次同步到文档站 太麻烦

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://v2ex.ih06.com/t/1175543

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX