2.1.5版本現(xiàn)已進(jìn)入收尾階段饰迹,此版本加入了一大波新特性,目前正在進(jìn)行穩(wěn)定性測試和修復(fù)余舶,在這里啊鸭,先來介紹下新版本中引入了哪些新特性和改進(jìn)。
1. 提供類似cmake的find_*系列接口匿值,實現(xiàn)各種查找赠制,例如:find_package, find_library, find_file, ...
2. 提供模塊接口,實現(xiàn)編譯器的各種檢測,例如:has_features, has_flags, has_cincludes, has_cfuncs, ...
3. 實現(xiàn)大量擴(kuò)展模塊钟些,提供文件下載烟号、解壓縮、git操作等接口
4. 支持預(yù)編譯頭文件支持政恍,改進(jìn)c++編譯效率
5. 支持在工程中自定義模塊進(jìn)行擴(kuò)展
6. 提供代碼片段檢測接口汪拥,實現(xiàn)更加靈活定制化的檢測需求
7. 改進(jìn)option和target,提供更加動態(tài)化的配置
8. 通過find_package實現(xiàn)包依賴管理2.0版本
9. 改進(jìn)root權(quán)限問題篙耗,實現(xiàn)更加安全的root下運行
10. 提供compile_commands.json導(dǎo)出插件
11. 改進(jìn)vs201x工程生成插件迫筑,支持多模式、多架構(gòu)同時構(gòu)建和自由切換不干擾
利用find_package查找依賴包
此接口參考了cmake對于find_*
系列接口的設(shè)計宗弯,實現(xiàn)在項目中動態(tài)的查找和添加包依賴脯燃。
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中去。
實現(xiàn)包管理2.0
2.1.4版本之前追他,xmake對于包管理坟募,是通過在項目內(nèi)置pkg/zlib.pkg
方式,來檢測鏈接的邑狸,雖然也支持自動檢測懈糯,但是查找功能有限,并且內(nèi)置的各個架構(gòu)的二進(jìn)制庫到項目单雾,對git并不是很友好赚哗。
現(xiàn)在通過find_package
和option
,我們可以實現(xiàn)更好的包管理:
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的選項作為包硅堆,關(guān)聯(lián)到target屿储,在選項被檢測之前,先從系統(tǒng)中查找zlib包渐逃,如果存在够掠,則添加對應(yīng)的links
, linkdirs
等配置信息,然后進(jìn)行選項檢測茄菊,如果選項檢測通過疯潭,這個target在編譯的時候就會啟用zlib。
如果要手動禁用這個zlib包面殖,使其不參與自動檢測和鏈接竖哩,只需要:
$ xmake f --zlib=n
$ xmake
注:2.2.1版本將會實現(xiàn)包管理3.0,更加自動化的依賴包管理和使用脊僚,具體詳情見:Remote package management相叁。
例如:
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")
目前正在努力開發(fā)中,盡情期待。增淹。
模塊的自定義擴(kuò)展
我們可以通過在工程的xmake.lua
文件的開頭指定下擴(kuò)展modules的目錄:
add_moduledirs("$(projectdir)/xmake/modules")
這樣xmake就能找到自定義的擴(kuò)展模塊了椿访,例如:
projectdir
- xmake
- modules
- detect/package/find_openssl.lua
通過在自定義的工程模塊目錄,添加一個find_openssl.lua
的腳本虑润,就可以擴(kuò)展find_package
赎离,使得包查找更加精準(zhǔn)。
這里順便總結(jié)下端辱,find_package
的查找順序:
- 如果指定
{packagedirs = ""}
參數(shù)梁剔,優(yōu)先從這個參數(shù)指定的路徑中查找本地包*.pkg
- 如果在
xmake/modules
下面存在detect.packages.find_xxx
腳本,那么嘗試調(diào)用此腳本來改進(jìn)查找結(jié)果 - 如果系統(tǒng)存在
pkg-config
舞蔽,并且查找的是系統(tǒng)環(huán)境的庫荣病,則嘗試使用pkg-config
提供的路徑和鏈接信息進(jìn)行查找 - 如果系統(tǒng)存在
homebrew
,并且查找的是系統(tǒng)環(huán)境的庫渗柿,則嘗試使用brew --prefix xxx
提供的信息進(jìn)行查找 - 從參數(shù)中指定的pathes路徑和一些已知的系統(tǒng)路徑
/usr/lib
,/usr/include
中進(jìn)行查找
快速判斷編譯器特性檢測支持
通過core.tool.compiler
模塊的compiler.has_features接口个盆,在xmake.lua
中預(yù)先判斷當(dāng)前編譯期支持的語言特性,實現(xiàn)條件編譯朵栖。
此處也是參考了cmake的設(shè)計颊亮,具體詳情見: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的時候终惑,判斷當(dāng)前編譯器是否支持c++的常量表達(dá)式語法特性,如果支持則添加宏定義:HAS_CXX_CONSTEXPR=1
门扇。
我們也可以在判斷時候雹有,追加一些參數(shù)控制編譯選項,例如上述特性需要c++11
支持臼寄,我們可以啟用它:
if compiler.has_features({"c_static_assert", "cxx_constexpr"}, {languages = "cxx11"}) then
-- ok
end
如果之前對這個target已經(jīng)設(shè)置了c++11
霸奕,那么我們也可以傳入target對象,繼承target的所有設(shè)置:
if compiler.has_features("cxx_constexpr", {target = target, defines = "..", includedirs = ".."}) then
-- ok
end
所有的c/c++編譯器特性列表吉拳,見:compiler.features
判斷指定c/c++頭文件是否存在
通過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
判斷指定c/c++函數(shù)是否存在
通過lib.detect.has_cfuncs來檢測c函數(shù)是否存在留攒。
import("lib.detect.has_cfuncs")
local ok = has_cfuncs("setjmp")
local ok = has_cfuncs({"sigsetjmp((void*)0, 0)", "setjmp"}, {includes = "setjmp.h"})
c++函數(shù)的檢測煤惩,見:lib.detect.has_cxxfuncs。
判斷指定c/c++類型是否存在
通過lib.detect.has_ctypes來檢測c函數(shù)是否存在稼跳。
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++代碼片段是否能夠編譯通過
通用的c/c++代碼片段檢測接口汤善,通過傳入多個代碼片段列表,它會自動生成一個編譯文件,然后常識對它進(jìn)行編譯红淡,如果編譯通過返回true不狮。
對于一些復(fù)雜的編譯器特性,連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驻仅,或者是組合起來一起檢測。
第一個參數(shù)為代碼片段列表登渣,一般用于一些自定義特性的檢測噪服,如果為空,則可以僅僅檢測可選參數(shù)中條件胜茧,例如:
local ok = check_cxsnippets({}, {types = {"wchar_t", "char*"}, includes = "stdio.h", funcs = {"sigsetjmp", "sigsetjmp((void*)0, 0)"}})
上面那個調(diào)用粘优,會去同時檢測types, includes和funcs是否都滿足,如果通過返回true呻顽。
更加強(qiáng)大的xmake lua插件
2.1.4版本的時候雹顺,此插件就已經(jīng)支持REPL(read-eval-print),實現(xiàn)交互式運行來方便測試模塊:
$ xmake lua
> 1 + 2
3
> a = 1
> a
1
> for _, v in pairs({1, 2, 3}) do
>> print(v)
>> end
1
2
3
現(xiàn)在可以通過一行命令廊遍,更加快速地測試模塊接口:
$ xmake lua lib.detect.find_package openssl
返回結(jié)果如下:{links = {"ssl", "crypto", "z"}, linkdirs = {"/usr/local/lib"}, includedirs = {"/usr/local/include"}}
預(yù)編譯頭文件支持
xmake新增通過預(yù)編譯頭文件去加速c/c++
程序編譯嬉愧,目前支持的編譯器有:gcc, clang和msvc。
使用方式如下:
target("test")
set_precompiled_header("header.h")
通常情況下喉前,設(shè)置c頭文件的預(yù)編譯英染,這需要加上這個配置即可,如果是對c++頭文件的預(yù)編譯被饿,改成:
target("test")
set_precompiled_header("header.hpp")
其中的參數(shù)指定的是需要預(yù)編譯的頭文件路徑四康,相對于當(dāng)前xmake.lua
所在的目錄。
如果只是調(diào)用xmake命令行進(jìn)行直接編譯狭握,那么上面的設(shè)置足夠了闪金,并且已經(jīng)對各個編譯器進(jìn)行支持,但是有些情況下论颅,上面的設(shè)置還不能滿足需求:
- 如果要使用
xmake project
工程插件生成vs工程文件哎垦,那么還缺少一個類似stdafx.cpp
的文件(上面的設(shè)置在msvc編譯的時候會自動生成一個臨時的,但是對IDE工程不友好)恃疯。 - 如果gcc/clang下漏设,
header.h
想作為c++的預(yù)編譯頭文件就不支持了,除非改成header.hpp
(默認(rèn)會當(dāng)做c頭文件進(jìn)行預(yù)編譯)今妄。
因此為了更加地通用跨平臺鸳碧,可以在工程里面創(chuàng)建一個類似vc中stdafx.cpp
的源文件:header.cpp
。
target("test")
set_precompiled_header("header.h", "header.cpp")
header.cpp
的內(nèi)容如下:
#include "header.h"
上面的設(shè)置犬性,就可以很好地處理各種情況下的預(yù)編譯處理乒裆,追加的header.cpp
也告訴了xmake:header.h
是作為c++來預(yù)編譯的鹤耍。
相對于經(jīng)典的vc工程中的stdafx.cpp
和stdafx.h
昂拂,也能完美支持:
target("test")
set_precompiled_header("stdafx.h", "stdafx.cpp")
生成compiler_commands插件
擴(kuò)展xmake project
工程生成插件伟姐,支持compiler_commands.json
文件輸出收苏,用于導(dǎo)出每個源文件的編譯信息,生成基于clang的編譯數(shù)據(jù)庫文件愤兵,json格式鹿霸,可用于跟ide,編輯器秆乳,靜態(tài)分析工具進(jìn)行交互懦鼠。
$ xmake project -k compile_commands
輸出的內(nèi)容格式如下:
[
{ "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、編輯器插件屹堰、靜態(tài)分析工具進(jìn)行集成肛冶,對于compile_commands
的詳細(xì)說明見:JSONCompilationDatabase
自定義選項檢測腳本
在選項檢測之前,動態(tài)增加一些配置條件:
option("zlib")
before_check(function (option)
import("lib.detect.find_package")
option:add(find_package("zlib"))
end)
通過覆寫檢測腳本扯键,控制選項的檢測結(jié)果:
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選項。
在選項檢測完成后荣刑,執(zhí)行此腳本做一些后期處理馅笙,也可以在此時重新禁用選項:
option("test")
add_deps("small")
add_links("pthread")
after_check(function (option)
option:enable(false)
end)
自定義目標(biāo)加載腳本
在target初始化加載的時候伦乔,將會執(zhí)行on_load,在里面可以做一些動態(tài)的目標(biāo)配置延蟹,實現(xiàn)更靈活的目標(biāo)描述定義,例如:
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
來動態(tài)添加各種target屬性阱飘。
目標(biāo)自定義構(gòu)建腳本支持分平臺架構(gòu)
通過設(shè)置平臺|架構(gòu)
參數(shù),控制自定義腳本的執(zhí)行條件虱颗,實現(xiàn)在不同平臺沥匈、架構(gòu)下,調(diào)用不同的腳本進(jìn)行構(gòu)建:
target("test")
on_build("iphoneos|arm*", function (target)
-- TODO
end)
或者對所有macosx平臺忘渔,執(zhí)行腳本:
target("test")
after_build("macosx", function (target)
-- TODO
end)
其他腳本高帖,例如:on_clean
, before_package
等也都是支持的哦,而在2.1.4之前畦粮,只支持:
target("test")
on_package(function (target)
-- TODO
end)
并不能對不同架構(gòu)散址、平臺分別處理。
獲取內(nèi)置變量的值
內(nèi)置變量可以通過此接口直接獲取宣赔,而不需要再加$()
的包裹预麸,使用更加簡單,例如:
print(val("host"))
print(val("env PATH"))
local s = val("shell echo hello")
而用vformat就比較繁瑣了:
local s = vformat("$(shell echo hello)")
不過vformat
支持字符串參數(shù)格式化儒将,更加強(qiáng)大吏祸,所以應(yīng)用場景不同。
目標(biāo)依賴實現(xiàn)屬性繼承
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還會自動繼承依賴目標(biāo)中的配置和屬性砰逻,不再需要額外調(diào)用add_links
, add_includedirs
和add_linkdirs
等接口去關(guān)聯(lián)依賴目標(biāo)了鸣驱,上述代碼可簡化為:
target("test1")
set_kind("static")
set_files("*.c")
target("test2")
set_kind("static")
set_files("*.c")
target("demo")
add_deps("test1", "test2") -- 會自動鏈接依賴目標(biāo)
并且繼承關(guān)系是支持級聯(lián)的,例如:
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接口用于查找可執(zhí)行程序蝠咆,比lib.detect.find_program更加的高級丐巫,功能也更加強(qiáng)大,它對可執(zhí)行程序進(jìn)行了封裝勺美,提供了工具這個概念:
- toolname: 工具名递胧,可執(zhí)行程序的簡稱,用于標(biāo)示某個工具赡茸,例如:
gcc
,clang
等 - program: 可執(zhí)行程序命令缎脾,例如:
xcrun -sdk macosx clang
lib.detect.find_program
只能通過傳入的原始program命令或路徑,去判斷該程序是否存在占卧。
而find_tool
則可以通過更加一致的toolname去查找工具遗菠,并且返回對應(yīng)的program完整命令路徑联喘,例如:
import("lib.detect.find_tool")
local tool = find_tool("clang")
我們也可以指定{version = true}
參數(shù)去獲取工具的版本,并且指定一個自定義的搜索路徑辙纬,也支持內(nèi)建變量和自定義腳本哦:
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}})
最后總結(jié)下豁遭,find_tool
的查找流程:
- 優(yōu)先通過
{program = "xxx"}
的參數(shù)來嘗試運行和檢測。 - 如果在
xmake/modules/detect/tools
下存在detect.tools.find_xxx
腳本贺拣,則調(diào)用此腳本進(jìn)行更加精準(zhǔn)的檢測蓖谢。 - 嘗試從
/usr/bin
,/usr/local/bin
等系統(tǒng)目錄進(jìn)行檢測譬涡。
我們也可以在工程xmake.lua
中add_moduledirs
指定的模塊目錄中闪幽,添加自定義查找腳本,來改進(jìn)檢測機(jī)制:
projectdir
- xmake/modules
- detect/tools/find_xxx.lua
更加安全的root權(quán)限編譯
由于xmake提供強(qiáng)大的自定義模塊和腳本支持涡匀,并且內(nèi)置安裝盯腌、卸載等action,如果xmake.lua
里面的腳本描述不當(dāng)陨瘩,容易導(dǎo)致覆寫系統(tǒng)文件腕够,因此新版本對此作了改進(jìn):
- 在root下編譯工程,先判斷工程目錄的用戶權(quán)限屬性舌劳,嘗試降權(quán)到非root用戶進(jìn)行編譯燕少。
- 如果需要寫一些系統(tǒng)文件,會提示用戶當(dāng)前權(quán)限不安全蒿囤,禁止繼續(xù)運行客们,除非加
--root
參數(shù)強(qiáng)制root運行。 - 如果當(dāng)期工程目錄是root用戶權(quán)限材诽,則同2底挫。
具體詳情見:pull#113
API接口改進(jìn)
使用includes替代老的add_subdirs和add_subfiles接口。
使用set_config_header替代老的set_config_h和set_config_h_prefix接口脸侥。
新增大量擴(kuò)展模塊
- 文件下載
- 解壓縮
- git操作等接口
具體詳情見文檔:擴(kuò)展模塊