xmake的工程描述文件xmake.lua雖然基於lua語法,但是爲了使得更加方便簡潔得編寫項目構建邏輯,xmake對其進行了一層封裝,使得編寫xmake.lua不會像些makefile那樣繁瑣
基本上寫個簡單的工程構建描述,只需三行就能搞定,例如:
target("test")
set_kind("binary")
add_files("src/*.c")
然後只需要執行編譯並且運行它:
$ xmake run test
這對於想要臨時寫些測試代碼來講,極大地提升了開發效率。。
作用域與工程描述語法
xmake的描述語法是按作用域劃分的,主要分爲:
- 外部作用域
- 內部作用域
那哪些屬於外部,哪些又屬於內部呢,看看下面的註釋,就知道個大概了:
-- 外部作用域
target("test")
-- 外部作用域
set_kind("binary")
add_files("src/*.c")
on_run(function ()
-- 內部作用域
end)
after_package(function ()
-- 內部作用域
end)
-- 外部作用域
task("hello")
-- 外部作用域
on_run(function ()
-- 內部作用域
end)
簡單的說,就是在自定義腳本function () end
之內的都屬於內部作用域,也就是腳本作用域,其他地方都是都屬於於外部作用域。。
外部作用域
對於大部分工程來說,並不需要很複雜的工程描述,也不需要自定義腳本支持,只需要簡單的 set_xxx
或者 add_xxx
就能滿足需求了
那麼根據二八定律,80%的情況下,我們只需要這麼寫:
target("test")
set_kind("static")
add_files("src/test/*.c")
target("demo")
add_deps("test")
set_kind("binary")
add_links("test")
add_files("src/demo/*.c")
不需要複雜的api調用,也不需要各種繁瑣的變量定義,以及 if
判斷 和 for
循環,要的就是簡潔可讀,一眼看過去,就算不懂lua語法也沒關係
就當做簡單的描述語法,看上去有點像函數調用而已,會點編程的基本一看就知道怎麼配置。
爲了做到簡潔、安全,在這個作用域內,很多lua 內置api是不開放出來的,尤其是跟寫文件、修改操作環境相關的,僅僅提供一些基本的只讀接口,和邏輯操作
目前外部作用域開放的lua內置api有:
- table
- string
- pairs
- ipairs
- print:修改版,提供格式化打印支持
- os:僅提供只讀接口,例如getenv等等
當然雖然內置lua api提供不多,但xmake還提供了很多擴展api,像描述api就不多說,詳細可參考:工程描述api文檔
還有些輔助api,例如:
- dirs:掃描獲取當前指定路徑中的所有目錄
- files:掃描獲取當前指定路徑中的所有文件
- format: 格式化字符串,string.format的簡寫版本
還有變量定義、邏輯操作也是可以使用的,畢竟是基於lua的,該有的基礎語法,還是要有的,我們可以通過if來切換編譯文件:
target("test")
set_kind("static")
if is_plat("iphoneos") then
add_files("src/test/ios/*.c")
else
add_files("src/test/*.c")
end
我們也可以啓用和禁用某個子工程target:
if is_arch("arm*") then
target("test1")
set_kind("static")
add_files("src/*.c")
else
target("test2")
set_kind("static")
add_files("src/*.c")
end
需要注意的是,變量定義分全局變量和局部變量,局部變量只對當前xmake.lua有效,不影響子xmake.lua
-- 局部變量,只對當前xmake.lua有效
local var1 = 0
-- 全局變量,影響所有之後 add_subfiles(), add_subdirs() 包含的子 xmake.lua
var2 = 1
add_subdirs("src")
內部作用域
也稱插件、腳本作用域,提供更加複雜、靈活的腳本支持,一般用於編寫一些自定義腳本、插件開發、自定義task任務、自定義模塊等等
一般通過 function () end
包含,並且被傳入到 on_xxx
, before_xxx
和after_xxx
接口內的,都屬於自作用域。
例如:
-- 自定義腳本
target("hello")
after_build(function ()
-- 內部作用域
end)
-- 自定義任務、插件
task("hello")
on_run(function ()
-- 內部作用域
end)
在此作用域中,不僅可以使用大部分lua的api,還可以使用很多xmake提供的擴展模塊,所有擴展模塊,通過import
來導入
具體可參考:插件開發之import類庫
這裏我們給個簡單的例子,在編譯完成後,對ios目標程序進行ldid簽名:
target("iosdemo")
set_kind("binary")
add_files("*.m")
after_build( function (target)
-- 執行簽名,如果失敗,自動中斷,給出高亮錯誤信息
os.run("ldid -S$(projectdir)/entitlements.plist %s", target:targetfile())
end)
需要注意的是,在內部作用域中,所有的調用都是啓用異常捕獲機制的,如果運行出錯,會自動中斷xmake,並給出錯誤提示信息
因此,腳本寫起來,不需要繁瑣的if retval then
判斷,腳本邏輯更加一目瞭然
接口作用域
在外部作用域中的所有描述api設置,本身也是有作用域之分的,在不同地方調用,影響範圍也不相同,例如:
-- 全局根作用域,影響所有target,包括 add_subdirs() 中的子工程target設置
add_defines("DEBUG")
-- 定義或者進入demo目標作用域(支持多次進入來追加設置)
target("demo")
set_kind("shared")
add_files("src/*.c")
-- 當前target作用域,僅僅影響當前target
add_defines("DEBUG2")
-- 選項設置,僅支持局部設置,不受全局api設置所影響
option("test")
-- 當前選項的局部作用域
set_default(false)
-- 其他target設置,-DDEBUG 也會被設置上
target("demo2")
set_kind("binary")
add_files("src/*.c")
-- 重新進入demo目標作用域
target("demo")
-- 追加宏定義,只對當前demo目標有效
add_defines("DEBUG3")
xmake裏面還有些全局api,僅提供全局作用域支持,例如:
- add_subfiles()
- add_subdirs()
- add_packagedirs()
等等,這些調用不要放置在 target 或者 option 的局部作用域之間,雖然沒什麼實際區別,但是會影響可讀性,容易被誤導
使用方式,如下:
target("xxxx")
set_kind("binary")
add_files("*.c")
-- 包含子模塊文件
add_subdirs("src")
作用域縮進
xmake.lua裏面縮進,只是個編寫規範,用於更加清楚的區分,當前的設置 是針對 那個作用域的,雖然就算不縮進,也一樣ok,但是可讀性上 並不是很好。。
例如:
target("xxxx")
set_kind("binary")
add_files("*.c")
和
target("xxxx")
set_kind("binary")
add_files("*.c")
上述兩種方式,效果上都是一樣的,但是理解上,第一種更加直觀,一看就知道 add_files 僅僅只是針對 target 設置的,並不是全局設置
因此,適當的進行縮進,有助於更好的維護xmake.lua
最後附上,tbox的xmake.lua和src/tbox/xmake.lua描述,僅供參考。。
個人主頁:TBOOX開源工程
原文出處:http://tboox.org/cn/2016/10/26/api-scope/