2.1.5版本现已进入收尾阶段,此版本加入了一大波新特性,目前正在进行稳定性测试和修复,在这里,先来介绍下新版本中引入了哪些新特性和改进。c++
1. 提供相似cmake的find_*系列接口,实现各类查找,例如:find_package, find_library, find_file, ... 2. 提供模块接口,实现编译器的各类检测,例如:has_features, has_flags, has_cincludes, has_cfuncs, ... 3. 实现大量扩展模块,提供文件下载、解压缩、git操做等接口 4. 支持预编译头文件支持,改进c++编译效率 5. 支持在工程中自定义模块进行扩展 6. 提供代码片断检测接口,实现更加灵活定制化的检测需求 7. 改进option和target,提供更加动态化的配置 8. 经过find_package实现包依赖管理2.0版本 9. 改进root权限问题,实现更加安全的root下运行 10. 提供compile_commands.json导出插件 11. 改进vs201x工程生成插件,支持多模式、多架构同时构建和自由切换不干扰
此接口参考了cmake对于find_*
系列接口的设计,实如今项目中动态的查找和添加包依赖。git
target("test") set_kind("binary") add_files("*.c") on_load(function (target) import("lib.detect.find_package") target:add(find_package("zlib")) end)
上述描述代码,经过lib.detect.find_package来查找包,若是找到zlib
包,则将links
, includedirs
和linkdirs
等信息添加到target中去。github
2.1.4版本以前,xmake对于包管理,是经过在项目内置pkg/zlib.pkg
方式,来检测连接的,虽然也支持自动检测,可是查找功能有限,而且内置的各个架构的二进制库到项目,对git并非很友好。shell
如今经过find_package
和option
,咱们能够实现更好的包管理:数据库
option("zlib") set_showmenu(true) before_check(function (option) import("lib.detect.find_package") option:add(find_package("zlib")) end) target("test") add_options("zlib")
经过定义一个名为zlib的选项做为包,关联到target,在选项被检测以前,先从系统中查找zlib包,若是存在,则添加对应的links
, linkdirs
等配置信息,而后进行选项检测,若是选项检测经过,这个target在编译的时候就会启用zlib。macos
若是要手动禁用这个zlib包,使其不参与自动检测和连接,只须要:json
$ xmake f --zlib=n $ xmake
注:2.2.1版本将会实现包管理3.0,更加自动化的依赖包管理和使用,具体详情见:Remote package management。安全
例如:bash
add_requires("mbedtls master optional") add_requires("pcre2 >=1.2.0", "zlib >= 1.2.11") add_requires("git@github.com:glennrp/libpng.git@libpng >=1.6.28") target("test") add_packages("pcre2", "zlib", "libpng", "mbedtls")
目前正在努力开发中,尽情期待。。架构
咱们能够经过在工程的xmake.lua
文件的开头指定下扩展modules的目录:
add_moduledirs("$(projectdir)/xmake/modules")
这样xmake就能找到自定义的扩展模块了,例如:
projectdir - xmake - modules - detect/package/find_openssl.lua
经过在自定义的工程模块目录,添加一个find_openssl.lua
的脚本,就能够扩展find_package
,使得包查找更加精准。
这里顺便总结下,find_package
的查找顺序:
{packagedirs = ""}
参数,优先从这个参数指定的路径中查找本地包*.pkg
xmake/modules
下面存在detect.packages.find_xxx
脚本,那么尝试调用此脚原本改进查找结果pkg-config
,而且查找的是系统环境的库,则尝试使用pkg-config
提供的路径和连接信息进行查找homebrew
,而且查找的是系统环境的库,则尝试使用brew --prefix xxx
提供的信息进行查找/usr/lib
, /usr/include
中进行查找经过core.tool.compiler
模块的compiler.has_features接口,在xmake.lua
中预先判断当前编译期支持的语言特性,实现条件编译。
此处也是参考了cmake的设计,具体详情见:issues#83。
target("test") on_load(function (target) import("core.tool.compiler") if compiler.has_features("cxx_constexpr") then target:add("defines", "HAS_CXX_CONSTEXPR=1") end end)
上述代码,在加载target的时候,判断当前编译器是否支持c++的常量表达式语法特性,若是支持则添加宏定义:HAS_CXX_CONSTEXPR=1
。
咱们也能够在判断时候,追加一些参数控制编译选项,例如上述特性须要c++11
支持,咱们能够启用它:
if compiler.has_features({"c_static_assert", "cxx_constexpr"}, {languages = "cxx11"}) then -- ok end
若是以前对这个target已经设置了c++11
,那么咱们也能够传入target对象,继承target的全部设置:
if compiler.has_features("cxx_constexpr", {target = target, defines = "..", includedirs = ".."}) then -- ok end
全部的c/c++编译器特性列表,见:compiler.features
经过lib.detect.has_cincludes来检测c头文件是否存在。
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"}, {defines = "_GNU_SOURCE=1", languages = "cxx11"})
c++头文件的检测,见:lib.detect.has_cxxincludes
经过lib.detect.has_cfuncs来检测c函数是否存在。
import("lib.detect.has_cfuncs") local ok = has_cfuncs("setjmp") local ok = has_cfuncs({"sigsetjmp((void*)0, 0)", "setjmp"}, {includes = "setjmp.h"})
c++函数的检测,见:lib.detect.has_cxxfuncs。
经过lib.detect.has_ctypes来检测c函数是否存在。
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"}, "defines = "_GNU_SOURCE=1", languages = "cxx11"})
c++类型的检测,见:lib.detect.has_cxxtypes。
通用的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。
2.1.4版本的时候,此插件就已经支持REPL(read-eval-print),实现交互式运行来方便测试模块:
$ xmake lua > 1 + 2 3 > a = 1 > a 1 > for _, v in pairs({1, 2, 3}) do >> print(v) >> end 1 2 3
如今能够经过一行命令,更加快速地测试模块接口:
$ xmake lua lib.detect.find_package openssl
返回结果以下:{links = {"ssl", "crypto", "z"}, linkdirs = {"/usr/local/lib"}, includedirs = {"/usr/local/include"}}
xmake新增经过预编译头文件去加速c/c++
程序编译,目前支持的编译器有:gcc, clang和msvc。
使用方式以下:
target("test") set_precompiled_header("header.h")
一般状况下,设置c头文件的预编译,这须要加上这个配置便可,若是是对c++头文件的预编译,改为:
target("test") set_precompiled_header("header.hpp")
其中的参数指定的是须要预编译的头文件路径,相对于当前xmake.lua
所在的目录。
若是只是调用xmake命令行进行直接编译,那么上面的设置足够了,而且已经对各个编译器进行支持,可是有些状况下,上面的设置还不能知足需求:
xmake project
工程插件生成vs工程文件,那么还缺乏一个相似stdafx.cpp
的文件(上面的设置在msvc编译的时候会自动生成一个临时的,可是对IDE工程不友好)。header.h
想做为c++的预编译头文件就不支持了,除非改为header.hpp
(默认会当作c头文件进行预编译)。所以为了更加地通用跨平台,能够在工程里面建立一个相似vc中stdafx.cpp
的源文件:header.cpp
。
target("test") set_precompiled_header("header.h", "header.cpp")
header.cpp
的内容以下:
#include "header.h"
上面的设置,就能够很好地处理各类状况下的预编译处理,追加的header.cpp
也告诉了xmake:header.h
是做为c++来预编译的。
相对于经典的vc工程中的stdafx.cpp
和stdafx.h
,也能完美支持:
target("test") set_precompiled_header("stdafx.h", "stdafx.cpp")
扩展xmake project
工程生成插件,支持compiler_commands.json
文件输出,用于导出每一个源文件的编译信息,生成基于clang的编译数据库文件,json格式,可用于跟ide,编辑器,静态分析工具进行交互。
$ xmake project -k compile_commands
输出的内容格式以下:
[ { "directory": "/home/user/llvm/build", "command": "/usr/bin/clang++ -Irelative -DSOMEDEF=\"With spaces, quotes and \\-es.\" -c -o file.o file.cc", "file": "file.cc" }, ... ]
通常用于跟IDE、编辑器插件、静态分析工具进行集成,对于compile_commands
的详细说明见:JSONCompilationDatabase
在选项检测以前,动态增长一些配置条件:
option("zlib") before_check(function (option) import("lib.detect.find_package") option:add(find_package("zlib")) end)
经过覆写检测脚本,控制选项的检测结果:
option("test") add_deps("small") set_default(true) on_check(function (option) if option:dep("small"):enabled() then option:enable(false) end end)
若是test依赖的选项经过,则禁用test选项。
在选项检测完成后,执行此脚本作一些后期处理,也能够在此时从新禁用选项:
option("test") add_deps("small") add_links("pthread") after_check(function (option) option:enable(false) end)
在target初始化加载的时候,将会执行on_load,在里面能够作一些动态的目标配置,实现更灵活的目标描述定义,例如:
target("test") on_load(function (target) target:add("defines", "DEBUG", "TEST=\"hello\"") target:add("linkdirs", "/usr/lib", "/usr/local/lib") target:add({includedirs = "/usr/include", "links" = "pthread"}) end)
能够在on_load
里面,经过target:set
, target:add
来动态添加各类target属性。
经过设置平台|架构
参数,控制自定义脚本的执行条件,实如今不一样平台、架构下,调用不一样的脚本进行构建:
target("test") on_build("iphoneos|arm*", function (target) -- TODO end)
或者对全部macosx平台,执行脚本:
target("test") after_build("macosx", function (target) -- TODO end)
其余脚本,例如:on_clean
, before_package
等也都是支持的哦,而在2.1.4以前,只支持:
target("test") on_package(function (target) -- TODO end)
并不能对不一样架构、平台分别处理。
内置变量能够经过此接口直接获取,而不须要再加$()
的包裹,使用更加简单,例如:
print(val("host")) print(val("env PATH")) local s = val("shell echo hello")
而用vformat就比较繁琐了:
local s = vformat("$(shell echo hello)")
不过vformat
支持字符串参数格式化,更增强大,因此应用场景不一样。
2.1.4以前的版本,target.add_deps仅用于添加依赖,修改编译顺序:
target("test1") set_kind("static") set_files("*.c") target("test2") set_kind("static") set_files("*.c") target("demo") add_deps("test1", "test2") add_links("test1", "test2") add_linkdirs("test1dir", "test2dir")
2.1.5版本后,target还会自动继承依赖目标中的配置和属性,再也不须要额外调用add_links
, add_includedirs
和add_linkdirs
等接口去关联依赖目标了,上述代码可简化为:
target("test1") set_kind("static") set_files("*.c") target("test2") set_kind("static") set_files("*.c") target("demo") add_deps("test1", "test2") -- 会自动连接依赖目标
而且继承关系是支持级联的,例如:
target("library1") set_kind("static") add_files("*.c") add_headers("inc1/*.h") target("library2") set_kind("static") add_deps("library1") add_files("*.c") add_headers("inc2/*.h") target("test") set_kind("binary") add_deps("library2")
lib.detect.find_tool接口用于查找可执行程序,比lib.detect.find_program更加的高级,功能也更增强大,它对可执行程序进行了封装,提供了工具这个概念:
gcc
, clang
等xcrun -sdk macosx clang
lib.detect.find_program
只能经过传入的原始program命令或路径,去判断该程序是否存在。
而find_tool
则能够经过更加一致的toolname去查找工具,而且返回对应的program完整命令路径,例如:
import("lib.detect.find_tool") local tool = find_tool("clang")
咱们也能够指定{version = true}
参数去获取工具的版本,而且指定一个自定义的搜索路径,也支持内建变量和自定义脚本哦:
local tool = find_tool("clang", {check = "--help"}) local tool = find_tool("clang", {check = function (tool) os.run("%s -h", tool) end}) local tool = find_tool("clang", {version = true, {pathes = {"/usr/bin", "/usr/local/bin", "$(env PATH)", function () return "/usr/xxx/bin" 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
因为xmake提供强大的自定义模块和脚本支持,而且内置安装、卸载等action,若是xmake.lua
里面的脚本描述不当,容易致使覆写系统文件,所以新版本对此做了改进:
--root
参数强制root运行。具体详情见:pull#113
使用includes替代老的add_subdirs和add_subfiles接口。
使用set_config_header替代老的set_config_h和set_config_h_prefix接口。
具体详情见文档:扩展模块