|
| 1 | +--- |
| 2 | +title: Adding Dependencies and Auto-detection Mechanism |
| 3 | +tags: [xmake, dependencies, auto-detection] |
| 4 | +date: 2016-08-06 |
| 5 | +author: Ruki |
| 6 | +outline: deep |
| 7 | +--- |
| 8 | + |
| 9 | +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. |
| 10 | + |
| 11 | +Let's look at how xmake's package mechanism works through a specific example. |
| 12 | + |
| 13 | +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: |
| 14 | + |
| 15 | +``` |
| 16 | +demo |
| 17 | + - xmake.lua |
| 18 | + - src |
| 19 | + main.c |
| 20 | + - pkg |
| 21 | + zlib.pkg |
| 22 | + polarssl.pkg |
| 23 | +``` |
| 24 | + |
| 25 | +You can modify `xmake.lua` to use the two dependency packages above: |
| 26 | + |
| 27 | +```lua |
| 28 | +-- Add dependency package directory, all required packages will be searched from this directory |
| 29 | +add_packagedirs("pkg") |
| 30 | + |
| 31 | +-- Add target |
| 32 | +target("demo") |
| 33 | + |
| 34 | + -- Set program type to binary executable |
| 35 | + set_kind("binary") |
| 36 | + |
| 37 | + -- Add source files |
| 38 | + add_files("src/*.c") |
| 39 | + |
| 40 | + -- Add polarssl and zlib packages through option mechanism, if detection passes, they will be automatically linked |
| 41 | + -- The first time you run xmake config or xmake build, it will automatically detect them and cache the configuration |
| 42 | + -- To re-detect, you can run xmake config -c to clear the original configuration and reconfigure everything |
| 43 | + add_options("polarssl", "zlib") |
| 44 | + |
| 45 | + -- Set auto-generated configuration header file, if mysql detection passes, it will generate CONFIG_PACKAGE_HAVE_MYSQL switch |
| 46 | + set_config_h("$(buildir)/config.h") |
| 47 | + |
| 48 | + -- Set config.h macro switch prefix: CONFIG_xxxx |
| 49 | + set_config_h_prefix("CONFIG") |
| 50 | + |
| 51 | + -- Add header file search directory, here to search for config.h |
| 52 | + add_includedirs("$(buildir)") |
| 53 | +``` |
| 54 | + |
| 55 | +Next, here's how to use it in your code: |
| 56 | + |
| 57 | +```c |
| 58 | +#include <stdio.h> |
| 59 | + |
| 60 | +// Include auto-generated config.h header file |
| 61 | +// Search path is set in ./build directory |
| 62 | +#include "config.h" |
| 63 | + |
| 64 | +// If zlib exists on current platform, use it |
| 65 | +#ifdef CONFIG_PACKAGE_HAVE_ZLIB |
| 66 | +# include "zlib/zlib.h" |
| 67 | +#endif |
| 68 | + |
| 69 | +// If polarssl exists on current platform, use it |
| 70 | +#ifdef CONFIG_PACKAGE_HAVE_POLARSSL |
| 71 | +# include "polarssl/polarssl.h" |
| 72 | +#endif |
| 73 | + |
| 74 | +int main(int argc, char** argv) |
| 75 | +{ |
| 76 | + printf("hello world!\n"); |
| 77 | + return 0; |
| 78 | +} |
| 79 | +``` |
| 80 | +
|
| 81 | +The above is the simplest example of package usage. Now let's see how this `zlib.pkg` is generated: |
| 82 | +
|
| 83 | +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. |
| 84 | +
|
| 85 | +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. |
| 86 | +
|
| 87 | +A pkg package directory structure: |
| 88 | +
|
| 89 | +``` |
| 90 | +zlib.pkg |
| 91 | + - inc (header file directory, optional) |
| 92 | + - zlib/zlib.h |
| 93 | + - lib (link library directory, optional) |
| 94 | + - linux/i386/libz.a |
| 95 | + - windows/i386/zlib.lib |
| 96 | + - xmake.lua (package description file) |
| 97 | +``` |
| 98 | +
|
| 99 | +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. |
| 100 | +
|
| 101 | +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. |
| 102 | +
|
| 103 | +Let's look at the description logic of `zlib.pkg/xmake.lua` I provided here: |
| 104 | +
|
| 105 | +```lua |
| 106 | +-- Add a zlib package auto-configuration option |
| 107 | +option("zlib") |
| 108 | +
|
| 109 | + -- Set whether to display in xmake f -h configuration menu |
| 110 | + -- If you want your package to allow users to manually disable it in the project, then enable this |
| 111 | + set_showmenu(true) |
| 112 | +
|
| 113 | + -- Display related description information in xmake f -h |
| 114 | + set_description("The mysql package") |
| 115 | +
|
| 116 | + -- If detection passes, define macro switch to config.h |
| 117 | + add_defines_h_if_ok("$(prefix)_PACKAGE_HAVE_ZLIB") |
| 118 | +
|
| 119 | + -- Detect linking |
| 120 | + add_links("z") |
| 121 | +
|
| 122 | + -- Add detected link library directory, here set to first detect if link library exists in zlib.pkg/lib/ for related platform, then detect system |
| 123 | + -- If this is not set, xmake can only detect link libraries in some system directories, such as: /usr/lib, /usr/local/lib |
| 124 | + -- If it cannot be detected in common system directories, but you have installed this library, you can set the search directory for detection yourself |
| 125 | + add_linkdirs("lib/$(plat)/$(arch)") |
| 126 | +
|
| 127 | + -- Detect if #include "zlib/zlib.h" can compile successfully |
| 128 | + add_cincludes("zlib/zlib.h") |
| 129 | +
|
| 130 | + -- Add some detected header file directories, by default it will search in zlib.pkg/inc, of course you can also specify other directories |
| 131 | + add_includedirs("inc/$(plat)", "inc") |
| 132 | +``` |
| 133 | + |
| 134 | +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. |
| 135 | + |
| 136 | +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. |
| 137 | + |
| 138 | +For example, the description of this base package `base.pkg`: |
| 139 | + |
| 140 | +```lua |
| 141 | +-- Base package base.pkg |
| 142 | +option("base") |
| 143 | + |
| 144 | + -- If current platform is windows, detect ws2_32 link library dependency |
| 145 | + if os("windows") then add_links("ws2_32") |
| 146 | + -- If it's other platforms, detect -lm, -ldl, -lpthread dependencies (since they are all system libraries, no search directory is set here) |
| 147 | + else add_links("m", "dl", "pthread") end |
| 148 | +``` |
| 149 | + |
| 150 | +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. |
| 151 | + |
| 152 | +If you want to add interface detection in your package detection, you only need to use: |
| 153 | + |
| 154 | +* `add_cfuncs` |
| 155 | +* `add_cxxfuncs` |
| 156 | +* `add_ctypes` |
| 157 | +* `add_cxxtypes` |
| 158 | + |
| 159 | +So using the package mechanism can maximize the reuse of your dependency environment across different projects. It's a very useful feature. |
| 160 | + |
0 commit comments