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

news2024/10/6 8:37:37

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/

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题

前言 本节内容是关于使用分布式锁解决并发访问“超卖”问题的最终篇&#xff0c;在前面的章节中我们介绍了使用mysql的行锁、乐观锁、悲观锁解决并发访问导致的超卖问题&#xff0c;存在的问题是行锁、乐观锁、悲观锁不太灵活&#xff0c;需要和具体的业务耦合到一起&#xff…

盘点几种常用加密算法

文章目录 前言常用算法DES算法DES算法特点DES算法示例 AES算法AES算法特点AES算法示例 RSA算法RSA算法特点RSA算法示例 MD5算法MD5算法特点MD5算法示例 SHA算法SHA算法特点SHA算法示例 总结写在最后 前言 随着互联网的发展,信息安全问题日益受到重视。加密算法在保证信息安全传…

力扣算法-----一刷总结

之前学习算法题坚持不了几天就很容易放弃&#xff0c;一直没怎么系统的练习&#xff0c;偶然发现代码随想录居然推出了算法训练营&#xff0c;趁着时间比较足报了名跟着学习了两个月。 过去的两个月&#xff0c;中间伴着各种琐事&#xff0c;但还是坚持了下来&#xff0c;走过…

网络安全之CSRF漏洞原理和实战,以及CSRF漏洞防护方法

一、引言 总体来说CSRF属于一种欺骗行为&#xff0c;是一种针对网站的恶意利用&#xff0c;尽管听起来像跨站脚本&#xff08;XSS&#xff09;&#xff0c;但是与XSS非常不同&#xff0c;并且攻击方式几乎向佐。XSS利用站点内的信任用户&#xff0c;而CSRF则通过伪装来自受信任…

如何使用ArcGIS Pro制作个性三维地形图

制作三维地图制作的多了&#xff0c;想着能不能换个“口味”&#xff0c;恰好看见制作六边形蜂窝图&#xff0c;灵光一闪&#xff0c;想着将二者结合&#xff0c;将平滑的三维地形图改成柱状图&#xff0c;从结果来看还可以&#xff0c;这里将制作方法分享给大家&#xff0c;希…

基于51单片机太阳能热水器控制系统-proteus仿真-程序

、系统方案 系统设计将软件设计内容分为了六大模块&#xff0c;分别是蜂鸣器报警、水位检测、DS18B20模块、液晶显示、加热模块、按键模块&#xff0c;系统将其进行分别设计&#xff0c;接通电源之后&#xff0c;单片机分别向LCD1602液晶显示器、DS18B20模块、和按键发出初始化…

C【整数正序分解】

// 整数正序分解 #include <stdio.h> #include <stdlib.h>int main() {int x;scanf("%d", &x);// 13425/10000->1(int一个d)// 13425%10000->3425(这是x)// 10000/10-.1000(这是mask)int mask 1;int t x;while (t > 9){t / 10;mask * 10;…

安装dubbo-admin报错node版本和test错误

✅作者简介&#xff1a;CSDN内容合伙人、信息安全专业在校大学生&#x1f3c6; &#x1f525;系列专栏 &#xff1a;dubbo-admin安装 &#x1f4c3;新人博主 &#xff1a;欢迎点赞收藏关注&#xff0c;会回访&#xff01; &#x1f4ac;舞台再大&#xff0c;你不上台&#xff0…

JAVA安全之Log4j-Jndi注入原理以及利用方式

什么是JNDI&#xff1f; JDNI&#xff08;Java Naming and Directory Interface&#xff09;是Java命名和目录接口&#xff0c;它提供了统一的访问命名和目录服务的API。 JDNI主要通过JNDI SPI&#xff08;Service Provider Interface&#xff09;规范来实现&#xff0c;该规…

matlab中实现画函数图像添加坐标轴

大家好&#xff0c;我是带我去滑雪&#xff01; 主函数matlab代码&#xff1a; function PlotAxisAtOrigin(x,y); if nargin 2 plot(x,y);hold on; elsedisplay( Not 2D Data set !) end; Xget(gca,Xtick); Yget(gca,Ytick); XLget(gca,XtickLabel); YLget(gca,YtickLabel)…

csdn初始模板【自用】

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

C++ Qt 学习(五):Qt Web 编程

1. Chrome 技术介绍 大多数 web 技术都是基于 chrome&#xff0c;例如 CEF、QCefView 以及 QWebEngineView&#xff0c;这些都是在 native 界面里用来显示 html 网页&#xff0c;并且可以与 web 交互 例如常见的登录窗口、优酷的视频区域、WPS 的稻壳商城等&#xff0c;这些都…

npm 下载包失败解决方案

1.【问题描述】使用 npm 下载vue项目依赖包时失败&#xff0c;版本不一致。 【解决方法】使用 npm install --force npm install --force 是一个命令行指令&#xff0c;用于在 Node.js 环境中使用 npm&#xff08;Node Package Manager&#xff09;安装包或模块。–force 参数表…

Apipost-Helper:IDEA中的类postman工具

今天给大家推荐一款IDEA插件&#xff1a;Apipost-Helper-2.0&#xff0c;写完代码IDEA内一键生成API文档&#xff0c;无需安装、打开任何其他软件&#xff1b;写完代码IDEA内一键调试&#xff0c;无需安装、打开任何其他软件&#xff1b;生成API目录树&#xff0c;双击即可快速…

AirTag追踪汽车

美国华盛顿特区&#xff0c;11月4日&#xff0c;在一项全新的抗击车辆盗窃的措施中&#xff0c;市长穆里尔•鲍泽签署了一项新计划&#xff0c;将向该市车辆盗窃频率较高的社区居民免费提供苹果AirTag追踪器。 AirTag是苹果公司推出的一款蓝牙跟踪设备&#xff0c;它依靠Findm…

JAVA将List转成Tree树形结构数据和深度优先遍历

引言&#xff1a; 在日常开发中&#xff0c;我们经常会遇到需要将数据库中返回的数据转成树形结构的数据返回&#xff0c;或者需要对转为树结构后的数据绑定层级关系再返回&#xff0c;比如需要统计当前节点下有多少个节点等&#xff0c;因此我们需要封装一个ListToTree的工具类…

Redis 键值类型及其存储结构

Redis 键值类型及其存储结构 键值类型 键的数据类型是字符串&#xff0c;值的类型有&#xff1a;字符串、列表、Hash、集合、有序集合。 键的存储和查找 Redis底层键的存储类似于Java中其他Hash存储结构&#xff1a;数组链表的组合。数组中存储的是Key Hash函数对数组长度取模…

《深入理解计算机系统》书籍学习笔记 - 第二课 - 位,字节和整型

Lecture 02 Bits,Bytes, and Integer 位&#xff0c;字节和整型 文章目录 Lecture 02 Bits,Bytes, and Integer 位&#xff0c;字节和整型Byte 字节位操作布尔代数集合的表现形式和操作C语言的逻辑操作 位移操作整型数值范围无符号与有符号数值无符号与有符号在C中 拓展和截断拓…

个人网厅——提取

目录 需求文档 公积金提取类 controller层 service层 service层实现类 1.验证&#xff08;个人账户&#xff09; 2.提交&#xff08;添加&#xff09; controller层 service层 service层实现类 3.分页查询 controller层 service层 service层实现类 4.详情查询 co…

键盘打字盲打练习系列之认识键盘——0

一.欢迎来到我的酒馆 盲打&#xff0c;yyds&#xff01; 目录 一.欢迎来到我的酒馆二.开始练习 二.开始练习 经常看视频&#xff0c;看到别人在键盘上一通干净利索的操作&#xff0c;就打出想要的文字。心里突然来一句&#xff1a;卧槽&#xff0c;打字贼快啊&#xff01;思索下…