亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

xmake如何通過自定義腳本實現更靈活地配置

發布時間:2021-11-11 16:45:04 來源:億速云 閱讀:186 作者:柒染 欄目:大數據

這期內容當中小編將會給大家帶來有關xmake如何通過自定義腳本實現更靈活地配置,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

xmake是一個基于Lua的輕量級現代化c/c++的項目構建工具,主要特點是:語法簡單易上手,提供更加可讀的項目維護,實現跨平臺行為一致的構建體驗。

如何通過添加自定義的腳本,在腳本域實現更加復雜靈活的定制。

配置分離

xmake.lua采用二八原則實現了描述域、腳本域兩層分離式配置。

什么是二八原則呢,簡單來說,大部分項目的配置,80%的情況下,都是些基礎的常規配置,比如:add_cxflags, add_links等,
只有剩下不到20%的地方才需要額外做些復雜來滿足一些特殊的配置需求。

而這剩余的20%的配置通常比較復雜,如果直接充斥在整個xmake.lua里面,會把整個項目的配置整個很混亂,非常不可讀。

因此,xmake通過描述域、腳本域兩種不同的配置方式,來隔離80%的簡單配置以及20%的復雜配置,使得整個xmake.lua看起來非常的清晰直觀,可讀性和可維護性都達到最佳。

 

描述域

對于剛入門的新手用戶,或者僅僅是維護一些簡單的小項目,通過完全在描述配置就已經完全滿足需求了,那什么是描述域呢?它長這樣:

target("test")
    set_kind("binary")
    add_files("src/*.c")
    add_defines("DEBUG")
    add_syslinks("pthread")
 

一眼望去,其實就是個 set_xxx/add_xxx的配置集,對于新手,完全可以不把它當做lua腳本,僅僅作為普通的,但有一些基礎規則的配置文件就行了。

這是不是看著更像配置文件了?其實描述域就是配置文件,類似像json等key/values的配置而已,所以即使完全不會lua的新手,也是能很快上手的。

而且,對于通常的項目,僅通過set_xxx/add_xxx去配置各種項目設置,已經完全滿足需求了。

這也就是開頭說的:80%的情況下,可以用最簡單的配置規則去簡化項目的配置,提高可讀性和可維護性,這樣對用戶和開發者都會非常的友好,也更加直觀。

如果我們要針對不同平臺,架構做一些條件判斷怎么辦?沒關系,描述域除了基礎配置,也是支持條件判斷,以及for循環的:

target("test")
    set_kind("binary")
    add_files("src/*.c")
    add_defines("DEBUG")
    if is_plat("linux", "macosx") then
        add_links("pthread", "m", "dl")
    end
 
target("test")
    set_kind("binary")
    add_files("src/*.c")
    add_defines("DEBUG")
    for _, name in ipairs({"pthread", "m", "dl"}) do
        add_links(name)
    end
 

這是不是看著有點像lua了?雖說,平常可以把它當做普通配置問題,但是xmake畢竟基于lua,所以描述域還是支持lua的基礎語言特性的。

!> 不過需要注意的是,描述域雖然支持lua的腳本語法,但在描述域盡量不要寫太復雜的lua腳本,比如一些耗時的函數調用和for循環

并且在描述域,主要目的是為了設置配置項,因此xmake并沒有完全開放所有的模塊接口,很多接口在描述域是被禁止調用的,
即使開放出來的一些可調用接口,也是完全只讀的,不耗時的安全接口,比如:os.getenv()等讀取一些常規的系統信息,用于配置邏輯的控制。

!> 另外需要注意一點,xmake.lua是會被多次解析的,用于在不同階段解析不同的配置域:比如:option(), target()等域。

因此,不要想著在xmake.lua的描述域,寫復雜的lua腳本,也不要在描述域調用print去顯示信息,因為會被執行多遍,記住:會被執行多遍!!!

 

腳本域

限制描述域寫復雜的lua,各種lua模塊和接口都用不了?怎么辦?這個時候就是腳本域出場的時候了。

如果用戶已經完全熟悉了xmake的描述域配置,并且感覺有些滿足不了項目上的一些特殊配置維護了,那么我們可以在腳本域做更加復雜的配置邏輯:

target("test")
    set_kind("binary")
    add_files("src/*.c")
    on_load(function (target)
        if is_plat("linux", "macosx") then
            target:add("links", "pthread", "m", "dl")
        end
    end)
    after_build(function (target)
        import("core.project.config")
        local targetfile = target:targetfile()
        os.cp(targetfile, path.join(config.buildir(), path.filename(targetfile)))
        print("build %s", targetfile)
    end)
 

只要是類似:on_xxx, after_xxx, before_xxx等字樣的function body內部的腳本,都屬于腳本域。

在腳本域中,用戶可以干任何事,xmake提供了import接口可以導入xmake內置的各種lua模塊,也可以導入用戶提供的lua腳本。

我們可以在腳本域實現你想實現的任意功能,甚至寫個獨立項目出來都是可以的。

對于一些腳本片段,不是很臃腫的話,像上面這么內置寫寫就足夠了,如果需要實現更加復雜的腳本,不想充斥在一個xmake.lua里面,可以把腳本分離到獨立的lua文件中去維護。

例如:

target("test")
    set_kind("binary")
    add_files("src/*.c")
    on_load("modules.test.load")
    on_install("modules.test.install")
 

我們可以把自定義的腳本放置到xmake.lua對應目錄下,modules/test/load.luamodules/test/install.lua中獨立維護。

單獨的lua腳本文件以main作為主入口,例如:

-- 我們也可以在此處導入一些內置模塊或者自己的擴展模塊來使用
import("core.project.config")
import("mymodule")

function main(target)
    if is_plat("linux", "macosx") then
        target:add("links", "pthread", "m", "dl")
    end
end
 

這些獨立的lua腳本里面,我們還可以通過import導入各種內置模塊和自定義模塊進來使用,就跟平常寫lua, java沒啥區別。

而對于腳本的域的不同階段,on_load主要用于target加載時候,做一些動態化的配置,這里不像描述域,只會執行一遍哦!!!

其他階段,還有很多,比如:on/after/before_build/install/package/run等,我們下面會詳細描述。

 

import

 
導入擴展模塊

在講解各個腳本域之前,我們先來簡單介紹下xmake的模塊導入和使用方式,xmake采用import來引入其他的擴展模塊,以及用戶自己定義的模塊,它可以在下面一些地方使用:

  • 自定義腳本(on_build, on_run ..)

  • 插件開發

  • 模板開發

  • 平臺擴展

  • 自定義任務task

導入機制如下:

  1. 優先從當前腳本目錄下導入

  2. 再從擴展類庫中導入

導入的語法規則:

基于.的類庫路徑規則,例如:

import("core.base.option")
import("core.base.task")

function main()

    -- 獲取參數選項
    print(option.get("version"))

    -- 運行任務和插件
    task.run("hello")
end
 

導入當前目錄下的自定義模塊:

目錄結構:

plugin
  - xmake.lua
  - main.lua
  - modules
    - hello1.lua
    - hello2.lua
 

在main.lua中導入modules

import("modules.hello1")
import("modules.hello2")
 

導入后就可以直接使用里面的所有公有接口,私有接口用_前綴標示,表明不會被導出,不會被外部調用到。。

除了當前目錄,我們還可以導入其他指定目錄里面的類庫,例如:

import("hello3", {rootdir = "/home/xxx/modules"})
 

為了防止命名沖突,導入后還可以指定的別名:

import("core.platform.platform", {alias = "p"})

function main()
    -- 這樣我們就可以使用p來調用platform模塊的plats接口,獲取所有xmake支持的平臺列表了
    print(p.plats())
end
 

2.1.5版本新增兩個新屬性:import("xxx.xxx", {try = true, anonymous = true})

try為true,則導入的模塊不存在的話,僅僅返回nil,并不會拋異常后中斷xmake.
anonymous為true,則導入的模塊不會引入當前作用域,僅僅在import接口返回導入的對象引用。

 

測試擴展模塊

一種方式我們可以在on_load等腳本中,直接調用print去打印模塊的調用結果信息,來測試和驗證。

不過xmake還提供了xmake lua插件可以更加靈活方便的測試腳本。

 
運行指定的腳本文件

比如,我們可以直接指定lua腳本來加載運行,這對于想要快速測試一些接口模塊,驗證自己的某些思路,都是一個不錯的方式。

我們先寫個簡單的lua腳本:

function main()
    print("hello xmake!")
end
 

然后直接運行它就行了:

$ xmake lua /tmp/test.lua
   
直接調用擴展模塊

所有內置模塊和擴展模塊的接口,我們都可以通過xmake lua直接調用,例如:

$ xmake lua lib.detect.find_tool gcc
 

上面的命令,我們直接調用了import("lib.detect.find_tool")模塊接口來快速執行。

 
運行交互命令 (REPL)

有時候在交互模式下,運行命令更加的方便測試和驗證一些模塊和api,也更加的靈活,不需要再去額外寫一個腳本文件來加載。

我們先看下,如何進入交互模式:

# 不帶任何參數執行,就可以進入
$ xmake lua
>

# 進行表達式計算
> 1 + 2
3

# 賦值和打印變量值
> a = 1
> a
1

# 多行輸入和執行
> for _, v in pairs({1, 2, 3}) do
>> print(v)
>> end
1
2
3
 

我們也能夠通過 import 來導入擴展模塊:

> task = import("core.project.task")
> task.run("hello")
hello xmake!
 

如果要中途取消多行輸入,只需要輸入字符:q 就行了

> for _, v in ipairs({1, 2}) do
>> print(v)
>> q             <--  取消多行輸入,清空先前的輸入數據
> 1 + 2
3
   

target:on_load

 
自定義目標加載腳本

在target初始化加載的時候,將會執行此腳本,在里面可以做一些動態的目標配置,實現更靈活的目標描述定義,例如:

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屬性,所有描述域的set_, add_配置都可以通過這種方式動態配置。

另外,我們可以調用target的一些接口,獲取和設置一些基礎信息,比如:

xmake如何通過自定義腳本實現更靈活地配置

 

target:on_link

 
自定義鏈接腳本

這個是在v2.2.7之后新加的接口,用于定制化處理target的鏈接過程。

target("test")
    on_link(function (target) 
        print("link it")
    end)
   

target:on_build

 
自定義編譯腳本

覆蓋target目標默認的構建行為,實現自定義的編譯過程,一般情況下,并不需要這么做,除非確實需要做一些xmake默認沒有提供的編譯操作。

你可以通過下面的方式覆蓋它,來自定義編譯操作:

target("test")

    -- 設置自定義編譯腳本
    on_build(function (target) 
        print("build it")
    end)
 

注:2.1.5版本之后,所有target的自定義腳本都可以針對不同平臺和架構,分別處理,例如:

target("test")
    on_build("iphoneos|arm*", function (target)
        print("build for iphoneos and arm")
    end)
 

其中如果第一個參數為字符串,那么就是指定這個腳本需要在哪個平臺|架構下,才會被執行,并且支持模式匹配,例如arm*匹配所有arm架構。

當然也可以只設置平臺,不設置架構,這樣就是匹配指定平臺下,執行腳本:

target("test")
    on_build("windows", function (target)
        print("build for windows")
    end)
 

注:一旦對這個target目標設置了自己的build過程,那么xmake默認的構建過程將不再被執行。

 

target:on_build_file

 
自定義編譯腳本, 實現單文件構建

通過此接口,可以用來hook指定target內置的構建過程,自己重新實現每個源文件編譯過程:

target("test")
    set_kind("binary")
    add_files("src/*.c")
    on_build_file(function (target, sourcefile, opt)
    end)
   

target:on_build_files

 
自定義編譯腳本, 實現多文件構建

通過此接口,可以用來hook指定target內置的構建過程,替換一批同類型源文件編譯過程:

target("test")
    set_kind("binary")
    add_files("src/*.c")
    on_build_files(function (target, sourcebatch, opt)
    end)
 

設置此接口后,對應源文件列表中文件,就不會出現在自定義的target.on_build_file了,因為這個是包含關系。

其中sourcebatch描述了這批同類型源文件:

  • sourcebatch.sourcekind: 獲取這批源文件的類型,比如:cc, as, ..

  • sourcebatch.sourcefiles(): 獲取源文件列表

  • sourcebatch.objectfiles(): 獲取對象文件列表

  • sourcebatch.dependfiles(): 獲取對應依賴文件列表,存有源文件中編譯依賴信息,例如:xxx.d

 

target:on_clean

 
自定義清理腳本

覆蓋target目標的xmake [c|clean}的清理操作,實現自定義清理過程。

target("test")

    -- 設置自定義清理腳本
    on_clean(function (target) 

        -- 僅刪掉目標文件
        os.rm(target:targetfile())
    end)
   

target:on_package

 
自定義打包腳本

覆蓋target目標的xmake [p|package}的打包操作,實現自定義打包過程,如果你想對指定target打包成自己想要的格式,可以通過這個接口自定義它。

target("demo")
    set_kind("shared")
    add_files("jni/*.c")
    on_package(function (target) 
        os.exec("./gradlew app:assembleDebug") 
    end)
 

當然這個例子有點老了,這里只是舉例說明下用法而已,現在xmake提供了專門的xmake-gradle插件,來與gradle更好的集成。

 

target:on_install

 
自定義安裝腳本

覆蓋target目標的xmake [i|install}的安裝操作,實現自定義安裝過程。

例如,將生成的apk包,進行安裝。

target("test")

    -- 設置自定義安裝腳本,自動安裝apk文件
    on_install(function (target) 

        -- 使用adb安裝打包生成的apk文件
        os.run("adb install -r ./bin/Demo-debug.apk")
    end)
   

target:on_uninstall

 
自定義卸載腳本

覆蓋target目標的xmake [u|uninstall}的卸載操作,實現自定義卸載過程。

target("test")
    on_uninstall(function (target) 
        ...
    end)
   

target:on_run

 
自定義運行腳本

覆蓋target目標的xmake [r|run}的運行操作,實現自定義運行過程。

例如,運行安裝好的apk程序:

target("test")

    -- 設置自定義運行腳本,自動運行安裝好的app程序,并且自動獲取設備輸出信息
    on_run(function (target) 
        os.run("adb shell am start -n com.demo/com.demo.DemoTest")
        os.run("adb logcat")
    end)
   

before_xxx和after_xxx

需要注意的是,target:on_xxx的所有接口都覆蓋內部默認實現,通常我們并不需要完全復寫,只是額外掛接自己的一些邏輯,那么可以使用target:before_xxxtarget:after_xxx系列腳本就行了。

所有的on_xxx都有對應的before_和after_xx版本,參數也完全一致,例如:

target("test")
    before_build(function (target)
        print("")
    end)
   

內置模塊

在自定義腳本中,除了使用import接口導入各種擴展模塊使用,xmake還提供了很多基礎的內置模塊,比如:os,io等基礎操作,實現更加跨平臺的處理系統接口。

 
os.cp

os.cp的行為和shell中的cp命令類似,不過更加強大,不僅支持模式匹配(使用的是lua模式匹配),而且還確保目的路徑遞歸目錄創建、以及支持xmake的內置變量。

例如:

os.cp("$(scriptdir)/*.h", "$(buildir)/inc")
os.cp("$(projectdir)/src/test/**.h", "$(buildir)/inc")
 

上面的代碼將:當前xmake.lua目錄下的所有頭文件、工程源碼test目錄下的頭文件全部復制到$(buildir)輸出目錄中。

其中$(scriptdir), $(projectdir) 這些變量是xmake的內置變量,具體詳情見:內置變量的相關文檔。

*.h**.h中的匹配模式,跟add_files中的類似,前者是單級目錄匹配,后者是遞歸多級目錄匹配。

上面的復制,會把所有文件全部展開復制到指定目錄,丟失源目錄層級,如果要按保持原有的目錄結構復制,可以設置rootdir參數:

os.cp("src/**.h", "/tmp/", {rootdir = "src"})
 

上面的腳本可以按src根目錄,將src下的所有子文件保持目錄結構復制過去。

注:盡量使用os.cp接口,而不是os.run("cp .."),這樣更能保證平臺一致性,實現跨平臺構建描述。

 
os.run

此接口會安靜運行原生shell命令,用于執行第三方的shell命令,但不會回顯輸出,僅僅在出錯后,高亮輸出錯誤信息。

此接口支持參數格式化、內置變量,例如:

-- 格式化參數傳入
os.run("echo hello %s!", "xmake")

-- 列舉構建目錄文件
os.run("ls -l $(buildir)")
   
os.execv

此接口相比os.run,在執行過程中還會回顯輸出,并且參數是通過列表方式傳入,更加的靈活。

os.execv("echo", {"hello", "xmake!"})
 

另外,此接口還支持一個可選的參數,用于傳遞設置:重定向輸出,執行環境變量設置,例如:

os.execv("echo", {"hello", "xmake!"}, {stdout = outfile, stderr = errfile, envs = {PATH = "xxx;xx", CFLAGS = "xx", curdir = "/tmp"}}
 

其中,stdout和stderr參數用于傳遞重定向輸出和錯誤輸出,可以直接傳入文件路徑,也可以傳入io.open打開的文件對象。

另外,如果想在這次執行中臨時設置和改寫一些環境變量,可以傳遞envs參數,里面的環境變量設置會替換已有的設置,但是不影響外層的執行環境,只影響當前命令。

我們也可以通過os.getenvs()接口獲取當前所有的環境變量,然后改寫部分后傳入envs參數。

另外,還能通過curdir參數設置,在執行過程中修改子進程的工作目錄。

其相關類似接口還有,os.runv, os.exec, os.execv, os.iorun, os.iorunv等等,比如os.iorun可以獲取運行的輸出內容。

這塊的具體詳情和差異,還有更多os接口,都可以到:os接口文檔 查看。

 
io.readfile

此接口,從指定路徑文件讀取所有內容,我們可在不打開文件的情況下,直接讀取整個文件的內容,更加的方便,例如:

local data = io.readfile("xxx.txt")
   
io.writefile

此接口寫入所有內容到指定路徑文件,我們可在不打開文件的情況下,直接寫入整個文件的內容,更加的方便,例如:

io.writefile("xxx.txt", "all data")
   
path.join

此接口實現跨平臺地路徑拼接操作,將多個路徑項進行追加拼接,由于windows/unix風格的路徑差異,使用api來追加路徑更加跨平臺,例如:

print(path.join("$(tmpdir)", "dir1", "dir2", "file.txt"))
 

上述拼接在unix上相當于:$(tmpdir)/dir1/dir2/file.txt,而在windows上相當于:$(tmpdir)\\dir1\\dir2\\file.txt

上述就是小編為大家分享的xmake如何通過自定義腳本實現更靈活地配置了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

清涧县| 阳新县| 许昌市| 藁城市| 萍乡市| 巩义市| 印江| 米易县| 依兰县| 运城市| 湛江市| 芷江| 孟州市| 高青县| 呈贡县| 嘉定区| 从化市| 涿鹿县| 安达市| 高要市| 麻栗坡县| 四平市| 雷州市| 措勤县| 迁安市| 吉林省| 永泰县| 通榆县| 天等县| 栖霞市| 新沂市| 马公市| 庆阳市| 揭阳市| 上虞市| 锡林浩特市| 宝清县| 贵南县| 灌阳县| 棋牌| 郧西县|