package_dependencies.html 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>xmake</title>
  6. <link rel="icon" href="/assets/img/favicon.ico">
  7. <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  8. <meta name="description" content="Description">
  9. <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  10. <link href="/assets/npm/github-markdown/github-markdown.min.css" rel="stylesheet">
  11. <style>
  12. .markdown-body {
  13. box-sizing: border-box;
  14. min-width: 200px;
  15. max-width: 980px;
  16. margin: 0 auto;
  17. padding: 45px;
  18. }
  19. @media (max-width: 767px) {
  20. .markdown-body {
  21. padding: 15px;
  22. }
  23. }
  24. </style>
  25. </head>
  26. <body>
  27. <article class="markdown-body">
  28. <h4>This is a mirror page, please see the original page: </h4><a href="https://xmake.io/#/zh-cn/manual/package_dependencies">https://xmake.io/#/zh-cn/manual/package_dependencies</a>
  29. <div id="wwads-panel" class="wwads-cn wwads-vertical wwads-sticky" data-id="239" style="max-width:180px;bottom:20px;right:20px;width:200px;height:260px;background:#fff;position:fixed"></div>
  30. </br>
  31. <script type="text/javascript" charset="UTF-8" src="https://cdn.wwads.cn/js/makemoney.js" async></script>
  32. <script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve=CE7I52QU&placement=xmakeio" id="_carbonads_js"></script>
  33. <style>
  34. #carbonads {
  35. font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu,
  36. Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
  37. }
  38. #carbonads {
  39. display: flex;
  40. max-width: 330px;
  41. background-color: hsl(0, 0%, 98%);
  42. box-shadow: 0 1px 4px 1px hsla(0, 0%, 0%, .1);
  43. }
  44. #carbonads a {
  45. color: inherit;
  46. text-decoration: none;
  47. }
  48. #carbonads a:hover {
  49. color: inherit;
  50. }
  51. #carbonads span {
  52. position: relative;
  53. display: block;
  54. overflow: hidden;
  55. }
  56. #carbonads .carbon-wrap {
  57. display: flex;
  58. }
  59. .carbon-img {
  60. display: block;
  61. margin: 0;
  62. line-height: 1;
  63. }
  64. .carbon-img img {
  65. display: block;
  66. }
  67. .carbon-text {
  68. font-size: 13px;
  69. padding: 10px;
  70. line-height: 1.5;
  71. text-align: left;
  72. }
  73. .carbon-poweredby {
  74. display: block;
  75. padding: 8px 10px;
  76. background: repeating-linear-gradient(-45deg, transparent, transparent 5px, hsla(0, 0%, 0%, .025) 5px, hsla(0, 0%, 0%, .025) 10px) hsla(203, 11%, 95%, .4);
  77. text-align: center;
  78. text-transform: uppercase;
  79. letter-spacing: .5px;
  80. font-weight: 600;
  81. font-size: 9px;
  82. line-height: 1;
  83. }
  84. </style>
  85. <h3 id="package">package</h3>
  86. <h4 id="">仓库依赖包定义描述</h4>
  87. <p>可先参考官方仓库中现有包描述:<a href="https://github.com/xmake-io/xmake-repo">xmake-repo</a></p>
  88. <p>这里给个比较具有代表性的实例供参考:</p>
  89. <pre><code class="lang-lua">package("libxml2")
  90. set_homepage("http://xmlsoft.org/")
  91. set_description("The XML C parser and toolkit of Gnome.")
  92. set_urls("https://github.com/GNOME/libxml2/archive/$(version).zip", {excludes = {"*/result/*", "*/test/*"}})
  93. add_versions("v2.9.8", "c87793e45e66a7aa19200f861873f75195065de786a21c1b469bdb7bfc1230fb")
  94. add_versions("v2.9.7", "31dd4c0e10fa625b47e27fd6a5295d246c883f214da947b9a4a9e13733905ed9")
  95. if is_plat("macosx", "linux") then
  96. add_deps("autoconf", "automake", "libtool", "pkg-config")
  97. end
  98. on_load(function (package)
  99. package:add("includedirs", "include/libxml2")
  100. package:add("links", "xml2")
  101. end)
  102. if is_plat("windows") and winos.version():gt("winxp") then
  103. on_install("windows", function (package)
  104. os.cd("win32")
  105. os.vrun("cscript configure.js iso8859x=yes iconv=no compiler=msvc cruntime=/MT debug=%s prefix=\"%s\"", package:debug() and "yes" or "no", package:installdir())
  106. os.vrun("nmake /f Makefile.msvc")
  107. os.vrun("nmake /f Makefile.msvc install")
  108. end)
  109. end
  110. on_install("macosx", "linux", function (package)
  111. import("package.tools.autoconf").install(package, {"--disable-dependency-tracking", "--without-python", "--without-lzma"})
  112. end)
  113. </code></pre>
  114. <h3 id="packageset_homepage">package:set_homepage</h3>
  115. <h4 id="">设置包所在项目的官方页面地址</h4>
  116. <h3 id="packageset_description">package:set_description</h3>
  117. <h4 id="">设置包的相关描述信息</h4>
  118. <p>一般通过<code>xmake require --info zlib</code>查看相关包信息时候,会看到。</p>
  119. <h3 id="packageset_kind">package:set_kind</h3>
  120. <h4 id="">设置包类型</h4>
  121. <p>对于依赖库,则不用设置,如果是可执行包,需要设置为binary。</p>
  122. <pre><code>package("cmake")
  123. set_kind("binary")
  124. set_homepage("https://cmake.org")
  125. set_description("A cross-platform family of tool designed to build, test and package software")
  126. </code></pre><h3 id="packageset_urls">package:set_urls</h3>
  127. <h4 id="">设置包源地址</h4>
  128. <p>设置包的源码包或者git仓库地址,跟add_urls不同的是,此接口是覆盖性设置,而add_urls是追加设置,其他使用方式类似,这个根据不同需要来选择。</p>
  129. <h3 id="packageadd_urls">package:add_urls</h3>
  130. <h4 id="">添加包源地址</h4>
  131. <p>添加包的源码包或者git仓库地址,此接口一般跟add_version配对使用,用于设置每个源码包的版本和对应的sha256值或者git的commit或者tag或者branch。</p>
  132. <p>!> 可以通过添加多个urls作为镜像源,xmake会自动检测优先选用最快的url进行下载,如果下载失败则会尝试其他urls。</p>
  133. <pre><code class="lang-lua">add_urls("https://github.com/protobuf-c/protobuf-c/releases/download/v$(version)/protobuf-c-$(version).tar.gz")
  134. add_versions("1.3.1", "51472d3a191d6d7b425e32b612e477c06f73fe23e07f6a6a839b11808e9d2267")
  135. </code></pre>
  136. <p>urls里面的<code>$(version)</code>内置变量,会根据实际安装时候选择的版本适配进去,而版本号都是从<code>add_versions</code>中指定的版本列表中选择的。</p>
  137. <p>如果对于urls里面带有比较复杂的版本串,没有跟add_versions有直接对应关系,则需要通过下面的方式定制化转换下:</p>
  138. <pre><code class="lang-lua">add_urls("https://sqlite.org/2018/sqlite-autoconf-$(version)000.tar.gz",
  139. {version = function (version) return version:gsub("%.", "") end})
  140. add_versions("3.24.0", "d9d14e88c6fb6d68de9ca0d1f9797477d82fc3aed613558f87ffbdbbc5ceb74a")
  141. add_versions("3.23.0", "b7711a1800a071674c2bf76898ae8584fc6c9643cfe933cfc1bc54361e3a6e49")
  142. </code></pre>
  143. <p>当然,我们也只可以添加git源码地址:</p>
  144. <pre><code class="lang-lua">add_urls("https://gitlab.gnome.org/GNOME/libxml2.git")
  145. </code></pre>
  146. <p>如果设置的多个镜像地址对应的源码包sha256是不同的,我们可以通过alias的方式来分别设置</p>
  147. <pre><code class="lang-lua">add_urls("https://ffmpeg.org/releases/ffmpeg-$(version).tar.bz2", {alias = "home"})
  148. add_urls("https://github.com/FFmpeg/FFmpeg/archive/n$(version).zip", {alias = "github"})
  149. add_versions("home:4.0.2", "346c51735f42c37e0712e0b3d2f6476c86ac15863e4445d9e823fe396420d056")
  150. add_versions("github:4.0.2", "4df1ef0bf73b7148caea1270539ef7bd06607e0ea8aa2fbf1bb34062a097f026")
  151. </code></pre>
  152. <p>我们也可以设置指定的 urls 的 http headers:</p>
  153. <pre><code class="lang-lua">add_urls("https://github.com/madler/zlib/archive/$(version).tar.gz", {
  154. http_headers = {"TEST1: foo", "TEST2: bar"}
  155. })
  156. </code></pre>
  157. <h3 id="packageadd_versions">package:add_versions</h3>
  158. <h4 id="">设置每个源码包的版本</h4>
  159. <p>它也会设置对应的sha256值,具体描述见:<a href="#packageadd_urls">add_urls</a></p>
  160. <h3 id="packageadd_versionfiles">package:add_versionfiles</h3>
  161. <h4 id="">添加包版本列表</h4>
  162. <p>通常我们可以通过 <code>add_versions</code> 接口添加包版本,但是如果版本越来越多,就会导致包配置太过臃肿,这个时候,我们可以使用 <code>add_versionfiles</code> 接口将所有的版本列表,存储到单独的文件中去维护。</p>
  163. <p>例如:</p>
  164. <pre><code class="lang-lua">package("libcurl")
  165. add_versionfiles("versions.txt")
  166. </code></pre>
  167. <pre><code class="lang-bash">8.5.0 ce4b6a6655431147624aaf582632a36fe1ade262d5fab385c60f78942dd8d87b
  168. 8.4.0 e5250581a9c032b1b6ed3cf2f9c114c811fc41881069e9892d115cc73f9e88c6
  169. 8.0.1 9b6b1e96b748d04b968786b6bdf407aa5c75ab53a3d37c1c8c81cdb736555ccf
  170. 7.87.0 5d6e128761b7110946d1276aff6f0f266f2b726f5e619f7e0a057a474155f307
  171. 7.31.0 a73b118eececff5de25111f35d1d0aafe1e71afdbb83082a8e44d847267e3e08
  172. ...
  173. </code></pre>
  174. <h3 id="packageadd_patches">package:add_patches</h3>
  175. <h4 id="">设置包补丁</h4>
  176. <p>此接口用于针对源码包,在编译安装前,先打对应设置的补丁包,再对其进行编译,并且可支持同时打多个补丁。</p>
  177. <pre><code class="lang-lua">if is_plat("macosx") then
  178. add_patches("1.15", "https://raw.githubusercontent.com/Homebrew/patches/9be2793af/libiconv/patch-utf8mac.diff",
  179. "e8128732f22f63b5c656659786d2cf76f1450008f36bcf541285268c66cabeab")
  180. end
  181. </code></pre>
  182. <p>例如,上面的代码,就是针对macosx下编译的时候,打上对应的patch-utf8mac.diff补丁,并且每个补丁后面也是要设置sha256值的,确保完整性。</p>
  183. <h3 id="packageadd_links">package:add_links</h3>
  184. <h4 id="">设置库链接</h4>
  185. <p>默认情况下,xmake会去自动检测安装后的库,设置链接关系,但是有时候并不是很准,如果要自己手动调整链接顺序,以及链接名,则可以通过这个接口来设置。</p>
  186. <pre><code class="lang-lua">add_links("mbedtls", "mbedx509", "mbedcrypto")
  187. </code></pre>
  188. <h3 id="packageadd_syslinks">package:add_syslinks</h3>
  189. <h4 id="">设置系统库链接</h4>
  190. <p>添加一些系统库链接,有些包集成链接的时候,还需要依赖一些系统库,才能链接通过,这个时候可以在包描述里面都附加上去。</p>
  191. <pre><code class="lang-lua">if is_plat("macosx") then
  192. add_frameworks("CoreGraphics", "CoreFoundation", "Foundation")
  193. elseif is_plat("windows") then
  194. add_defines("CAIRO_WIN32_STATIC_BUILD=1")
  195. add_syslinks("gdi32", "msimg32", "user32")
  196. else
  197. add_syslinks("pthread")
  198. end
  199. </code></pre>
  200. <h3 id="packageadd_linkorders">package:add_linkorders</h3>
  201. <h4 id="">调整包内部的链接顺序</h4>
  202. <p>具体详情可以看下 target 内部对 <code>add_linkorders</code> 的文档说明,<a href="https://xmake.io/#/zh-cn/manual/project_target?id=targetadd_linkorders">target/add_linkorders</a>。</p>
  203. <pre><code class="lang-lua">package("libpng")
  204. add_linkorders("png16", "png", "linkgroup::foo")
  205. add_linkgroups("dl", {name = "foo", group = true})
  206. </code></pre>
  207. <h3 id="packageadd_linkgroups">package:add_linkgroups</h3>
  208. <h4 id="">配置包的链接组</h4>
  209. <p>具体详情可以看下 target 内部对 <code>add_linkgroups</code> 的文档说明,<a href="https://xmake.io/#/zh-cn/manual/project_target?id=targetadd_linkgroups">target/add_linkgroups</a>。</p>
  210. <pre><code class="lang-lua">package("libpng")
  211. add_linkorders("png16", "png", "linkgroup::foo")
  212. add_linkgroups("dl", {name = "foo", group = true})
  213. </code></pre>
  214. <h3 id="packageadd_frameworks">package:add_frameworks</h3>
  215. <h4 id="frameworks">添加依赖的系统 frameworks 链接</h4>
  216. <p>示例见:<a href="#packageadd_syslinks">add_syslinks</a></p>
  217. <h3 id="packageadd_linkdirs">package:add_linkdirs</h3>
  218. <h4 id="">添加链接目录</h4>
  219. <p>包的链接库搜索目录也是可以调整的,不过通常都不需要,除非一些库安装完不在prefix/lib下面,而在lib的子目录下,默认搜索不到的话。</p>
  220. <h3 id="packageadd_includedirs">package:add_includedirs</h3>
  221. <h4 id="">添加其他头文件搜索目录</h4>
  222. <h3 id="packageadd_bindirs">package:add_bindirs</h3>
  223. <h4 id="">添加可执行文件目录</h4>
  224. <p>默认情况下,如果配置了 <code>set_kind("binary")</code> 或者 <code>set_kind("toolchain")</code> 作为可执行的包。</p>
  225. <p>那么,它默认会将 bin 目录作为可执行目录,并且自动将它加入到 PATH 环境变量中去。</p>
  226. <p>而如果对应 library 包,想要将里面附带编译的一些可执行工具开放给用户执行,那么需要在包中配置 <code>package:addenv("PATH", "bin")</code> 中才行。</p>
  227. <p>而通过这个接口去配置 <code>add_bindirs("bin")</code> ,那么将会自动将 bin 添加到 PATH,不再需要单独配置 PATH,另外,这也提供了一种可以修改可执行目录的方式。</p>
  228. <h3 id="packageadd_defines">package:add_defines</h3>
  229. <h4 id="">添加宏定义</h4>
  230. <p>可以对集成的包对外输出一些特定的定义选项。</p>
  231. <h3 id="packageadd_configs">package:add_configs</h3>
  232. <h4 id="">添加包配置</h4>
  233. <p>我们可以通过此接口添加每个包的对外输出配置参数:</p>
  234. <pre><code class="lang-lua">package("pcre2")
  235. set_homepage("https://www.pcre.org/")
  236. set_description("A Perl Compatible Regular Expressions Library")
  237. add_configs("bitwidth", {description = "Set the code unit width.", default = "8", values = {"8", "16", "32"}})
  238. on_load(function (package)
  239. local bitwidth = package:config("bitwidth") or "8"
  240. package:add("links", "pcre2-" .. bitwidth)
  241. package:add("defines", "PCRE2_CODE_UNIT_WIDTH=" .. bitwidth)
  242. end)
  243. </code></pre>
  244. <p>在工程项目里面,我们也可以查看特定包的可配置参数和值列表:</p>
  245. <pre><code class="lang-bash">$ xmake require --info pcre2
  246. The package info of project:
  247. require(pcre2):
  248. -> description: A Perl Compatible Regular Expressions Library
  249. -> version: 10.31
  250. ...
  251. -> configs:
  252. -> bitwidth:
  253. -> description: Set the code unit width.
  254. -> values: {"8","16","32"}
  255. -> default: 8
  256. </code></pre>
  257. <p>然后在项目里面,启用这些配置,编译集成带有特定配置的包:</p>
  258. <pre><code class="lang-lua">add_requires("pcre2", {configs = {bitwidth = 16}})
  259. </code></pre>
  260. <h3 id="packageadd_extsources">package:add_extsources</h3>
  261. <h4 id="">添加扩展的包源</h4>
  262. <p>2.5.2 版本开始,我们也新增了 <code>add_extsources</code> 和 <code>on_fetch</code> 两个配置接口,可以更好的配置 xmake 在安装 C/C++ 包的过程中,对系统库的查找过程。</p>
  263. <p>至于具体背景,我们可以举个例子,比如我们在 <a href="https://github.com/xmake-io/xmake-repo">xmake-repo</a> 仓库新增了一个 <code>package("libusb")</code> 的包。</p>
  264. <p>那么用户就可以通过下面的方式,直接集成使用它:</p>
  265. <pre><code class="lang-lua">add_requires("libusb")
  266. target("test")
  267. set_kind("binary")
  268. add_files("src/*.c")
  269. add_packages("libusb")
  270. </code></pre>
  271. <p>如果用户系统上确实没有安装 libusb,那么 xmake 会自动下载 libusb 库源码,自动编译安装集成,没啥问题。</p>
  272. <p>但如果用户通过 <code>apt install libusb-1.0</code> 安装了 libusb 库到系统,那么按理 xmake 应该会自动优先查找用户安装到系统环境的 libusb 包,直接使用,避免额外的下载编译安装。</p>
  273. <p>但是问题来了,xmake 内部通过 <code>find_package("libusb")</code> 并没有找打它,这是为什么呢?因为通过 apt 安装的 libusb 包名是 <code>libusb-1.0</code>, 而不是 libusb。</p>
  274. <p>我们只能通过 <code>pkg-config --cflags libusb-1.0</code> 才能找到它,但是 xmake 内部的默认 find_package 逻辑并不知道 <code>libusb-1.0</code> 的存在,所以找不到。</p>
  275. <p>因此为了更好地适配不同系统环境下,系统库的查找,我们可以通过 <code>add_extsources("pkgconfig::libusb-1.0")</code> 去让 xmake 改进查找逻辑,例如:</p>
  276. <pre><code class="lang-lua">package("libusb")
  277. add_extsources("pkgconfig::libusb-1.0")
  278. on_install(function (package)
  279. -- ...
  280. end)
  281. </code></pre>
  282. <p>另外,我们也可以通过这个方式,改进查找 homebrew/pacman 等其他包管理器安装的包,例如:<code>add_extsources("pacman::libusb-1.0")</code>。</p>
  283. <h3 id="packageadd_deps">package:add_deps</h3>
  284. <h4 id="">添加包依赖</h4>
  285. <p>添加包依赖接口,通过配置包之间的依赖关系,我们能够在安装包的同时,自动安装它的所有依赖包。</p>
  286. <p>另外,默认情况下,我们只要配置了依赖关系,cmake/autoconf 就能够自动找到所有依赖包的库和头文件。</p>
  287. <p>当然,如果由于一些特殊原因,导致当前包的 cmake 脚本没能够正常找到依赖包,那么我们也可以通过 <code>{packagedeps = "xxx"}</code> 来强行打入依赖包信息。</p>
  288. <p>例如:</p>
  289. <pre><code class="lang-lua">package("foo")
  290. add_deps("cmake", "bar")
  291. on_install(function (package)
  292. local configs = {}
  293. import("package.tools.cmake").install(package, configs)
  294. end)
  295. </code></pre>
  296. <p>foo 包是使用 CMakeLists.txt 维护的,它在安装过程中,依赖 bar 包,因此,xmake 会优先安装 bar,并且让 cmake.install 在调用 cmake 时候,自动找到 bar 安装后的库。</p>
  297. <p>但是,如果 foo 的 CMakeLists.txt 还是无法自动找到 bar,那么我们可以修改成下面的配置,强制将 bar 的 includedirs/links 等信息通过 flags 的方式,传入 foo。</p>
  298. <pre><code class="lang-lua">package("foo")
  299. add_deps("cmake", "bar")
  300. on_install(function (package)
  301. local configs = {}
  302. import("package.tools.cmake").install(package, configs, {packages = "bar"})
  303. end)
  304. </code></pre>
  305. <h3 id="packageadd_components">package:add_components</h3>
  306. <h4 id="">添加包组件</h4>
  307. <p>这是 2.7.3 新加的接口,用于支持包的组件化配置,详情见:<a href="https://github.com/xmake-io/xmake/issues/2636">#2636</a>。</p>
  308. <p>通过这个接口,我们可以配置当前包实际可以提供的组件列表。</p>
  309. <pre><code class="lang-lua">package("sfml")
  310. add_components("graphics")
  311. add_components("audio", "network", "window")
  312. add_components("system")
  313. </code></pre>
  314. <p>在用户端,我们可以通过下面的方式来使用包的特定组件。</p>
  315. <pre><code class="lang-lua">add_requires("sfml")
  316. target("test")
  317. add_packages("sfml", {components = "graphics")
  318. </code></pre>
  319. <p>!> 注:除了配置可用的组件列表,我们还需要对每个组件进行详细配置,才能正常使用,因此,它通常和 <code>on_component</code> 接口配合使用。</p>
  320. <p>一个关于包组件的配置和使用的完整例子见:<a href="https://github.com/xmake-io/xmake/blob/master/tests/projects/package/components/xmake.lua">components example</a></p>
  321. <h3 id="packageset_base">package:set_base</h3>
  322. <h4 id="">继承包配置</h4>
  323. <p>这是 2.6.4 新加的接口,我们可以通过它去继承一个已有的包的全部配置,然后在此基础上重写部分配置。</p>
  324. <p>这通常在用户自己的项目中,修改 xmake-repo 官方仓库的内置包比较有用,比如:修复改 urls,修改版本列表,安装逻辑等等。</p>
  325. <p>例如,修改内置 zlib 包的 url,切到自己的 zlib 源码地址。</p>
  326. <pre><code class="lang-lua">package("myzlib")
  327. set_base("zlib")
  328. set_urls("https://github.com/madler/zlib.git")
  329. package_end()
  330. add_requires("myzlib", {system = false, alias = "zlib"})
  331. target("test")
  332. set_kind("binary")
  333. add_files("src/*.c")
  334. add_packages("zlib")
  335. </code></pre>
  336. <p>我们也可以用来单纯添加一个别名包。</p>
  337. <pre><code class="lang-lua">package("onetbb")
  338. set_base("tbb")
  339. </code></pre>
  340. <p>我们可以通过 <code>add_requires("onetbb")</code> 集成安装 tbb 包,只是包名不同而已。</p>
  341. <h3 id="packageon_load">package:on_load</h3>
  342. <h4 id="">加载包配置</h4>
  343. <p>这是个可选的接口,如果要更加灵活的动态判断各种平台架构,针对性做设置,可以在这个里面完成,例如:</p>
  344. <pre><code class="lang-lua">on_load(function (package)
  345. local bitwidth = package:config("bitwidth") or "8"
  346. package:add("links", "pcre" .. (bitwidth ~= "8" and bitwidth or ""))
  347. if not package:config("shared") then
  348. package:add("defines", "PCRE_STATIC")
  349. end
  350. end)
  351. </code></pre>
  352. <p>pcre包需要做一些针对bitwidth的判断,才能确定对外输出的链接库名字,还需要针对动态库增加一些defines导出,这个时候在on_load里面设置,就更加灵活了。</p>
  353. <h3 id="packageon_fetch">package:on_fetch</h3>
  354. <h4 id="">从系统中查找库</h4>
  355. <p>这是个可选配置,2.5.2 之后,如果不同系统下安装的系统库,仅仅只是包名不同,那么使用 <code>add_extsources</code> 改进系统库查找已经足够,简单方便。</p>
  356. <p>但是如果有些安装到系统的包,位置更加复杂,想要找到它们,也许需要一些额外的脚本才能实现,例如:windows 下注册表的访问去查找包等等,这个时候,我们就可以通过 <code>on_fetch</code> 完全定制化查找系统库逻辑。</p>
  357. <p>还是以 libusb 为例,我们不用 <code>add_extsources</code>,可以使用下面的方式,实现相同的效果,当然,我们可以在里面做更多的事情。</p>
  358. <pre><code>package("libusb")
  359. on_fetch("linux", function(package, opt)
  360. if opt.system then
  361. return find_package("pkgconfig::libusb-1.0")
  362. end
  363. end)
  364. </code></pre><h3 id="packageon_check">package:on_check</h3>
  365. <h4 id="">检测包是否支持当前平台</h4>
  366. <p>有时候,单纯用 <code>on_install("windows", "android", function () end)</code> 无法很好的限制包对当前平台的支持力度。</p>
  367. <p>例如,同样都是在 windows 上使用 msvc 编译,但是它仅仅只支持使用 vs2022 工具链。那么我们无法简单的去通过禁用 windows 平台,来限制包的安装。</p>
  368. <p>因为每个用户的编译工具链环境都可能是不同的。这个时候,我们可以通过配置 <code>on_check</code> 去做更细致的检测,来判断包是否支持当前的工具链环境。</p>
  369. <p>如果包不被支持,那么它会在包被下载安装前,更早的提示用户,也可以在 xmake-repo 的 ci 上,规避掉一些不支持的 ci job 测试。</p>
  370. <p>例如,下面的配置,就可以判断当前的 msvc 是否提供了对应的 vs sdk 版本,如果版本不满足,那么这个包就无法被编译安装,用户会看到更加可读的不支持的错误提示。</p>
  371. <pre><code class="lang-lua">package("test")
  372. on_check("windows", function (package)
  373. import("core.tool.toolchain")
  374. import("core.base.semver")
  375. local msvc = toolchain.load("msvc", {plat = package:plat(), arch = package:arch()})
  376. if msvc then
  377. local vs_sdkver = msvc:config("vs_sdkver")
  378. assert(vs_sdkver and semver.match(vs_sdkver):gt("10.0.19041"), "package(cglm): need vs_sdkver > 10.0.19041.0")
  379. end
  380. end)
  381. </code></pre>
  382. <p>例如,我们也可以用它来判断,当前编译器对 c++20 的支持力度,如果不支持 c++20 才有的 std::input_iterator。那么这个包就没必要继续下载编译安装。</p>
  383. <p>用户会看到 <code>Require at least C++20.</code> 的错误,来提示用户取升级自己的编译器。</p>
  384. <pre><code class="lang-lua">package("test")
  385. on_check(function (package)
  386. assert(package:check_cxxsnippets({test = [[
  387. #include <cstddef>
  388. #include <iterator>
  389. struct SimpleInputIterator {
  390. using difference_type = std::ptrdiff_t;
  391. using value_type = int;
  392. int operator*() const;
  393. SimpleInputIterator&amp; operator++();
  394. void operator++(int) { ++*this; }
  395. };
  396. static_assert(std::input_iterator<SimpleInputIterator>);
  397. ]]}, {configs = {languages = "c++20"}}), "Require at least C++20.")
  398. end)
  399. </code></pre>
  400. <h3 id="packageon_install">package:on_install</h3>
  401. <h4 id="">安装包</h4>
  402. <p>这个接口主要用于添加安装脚本,前面的字符串参数用于设置支持的平台,像<code>on_load</code>, <code>on_test</code>等其他脚本域也是同样支持的。</p>
  403. <h5 id="">平台过滤</h5>
  404. <p>完整的过滤语法如下:<code>plat|arch1,arch2@host|arch1,arch2</code></p>
  405. <p>看上去非常的复杂,其实很简单,其中每个阶段都是可选的,可部分省略,对应:<code>编译平台|编译架构@主机平台|主机架构</code></p>
  406. <p>如果不设置任何平台过滤条件,那么默认全平台支持,里面的脚本对所有平台生效,例如:</p>
  407. <pre><code class="lang-lua">on_install(function (package)
  408. -- TODO
  409. end)
  410. </code></pre>
  411. <p>如果安装脚本对特定平台生效,那么直接指定对应的编译平台,可以同时指定多个:</p>
  412. <pre><code class="lang-lua">on_install("linux", "macosx", function (package)
  413. -- TODO
  414. end)
  415. </code></pre>
  416. <p>如果还要细分到指定架构才能生效,可以这么写:</p>
  417. <pre><code class="lang-lua">on_install("linux|x86_64", "iphoneos|arm64", function (package)
  418. -- TODO
  419. end)
  420. </code></pre>
  421. <p>如果还要限制执行的主机环境平台和架构,可以在后面追加<code>@host|arch</code>,例如:</p>
  422. <pre><code class="lang-lua">on_install("mingw@windows", function (package)
  423. -- TODO
  424. end)
  425. </code></pre>
  426. <p>意思就是仅对windows下编译mingw平台生效。</p>
  427. <p>我们也可以不指定比那一平台和架构,仅设置主机平台和架构,这通常用于描述一些跟编译工具相关的依赖包,只能在主机环境运行。</p>
  428. <p>例如,我们编译的包,依赖了cmake,需要添加cmake的包描述,那么里面编译安装环境,只能是主机平台:</p>
  429. <pre><code class="lang-lua">on_install("@windows", "@linux", "@macosx", function (package)
  430. -- TODO
  431. end)
  432. </code></pre>
  433. <p>其他一些例子:</p>
  434. <pre><code class="lang-lua">-- `@linux`
  435. -- `@linux|x86_64`
  436. -- `@macosx,linux`
  437. -- `android@macosx,linux`
  438. -- `android|armeabi-v7a@macosx,linux`
  439. -- `android|armeabi-v7a@macosx,linux|x86_64`
  440. -- `android|armeabi-v7a@linux|x86_64`
  441. </code></pre>
  442. <p>在 2.8.7 中,我们改进了模式匹配支持,新增排除指定平台和架构,例如:</p>
  443. <pre><code>!plat|!arch@!subhost|!subarch
  444. </code></pre><pre><code class="lang-lua">@!linux
  445. @!linux|x86_64
  446. @!macosx,!linux
  447. !android@macosx,!linux
  448. android|!armeabi-v7a@macosx,!linux
  449. android|armeabi-v7a,!iphoneos@macosx,!linux|x86_64
  450. !android|armeabi-v7a@!linux|!x86_64
  451. !linux|*
  452. </code></pre>
  453. <p>同时,还提供了一个内置的 <code>native</code> 架构,用于匹配当前平台的本地架构,主要用于指定或者排除交叉编译平台。</p>
  454. <pre><code class="lang-lua">on_install("macosx|native", ...)
  455. </code></pre>
  456. <p>上面的配置,如果在 macOS x86_64 的设备上,它仅仅只会匹配 <code>xmake f -a x86_64</code> 的本地架构编译。</p>
  457. <p>如果是 <code>xmake f -a arm64</code> 交叉编译,就不会被匹配到。</p>
  458. <p>同理,如果只想匹配交叉编译,可以使用 <code>macosx|!native</code> 进行取反排除就行了。</p>
  459. <p>2.9.1 版本,我们继续对它做了改进,增加了条件逻辑判断的支持:</p>
  460. <p>例如:</p>
  461. <pre><code class="lang-lua">on_install("!wasm|!arm* and !cross|!arm*", function (package)
  462. end)
  463. </code></pre>
  464. <p>来表述排除 wasm 和 cross 平台之外的 arm 架构。</p>
  465. <p>并且,它也支持通过 <code>()</code> 描述的嵌套逻辑,<code>a and b or (a and (c or d))</code>。</p>
  466. <h5 id="">编译工具</h5>
  467. <p>我们内置了一些安装常用编译工具脚本,用于针对不同源码依赖的构建工具链,进行方便的构架支持,例如:autoconf, cmake, meson等,</p>
  468. <h6 id="xmake">xmake</h6>
  469. <p>如果是基于xmake的依赖包,那么集成起来就非常简单了,xmake对其做了非常好的内置集成支持,可以直接对其进行跨平台编译支持,一般情况下只需要:</p>
  470. <pre><code class="lang-lua">on_install(function (package)
  471. import("package.tools.xmake").install(package)
  472. end)
  473. </code></pre>
  474. <p>如果要传递一些特有的编译配置参数:</p>
  475. <pre><code class="lang-lua">on_install(function (package)
  476. import("package.tools.xmake").install(package, {"--xxx=y"})
  477. end)
  478. </code></pre>
  479. <h6 id="cmake">cmake</h6>
  480. <p>如果是基于cmake的包,集成起来也很简答,通常也只需要设置一些配置参数即可,不过还需要先添加上cmake的依赖才行:</p>
  481. <pre><code class="lang-lua">add_deps("cmake")
  482. on_install(function (package)
  483. import("package.tools.cmake").install(package, {"-Dxxx=ON"})
  484. end)
  485. </code></pre>
  486. <h6 id="autoconf">autoconf</h6>
  487. <p>如果是基于autoconf的包,集成方式跟cmake类似,只是传递的配置参数不同而已,不过通常情况下,unix系统都内置了autoconf系列工具,所以不加相关依赖也没事。</p>
  488. <pre><code class="lang-lua">on_install(function (package)
  489. import("package.tools.autoconf").install(package, {"--enable-shared=no"})
  490. end)
  491. </code></pre>
  492. <p>不过,有些源码包用系统内置的autoconf可能不能完全满足,那么可以加上autoconf系列依赖,对其进行构建:</p>
  493. <pre><code class="lang-lua">add_deps("autoconf", "automake", "libtool", "pkg-config")
  494. on_install(function (package)
  495. import("package.tools.autoconf").install(package, {"--enable-shared=no"})
  496. end)
  497. </code></pre>
  498. <h6 id="meson">meson</h6>
  499. <p>如果是meson,还需要加上ninja的依赖来执行构建才行。</p>
  500. <pre><code class="lang-lua">add_deps("meson", "ninja")
  501. on_install(function (package)
  502. import("package.tools.meson").install(package, {"-Dxxx=ON"})
  503. end)
  504. </code></pre>
  505. <h3 id="packageon_test">package:on_test</h3>
  506. <h4 id="">测试包</h4>
  507. <p>安装后,需要设置对应的测试脚本,执行一些测试,确保安装包的可靠性,如果测试不通过,则会撤销整个安装包。</p>
  508. <pre><code class="lang-lua">on_test(function (package)
  509. assert(package:has_cfuncs("inflate", {includes = "zlib.h"}))
  510. end)
  511. </code></pre>
  512. <p>上面的脚本调用包内置的<code>has_cfuncs</code>接口,检测安装后的包是否存在zlib.h头文件,以及库和头文件里面是否存在<code>inflate</code>这个接口函数。</p>
  513. <p>xmake会去尝试编译链接来做测试,<code>has_cfuncs</code>用于检测c函数,而<code>has_cxxfuncs</code>则可以检测c++库函数。</p>
  514. <p>而includes里面可以设置多个头文件,例如:<code>includes = {"xxx.h", "yyy.h"}</code></p>
  515. <p>我们还可以传递一些自己的编译参数进去检测,例如:</p>
  516. <pre><code class="lang-lua">on_test(function (package)
  517. assert(package:has_cxxfuncs("func1", {includes = "xxx.h", configs = {defines = "c++14", cxflags = "-Dxxx"}}))
  518. end)
  519. </code></pre>
  520. <p>我们也可以通过<code>check_csnippets</code>和<code>check_cxxsnippets</code>检测一个代码片段:</p>
  521. <pre><code class="lang-lua">on_test(function (package)
  522. assert(package:check_cxxsnippets({test = [[
  523. #include <boost/algorithm/string.hpp>
  524. #include <string>
  525. #include <vector>
  526. #include <assert.h>
  527. using namespace boost::algorithm;
  528. using namespace std;
  529. static void test() {
  530. string str("a,b");
  531. vector<string> strVec;
  532. split(strVec, str, is_any_of(","));
  533. assert(strVec.size()==2);
  534. assert(strVec[0]=="a");
  535. assert(strVec[1]=="b");
  536. }
  537. ]]}, {configs = {languages = "c++14"}}))
  538. end)
  539. </code></pre>
  540. <p>如果是可执行包,也可以通过尝试运行来检测:</p>
  541. <pre><code class="lang-lua">on_test(function (package)
  542. os.run("xxx --help")
  543. end)
  544. </code></pre>
  545. <p>如果运行失败,那么测试不会通过。</p>
  546. <h3 id="packageon_download">package:on_download</h3>
  547. <h4 id="">自定义下载包</h4>
  548. <p>自定义包的下载逻辑,这是 2.6.4 新加的接口,通常用不到,使用 Xmake 的内置下载就足够了。</p>
  549. <p>如果用户自建私有仓库,对包的下载有更复杂的鉴权机制,特殊处理逻辑,那么可以重写内部的下载逻辑来实现。</p>
  550. <pre><code class="lang-lua">on_download(function (package, opt)
  551. local url = opt.url
  552. local sourcedir = opt.sourcedir
  553. -- download url to the current directory
  554. -- and extract it&#39;s source code to sourcedir
  555. -- ...
  556. end)
  557. </code></pre>
  558. <p>opt 参数里面,可以获取到下载包的目的源码目录 <code>opt.sourcedir</code>,我们只需要从 <code>opt.url</code> 获取到包地址,下载下来就可以了。</p>
  559. <p>然后,根据需要,添加一些自定义的处理逻辑。另外,自己可以添加下载缓存处理等等。</p>
  560. <p>下面是一个自定义下载 tar.gz 文件,并且实现缓存和源文件目录解压的例子,可以参考下:</p>
  561. <pre><code class="lang-lua">package("zlib")
  562. add_urls("https://github.com/madler/zlib/archive/$(version).tar.gz")
  563. add_versions("v1.2.10", "42cd7b2bdaf1c4570e0877e61f2fdc0bce8019492431d054d3d86925e5058dc5")
  564. on_download(function (package, opt)
  565. import("net.http")
  566. import("utils.archive")
  567. local url = opt.url
  568. local sourcedir = opt.sourcedir
  569. local packagefile = path.filename(url)
  570. local sourcehash = package:sourcehash(opt.url_alias)
  571. local cached = true
  572. if not os.isfile(packagefile) or sourcehash ~= hash.sha256(packagefile) then
  573. cached = false
  574. -- attempt to remove package file first
  575. os.tryrm(packagefile)
  576. http.download(url, packagefile)
  577. -- check hash
  578. if sourcehash and sourcehash ~= hash.sha256(packagefile) then
  579. raise("unmatched checksum, current hash(%s) != original hash(%s)", hash.sha256(packagefile):sub(1, 8), sourcehash:sub(1, 8))
  580. end
  581. end
  582. -- extract package file
  583. local sourcedir_tmp = sourcedir .. ".tmp"
  584. os.rm(sourcedir_tmp)
  585. if archive.extract(packagefile, sourcedir_tmp) then
  586. os.rm(sourcedir)
  587. os.mv(sourcedir_tmp, sourcedir)
  588. else
  589. -- if it is not archive file, we need only create empty source file and use package:originfile()
  590. os.tryrm(sourcedir)
  591. os.mkdir(sourcedir)
  592. end
  593. -- save original file path
  594. package:originfile_set(path.absolute(packagefile))
  595. end)
  596. </code></pre>
  597. <p>自定义下载需要用户完全自己控制下载逻辑,会比较复杂,除非必要,不推荐这么做。</p>
  598. <p>如果仅仅只是想增加自定义 http headers 去获取下载授权,可以使用 <a href="https://xmake.io/#/zh-cn/manual/project_target?id=%e8%ae%be%e7%bd%ae%e5%8c%85%e4%b8%8b%e8%bd%bd%e7%9a%84-http-headers">设置包下载的 http headers</a></p>
  599. <h3 id="packageon_component">package:on_component</h3>
  600. <h4 id="">配置包组件</h4>
  601. <p>这是 2.7.3 新加的接口,用于支持包的组件化配置,详情见:<a href="https://github.com/xmake-io/xmake/issues/2636">#2636</a>。</p>
  602. <p>通过这个接口,我们可以配置当前包,指定组件的详细信息,比如组件的链接,依赖等等。</p>
  603. <h5 id="">配置组件链接信息</h5>
  604. <pre><code class="lang-lua">package("sfml")
  605. add_components("graphics")
  606. add_components("audio", "network", "window")
  607. add_components("system")
  608. on_component("graphics", function (package, component)
  609. local e = package:config("shared") and "" or "-s"
  610. component:add("links", "sfml-graphics" .. e)
  611. if package:is_plat("windows", "mingw") and not package:config("shared") then
  612. component:add("links", "freetype")
  613. component:add("syslinks", "opengl32", "gdi32", "user32", "advapi32")
  614. end
  615. end)
  616. on_component("window", function (package, component)
  617. local e = package:config("shared") and "" or "-s"
  618. component:add("links", "sfml-window" .. e)
  619. if package:is_plat("windows", "mingw") and not package:config("shared") then
  620. component:add("syslinks", "opengl32", "gdi32", "user32", "advapi32")
  621. end
  622. end)
  623. ...
  624. </code></pre>
  625. <p>在用户端,我们可以通过下面的方式来使用包的特定组件。</p>
  626. <pre><code class="lang-lua">add_requires("sfml")
  627. target("test")
  628. add_packages("sfml", {components = "graphics")
  629. </code></pre>
  630. <p>!> 注:除了配置组件信息,我们还需要配置可用的组件列表,才能正常使用,因此,它通常和 <code>add_components</code> 接口配合使用。</p>
  631. <p>一个关于包组件的配置和使用的完整例子见:<a href="https://github.com/xmake-io/xmake/blob/master/tests/projects/package/components/xmake.lua">components example</a></p>
  632. <h5 id="">配置组件的编译信息</h5>
  633. <p>我们不仅可以配置每个组件的链接信息,还有 includedirs, defines 等等编译信息,我们也可以对每个组件单独配置。</p>
  634. <pre><code class="lang-lua">package("sfml")
  635. on_component("graphics", function (package, component)
  636. package:add("defines", "TEST")
  637. end)
  638. </code></pre>
  639. <h5 id="">配置组件依赖</h5>
  640. <pre><code class="lang-lua">package("sfml")
  641. add_components("graphics")
  642. add_components("audio", "network", "window")
  643. add_components("system")
  644. on_component("graphics", function (package, component)
  645. component:add("deps", "window", "system")
  646. end)
  647. </code></pre>
  648. <p>上面的配置,告诉包,我们的 graphics 组件还会额外依赖 <code>window</code> 和 <code>system</code> 两个组件。</p>
  649. <p>因此,在用户端,我们对 graphics 的组件使用,可以从</p>
  650. <pre><code class="lang-lua"> add_packages("sfml", {components = {"graphics", "window", "system"})
  651. </code></pre>
  652. <p>简化为:</p>
  653. <pre><code class="lang-lua"> add_packages("sfml", {components = "graphics")
  654. </code></pre>
  655. <p>因为,只要我们开启了 graphics 组件,它也会自动启用依赖的 window 和 system 组件。</p>
  656. <p>另外,我们也可以通过 <code>add_components("graphics", {deps = {"window", "system"}})</code> 来配置组件依赖关系。</p>
  657. <h5 id="">从系统库中查找组件</h5>
  658. <p>我们知道,在包配置中,配置 <code>add_extsources</code> 可以改进包在系统中的查找,比如从 apt/pacman 等系统包管理器中找库。</p>
  659. <p>当然,我们也可以让每个组件也能通过 <code>extsources</code> 配置,去优先从系统库中找到它们。</p>
  660. <p>例如,sfml 包,它在 homebrew 中其实也是组件化的,我们完全可以让包从系统库中,找到对应的每个组件,而不需要每次源码安装它们。</p>
  661. <pre><code class="lang-bash">$ ls -l /usr/local/opt/sfml/lib/pkgconfig
  662. -r--r--r-- 1 ruki admin 317 10 19 17:52 sfml-all.pc
  663. -r--r--r-- 1 ruki admin 534 10 19 17:52 sfml-audio.pc
  664. -r--r--r-- 1 ruki admin 609 10 19 17:52 sfml-graphics.pc
  665. -r--r--r-- 1 ruki admin 327 10 19 17:52 sfml-network.pc
  666. -r--r--r-- 1 ruki admin 302 10 19 17:52 sfml-system.pc
  667. -r--r--r-- 1 ruki admin 562 10 19 17:52 sfml-window.pc
  668. </code></pre>
  669. <p>我们只需要,对每个组件配置它的 extsources:</p>
  670. <pre><code class="lang-lua"> if is_plat("macosx") then
  671. add_extsources("brew::sfml/sfml-all")
  672. end
  673. on_component("graphics", function (package, component)
  674. -- ...
  675. component:add("extsources", "brew::sfml/sfml-graphics")
  676. end)
  677. </code></pre>
  678. <h5 id="">默认的全局组件配置</h5>
  679. <p>除了通过指定组件名的方式,配置特定组件,如果我们没有指定组件名,默认就是全局配置所有组件。</p>
  680. <pre><code class="lang-lua">package("sfml")
  681. on_component(function (package, component)
  682. -- configure all components
  683. end)
  684. </code></pre>
  685. <p>当然,我们也可以通过下面的方式,指定配置 graphics 组件,剩下的组件通过默认的全局配置接口进行配置:</p>
  686. <pre><code class="lang-lua">package("sfml")
  687. add_components("graphics")
  688. add_components("audio", "network", "window")
  689. add_components("system")
  690. on_component("graphics", function (package, component)
  691. -- configure graphics
  692. end)
  693. on_component(function (package, component)
  694. -- component audio, network, window, system
  695. end)
  696. </code></pre>
  697. </article>
  698. </body>
  699. </html>