xmake v2.1.5版本新特性介紹

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工程生成插件,支持多模式、多架構同時構建和自由切換不干擾

利用find_package查找依賴包

此接口參考了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, includedirslinkdirs等信息添加到target中去。github

實現包管理2.0

2.1.4版本以前,xmake對於包管理,是經過在項目內置pkg/zlib.pkg方式,來檢測連接的,雖然也支持自動檢測,可是查找功能有限,而且內置的各個架構的二進制庫到項目,對git並非很友好。shell

如今經過find_packageoption,咱們能夠實現更好的包管理:數據庫

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的查找順序:

  1. 若是指定{packagedirs = ""}參數,優先從這個參數指定的路徑中查找本地包*.pkg
  2. 若是在xmake/modules下面存在detect.packages.find_xxx腳本,那麼嘗試調用此腳原本改進查找結果
  3. 若是系統存在pkg-config,而且查找的是系統環境的庫,則嘗試使用pkg-config提供的路徑和連接信息進行查找
  4. 若是系統存在homebrew,而且查找的是系統環境的庫,則嘗試使用brew --prefix xxx提供的信息進行查找
  5. 從參數中指定的pathes路徑和一些已知的系統路徑/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

判斷指定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++函數是否存在

經過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

判斷指定c/c++類型是否存在

經過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++代碼片斷是否可以編譯經過

通用的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_cincludesdetect.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。

更增強大的xmake lua插件

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命令行進行直接編譯,那麼上面的設置足夠了,而且已經對各個編譯器進行支持,可是有些狀況下,上面的設置還不能知足需求:

  1. 若是要使用xmake project工程插件生成vs工程文件,那麼還缺乏一個相似stdafx.cpp的文件(上面的設置在msvc編譯的時候會自動生成一個臨時的,可是對IDE工程不友好)。
  2. 若是gcc/clang下,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.cppstdafx.h,也能完美支持:

target("test")
    set_precompiled_header("stdafx.h", "stdafx.cpp")

生成compiler_commands插件

擴展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_includedirsadd_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更加的高級,功能也更增強大,它對可執行程序進行了封裝,提供了工具這個概念:

  • toolname: 工具名,可執行程序的簡稱,用於標示某個工具,例如:gcc, clang
  • program: 可執行程序命令,例如: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的查找流程:

  1. 優先經過{program = "xxx"}的參數來嘗試運行和檢測。
  2. 若是在xmake/modules/detect/tools下存在detect.tools.find_xxx腳本,則調用此腳本進行更加精準的檢測。
  3. 嘗試從/usr/bin/usr/local/bin等系統目錄進行檢測。

咱們也能夠在工程xmake.luaadd_moduledirs指定的模塊目錄中,添加自定義查找腳本,來改進檢測機制:

projectdir
  - xmake/modules
    - detect/tools/find_xxx.lua

更加安全的root權限編譯

因爲xmake提供強大的自定義模塊和腳本支持,而且內置安裝、卸載等action,若是xmake.lua裏面的腳本描述不當,容易致使覆寫系統文件,所以新版本對此做了改進:

  1. 在root下編譯工程,先判斷工程目錄的用戶權限屬性,嘗試降權到非root用戶進行編譯。
  2. 若是須要寫一些系統文件,會提示用戶當前權限不安全,禁止繼續運行,除非加--root參數強制root運行。
  3. 若是當期工程目錄是root用戶權限,則同2。

具體詳情見:pull#113

API接口改進

使用includes替代老的add_subdirsadd_subfiles接口。
使用set_config_header替代老的set_config_hset_config_h_prefix接口。

新增大量擴展模塊

  • 文件下載
  • 解壓縮
  • git操做等接口

具體詳情見文檔:擴展模塊

原文出處:http://tboox.org/cn/2017/07/2...

相關文章
相關標籤/搜索