跳转到内容

分发私有库

Xmake 不仅支持从官方仓库安装包,也支持用户创建和分发私有库。这对于公司内部的私有代码库复用非常有用。

我们可以将编译好的静态库/动态库打包成本地包或者远程包进行分发,也可以直接安装到系统目录

创建库工程

首先,我们可以使用 xmake create 命令快速创建一个空的静态库或者动态库工程。

bash
$ xmake create -t static test
create test ...
  [+]: xmake.lua
  [+]: src/foo.cpp
  [+]: src/foo.h
  [+]: src/main.cpp
  [+]: .gitignore
create ok!

默认生成的 xmake.lua 比较简单,我们需要稍微修改下,加上 add_headerfiles 导出头文件,以便安装的时候,能把库的头文件也安装过去。

lua
add_rules("mode.debug", "mode.release")

target("foo")
    set_kind("static")
    add_files("src/foo.cpp")
    add_headerfiles("src/foo.h")

target("test")
    set_kind("binary")
    add_deps("foo")
    add_files("src/main.cpp")

默认情况下,add_headerfiles 会将头文件直接安装到 include 目录下。

如果想让头文件安装到子目录(例如 include/foo/foo.h),以避免文件名冲突,我们可以设置 prefixdir

lua
add_headerfiles("src/foo.h", {prefixdir = "foo"})

更多详细用法,请参考:add_headerfiles 接口文档

除了头文件,我们还可以使用 add_installfiles 安装其他任意文件,比如文档、脚本、资源文件等。

lua
-- 安装 readme.md 到 share/doc/foo 目录
add_installfiles("readme.md", {prefixdir = "share/doc/foo"})

-- 安装 assets 下的所有文件
add_installfiles("assets/**", {prefixdir = "share/foo/assets"})

分发本地包 (Local Package)

如果我们的库仅仅在本地局域网,或者通过网盘共享给其他人使用,不需要部署到远程 git 仓库,可以使用本地包分发。

本地包的优势在于:

  • 不需要搭建 Git 仓库
  • 编译好的二进制直接分发,集成速度快
  • 支持多平台、多架构、多编译模式(Debug/Release)的二进制包

打包生成

在库工程根目录下,执行 xmake package 命令(或者完整命令 xmake package -f local)即可打包。

bash
$ xmake package
checking for platform ... macosx 
checking for architecture ... x86_64 
checking for Xcode directory ... /Applications/Xcode.app 
checking for SDK version of Xcode for macosx (x86_64) ... 15.2 
checking for Minimal target version of Xcode for macosx (x86_64) ... 15.2 
[ 23%]: cache compiling.release src/main.cpp 
[ 23%]: cache compiling.release src/foo.cpp 
[ 35%]: archiving.release libfoo.a 
[ 71%]: linking.release test 
[100%]: build ok, spent 3.31s 
installing foo to build/packages/f/foo/macosx/x86_64/release .. 
package(foo): build/packages/f/foo generated 
installing test to build/packages/t/test/macosx/x86_64/release .. 
package(test): build/packages/t/test generated

默认情况下,它会生成在 build/packages 目录下。

build/packages/
├── f 
│   └── foo 
│       ├── macosx 
│       │   └── x86_64 
│       │       └── release 
│       │           ├── include 
│       │           │   └── foo.h 
│       │           └── lib 
│       │               └── libfoo.a 
│       └── xmake.lua 
└── t 
    └── test 
        ├── macosx 
        │   └── x86_64 
        │       └── release 
        │           └── bin 
        │               └── test 
        └── xmake.lua

每个包目录下都包含了生成的二进制库文件 (.a/.lib/.so/.dll) 和头文件。

使用本地包

我们将生成的 build/packages 目录复制到任意位置,或者直接使用它。然后在消费端的工程 xmake.lua 中配置:

lua
add_rules("mode.debug", "mode.release")

-- 添加本地包仓库目录,指向 packages 目录
add_repositories("local-repo /path/to/test/build/packages")

-- 引入 foo 包
add_requires("foo")

target("bar")
    set_kind("binary")
    add_files("src/*.cpp")
    add_packages("foo")

这里我们使用了几个接口:

  • add_repositories: 添加自定义的包仓库。第一个参数 local-repo 是仓库名称,第二个参数是仓库地址(这里是本地路径)。
  • add_requires: 声明项目依赖的包。
  • add_packages: 将指定的包链接到当前目标(target)。

仓库与包的区别

这里需要区分 仓库 (Repository)包 (Package) 的概念:

  • 仓库:是一个包描述文件的集合,它可以维护和管理多个包。
  • :具体的某个库或工具(比如 foo, zlib)。

一个仓库可以包含多个包,它们的组织结构通常如下(首字母分类):

repository/
└── packages/
    ├── f/
    │   ├── foo/
    │   │   └── xmake.lua
    │   └── bar/
    │       └── xmake.lua
    └── z/
        └── zlib/
            └── xmake.lua

上文中 xmake package 生成的 build/packages 目录,实际上就是一个符合 Xmake 仓库规范的目录结构。因此,我们可以通过 add_repositories 将其作为仓库导入,Xmake 会自动在里面查找我们需要的 foo 包。

执行 xmake 编译时,Xmake 会直接从本地仓库中链接对应的二进制库。

分发远程包 (Remote Package)

如果我们需要通过 git 仓库进行版本管理和分发,可以使用远程包模式。它既支持分发源码包,也支持分发二进制包。

远程包的优势在于:

  • 基于 Git 进行版本控制
  • 支持源码分发(自动编译)和二进制分发(直接安装)
  • 支持多版本切换

配置远程包

我们运行 xmake package -f remote 来生成远程包的配置模板。

bash
$ xmake package -f remote

它会生成 packages/f/foo/xmake.lua 文件。我们可以根据需要修改它,支持源码分发或二进制分发。

源码分发

这是最常用的方式,我们只需要配置 git 仓库地址,Xmake 会自动下载源码并编译安装。

lua
package("foo")
    set_description("The foo package")
    set_license("Apache-2.0")

    -- 设置私有 git 仓库地址
    add_urls("git@github.com:mycompany/foo.git")
    add_versions("1.0", "<commit-sha>")

    on_install(function (package)
        local configs = {}
        if package:config("shared") then
            configs.kind = "shared"
        end
        import("package.tools.xmake").install(package, configs)
    end)

二进制分发

如果我们不想泄露源码,或者想加速安装,可以预先编译好二进制包(例如 .tar.gz),上传到服务器,然后配置下载地址。

lua
package("foo")
    set_description("The foo package")

    -- 设置预编译的二进制包地址
    add_urls("https://example.com/releases/foo-$(version).tar.gz")
    add_versions("1.0", "<shasum>")

    on_install(function (package)
        -- 直接解压安装二进制文件
        os.cp("include", package:installdir())
        os.cp("lib", package:installdir())
    end)

关于包配置的更多详细参数说明,请参考:包配置说明包依赖 API

另外,我们也可以参考官方仓库 xmake-repo 中现有包的配置进行参考。

我们需要建立一个私有的 git 仓库(例如 my-repo),用来存储所有的包配置。将生成的 packages 目录推送到这个仓库中。

目录结构类似:

my-repo/
└── packages/
    └── f/
        └── foo/
            └── xmake.lua

使用远程包

在消费端的工程中,我们需要添加这个私有仓库。

lua
add_rules("mode.debug", "mode.release")

-- 添加私有 Git 仓库
add_repositories("my-repo git@github.com:mycompany/my-repo.git")

add_requires("foo")

target("bar")
    set_kind("binary")
    add_files("src/*.cpp")
    add_packages("foo")

Xmake 会自动拉取 my-repo 仓库中的包描述,然后根据 add_urls 下载 foo 的源码进行编译安装。

分发 C++ Modules

Xmake 也支持分发 C++ Modules 库。我们只需要在 add_files 中添加 {install = true},即可将模块文件(.mpp, .ixx 等)一同打包分发。

通常,我们需要将包定义在独立的仓库中。

库工程

在库工程的 xmake.lua 中,我们需要导出模块文件:

lua
target("foo")
    set_kind("static")
    add_files("src/*.cpp")
    -- 安装分发模块文件
    add_files("src/*.mpp", {install = true})

配置包仓库

在私有包仓库(例如 my-repo)中,添加包描述文件 packages/f/foo/xmake.lua

lua
package("foo")
    add_urls("git@github.com:mycompany/foo.git")
    add_versions("1.0", "<commit-sha>")
    on_install(function (package)
        import("package.tools.xmake").install(package)
    end)

使用模块包

消费端集成时,只需要引入仓库,并开启 build.c++.modules 策略:

lua
add_repositories("my-repo git@github.com:mycompany/my-repo.git")
add_requires("foo")

target("bar")
    set_kind("binary")
    set_languages("c++20")
    -- 开启 C++ Modules 支持
    set_policy("build.c++.modules", true)
    add_packages("foo")

更多完整示例,可以参考:C++ Modules 包分发例子

管理包仓库

不管是远程包还是本地包,我们都可以灵活选择包仓库的管理方式。

项目内部维护

如果是项目内部私有的包,不需要跨项目共享,我们可以直接将包仓库放置在项目目录下。

例如,我们在项目根目录下创建一个 packages 目录作为仓库:

projectdir/
├── packages/
│   └── f/
│       └── foo/
│           └── xmake.lua
├── src/
│   └── main.cpp
└── xmake.lua

然后在 xmake.lua 中通过 add_repositories 添加这个仓库(支持相对路径):

lua
add_repositories("my-repo packages")
add_requires("foo")

target("test")
    set_kind("binary")
    add_files("src/*.cpp")
    add_packages("foo")

这样,Xmake 就会自动在当前项目下的 packages 目录中查找 foo 包的配置。

私有 Git 仓库

如果需要在公司内部多个项目之间共享包,建立独立的 Git 仓库(如 GitHub 私有库、GitLab、Gitee 或公司内网 Git)来维护包配置是更好的选择。

我们可以通过 add_repositories 直接引入 git 仓库地址:

lua
add_repositories("my-repo git@github.com:mycompany/my-repo.git")

如果仓库是私有的,请确保本地环境配置了 SSH Key 或者有访问权限。

另外,我们也可以通过 xrepo add-repo 或者 xmake repo --add 命令来添加仓库地址。

xrepo 是全局的独立命令,而 xmake repo 可以仅仅在本地项目中添加包仓库,不影响其他项目。

bash
# 全局添加
$ xrepo add-repo my-repo git@github.com:mycompany/my-repo.git

# 仅当前项目添加
$ xmake repo --add my-repo git@github.com:mycompany/my-repo.git

官方仓库

如果你的包是开源的,并且希望分享给所有 Xmake 用户,可以提交到官方的 xmake-repo 仓库。具体贡献指南请参考:提交包到官方仓库

安装到系统

除了打包分发,我们在开发调试阶段,或者想直接将库安装到系统目录,供其他项目使用,可以直接运行 xmake install

bash
$ xmake install

默认它会安装到系统目录,如果想安装到指定目录,可以指定输出目录:

bash
$ xmake install -o /tmp/output

安装完成后,目录结构大概是这样的:

/tmp/output
├── include
│   └── foo.h
├── lib
│   ├── libfoo.a
│   └── pkgconfig
│       └── foo.pc
└── share
    └── cmake
        └── modules
            └── foo-config.cmake

由于我们配置了 utils.install.pkgconfig_importfilesutils.install.cmake_importfiles 规则(详见 在其他构建系统中使用),所以安装的时候会自动生成 foo.pcfoo-config.cmake 文件。

这样,其他非 xmake 的第三方项目(例如 CMake 项目)也可以通过 find_package(foo) 找到并集成它。

集成到第三方构建系统

如果我们开发的是一个库,最终需要给其他非 xmake 项目使用,我们可以通过以下几种方式进行集成。

使用 CMake (Xrepo)

如果是 CMake 项目,我们可以使用 xrepo-cmake 来直接集成 Xmake 管理的包。

cmake
# 下载 xrepo.cmake
if(NOT EXISTS "${CMAKE_BINARY_DIR}/xrepo.cmake")
    file(DOWNLOAD "https://raw.githubusercontent.com/xmake-io/xrepo-cmake/main/xrepo.cmake"
                  "${CMAKE_BINARY_DIR}/xrepo.cmake" TLS_VERIFY ON)
endif()
include(${CMAKE_BINARY_DIR}/xrepo.cmake)

# 引入包
xrepo_package("foo")

# 链接包
target_link_libraries(demo PRIVATE foo)

对于私有仓库的包,我们需要确保本地已经添加了对应的仓库(xrepo add-repo myrepo ...)。

更详细的使用说明,请参考:在 CMake 中使用 Xmake 包

使用 CMake/Pkg-config

我们也可以通过配置 utils.install.pkgconfig_importfilesutils.install.cmake_importfiles 规则,在安装库的同时生成导入文件。

lua
target("foo")
    set_kind("static")
    add_files("src/foo.cpp")
    add_headerfiles("src/foo.h")
    
    -- 导出 pkgconfig/cmake 导入文件
    add_rules("utils.install.pkgconfig_importfiles")
    add_rules("utils.install.cmake_importfiles")

这样,用户执行 xmake install 将库安装到系统后,就可以直接使用标准的方式来查找和使用库。

CMake

cmake
find_package(foo REQUIRED)
target_link_libraries(demo PRIVATE foo::foo)

Pkg-config

bash
pkg-config --cflags --libs foo

生成安装包 (XPack)

Xmake 还支持使用 XPack 插件生成各种格式的安装包,例如 NSIS, Deb, RPM, Zip 等。

这对于分发二进制 SDK 或者部署到生产环境非常有用。

支持格式

  • Windows: nsis, wix, zip, targz
  • Linux: deb, rpm, srpm, runself (shell 自解压脚本), targz, srczip, appimage
  • MacOS: dmg, zip, targz, runself

配置示例

我们可以在 xmake.lua 中添加 xpack 配置域来定义打包规则。

例如,配置生成一个 NSIS 安装包:

lua
-- 引入 xpack 插件
includes("@builtin/xpack")

target("foo")
    set_kind("shared")
    add_files("src/*.cpp")
    add_headerfiles("src/*.h")

xpack("foo")
    set_formats("nsis")
    set_title("Foo Library")
    set_description("The foo library package")
    set_author("ruki")
    set_version("1.0.0")
    
    -- 添加需要打包的目标
    add_targets("foo")
    
    -- 添加其他文件
    add_installfiles("doc/*.md", {prefixdir = "share/doc/foo"})

然后执行打包命令:

bash
$ xmake pack

它会自动下载 NSIS 工具并生成安装包。生成的安装包可以双击安装,自动配置 PATH 等环境变量,方便用户使用。

更多详情,请查看文档:XPack 打包