# Using packages in CMake

CMake wrapper for [Xrepo](https://xrepo.xmake.io/) C and C++ package manager.

This allows you to use CMake to build your project, while using Xrepo to manage
dependent packages. This project is partially inspired by
[cmake-conan](https://github.com/conan-io/cmake-conan).

Example use cases for this project:

- Existing CMake projects which want to use Xrepo to manage packages.
- New projects which have to use CMake, but want to use Xrepo to manage
  packages.

## APIs

### xrepo_package

[xrepo.cmake](https://github.com/xmake-io/xrepo-cmake/blob/main/xrepo.cmake) provides `xrepo_package` function to manage
packages.

```cmake
xrepo_package(
    "foo 1.2.3"
    [CONFIGS feature1=true,feature2=false]
    [CONFIGS path/to/script.lua]
    [DEPS]
    [MODE debug|release]
    [ALIAS aliasname]
    [OUTPUT verbose|diagnosis|quiet]
    [DIRECTORY_SCOPE]
)
```

Some of the function arguments correspond directly to Xrepo command options.

`xrepo_package` adds the package install directory to `CMAKE_PREFIX_PATH`. So `find_package`
can be used. If `CMAKE_MINIMUM_REQUIRED_VERSION` >= 3.1, CMake `PkgConfig` will also search
for pkgconfig files under package install directories.

After calling `xrepo_package(foo)`, there are three ways to use `foo` package:

1. Call `find_package(foo)` if package provides cmake config-files.
    - Refer to CMake [`find_package`](https://cmake.org/cmake/help/latest/command/find_package.html) documentation for more details.
2. If the package does not provide cmake config files or find modules
   - Following variables can be used to use the package (variable names following CMake
     find modules [standard variable names](https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#standard-variable-names))
     - `foo_INCLUDE_DIRS`
     - `foo_LIBRARY_DIRS`
     - `foo_LIBRARIES`
     - `foo_DEFINITIONS`
   - If `DIRECTORY_SCOPE` is specified, `xrepo_package` will run following code
     ```cmake
     include_directories(${foo_INCLUDE_DIRS})
     link_directories(${foo_LIBRARY_DIRS})
     ```
3. Use `xrepo_target_packages`. Please refer to following section.

Note `CONFIGS path/to/script.lua` is for fine control over package configs.
For example:
  - Exclude packages on system.
  - Override dependent packages' default configs, e.g. set `shared=true`.

If `DEPS` is specified, all dependent libraries will add to `CMAKE_PREFIX_PATH`, along with include,
libraries being included in the four variables.

### xrepo_target_packages

Add package includedirs and links/linkdirs to the given target.

```cmake
xrepo_target_packages(
    target
    [NO_LINK_LIBRARIES]
    [PRIVATE|PUBLIC|INTERFACE]
    package1 package2 ...
)
```

- `NO_LINK_LIBRARIES`
  - In case a package provides multiple libs and user need to select which one
    to link, pass `NO_LINK_LIBRARIES` to disable calling `target_link_libraries`.
    User should call `target_link_libraries` to setup correct library linking.
- `PRIVATE|PUBLIC|INTERFACE`
  - The default is not specifying a propagation control keyword when calling
    `target_include_libraries`, `target_link_libraries`, etc., because there's no
    default choice on this in CMake.
  - Refer to this [Stack Overflow answer](https://stackoverflow.com/a/26038443)
    for differences.

## Use package from official repository

Xrepo official repository: [xmake-repo](https://github.com/xmake-io/xmake-repo)

Here's an example `CMakeLists.txt` that uses `gflags` package version 2.2.2
managed by Xrepo.

### Integrate xrepo.cmake

```cmake
cmake_minimum_required(VERSION 3.13.0)

project(foo)

# Download xrepo.cmake if not exists in build directory.
if(NOT EXISTS "${CMAKE_BINARY_DIR}/xrepo.cmake")
    message(STATUS "Downloading xrepo.cmake from https://github.com/xmake-io/xrepo-cmake/")
    # mirror https://cdn.jsdelivr.net/gh/xmake-io/xrepo-cmake@main/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 xrepo.cmake so we can use xrepo_package function.
include(${CMAKE_BINARY_DIR}/xrepo.cmake)
```

### Add basic packages

```cmake
xrepo_package("zlib")

add_executable(example-bin "")
target_sources(example-bin PRIVATE
    src/main.cpp
)
xrepo_target_packages(example-bin zlib)
```

### Add packages with configs

```cmake
xrepo_package("gflags 2.2.2" CONFIGS "shared=true,mt=true")

add_executable(example-bin "")
target_sources(example-bin PRIVATE
    src/main.cpp
)
xrepo_target_packages(example-bin gflags)
```

### Add packages with cmake modules

```cmake
xrepo_package("gflags 2.2.2" CONFIGS "shared=true,mt=true")

# `xrepo_package` add gflags install directory to CMAKE_PREFIX_PATH.
# As gflags provides CMake config-files, we can now call `find_package` to find
# the gflags package.
# Refer to https://cmake.org/cmake/help/latest/command/find_package.html#search-modes
find_package(gflags CONFIG COMPONENTS shared)

add_executable(example-bin "")
target_sources(example-bin PRIVATE
    src/main.cpp
)
target_link_libraries(example-bin gflags)
```

### Add custom packages

We can also add custom packages in our project.

```cmake
set(XREPO_XMAKEFILE ${CMAKE_CURRENT_SOURCE_DIR}/packages/xmake.lua)
xrepo_package("myzlib")

add_executable(example-bin "")
target_sources(example-bin PRIVATE
    src/main.cpp
)
xrepo_target_packages(example-bin myzlib)
```

Define myzlib package in packages/xmake.lua

```lua
package("myzlib")
    set_homepage("http://www.zlib.net")
    set_description("A Massively Spiffy Yet Delicately Unobtrusive Compression Library")

    set_urls("http://zlib.net/zlib-$(version).tar.gz",
             "https://downloads.sourceforge.net/project/libpng/zlib/$(version)/zlib-$(version).tar.gz")

    add_versions("1.2.10", "8d7e9f698ce48787b6e1c67e6bff79e487303e66077e25cb9784ac8835978017")

    on_install(function (package)
  	-- TODO
    end)

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

We can write a custom package in xmake.lua. Please refer to [Define Xrepo package](/guide/package-management/package-distribution.html#define-package-configuration).

## Options and variables for `xrepo.cmake`

Following options can be specified with `cmake -D<var>=<value>`.
Or use `set(var value)` in `CMakeLists.txt`.

- `XMAKE_CMD`: string, defaults to empty string
  - Specify path to `xmake` command. Use this option if `xmake` is not installed
    in standard location and can't be detected automatically.
- `XREPO_PACKAGE_VERBOSE`: `[ON|OFF]`
  - Enable verbose output for Xrepo Packages.
- `XREPO_BOOTSTRAP_XMAKE`: `[ON|OFF]`
  - If `ON`, `xrepo.cmake` will install `xmake` if it is not found.
- `XREPO_PACKAGE_DISABLE`: `[ON|OFF]`
  - Set this to `ON` to disable `xrepo_package` function.
  - If setting this variable in `CMakeLists.txt`, please set it before including
    `xrepo.cmake`.

## Switching compiler and cross compilation

Following variables controll cross compilation. Note: to specify a different compiler other than
the default one on system, platform must be set to "cross".

- `XREPO_TOOLCHAIN`: string, defaults to empty string
  - Specify toolchain name. Run `xmake show -l toolchains` to see available toolchains.
- `XREPO_PLATFORM`: string, defaults to empty string
  - Specify platform name. If `XREPO_TOOLCHAIN` is specified and this is not,
    `XREPO_PLATFORM` will be set to `cross`.
- `XREPO_ARCH`: string, defaults to empty string
  - Specify architecture name.
- `XREPO_XMAKEFILE`: string, defaults to empty string
  - Specify Xmake script file of Xrepo package.

## Use package from 3rd repository

In addition to installing packages from officially maintained repository,
Xrepo can also install packages from third-party package managers such as vcpkg/conan/conda/pacman/homebrew/apt/dub/cargo.

For the use of the command line, we can refer to the documentation: [Xrepo command usage](/guide/package-management/xrepo-cli).

We can also use it directly in cmake to install packages from third-party repositories, just add the repository name as a namespace. e.g. `vcpkg::zlib`, `conan::pcre2`

### Conan

```cmake
xrepo_package("conan::gflags/2.2.2")
```

### Conda

```cmake
xrepo_package("conda::gflags 2.2.2")
```

### Vcpkg

```cmake
xrepo_package("vcpkg::gflags")
```

### Homebrew

```cmake
xrepo_package("brew::gflags")
```

## How does it work?

[xrepo.cmake](https://github.com/xmake-io/xrepo-cmake/blob/main/xrepo.cmake) module basically does the following tasks:

- Call `xrepo install` to ensure specific package is installed.
- Call `xrepo fetch` to get package information and setup various variables for
  using the installed package in CMake.

The following section is a short introduction to using Xrepo. It helps to
understand how `xrepo.cmake` works and how to specify some of the options in
`xrepo_package`.

## Xrepo workflow

Assmuing [Xmake](https://github.com/xmake-io/xmake/) is installed.

Suppose we want to use `gflags` packages.

First, search for `gflags` package in Xrepo.

```
$ xrepo search gflags
The package names:
    gflags:
      -> gflags-v2.2.2: The gflags package contains a C++ library that implements commandline flags processing. (in builtin-repo)
```

It's already in Xrepo, so we can use it. If it's not in Xrepo, we can create it in
[self-built repositories](/guide/package-management/xrepo-cli.html#support-distributed-repository).

Let's see what configs are available for the package before using it:

```
$ xrepo info gflags
...
      -> configs:
         -> mt: Build the multi-threaded gflags library. (default: false)
      -> configs (builtin):
         -> debug: Enable debug symbols. (default: false)
         -> shared: Build shared library. (default: false)
         -> pic: Enable the position independent code. (default: true)
...
```

Suppose we want to use multi-threaded gflags shared library. We can install the package with following command:

```
xrepo install --mode=release --configs='mt=true,shared=true' 'gflags 2.2.2'
```

Only the first call to the above command will compile and install the package.
To speed up cmake configuration, `xrepo` command will only be executed when the
package is not installed or `xrepo_package` parameters have changed.

After package installation, because we are using CMake instead of Xmake, we have
to get package installation information by ourself. `xrepo fetch` command does
exactly this:

```
xrepo fetch --json --mode=release --configs='mt=true,shared=true' 'gflags 2.2.2'
```

The above command will print out package's include, library directory along with
other information. `xrepo_package` uses these information to setup variables to use
the specified package.

For CMake 3.19 and later which has JSON support, `xrepo_package` parses the JSON
output. For previous version of CMake, `xrepo_package` uses only the `--cflags` option
to get package include directory. Library and cmake module directory are infered from that
directory, so it maybe unreliable to detect the correct paths.
