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") thenadd_syslinks("pthread", "m", "dl")endif is_plat("macosx") thenadd_frameworks("Foundation", "CoreFoundation")endadd_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")) dolocal 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.000s80% 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-- ...-- passedreturn true-- failiedreturn false, errorsend)

其中,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.000s100% 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")) doadd_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.009s50% 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 functiontests/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.010s50% 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")) doadd_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") thenadd_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.mzph.cn/news/137482.shtml

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

相关文章

java传base64返回给数据报404踩坑

一、问题复现 1.可能因为base64字符太长&#xff0c;导致后端处理时出错&#xff0c;表现为前端请求报400错误&#xff1b; 这一步debug进去发现base64数据是正常传值的 所以排除掉不是后端问题,但是看了下前端请求,猜测可能是转换base64时间太长数据过大导致的404 2.前端传…

FPGA设计过程中有关数据之间的并串转化

1.原理 并串转化是指的是完成串行传输和并行传输两种传输方式之间的转换的技术&#xff0c;通过移位寄存器可以实现串并转换。 串转并&#xff0c;将数据移位保存在寄存器中&#xff0c;再将寄存器的数值同时输出&#xff1b; 并转串&#xff0c;将数据先进行移位&#xff0…

AI:74-基于深度学习的宠物品种识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

OpenHarmony,奏响中国基础软件的“光辉岁月”

梦想需要多久的时间&#xff0c;多少血和泪&#xff0c;才能慢慢实现&#xff1f; 天地间任我展翅高飞&#xff0c;谁说那是天真的预言&#xff1f; 《光辉岁月》歌词中的这两个问题&#xff0c;恰好可以送给今天的中国基础软件事业。 曾几何时&#xff0c;我们认为中国基础软件…

使用jdk21预览版 --enable-preview

异常 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.10.1:compile (default-compile) on project sb3: Compilation failure [ERROR] --enable-preview 一起使用时无效 [ERROR] &#xff08;仅发行版 21 支持预览语言功能&#xff09; 解决…

Matlab导出高清图片方法

一、背景 使用matlab绘制图片后&#xff0c;需要将图片导出为.jpg或.eps格式以便后期使用。但通过文件–另存为.jpg时&#xff0c;并没有清晰度选择&#xff0c;导出的图片只有30几k&#xff0c;以至于图片很模糊。 二、Matlab导出高清图片方法 文件—导出设置 1、大小&…

Unreal Engine 学习笔记 (3)—— 导入资源

1.导入FBX文件 打开系统文件管理器按下鼠标左键拖动fbx文件到UE编辑器中松开鼠标左键在弹出对话框FBX导入选项页面中&#xff0c;选择对应的骨骼 重定向骨骼 拖动UE4的walk_strafe_back.fbx文件到UE5编辑器中 在弹出的FBX导入选项对话框中选择UE4对应的骨骼 使用重定向资产…

【Spring】SpringBoot配置文件

SpringBoot配置文件 配置文件作用SpringBoot配置文件配置文件快速入手配置文件的格式properties配置文件说明基本语法读取配置文件properties缺点分析 yml配置文件说明yml基本语法yml使用进阶yml配置读取配置对象配置集合配置Mapyml优缺点 配置文件作用 计算机上有数以千计的配…

如何利用产品帮助中心提升用户体验

在当今竞争激烈的市场中&#xff0c;提供优秀的用户体验是吸引和保留客户的关键。而一个高效和易于使用的产品帮助中心&#xff0c;正成为越来越多企业用以提升用户体验的重要工具。产品帮助中心是一个集中的信息库&#xff0c;为用户提供关于产品功能、故障排除、常见问题解答…

oracle使用regexp_substr来拆分,CONNECT BY LEVEL查询卡死,速度慢的问题。

一、问题 oracle 使用regexp_substrCONNECT BY LEVEL来&#xff0c;根据特定字符拆分成多行。 &#xff08;注意这里我的数据是每个值都有“ ; ”&#xff0c;即使后面没有值&#xff0c;后面也会有个“ ; ”&#xff0c; 如果是正常的分隔符&#xff0c;sql 需要改成” LEVEL…

github遇到想要强制拉取远程仓库内容

进行项目的时候&#xff0c;遇到了我的远程仓库 Sync fork 更新以后&#xff0c;这时候我的本地就和远程不同步&#xff0c;如果使用 git pull 的时候&#xff0c;如果出现 conficts 过多的情况怎么办&#xff0c;如果我们想要直接把远程仓库拉下来应该怎么办&#xff1f; git…

爆肝一文,走进大名鼎鼎的HTTP协议(通俗白话+三万字超详细+抓包工具使用)

文章目录 前言1. HTTP 是什么1.1 HTTP 完整请求流程1.2 理解 HTTP 协议的工作过程 2. HTTP 协议格式2.1 抓包工具的使用2.2 抓包工具的原理2.3 抓包结果2.4 协议格式总结 3. HTTP 请求(Request)3.1 认识 URL(Uniform Resource Locator)URL 基本格式关于 URL encode 3.2 认识请求…

Spring Cloud 微服务入门篇

文章目录 什么是微服务架构 Microservice微服务的发展历史微服务的定义微小的服务微服务 微服务的发展历史1. 微服务架构的发展历史2. 微服务架构的先驱 微服务架构 Microservice 的优缺点1. 微服务 e Microservice 优点2. 微服务 Microservice 缺点微服务不是银弹&#xff1a;…

【hcie-cloud】【5】华为云Stack规划设计之华为云Stack标准化配置、缩略语【下】

文章目录 前言、华为云Stack交付综述为云Stack标准组网华为云Stack标准化配置华为云Stack配置概览华为云Stack云服务全视图华为云Stack部署方案节点类型说明华为云Stack云服务组件部署场景管理节点部署原则云平台管理规格华为云Stack IaaS场景&高阶场景起步必选部署组件x86…

已解决:Rust Error: the trait bound is not satisfied 问题

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页: &#x1f405;&#x1f43e;猫头虎的博客&#x1f390;《面试题大全专栏》 &#x1f995; 文章图文并茂&#x1f996…

Android 进阶——Binder IPC之学习Binder IPC架构及原理概述(十二)

文章大纲 引言一、Binder IPC 基础架构1、Binder IPC核心角色2、Binder IPC的数据流 二、Binder IPC 协议通信流程三、Binder IPC 核心角色详解1、Server 进程及Server 组件2、Client进程及Client组件3、Service Manager 与实名 Binder4、Binder 驱动 四、Binder 通信过程五、开…

mybatis嵌套查询子集合只有一条数据

我们再用mybatis做嵌套查询时&#xff0c;有时会遇到子集合只有1条数据的情况&#xff0c;例如下这样&#xff1a; 数据库查询结果 xml <resultMap id"userMap" type"com.springboot.demo.test.entity.User"><id column"uid" property…

Leetcode150. 逆波兰表达式求值

Every day a Leetcode 题目来源&#xff1a;150. 逆波兰表达式求值 解法1&#xff1a;栈 用栈模拟逆波兰表示法表示的算术表达式的计算过程。 初始化一个栈 stk。 遍历字符串数组 tokens&#xff0c;根据当前字符串 token 决定操作&#xff1a; 若 token 是 1 个算符&am…

基于JavaWeb+SSM+Vue微信小程序校园兼职任务平台系统的设计和实现

基于JavaWebSSMVue微信小程序校园兼职任务平台系统的设计和实现 源码传送入口前言主要技术系统设计功能截图Lun文目录订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码传送入口 前言 随着社会的发展和全球疫情的冲击&#xff0c;大学生的就业形势越来越严峻。越…

基于FPGA的模板匹配红外目标跟踪算法设计

为什么要写这篇文章 我写这篇文章的原因是一天在B站看到了一个大神发的视频是关于跟踪一个无人机的&#xff0c;看到作者跟网友的回复说是用的图像匹配算法&#xff0c;我就在网上搜索相关资料&#xff0c;最终找到一篇文献。文献中对该算法的评价很高&#xff0c;满足制导系统…