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 供给了一种 TryBuild 形式,能够在没有 xmake.lua 的状况下,运用 Xmake 测验对 autoconf/cmake/meson 等保护的第三方项目进行直接构建。

其实,也便是让 Xmake 检测到对应的构建体系后,调用 cmake 等命令来完结,可是会协助用户简化装备操作,其他还能对接 xmake 的穿插编译东西链装备。

可是,这种形式有必定的失利率,比如以下一些状况,都会或许导致构建失利:

  1. 项目代码自身存在缺点,导致编译过错
  2. 项目代码不支撑当时渠道
  3. 构建脚本存在缺点
  4. 短少特定的装备参数
  5. 短少依靠库,需求用户手动装置
  6. 编译器版别太低,不支撑部分代码

而 TryBuild 形式一般处理这些状况,可是在新版别中,咱们对 TryBuild 形式引进了一种新的机制,经过复用 xmake-repo 库房中的构建脚本,来改善构建逻辑。

它大约得处理流程是这样子的:

  1. 在第三方源码库目录履行 xmake 命令
  2. Xmake 获取目录名,测验解析项目名和版别
  3. 测验从 xmake-repo 库房匹配现有的包
  4. 假如匹配成功,直接选用包中构建逻辑来构建
  5. 假如没匹配成功,回退到本来的 TryBuild 逻辑

这能带来什么优点呢,假如匹配成功,咱们能够处理上面说到的各种问题。

即使当时项目源码不支撑指定渠道,或者源码和构建脚本存在必定的缺点,Xmake 也能主动打入特定 patch 去修正它,并引进需求的依靠包,保证它必定能够一键编译经过。

咱们运用 libjpeg 库为例,来直观的感受下。

首先是下载对应源码包

$ wget https://jaist.dl.sourceforge.net/project/libjpeg-turbo/2.1.4/libjpeg-turbo-2.1.4.tar.gz
$ tar -xvf libjpeg-turbo-2.1.4.tar.gz
$ cd libjpeg-turbo-2.1.4

然后进入目录履行 Xmake 命令

Xmake 假如检测到是 libjpeg 库,就会提示用户,是否作为 libjpeg 2.1.4 来构建。

ruki-2:libjpeg-turbo-2.1.4 ruki$ xmake
note: libjpeg-turbo 2.1.4 in xmake-repo found, try building it or you can run `xmake f --trybuild=` to set buildsystem (pass -y or --confirm=y/n/d to skip confirm)?
please input: y (y/n)

咱们按下回车键承认持续构建。

checking for cmake ... /usr/local/bin/cmake
/usr/local/bin/cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_SHARED=OFF -DENABLE_STATIC=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_INSTALL_LIBDIR:PATH=lib -DCMAKE_INSTALL_PREFIX=/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2 -G "Unix Makefiles" -DCMAKE_POSITION_INDEPENDENT_CODE=ON /Users/ruki/Downloads/libjpeg-turbo-2.1.4
-- CMAKE_BUILD_TYPE = Release
-- VERSION = 2.1.4, BUILD = 20220923
-- 64-bit build (x86_64)
-- CMAKE_INSTALL_PREFIX = /Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2
-- CMAKE_INSTALL_BINDIR = bin (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/bin)
-- CMAKE_INSTALL_DATAROOTDIR = share (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/share)
-- CMAKE_INSTALL_DOCDIR = share/doc/libjpeg-turbo (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/share/doc/libjpeg-turbo)
-- CMAKE_INSTALL_INCLUDEDIR = include (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/include)
-- CMAKE_INSTALL_LIBDIR = lib (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/lib)
-- CMAKE_INSTALL_MANDIR = share/man (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/share/man)
-- Shared libraries disabled (ENABLE_SHARED = 0)
-- Static libraries enabled (ENABLE_STATIC = 1)
-- 12-bit JPEG support disabled (WITH_12BIT = 0)
-- Arithmetic decoding support enabled (WITH_ARITH_DEC = 1)
-- Arithmetic encoding support enabled (WITH_ARITH_ENC = 1)
-- TurboJPEG API library enabled (WITH_TURBOJPEG = 1)
-- TurboJPEG Java wrapper disabled (WITH_JAVA = 0)
-- In-memory source/destination managers enabled (WITH_MEM_SRCDST = 1)
-- Emulating libjpeg API/ABI v6.2 (WITH_JPEG7 = 0, WITH_JPEG8 = 0)
-- libjpeg API shared library version = 62.3.0
-- Compiler flags =  -O3 -DNDEBUG
-- Linker flags =
-- INLINE = __inline__ __attribute__((always_inline)) (FORCE_INLINE = 1)
-- THREAD_LOCAL = __thread
-- CMAKE_EXECUTABLE_SUFFIX =
-- CMAKE_ASM_NASM_COMPILER = /usr/local/bin/nasm
-- CMAKE_ASM_NASM_OBJECT_FORMAT = macho64
-- CMAKE_ASM_NASM_FLAGS =  -DMACHO -D__x86_64__ -DPIC
-- SIMD extensions: x86_64 (WITH_SIMD = 1)
-- FLOATTEST = sse
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/ruki/Downloads/libjpeg-turbo-2.1.4/build_646b7957
make -j10
[  2%] Built target md5cmp
[ 19%] Built target wrjpgcom
[ 20%] Built target simd
[ 21%] Built target strtest
[ 22%] Built target rdjpgcom
[ 80%] Built target jpeg-static
[ 84%] Built target turbojpeg-static
[ 90%] Built target tjbench-static
[ 90%] Built target tjunittest-static
[ 91%] Built target jpegtran-static
[ 98%] Built target djpeg-static
[100%] Built target cjpeg-static
make install
[  1%] Built target strtest
[  3%] Built target wrjpgcom
[ 19%] Built target simd
[ 52%] Built target turbojpeg-static
[ 53%] Built target rdjpgcom
[ 82%] Built target jpeg-static
[ 85%] Built target jpegtran-static
[ 90%] Built target djpeg-static
[ 93%] Built target tjunittest-static
[ 97%] Built target cjpeg-static
[ 98%] Built target tjbench-static
[100%] Built target md5cmp
Install the project...
exporting libjpeg-turbo-2.1.4
  -> /Users/ruki/Downloads/libjpeg-turbo-2.1.4/build/artifacts/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2
output to /Users/ruki/Downloads/libjpeg-turbo-2.1.4/build/artifacts
build ok!

只要检测匹配成功,一般必定能够完结编译,成功率接近 100%,最后 Xmake 会将编译产品输出到当时目录的 build/artifacts 下面。

对接穿插编译东西链

这种智能构建形式,咱们不仅能够编译本机程序,还能够对接穿插编译东西链,完结对 ios/android 以及任意穿插编译渠道的支撑。

例如,编译 Android 渠道,咱们只需求传递 --trybuild=xrepo 参数,然后切换到 android 渠道即可,Xmake 会透传一切 ndk 东西链信息。

$ xmake f -p android --trybuild=xrepo --ndk=~/files/android-ndk-r20b -c
$ xmake
xmake f -c --require=n -v -p android -a armeabi-v7a -m release -k static --ndk=/Users/ruki/files/android-ndk-r20b
checking for Android SDK directory ... ~/Library/Android/sdk
checking for Build Tools Version of Android SDK ... 33.0.0
checking for NDK directory ... /Users/ruki/files/android-ndk-r20b
checking for SDK version of NDK ... 21
checking for clang++ ... /Users/ruki/files/android-ndk-r20b/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++
checking for the shared library linker (sh) ... clang++
checking for clang++ ... /Users/ruki/files/android-ndk-r20b/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++
checking for the linker (ld) ... clang++
...
exporting libjpeg-turbo-2.1.4
  -> /Users/ruki/Downloads/libjpeg-turbo-2.1.4/build/artifacts/l/libjpeg-turbo/2.1.4/79c2e21f436b4ab08a3c23a6cbae8c0e
output to /Users/ruki/Downloads/libjpeg-turbo-2.1.4/build/artifacts
build ok!

回退到直接编译

假如咱们不想运用 xmake-repo 的构建脚本,咱们也能回退到 cmake/autoconf 直接去测验构建它们。

可是这样或许会存在必定的失利率,并且有或许会额定编译一些不需求的二进制方针。而 xmake-repo 里边的构建脚本是最优化的,精简了许多没必要的构建参数,比如禁用 tests/examples 构建等等。

咱们只需求先敲 n 撤销基于包脚本的智能构建形式,Xmake 会有新的提示,让用户挑选是否持续选用 cmake/autoconf 来测验构建。

$ xmake
note: libjpeg-turbo 2.1.4 in xmake-repo found, try building it or you can run `xmake f --trybuild=` to set buildsystem (pass -y or --confirm=y/n/d to skip confirm)?
please input: y (y/n)
n
note: CMakeLists.txt found, try building it or you can run `xmake f --trybuild=` to set buildsystem (pass -y or --confirm=y/n/d to skip confirm)?
please input: y (y/n)

支撑 Windows Arm64

新版别咱们还对 Windows 的构建支撑做了改善,新增了 Windows Arm64 渠道支撑,只需求切换到 arm64 架构即可。

$ xmake f -a arm64
$ xmake

改善规矩支撑依靠次序履行

关联依靠能够绑定一批规矩,也便是不必对 target 挨个去运用 add_rules() 增加规矩,只需求运用一个规矩,就能生效它和它的一切依靠规矩。

例如:

rule("foo")
    add_deps("bar")
rule("bar")
   ...

咱们只需求 add_rules("foo"),就能一同运用 foo 和 bar 两个规矩。

可是,默许状况下,依靠之间是不存在履行的先后次序的,foo 和 bar 的 on_build_file 等脚本是并行履行的,次序未界说。

假如要严厉操控履行次序,在新版别中,咱们能够装备 add_deps("bar", {order = true}),告诉 xmake,咱们需求依据依靠次序来履行同级其他脚本。

例如:

rule("foo")
    add_deps("bar", {order = true})
    on_build_file(function (target, sourcefile)
    end)
rule("bar")
    on_build_file(function (target, sourcefile)
    end)

bar 的 on_build_file 将会被先履行。

更好的动态装备方针和规矩

上面这种操控规矩依靠的方式,只适合 foo 和 bar 两个规矩都是自界说规矩,假如想要将自己的规矩插入到 xmake 的内置规矩之前履行,这就不适用了。

这个时分,咱们需求运用愈加灵敏的动态规矩创建和注入的方式,去修正内置规矩。

例如,咱们想在内置的 c++.build 规矩之前,履行自界说 cppfront 规矩的 on_build_file 脚本,咱们能够经过下面的方式来完结。

rule("cppfront")
    set_extensions(".cpp2")
    on_load(function (target)
        local rule = target:rule("c++.build"):clone()
        rule:add("deps", "cppfront", {order = true})
        target:rule_add(rule)
    end)
    on_build_file(function (target, sourcefile, opt)
        print("build cppfront file")
    end)
target("test")
    set_kind("binary")
    add_rules("cppfront")
    add_files("src/*.cpp")
    add_files("src/*.cpp2")

支撑从包中引进自界说规矩

现在,咱们还能够在包办理库房中,增加自界说构架规矩脚本,完结跟从包进行动态下发和装置。

咱们需求将自界说规矩放到库房的 packages/x/xxx/rules 目录中,它会跟从包一同被装置。

当然,它也存在一些约束:

  • 在包中规矩,咱们不能增加 on_load, after_load 脚本,可是一般咱们能够运用 on_config 来代替。

增加包规矩

咱们需求将规矩脚本增加到 rules 固定目录下,例如:packages/z/zlib/rules/foo.lua

rule("foo")
    on_config(function (target)
        print("foo: on_config %s", target:name())
    end)

运用包规矩

运用规矩的方式跟之前相似,唯一的差异便是,咱们需求经过 @packagename/ 前缀去指定访问哪个包里边的规矩。

详细格局:add_rules("@packagename/rulename"),例如:add_rules("@zlib/foo")

add_requires("zlib", {system = false})
target("test")
    set_kind("binary")
    add_files("src/*.cpp")
    add_packages("zlib")
    add_rules("@zlib/foo")

经过包别号引证规矩

假如存在一个包的别号,xmake 将优先考虑包的别号来取得规矩。

add_requires("zlib", {alias = "zlib2", system = false})
target("test")
    set_kind("binary")
    add_files("src/*.cpp")
    add_packages("zlib2")
    add_rules("@zlib2/foo")

增加包规矩依靠

咱们能够运用add_deps("@bar")来增加相关于当时包目录的其他规矩。

然而,咱们不能增加来自其他包的规矩依靠,它们是彻底阻隔的,咱们只能参阅用户项目中由add_requires导入的其他包的规矩。

packages/z/zlib/rules/foo.lua

rule("foo")
    add_deps("@bar")
    on_config(function (target)
        print("foo: on_config %s", target:name())
    end)

packages/z/zlib/rules/bar.lua

rule("bar")
    on_config(function (target)
        print("bar: on_config %s", target:name())
    end)

愈加严厉的包依靠兼容性支撑

咱们新增了两个包相关的战略,用于敞开愈加严厉的包依靠兼容性操控。

这首要用于处理一些包每次版别更新,或许都会存在一些 abi 不兼容,或者损坏其他依靠它的包,而默许 Xmake 是不会去从头编译装置它们的,除非它们的版别和装备也被更新了。

这就或许存在必定概率编译兼容性被损坏,导致最终链接失利。

package.librarydeps.strict_compatibility

默许禁用,假如启用它,那么当时包和它的一切库依靠包之间会坚持严厉的兼容性,任何依靠包的版别更新,都会强制触发当时包的从头编译装置。

以保证一切的包都是二进制兼容的,不会由于某个依靠包接口改动,导致和其他已被装置的其他包一同链接时分,产生链接和运转过错。

package("foo")
    add_deps("bar", "zoo")
    set_policy("package.librarydeps.strict_compatibility", true)

例如,假如 bar 或者 zoo 的版别有更新,那么 foo 也会从头编译装置。

package.strict_compatibility

默许禁用,假如启用它,那么当时包和其他一切依靠它的包之间会坚持严厉的兼容性,这个包的版别更新,都会强制触发其他父包的从头编译装置。

以保证一切的包都是二进制兼容的,不会由于某个依靠包接口改动,导致和其他已被装置的其他包一同链接时分,产生链接和运转过错。

package("foo")
    set_policy("package.strict_compatibility", true)
package("bar")
    add_deps("foo")
package("zoo")
    add_deps("foo")

例如,假如 foo 的版别有更新,那么 bar 和 zoo 都会被强制从头编译装置。

package.install_always

每次运转 xmake f -c 从头装备的时分,总是会从头装置包,这关于本地第三方源码包集成时分比较有用。

由于,用户或许随时需求修正第三方源码,然后从头编译集成它们。

之前只能经过每次修正包版别号,来触发从头编译,可是有了这个战略,就能每次都会触发重编。

add_rules("mode.debug", "mode.release")
package("foo")
    add_deps("cmake")
    set_sourcedir(path.join(os.scriptdir(), "foo"))
    set_policy("package.install_always", true)
    on_install(function (package)
        local configs = {}
        table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:debug() and "Debug" or "Release"))
        table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF"))
        import("package.tools.cmake").install(package, configs)
    end)
    on_test(function (package)
        assert(package:has_cfuncs("add", {includes = "foo.h"}))
    end)
package_end()
add_requires("foo")
target("demo")
    set_kind("binary")
    add_files("src/main.c")
    add_packages("foo")

新增 clang-cl 东西链

尽管之前的版别,咱们也支撑切换到 clang-cl 编译器,可是切换比较繁琐,得挨个设置。

$ xmake f --cxx=clang-cl --cc=clang-cl -c
$ xmake

而且还得将 clang-cl.exe 地点目录参加 %PATH% 才行。

已然现在 vs 都自带了 clang-cl 东西链,那么 Xmake 彻底能够主动检测到并运用它。

因而,在新版别中,咱们新增了 clang-cl 东西链,只是只需求 xmake f --toolchain=clang-cl 就能够快速切换到 clang-cl 东西链,而无需任何 PATH 设置。

更新内容

新特性

  • #2140: 支撑 Windows Arm64
  • #2719: 增加 package.librarydeps.strict_compatibility 战略严厉约束包依靠兼容性
  • #2810: 支撑 os.execv 去履行 shell 脚本文件
  • #2817: 改善规矩支撑依靠次序履行
  • #2824: 传递 cross-file 穿插编译环境给 meson.install 和 trybuild
  • #2856: xrepo 支撑从当时指定源码目录调试程序
  • #2859: 改善对三方库的 trybuild 构建,使用 xmake-repo 库房脚本愈加智能化地构建三方库
  • #2879: 更好的动态创建和装备 target 和 rule
  • #2374: 答应 xmake 包中引进自界说规矩
  • 增加 clang-cl 东西链

改善

  • #2745: 改善 os.cp 支撑符号链接仿制
  • #2773: 改善 vcpkg 包装置,支撑 freebsd 渠道
  • #2778: 改善 xrepo.env 支撑 target 的运转环境加载
  • #2783: 增加摘要算法选项到 WDK 的 signtool 签名东西
  • #2787: 改善 json 支撑空数组
  • #2782: 改善查找 matlib sdk 和运转时
  • #2793: 改善 mconfdialog 装备操作体验
  • #2804: 装置依靠包支撑 macOS arm64/x86_64 穿插编译
  • #2809: 改善 msvc 的编译优化选项
  • 改善 trybuild 形式,为 meson/autoconf/cmake 供给更好的穿插编译支撑
  • #2846: 改善对 configfiles 的生成
  • #2866: 更好地操控 rule 规矩履行次序

Bugs 修正

  • #2740: 修正 msvc 构建 C++ modules 卡死问题
  • #2875: 修正构建 linux 驱动过错
  • #2885: 修正 ccache 下,msvc 编译 pch 失利问题

tboox.org/cn/2022/10/…