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_linkdirs
和add_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
宏定義開關纔行。
看上去沒啥,其實這麼寫有兩個問題:
- test目標和另外兩個庫目標之間是有編譯順序依賴的,如果test先編譯就會提示鏈接庫找不到
- 配置太過繁瑣不好維護,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_linkdirs
和add_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/