Xmake v2.7.8 发布,改进包虚拟环境和构建速度

Posted TBOOX开源工程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Xmake v2.7.8 发布,改进包虚拟环境和构建速度相关的知识,希望对你有一定的参考价值。

Xmake 是一个基于 Lua 的轻量级跨平台构建工具。

它非常的轻量,没有任何依赖,因为它内置了 Lua 运行时。

它使用 xmake.lua 维护项目构建,相比 makefile/CMakeLists.txt,配置语法更加简洁直观,对新手非常友好,短时间内就能快速入门,能够让用户把更多的精力集中在实际的项目开发上。

我们能够使用它像 Make/Ninja 那样可以直接编译项目,也可以像 CMake/Meson 那样生成工程文件,另外它还有内置的包管理系统来帮助用户解决 C/C++ 依赖库的集成使用问题。

目前,Xmake 主要用于 C/C++ 项目的构建,但是同时也支持其他 native 语言的构建,可以实现跟 C/C++ 进行混合编译,同时编译速度也是非常的快,可以跟 Ninja 持平。

Xmake = Build backend + Project Generator + Package Manager + [Remote|Distributed] Build + Cache

尽管不是很准确,但我们还是可以把 Xmake 按下面的方式来理解:

Xmake ≈ Make/Ninja + CMake/Meson + Vcpkg/Conan + distcc + ccache/sccache

新特性介绍

快速切换临时虚拟环境

Xmake 很早就支持了包的虚拟环境管理,可以通过配置文件的方式,实现不同包环境之间的切换。

我们可以通过在当前目录下,添加 xmake.lua 文件,定制化一些包配置,然后进入特定的包虚拟环境。

add_requires("zlib 1.2.11")
add_requires("python 3.x", "luajit")
$ xrepo env shell
> python --version
> luajit --version

也可以通过导入自定义环境配置文件,来切换环境:

$ xrepo env --add /tmp/base.lua
$ xrepo env -b base shell

而在新版本中,我们进一步做了改进,让 Xrepo 能够直接在命令行临时指定需要绑定的环境包列表,实现快速切换,无需任何配置。

并且支持同时指定多个包环境。

例如,我们想进入一个带有 python 3.0, luajit 和 cmake 的环境,只需要执行:

$ xrepo env -b "python 3.x,luajit,cmake" shell
[python,luajit,cmake] $ python --version
Python 3.10.6
[python,luajit,cmake] $ cmake --version
cmake version 3.25.3

Xmake 会自动安装相关依赖,然后开启一个新的 shell 环境,新环境终端左边也有 prompt 提示。

如果我们想退出当前环境,仅仅需要执行

[python,luajit,cmake] $ xrepo env quit
$

改进代码特性检测

has_cfuncs/check_cxxsnippets 等系列检测接口,在 option 中已经有提供,并且有对应的辅助 API 来帮助检测。

相关文档可以参考:辅助检测接口

但是目前 option 提供的检测接口仅仅针对全局平台工具链,无法根据每个特定的 target 配置在针对性做一些检测。

因为 target 本身可能还会附带依赖包,不同的工具链,编译宏等差异性,检测结果也会有一些差异。

因此,如果用户想要更加灵活细粒度的检测每个 target 目标的编译特性,可以通过新版本提供的 target 目标实例接口。

  • target:has_cfuncs
  • target:has_cxxfuncs
  • target:has_ctypes
  • target:has_cxxtypes
  • target:has_cincludes
  • target:has_cxxincludes
  • target:has_cflags
  • target:has_cxxflags
  • target:has_features
  • target:check_csnippets
  • target:check_cxxsnippets

这里,仅仅针对其中一些比较常用的接口,稍微展开介绍下使用方式。

target:has_cfuncs

  • 检测目标编译配置能否获取给定的 C 函数

这应该在 on_config 中使用,比如可以用它来判断当前目标能否获取到 zlib 依赖包的一些函数接口,然后自动定义 HAVE_INFLATE

add_requires("zlib")
target("test")
    set_kind("binary")
    add_files("src/*.c")
    add_packages("zlib")
    on_config(function (target)
        if target:has_cfuncs("inflate", includes = "zlib.h") then
            target:add("defines", "HAVE_INFLATE")
        end
    end)

尽管 option 也提供了类似的检测功能,但 option 的检测使用的是全局的平台工具链,它无法附带上 target 相关的一些编译配置,
也无法根据 target 设置不同编译工具链来适配检测,并且无法检测包里面的一些接口。

如果我们仅仅是想粗粒度的检测函数接口,并且 target 没有额外设置不同的工具链,那么 option 提供的检测功能已经足够使用了。

如果想要更细粒度控制检测,可以使用 target 实例接口提供的检测特性。

target:has_cxxfuncs

  • 检测目标编译配置能否获取给定的 C++ 函数

用法跟 target:has_cfuncs 类似,只是这里主要用于检测 C++ 的函数。

不过,在检测函数的同时,我们还可以额外配置 std languages,来辅助检测。

target:has_cxxfuncs("foo", includes = "foo.h", configs = languages = "cxx17")

target:has_ctypes

  • 检测目标编译配置能否获取给定的 C 类型

这应该在 on_config 中使用,如下所示:

add_requires("zlib")
target("test")
    set_kind("binary")
    add_files("src/*.c")
    add_packages("zlib")
    on_config(function (target)
        if target:has_ctypes("z_stream", includes = "zlib.h") then
            target:add("defines", "HAVE_ZSTEAM_T")
        end
    end)

target:has_cflags

  • 检测目标编译配置能否获取给定的 C 编译 flags
target("test")
    set_kind("binary")
    add_files("src/*.cpp")
    on_config(function (target)
        if target:has_cxxflags("-fPIC") then
            target:add("defines", "HAS_PIC")
        end
    end)

target:has_cincludes

  • 检测目标编译配置能否获取给定的 C 头文件

这应该在 on_config 中使用,比如可以用它来判断当前目标能否获取到 zlib 依赖包的 zlib.h 头文件,然后自动定义 HAVE_INFLATE

add_requires("zlib")
target("test")
    set_kind("binary")
    add_files("src/*.c")
    add_packages("zlib")
    on_config(function (target)
        if target:has_cincludes("zlib.h") then
            target:add("defines", "HAVE_ZLIB_H")
        end
    end)

target:check_cxxsnippets

  • 检测是否可以编译和链接给定的 C++ 代码片段

这应该在 on_config 中使用,如下所示:

add_requires("libtins")
target("test")
    set_kind("binary")
    add_files("src/*.cpp")
    add_packages("libtins")
    on_config(function (target)
        local has_snippet = target:check_cxxsnippets(test = [[
            #include <string>
            using namespace Tins;
            void test() 
                std::string name = NetworkInterface::default_interface().name();
                printf("%s\\n", name.c_str());
            
        ]], configs = languages = "c++11", includes = "tins/tins.h"))
        if has_snippet then
            target:add("defines", "HAS_XXX")
        end
    end)

默认仅仅检测编译链接是否通过,如果想要尝试运行时检测,可以再设置 tryrun = true

target("test")
    set_kind("binary")
    add_files("src/*.cpp")
    on_config(function (target)
        local has_int_4 = target:check_cxxsnippets(test = [[
            return (sizeof(int) == 4)? 0 : -1;
        ]], configs = languages = "c++11", tryrun = true))
        if has_int_4 then
            target:add("defines", "HAS_INT4")
        end
    end)

我们也可以继续通过设置 output = true 来捕获检测的运行输出,并且加上自定义的 main 入口,实现完整的测试代码,而不仅仅是代码片段。

target("test")
    set_kind("binary")
    add_files("src/*.cpp")
    on_config(function (target)
        local int_size = target:check_cxxsnippets(test = [[
            #include <stdio.h>
            int main(int argc, char** argv) 
                printf("%d", sizeof(int)); return 0;
                return 0;
            
        ]], configs = languages = "c++11", tryrun = true, output = true))
    end)

target:has_features

  • 检测是否指定的 C/C++ 编译特性

它相比使用 check_cxxsnippets 来检测,会更加快一些,因为它仅仅执行一次预处理就能检测所有的编译器特性,而不是每次都去调用编译器尝试编译。

target("test")
    set_kind("binary")
    add_files("src/*.cpp")
    on_config(function (target)
        if target:has_features("c_static_assert") then
            target:add("defines", "HAS_STATIC_ASSERT")
        end
        if target:has_features("cxx_constexpr") then
            target:add("defines", "HAS_CXX_CONSTEXPR")
        end
    end)

优化编译性能

Xmake 的 build cache 加速类似 ccache,采用预处理器计算 hash 后缓存编译对象文件来实现加速,它在 linux/mac 上提速效果非常明显。

而由于 msvc 的预处理器很慢,也可能是起进程相比 linux/mac 下更重,导致开启 build cache 后,windows 上使用 msvc 的整体编译效率反而慢了非常多。

尝试使用第三方的 ccache 来测试对比,也是一样的问题,因此我暂时针对 msvc 默认禁用了 build cache,使得整体构建速度恢复到正常水平。

clang-tidy 自动修复

上个版本,我们新增了对 clang-tidy 支持,可以通过 xmake check clang.tidy 来检测代码。
而在这个版本中,我们继续对它做了改进,新增了 --fix 参数,可以让 clang-tidy 去自动修复检测出来的问题代码。

$ xmake check clang.tidy --fix
$ xmake check clang.tidy --fix_errors
$ xmake check clang.tidy --fix_notes

Swig/Java 模块构建支持

另外,其他用户也帮忙贡献了 Swig/Java 模块的构建支持。

add_rules("mode.release", "mode.debug")

target("example")
    set_kind(\'shared\')
    -- set moduletype to java
    add_rules("swig.c", moduletype = "java")
    -- use swigflags to provider package name and output path of java files
    add_files("src/example.i", swigflags = 
        "-package",
        "com.example",
        "-outdir",
        "build/java/com/example/"
    )
    add_files("src/example.c")
    before_build(function()
        -- ensure output path exists before running swig
        os.mkdir("build/java/com/example/")
    end)

完整例子见:Swig/Java Example

开源之夏 2023

今年 Xmake 社区继续参加了开源之夏 2023 活动,它是由中科院软件所“开源软件供应链点亮计划”发起并长期支持的一项暑期开源活动
旨在鼓励在校学生积极参与开源软件的开发维护。

如果有感兴趣的同学,欢迎报名参与 Xmake 社区发布的项目开发(具体项目待定中),相关详情进展,请关注:Xmake 开源之夏

更新内容

新特性

  • #3518: 分析编译和链接性能
  • #3522: 为 target 添加 has_cflags, has_xxx 等辅助接口
  • #3537: 为 clang.tidy 检测器添加 --fix 自动修复

改进

  • #3433: 改进 QT 在 msys2/mingw64 和 wasm 上的构建支持
  • #3419: 支持 fish shell 环境
  • #3455: Dlang 增量编译支持
  • #3498: 改进绑定包虚拟环境
  • #3504: 添加 swig java 支持
  • #3508: 改进 trybuild/cmake 去支持工具链切换
  • 为 msvc 禁用 build cache 加速,因为 msvc 的预处理器太慢,反而极大影响构建性能。

Bugs 修复

  • #3436: 修复自动补全和 menuconf
  • #3463: 修复 c++modules 缓存问题
  • #3545: 修复 armcc 的头文件依赖解析

xmake v2.6.4 发布,大量包管理特性改进

Xmake 是一个基于 Lua 的轻量级跨平台构建工具。

它非常的轻量,没有任何依赖,因为它内置了 Lua 运行时。

它使用 xmake.lua 维护项目构建,相比 makefile/CMakeLists.txt,配置语法更加简洁直观,对新手非常友好,短时间内就能快速入门,能够让用户把更多的精力集中在实际的项目开发上。

我们能够使用它像 Make/Ninja 那样可以直接编译项目,也可以像 CMake/Meson 那样生成工程文件,另外它还有内置的包管理系统来帮助用户解决 C/C++ 依赖库的集成使用问题。

目前,Xmake 主要用于 C/C++ 项目的构建,但是同时也支持其他 native 语言的构建,可以实现跟 C/C++ 进行混合编译,同时编译速度也是非常的快,可以跟 Ninja 持平。

Xmake = Build backend + Project Generator + Package Manager

新特性介绍

更灵活的包扩展

现在,我们可以通过 set_base 接口去继承一个已有的包的全部配置,然后在此基础上重写部分配置。

这通常在用户自己的项目中,修改 xmake-repo 官方仓库的内置包比较有用,比如:修复改 urls,修改版本列表,安装逻辑等等。

例如,修改内置 zlib 包的 url,切到自己的 zlib 源码地址。

package("myzlib")
    set_base("zlib")
    set_urls("https://github.com/madler/zlib.git")
package_end()

add_requires("myzlib", system = false, alias = "zlib")

target("test")
    set_kind("binary")
    add_files("src/*.c")
    add_packages("zlib")

我们也可以用来单纯添加一个别名包。

package("onetbb")
    set_base("tbb")

我们可以通过 add_requires("onetbb") 集成安装 tbb 包,只是包名不同而已。

包管理支持工具链切换

之前,我们限制了只能在 cross 平台下切换包安装的工具链,新版本中,我们可以支持更多平台下,对工具链的切换。

例如:

$ xrepo install --toolchains=clang zlib

我们可以在 linux 等平台上,快速切换到 clang 工具链编译安装 zlib 库。

我们也可以在 xmake.lua 的配置文件中去切换他们。

add_requires("zlib", configs = toolchains = "gcc-11")

不同的工具链安装的 zlib 包,会被分别存储在不同目录,互不干扰,不会存在编译器差异导致的链接兼容问题。

内置的包虚拟环境

Xrepo 命令之前已经很好的支持了包虚拟环境管理,xrepo env shell,但是对于复杂的包环境,还是需要用户自己配置一个 xmake.lua 文件,用于管理自己的包环境。

例如,我们需要一个常用的开发环境 shell,默认带有 cmake, python 和 vs/autoconf 等常用的开发工具链,我们需要自己起一个配置文件 devel.lua。

add_requires("cmake")
add_requires("python")
if is_host("linux", "bsd", "macosx") then
    add_requires("pkg-config", "autoconf", "automake", "libtool")
elseif is_host("windows") then
    set_toolchains("msvc")
end

然后,执行下面的命令去导入到全局配置。

$ xrepo env --add devel.lua

这样,我们可以通过下面的命令,去加载 shell 绑定这个环境:

$ xrepo env -b devel shell
> cmake --version
cmake version 3.19.3

而在新版本中,我们内置了一些常用的环境,可以通过 xrepo env -l 查看:

$ xrepo env -l
  - msvc
  - llvm-mingw
  - llvm
  - mingw-w64
  - devel
  - python3
  - depot_tools
  - python2

其中 devel 也在里面,所以,我们只需要执行 xrepo env -b devel shell 就可以带起一个 devel 开发环境,而不需要自己配置它们。

像 python, msvc 等也都是一些比较常用的环境,都可以直接使用。

当然,我们也支持临时在本地创建一个 xmake.lua 来配置加载包环境,而不放置到全局配置中去。

自定义安装包下载

我们可以通过新增的 on_download 接口,自定义包的下载逻辑,通常用不到,使用 Xmake 的内置下载就足够了。

如果用户自建私有仓库,对包的下载有更复杂的鉴权机制,特殊处理逻辑,那么可以重写内部的下载逻辑来实现。

on_download(function (package, opt)
    -- download packages:urls() to opt.sourcedir
end)

opt 参数里面,可以获取到下载包的目的源码目录 opt.sourcedir,我们只需要从 package:urls() 获取到包地址,下载下来就可以了。

然后,根据需要,添加一些自定义的处理逻辑。另外,自己可以添加下载缓存处理等等。

ASN.1 程序构建支持

ASN.1 程序,需要借助 ASN.1 Compiler 去生成相关的 .c 文件参与项目编译。

而 Xmake 内置提供了 add_rules("asn1c") 规则去处理 .c 文件生成,add_requires("asn1c") 自动拉取集成 ASN.1 编译器工具。

下面是一个基础的配置例子:

add_rules("mode.debug", "mode.release")
add_requires("asn1c")

target("test")
    set_kind("binary")
    add_files("src/*.c")
    add_files("src/*.asn1")
    add_rules("asn1c")
    add_packages("asn1c")

具体见 完整例子工程

支持全平台构建 Swift 程序

之前,Xmake 仅支持 macOS 下借助 Xcode 工具链实现对 Swift 程序的构建,新版本中,我们也进行了改进,可以独立使用 swift 工具链,支持在 linux/windows 上构架 swift 程序,用法跟之前相同。

支持指定符号列表导出

在之前的版本中,我们提供了 utils.symbols.export_all 对 windows 的 dll 库实现自动的完整符号导出。

这尽管很方便,但只能支持 windows 程序,并且全量导出对生成的 dll 大小不好控制,有可能会存在不少根本不需要的内部符号被导出。

而,我们新版本提供的 utils.symbols.export_list 规则,可以在 xmake.lua 里面直接定义导出的符号列表,例如:

target("foo")
    set_kind("shared")
    add_files("src/foo.c")
    add_rules("utils.symbols.export_list", symbols = 
        "add",
        "sub")

或者,在 *.export.txt 文件中添加导出的符号列表。

target("foo2")
    set_kind("shared")
    add_files("src/foo.c")
    add_files("src/foo.export.txt")
    add_rules("utils.symbols.export_list")

完整的工程例子见:导出符号例子

通过指定符号导出,我们可以使得生成的动态库尽可能的小,无关的内部符号完全不去导出它们,另外这个规则支持 linux, macOS 和 windows,更加的通用。

它内部会自动使用 .def, version scripts 和 --exported_symbols_list 去处理符号导出。

内置支持 linker scripts

新版中,我们也内置了 对 linker scripts 和 version scripts 文件的支持,我们可以使用 add_files 直接添加它们,而不需要配置 add_ldflags("-Txxx.lds")

当前支持 .ld.lds 作为 linker scripts 配置文件来添加:

add_rules("mode.debug", "mode.release")

target("test")
    add_deps("foo")
    set_kind("binary")
    add_files("src/main.c")
    add_files("src/main.lds")

我们也支持 .ver, .map 后缀文件作为 version script 来添加。

target("foo")
    set_kind("shared")
    add_files("src/foo.c")
    add_files("src/foo.map")

foo.map 文件内容如下:


    global:
        foo;

    local:
        *;
;

更新内容

新特性

  • #2011: 支持继承和局部修改官方包,例如对现有的包更换 urls 和 versions
  • 支持在 sparc, alpha, powerpc, s390x 和 sh4 上编译运行 xmake
  • 为 package() 添加 on_download 自定义下载
  • #2021: 支持 Linux/Windows 下构建 Swift 程序
  • #2024: 添加 asn1c 支持
  • #2031: 为 add_files 增加 linker scripts 和 version scripts 支持
  • #2033: 捕获 ctrl-c 去打印当前运行栈,用于调试分析卡死问题
  • #2059: 添加 xmake update --integrate 命令去整合 shell
  • #2070: 添加一些内置的 xrepo env 环境配置
  • #2117: 支持为任意平台传递工具链到包
  • #2121: 支持导出指定的符号列表,可用于减少动态库的大小

改进

  • #2036: 改进 xrepo 支持从配置文件批量安装包,例如:xrepo install xxx.lua
  • #2039: 改进 vs generator 的 filter 目录展示
  • #2025: 支持为 phony 和 headeronly 目标生成 vs 工程
  • 优化 vs 和 codesign 的探测速度
  • #2077: 改进 vs 工程生成器去支持 cuda

Bugs 修复

  • #2005: 修复 path.extension
  • #2008: 修复 windows manifest 文件编译
  • #2016: 修复 vs project generator 里,对象文件名冲突导致的编译失败

以上是关于Xmake v2.7.8 发布,改进包虚拟环境和构建速度的主要内容,如果未能解决你的问题,请参考以下文章

xmake v2.6.4 发布,大量包管理特性改进

xmake v2.6.4 发布,大量包管理特性改进

xmake v2.5.5 发布,支持下载集成二进制镜像包

xmake v2.5.5 发布,支持下载集成二进制镜像包

Xmake v2.7.7 发布,支持 Haiku 平台,改进 API 检测和 C++ Modules 支持

xmake v2.5.9 发布,改进 C++20 模块,并支持 Nim, Keil MDK 和 Unity Build