# 自定义工具链 {#custom-toolchain} 在2.3.4版本之后,xmake已经支持在用户的项目xmake.lua中自定义工具链,例如: ```lua -- define toolchain toolchain("myclang") -- mark as standalone toolchain set_kind("standalone") -- set toolset set_toolset("cc", "clang") set_toolset("cxx", "clang", "clang++") set_toolset("ld", "clang++", "clang") set_toolset("sh", "clang++", "clang") set_toolset("ar", "ar") set_toolset("ex", "ar") set_toolset("strip", "strip") set_toolset("mm", "clang") set_toolset("mxx", "clang", "clang++") set_toolset("as", "clang") add_defines("MYCLANG") -- check toolchain on_check(function (toolchain) return import("lib.detect.find_tool")("clang") end) -- on load on_load(function (toolchain) -- get march local march = is_arch("x86_64", "x64") and "-m64" or "-m32" -- init flags for c/c++ toolchain:add("cxflags", march) toolchain:add("ldflags", march) toolchain:add("shflags", march) if not is_plat("windows") and os.isdir("/usr") then for _, includedir in ipairs({"/usr/local/include", "/usr/include"}) do if os.isdir(includedir) then toolchain:add("includedirs", includedir) end end for _, linkdir in ipairs({"/usr/local/lib", "/usr/lib"}) do if os.isdir(linkdir) then toolchain:add("linkdirs", linkdir) end end end -- init flags for objc/c++ (with ldflags and shflags) toolchain:add("mxflags", march) -- init flags for asm toolchain:add("asflags", march) end) ``` 然后通过下面的命令切到自己定义的工具链就行了: ```sh $ xmake f --toolchain=myclang ``` 当然,我们也可以通过`set_toolchains`接口直接对指定target切换设置到自定义工具链。 在自定义工具前,我们可以通过先运行以下命令,查看完整的内置工具链列表,确保xmake没有提供,如果有的话,直接使用就行了,没必要自己定义: ```sh $ xmake show -l toolchains ``` ## toolchain - 定义工具链 #### 函数原型 ::: tip API ```lua toolchain(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 工具链名称字符串 | #### 用法说明 可以在用户项目xmake.lua中定义,也可以通过includes独立到单独的xmake.lua去专门定义各种工具链 ```lua toolchain("myclang") set_toolset("cc", "clang") set_toolset("cxx", "clang", "clang++") toolchain_end() ``` - 定义交叉工具链 我们也可以在 xmake.lua 中针对不同的交叉工具链sdk进行自定义配置,通常只需要指定 sdkdir,xmake就可以自动检测其他的配置,比如 cross 等信息,例如: ```lua toolchain("my_toolchain") set_kind("standalone") set_sdkdir("/tmp/arm-linux-musleabi-cross") toolchain_end() target("hello") set_kind("binary") add_files("apps/hello/*.c") ``` 这是一个最精简的交叉工具链配置,仅仅设置了对应的sdk路径,然后通过 `set_kind("standalone")` 将其标记为完整独立的工具链。 这个时候,我们就可以通过命令行 `--toolchain=my_toolchain` 去手动切换到此工具链来使用。 ```sh xmake f --toolchain=my_toolchain xmake ``` 另外,我们还可以直接在 xmake.lua 中通过 `set_toolchains` 将其绑定到对应的 target 上去,那么仅仅只在编译此 target 时候,才会切换到我们自定义的工具链。 ```lua toolchain("my_toolchain") set_kind("standalone") set_sdkdir("/tmp/arm-linux-musleabi-cross") toolchain_end() target("hello") set_kind("binary") add_files("apps/hello/*.c") set_toolchains("my_toolchain") ``` 这样,我们不再需要手动切换工具链了,只需要执行 xmake,就会默认自动切换到 my_toolchain 工具链。 这对于嵌入式开发来讲尤其有用,因为嵌入式平台的交叉编译工具链非常多,我们经常需要各种切换来完成不同平台的编译。 因此,我们可以将所有的工具链定义放置到独立的 lua 文件中去定义,例如: ``` projectdir - xmake.lua - toolchains - my_toolchain1.lua - my_toolchain2.lua - ... ``` 然后,我们只需要再 xmake.lua 中通过 includes 去引入它们,并根据不同的自定义平台,绑定不同的工具链: ```lua includes("toolchains/*.lua") target("hello") set_kind("binary") add_files("apps/hello/*.c") if is_plat("myplat1") then set_toolchains("my_toolchain1") elseif is_plat("myplat2") then set_toolchains("my_toolchain2") end ``` 这样,我们就可以编译的时候,直接快速切换指定平台,来自动切换对应的工具链了。 ```sh xmake f -p myplat1 xmake ``` 如果,有些交叉编译工具链结构复杂,自动检测还不足够,那么可以根据实际情况,使用 `set_toolset`, `set_cross` 和 `set_bindir` 等接口,针对性的配置上其他的设置。 例如下面的例子,我们还额外添加了一些 cxflags/ldflags 以及内置的系统库 links。 ```lua toolchain("my_toolchain") set_kind("standalone") set_sdkdir("/tmp/arm-linux-musleabi-cross") on_load(function (toolchain) -- add flags for arch if toolchain:is_arch("arm") then toolchain:add("cxflags", "-march=armv7-a", "-msoft-float", {force = true}) toolchain:add("ldflags", "-march=armv7-a", "-msoft-float", {force = true}) end toolchain:add("ldflags", "--static", {force = true}) toolchain:add("syslinks", "gcc", "c") end) ``` 更多自定义工具链的例子,我们可以看下面的接口文档,也可以到 xmake 的源码的目录参考内置的工具链定义:[内部工具链列表](https://github.com/xmake-io/xmake/blob/master/xmake/toolchains/) ## set_kind - 设置工具链类型 #### 函数原型 ::: tip API ```lua set_kind(kind: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | kind | 工具链类型: "standalone" | #### 用法说明 目前仅支持设置为`standalone`类型,表示当前工具链是独立完整的工具链,包括cc/cxx/ld/sh/ar等编译器、归档器、链接器等一整套工具集的配置。 通常用于某个target被同时设置了多个工具链的情况,但同时只能生效一个独立工具链,通过此配置可以保证生效的工具链存在互斥关系,比如gcc/clang工具链不会同时生效。 而像yasm/nasm这种局部工具链,属于附加的局部工具链扩展,不用设置standalone,因为clang/yasm两个工具链有可能同时存在。 ::: tip 注意 只要记住,存在完整编译环境的工具链,都设置为standalone就行了 ::: ## set_toolset - 设置工具集 #### 函数原型 ::: tip API ```lua set_toolset(tool: , tools: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | tool | 工具名称字符串 (cc, cxx, ld, sh, ar, ex, strip, mm, mxx, as) | | tools | 工具程序名称字符串或数组 | | ... | 可变参数,可传递多个工具名称 | #### 用法说明 用于设置每个单独工具名和路径,例如: ```lua toolchain("myclang") set_kind("standalone") set_toolset("cc", "clang") set_toolset("cxx", "clang", "clang++") set_toolset("ld", "clang++", "clang") set_toolset("sh", "clang++", "clang") set_toolset("ar", "ar") set_toolset("ex", "ar") set_toolset("strip", "strip") set_toolset("mm", "clang") set_toolset("mxx", "clang", "clang++") set_toolset("as", "clang") ``` 关于这个接口的详情,可以看下:[target.set_toolset](/zh/api/description/project-target.html#set-toolset) ## set_sdkdir - 设置工具链sdk目录路径 #### 函数原型 ::: tip API ```lua set_sdkdir(sdkdir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | sdkdir | SDK目录路径字符串 | #### 用法说明 通常我们可以通过`xmake f --toolchain=myclang --sdk=xxx` 来配置 sdk 目录,但是每次配置比较繁琐,我们也可以通过此接口预先配置到 xmake.lua 中去,方便快速切换使用。 ```lua toolchain("myclang") set_kind("standalone") set_sdkdir("/tmp/sdkdir") set_toolset("cc", "clang") ``` ## set_bindir - 设置工具链bin目录路径 #### 函数原型 ::: tip API ```lua set_bindir(bindir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | bindir | 二进制目录路径字符串 | #### 用法说明 通常我们可以通过`xmake f --toolchain=myclang --bin=xxx`来配置 sdk 目录,但是每次配置比较繁琐,我们也可以通过此接口预先配置到 xmake.lua 中去,方便快速切换使用。 ```lua toolchain("myclang") set_kind("standalone") set_bindir("/tmp/sdkdir/bin") set_toolset("cc", "clang") ``` ## on_check - 检测工具链 #### 函数原型 ::: tip API ```lua on_check(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 检测脚本函数,参数为toolchain | #### 用法说明 用于检测指定工具链所在sdk或者程序在当前系统上是否存在,通常用于多个 standalone 工具链的情况,进行自动探测和选择有效工具链。 而对于 `xmake f --toolchain=myclang` 手动指定的场景,此检测配置不是必须的,可以省略。 ```lua toolchain("myclang") on_check(function (toolchain) return import("lib.detect.find_tool")("clang") end) ``` ## on_load - 加载工具链 #### 函数原型 ::: tip API ```lua on_load(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 加载脚本函数,参数为toolchain | #### 用法说明 对于一些复杂的场景,我们可以在 on_load 中动态灵活的设置各种工具链配置,比在描述域设置更加灵活,更加强大: ```lua toolchain("myclang") set_kind("standalone") on_load(function (toolchain) -- set toolset toolchain:set("toolset", "cc", "clang") toolchain:set("toolset", "ld", "clang++") -- init flags local march = toolchain:is_arch("x86_64", "x64") and "-m64" or "-m32" toolchain:add("cxflags", march) toolchain:add("ldflags", march) toolchain:add("shflags", march) end) ``` ## toolchain_end - 结束定义工具链 #### 函数原型 ::: tip API ```lua toolchain_end() ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | - | 无参数 | #### 用法说明 这个是可选的,如果想要手动结束 toolchain 的定义,可以调用它: ```lua toolchain("myclang") -- .. toolchain_end() ```