lib.detect
此模块提供了非常强大的探测功能,用于探测程序、编译器、语言特性、依赖包等。
注意
此模块的接口分散在多个模块目录中,尽量通过导入单个接口来使用,这样效率更高。
detect.find_file
- 查找文件
函数原型
API
find_file(file: <string>, paths: <table>, opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| file | 必需。文件名或路径 |
| paths | 必需。搜索路径列表 |
| opt | 可选。选项参数,支持以下选项: - suffixes - 子目录后缀列表 |
返回值说明
| 类型 | 描述 |
|---|---|
| string | 找到文件返回文件路径,未找到返回 nil |
用法说明
这个接口提供了比os.files更加强大的工程, 可以同时指定多个搜索目录,并且还能对每个目录指定附加的子目录, 来模式匹配查找,相当于是os.files的增强版。
例如:
import("lib.detect.find_file")
local file = find_file("ccache", { "/usr/bin", "/usr/local/bin"})如果找到,返回的结果是:/usr/bin/ccache
它同时也支持模式匹配路径,进行递归查找,类似os.files:
local file = find_file("test.h", { "/usr/include", "/usr/local/include/**"})不仅如此,里面的路径也支持内建变量,来从环境变量和注册表中获取路径进行查找:
local file = find_file("xxx.h", { "$(env PATH)", "$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\XXXX;Name)"})如果路径规则比较复杂多变,还可以通过自定义脚本来动态生成路径传入:
local file = find_file("xxx.h", { "$(env PATH)", function () return val("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\XXXX;Name"):match("\"(.-)\"") end})大部分场合下,上面的使用已经满足各种需求了,如果还需要一些扩展功能,可以通过传入第三个参数,自定义一些可选配置,例如:
local file = find_file("test.h", { "/usr", "/usr/local"}, {suffixes = {"/include", "/lib"}})通过指定suffixes子目录列表,可以扩展路径列表(第二个参数),使得实际的搜索目录扩展为:
/usr/include
/usr/lib
/usr/local/include
/usr/local/lib并且不用改变路径列表,就能动态切换子目录来搜索文件。
注意
我们也可以通过xmake lua插件来快速调用和测试此接口:xmake lua lib.detect.find_file test.h /usr/local
detect.find_path
- 查找路径
函数原型
API
find_path(file: <string>, paths: <table>, opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| file | 必需。文件或目录路径 |
| paths | 必需。搜索路径列表 |
| opt | 可选。选项参数,支持以下选项: - suffixes - 子目录后缀 |
返回值说明
| 类型 | 描述 |
|---|---|
| string | 找到返回路径,未找到返回 nil |
用法说明
这个接口的用法跟lib.detect.find_file类似,唯一的区别是返回的结果不同。 此接口查找到传入的文件路径后,返回的是对应的搜索路径,而不是文件路径本身,一般用于查找文件对应的父目录位置。
import("lib.detect.find_path")
local p = find_path("include/test.h", { "/usr", "/usr/local"})上述代码如果查找成功,则返回:/usr/local,如果test.h在/usr/local/include/test.h的话。
还有一个区别就是,这个接口传入不只是文件路径,还可以传入目录路径来查找:
local p = find_path("lib/xxx", { "$(env PATH)", "$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\XXXX;Name)"})同样,此接口也支持模式匹配和后缀子目录:
local p = find_path("include/*.h", { "/usr", "/usr/local/**"}, {suffixes = "/subdir"})detect.find_library
- 查找库文件
函数原型
API
find_library(name: <string>, paths: <table>, opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| name | 必需。库名称 |
| paths | 必需。搜索路径列表 |
| opt | 可选。选项参数,支持以下选项: - kind - 库类型,static 或 shared- suffixes - 子目录后缀 |
返回值说明
| 类型 | 描述 |
|---|---|
| table | 返回库信息表(包含 filename, linkdir, link, kind),未找到返回 nil |
用法说明
此接口用于指定的搜索目录中查找库文件(静态库,动态库),例如:
import("lib.detect.find_library")
local library = find_library("crypto", {"/usr/lib", "/usr/local/lib"})在macosx上运行,返回的结果如下:
{
filename = libcrypto.dylib
, linkdir = /usr/lib
, link = crypto
, kind = shared
}如果不指定是否需要静态库还是动态库,那么此接口会自动选择一个存在的库(有可能是静态库、也有可能是动态库)进行返回。
如果需要强制指定需要查找的库类型,可以指定kind参数为(static/shared):
local library = find_library("crypto", {"/usr/lib", "/usr/local/lib"}, {kind = "static"})此接口也支持suffixes后缀子目录搜索和模式匹配操作:
local library = find_library("cryp*", {"/usr", "/usr/local"}, {suffixes = "/lib"})detect.find_program
- 查找可执行程序
函数原型
API
find_program(name: <string>, opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| name | 必需。程序名称 |
| opt | 可选。选项参数,支持以下选项: - paths - 搜索路径列表- check - 检查命令或函数 |
返回值说明
| 类型 | 描述 |
|---|---|
| string | 找到返回程序路径,未找到返回 nil |
用法说明
这个接口比lib.detect.find_tool较为原始底层,通过指定的参数目录来查找可执行程序。
import("lib.detect.find_program")
local program = find_program("ccache")上述代码犹如没有传递搜索目录,所以它会尝试直接执行指定程序,如果运行ok,那么直接返回:ccache,表示查找成功。
指定搜索目录,修改尝试运行的检测命令参数(默认是:ccache --version):
local program = find_program("ccache", {paths = {"/usr/bin", "/usr/local/bin"}, check = "--help"})上述代码会尝试运行:/usr/bin/ccache --help,如果运行成功,则返回:/usr/bin/ccache。
如果--help也没法满足需求,有些程序没有--version/--help参数,那么可以自定义运行脚本,来运行检测:
local program = find_program("ccache", {paths = {"/usr/bin", "/usr/local/bin"}, check = function (program) os.run("%s -h", program) end})同样,搜索路径列表支持内建变量和自定义脚本:
local program = find_program("ccache", {paths = {"$(env PATH)", "$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug;Debugger)"}})
local program = find_program("ccache", {paths = {"$(env PATH)", function () return "/usr/local/bin" end}})注意
为了加速频发查找的效率,此接口是默认自带cache的,所以就算频繁查找相同的程序,也不会花太多时间。 如果要禁用cache,可以在工程目录执行xmake f -c清除本地cache。
我们也可以通过xmake lua lib.detect.find_program ccache 来快速测试。
detect.find_programver
- 查找可执行程序版本号
函数原型
API
find_programver(name: <string>, opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| name | 必需。程序名称 |
| opt | 可选。选项参数,支持以下选项: - command - 版本命令或函数- parse - 版本解析规则或函数 |
返回值说明
| 类型 | 描述 |
|---|---|
| string | 找到返回版本号,未找到返回 nil |
用法说明
import("lib.detect.find_programver")
local programver = find_programver("ccache")返回结果为:3.2.2
默认它会通过ccache --version尝试获取版本,如果不存在此参数,可以自己指定其他参数:
local version = find_programver("ccache", {command = "-v"})甚至自定义版本获取脚本:
local version = find_programver("ccache", {command = function () return os.iorun("ccache --version") end})对于版本号的提取规则,如果内置的匹配模式不满足要求,也可以自定义:
local version = find_programver("ccache", {command = "--version", parse = "(%d+%.?%d*%.?%d*.-)%s"})
local version = find_programver("ccache", {command = "--version", parse = function (output) return output:match("(%d+%.?%d*%.?%d*.-)%s") end})注意
为了加速频发查找的效率,此接口是默认自带cache的,如果要禁用cache,可以在工程目录执行xmake f -c清除本地cache。
我们也可以通过xmake lua lib.detect.find_programver ccache 来快速测试。
detect.find_package
- 查找包文件
注意
2.6.x 之后,这个接口不推荐直接使用(仅供内部使用),库集成,请尽量使用 add_requires() 和 add_packages()。
detect.find_tool
- 查找工具
函数原型
API
find_tool(toolname: <string>, opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| toolname | 必需。工具名称 |
| opt | 可选。选项参数,支持以下选项: - program - 程序命令- version - 是否获取版本- paths - 搜索路径- check - 检查命令或函数 |
返回值说明
| 类型 | 描述 |
|---|---|
| table | 返回工具信息表(包含 name, program, version),未找到返回 nil |
用法说明
此接口也是用于查找可执行程序,不过比lib.detect.find_program更加的高级,功能也更加强大,它对可执行程序进行了封装,提供了工具这个概念:
- toolname: 工具名,可执行程序的简称,用于标示某个工具,例如:
gcc,clang等 - program: 可执行程序命令,例如:
xcrun -sdk macosx clang
其对应关系如下:
| toolname | program |
|---|---|
| clang | xcrun -sdk macosx clang |
| gcc | /usr/toolchains/bin/arm-linux-gcc |
| link | link.exe -lib |
lib.detect.find_program只能通过传入的原始program命令或路径,去判断该程序是否存在。 而find_tool则可以通过更加一致的toolname去查找工具,并且返回对应的program完整命令路径,例如:
import("lib.detect.find_tool")
local tool = find_tool("clang")返回的结果为:{name = "clang", program = "clang"},这个时候还看不出区别,我们可以手动指定可执行的命令:
local tool = find_tool("clang", {program = "xcrun -sdk macosx clang"})返回的结果为:{name = "clang", program = "xcrun -sdk macosx clang"}
而在macosx下,gcc就是clang,如果我们执行gcc --version可以看到就是clang的一个马甲,我们可以通过find_tool接口进行智能识别:
local tool = find_tool("gcc")返回的结果为:{name = "clang", program = "gcc"}
通过这个结果就可以看的区别来了,工具名实际会被标示为clang,但是可执行的命令用的是gcc。
我们也可以指定{version = true}参数去获取工具的版本,并且指定一个自定义的搜索路径,也支持内建变量和自定义脚本哦:
local tool = find_tool("clang", {version = true, paths = {"/usr/bin", "/usr/local/bin", "$(env PATH)", function () return "/usr/xxx/bin" end}})返回的结果为:{name = "clang", program = "/usr/bin/clang", version = "4.0"}
这个接口是对find_program的上层封装,因此也支持自定义脚本检测:
local tool = find_tool("clang", {check = "--help"})
local tool = find_tool("clang", {check = function (tool) os.run("%s -h", tool) end})最后总结下,find_tool的查找流程:
- 优先通过
{program = "xxx"}的参数来尝试运行和检测。 - 如果在
xmake/modules/detect/tools下存在detect.tools.find_xxx脚本,则调用此脚本进行更加精准的检测。 - 尝试从
/usr/bin,/usr/local/bin等系统目录进行检测。
我们也可以在工程xmake.lua中add_moduledirs指定的模块目录中,添加自定义查找脚本,来改进检测机制:
projectdir
- xmake/modules
- detect/tools/find_xxx.lua例如我们自定义一个find_7z.lua的查找脚本:
import("lib.detect.find_program")
import("lib.detect.find_programver")
function main(opt)
-- init options
opt = opt or {}
-- find program
local program = find_program(opt.program or "7z", opt.pathes, opt.check or "--help")
-- find program version
local version = nil
if program and opt and opt.version then
version = find_programver(program, "--help", "(%d+%.?%d*)%s")
end
-- ok?
return program, version
end将它放置到工程的模块目录下后,执行:xmake l lib.detect.find_tool 7z就可以查找到了。
注意
为了加速频发查找的效率,此接口是默认自带cache的,如果要禁用cache,可以在工程目录执行xmake f -c清除本地cache。
我们也可以通过xmake lua lib.detect.find_tool clang 来快速测试。
detect.find_toolname
- 查找工具名
函数原型
API
find_toolname(program: <string>)参数说明
| 参数 | 描述 |
|---|---|
| program | 必需。程序命令或路径 |
返回值说明
| 类型 | 描述 |
|---|---|
| string | 返回工具名 |
用法说明
通过program命令匹配对应的工具名,例如:
| program | toolname |
|---|---|
xcrun -sdk macosx clang | clang |
/usr/bin/arm-linux-gcc | gcc |
link.exe -lib | link |
gcc-5 | gcc |
arm-android-clang++ | clangxx |
pkg-config | pkg_config |
toolname相比program,更能唯一标示某个工具,也方便查找和加载对应的脚本find_xxx.lua。
detect.find_cudadevices
- 查找本机的 CUDA 设备
函数原型
API
find_cudadevices(opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| opt | 可选。选项参数,支持以下选项: - skip_compute_mode_prohibited - 跳过计算模式禁止的设备- min_sm_arch - 最小SM架构- order_by_flops - 按性能排序 |
返回值说明
| 类型 | 描述 |
|---|---|
| table | 返回CUDA设备列表 |
用法说明
通过 CUDA Runtime API 枚举本机的 CUDA 设备,并查询其属性。
import("lib.detect.find_cudadevices")
local devices = find_cudadevices({ skip_compute_mode_prohibited = true })
local devices = find_cudadevices({ min_sm_arch = 35, order_by_flops = true })返回的结果为:{ { ['$id'] = 0, name = "GeForce GTX 960M", major = 5, minor = 0, ... }, ... }
包含的属性依据当前 CUDA 版本会有所不同,可以参考 CUDA 官方文档及其历史版本。
detect.features
- 获取指定工具的所有特性
函数原型
API
features(toolname: <string>, opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| toolname | 必需。工具名称 |
| opt | 可选。选项参数,支持以下选项: - flags - 标志列表- program - 程序命令 |
返回值说明
| 类型 | 描述 |
|---|---|
| table | 返回特性列表 |
用法说明
此接口跟compiler.features类似,区别就是此接口更加的原始,传入的参数是实际的工具名toolname。
并且此接口不仅能够获取编译器的特性,任何工具的特性都可以获取,因此更加通用。
import("lib.detect.features")
local features = features("clang")
local features = features("clang", {flags = "-O0", program = "xcrun -sdk macosx clang"})
local features = features("clang", {flags = {"-g", "-O0", "-std=c++11"}})通过传入flags,可以改变特性的获取结果,例如一些c++11的特性,默认情况下获取不到,通过启用-std=c++11后,就可以获取到了。
所有编译器的特性列表,可以见:compiler.features。
detect.has_features
- 判断指定特性是否支持
函数原型
API
has_features(toolname: <string>, features: <string|table>, opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| toolname | 必需。工具名称 |
| features | 必需。特性名或特性列表 |
| opt | 可选。选项参数,支持以下选项: - flags - 标志列表- program - 程序命令 |
返回值说明
| 类型 | 描述 |
|---|---|
| boolean|table | 如果传入单个特性返回boolean,传入列表返回支持的特性子列表 |
用法说明
此接口跟compiler.has_features类似,但是更加原始,传入的参数是实际的工具名toolname。
并且此接口不仅能够判断编译器的特性,任何工具的特性都可以判断,因此更加通用。
import("lib.detect.has_features")
local features = has_features("clang", "cxx_constexpr")
local features = has_features("clang", {"cxx_constexpr", "c_static_assert"}, {flags = {"-g", "-O0"}, program = "xcrun -sdk macosx clang"})
local features = has_features("clang", {"cxx_constexpr", "c_static_assert"}, {flags = "-g"})如果指定的特性列表存在,则返回实际支持的特性子列表,如果都不支持,则返回nil,我们也可以通过指定flags去改变特性的获取规则。
所有编译器的特性列表,可以见:compiler.features。
detect.has_flags
- 判断指定参数选项是否支持
函数原型
API
has_flags(toolname: <string>, flags: <string|table>, opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| toolname | 必需。工具名称 |
| flags | 必需。标志或标志列表 |
| opt | 可选。选项参数,支持以下选项: - program - 程序命令- toolkind - 工具类型 |
返回值说明
| 类型 | 描述 |
|---|---|
| boolean | 支持返回 true,否则返回 false |
用法说明
此接口跟compiler.has_flags类似,但是更加原始,传入的参数是实际的工具名toolname。
import("lib.detect.has_flags")
local ok = has_flags("clang", "-g")
local ok = has_flags("clang", {"-g", "-O0"}, {program = "xcrun -sdk macosx clang"})
local ok = has_flags("clang", "-g -O0", {toolkind = "cxx"})如果检测通过,则返回true。
此接口的检测做了一些优化,除了cache机制外,大部分场合下,会去拉取工具的选项列表(--help)直接判断,如果选项列表里获取不到的话,才会通过尝试运行的方式来检测。
detect.has_cfuncs
- 判断指定c函数是否存在
函数原型
API
has_cfuncs(funcs: <string|table>, opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| funcs | 必需。函数名或函数列表 |
| opt | 可选。选项参数,支持以下选项: - includes - 头文件列表- configs - 配置选项- target - 目标对象- verbose - 是否详细输出 |
返回值说明
| 类型 | 描述 |
|---|---|
| boolean | 存在返回 true,否则返回 false |
用法说明
此接口是lib.detect.check_cxsnippets的简化版本,仅用于检测函数。
import("lib.detect.has_cfuncs")
local ok = has_cfuncs("setjmp")
local ok = has_cfuncs({"sigsetjmp((void*)0, 0)", "setjmp"}, {includes = "setjmp.h"})对于函数的描述规则如下:
| 函数描述 | 说明 |
|---|---|
sigsetjmp | 纯函数名 |
sigsetjmp((void*)0, 0) | 函数调用 |
sigsetjmp{int a = 0; sigsetjmp((void*)a, a);} | 函数名 + {}块 |
在最后的可选参数中,除了可以指定includes外,还可以指定其他的一些参数用于控制编译检测的选项条件:
{ verbose = false, target = [target|option], includes = .., configs = {linkdirs = .., links = .., defines = ..}}其中verbose用于回显检测信息,target用于在检测前追加target中的配置信息, 而config用于自定义配置跟target相关的编译选项。
detect.has_cxxfuncs
- 判断指定c++函数是否存在
函数原型
API
has_cxxfuncs(funcs: <string|table>, opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| funcs | 必需。函数名或函数列表 |
| opt | 可选。选项参数,支持以下选项: - includes - 头文件列表- configs - 配置选项- target - 目标对象- verbose - 是否详细输出 |
返回值说明
| 类型 | 描述 |
|---|---|
| boolean | 存在返回 true,否则返回 false |
用法说明
此接口跟lib.detect.has_cfuncs类似,请直接参考它的使用说明,唯一区别是这个接口用于检测c++函数。
detect.has_cincludes
- 判断指定c头文件是否存在
函数原型
API
has_cincludes(includes: <string|table>, opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| includes | 必需。头文件名或头文件列表 |
| opt | 可选。选项参数,支持以下选项: - target - 目标对象- configs - 配置选项 |
返回值说明
| 类型 | 描述 |
|---|---|
| boolean | 存在返回 true,否则返回 false |
用法说明
此接口是lib.detect.check_cxsnippets的简化版本,仅用于检测头文件。
import("lib.detect.has_cincludes")
local ok = has_cincludes("stdio.h")
local ok = has_cincludes({"stdio.h", "stdlib.h"}, {target = target})
local ok = has_cincludes({"stdio.h", "stdlib.h"}, {configs = {defines = "_GNU_SOURCE=1", languages = "cxx11"}})detect.has_cxxincludes
- 判断指定c++头文件是否存在
函数原型
API
has_cxxincludes(includes: <string|table>, opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| includes | 必需。头文件名或头文件列表 |
| opt | 可选。选项参数,支持以下选项: - target - 目标对象- configs - 配置选项 |
返回值说明
| 类型 | 描述 |
|---|---|
| boolean | 存在返回 true,否则返回 false |
用法说明
此接口跟lib.detect.has_cincludes类似,请直接参考它的使用说明,唯一区别是这个接口用于检测c++头文件。
detect.has_ctypes
- 判断指定c类型是否存在
函数原型
API
has_ctypes(types: <string|table>, opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| types | 必需。类型名或类型列表 |
| opt | 可选。选项参数,支持以下选项: - includes - 头文件列表- configs - 配置选项 |
返回值说明
| 类型 | 描述 |
|---|---|
| boolean | 存在返回 true,否则返回 false |
用法说明
此接口是lib.detect.check_cxsnippets的简化版本,仅用于检测类型。
import("lib.detect.has_ctypes")
local ok = has_ctypes("wchar_t")
local ok = has_ctypes({"char", "wchar_t"}, {includes = "stdio.h"})
local ok = has_ctypes("wchar_t", {includes = {"stdio.h", "stdlib.h"}, configs = {"defines = "_GNU_SOURCE=1", languages = "cxx11"}})detect.has_cxxtypes
- 判断指定c++类型是否存在
函数原型
API
has_cxxtypes(types: <string|table>, opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| types | 必需。类型名或类型列表 |
| opt | 可选。选项参数,支持以下选项: - includes - 头文件列表- configs - 配置选项 |
返回值说明
| 类型 | 描述 |
|---|---|
| boolean | 存在返回 true,否则返回 false |
用法说明
此接口跟lib.detect.has_ctypes类似,请直接参考它的使用说明,唯一区别是这个接口用于检测c++类型。
detect.check_cxsnippets
- 检测c/c++代码片段是否能够编译通过
函数原型
API
check_cxsnippets(snippets: <string|table>, opt: <table>)参数说明
| 参数 | 描述 |
|---|---|
| snippets | 必需。代码片段或代码片段列表 |
| opt | 可选。选项参数,支持以下选项: - types - 类型列表- includes - 头文件列表- funcs - 函数列表- links - 链接库列表- target - 目标对象- sourcekind - 源文件类型 |
返回值说明
| 类型 | 描述 |
|---|---|
| boolean | 编译通过返回 true,否则返回 false |
用法说明
通用的c/c++代码片段检测接口,通过传入多个代码片段列表,它会自动生成一个编译文件,然后尝试对它进行编译,如果编译通过返回true。
对于一些复杂的编译器特性,连compiler.has_features都无法检测到的时候,可以通过此接口通过尝试编译来检测它。
import("lib.detect.check_cxsnippets")
local ok = check_cxsnippets("void test() {}")
local ok = check_cxsnippets({"void test(){}", "#define TEST 1"}, {types = "wchar_t", includes = "stdio.h"})此接口是detect.has_cfuncs, detect.has_cincludes和detect.has_ctypes等接口的通用版本,也更加底层。
因此我们可以用它来检测:types, functions, includes 还有 links,或者是组合起来一起检测。
第一个参数为代码片段列表,一般用于一些自定义特性的检测,如果为空,则可以仅仅检测可选参数中条件,例如:
local ok = check_cxsnippets({}, {types = {"wchar_t", "char*"}, includes = "stdio.h", funcs = {"sigsetjmp", "sigsetjmp((void*)0, 0)"}})上面那个调用,会去同时检测types, includes和funcs是否都满足,如果通过返回true。
还有其他一些可选参数:
{ verbose = false, target = [target|option], sourcekind = "[cc|cxx]"}其中verbose用于回显检测信息,target用于在检测前追加target中的配置信息, sourcekind 用于指定编译器等工具类型,例如传入cxx强制作为c++代码来检测。