Xmake v2.8.5 发布,支持链接排序和单元测试

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 最近进入了 Debian 的官方仓库:https://packages.debian.org/sid/xmake,
等到明年4月份 Ubuntu 24.04 发布,我们应该就能直接通过 apt install xmake 命令去快速安装 Xmake 了。

同时也感谢 @Lance Lin 的帮助,他全程帮助我们维护并上传 Xmake 包到 Debian 仓库,真的非常感谢!

接下来,我们来介绍下 2.8.5 版本引入的一些改动,这个版本带来了很多的新特性,尤其是对链接排序,链接组的支持,还有对 xmake test 内置单元测试的支持。
另外,我们还新增了 Apple XROS 平台的构建支持,可以用于构建苹果新的 VisionOS 上的程序,还有我们还提供了更加灵活通用的 check_sizeof 检测接口,用于快速检测类型的大小。

链接重排序支持

这是一个存在了两年多的需求,主要用于调整 target 内部的链接顺序。

由于 xmake 提供了 add_links, add_deps, add_packages, add_options 接口,可以配置目标、依赖,包和选项中的链接,尽管 add_links 本身的链接顺序可以根据添加顺序来调整。

但是 links,deps 和 packages 之间的链接顺序,只能按固定顺序生成,无法灵活调整,这对于一些复杂的项目,就有点显得力不从心了。

而我们在这个版本,彻底解决了这个问题,新增了 add_linkorders 接口,可用于配置目标、依赖、包、选项、链接组引入的各种链接顺序。

更多详情和背景,请见:#1452

排序链接

为了更加灵活的调整 target 内部的各种链接顺序,我们可以通过 add_linkorders 这个新接口来实现,例如:

add_links("a", "b", "c", "d", "e")
-- e -> b -> a
add_linkorders("e", "b", "a")
-- e -> d
add_linkorders("e", "d")

add_links 是配置的初始链接顺序,然后我们通过 add_linkorders 配置了两个局部链接依赖 e -> b -> ae -> d 后。

xmake 内部就会根据这些配置,生成 DAG 图,通过拓扑排序的方式,生成最终的链接顺序,提供给链接器。

当然,如果存在循环依赖,产生了环,它也会提供警告信息。

排序链接和链接组

另外,对于循环依赖,我们也可以通过 add_linkgroups 配置链接组的方式也解决。

并且 add_linkorders 也能够对链接组进行排序。

add_links("a", "b", "c", "d", "e")
add_linkgroups("c", "d", {name = "foo", group = true})
add_linkorders("e", "linkgroup::foo")

如果要排序链接组,我们需要对每个链接组取个名,{name = "foo"} ,然后就能在 add_linkorders 里面通过 linkgroup::foo 去引用配置了。

排序链接和frameworks

我们也可以排序链接和 macOS/iPhoneOS 的 frameworks。

add_links("a", "b", "c", "d", "e")
add_frameworks("Foundation", "CoreFoundation")
add_linkorders("e", "framework::CoreFoundation")
完整例子

相关的完整例子,我们可以看下:

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

add_requires("libpng")

target("bar")
    set_kind("shared")
    add_files("src/foo.cpp")
    add_linkgroups("m", "pthread", {whole = true})

target("foo")
    set_kind("static")
    add_files("src/foo.cpp")
    add_packages("libpng", {public = true})

target("demo")
    set_kind("binary")
    add_deps("foo")
    add_files("src/main.cpp")
    if is_plat("linux", "macosx") then
        add_syslinks("pthread", "m", "dl")
    end
    if is_plat("macosx") then
        add_frameworks("Foundation", "CoreFoundation")
    end
    add_linkorders("framework::Foundation", "png16", "foo")
    add_linkorders("dl", "linkgroup::syslib")
    add_linkgroups("m", "pthread", {name = "syslib", group = true})

完整工程在:linkorders example

链接组支持

另外,这个版本,我们还新增了链接组的原生支持,它目前主要用于 linux 平台的编译,仅支持 gcc/clang 编译器。

需要注意的是 gcc/clang 里面的链接组概念主要特指:-Wl,--start-group

而 Xmake 对齐进行了封装,做了进一步抽象,并且不仅仅用于处理 -Wl,--start-group,还可以处理 -Wl,--whole-archive-Wl,-Bstatic

下面我们会一一对其进行讲解。

更多详情见:#1452

–start-group 支持

-Wl,--start-group-Wl,--end-group 是用于处理复杂库依赖关系的链接器选项,确保链接器可以解决符号依赖并成功连接多个库。

在 xmake 中,我们可以通过下面的方式实现:

add_linkgroups("a", "b", {group = true})

它会对应生成 -Wl,--start-group -la -lb -Wl,--end-group 链接选项。

如果 a 和 b 库之间有符号的循环依赖,也不会报链接错误,能够正常链接成功。

对于不支持的平台和编译,会退化成 -la -lb

–whole-archive 支持

--whole-archive 是一个链接器选项,通常用于处理静态库。
它的作用是告诉链接器将指定的静态库中的所有目标文件都包含到最终可执行文件中,而不仅仅是满足当前符号依赖的目标文件。
这可以用于确保某些库的所有代码都被链接,即使它们在当前的符号依赖关系中没有直接引用。

更多信息,可以参考 gcc/clang 的文档。

在 xmake 中,我们可以通过下面的方式实现:

add_linkgroups("a", "b", {whole = true})

它会对应生成 -Wl,--whole-archive -la -lb -Wl,--no-whole-archive 链接选项。

对于不支持的平台和编译,会退化成 -la -lb

另外,我们可以同时配置 group/whole:

add_linkgroups("a", "b", {whole = true, group = true})
-Bstatic 支持

-Bstatic 也是用于编译器(如gcc)的选项,用于指示编译器在链接时只使用静态库而不使用共享库。

更多信息,可以参考 gcc/clang 的文档。

在 xmake 中,我们可以通过下面的方式实现:

add_linkgroups("a", "b", {static = true})

它会对应生成 -Wl,-Bstatic -la -lb -Wl,-Bdynamic 链接选项。

单元测试支持

新版本中,我们还增加了一个内置的测试命令:xmake test,我们只需要在需要测试的 target 上通过 add_tests 配置一些测试用例,就可以自动执行测试。

即使当前 target 被设置成了 set_default(false),在执行测试的时候,xmake 也还是会先自动编译它们,然后自动运行所有的测试。

我们可以先看个整体的例子,大概知道下它是怎么样子的。

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

for _, file in ipairs(os.files("src/test_*.cpp")) do
    local name = path.basename(file)
    target(name)
        set_kind("binary")
        set_default(false)
        add_files("src/" .. name .. ".cpp")
        add_tests("default")
        add_tests("args", {runargs = {"foo", "bar"}})
        add_tests("pass_output", {trim_output = true, runargs = "foo", pass_outputs = "hello foo"})
        add_tests("fail_output", {fail_outputs = {"hello2 .*", "hello xmake"}})
end

这个例子,自动扫描源码目录下的 test_*.cpp 源文件,然后每个文件自动创建一个测试目标,它被设置成了 set_default(false),也就是正常情况下,默认不会编译它们。

但是,如果执行 xmake test 进行测试,它们就会被自动编译,然后测试运行,运行效果如下:

ruki-2:test ruki$ xmake test
running tests ...
[  2%]: test_1/args        .................................... passed 7.000s
[  5%]: test_1/default     .................................... passed 5.000s
[  8%]: test_1/fail_output .................................... passed 5.000s
[ 11%]: test_1/pass_output .................................... passed 6.000s
[ 13%]: test_2/args        .................................... passed 7.000s
[ 16%]: test_2/default     .................................... passed 6.000s
[ 19%]: test_2/fail_output .................................... passed 6.000s
[ 22%]: test_2/pass_output .................................... passed 6.000s
[ 25%]: test_3/args        .................................... passed 7.000s
[ 27%]: test_3/default     .................................... passed 7.000s
[ 30%]: test_3/fail_output .................................... passed 6.000s
[ 33%]: test_3/pass_output .................................... passed 6.000s
[ 36%]: test_4/args        .................................... passed 6.000s
[ 38%]: test_4/default     .................................... passed 6.000s
[ 41%]: test_4/fail_output .................................... passed 5.000s
[ 44%]: test_4/pass_output .................................... passed 6.000s
[ 47%]: test_5/args        .................................... passed 5.000s
[ 50%]: test_5/default     .................................... passed 6.000s
[ 52%]: test_5/fail_output .................................... failed 6.000s
[ 55%]: test_5/pass_output .................................... failed 5.000s
[ 58%]: test_6/args        .................................... passed 7.000s
[ 61%]: test_6/default     .................................... passed 6.000s
[ 63%]: test_6/fail_output .................................... passed 6.000s
[ 66%]: test_6/pass_output .................................... passed 6.000s
[ 69%]: test_7/args        .................................... failed 6.000s
[ 72%]: test_7/default     .................................... failed 7.000s
[ 75%]: test_7/fail_output .................................... failed 6.000s
[ 77%]: test_7/pass_output .................................... failed 5.000s
[ 80%]: test_8/args        .................................... passed 7.000s
[ 83%]: test_8/default     .................................... passed 6.000s
[ 86%]: test_8/fail_output .................................... passed 6.000s
[ 88%]: test_8/pass_output .................................... failed 5.000s
[ 91%]: test_9/args        .................................... passed 6.000s
[ 94%]: test_9/default     .................................... passed 6.000s
[ 97%]: test_9/fail_output .................................... passed 6.000s
[100%]: test_9/pass_output .................................... passed 6.000s

80% tests passed, 7 tests failed out of 36, spent 0.242s

我们也可以执行 xmake test -vD 查看详细的测试失败的错误信息:

运行指定测试目标

我们也可以指定运行指定 target 的某个测试:

$ xmake test targetname/testname

或者按模式匹配的方式,运行一个 target 的所有测试,或者一批测试:

$ xmake test targetname/*
$ xmake test targetname/foo*

也可以运行所有 target 的同名测试:

$ xmake test */testname
并行化运行测试

其实,默认就是并行化运行的,但是我们可以通过 -jN 调整运行的并行度。

$ xmake test -jN
分组运行测试
$ xmake test -g "foo"
$ xmake test -g "foo*"
添加测试到目标(无参数)

如果没有配置任何参数,仅仅配置了测试名到 add_tests,那么仅仅测试这个目标程序的是否会运行失败,根据退出代码来判断是否通过测试。

target("test")
    add_tests("testname")
配置运行参数

我们也可以通过 {runargs = {"arg1", "arg2"}} 的方式,给 add_tests 配置指定测试需要运行的参数。

另外,一个 target 可以同时配置多个测试用例,每个测试用例可独立运行,互不冲突。

target("test")
    add_tests("testname", {runargs = "arg1"})
    add_tests("testname", {runargs = {"arg1", "arg2"}})

如果我们没有配置 runargs 到 add_tests,那么我们也会尝试从被绑定的 target 中,获取 set_runargs 设置的运行参数。

target("test")
    add_tests("testname")
    set_runargs("arg1", "arg2")
配置运行目录

我们也可以通过 rundir 设置测试运行的当前工作目录,例如:

target("test")
    add_tests("testname", {rundir = os.projectdir()})

如果我们没有配置 rundir 到 add_tests,那么我们也会尝试从被绑定的 target 中,获取 set_rundir 设置的运行目录。

target("test")
    add_tests("testname")
    set_rundir("$(projectdir)")
配置运行环境

我们也可以通过 runenvs 设置一些运行时候的环境变量,例如:

target("test")
    add_tests("testname", {runenvs = {LD_LIBRARY_PATH = "/lib"}})

如果我们没有配置 runenvs 到 add_tests,那么我们也会尝试从被绑定的 target 中,获取 add_runenvs 设置的运行环境。

target("test")
    add_tests("testname")
    add_runenvs("LD_LIBRARY_PATH", "/lib")
匹配输出结果

默认情况下,xmake test 会根据测试运行的退出代码是否为 0,来判断是否测试通过。

当然,我们也可以通过配置测试运行的输出结果是否满足我们的指定的匹配模式,来判断是否测试通过。

主要通过这两个参数控制:

参数说明
pass_outputs如果输出匹配,则测试通过
fail_outputs如果输出匹配,则测试失败

传入 pass_outputsfail_outputs 的是一个 lua 匹配模式的列表,但模式稍微做了一些简化,比如对 * 的处理。

如果要匹配成功,则测试通过,可以这么配置:

target("test")
    add_tests("testname1", {pass_outputs = "hello"})
    add_tests("testname2", {pass_outputs = "hello *"})
    add_tests("testname3", {pass_outputs = {"hello", "hello *"}})

如果要匹配成功,则测试失败,可以这么配置:

target("test")
    add_tests("testname1", {fail_outputs = "hello"})
    add_tests("testname2", {fail_outputs = "hello *"})
    add_tests("testname3", {fail_outputs = {"hello", "hello *"}})

我们也可以同时配置它们:

target("test")
    add_tests("testname", {pass_outputs = "foo", fail_outputs = "hello"})

由于一些测试输出的结果,尾部会有一些换行什么的空白字符,干扰匹配模式,我们可以再配置 trim_output = true,先截断空白字符后,再做匹配。

target("test")
    add_tests("testname", {trim_output = true, pass_outputs = "foo", fail_outputs = "hello"})

我们还可以配置 {plain = true} 是禁用 lua 模式匹配,仅仅做最基础的平坦文本匹配。

target("test")
    add_tests("testname", {plain = true, pass_outputs = "foo", fail_outputs = "hello"})
配置测试组

我们也可以通过 group = "foo" 来配置一个测试组,进行分组测试:

target("test")
    add_tests("testname1", {group = "foo"})
    add_tests("testname2", {group = "foo"})
    add_tests("testname3", {group = "bar"})
    add_tests("testname4", {group = "bae"})

其中 testname1/testname2 是一个组 foo,另外两个是在另外一个组。

然后,我们就可以使用 xmake test -g groupname 来进行分组测试了。

$ xmake test -g "foo"
$ xmake test -g "foo*"

!> 运行分组,也是支持模式匹配的。

另外,如果没有设置 group 参数给 add_tests,我们也可以默认获取绑定到 target 的组名。

target("test")
    add_tests("testname")
    set_group("foo")
自定义测试脚本

我们还新增了 before_test, on_testafter_test 配置脚本,用户可以在 rule 和 target 域,自定义配置它们实现定制化的测试执行。

target("test")
     on_test(function (target, opt)
        print(opt.name, opt.runenvs, opt.runargs, opt.pass_outputs)

        -- do test
        -- ...

        -- passed
        return true

        -- failied
        return false, errors
     end)

其中,opt 里面可以获取到所有传入 add_tests 的参数,我们在 on_test 里面自定义测试逻辑,然后返回 true 就是测试通过,返回 false 就是测试失败,然后继续返回测试失败的错误信息。

自动化构建

由于测试目标在正常开发构建阶段,通常是不需要被构建的,因此我们会设置 set_default(false)

target("test")
    add_tests("testname")
    set_default(false)

但是运行 xmake test 进行测试时候,这些测试对应的 target 还是会被自动构建,确保能够被运行。

$ xmake test
[ 25%]: cache compiling.release src/main.cpp
[ 50%]: linking.release test
running tests ...
[100%]: test/testname .................................... passed 6.000s

100% tests passed, 0 tests failed out of 1, spent 0.006s
首次测试失败就终止

默认情况下,xmake test 会等到所有测试都运行完,不管里面有多少是没通过的。

而有时候,我们想在第一个测试没通过,就直接中断测试,那么我们可以通过下面的配置启用:

set_policy("test.return_zero_on_failure", true)
测试失败返回0

默认情况下,只要有一个测试没通过,等到 xmake test 运行完成,它都会返回非0退出代码,这对于一些 CI 环境非常有用,可以中断 CI 的其他脚本继续运行。

然后触发信号告诉 CI,我们需要生成测试报告和告警了。

然后,如果我们想要压制这种行为,可以强制将 xmake test 的退出代码总是设置成 0。

set_policy("test.return_zero_on_failure", true)
仅仅测试编译

有时候,我们仅仅想要测试代码是否通过编译,或者没有通过编译,不需要运行它们,那么可以通过配置 build_should_passbuild_should_fail 来实现。

target("test_10")
    set_kind("binary")
    set_default(false)
    add_files("src/compile.cpp")
    add_tests("compile_fail", {build_should_fail = true})

target("test_11")
    set_kind("binary")
    set_default(false)
    add_files("src/compile.cpp")
    add_tests("compile_pass", {build_should_pass = true})

这通常用于一些测试代码中带有 static_assert 的场景,例如:

template <typename T>
bool foo(T val) {
  if constexpr (std::is_same_v<T, int>) {
    printf("int!\n");
  } else if constexpr (std::is_same_v<T, float>) {
    printf("float!\n");
  } else {
    static_assert(false, "unsupported type");
  }
}

int main(int, char**) {
  foo("BAD");
  return 0;
}
配置额外的代码编译

我们还可以在配置测试用例的时候,对每个测试配置额外需要编译的代码,以及一些宏定义,实现内联测试。

xmake 会为每个测试单独编译一个独立的可执行程序去运行它,但这并不会影响到 target 在生产环境的编译结果。

target("test_13")
    set_kind("binary")
    set_default(false)
    add_files("src/test_1.cpp")
    add_tests("stub_1", {files = "tests/stub_1.cpp", defines = "STUB_1"})

target("test_14")
    set_kind("binary")
    set_default(false)
    add_files("src/test_2.cpp")
    add_tests("stub_2", {files = "tests/stub_2.cpp", defines = "STUB_2"})

target("test_15")
    set_kind("binary")
    set_default(false)
    add_files("src/test_1.cpp")
    add_tests("stub_n", {files = "tests/stub_n*.cpp", defines = "STUB_N"})

以 doctest 为例,我们可以在不修改任何 main.cpp 的情况下,外置单元测试:

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

add_requires("doctest")

target("doctest")
    set_kind("binary")
    add_files("src/*.cpp")
    for _, testfile in ipairs(os.files("tests/*.cpp")) do
        add_tests(path.basename(testfile), {
            files = testfile,
            remove_files = "src/main.cpp",
            languages = "c++11",
            packages = "doctest",
            defines = "DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN"})
    end

定义 DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 会引入额外的 main 入口函数,因此我们需要配置 remove_files 去移除已有的 main.cpp 文件。

运行效果如下:

ruki-2:doctest ruki$ xmake test
running tests ...
[ 50%]: doctest/test_1 .................................... failed 0.009s
[100%]: doctest/test_2 .................................... passed 0.009s

50% tests passed, 1 tests failed out of 2, spent 0.019s
ruki-2:doctest ruki$ xmake test -v
running tests ...
[ 50%]: doctest/test_1 .................................... failed 0.026s
[doctest] doctest version is "2.4.11"
[doctest] run with "--help" for options
===============================================================================
tests/test_1.cpp:7:
TEST CASE:  testing the factorial function

tests/test_1.cpp:8: ERROR: CHECK( factorial(1) == 10 ) is NOT correct!
  values: CHECK( 1 == 10 )

===============================================================================
[doctest] test cases: 1 | 0 passed | 1 failed | 0 skipped
[doctest] assertions: 4 | 3 passed | 1 failed |
[doctest] Status: FAILURE!

run failed, exit code: 1
[100%]: doctest/test_2 .................................... passed 0.010s

50% tests passed, 1 tests failed out of 2, spent 0.038s
测试动态库

通常,add_tests 仅用于对可执行程序进行运行测试,运行动态库需要有一个额外的 main 主入口,因此我们需要额外配置一个可执行程序去加载它,例如:


target("doctest_shared")
    set_kind("shared")
    add_files("src/foo.cpp")
    for _, testfile in ipairs(os.files("tests/*.cpp")) do
        add_tests(path.basename(testfile), {
            kind = "binary",
            files = testfile,
            languages = "c++11",
            packages = "doctest",
            defines = "DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN"})
    end

通过 kind = "binary" 可以将每个单元测试改为 binary 可执行程序,并通过 DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 引入 main 入口函数。

这样就能实现动态库目标中外置可运行的单元测试。

新增类型大小检测

在先前的版本中,我们可以通过 check_csnippetsoutput = true 的方式,来实现类型检测。

check_csnippets("INT_SIZE", 'printf("%d", sizeof(int)); return 0;', {output = true, number = true})

但是这种方式,是通过尝试运行测试代码,然后获取运行输出结果,提取类型大小信息。

这对于交叉编译,就不适用了。

在 2.8.5 版本中,我们新增了 check_sizeof 辅助接口,可以通过直接解析测试程序的二进制文件,提取类型大小信息。

由于不需要运行测试,这种方式不仅可以支持交叉编译,而且对检测效率也有极大的提升,使用也更加的简单。

includes("@builtin/check")

target("test")
    set_kind("static")
    add_files("*.cpp")
    check_sizeof("LONG_SIZE", "long")
    check_sizeof("STRING_SIZE", "std::string", {includes = "string"})
$ xmake f -c
checking for LONG_SIZE ... 8
checking for STRING_SIZE ... 24

另外,我也可以通过 target:check_sizeof 在脚本域进行检测。

新增 Apple XROS 平台

苹果在 Xcode15 中新增了 visionOS 设备的构建支持,因此我们也在第一时间对其进行了支持,只需要执行:

$ xmake f -p applexros
$ xmake

就可以完成 visionOS/XROS 平台的构建。

支持代码合并

最后,我们还提供了一个小工具模块,它可以用于快速合并指定 target 里面的所有 c/c++ 和 头文件源码到单个源文件。

会生成类似 sqlite3.c 的这种单源码文件,用户可以根据自己的实际需求来决定是否使用这个功能。

而在做合并的时候,Xmake 会将内部 includes 头文件全部展开,并生成 DAG,通过拓扑排序引入。

默认它会处理所有 target 的合并,例如:

$ xmake l cli.amalgamate
build/tbox.c generated!
build/tbox.h generated!

我们也可以指定合并需要的目标:

$ xmake l cli.amalgamate tbox
build/tbox.c generated!
build/tbox.h generated!

也可以在合并每个源文件时候,指定一个自定义的 unique ID 的宏定义,来处理符号冲突问题。

$ xmake l cli.amalgamate -u MY_UNIQUEU_ID
build/tbox.c generated!
build/tbox.h generated!

如果多个源文件内部有重名符号,就可以判断这个 MY_UNIQUEU_ID 宏是否被定义,如果定义了,说明是在单文件中,就自己在源码中处理下重名符号。

#ifdef MY_UNIQUEU_ID
    // do some thing
#endif

我们也可以指定输出位置:

$ xmake l cli.amalgamate -o /xxx
/xxx/tbox.c generated!
/xxx/tbox.h generated!

新增 windows.manifest.uac 策略

通过这个策略,我们可以快速方便的设置并启用 Windows UAC。

它支持以下几个 Level:

  • invoker: asInvoker
  • admin: requireAdministrator
  • highest: highestAvailable

例如:

set_policy("windows.manifest.uac", "admin")

它等价于设置

if is_plat("windows") then
    add_ldflags("/manifest:embed", {"/manifestuac:level='requireAdministrator' uiAccess='false'"}, {force = true, expand = false})
end

但是更加方便简洁,并且不需要判断平台,其他平台自动忽略。

我们也可以通过 windows.manifest.uac.ui 策略,设置 Windows UAC 的 uiAccess,如果没有设置它,默认是 false。

set_policy("windows.manifest.uac.ui", true)

更新日志

新特性

  • #1452: 支持链接顺序调整,链接组
  • #1438: 支持代码 amalgamation
  • #3381: 添加 xmake test 支持
  • #4276: 支持自定义域 API
  • #4286: 添加 Apple XROS 支持
  • #4345: 支持检测类型大小 sizeof
  • #4369: 添加 windows.manifest.uac 策略

改进

  • #4284: 改进内置 includes 模块

Bugs 修复

  • #4256: 为 vsxmake 生成器修复 c++ modules intellisense

https://tboox.org/cn/2023/11/05/xmake-update-v2.8.5/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/121098.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux下IO模及其特点及select

ftp实现 模拟FTP核心原理&#xff1a;客户端连接服务器后&#xff0c;向服务器发送一个文件。文件名可以通过参数指定&#xff0c;服务器端接收客户端传来的文件&#xff08;文件名随意&#xff09;&#xff0c;如果文件不存在自动创建文件&#xff0c;如果文件存在&#xff0c…

Discourse 如何在 header 上添加 HTML

虽然现在大部分网站都开始支持使用 CDN 的网站校验了。 但还有些网站在你需要他们提供服务的时候要求使用 header 的 meta 数据校验。 Discourse 是可以轻松的实现上面的功能的。 添加方法 选择你的 Discourse 网站下的自定义。 然后在左侧选择你需要添加的主题。 为了方便…

AD9371 官方例程 NO-OS 主函数 headless 梳理(一)

AD9371 系列快速入口 AD9371ZCU102 移植到 ZCU106 &#xff1a; AD9371 官方例程构建及单音信号收发 ad9371_tx_jesd -->util_ad9371_xcvr接口映射&#xff1a; AD9371 官方例程之 tx_jesd 与 xcvr接口映射 AD9371 官方例程 时钟间的关系与生成 &#xff1a; AD9371 官方…

【Java 进阶篇】JSTL 详解

Java JSTL&#xff08;JavaServer Pages Standard Tag Library&#xff09;是用于简化在 JSP 页面上的开发工作的 Java 标签库。它提供了在 JSP 页面上使用的标准标签&#xff0c;可以帮助开发人员更轻松地访问和操作数据&#xff0c;而无需编写大量的 Java 代码。Java JSTL 是…

工业相机基本知识理解:工业相机IO接口,功耗和供电方式

I-input 相机接收外部信号&#xff0c;可用于触发相机&#xff08;硬触发&#xff09;&#xff0c;也可用于定制不同的 功能&#xff0c;例如使用不同信号宽度来改变相机的曝光时间。主要用于现场设 备控制相机使用&#xff0c;常常配合各种传感器使用 O-output 相机输出信号&a…

antd-vue + vue3 实现a-table动态增减行,通过a-from实现a-table行内输入验证

一、效果图 图一&#xff1a;校验效果 二、主要代码 注意&#xff1a; 1、form 与 table 绑定的是同一个数据 tableSource 并且是一个数据&#xff08;ElementUI 需要 对象包数组&#xff09; 2、form用的是 name 绑定 -> :name"[index, vlan_id]" 3、form-i…

【分布式事务】深入探索 Seata 的四种分布式事务解决方案的原理,优缺点以及在微服务中的实现

文章目录 前言一、XA 模式1.1 XA 模式原理1.2 XA 模式的优缺点及应用场景1.3 Seata XA 模式在微服务中的实现 二、AT 模式2.1 Seata AT 模式原理2.2 AT 模式的脏写问题和写隔离3.3 AT 模式的优缺点3.4 Seata AT 模式在微服务中的实现 三、TCC 模式3.1 TCC 模式原理3.2 Seata 的…

内窥镜项目

★ 手持pad内窥镜项目 项目描述&#xff1a;3D电子内窥镜软件项目是一个基于BS&#xff08;浏览器服务器&#xff09;架构的项目&#xff0c;旨在实现对内窥镜设备的远程控制和高级功能操作。该项目允许操作员使用平板电脑手动触摸屏上的按钮、外部按键或脚踏板 来控制内窥镜设…

初阶JavaEE(15)(Cookie 和 Session、理解会话机制 (Session)、实现用户登录网页、上传文件网页、常用的代码片段)

接上次博客&#xff1a;初阶JavaEE&#xff08;14&#xff09;表白墙程序-CSDN博客 Cookie 和 Session 你还记得我们之前提到的Cookie吗&#xff1f; Cookie是HTTP请求header中的一个属性&#xff0c;是一种用于在浏览器和服务器之间持久存储数据的机制&#xff0c;允许网站…

【C++】类和对象(一):什么是面向对象,访问限定符有哪些,类定义细节,结构体和类的关系。

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

机组 指令系统

机器指令 机器指令&#xff1a;每一条机器语言的语句 指令系统&#xff1a;全部机器指令的集合 指令的一般格式 指令由操作码和地址码两部分组成 操作码 作用&#xff1a;指明该指令要完成的操作 位数&#xff1a;反映机器的操作种类&#xff0c;即机器允许的指令条数 …

瞅瞅 Opencv:扫描图像

扫描图像查询表 一、概述二、图像矩阵如何存储在内存中?三、高效的方式四、迭代器(安全)方法五、带引用返回的动态地址计算六、核心功能七、性能差异 一、概述 让我们考虑一种简单的色彩还原方法。通过使用unsigned char C和c类型进行矩阵项存储&#xff0c;一个像素通道可以…

音视频技术开发周刊 | 318

每周一期&#xff0c;纵览音视频技术领域的干货。 新闻投稿&#xff1a;contributelivevideostack.com。 日程揭晓&#xff01;速览深圳站大会专题议程详解 LiveVideoStackCon 2023 音视频技术大会深圳站&#xff0c;保持着往届强大的讲师阵容以及高水准的演讲质量。两天的参会…

git commit规范提交

Git每次提交代码时&#xff0c;都要写Commit Message&#xff08;提交说明&#xff09;&#xff0c;通常情况下&#xff0c;Commit Message应该清晰明了&#xff0c;说明本次提交的目的和具体操作等。然而笔者工作多年来发现&#xff0c;有些公司对Commit Message没有明确的要求…

wpf Grid布局详解 `Auto` 和 `*` 是两种常见的设置方式 行或列占多个单元格,有点像excel里的合并单元格。使其余的列平均分配剩余的空间

比如只有行的界面 <Window x:Class"GenerateTokenApp.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.microsoft.com/exp…

SpringCloudAlibaba——Sentinel

Sentinel也就是我们之前的Hystrix&#xff0c;而且比Hystrix功能更加的强大。Sentinel是分布式系统的流量防卫兵&#xff0c;以流量为切入点&#xff0c;从流量控制、流量路由、熔断降级等多个维度保护服务的稳定性。 Sentinel采用的是懒加载&#xff0c;这个接口被访问一次&a…

企业级低代码开发,科技赋能让企业具备“驾驭软件的能力”

科技作为第一生产力&#xff0c;其强大的影响力在各个领域中都有所体现。数字技术&#xff0c;作为科技领域中的一股重要力量&#xff0c;正在对传统的商业模式进行深度的变革&#xff0c;为各行业注入新的生命力。随着数字技术的不断发展和应用&#xff0c;企业数字化转型的趋…

SpringBoot自动装配 Spring相关 常用设计模式 双亲委派 MongoDB Redis 适配器模式与策略模式

SpringBoot自动装配 阿里云登录 - 欢迎登录阿里云&#xff0c;安全稳定的云计算服务平台 Spring相关 阿里云登录 - 欢迎登录阿里云&#xff0c;安全稳定的云计算服务平台 常用设计模式 双亲委派 Java虚拟机定义了三个主要的类加载器: 1、启动类加载器 2、扩展类加载器 …

《网络协议》02. 物理层 · 数据链路层 · 网络层

title: 《网络协议》02. 物理层 数据链路层 网络层 date: 2022-08-31 22:26:48 updated: 2023-11-08 06:58:52 categories: 学习记录&#xff1a;网络协议 excerpt: 物理层&#xff08;数据通信模型&#xff0c;信道&#xff09;、数据链路层&#xff08;封装成帧&#xff0c…

CSDN中: Markdown编辑器使用说明

Markdown编辑器使用说明 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一…