Xmake 常见问题解答
如果在本文没找到解答,可以去 github 提 issue 和讨论区搜索。
如果是官方包管理的问题,或者想请求加入一些包,去 xmake-repo 提 issue。
提 issue 请附带 log,
xmake -vD
命令可以输出详细的 log 信息。
基础
避免外网访问
xmake 很多操作需要访问 github(拉取 xmake-repo/build-artifact),而在公司内网开发是不需要这些操作的。
xmake g --network=private
参考:
Windows 上 Qt 项目控制台没有输出
使用:
add_ldflags("/subsystem:console")
xmake 的 qt rule 里会检测 ldflags 里有没有/subsystem
,如果没有就默认/subsystem:windows
。
给单独源文件添加参数
add_files("test/*.c", "test2/test2.c", {defines = "TEST2", languages = "c99", includedirs = ".", cflags = "-O0"})
-- 强制禁用 cxflags,cflags 等编译选项的自动检测
add_files("src/*.c", {force = {cxflags = "-DTEST", mflags = "-framework xxx"}})
给指定文件添加 config
target("test")
add_files("test/*.cpp", {foo = 1})
on_config(function (target)
-- configs = {foo = 1}
local configs = target:fileconfig("src/main.cpp")
end)
找不到 vs 工具链
在 Visual Studio Installer 修改了配置后,清除全局缓存。
xmake g -c
xmake f -c
参考:
- 显示找不到 Microsoft Visual Studio (x64) version
- xmake can not find visual studio
- XMake无法自动发现MSVC编译工具
指定 vs 版本
xmake f --vs=2017 --vs_toolset=14.0 --vs_sdkver=10.0.15063.0
如果想给单个 target 指定版本:
set_toolchains("msvc", {vs_toolset = "14.29.30133"})
IDE(vscode/vs)不显示头文件
给 target 加上add_headerfiles("**.h")
终端不支持色彩输出
切换主题。
xmake g --theme=plain
如何在脚本域添加文件
只能在on_load
添加,并且需要确保路径正确。
因为描述域的add_files()
和脚本域的target:add("files")
有一些差异,并不会做路径转换。
后者接口可能会在任何地方使用,所以路径只能用os.projectdir
或者os.scriptdir
进行拼接路径。
此外,需要确保该 target 已经应用了一个编译规则,否则不会根据后缀名去添加文件(可能会出现error: unknown source file: xxx.cpp
)。
比如你在脚本域添加 .cpp 文件,那么首先在描述域添加add_rules("c++")
。
增量配置
假如我们做以下操作:
xmake f -m debug --test=y
xmake f --test=n
你会发现当前mode
又切换了为默认配置的release
。
xmake 暂时没有支持增量配置,这里有过相关讨论。
但我们也有解决的方法,使用SirLynix
写的插件。
xmake plugin --install https://github.com/SirLynix/xmake-plugins
用xmake config-update
或者xmake cu
代替原来的xmake config
或者xmake f
。
在不同脚本域之间传递数据
memcache
target("foo")
on_load(function (target)
import("core.cache.memcache")
memcache.set("cachename", "key", "value")
end)
target("bar")
on_load(function (target)
import("core.cache.memcache")
local memcache.get("cachename", "key")
end)
target:data()
target("foo")
on_load(function (target)
target:data_set("key", "value")
end)
target("bar")
on_load(function (target)
import("core.project.project")
local foo = project.targets()["foo"]
local value = foo:data("key")
end)
- 单个脚本文件
target("foo")
on_load(function (target)
import("load_module").load_foo(target)
end)
target("bar")
on_load(function (target)
import("load_module").load_bar(target)
end)
load_module.lua
function load_foo(target)
_g.key = "value"
end
function load_bar(target)
local value = _g.key
end
参考:
自动化
如何一键编译运行
2.8.3 版本以前:
在 target 下加入以下代码:
before_run(function (target)
os.execv("xmake build " .. target:name())
end)
-- xmake run target
2.8.3 版本以后:
$ xmake g --policies=run.autobuild
vs 工程
- 自动更新 sln。
add_rules("plugin.vsxmake.autoupdate")
- 使用
add_filegroups
可以打平嵌套过深的目录树,可读性更高。
单元测试
2.8.4 版本后官方支持单元测试,具体例子看:https://github.com/xmake-io/xmake/issues/3381
以前的版本可以考虑这样写:
rule("module.test")
on_load(function (target)
if not has_config("test") then
target:set("enabled", false)
return
end
target:set("policy", "build.warning", true)
target:set("rundir", "$(projectdir)")
target:set("group", "test")
-- 选择你想要的单元测试库
target:add("packages", "gtest")
end)
rule_end()
add_rules("module.test")
-- 假设 test 目录下每个 cpp 文件都有自己的 main 函数
for _, file in ipairs(os.files("test/*.cpp")) do
local name = path.basename(file)
target("test." .. name)
set_kind("binary")
add_files(file)
target_end()
end
然后我们使用下面命令:
xmake f -m debug --test=y
xmake build -g test
xmake run -g test
如果想一行xmake test
命令搞定,使用task
。
task("test")
on_run(function ()
os.exec("xmake f -m debug --test=y")
os.exec("xmake build -g test")
os.exec("xmake run -g test")
end)
set_menu{}
自定义检测行为
xmake 在检测各种工具、配置等等信息时都会有缓存,我们可以自定义一个检测行为并对其缓存。
on_config(function (target)
import("core.cache.detectcache")
local has_deprecated_key = "custom.has_deprecated"
local has_deprecated = detectcache:get(has_deprecated_key)
if not has_deprecated then
has_deprecated = target:check_csnippets({
has_deprecated = [[
#define TEST __declspec(deprecated)
int somefunc() { return 0; }
int main() { return somefunc();}
]]
})
detectcache:set(has_deprecated_key, has_deprecated)
detectcache:save()
end
-- has_deprecated is now cached
end)
包管理
依赖同一个包多个版本
有依赖包 A 和 B,A 包依赖 1.0.0 版本的 C 包,B 包依赖 1.1.0 版本的 C 包,如果继续编译的话,可能会链接失败,解决方法就是打平依赖,只依赖 C 包同一个版本。
add_requireconfs("B.C", {version = "1.0.0", override = true})
这里把 C 包统一到 1.0.0 版本。
参考
vcpkg 包不会自动处理依赖
暂时不支持,最好去用官方包
参考
如何调试包源码
xmake 早期是保留源码的,但每次安装都保留源码和编译产物,容易把磁盘用满,所以现在优化了,不再保留。
# -d 指定源码覆盖安装,本地源码调试包
xrepo install -m debug -d package_sourcedir xxx
有些远程包为什么没有拉预编译版本
众所周知 c++ 编译器编译出的二进制有 abi 问题,同一个编译器不同版本都可能不兼容,所以当预编译库的编译器版本(github ci 编译)和本地编译器版本不一致的时候,会拉取源码进行编译。
有些库很大,编译需要很久,可以考虑下面方案:
-
比较推荐通过
set_base("package")
继承包,然后覆盖包的一些设置,跳过 xmake 的检查直接安装二进制包。这里可能需要对 xmake 有一定的基础,不过完成后都是自动化,这是值得的。 -
手动下载预编译包到本地包仓库
假设预编译包目录和xmake.lua
同一个目录,使用on_fetch
进行配置。
这里拿 glfw 作为参考。
我这里下载并解压了glfw-3.3.8.bin.WIN64.zip
,然后在xmake.lua
这样写:
-- 演示操作,不一定正确
package("glfw")
on_load(function (package)
-- set package dir
package:set("installdir", path.join(os.scriptdir(), "glfw-3.3.8.bin.WIN64"))
end)
on_fetch(function (package)
-- add dll
package:addenv("PATH", package:installdir("lib-vc2022"))
local result = {}
if is_plat("windows") then
result.syslinks = {"user32", "shell32", "gdi32"}
end
result.links = {"glfw3", "glfw3_mt", "glfw3dll"}
result.includedirs = package:installdir("include")
result.linkdirs = package:installdir("lib-vc2022")
return result
end)
package_end()
使用:
set_runtimes("MD")
add_requires("glfw")
target("test")
set_kind("binary")
add_files("*.cpp")
add_packages("glfw")
其他
xmake 源码怎么看
有一篇老文章可以看看 -> xmake 源码架构剖析
定制 api
参考一下这个 pr 是怎么写的:Add set_encodings api to set source/target encoding