使用xmake優(yōu)雅地描述工程

描述語(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_linksadd_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),與targetoption同級(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)源工程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末穴翩,一起剝皮案震驚了整個(gè)濱河市萎河,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荸型,老刑警劉巖繁涂,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拱她,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡爆土,警方通過(guò)查閱死者的電腦和手機(jī)椭懊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)步势,“玉大人氧猬,你說(shuō)我怎么就攤上這事』荡瘢” “怎么了盅抚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)倔矾。 經(jīng)常有香客問(wèn)我妄均,道長(zhǎng),這世上最難降的妖魔是什么哪自? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任丰包,我火速辦了婚禮,結(jié)果婚禮上壤巷,老公的妹妹穿的比我還像新娘邑彪。我一直安慰自己,他們只是感情好胧华,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布寄症。 她就那樣靜靜地躺著,像睡著了一般矩动。 火紅的嫁衣襯著肌膚如雪有巧。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,021評(píng)論 1 291
  • 那天悲没,我揣著相機(jī)與錄音篮迎,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛柑潦,可吹牛的內(nèi)容都是我干的享言。 我是一名探鬼主播,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼渗鬼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼览露!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起譬胎,我...
    開(kāi)封第一講書(shū)人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤差牛,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后堰乔,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體偏化,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年镐侯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了侦讨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡苟翻,死狀恐怖韵卤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情崇猫,我是刑警寧澤沈条,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站诅炉,受9級(jí)特大地震影響蜡歹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜涕烧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一月而、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧议纯,春花似錦父款、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)溯香。三九已至鲫构,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間玫坛,已是汗流浹背结笨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人炕吸。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓伐憾,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親赫模。 傳聞我的和親對(duì)象是個(gè)殘疾皇子树肃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容