xmake從入門到精通10:多個子工程目標的依賴配置

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

本文主要詳細講解下,如果在一個項目中維護和生成多個目標文件的生成,以及它們之間的依賴關係設置。

target到底是什麼?

xmake的概念定義裏,一個獨立的項目工程可能會有多個子工程組織在一起,每個子工程對應只能生成一個唯一的目標文件,例如:可執行程序,靜態庫或者動態庫等。

而這裏所說的每個子工程就是xmake裏面所說的target,字面意思就是目標子工程

因此每個子工程,我們都可以通過新增一個target在xmake.lua裏面維護,例如:

target("test1")
    set_kind("binary")
    add_files("src/test1/*.c")
    
target("test2")
    set_kind("binary")
    add_files("src/test2/*.c")    

上面我們就定義了兩個獨立的子工程目標,編譯時候會生成兩個互不依賴的可執行文件。

從根域繼承全局設置

暫時先不談target間的依賴問題,如果我們有許多通用設置,每個target下都得設置一遍,那會非常冗餘,也不好維護。

因此,我們可以把這些配置移到target域的外面,也就是根作用域中去設置,這樣對當前xmake.lua以及所有子xmake.lua中的target都會生效,例如:

add_links("tbox")
add_linkdirs("lib")
add_includedirs("include")

target("test1")
    set_kind("binary")
    add_files("src/test1/*.c")
    
target("test2")
    set_kind("binary")
    add_files("src/test2/*.c")    

比如這兩target都需要鏈接tbox庫,放置在外層根域設置,test1和test2都能加上對應links。

目標間的依賴設置

那如果某個target需要用到另外一個tatget生成的靜態庫,應該怎麼配置呢?

一種方式就是通過add_linkdirsadd_links手動指定對應target最後生成的目錄庫所在目錄,然後把鏈接加上。

target("foo")
    set_kind("static")
    add_files("foo/*.c")
    add_defines("FOO")

target("test1")
    set_kind("binary")
    add_includedirs("foo/inc")
    add_links("foo")
    add_linkdirs("$(buildir)")
    add_files("test1/*.c")
    add_defines("FOO")
    
target("test2")
    set_kind("binary")
    add_includedirs("foo/inc")
    add_links("foo")
    add_linkdirs("$(buildir)")
    add_files("test2/*.c")
    add_defines("FOO")

上述配置中,test1和test2都會用到libfoo庫,並且需要獲取到libfoo庫的頭文件路徑,庫路徑和鏈接,並且在使用過程中還需要額外設置-DFOO宏定義開關纔行。

看上去沒啥,其實這麼寫有兩個問題:

  1. test目標和另外兩個庫目標之間是有編譯順序依賴的,如果test先編譯就會提示鏈接庫找不到
  2. 配置太過繁瑣不好維護,test1和test2有很多冗餘配置

那有沒有更加簡單可靠的配置方式呢,其實我們只需要add_deps來對target間配置上依賴關係即可。

target("foo")
    set_kind("static")
    add_files("*.c")
    add_defines("FOO", {public = true})
    add_includedirs("foo/inc", {public = true})

target("test1")
    set_kind("binary")
    add_deps("foo")
    add_files("test1/*.c")
    
target("test2")
    set_kind("binary")
    add_deps("foo")
    add_files("test2/*.c")

對比下,test1和test2的配置,是不是精簡了好多?僅僅通過add_deps("foo")就繼承了libfoo的所有導出設置:linkdirs, links, includedirs以及defines

其中target自身生成的庫默認就會自動導出鏈接設置,而includedirs和defines通過設置public屬性,我們也將它們標記爲導出,這樣可以被test目標繼承到。

並且,現在有了依賴關係,xmake在編譯的時候,會自動處理這些target之間的編譯順序,保證不會出現鏈接的時候,libfoo庫還沒有生成的問題。

依賴繼承的進一步解析

級聯依賴繼承

根據上文所說,target會自動繼承依賴目標中的配置和屬性,不需要額外調用add_links, add_linkdirsadd_rpathdirs等接口去關聯依賴目標了。

並且繼承關係是支持級聯的,例如:

target("library1")
    set_kind("static")
    add_files("*.c")
    add_includedirs("inc") -- 默認私有頭文件目錄不會被繼承
    add_includedirs("inc1", {public = true}) -- 此處的頭文件相關目錄也會被繼承

target("library2")
    set_kind("static")
    add_deps("library1")
    add_files("*.c")

target("test")
    set_kind("binary")
    add_deps("library2")

上面的配置中,test依賴library2,然後library2又依賴library1,那麼通過add_deps僅僅添加library2的依賴,test就可以完整繼承整個依賴鏈上的所有導出設置。

禁用默認的繼承行爲

那如果我們不想繼承依賴target的任何配置,如何操作呢?

add_deps("dep1", "dep2", {inherit = false})

通過顯式設置inherit配置,來告訴xmake,這兩個依賴的配置是否需要被繼承,如果不設置,默認就是啓用繼承的。

可繼承的導出屬性詳解

上文,我們還通過 add_includedirs("inc1", {public = true}), 設置public爲true, 將includedirs的設置公開給其他依賴的子target繼承。

目前對於target的編譯鏈接flags相關接口設置,都是支持繼承屬性的,可以人爲控制是否需要導出給其他target來依賴繼承,目前支持的屬性有:

屬性
描述
private
默認設置,作爲當前target的私有配置,不會被依賴的其他target所繼承
public
公有配置,當前target,依賴的子target都會被設置
interface 接口設置,僅被依賴的子target所繼承設置,當前target不參與

這個其實參考借鑑了cmake的設計,目前xmake中只要跟target相關的所有編譯鏈接設置接口,都是支持可見性導出的,例如:add_includedirs, add_defines, add_cflags等等。

關於這塊的詳細信息,可以看下:https://github.com/xmake-io/xmake/issues/368

原文:https://tboox.org/cn/2019/12/13/quickstart-10-target-deps/

個人主頁

個人項目

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章