Distribute Private Libraries
Xmake supports not only installing packages from the official repository but also creating and distributing private libraries. This is very useful for reusing private code libraries within a company.
We can package compiled static/dynamic libraries into local packages or remote packages for distribution, or install them directly to the system directory.
Create Library Project
First, we can use the xmake create command to quickly create an empty static or dynamic library project.
$ xmake create -t static test
create test ...
[+]: xmake.lua
[+]: src/foo.cpp
[+]: src/foo.h
[+]: src/main.cpp
[+]: .gitignore
create ok!The default generated xmake.lua is quite simple. We need to modify it slightly to add add_headerfiles to export header files, so that the library headers can be installed together during installation.
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")By default, add_headerfiles installs header files directly to the include directory.
If you want to install header files to a subdirectory (e.g. include/foo/foo.h) to avoid filename conflicts, you can set prefixdir:
add_headerfiles("src/foo.h", {prefixdir = "foo"})For more details, please refer to: add_headerfiles documentation.
In addition to header files, we can also use add_installfiles to install any other files, such as documentation, scripts, resource files, etc.
-- Install readme.md to share/doc/foo directory
add_installfiles("readme.md", {prefixdir = "share/doc/foo"})
-- Install all files under assets
add_installfiles("assets/**", {prefixdir = "share/foo/assets"})Distribute as Local Package
If our library is only used within the local LAN or shared with others via a network drive, and does not need to be deployed to a remote git repository, we can use local package distribution.
The advantages of local packages are:
- No need to set up a Git repository
- Pre-compiled binaries are distributed directly, resulting in fast integration
- Supports binary packages for multiple platforms, architectures, and build modes (Debug/Release)
Packaging
Run the xmake package command (or the full command xmake package -f local) in the root directory of the library project to package it.
$ 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 generatedBy default, it will be generated in the build/packages directory.
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.luaEach package directory contains the generated binary library files (.a/.lib/.so/.dll) and header files.
Consume Local Package
We copy the generated build/packages directory to any location, or use it directly. Then configure it in the consumer project's xmake.lua:
add_rules("mode.debug", "mode.release")
-- Add local package repository directory, pointing to the packages directory
add_repositories("local-repo /path/to/test/build/packages")
-- Import foo package
add_requires("foo")
target("bar")
set_kind("binary")
add_files("src/*.cpp")
add_packages("foo")Here we use several interfaces:
add_repositories: Add a custom package repository. The first argumentlocal-repois the repository name, and the second argument is the repository address (here it is a local path).add_requires: Declare the packages that the project depends on.add_packages: Link the specified packages to the current target.
Repository vs Package
We need to distinguish between Repository and Package:
- Repository: A collection of package description files. It can maintain and manage multiple packages.
- Package: A specific library or tool (e.g.,
foo,zlib).
A repository can contain multiple packages, and their organization structure is usually as follows (classified by the first letter):
repository/
└── packages/
├── f/
│ ├── foo/
│ │ └── xmake.lua
│ └── bar/
│ └── xmake.lua
└── z/
└── zlib/
└── xmake.luaThe build/packages directory generated by xmake package mentioned above is actually a directory structure that conforms to the Xmake repository specification. Therefore, we can import it as a repository via add_repositories, and Xmake will automatically search for the foo package we need in it.
When executing xmake to build, Xmake will link the corresponding binary libraries directly from the local repository.
Distribute Remote Package
If we need to manage versions and distribute via a git repository, we can use the remote package mode. It supports distributing both source packages and binary packages.
The advantages of remote packages are:
- Version control based on Git
- Supports source distribution (automatic compilation) and binary distribution (direct installation)
- Support for multi-version switching
Configure Remote Package
We run xmake package -f remote to generate a configuration template for the remote package.
$ xmake package -f remoteIt will generate the packages/f/foo/xmake.lua file. We can modify it as needed to support source distribution or binary distribution.
Source Distribution
This is the most common way. We only need to configure the git repository address, and Xmake will automatically download the source code and compile and install it.
package("foo")
set_description("The foo package")
set_license("Apache-2.0")
-- Set private git repository address
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)Binary Distribution
If we don't want to leak source code, or want to speed up installation, we can pre-compile binary packages (e.g. .tar.gz), upload them to a server, and then configure the download address.
package("foo")
set_description("The foo package")
-- Set pre-compiled binary package address
add_urls("https://example.com/releases/foo-$(version).tar.gz")
add_versions("1.0", "<shasum>")
on_install(function (package)
-- Install binary files directly
os.cp("include", package:installdir())
os.cp("lib", package:installdir())
end)For more detailed package configuration parameters, please refer to: Package Configuration and Package Dependency API.
In addition, we can also refer to the existing package configurations in the official repository xmake-repo.
We need to create a private git repository (e.g., my-repo) to store all package configurations. Push the generated packages directory to this repository.
The directory structure is similar to:
my-repo/
└── packages/
└── f/
└── foo/
└── xmake.luaConsume Remote Package
In the consumer project, we need to add this private repository.
add_rules("mode.debug", "mode.release")
-- Add private Git repository
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 will automatically pull the package description from the my-repo repository, and then download the foo source code according to add_urls for compilation and installation.
Distribute C++ Modules Package
Xmake also supports distributing C++ Modules libraries. We only need to add {install = true} in add_files to package and distribute module files (.mpp, .ixx, etc.) together.
Usually, we need to define the package in an independent repository.
Configure Library Project
In the xmake.lua of the library project, we need to export the module files:
target("foo")
set_kind("static")
add_files("src/*.cpp")
-- Install and distribute module files
add_files("src/*.mpp", {install = true})Package Repository
In the private package repository (e.g., my-repo), add the package description file packages/f/foo/xmake.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)Integration
When integrating on the consumer side, you only need to import the repository and enable the build.c++.modules policy:
add_repositories("my-repo git@github.com:mycompany/my-repo.git")
add_requires("foo")
target("bar")
set_kind("binary")
set_languages("c++20")
-- Enable C++ Modules support
set_policy("build.c++.modules", true)
add_packages("foo")For more complete examples, please refer to: C++ Modules Package Distribution Example.
Manage Package Repositories
Whether it is a remote package or a local package, we can flexibly choose how to manage the package repository.
Project Internal Maintenance
If it is a private package within the project and does not need to be shared across projects, we can place the package repository directly under the project directory.
For example, we create a packages directory in the project root directory as a repository:
projectdir/
├── packages/
│ └── f/
│ └── foo/
│ └── xmake.lua
├── src/
│ └── main.cpp
└── xmake.luaThen add this repository in xmake.lua via add_repositories (relative paths are supported):
add_repositories("my-repo packages")
add_requires("foo")
target("test")
set_kind("binary")
add_files("src/*.cpp")
add_packages("foo")In this way, Xmake will automatically look for the foo package configuration in the packages directory under the current project.
Private Git Repository
If you need to share packages among multiple projects within the company, establishing an independent Git repository (such as GitHub private repository, GitLab, Gitee, or company intranet Git) to maintain package configurations is a better choice.
We can import the git repository address directly via add_repositories:
add_repositories("my-repo git@github.com:mycompany/my-repo.git")If the repository is private, please ensure that the local environment has configured SSH Key or has access permissions.
In addition, we can also use the xrepo add-repo or xmake repo --add command to add the repository address.
xrepo is a global independent command, while xmake repo can add package repositories only in the local project without affecting other projects.
# Add globally
$ xrepo add-repo my-repo git@github.com:mycompany/my-repo.git
# Add only for the current project
$ xmake repo --add my-repo git@github.com:mycompany/my-repo.gitOfficial Repository
If your package is open source and you want to share it with all Xmake users, you can submit it to the official xmake-repo repository. For specific contribution guidelines, please refer to: Submit Packages to Official Repository.
Install to System
In addition to packaging and distribution, during the development and debugging stage, or if we want to install the library directly to the system directory for other projects to use, we can run xmake install directly.
$ xmake installBy default, it will install to the system directory. If you want to install to a specific directory, you can specify the output directory:
$ xmake install -o /tmp/outputAfter installation, the directory structure is roughly as follows:
/tmp/output
├── include
│ └── foo.h
├── lib
│ ├── libfoo.a
│ └── pkgconfig
│ └── foo.pc
└── share
└── cmake
└── modules
└── foo-config.cmakeSince we configured the utils.install.pkgconfig_importfiles and utils.install.cmake_importfiles rules (see Use in Other Build Systems), foo.pc and foo-config.cmake files will be automatically generated during installation.
In this way, other non-xmake third-party projects (such as CMake projects) can also find and integrate it via find_package(foo).
Integrate with Other Build Systems
If we are developing a library that needs to be used by other non-xmake projects, we can integrate it in the following ways.
Use CMake (Xrepo)
If it is a CMake project, we can use xrepo-cmake to directly integrate packages managed by Xmake.
# Download 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)
# Import package
xrepo_package("foo")
# Link package
target_link_libraries(demo PRIVATE foo)For packages in private repositories, we need to ensure that the corresponding repository has been added locally (xrepo add-repo myrepo ...).
For more detailed instructions, please refer to: Using Xmake Packages in CMake.
Use CMake/Pkg-config
We can also configure utils.install.pkgconfig_importfiles and utils.install.cmake_importfiles rules to generate import files while installing the library.
target("foo")
set_kind("static")
add_files("src/foo.cpp")
add_headerfiles("src/foo.h")
-- Export pkgconfig/cmake import files
add_rules("utils.install.pkgconfig_importfiles")
add_rules("utils.install.cmake_importfiles")In this way, after users execute xmake install to install the library to the system, they can directly use standard methods to find and use the library.
CMake
find_package(foo REQUIRED)
target_link_libraries(demo PRIVATE foo::foo)Pkg-config
pkg-config --cflags --libs fooGenerate Installation Package (XPack)
Xmake also supports using the XPack plugin to generate installation packages in various formats, such as NSIS, Deb, RPM, Zip, etc.
This is very useful for distributing binary SDKs or deploying to production environments.
Supported Formats
- Windows:
nsis,wix,zip,targz - Linux:
deb,rpm,srpm,runself(shell self-extracting script),targz,srczip,appimage - MacOS:
dmg,zip,targz,runself
Configuration Example
We can add an xpack configuration block in xmake.lua to define packaging rules.
For example, to configure generating an NSIS installer:
-- Include xpack plugin
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 to package
add_targets("foo")
-- Add other files
add_installfiles("doc/*.md", {prefixdir = "share/doc/foo"})Then execute the packaging command:
$ xmake packIt will automatically download the NSIS tool and generate the installation package. The generated installation package can be installed by double-clicking, and it automatically configures environment variables such as PATH, making it convenient for users to use.
For more details, please see the documentation: XPack Packaging.