---
title: Adding Dependencies and Auto-detection Mechanism
tags: [xmake, dependencies, auto-detection]
date: 2016-08-06
author: Ruki
outline: deep
---

xmake encapsulates dependency libraries, dependency headers, dependency types, and dependency interfaces uniformly using the option mechanism, and further introduces a package mechanism at a higher level, making adding and detecting dependencies more modular and simpler.

Let's look at how xmake's package mechanism works through a specific example.

Suppose your project already has two packages: `zlib.pkg` and `polarssl.pkg` (how to build packages will be explained in detail later; for now, you can refer to the examples of existing packages in [TBOX dependencies](https://github.com/waruqi/tbox/tree/master/pkg)). Your project directory structure is as follows:

```
demo
 - xmake.lua
 - src
   main.c
 - pkg
   zlib.pkg
   polarssl.pkg
```

You can modify `xmake.lua` to use the two dependency packages above:

```lua
-- Add dependency package directory, all required packages will be searched from this directory
add_packagedirs("pkg")

-- Add target
target("demo")

    -- Set program type to binary executable
    set_kind("binary")

    -- Add source files
    add_files("src/*.c") 

    -- Add polarssl and zlib packages through option mechanism, if detection passes, they will be automatically linked
    -- The first time you run xmake config or xmake build, it will automatically detect them and cache the configuration
    -- To re-detect, you can run xmake config -c to clear the original configuration and reconfigure everything
    add_options("polarssl", "zlib")

    -- Set auto-generated configuration header file, if mysql detection passes, it will generate CONFIG_PACKAGE_HAVE_MYSQL switch
    set_config_h("$(buildir)/config.h")

    -- Set config.h macro switch prefix: CONFIG_xxxx
    set_config_h_prefix("CONFIG")

    -- Add header file search directory, here to search for config.h
    add_includedirs("$(buildir)")
```

Next, here's how to use it in your code:

```c
#include <stdio.h>

// Include auto-generated config.h header file
// Search path is set in ./build directory
#include "config.h"

// If zlib exists on current platform, use it
#ifdef CONFIG_PACKAGE_HAVE_ZLIB
#   include "zlib/zlib.h"
#endif

// If polarssl exists on current platform, use it
#ifdef CONFIG_PACKAGE_HAVE_POLARSSL
#   include "polarssl/polarssl.h"
#endif

int main(int argc, char** argv)
{
    printf("hello world!\n");
    return 0;
}
```

The above is the simplest example of package usage. Now let's see how this `zlib.pkg` is generated:

If this package is developed by your own project `xxx`, you only need to run `xmake p` to package it, and it will automatically generate an `xxx.pkg` package in the `./build` directory, which you can directly use in other projects.

If it's a third-party library, you need to build it yourself, but it's also very convenient. If you can't, you can refer to some packages in the existing [TBOX dependencies](https://github.com/waruqi/tbox/tree/master/pkg) and modify them.

A pkg package directory structure:

```
zlib.pkg
    - inc (header file directory, optional)
       - zlib/zlib.h
    - lib (link library directory, optional)
       - linux/i386/libz.a
       - windows/i386/zlib.lib
    - xmake.lua (package description file)
```

Among them, `inc` and `lib` are optional. The specific logic is described in `xmake.lua`. The default package logic generated by xmake will first detect whether there are available libraries and header files in the `zlib.pkg` directory for the current platform. If the detection fails, it will then detect the system platform.

Of course, you can also modify the detection logic yourself. You don't have to do it this way. You only need to describe the `xxx.pkg/xmake.lua` file according to your needs.

Let's look at the description logic of `zlib.pkg/xmake.lua` I provided here:

```lua
-- Add a zlib package auto-configuration option
option("zlib")

    -- Set whether to display in xmake f -h configuration menu
    -- If you want your package to allow users to manually disable it in the project, then enable this
    set_showmenu(true)

    -- Display related description information in xmake f -h
    set_description("The mysql package")

    -- If detection passes, define macro switch to config.h
    add_defines_h_if_ok("$(prefix)_PACKAGE_HAVE_ZLIB")

    -- Detect linking
    add_links("z")

    -- Add detected link library directory, here set to first detect if link library exists in zlib.pkg/lib/ for related platform, then detect system
    -- If this is not set, xmake can only detect link libraries in some system directories, such as: /usr/lib, /usr/local/lib
    -- If it cannot be detected in common system directories, but you have installed this library, you can set the search directory for detection yourself
    add_linkdirs("lib/$(plat)/$(arch)")

    -- Detect if #include "zlib/zlib.h" can compile successfully
    add_cincludes("zlib/zlib.h")

    -- Add some detected header file directories, by default it will search in zlib.pkg/inc, of course you can also specify other directories
    add_includedirs("inc/$(plat)", "inc")
```

As long as you describe `xxx.pkg/xmake.lua` well, a package can be used by xmake and automatically detected. It uses xmake's option mechanism. Of course, in the package, you can not only detect dependency libraries and header files, but also detect whether certain required interfaces, type definitions, etc. exist.

And the detection mechanism completely uses lua syntax, supports if conditional logic, you can do some special processing for some specific platforms, making your package more universal.

For example, the description of this base package `base.pkg`:

```lua
-- Base package base.pkg
option("base")
    
    -- If current platform is windows, detect ws2_32 link library dependency
    if os("windows") then add_links("ws2_32") 
    -- If it's other platforms, detect -lm, -ldl, -lpthread dependencies (since they are all system libraries, no search directory is set here)
    else add_links("m", "dl", "pthread") end
```

If your package is only described through `xmake.lua` without other file directories, you can also embed your package `xmake.lua` description content directly into the project description file `xmake.lua`. These two are originally universal. In other words, the mechanism of `add_packagedirs("pkg")` is to call the project description API: `add_subdirs("pkg/*")` to add sub-projects. And `xxx.pkg` is just a sub-project description file.

If you want to add interface detection in your package detection, you only need to use:

* `add_cfuncs`
* `add_cxxfuncs`
* `add_ctypes`
* `add_cxxtypes`

So using the package mechanism can maximize the reuse of your dependency environment across different projects. It's a very useful feature.

