描述語(yǔ)法
xmake的描述語(yǔ)法基于lua實(shí)現(xiàn)作箍,因此描述語(yǔ)法繼承了lua的靈活性和簡(jiǎn)潔性定躏,并且通過(guò)28原則,將描述作用域(簡(jiǎn)單描述)谴麦、腳本作用域(復(fù)雜描述)進(jìn)行分離,使得工程更加的簡(jiǎn)潔直觀伸头,可讀性非常好匾效。
因?yàn)?0%的工程,并不需要很復(fù)雜的腳本控制邏輯恤磷,只需要簡(jiǎn)單的幾行配置描述面哼,就可滿足構(gòu)建需求,基于這個(gè)假設(shè)扫步,xmake分離作用域魔策,使得80%的xmake.lua
文件,只需要這樣描述:
target("demo")
set_kind("binary")
add_files("src/*.c")
而僅有的20%的工程河胎,才需要這樣描述:
target("demo")
set_kind("shared")
set_objectdir("$(buildir)/.objs")
set_targetdir("libs/armeabi")
add_files("jni/*.c")
on_package(function (target)
os.run("ant debug")
end)
on_install(function (target)
os.run("adb install -r ./bin/Demo-debug.apk")
end)
on_run(function (target)
os.run("adb shell am start -n com.demo/com.demo.DemoTest")
os.run("adb logcat")
end)
上面的function () end
部分屬于自定義腳本域闯袒,一般情況下是不需要設(shè)置的,只有在需要復(fù)雜的工程描述游岳、高度定制化需求的情況下政敢,才需要自定義他們,在這個(gè)作用域可以使用各種xmake提供的擴(kuò)展模塊胚迫,關(guān)于這個(gè)的更多介紹喷户,見(jiàn):xmake 描述語(yǔ)法和作用域詳解。
而上面的代碼晌区,也是一個(gè)自定義混合構(gòu)建jni和java代碼的android工程摩骨,可以直接通過(guò)xmake run
命令通贞,實(shí)現(xiàn)一鍵自動(dòng)構(gòu)建、安裝恼五、運(yùn)行apk程序昌罩。
下面介紹一些比較常用的xmake描述實(shí)例:
構(gòu)建一個(gè)可執(zhí)行程序
target("demo")
set_kind("binary")
add_files("src/*.c")
這是一個(gè)最簡(jiǎn)單經(jīng)典的實(shí)例,一般情況下灾馒,這種情況茎用,你不需要自己寫(xiě)任何xmake.lua
文件,在當(dāng)前代碼目錄下睬罗,直接執(zhí)行xmake
命令轨功,就可以完成構(gòu)建,并且會(huì)自動(dòng)幫你生成一個(gè)xmake.lua
容达。
關(guān)于自動(dòng)生成的詳細(xì)信息古涧,見(jiàn):xmake智能代碼掃描編譯模式,無(wú)需手寫(xiě)任何make文件花盐。
構(gòu)建一個(gè)可配置切換的庫(kù)程序
target("demo")
set_kind("$(kind)")
add_files("src/*.c")
可通過(guò)配置羡滑,切換是否編譯動(dòng)態(tài)庫(kù)還是靜態(tài)庫(kù):
$ xmake f --kind=static; xmake
$ xmake f --kind=shared; xmake
增加debug和release編譯模式支持
也許默認(rèn)的幾行描述配置,已經(jīng)不能滿足你的需求算芯,你需要可以通過(guò)切換編譯模式柒昏,構(gòu)建debug和release版本的程序,那么只需要:
if is_mode("debug") then
set_symbols("debug")
set_optimize("none")
end
if is_mode("release") then
set_symbols("hidden")
set_optimize("fastest")
set_strip("all")
end
target("demo")
set_kind("binary")
add_files("src/*.c")
你只需要通過(guò)配置來(lái)切換構(gòu)建模式:
$ xmake f -m debug; xmake
$ xmake f -m release; xmake
[-m|--mode]
屬于內(nèi)置選項(xiàng)熙揍,不需要自己定義option
职祷,就可使用,并且模式的值是用戶自己定義和維護(hù)的届囚,你可以在is_mode("xxx")
判斷各種模式狀態(tài)有梆。
通過(guò)自定義腳本簽名ios程序
ios的可執(zhí)行程序,在設(shè)備上運(yùn)行奖亚,需要在構(gòu)建完成后進(jìn)行簽名淳梦,這個(gè)時(shí)候就可以使用自定義腳本來(lái)實(shí)現(xiàn):
target("demo")
set_kind("binary")
add_files("src/*.m")
after_build(function (target))
os.run("ldid -S %s", target:targetfile())
end
這里只是用ldid程序做了個(gè)假簽名,只能在越獄設(shè)備上用哦昔字,僅僅作為例子參考哈爆袍。
內(nèi)置變量和外置變量
xmake提供了$(varname)
的語(yǔ)法,來(lái)支持內(nèi)置變量的獲取作郭,例如:
add_cxflags("-I$(buildir)")
它將會(huì)在在實(shí)際編譯的時(shí)候陨囊,將內(nèi)置的buildir
變量轉(zhuǎn)換為實(shí)際的構(gòu)建輸出目錄:-I./build
一般內(nèi)置變量可用于在傳參時(shí)快速獲取和拼接變量字符串,例如:
target("test")
add_files("$(projectdir)/src/*.c")
add_includedirs("$(buildir)/inc")
也可以在自定義腳本的模塊接口中使用夹攒,例如:
target("test")
on_run(function (target)
os.cp("$(scriptdir)/xxx.h", "$(buildir)/inc")
end)
當(dāng)然這種變量模式蜘醋,也是可以擴(kuò)展的,默認(rèn)通過(guò)xmake f --var=val
命令咏尝,配置的參數(shù)都是可以直接獲取压语,例如:
target("test")
add_defines("-DTEST=$(var)")
既然支持直接從配置選項(xiàng)中獲取啸罢,那么當(dāng)然也就能很方便的擴(kuò)展自定義的選項(xiàng),來(lái)獲取自定義的變量了胎食,具體如何自定義選項(xiàng)見(jiàn):option
修改目標(biāo)文件名
我們可以通過(guò)內(nèi)建變量扰才,將生成的目標(biāo)文件按不同架構(gòu)和平臺(tái)進(jìn)行分離,例如:
target("demo")
set_kind("binary")
set_basename("demo_$(arch)")
set_targetdir("$(buildir)/$(plat)")
之前的默認(rèn)設(shè)置厕怜,目標(biāo)文件會(huì)生成為build\demo
衩匣,而通過(guò)上述代碼的設(shè)置,目標(biāo)文件在不同配置構(gòu)建下粥航,路徑和文件名也不盡相同琅捏,執(zhí)行:
$ xmake f -p iphoneos -a arm64; xmake
則目標(biāo)文件為:build/iphoneos/demo_arm64
。
添加子目錄工程模塊
如果你有多個(gè)target子模塊递雀,那么可以在一個(gè)xmake.lua
中進(jìn)行定義柄延,例如:
target("demo")
set_kind("binary")
add_files("src/demo.c")
target("test")
set_kind("binary")
add_files("src/test.c")
但是,如果子模塊非常多缀程,那么放置在一個(gè)xmake文件拦焚,就顯得有些臃腫了,可以放置到獨(dú)立模塊的子目錄去:
target("demo")
set_kind("binary")
add_files("src/demo.c")
add_subdirs("src/test")
通過(guò)上述代碼杠输,關(guān)聯(lián)一個(gè)子工程目錄,在里面加上test
的工程目標(biāo)就行了秕衙。
安裝頭文件
target("tbox")
set_kind("static")
add_files("src/*.c")
add_headers("../(tbox/**.h)|**/impl/**.h")
set_headerdir("$(buildir)/inc")
安裝好的頭文件位置和目錄結(jié)構(gòu)為:build/inc/tbox/*.h
蠢甲。
其中../(tbox/**.h)
帶括號(hào)的部分,為實(shí)際要安裝的根路徑据忘,|**/impl/**.h
部分用于排除不需要安裝的文件鹦牛。
其通配符匹配規(guī)則、排除規(guī)則可參考add_files勇吊。
多目標(biāo)依賴構(gòu)建
多個(gè)target工程目標(biāo)曼追,默認(rèn)構(gòu)建順序是未定義的,一般按順序的方式進(jìn)行汉规,如果你需要調(diào)整構(gòu)建順序礼殊,可以通過(guò)添加依賴順序來(lái)實(shí)現(xiàn):
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")
上面的例子,在編譯目標(biāo)demo的時(shí)候针史,需要先編譯test1, test2目標(biāo)晶伦,因?yàn)閐emo會(huì)去用到它們。
合并靜態(tài)庫(kù)
xmake的add_files接口功能是非常強(qiáng)大的啄枕,不僅可以支持多種語(yǔ)言文件的混合添加構(gòu)建婚陪,還可以直接添加靜態(tài)庫(kù),進(jìn)行自動(dòng)合并庫(kù)到當(dāng)前的工程目標(biāo)中去频祝。
我們可以這么寫(xiě):
target("demo")
set_kind("static")
add_files("src/*.c", "libxxx.a", "lib*.a", "xxx.lib")
直接在編譯靜態(tài)庫(kù)的時(shí)候泌参,合并多個(gè)已有的靜態(tài)庫(kù)脆淹,注意不是鏈接哦,這跟add_links是有區(qū)別的沽一。
并且你也可以直接追加對(duì)象文件:
target("demo")
set_kind("binary")
add_files("src/*.c", "objs/*.o")
添加自定義配置選項(xiàng)
我們可以自己定義一個(gè)配置選項(xiàng)盖溺,例如用于啟用test:
option("test")
set_default(false)
set_showmenu(true)
add_defines("-DTEST")
然后關(guān)聯(lián)到指定的target中去:
target("demo")
add_options("test")
這樣,一個(gè)選項(xiàng)就算定義好了锯玛,如果這個(gè)選項(xiàng)被啟用咐柜,那么編譯這個(gè)target的時(shí)候,就會(huì)自動(dòng)加上-DTEST
的宏定義攘残。
上面的設(shè)置拙友,默認(rèn)是禁用test
選項(xiàng)的,接下來(lái)我們通過(guò)配置去啟用這個(gè)選項(xiàng):
$ xmake f --test=y
$ xmake
xmake的選項(xiàng)支持是非常強(qiáng)大的歼郭,除了上述基礎(chǔ)用法外遗契,還可以配置各種檢測(cè)條件,實(shí)現(xiàn)自動(dòng)檢測(cè)病曾,具體詳情可參考:option和依賴包的添加和自動(dòng)檢測(cè)機(jī)制牍蜂。
添加第三方依賴包
在target作用域中,添加集成第三方包依賴泰涂,例如:
target("test")
set_kind("binary")
add_packages("zlib", "polarssl", "pcre", "mysql")
這樣鲫竞,在編譯test目標(biāo)時(shí),如果這個(gè)包存在的逼蒙,將會(huì)自動(dòng)追加包里面的宏定義从绘、頭文件搜索路徑、鏈接庫(kù)目錄是牢,也會(huì)自動(dòng)鏈接包中所有庫(kù)僵井。
用戶不再需要自己?jiǎn)为?dú)調(diào)用add_links
,add_includedirs
, add_ldflags
等接口驳棱,來(lái)配置依賴庫(kù)鏈接了批什。
對(duì)于如何設(shè)置包搜索目錄,可參考add_packagedirs接口社搅,依賴包詳情請(qǐng)參考:依賴包的添加和自動(dòng)檢測(cè)機(jī)制驻债。
生成配置頭文件
如果你想在xmake配置項(xiàng)目成功后,或者自動(dòng)檢測(cè)某個(gè)選項(xiàng)通過(guò)后形葬,把檢測(cè)的結(jié)果寫(xiě)入配置頭文件却汉,那么需要調(diào)用這個(gè)接口來(lái)啟用自動(dòng)生成config.h
文件。
使用方式例如:
target("test")
set_config_h("$(buildir)/config.h")
set_config_h_prefix("TB_CONFIG")
當(dāng)這個(gè)target中通過(guò)下面的這些接口荷并,對(duì)這個(gè)target添加了相關(guān)的選項(xiàng)依賴合砂、包依賴、接口依賴后,如果某依賴被啟用翩伪,那么對(duì)應(yīng)的一些宏定義配置微猖,會(huì)自動(dòng)寫(xiě)入被設(shè)置的config.h
文件中去。
這些接口缘屹,其實(shí)底層都用到了option選項(xiàng)中的一些檢測(cè)設(shè)置凛剥,例如:
option("wchar")
-- 添加對(duì)wchar_t類型的檢測(cè)
add_ctypes("wchar_t")
-- 如果檢測(cè)通過(guò),自動(dòng)生成TB_CONFIG_TYPE_HAVE_WCHAR的宏開(kāi)關(guān)到config.h
add_defines_h_if_ok("$(prefix)_TYPE_HAVE_WCHAR")
target("test")
-- 啟用頭文件自動(dòng)生成
set_config_h("$(buildir)/config.h")
set_config_h_prefix("TB_CONFIG")
-- 添加對(duì)wchar選項(xiàng)的依賴關(guān)聯(lián)轻姿,只有加上這個(gè)關(guān)聯(lián)犁珠,wchar選項(xiàng)的檢測(cè)結(jié)果才會(huì)寫(xiě)入指定的config.h中去
add_options("wchar")
檢測(cè)庫(kù)頭文件和接口
我們可以在剛剛生成的config.h
中增加一些庫(kù)接口檢測(cè),例如:
target("demo")
-- 設(shè)置和啟用config.h
set_config_h("$(buildir)/config.h")
set_config_h_prefix("TEST")
-- 僅通過(guò)參數(shù)一設(shè)置模塊名前綴
add_cfunc("libc", nil, nil, {"sys/select.h"}, "select")
-- 通過(guò)參數(shù)三互亮,設(shè)置同時(shí)檢測(cè)鏈接庫(kù):libpthread.a
add_cfunc("pthread", nil, "pthread", "pthread.h", "pthread_create")
-- 通過(guò)參數(shù)二設(shè)置接口別名
add_cfunc(nil, "PTHREAD", nil, "pthread.h", "pthread_create")
生成的config.h
結(jié)果如下:
#ifndef TEST_H
#define TEST_H
// 宏命名規(guī)則:$(prefix)前綴 _ 模塊名(如果非nil)_ HAVE _ 接口名或者別名 (大寫(xiě))
#define TEST_LIBC_HAVE_SELECT 1
#define TEST_PTHREAD_HAVE_PTHREAD_CREATE 1
#define TEST_HAVE_PTHREAD 1
#endif
這樣我們?cè)诖a里面就可以根據(jù)接口的支持力度來(lái)控制代碼編譯了犁享。
自定義插件任務(wù)
task域用于描述一個(gè)自定義的任務(wù)實(shí)現(xiàn),與target和option同級(jí)豹休。
例如炊昆,這里定義一個(gè)最簡(jiǎn)單的任務(wù):
task("hello")
-- 設(shè)置運(yùn)行腳本
on_run(function ()
print("hello xmake!")
end)
這個(gè)任務(wù)只需要打印hello xmake!
,那如何來(lái)運(yùn)行呢威根?
由于這里沒(méi)有使用set_menu設(shè)置菜單凤巨,因此這個(gè)任務(wù)只能在xmake.lua
的自定義腳本或者其他任務(wù)內(nèi)部調(diào)用,例如:
target("test")
after_build(function (target)
-- 導(dǎo)入task模塊
import("core.project.task")
-- 運(yùn)行hello任務(wù)
task.run("hello")
end)
此處在構(gòu)建完test目標(biāo)后運(yùn)行hello任務(wù)洛搀,當(dāng)然我們還可以傳遞參數(shù)哦:
task("hello")
on_run(function (arg1, arg2, arg3)
print("hello xmake!", arg1, arg2, arg3)
end)
target("test")
after_build(function (target)
import("core.project.task")
task.run("hello", {}, "arg1", "arg2", "arg3")
end)
上述task.run
的{}
這個(gè)是用于傳遞插件菜單中的參數(shù)敢茁,這里沒(méi)有通過(guò)set_menu設(shè)置菜單,此處傳空留美。
xmake的插件支持也是功能很強(qiáng)大的卷要,并且提供了很多內(nèi)置的使用插件,具體請(qǐng)參考:xmake插件手冊(cè)和task手冊(cè)
或者可以參考xmake自帶的一些插件demo独榴。
另外一種語(yǔ)法風(fēng)格
xmake除了支持最常使用的set-add
描述風(fēng)格外,還支持另外一種語(yǔ)法風(fēng)格:key-val
奕枝,例如:
target
{
name = "test",
defines = "DEBUG",
files = {"src/*.c", "test/*.cpp"}
}
這個(gè)等價(jià)于:
target("test")
set_kind("static")
add_defines("DEBUG")
add_files("src/*.c", "test/*.cpp")
用戶可以根據(jù)自己的喜好來(lái)選擇合適的風(fēng)格描述棺榔,但是這邊的建議是:
* 針對(duì)簡(jiǎn)單的工程,不需要太過(guò)復(fù)雜的條件編譯隘道,可以使用key-val方式症歇,更加精簡(jiǎn),可讀性好
* 針對(duì)復(fù)雜工程谭梗,需要更高的可控性忘晤,和靈活性的話,建議使用set-add方式
* 盡量不要兩種風(fēng)格混著寫(xiě)激捏,雖然是支持的设塔,但是這樣對(duì)整個(gè)工程描述會(huì)感覺(jué)很亂,因此盡量統(tǒng)一風(fēng)格作為自己的描述規(guī)范
另外远舅,不僅對(duì)target闰蛔,像option, task, template都是支持兩種方式設(shè)置的痕钢,例如:
-- set-add風(fēng)格
option("demo")
set_default(true)
set_showmenu(true)
set_category("option")
set_description("Enable or disable the demo module", " =y|n")
-- key-val風(fēng)格
option
{
name = "demo",
default = true,
showmenu = true,
category = "option",
desciption = {"Enable or disable the demo module", " =y|n"}
}
自定義的任務(wù)或者插件可以這么寫(xiě):
-- set-add風(fēng)格
task("hello")
on_run(function ()
print("hello xmake!")
end)
set_menu {
usage = "xmake hello [options]",
description = "Hello xmake!",
options = {}
}
-- key-val風(fēng)格
task
{
name = "hello",
run = (function ()
print("hello xmake!")
end),
menu = {
usage = "xmake hello [options]",
description = "Hello xmake!",
options = {}
}
}
結(jié)語(yǔ)
更多描述說(shuō)明,可直接閱讀xmake的官方手冊(cè)序六,上面提供了完整的api文檔和使用描述任连。
個(gè)人主頁(yè):TBOOX開(kāi)源工程