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接口。
具體詳情見文檔:擴展模塊