This is a mirror page, please see the original page:

https://xmake.io/#/zh-cn/manual/package_dependencies

package

仓库依赖包定义描述

可先参考官方仓库中现有包描述:xmake-repo

这里给个比较具有代表性的实例供参考:

package("libxml2")

    set_homepage("http://xmlsoft.org/")
    set_description("The XML C parser and toolkit of Gnome.")

    set_urls("https://github.com/GNOME/libxml2/archive/$(version).zip", {excludes = {"*/result/*", "*/test/*"}})

    add_versions("v2.9.8", "c87793e45e66a7aa19200f861873f75195065de786a21c1b469bdb7bfc1230fb")
    add_versions("v2.9.7", "31dd4c0e10fa625b47e27fd6a5295d246c883f214da947b9a4a9e13733905ed9")

    if is_plat("macosx", "linux") then
        add_deps("autoconf", "automake", "libtool", "pkg-config")
    end

    on_load(function (package)
        package:add("includedirs", "include/libxml2")
        package:add("links", "xml2")
    end)

    if is_plat("windows") and winos.version():gt("winxp") then
        on_install("windows", function (package)
            os.cd("win32")
            os.vrun("cscript configure.js iso8859x=yes iconv=no compiler=msvc cruntime=/MT debug=%s prefix=\"%s\"", package:debug() and "yes" or "no", package:installdir())
            os.vrun("nmake /f Makefile.msvc")
            os.vrun("nmake /f Makefile.msvc install")
        end)
    end

    on_install("macosx", "linux", function (package)
        import("package.tools.autoconf").install(package, {"--disable-dependency-tracking", "--without-python", "--without-lzma"})
    end)

package:set_homepage

设置包所在项目的官方页面地址

package:set_description

设置包的相关描述信息

一般通过xmake require --info zlib查看相关包信息时候,会看到。

package:set_kind

设置包类型

对于依赖库,则不用设置,如果是可执行包,需要设置为binary。

package("cmake")

    set_kind("binary")
    set_homepage("https://cmake.org")
    set_description("A cross-platform family of tool designed to build, test and package software")

package:set_urls

设置包源地址

设置包的源码包或者git仓库地址,跟add_urls不同的是,此接口是覆盖性设置,而add_urls是追加设置,其他使用方式类似,这个根据不同需要来选择。

package:add_urls

添加包源地址

添加包的源码包或者git仓库地址,此接口一般跟add_version配对使用,用于设置每个源码包的版本和对应的sha256值。

!> 可以通过添加多个urls作为镜像源,xmake会自动检测优先选用最快的url进行下载,如果下载失败则会尝试其他urls。

add_urls("https://github.com/protobuf-c/protobuf-c/releases/download/v$(version)/protobuf-c-$(version).tar.gz")
add_versions("1.3.1", "51472d3a191d6d7b425e32b612e477c06f73fe23e07f6a6a839b11808e9d2267")

urls里面的$(version)内置变量,会根据实际安装时候选择的版本适配进去,而版本号都是从add_versions中指定的版本列表中选择的。

如果对于urls里面带有比较复杂的版本串,没有跟add_versions有直接对应关系,则需要通过下面的方式定制化转换下:

add_urls("https://sqlite.org/2018/sqlite-autoconf-$(version)000.tar.gz",
         {version = function (version) return version:gsub("%.", "") end})

add_versions("3.24.0", "d9d14e88c6fb6d68de9ca0d1f9797477d82fc3aed613558f87ffbdbbc5ceb74a")
add_versions("3.23.0", "b7711a1800a071674c2bf76898ae8584fc6c9643cfe933cfc1bc54361e3a6e49")

当然,我们也只可以添加git源码地址:

add_urls("https://gitlab.gnome.org/GNOME/libxml2.git")

如果设置的多个镜像地址对应的源码包sha256是不同的,我们可以通过alias的方式来分别设置

add_urls("https://ffmpeg.org/releases/ffmpeg-$(version).tar.bz2", {alias = "home"})
add_urls("https://github.com/FFmpeg/FFmpeg/archive/n$(version).zip", {alias = "github"})
add_versions("home:4.0.2", "346c51735f42c37e0712e0b3d2f6476c86ac15863e4445d9e823fe396420d056")
add_versions("github:4.0.2", "4df1ef0bf73b7148caea1270539ef7bd06607e0ea8aa2fbf1bb34062a097f026")

我们也可以设置指定的 urls 的 http headers:

add_urls("https://github.com/madler/zlib/archive/$(version).tar.gz", {
    http_headers = {"TEST1: foo", "TEST2: bar"}
})

package:add_versions

设置每个源码包的版本

它也会设置对应的sha256值,具体描述见:add_urls

package:add_versionfiles

添加包版本列表

通常我们可以通过 add_versions 接口添加包版本,但是如果版本越来越多,就会导致包配置太过臃肿,这个时候,我们可以使用 add_versionfiles 接口将所有的版本列表,存储到单独的文件中去维护。

例如:

package("libcurl")
    add_versionfiles("versions.txt")
8.5.0 ce4b6a6655431147624aaf582632a36fe1ade262d5fab385c60f78942dd8d87b
8.4.0 e5250581a9c032b1b6ed3cf2f9c114c811fc41881069e9892d115cc73f9e88c6
8.0.1 9b6b1e96b748d04b968786b6bdf407aa5c75ab53a3d37c1c8c81cdb736555ccf
7.87.0 5d6e128761b7110946d1276aff6f0f266f2b726f5e619f7e0a057a474155f307
7.31.0 a73b118eececff5de25111f35d1d0aafe1e71afdbb83082a8e44d847267e3e08
...

package:add_patches

设置包补丁

此接口用于针对源码包,在编译安装前,先打对应设置的补丁包,再对其进行编译,并且可支持同时打多个补丁。

if is_plat("macosx") then
    add_patches("1.15", "https://raw.githubusercontent.com/Homebrew/patches/9be2793af/libiconv/patch-utf8mac.diff",
                        "e8128732f22f63b5c656659786d2cf76f1450008f36bcf541285268c66cabeab")
end

例如,上面的代码,就是针对macosx下编译的时候,打上对应的patch-utf8mac.diff补丁,并且每个补丁后面也是要设置sha256值的,确保完整性。

设置库链接

默认情况下,xmake会去自动检测安装后的库,设置链接关系,但是有时候并不是很准,如果要自己手动调整链接顺序,以及链接名,则可以通过这个接口来设置。

add_links("mbedtls", "mbedx509", "mbedcrypto")

设置系统库链接

添加一些系统库链接,有些包集成链接的时候,还需要依赖一些系统库,才能链接通过,这个时候可以在包描述里面都附加上去。

if is_plat("macosx") then
    add_frameworks("CoreGraphics", "CoreFoundation", "Foundation")
elseif is_plat("windows") then
    add_defines("CAIRO_WIN32_STATIC_BUILD=1")
    add_syslinks("gdi32", "msimg32", "user32")
else
    add_syslinks("pthread")
end

package:add_linkorders

调整包内部的链接顺序

具体详情可以看下 target 内部对 add_linkorders 的文档说明,target/add_linkorders

package("libpng")
    add_linkorders("png16", "png", "linkgroup::foo")
    add_linkgroups("dl", {name = "foo", group = true})

package:add_linkgroups

配置包的链接组

具体详情可以看下 target 内部对 add_linkgroups 的文档说明,target/add_linkgroups

package("libpng")
    add_linkorders("png16", "png", "linkgroup::foo")
    add_linkgroups("dl", {name = "foo", group = true})

package:add_frameworks

添加依赖的系统 frameworks 链接

示例见:add_syslinks

package:add_linkdirs

添加链接目录

包的链接库搜索目录也是可以调整的,不过通常都不需要,除非一些库安装完不在prefix/lib下面,而在lib的子目录下,默认搜索不到的话。

package:add_includedirs

添加其他头文件搜索目录

package:add_defines

添加宏定义

可以对集成的包对外输出一些特定的定义选项。

package:add_configs

添加包配置

我们可以通过此接口添加每个包的对外输出配置参数:

package("pcre2")

    set_homepage("https://www.pcre.org/")
    set_description("A Perl Compatible Regular Expressions Library")

    add_configs("bitwidth", {description = "Set the code unit width.", default = "8", values = {"8", "16", "32"}})

    on_load(function (package)
        local bitwidth = package:config("bitwidth") or "8"
        package:add("links", "pcre2-" .. bitwidth)
        package:add("defines", "PCRE2_CODE_UNIT_WIDTH=" .. bitwidth)
    end)

在工程项目里面,我们也可以查看特定包的可配置参数和值列表:

$ xmake require --info pcre2
The package info of project:
    require(pcre2):
      -> description: A Perl Compatible Regular Expressions Library
      -> version: 10.31
      ...
      -> configs:
         -> bitwidth:
            -> description: Set the code unit width.
            -> values: {"8","16","32"}
            -> default: 8

然后在项目里面,启用这些配置,编译集成带有特定配置的包:

add_requires("pcre2", {configs = {bitwidth = 16}})

package:add_extsources

添加扩展的包源

2.5.2 版本开始,我们也新增了 add_extsourceson_fetch 两个配置接口,可以更好的配置 xmake 在安装 C/C++ 包的过程中,对系统库的查找过程。

至于具体背景,我们可以举个例子,比如我们在 xmake-repo 仓库新增了一个 package("libusb") 的包。

那么用户就可以通过下面的方式,直接集成使用它:

add_requires("libusb")
target("test")
    set_kind("binary")
    add_files("src/*.c")
    add_packages("libusb")

如果用户系统上确实没有安装 libusb,那么 xmake 会自动下载 libusb 库源码,自动编译安装集成,没啥问题。

但如果用户通过 apt install libusb-1.0 安装了 libusb 库到系统,那么按理 xmake 应该会自动优先查找用户安装到系统环境的 libusb 包,直接使用,避免额外的下载编译安装。

但是问题来了,xmake 内部通过 find_package("libusb") 并没有找打它,这是为什么呢?因为通过 apt 安装的 libusb 包名是 libusb-1.0, 而不是 libusb。

我们只能通过 pkg-config --cflags libusb-1.0 才能找到它,但是 xmake 内部的默认 find_package 逻辑并不知道 libusb-1.0 的存在,所以找不到。

因此为了更好地适配不同系统环境下,系统库的查找,我们可以通过 add_extsources("pkgconfig::libusb-1.0") 去让 xmake 改进查找逻辑,例如:

package("libusb")
    add_extsources("pkgconfig::libusb-1.0")
    on_install(function (package)
        -- ...
    end)

另外,我们也可以通过这个方式,改进查找 homebrew/pacman 等其他包管理器安装的包,例如:add_extsources("pacman::libusb-1.0")

package:add_deps

添加包依赖

添加包依赖接口,通过配置包之间的依赖关系,我们能够在安装包的同时,自动安装它的所有依赖包。

另外,默认情况下,我们只要配置了依赖关系,cmake/autoconf 就能够自动找到所有依赖包的库和头文件。

当然,如果由于一些特殊原因,导致当前包的 cmake 脚本没能够正常找到依赖包,那么我们也可以通过 {packagedeps = "xxx"} 来强行打入依赖包信息。

例如:

package("foo")
    add_deps("cmake", "bar")
    on_install(function (package)
        local configs = {}
        import("package.tools.cmake").install(package, configs)
    end)

foo 包是使用 CMakeLists.txt 维护的,它在安装过程中,依赖 bar 包,因此,xmake 会优先安装 bar,并且让 cmake.install 在调用 cmake 时候,自动找到 bar 安装后的库。

但是,如果 foo 的 CMakeLists.txt 还是无法自动找到 bar,那么我们可以修改成下面的配置,强制将 bar 的 includedirs/links 等信息通过 flags 的方式,传入 foo。

package("foo")
    add_deps("cmake", "bar")
    on_install(function (package)
        local configs = {}
        import("package.tools.cmake").install(package, configs, {packages = "bar"})
    end)

package:add_components

添加包组件

这是 2.7.3 新加的接口,用于支持包的组件化配置,详情见:#2636

通过这个接口,我们可以配置当前包实际可以提供的组件列表。

package("sfml")
    add_components("graphics")
    add_components("audio", "network", "window")
    add_components("system")

在用户端,我们可以通过下面的方式来使用包的特定组件。

add_requires("sfml")

target("test")
    add_packages("sfml", {components = "graphics")

!> 注:除了配置可用的组件列表,我们还需要对每个组件进行详细配置,才能正常使用,因此,它通常和 on_componment 接口配合使用。

一个关于包组件的配置和使用的完整例子见:components example

package:set_base

继承包配置

这是 2.6.4 新加的接口,我们可以通过它去继承一个已有的包的全部配置,然后在此基础上重写部分配置。

这通常在用户自己的项目中,修改 xmake-repo 官方仓库的内置包比较有用,比如:修复改 urls,修改版本列表,安装逻辑等等。

例如,修改内置 zlib 包的 url,切到自己的 zlib 源码地址。

package("myzlib")
    set_base("zlib")
    set_urls("https://github.com/madler/zlib.git")
package_end()

add_requires("myzlib", {system = false, alias = "zlib"})

target("test")
    set_kind("binary")
    add_files("src/*.c")
    add_packages("zlib")

我们也可以用来单纯添加一个别名包。

package("onetbb")
    set_base("tbb")

我们可以通过 add_requires("onetbb") 集成安装 tbb 包,只是包名不同而已。

package:on_load

加载包配置

这是个可选的接口,如果要更加灵活的动态判断各种平台架构,针对性做设置,可以在这个里面完成,例如:

on_load(function (package)
    local bitwidth = package:config("bitwidth") or "8"
    package:add("links", "pcre" .. (bitwidth ~= "8" and bitwidth or ""))
    if not package:config("shared") then
        package:add("defines", "PCRE_STATIC")
    end
end)

pcre包需要做一些针对bitwidth的判断,才能确定对外输出的链接库名字,还需要针对动态库增加一些defines导出,这个时候在on_load里面设置,就更加灵活了。

package:on_fetch

从系统中查找库

这是个可选配置,2.5.2 之后,如果不同系统下安装的系统库,仅仅只是包名不同,那么使用 add_extsources 改进系统库查找已经足够,简单方便。

但是如果有些安装到系统的包,位置更加复杂,想要找到它们,也许需要一些额外的脚本才能实现,例如:windows 下注册表的访问去查找包等等,这个时候,我们就可以通过 on_fetch 完全定制化查找系统库逻辑。

还是以 libusb 为例,我们不用 add_extsources,可以使用下面的方式,实现相同的效果,当然,我们可以在里面做更多的事情。

package("libusb")
    on_fetch("linux", function(package, opt)
        if opt.system then
            return find_package("pkgconfig::libusb-1.0")
        end
    end)

package:on_install

安装包

这个接口主要用于添加安装脚本,前面的字符串参数用于设置支持的平台,像on_load, on_test等其他脚本域也是同样支持的。

平台过滤

完整的过滤语法如下:plat|arch1,arch2@host|arch1,arch2

看上去非常的复杂,其实很简单,其中每个阶段都是可选的,可部分省略,对应:编译平台|编译架构@主机平台|主机架构

如果不设置任何平台过滤条件,那么默认全平台支持,里面的脚本对所有平台生效,例如:

on_install(function (package)
    -- TODO
end)

如果安装脚本对特定平台生效,那么直接指定对应的编译平台,可以同时指定多个:

on_install("linux", "macosx", function (package)
    -- TODO
end)

如果还要细分到指定架构才能生效,可以这么写:

on_install("linux|x86_64", "iphoneos|arm64", function (package)
    -- TODO
end)

如果还要限制执行的主机环境平台和架构,可以在后面追加@host|arch,例如:

on_install("mingw@windows", function (package)
    -- TODO
end)

意思就是仅对windows下编译mingw平台生效。

我们也可以不指定比那一平台和架构,仅设置主机平台和架构,这通常用于描述一些跟编译工具相关的依赖包,只能在主机环境运行。

例如,我们编译的包,依赖了cmake,需要添加cmake的包描述,那么里面编译安装环境,只能是主机平台:

on_install("@windows", "@linux", "@macosx", function (package)
    -- TODO
end)

其他一些例子:

-- `@linux`
-- `@linux|x86_64`
-- `@macosx,linux`
-- `android@macosx,linux`
-- `android|armeabi-v7a@macosx,linux`
-- `android|armeabi-v7a@macosx,linux|x86_64`
-- `android|armeabi-v7a@linux|x86_64`

在 2.8.7 中,我们改进了模式匹配支持,新增排除指定平台和架构,例如:

!plat|!arch@!subhost|!subarch
@!linux
@!linux|x86_64
@!macosx,!linux
!android@macosx,!linux
android|!armeabi-v7a@macosx,!linux
android|armeabi-v7a,!iphoneos@macosx,!linux|x86_64
!android|armeabi-v7a@!linux|!x86_64
!linux|*

同时,还提供了一个内置的 native 架构,用于匹配当前平台的本地架构,主要用于指定或者排除交叉编译平台。

on_install("macosx|native", ...)

上面的配置,如果在 macOS x86_64 的设备上,它仅仅只会匹配 xmake f -a x86_64 的本地架构编译。

如果是 xmake f -a arm64 交叉编译,就不会被匹配到。

同理,如果只想匹配交叉编译,可以使用 macosx|!native 进行取反排除就行了。

2.9.1 版本,我们继续对它做了改进,增加了条件逻辑判断的支持:

例如:

on_install("!wasm|!arm* and !cross|!arm*", function (package)
end)

来表述排除 wasm 和 cross 平台之外的 arm 架构。

并且,它也支持通过 () 描述的嵌套逻辑,a and b or (a and (c or d))

编译工具

我们内置了一些安装常用编译工具脚本,用于针对不同源码依赖的构建工具链,进行方便的构架支持,例如:autoconf, cmake, meson等,

xmake

如果是基于xmake的依赖包,那么集成起来就非常简单了,xmake对其做了非常好的内置集成支持,可以直接对其进行跨平台编译支持,一般情况下只需要:

on_install(function (package)
    import("package.tools.xmake").install(package)
end)

如果要传递一些特有的编译配置参数:

on_install(function (package)
    import("package.tools.xmake").install(package, {"--xxx=y"})
end)
cmake

如果是基于cmake的包,集成起来也很简答,通常也只需要设置一些配置参数即可,不过还需要先添加上cmake的依赖才行:

add_deps("cmake")
on_install(function (package)
    import("package.tools.cmake").install(package, {"-Dxxx=ON"})
end)
autoconf

如果是基于autoconf的包,集成方式跟cmake类似,只是传递的配置参数不同而已,不过通常情况下,unix系统都内置了autoconf系列工具,所以不加相关依赖也没事。

on_install(function (package)
    import("package.tools.autoconf").install(package, {"--enable-shared=no"})
end)

不过,有些源码包用系统内置的autoconf可能不能完全满足,那么可以加上autoconf系列依赖,对其进行构建:

add_deps("autoconf", "automake", "libtool", "pkg-config")
on_install(function (package)
    import("package.tools.autoconf").install(package, {"--enable-shared=no"})
end)
meson

如果是meson,还需要加上ninja的依赖来执行构建才行。

add_deps("meson", "ninja")
on_install(function (package)
    import("package.tools.meson").install(package, {"-Dxxx=ON"})
end)

package:on_test

测试包

安装后,需要设置对应的测试脚本,执行一些测试,确保安装包的可靠性,如果测试不通过,则会撤销整个安装包。

on_test(function (package)
    assert(package:has_cfuncs("inflate", {includes = "zlib.h"}))
end)

上面的脚本调用包内置的has_cfuncs接口,检测安装后的包是否存在zlib.h头文件,以及库和头文件里面是否存在inflate这个接口函数。

xmake会去尝试编译链接来做测试,has_cfuncs用于检测c函数,而has_cxxfuncs则可以检测c++库函数。

而includes里面可以设置多个头文件,例如:includes = {"xxx.h", "yyy.h"}

我们还可以传递一些自己的编译参数进去检测,例如:

on_test(function (package)
    assert(package:has_cxxfuncs("func1", {includes = "xxx.h", configs = {defines = "c++14", cxflags = "-Dxxx"}}))
end)

我们也可以通过check_csnippetscheck_cxxsnippets检测一个代码片段:

on_test(function (package)
    assert(package:check_cxxsnippets({test = [[
        #include 
        #include 
        #include 
        #include 
        using namespace boost::algorithm;
        using namespace std;
        static void test() {
            string str("a,b");
            vector strVec;
            split(strVec, str, is_any_of(","));
            assert(strVec.size()==2);
            assert(strVec[0]=="a");
            assert(strVec[1]=="b");
        }
    ]]}, {configs = {languages = "c++14"}}))
end)

如果是可执行包,也可以通过尝试运行来检测:

on_test(function (package)
    os.run("xxx --help")
end)

如果运行失败,那么测试不会通过。

package:on_download

自定义下载包

自定义包的下载逻辑,这是 2.6.4 新加的接口,通常用不到,使用 Xmake 的内置下载就足够了。

如果用户自建私有仓库,对包的下载有更复杂的鉴权机制,特殊处理逻辑,那么可以重写内部的下载逻辑来实现。

on_download(function (package, opt)
    local url = opt.url
    local sourcedir = opt.sourcedir

    -- download url to the current directory
    -- and extract it's source code to sourcedir
    -- ...
end)

opt 参数里面,可以获取到下载包的目的源码目录 opt.sourcedir,我们只需要从 opt.url 获取到包地址,下载下来就可以了。

然后,根据需要,添加一些自定义的处理逻辑。另外,自己可以添加下载缓存处理等等。

下面是一个自定义下载 tar.gz 文件,并且实现缓存和源文件目录解压的例子,可以参考下:

package("zlib")
    add_urls("https://github.com/madler/zlib/archive/$(version).tar.gz")
    add_versions("v1.2.10", "42cd7b2bdaf1c4570e0877e61f2fdc0bce8019492431d054d3d86925e5058dc5")

    on_download(function (package, opt)
        import("net.http")
        import("utils.archive")

        local url = opt.url
        local sourcedir = opt.sourcedir
        local packagefile = path.filename(url)
        local sourcehash = package:sourcehash(opt.url_alias)

        local cached = true
        if not os.isfile(packagefile) or sourcehash ~= hash.sha256(packagefile) then
            cached = false

            -- attempt to remove package file first
            os.tryrm(packagefile)
            http.download(url, packagefile)

            -- check hash
            if sourcehash and sourcehash ~= hash.sha256(packagefile) then
                raise("unmatched checksum, current hash(%s) != original hash(%s)", hash.sha256(packagefile):sub(1, 8), sourcehash:sub(1, 8))
            end
        end

        -- extract package file
        local sourcedir_tmp = sourcedir .. ".tmp"
        os.rm(sourcedir_tmp)
        if archive.extract(packagefile, sourcedir_tmp) then
            os.rm(sourcedir)
            os.mv(sourcedir_tmp, sourcedir)
        else
            -- if it is not archive file, we need only create empty source file and use package:originfile()
            os.tryrm(sourcedir)
            os.mkdir(sourcedir)
        end

        -- save original file path
        package:originfile_set(path.absolute(packagefile))
    end)

自定义下载需要用户完全自己控制下载逻辑,会比较复杂,除非必要,不推荐这么做。

如果仅仅只是想增加自定义 http headers 去获取下载授权,可以使用 设置包下载的 http headers

package:on_componment

配置包组件

这是 2.7.3 新加的接口,用于支持包的组件化配置,详情见:#2636

通过这个接口,我们可以配置当前包,指定组件的详细信息,比如组件的链接,依赖等等。

配置组件链接信息
package("sfml")
    add_components("graphics")
    add_components("audio", "network", "window")
    add_components("system")

    on_component("graphics", function (package, component)
        local e = package:config("shared") and "" or "-s"
        component:add("links", "sfml-graphics" .. e)
        if package:is_plat("windows", "mingw") and not package:config("shared") then
            component:add("links", "freetype")
            component:add("syslinks", "opengl32", "gdi32", "user32", "advapi32")
        end
    end)

    on_component("window", function (package, component)
        local e = package:config("shared") and "" or "-s"
        component:add("links", "sfml-window" .. e)
        if package:is_plat("windows", "mingw") and not package:config("shared") then
            component:add("syslinks", "opengl32", "gdi32", "user32", "advapi32")
        end
    end)

    ...

在用户端,我们可以通过下面的方式来使用包的特定组件。

add_requires("sfml")

target("test")
    add_packages("sfml", {components = "graphics")

!> 注:除了配置组件信息,我们还需要配置可用的组件列表,才能正常使用,因此,它通常和 add_components 接口配合使用。

一个关于包组件的配置和使用的完整例子见:components example

配置组件的编译信息

我们不仅可以配置每个组件的链接信息,还有 includedirs, defines 等等编译信息,我们也可以对每个组件单独配置。

package("sfml")
    on_component("graphics", function (package, component)
        package:add("defines", "TEST")
    end)
配置组件依赖
package("sfml")
    add_components("graphics")
    add_components("audio", "network", "window")
    add_components("system")

    on_component("graphics", function (package, component)
          component:add("deps", "window", "system")
    end)

上面的配置,告诉包,我们的 graphics 组件还会额外依赖 windowsystem 两个组件。

因此,在用户端,我们对 graphics 的组件使用,可以从

    add_packages("sfml", {components = {"graphics", "window", "system"})

简化为:

    add_packages("sfml", {components = "graphics")

因为,只要我们开启了 graphics 组件,它也会自动启用依赖的 window 和 system 组件。

另外,我们也可以通过 add_components("graphics", {deps = {"window", "system"}}) 来配置组件依赖关系。

从系统库中查找组件

我们知道,在包配置中,配置 add_extsources 可以改进包在系统中的查找,比如从 apt/pacman 等系统包管理器中找库。

当然,我们也可以让每个组件也能通过 extsources 配置,去优先从系统库中找到它们。

例如,sfml 包,它在 homebrew 中其实也是组件化的,我们完全可以让包从系统库中,找到对应的每个组件,而不需要每次源码安装它们。

$ ls -l /usr/local/opt/sfml/lib/pkgconfig
-r--r--r--  1 ruki  admin  317 10 19 17:52 sfml-all.pc
-r--r--r--  1 ruki  admin  534 10 19 17:52 sfml-audio.pc
-r--r--r--  1 ruki  admin  609 10 19 17:52 sfml-graphics.pc
-r--r--r--  1 ruki  admin  327 10 19 17:52 sfml-network.pc
-r--r--r--  1 ruki  admin  302 10 19 17:52 sfml-system.pc
-r--r--r--  1 ruki  admin  562 10 19 17:52 sfml-window.pc

我们只需要,对每个组件配置它的 extsources:

    if is_plat("macosx") then
        add_extsources("brew::sfml/sfml-all")
    end

    on_component("graphics", function (package, component)
        -- ...
        component:add("extsources", "brew::sfml/sfml-graphics")
    end)
默认的全局组件配置

除了通过指定组件名的方式,配置特定组件,如果我们没有指定组件名,默认就是全局配置所有组件。

package("sfml")
    on_component(function (package, component)
        -- configure all components
    end)

当然,我们也可以通过下面的方式,指定配置 graphics 组件,剩下的组件通过默认的全局配置接口进行配置:

package("sfml")
    add_components("graphics")
    add_components("audio", "network", "window")
    add_components("system")

    on_component("graphics", function (package, component)
        -- configure graphics
    end)

    on_component(function (package, component)
        -- component audio, network, window, system
    end)