使用xmake优雅地描述工程
描述語法
xmake的描述語法基于lua實現,因此描述語法繼承了lua的靈活性和簡潔性,并且通過28原則,將描述作用域(簡單描述)、腳本作用域(復雜描述)進行分離,使得工程更加的簡潔直觀,可讀性非常好。
因為80%的工程,并不需要很復雜的腳本控制邏輯,只需要簡單的幾行配置描述,就可滿足構建需求,基于這個假設,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部分屬于自定義腳本域,一般情況下是不需要設置的,只有在需要復雜的工程描述、高度定制化需求的情況下,才需要自定義他們,在這個作用域可以使用各種xmake提供的擴展模塊,關于這個的更多介紹,見:xmake 描述語法和作用域詳解。
而上面的代碼,也是一個自定義混合構建jni和java代碼的android工程,可以直接通過xmake run命令,實現一鍵自動構建、安裝、運行apk程序。
下面介紹一些比較常用的xmake描述實例:
構建一個可執行程序
target("demo")set_kind("binary")add_files("src/*.c")這是一個最簡單經典的實例,一般情況下,這種情況,你不需要自己寫任何xmake.lua文件,在當前代碼目錄下,直接執行xmake命令,就可以完成構建,并且會自動幫你生成一個xmake.lua。
關于自動生成的詳細信息,見:xmake智能代碼掃描編譯模式,無需手寫任何make文件。
構建一個可配置切換的庫程序
target("demo")set_kind("$(kind)")add_files("src/*.c")可通過配置,切換是否編譯動態庫還是靜態庫:
$ xmake f --kind=static; xmake $ xmake f --kind=shared; xmake增加debug和release編譯模式支持
也許默認的幾行描述配置,已經不能滿足你的需求,你需要可以通過切換編譯模式,構建debug和release版本的程序,那么只需要:
if is_mode("debug") thenset_symbols("debug")set_optimize("none") endif is_mode("release") thenset_symbols("hidden")set_optimize("fastest")set_strip("all") endtarget("demo")set_kind("binary")add_files("src/*.c")你只需要通過配置來切換構建模式:
$ xmake f -m debug; xmake $ xmake f -m release; xmake[-m|--mode]屬于內置選項,不需要自己定義option,就可使用,并且模式的值是用戶自己定義和維護的,你可以在is_mode("xxx")判斷各種模式狀態。
通過自定義腳本簽名ios程序
ios的可執行程序,在設備上運行,需要在構建完成后進行簽名,這個時候就可以使用自定義腳本來實現:
target("demo")set_kind("binary")add_files("src/*.m") after_build(function (target))os.run("ldid -S %s", target:targetfile())end這里只是用ldid程序做了個假簽名,只能在越獄設備上用哦,僅僅作為例子參考哈。
內置變量和外置變量
xmake提供了$(varname)的語法,來支持內置變量的獲取,例如:
add_cxflags("-I$(buildir)")它將會在在實際編譯的時候,將內置的buildir變量轉換為實際的構建輸出目錄:-I./build
一般內置變量可用于在傳參時快速獲取和拼接變量字符串,例如:
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)當然這種變量模式,也是可以擴展的,默認通過xmake f --var=val命令,配置的參數都是可以直接獲取,例如:
target("test")add_defines("-DTEST=$(var)")既然支持直接從配置選項中獲取,那么當然也就能很方便的擴展自定義的選項,來獲取自定義的變量了,具體如何自定義選項見:option
修改目標文件名
我們可以通過內建變量,將生成的目標文件按不同架構和平臺進行分離,例如:
target("demo")set_kind("binary")set_basename("demo_$(arch)")set_targetdir("$(buildir)/$(plat)")之前的默認設置,目標文件會生成為build\demo,而通過上述代碼的設置,目標文件在不同配置構建下,路徑和文件名也不盡相同,執行:
$ xmake f -p iphoneos -a arm64; xmake則目標文件為:build/iphoneos/demo_arm64。
添加子目錄工程模塊
如果你有多個target子模塊,那么可以在一個xmake.lua中進行定義,例如:
target("demo")set_kind("binary")add_files("src/demo.c")target("test")set_kind("binary")add_files("src/test.c")但是,如果子模塊非常多,那么放置在一個xmake文件,就顯得有些臃腫了,可以放置到獨立模塊的子目錄去:
target("demo")set_kind("binary")add_files("src/demo.c")add_subdirs("src/test")通過上述代碼,關聯一個子工程目錄,在里面加上test的工程目標就行了。
安裝頭文件
target("tbox")set_kind("static")add_files("src/*.c")add_headers("../(tbox/**.h)|**/impl/**.h")set_headerdir("$(buildir)/inc")安裝好的頭文件位置和目錄結構為:build/inc/tbox/*.h。
其中../(tbox/**.h)帶括號的部分,為實際要安裝的根路徑,|**/impl/**.h部分用于排除不需要安裝的文件。
其通配符匹配規則、排除規則可參考add_files。
多目標依賴構建
多個target工程目標,默認構建順序是未定義的,一般按順序的方式進行,如果你需要調整構建順序,可以通過添加依賴順序來實現:
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")上面的例子,在編譯目標demo的時候,需要先編譯test1, test2目標,因為demo會去用到它們。
合并靜態庫
xmake的add_files接口功能是非常強大的,不僅可以支持多種語言文件的混合添加構建,還可以直接添加靜態庫,進行自動合并庫到當前的工程目標中去。
我們可以這么寫:
target("demo")set_kind("static")add_files("src/*.c", "libxxx.a", "lib*.a", "xxx.lib")直接在編譯靜態庫的時候,合并多個已有的靜態庫,注意不是鏈接哦,這跟add_links是有區別的。
并且你也可以直接追加對象文件:
target("demo")set_kind("binary")add_files("src/*.c", "objs/*.o")添加自定義配置選項
我們可以自己定義一個配置選項,例如用于啟用test:
option("test")set_default(false)set_showmenu(true)add_defines("-DTEST")然后關聯到指定的target中去:
target("demo")add_options("test")這樣,一個選項就算定義好了,如果這個選項被啟用,那么編譯這個target的時候,就會自動加上-DTEST的宏定義。
上面的設置,默認是禁用test選項的,接下來我們通過配置去啟用這個選項:
$ xmake f --test=y $ xmakexmake的選項支持是非常強大的,除了上述基礎用法外,還可以配置各種檢測條件,實現自動檢測,具體詳情可參考:option和依賴包的添加和自動檢測機制。
添加第三方依賴包
在target作用域中,添加集成第三方包依賴,例如:
target("test")set_kind("binary")add_packages("zlib", "polarssl", "pcre", "mysql")這樣,在編譯test目標時,如果這個包存在的,將會自動追加包里面的宏定義、頭文件搜索路徑、鏈接庫目錄,也會自動鏈接包中所有庫。
用戶不再需要自己單獨調用add_links,add_includedirs, add_ldflags等接口,來配置依賴庫鏈接了。
對于如何設置包搜索目錄,可參考add_packagedirs接口,依賴包詳情請參考:依賴包的添加和自動檢測機制。
生成配置頭文件
如果你想在xmake配置項目成功后,或者自動檢測某個選項通過后,把檢測的結果寫入配置頭文件,那么需要調用這個接口來啟用自動生成config.h文件。
使用方式例如:
target("test")set_config_h("$(buildir)/config.h")set_config_h_prefix("TB_CONFIG")當這個target中通過下面的這些接口,對這個target添加了相關的選項依賴、包依賴、接口依賴后,如果某依賴被啟用,那么對應的一些宏定義配置,會自動寫入被設置的config.h文件中去。
- add_options
- add_packages
- add_cfuncs
- add_cxxfuncs
這些接口,其實底層都用到了option選項中的一些檢測設置,例如:
option("wchar")-- 添加對wchar_t類型的檢測add_ctypes("wchar_t")-- 如果檢測通過,自動生成TB_CONFIG_TYPE_HAVE_WCHAR的宏開關到config.hadd_defines_h_if_ok("$(prefix)_TYPE_HAVE_WCHAR")target("test")-- 啟用頭文件自動生成set_config_h("$(buildir)/config.h")set_config_h_prefix("TB_CONFIG")-- 添加對wchar選項的依賴關聯,只有加上這個關聯,wchar選項的檢測結果才會寫入指定的config.h中去add_options("wchar")檢測庫頭文件和接口
我們可以在剛剛生成的config.h中增加一些庫接口檢測,例如:
target("demo")-- 設置和啟用config.hset_config_h("$(buildir)/config.h")set_config_h_prefix("TEST")-- 僅通過參數一設置模塊名前綴add_cfunc("libc", nil, nil, {"sys/select.h"}, "select")-- 通過參數三,設置同時檢測鏈接庫:libpthread.aadd_cfunc("pthread", nil, "pthread", "pthread.h", "pthread_create")-- 通過參數二設置接口別名add_cfunc(nil, "PTHREAD", nil, "pthread.h", "pthread_create")生成的config.h結果如下:
#ifndef TEST_H #define TEST_H// 宏命名規則:$(prefix)前綴 _ 模塊名(如果非nil)_ HAVE _ 接口名或者別名 (大寫) #define TEST_LIBC_HAVE_SELECT 1 #define TEST_PTHREAD_HAVE_PTHREAD_CREATE 1 #define TEST_HAVE_PTHREAD 1#endif這樣我們在代碼里面就可以根據接口的支持力度來控制代碼編譯了。
自定義插件任務
task域用于描述一個自定義的任務實現,與target和option同級。
例如,這里定義一個最簡單的任務:
task("hello")-- 設置運行腳本on_run(function ()print("hello xmake!")end)這個任務只需要打印hello xmake!,那如何來運行呢?
由于這里沒有使用set_menu設置菜單,因此這個任務只能在xmake.lua的自定義腳本或者其他任務內部調用,例如:
target("test")after_build(function (target)-- 導入task模塊import("core.project.task")-- 運行hello任務task.run("hello")end)此處在構建完test目標后運行hello任務,當然我們還可以傳遞參數哦:
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的{}這個是用于傳遞插件菜單中的參數,這里沒有通過set_menu設置菜單,此處傳空。
xmake的插件支持也是功能很強大的,并且提供了很多內置的使用插件,具體請參考:xmake插件手冊和task手冊
或者可以參考xmake自帶的一些插件demo。
另外一種語法風格
xmake除了支持最常使用的set-add描述風格外,還支持另外一種語法風格:key-val,例如:
target {name = "test",defines = "DEBUG",files = {"src/*.c", "test/*.cpp"} }這個等價于:
target("test")set_kind("static")add_defines("DEBUG")add_files("src/*.c", "test/*.cpp")用戶可以根據自己的喜好來選擇合適的風格描述,但是這邊的建議是:
* 針對簡單的工程,不需要太過復雜的條件編譯,可以使用key-val方式,更加精簡,可讀性好 * 針對復雜工程,需要更高的可控性,和靈活性的話,建議使用set-add方式 * 盡量不要兩種風格混著寫,雖然是支持的,但是這樣對整個工程描述會感覺很亂,因此盡量統一風格作為自己的描述規范另外,不僅對target,像option, task, template都是支持兩種方式設置的,例如:
-- set-add風格 option("demo")set_default(true)set_showmenu(true)set_category("option")set_description("Enable or disable the demo module", " =y|n") -- key-val風格 option {name = "demo",default = true,showmenu = true,category = "option",desciption = {"Enable or disable the demo module", " =y|n"} }自定義的任務或者插件可以這么寫:
-- set-add風格 task("hello")on_run(function ()print("hello xmake!")end)set_menu {usage = "xmake hello [options]",description = "Hello xmake!",options = {}} -- key-val風格 task {name = "hello",run = (function ()print("hello xmake!")end),menu = {usage = "xmake hello [options]",description = "Hello xmake!",options = {}} }結語
更多描述說明,可直接閱讀xmake的官方手冊,上面提供了完整的api文檔和使用描述。
個人主頁:TBOOX開源工程
總結
以上是生活随笔為你收集整理的使用xmake优雅地描述工程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: webpack+vue+mint-ui
- 下一篇: 在fedora23中安装virtualb