From 9078c01303c065fbf6e6cefbddcb462adede7bfb Mon Sep 17 00:00:00 2001
From: ruki
+
+ 如果你想了解更多参数选项,请运行: `xmake create --help` +
+ +#### 构建工程 + +```bash +$ xmake +``` + +#### 运行程序 + +```bash +$ xmake run hello +``` + +#### 调试程序 + +```bash +$ xmake run -d hello +``` + +xmake将会使用系统自带的调试器去加载程序运行,目前支持:lldb, gdb, windbg, vsjitdebugger, ollydbg 等各种调试器。 + +```bash +[lldb]$target create "build/hello" +Current executable set to 'build/hello' (x86_64). +[lldb]$b main +Breakpoint 1: where = hello`main, address = 0x0000000100000f50 +[lldb]$r +Process 7509 launched: '/private/tmp/hello/build/hello' (x86_64) +Process 7509 stopped +* thread #1: tid = 0x435a2, 0x0000000100000f50 hello`main, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 + frame #0: 0x0000000100000f50 hello`main +hello`main: +-> 0x100000f50 <+0>: pushq %rbp + 0x100000f51 <+1>: movq %rsp, %rbp + 0x100000f54 <+4>: leaq 0x2b(%rip), %rdi ; "hello world!" + 0x100000f5b <+11>: callq 0x100000f64 ; symbol stub for: puts +[lldb]$ +``` + ++ 你也可以使用简写的命令行选项,例如: `xmake r` 或者 `xmake run` +
+ +## 配置 + +通过`xmake f|config`配置命令,设置构建前的相关配置信息,详细参数选项,请运行: `xmake f --help`。 + +
+ 你可以使用命令行缩写来简化输入,也可以使用全名,例如:
+ `xmake f` 或者 `xmake config`.
+ `xmake f -p linux` 或者 `xmake config --plat=linux`.
+
+ xmake将会自动探测当前主机平台,默认自动生成对应的目标程序。 +
+ +##### Linux + +```bash +$ xmake f -p linux [-a i386|x86_64] +$ xmake +``` + +##### Android + +```bash +$ xmake f -p android --ndk=~/files/android-ndk-r10e/ [-a armv5te|armv6|armv7-a|armv8-a|arm64-v8a] +$ xmake +``` + +如果要手动指定ndk中具体某个工具链,而不是使用默认检测的配置,可以通过[--toolchains](#-toolchains)来设置,例如: + +```bash +$ xmake f -p android --ndk=~/files/android-ndk-r10e/ -a arm64-v8a --toolchains=~/files/android-ndk-r10e/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin +``` + +[--toolchains](#-toolchains)主要用于设置选择编译工具的具体bin目录,这个的使用跟[交叉编译](#交叉编译)中的[--toolchains](#-toolchains)的行为是一致的。 + ++如果手动设置了`toolchains`的bin目录,没有通过检测,可以看下是否`--arch=`参数没有匹配对。 +
+ +##### iPhoneOS + +```bash +$ xmake f -p iphoneos [-a armv7|armv7s|arm64|i386|x86_64] +$ xmake +``` + +##### Windows + +```bash +$ xmake f -p windows [-a x86|x64] +$ xmake +``` + +##### Mingw + +```bash +$ xmake f -p mingw --sdk=/usr/local/i386-mingw32-4.3.0/ [-a i386|x86_64] +$ xmake +``` + +##### Apple WatchOS + +```bash +$ xmake f -p watchos [-a i386|armv7k] +$ xmake +``` + +##### 交叉编译 + +linux平台的交叉编译: + +```bash +$ xmake f -p linux --sdk=/usr/local/arm-linux-gcc/ [--toolchains=/sdk/bin] [--cross=arm-linux-] +$ xmake +``` + +其他平台的交叉编译: + +```bash +$ xmake f -p cross --sdk=/usr/local/arm-xxx-gcc/ [--toolchains=/sdk/bin] [--cross=arm-linux-] +$ xmake +``` + +如果不关心实际的平台名,只想交叉编译,可以直接用上面的命令,如果需要通过`is_plat("myplat")`判断自己的平台逻辑,则: + +```bash +$ xmake f -p myplat --sdk=/usr/local/arm-xxx-gcc/ [--toolchains=/sdk/bin] [--cross=arm-linux-] +$ xmake +``` + +其中: + +| 参数名 | 描述 | +| ---------------------------- | -------------------------------- | +| [--sdk](#-sdk) | 设置交叉工具链的sdk根目录 | +| [--toolchains](#-toolchains) | 设置工具链bin目录 | +| [--cross](#-cross) | 设置交叉工具链工具前缀 | +| [--as](#-as) | 设置`asm`汇编器 | +| [--cc](#-cc) | 设置`c`编译器 | +| [--cxx](#-cxx) | 设置`c++`编译器 | +| [--mm](#-mm) | 设置`objc`编译器 | +| [--mxx](#-mxx) | 设置`objc++`编译器 | +| [--sc](#-sc) | 设置`swift`编译器 | +| [--gc](#-gc) | 设置`golang`编译器 | +| [--dc](#-dc) | 设置`dlang`编译器 | +| [--rc](#-rc) | 设置`rust`编译器 | +| [--ld](#-ld) | 设置`c/c++/objc/asm`链接器 | +| [--sh](#-sh) | 设置`c/c++/objc/asm`共享库链接器 | +| [--ar](#-ar) | 设置`c/c++/objc/asm`静态库归档器 | +| [--sc-ld](#-sc-ld) | 设置`swift`链接器 | +| [--sc-sh](#-sc-sh) | 设置`swift`共享库链接器 | +| [--gc-ld](#-gc-ld) | 设置`golang`链接器 | +| [--gc-ar](#-gc-ar) | 设置`golang`静态库归档器 | +| [--dc-ld](#-dc-ld) | 设置`dlang`链接器 | +| [--dc-sh](#-dc-sh) | 设置`dlang`共享库链接器 | +| [--dc-ar](#-dc-ar) | 设置`dlang`静态库归档器 | +| [--rc-ld](#-rc-ld) | 设置`rust`链接器 | +| [--rc-sh](#-rc-sh) | 设置`rust`共享库链接器 | +| [--rc-ar](#-rc-ar) | 设置`rust`静态库归档器 | +| [--asflags](#-asflags) | 设置`asm`汇编编译选项 | +| [--cflags](#-cflags) | 设置`c`编译选项 | +| [--cxflags](#-cxflags) | 设置`c/c++`编译选项 | +| [--cxxflags](#-cxxflags) | 设置`c++`编译选项 | +| [--mflags](#-mflags) | 设置`objc`编译选项 | +| [--mxflags](#-mxflags) | 设置`objc/c++`编译选项 | +| [--mxxflags](#-mxxflags) | 设置`objc++`编译选项 | +| [--scflags](#-scflags) | 设置`swift`编译选项 | +| [--gcflags](#-gcflags) | 设置`golang`编译选项 | +| [--dcflags](#-dcflags) | 设置`dlang`编译选项 | +| [--rcflags](#-rcflags) | 设置`rust`编译选项 | +| [--ldflags](#-ldflags) | 设置链接选项 | +| [--shflags](#-shflags) | 设置共享库链接选项 | +| [--arflags](#-arflags) | 设置静态库归档选项 | + ++如果你想要了解更多参数选项,请运行: `xmake f --help`。 +
+ +###### --sdk + +- 设置交叉工具链的sdk根目录 + +大部分情况下,都不需要配置很复杂的toolchains前缀,例如:`arm-linux-` 什么的 + +只要这个工具链的sdk目录满足如下结构(大部分的交叉工具链都是这个结构): + +``` +/home/toolchains_sdkdir + - bin + - arm-linux-gcc + - arm-linux-ld + - ... + - lib + - libxxx.a + - include + - xxx.h +``` + +那么,使用xmake进行交叉编译的时候,只需要进行如下配置和编译: + +```bash +$ xmake f -p linux --sdk=/home/toolchains_sdkdir +$ xmake +``` + +这个时候,xmake会去自动探测,gcc等编译器的前缀名:`arm-linux-`,并且编译的时候,也会自动加上`链接库`和`头文件`的搜索选项,例如: + +``` +-I/home/toolchains_sdkdir/include -L/home/toolchains_sdkdir/lib +``` + +这些都是xmake自动处理的,不需要手动配置他们。。 + +###### --toolchains + +- 设置工具链bin目录 + +对于不规则工具链目录结构,靠单纯地[--sdk](#-sdk)选项设置,没法完全检测通过的情况下,可以通过这个选项继续附加设置工具链的bin目录位置。 + +例如:一些特殊的交叉工具链的,编译器bin目录,并不在 `/home/toolchains_sdkdir/bin` 这个位置,而是独立到了 `/usr/opt/bin` + +```bash +$ xmake f -p linux --sdk=/home/toolchains_sdkdir --toolchains=/usr/opt/bin +$ xmake +``` + +###### --cross + +- 设置交叉工具链工具前缀 + +像`aarch64-linux-android-`这种,通常如果你配置了[--sdk](-sdk)或者[--toolchains](-toolchains)的情况下,xmake会去自动检测的,不需要自己手动设置。 + +但是对于一些极特殊的工具链,一个目录下同时有多个cross前缀的工具bin混在一起的情况,你需要手动设置这个配置,来区分到底需要选用哪个bin。 + +例如,toolchains的bin目录下同时存在两个不同的编译器: + +``` +/opt/bin + - armv7-linux-gcc + - aarch64-linux-gcc +``` + +我们现在想要选用armv7的版本,则配置如下: + +```bash +$ xmake f -p linux --sdk=/usr/toolsdk --toolchains=/opt/bin --cross=armv7-linux- +``` + +###### --as + +- 设置`asm`汇编器 + +如果还要继续细分选择编译器,则继续追加相关编译器选项,例如: + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --as=armv7-linux-as +``` + +如果存在`AS`环境变量的话,会优先使用当前环境变量中指定的值。 + +###### --cc + +- 设置c编译器 + +如果还要继续细分选择编译器,则继续追加相关编译器选项,例如: + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --cc=armv7-linux-clang +``` + +如果存在`CC`环境变量的话,会优先使用当前环境变量中指定的值。 + +###### --cxx + +- 设置`c++`编译器 + +如果还要继续细分选择编译器,则继续追加相关编译器选项,例如: + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --cxx=armv7-linux-clang++ +``` + +如果存在`CXX`环境变量的话,会优先使用当前环境变量中指定的值。 + +###### --ld + +- 设置`c/c++/objc/asm`链接器 + +如果还要继续细分选择链接器,则继续追加相关编译器选项,例如: + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --ld=armv7-linux-clang++ +``` + +如果存在`LD`环境变量的话,会优先使用当前环境变量中指定的值。 + +###### --sh + +- 设置`c/c++/objc/asm`共享库链接器 + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --sh=armv7-linux-clang++ +``` + +如果存在`SH`环境变量的话,会优先使用当前环境变量中指定的值。 + +###### --ar + +- 设置`c/c++/objc/asm`静态库归档器 + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --ar=armv7-linux-ar +``` + +如果存在`AR`环境变量的话,会优先使用当前环境变量中指定的值。 + +#### 全局配置 + +我们也可以将一些常用配置保存到全局配置中,来简化频繁地输入: + +例如: + +```bash +$ xmake g --ndk=~/files/android-ndk-r10e/ +``` + +现在,我们重新配置和编译`android`程序: + +```bash +$ xmake f -p android +$ xmake +``` + +以后,就不需要每次重复配置`--ndk=`参数了。 + +
+ 每个命令都有其简写,例如: `xmake g` 或者 `xmake global`.
+
+当然,对于[is_mode](#is_mode)接口,`set_modes`不是必须的,就算不设置,也是可以通过`is_mode`正常判断当前的编译模式。 +
+ +##### set_project + +###### 设置工程名 + +设置工程名,在doxygen自动文档生成插件、工程文件生成插件中会用到,一般设置在xmake.lua的最开头,当然放在其他地方也是可以的 + +```lua +-- 设置工程名 +set_project("tbox") + +-- 设置工程版本 +set_version("1.5.1") +``` + +##### set_version + +###### 设置工程版本 + +设置项目版本,可以放在xmake.lua任何地方,一般放在最开头,例如: + +```lua +set_version("1.5.1") +``` + +以tbox为例,如果调用[set_config_header](#targetset_config_header)设置了`config.h`,那么会自动生成如下宏: + +```c +// version +#define TB_CONFIG_VERSION "1.5.1" +#define TB_CONFIG_VERSION_MAJOR 1 +#define TB_CONFIG_VERSION_MINOR 5 +#define TB_CONFIG_VERSION_ALTER 1 +#define TB_CONFIG_VERSION_BUILD 201510220917 +``` + +2.1.7版本支持buildversion的配置: + +```lua +set_version("1.5.1", {build = "%Y%m%d%H%M"}) +``` + +##### set_xmakever + +###### 设置最小xmake版本 + +用于处理xmake版本兼容性问题,如果项目的`xmake.lua`,通过这个接口设置了最小xmake版本支持,那么用户环境装的xmake低于要求的版本,就会提示错误。 + +一般情况下,建议默认对其进行设置,这样对用户比较友好,如果`xmake.lua`中用到了高版本的api接口,用户那边至少可以知道是否因为版本不对导致的构建失败。 + +设置如下: + +```lua +-- 设置最小版本为:2.1.0,低于此版本的xmake编译此工程将会提示版本错误信息 +set_xmakever("2.1.0") +``` + +##### add_subdirs + +###### 添加子工程目录 + +每个子工程对应一个`xmake.lua`的工程描述文件。 + +虽然一个`xmake.lua`也可以描述多个子工程模块,但是如果工程越来越大,越来越复杂,适当的模块化是很有必要的。。 + +这就需要`add_subdirs`了,将每个子模块放到不同目录中,并为其建立一个新的`xmake.lua`独立去维护它,例如: + +``` +./tbox +├── src +│ ├── demo +│ │ └── xmake.lua (用来描述测试模块) +│ └── tbox +│ └── xmake.lua(用来描述libtbox库模块) +└── xmake.lua(用该描述通用配置信息,以及对子模块的维护) +```` + +在`tbox/xmake.lua`中通过`add_subdirs`将拥有`xmale.lua`的子模块的目录,添加进来,就可以了,例如: + +```lua +-- 添加libtbox库模块目录 +add_subdirs("src/tbox") + +-- 如果xmake f --demo=y,启用了demo模块,那么包含demo目录 +if is_option("demo") then + add_subdirs("src/demo") +end +``` + +默认情况下,xmake会去编译在所有xmake.lua中描述的所有target目标,如果只想编译指定目标,可以执行: + +```bash +# 仅仅编译tbox库模块 +$ xmake build tbox +``` + +需要注意的是,每个子`xmake.lua`中所有的路径设置都是相对于当前这个子`xmake.lua`所在的目录的,都是相对路径,这样方便维护 + +##### add_subfiles + +###### 添加子工程文件 + +`add_subfiles`的作用与[add_subdirs](#add_subdirs)类似,唯一的区别就是:这个接口直接指定`xmake.lua`文件所在的路径,而不是目录,例如: + +```lua +add_subfiles("src/tbox/xmake.lua") +``` + +##### add_moduledirs + +###### 添加模块目录 + +xmake内置的扩展模块都在`xmake/modules`目录下,可通过[import](#import)来导入他们,如果自己在工程里面实现了一些扩展模块, +可以放置在这个接口指定的目录下,import也就会能找到,并且优先进行导入。 + +例如定义一个`find_openssl.lua`的扩展模块,用于扩展内置的[lib.detect.find_package](#detect-find_package)接口,则只需要将它放置在: + +``` +projectdir/xmake/modules/detect/packages/find_openssl.lua +``` + +然后在工程`xmake.lua`下指定这个模块目录,`find_package`就可以自动找到了: + +```lua +add_moduledirs("projectdir/xmake/modules") +``` + +##### add_plugindirs + +###### 添加插件目录 + +xmake内置的插件都是放在`xmake/plugins`目录下,但是对于用户自定义的一些特定工程的插件,如果不想放置在xmake安装目录下,那么可以在`xmake.lua`中进行配置指定的其他插件路径。 + +```lua +-- 将当前工程下的plugins目录设置为自定义插件目录 +add_plugindirs("$(projectdir)/plugins") +``` + +这样,xmake在编译此工程的时候,也就加载这些插件。 + +##### add_packagedirs + +###### 添加包目录 + +通过设置依赖包目录,可以方便的集成一些第三方的依赖库,以tbox工程为例,其包目录如下: + +``` +tbox.pkg +- base.pkg +- zlib.pkg +- polarssl.pkg +- openssl.pkg +- mysql.pkg +- pcre.pkg +- ... +``` + +如果要让当前工程识别加载这些包,首先要指定包目录路径,例如: + +```lua +add_packagedirs("pkg") +``` + +指定好后,就可以在target作用域中,通过[add_packages](#add_packages)接口,来添加集成包依赖了,例如: + +```lua +target("tbox") + add_packages("zlib", "polarssl", "pcre", "mysql") +``` + +#### 工程目标 + +定义和设置子工程模块,每个`target`对应一个子工程,最后会生成一个目标程序,有可能是可执行程序,也有可能是库模块。 + ++target的接口,都是可以放置在target外面的全局作用域中的,如果在全局中设置,那么会影响所有子工程target。 +
+ +例如: + +```lua +-- 会同时影响test和test2目标 +add_defines("DEBUG") + +target("test") + add_files("*.c") + +target("test2") + add_files("*.c") +``` + ++`target`域是可以重复进入来实现分离设置的。 +
+ + +| 接口 | 描述 | 支持版本 | +| --------------------------------------------- | ------------------------------------ | -------- | +| [target](#target) | 定义工程目标 | >= 1.0.1 | +| [target_end](#target_end) | 结束定义工程目标 | >= 2.1.1 | +| [set_kind](#targetset_kind) | 设置目标编译类型 | >= 1.0.1 | +| [set_strip](#targetset_strip) | 设置是否strip信息 | >= 1.0.1 | +| [set_default](#targetset_default) | 设置是否为默认构建安装目标 | >= 2.1.3 | +| [set_options](#targetset_options) | 设置关联选项 | >= 1.0.1 | +| [set_symbols](#targetset_symbols) | 设置符号信息 | >= 1.0.1 | +| [set_basename](#targetset_basename) | 设置目标文件名 | >= 2.1.2 | +| [set_warnings](#targetset_warnings) | 设置警告级别 | >= 1.0.1 | +| [set_optimize](#targetset_optimize) | 设置优化级别 | >= 1.0.1 | +| [set_languages](#targetset_languages) | 设置代码语言标准 | >= 1.0.1 | +| [set_headerdir](#targetset_headerdir) | 设置头文件安装目录 | >= 1.0.1 | +| [set_targetdir](#targetset_targetdir) | 设置生成目标文件目录 | >= 1.0.1 | +| [set_objectdir](#targetset_objectdir) | 设置对象文件生成目录 | >= 1.0.1 | +| [on_load](#targeton_load) | 自定义目标加载脚本 | >= 2.1.5 | +| [on_build](#targeton_build) | 自定义编译脚本 | >= 2.0.1 | +| [on_clean](#targeton_clean) | 自定义清理脚本 | >= 2.0.1 | +| [on_package](#targeton_package) | 自定义打包脚本 | >= 2.0.1 | +| [on_install](#targeton_install) | 自定义安装脚本 | >= 2.0.1 | +| [on_uninstall](#targeton_uninstall) | 自定义卸载脚本 | >= 2.0.1 | +| [on_run](#targeton_run) | 自定义运行脚本 | >= 2.0.1 | +| [before_build](#targetbefore_build) | 在构建之前执行一些自定义脚本 | >= 2.0.1 | +| [before_clean](#targetbefore_clean) | 在清除之前执行一些自定义脚本 | >= 2.0.1 | +| [before_package](#targetbefore_package) | 在打包之前执行一些自定义脚本 | >= 2.0.1 | +| [before_install](#targetbefore_install) | 在安装之前执行一些自定义脚本 | >= 2.0.1 | +| [before_uninstall](#targetbefore_uninstall) | 在卸载之前执行一些自定义脚本 | >= 2.0.1 | +| [before_run](#targetbefore_run) | 在运行之前执行一些自定义脚本 | >= 2.0.1 | +| [after_build](#targetafter_build) | 在构建之后执行一些自定义脚本 | >= 2.0.1 | +| [after_clean](#targetafter_clean) | 在清除之后执行一些自定义脚本 | >= 2.0.1 | +| [after_package](#targetafter_package) | 在打包之后执行一些自定义脚本 | >= 2.0.1 | +| [after_install](#targetafter_install) | 在安装之后执行一些自定义脚本 | >= 2.0.1 | +| [after_uninstall](#targetafter_uninstall) | 在卸载之后执行一些自定义脚本 | >= 2.0.1 | +| [after_run](#targetafter_run) | 在运行之后执行一些自定义脚本 | >= 2.0.1 | +| [set_config_h](#targetset_config_h) | 设置自动生成的配置头文件路径 | >= 1.0.1 < 2.1.5 已废弃 | +| [set_config_h_prefix](#targetset_config_h) | 设置自动生成的头文件中宏定义命名前缀 | >= 1.0.1 < 2.1.5 已废弃 | +| [set_config_header](#targetset_config_header) | 设置自动生成的配置头文件路径和前缀 | >= 2.1.5 | +| [set_pcheader](#targetset_pcheader) | 设置c预编译头文件 | >= 2.1.5 | +| [set_pcxxheader](#targetset_pcxxheader) | 设置c++预编译头文件 | >= 2.1.5 | +| [add_deps](#targetadd_deps) | 添加子工程目标依赖 | >= 1.0.1 | +| [add_links](#targetadd_links) | 添加链接库名 | >= 1.0.1 | +| [add_files](#targetadd_files) | 添加源代码文件 | >= 1.0.1 | +| [add_headers](#targetadd_headers) | 添加安装的头文件 | >= 1.0.1 | +| [add_linkdirs](#targetadd_linkdirs) | 添加链接库搜索目录 | >= 1.0.1 | +| [add_rpathdirs](#targetadd_rpathdirs) | 添加运行时候动态链接库搜索目录 | >= 2.1.3 | +| [add_includedirs](#targetadd_includedirs) | 添加头文件搜索目录 | >= 1.0.1 | +| [add_defines](#targetadd_defines) | 添加宏定义 | >= 1.0.1 | +| [add_undefines](#targetadd_undefines) | 取消宏定义 | >= 1.0.1 | +| [add_defines_h](#targetadd_defines_h) | 添加宏定义到头文件 | >= 1.0.1 | +| [add_undefines_h](#targetadd_undefines_h) | 取消宏定义到头文件 | >= 1.0.1 | +| [add_cflags](#targetadd_cflags) | 添加c编译选项 | >= 1.0.1 | +| [add_cxflags](#targetadd_cxflags) | 添加c/c++编译选项 | >= 1.0.1 | +| [add_cxxflags](#targetadd_cxxflags) | 添加c++编译选项 | >= 1.0.1 | +| [add_mflags](#targetadd_mflags) | 添加objc编译选项 | >= 1.0.1 | +| [add_mxflags](#targetadd_mxflags) | 添加objc/objc++编译选项 | >= 1.0.1 | +| [add_mxxflags](#targetadd_mxxflags) | 添加objc++编译选项 | >= 1.0.1 | +| [add_scflags](#targetadd_scflags) | 添加swift编译选项 | >= 2.0.1 | +| [add_asflags](#targetadd_asflags) | 添加汇编编译选项 | >= 2.0.1 | +| [add_gcflags](#targetadd_gcflags) | 添加go编译选项 | >= 2.1.1 | +| [add_ldflags](#targetadd_ldflags) | 添加链接选项 | >= 1.0.1 | +| [add_arflags](#targetadd_arflags) | 添加静态库归档选项 | >= 1.0.1 | +| [add_shflags](#targetadd_shflags) | 添加动态库链接选项 | >= 1.0.1 | +| [add_cfunc](#targetadd_cfunc) | 添加单个c库函数检测 | >= 2.0.1 | +| [add_cxxfunc](#targetadd_cxxfunc) | 添加单个c++库函数检测 | >= 2.0.1 | +| [add_cfuncs](#targetadd_cfuncs) | 添加c库函数检测 | >= 2.0.1 | +| [add_cxxfuncs](#targetadd_cxxfuncs) | 添加c++库函数接口 | >= 2.0.1 | +| [add_packages](#targetadd_packages) | 添加包依赖 | >= 2.0.1 | +| [add_options](#targetadd_options) | 添加关联选项 | >= 2.0.1 | +| [add_languages](#targetadd_languages) | 添加语言标准 | >= 1.0.1 | +| [add_vectorexts](#targetadd_vectorexts) | 添加向量扩展指令 | >= 1.0.1 | +| [add_frameworks](#targetadd_frameworks) | 添加链接框架 | >= 2.1.1 | +| [add_frameworkdirs](#targetadd_frameworkdirs) | 添加链接框架的搜索目录 | >= 2.1.5 | + +##### target + +###### 定义工程目标 + +定义一个新的控制台工程目标,工程名为`test`,最后生成的目标名也是`test`。 + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") +``` + +可以重复调用这个api,进入target域修改设置 + +```lua +-- 定义目标demo,并进入demo设置模式 +target("demo") + set_kind("binary") + add_files("src/demo.c") + +-- 定义和设置其他目标 +target("other") + ... + +-- 重新进入demo目标域,添加test.c文件 +target("demo") + add_files("src/test.c") +``` + ++所有根域的设置,会全局影响所有target目标,但是不会影响option的定义。 +
+ +```lua +-- 在根域对所有target添加-DDEBUG的宏定义,影响所有target(demo和test都会加上此宏定义) +add_defines("DEBUG") + +target("demo") + set_kind("binary") + add_files("src/demo.c") + +target("test") + set_kind("binary") + add_files("src/test.c") +``` + +##### target_end + +###### 结束定义工程目标 + +这是一个可选的api,如果不调用,那么`target("xxx")`之后的所有设置都是针对这个target进行的,除非进入其他`target`, `option`, `task`域。 + +如果想设置完当前`target`后,显示离开`target`域,进入根域设置,那么可以通过这个api才操作,例如: + +```lua +target("test") + set_kind("static") + add_files("src/*.c") +target_end() + +-- 此处已在根域 +-- ... +``` + +如果不调用这个api的话: + +```lua +target("test") + set_kind("static") + add_files("src/*.c") + +-- 此处还在上面target域中,之后的设置还是针对test进行的设置 +-- ... + +-- 这个时候才离开test,进入另外一个target域中 +target("test2") + ... +``` + +##### target:set_kind + +###### 设置目标编译类型 + +设置目标类型,目前支持的类型有: + +| 值 | 描述 | +| ------ | -----------| +| binary | 二进制程序 | +| static | 静态库程序 | +| shared | 动态库程序 | + +```lua +target("demo") + set_kind("binary") +``` + +##### target:set_strip + +###### 设置是否strip信息 + +设置当前目标的strip模式,目前支持一下模式: + +| 值 | 描述 | +| ------ | ----------------------------------------- | +| debug | 链接的时候,strip掉调试符号 | +| all | 链接的时候,strip掉所有符号,包括调试符号 | + +这个api一般在release模式下使用,可以生成更小的二进制程序。。 + +```lua +target("xxxx") + set_strip("all") +``` + ++这个api不一定非得在target之后使用,如果没有target指定,那么将会设置到全局模式。。 +
+ +##### target:set_default + +###### 设置是否为默认构建安装目标 + +这个接口用于设置给定工程目标是否作为默认构建,如果没有调用此接口进行设置,那么这个目标就是默认被构建的,例如: + +```lua +target("test1") + set_default(false) + +target("test2") + set_default(true) + +target("test3") + ... +``` + +上述代码的三个目标,在执行`xmake`, `xmake install`, `xmake package`, `xmake run`等命令的时候,如果不指定目标名,那么: + +| 目标名 | 行为 | +| ------ | -------------------------------- | +| test1 | 不会被默认构建、安装、打包和运行 | +| test2 | 默认构建、安装、打包和运行 | +| test3 | 默认构建、安装、打包和运行 | + +通过上面的例子,可以看到默认目标可以设置多个,运行的时候也会依次运行。 + ++ 需要注意的是,`xmake uninstall`和`xmake clean`命令不受此接口设置影响,因为用户大部分情况下都是喜欢清除和卸载所有。 +
+ +如果不想使用默认的目标,那么可以手动指定需要构建安装的目标: + +```bash +$ xmake build targetname +$ xmake install targetname +``` + +如果要强制构建安装所有目标,可以传入`[-a|--all]`参数: + +```bash +$ xmake build [-a|--all] +$ xmake install [-a|--all] +``` + +##### target:set_options + +###### 设置关联选项 + +添加选项依赖,如果通过[option](#option)接口自定义了一些选项,那么只有在指定`target`目标域下,添加此选项,才能进行关联生效。 + +```lua +-- 定义一个hello选项 +option("hello") + set_default(false) + set_showmenu(true) + add_defines("HELLO_ENABLE") + +target("test") + -- 如果hello选项被启用了,这个时候就会将-DHELLO_ENABLE宏应用到test目标上去 + set_options("hello") +``` + ++只有调用`set_options`进行关联生效后,[option](#option) 中定义的一些设置才会影响到此`target`目标,例如:宏定义、链接库、编译选项等等 +
+ +##### target:set_symbols + +###### 设置符号信息 + +设置目标的符号模式,如果当前没有定义target,那么将会设置到全局状态中,影响所有后续的目标。 + +目前主要支持一下几个级别: + +| 值 | 描述 | +| ------ | ---------------------- | +| debug | 添加调试符号 | +| hidden | 设置符号不可见 | + +这两个值也可以同时被设置,例如: + +```lua +-- 添加调试符号, 设置符号不可见 +set_symbols("debug", "hidden") +``` + +如果没有调用这个api,默认是禁用调试符号的。。 + +##### target:set_basename + +###### 设置目标文件名 + +默认情况下,生成的目标文件名基于`target("name")`中配置的值,例如: + +```lua +-- 目标文件名为:libxxx.a +target("xxx") + set_kind("static") + +-- 目标文件名为:libxxx2.so +target("xxx2") + set_kind("shared") +``` + +默认的命名方式,基本上可以满足大部分情况下的需求,但是如果有时候想要更加定制化目标文件名 + +例如,按编译模式和架构区分目标名,这个时候可以使用这个接口,来设置: + +```lua +target("xxx") + set_kind("static") + set_basename("xxx_$(mode)_$(arch)") +``` + +如果这个时候,编译配置为:`xmake f -m debug -a armv7`,那么生成的文件名为:`libxxx_debug_armv7.a` + +如果还想进一步定制目标文件的目录名,可参考:[set_targetdir](#targetset_targetdir)。 + +或者通过编写自定义脚本,实现更高级的逻辑,具体见:[after_build](#targetafter_build)和[os.mv](#os-mv)。 + +##### target:set_warnings + +###### 设置警告级别 + +设置当前目标的编译的警告级别,一般支持一下几个级别: + +| 值 | 描述 | +| ----- | ---------------------- | +| none | 禁用所有警告 | +| less | 启用较少的警告 | +| more | 启用较多的警告 | +| all | 启用所有警告 | +| error | 将所有警告作为编译错误 | + +这个api的参数是可以混合添加的,例如: + +```lua +-- 启用所有警告,并且作为编译错误处理 +set_warnings("all", "error") +``` + +如果当前没有目标,调用这个api将会设置到全局模式。。 + +##### target:set_optimize + +###### 设置优化级别 + +设置目标的编译优化等级,如果当前没有设置目标,那么将会设置到全局状态中,影响所有后续的目标。 + +目前主要支持一下几个级别: + +| 值 | 描述 | +| ---------- | ---------------------- | +| none | 禁用优化 | +| fast | 快速优化 | +| faster | 更快的优化 | +| fastest | 最快运行速度的优化 | +| smallest | 最小化代码优化 | +| aggressive | 过度优化 | + +例如: + +```lua +-- 最快运行速度的优化 +set_optimize("fastest") +``` + +##### target:set_languages + +###### 设置代码语言标准 + +设置目标代码编译的语言标准,如果当前没有目标存在,将会设置到全局模式中。。。 + +支持的语言标准目前主要有以下几个: + +| 值 | 描述 | +| ---------- | ---------------------- | +| ansi | c语言标准: ansi | +| c89 | c语言标准: c89 | +| gnu89 | c语言标准: gnu89 | +| c99 | c语言标准: c99 | +| gnu99 | c语言标准: gnu99 | +| cxx98 | c++语言标准: `c++98` | +| gnuxx98 | c++语言标准: `gnu++98` | +| cxx11 | c++语言标准: `c++11` | +| gnuxx11 | c++语言标准: `gnu++11` | +| cxx14 | c++语言标准: `c++14` | +| gnuxx14 | c++语言标准: `gnu++14` | +| cxx1z | c++语言标准: `c++1z` | +| gnuxx1z | c++语言标准: `gnu++1z` | +| cxx17 | c++语言标准: `c++17` | +| gnuxx17 | c++语言标准: `gnu++17` | + +c标准和c++标准可同时进行设置,例如: + +```lua +-- 设置c代码标准:c99, c++代码标准:c++11 +set_languages("c99", "cxx11") +``` + +
+并不是设置了指定的标准,编译器就一定会按这个标准来编译,毕竟每个编译器支持的力度不一样,但是xmake会尽最大可能的去适配当前编译工具的支持标准。。。
+
+例如:
+
+windows下vs的编译器并不支持按c99的标准来编译c代码,只能支持到c89,但是xmake为了尽可能的支持它,所以在设置c99的标准后,xmake会强制按c++代码模式去编译c代码,从一定程度上解决了windows下编译c99的c代码问题。。
+用户不需要去额外做任何修改。。
+
+如果显示设置了`set_targetdir`, 那么优先选择`set_targetdir`指定的目录为目标文件的输出目录。 +
+ +##### target:set_objectdir + +###### 设置对象文件生成目录 + +设置目标target的对象文件(`*.o/obj`)的输出目录,例如: + +```lua +target("test") + set_objectdir("$(buildir)/.objs") +``` + +##### target:on_load + +###### 自定义目标加载脚本 + +在target初始化加载的时候,将会执行此脚本,在里面可以做一些动态的目标配置,实现更灵活的目标描述定义,例如: + +```lua +target("test") + on_load(function (target) + target:add("defines", "DEBUG", "TEST=\"hello\"") + target:add("linkdirs", "/usr/lib", "/usr/local/lib") + target:add({includedirs = "/usr/include", "links" = "pthread"}) + end) +``` + +可以在`on_load`里面,通过`target:set`, `target:add` 来动态添加各种target属性。 + +##### target:on_build + +###### 自定义编译脚本 + +覆盖target目标默认的构建行为,实现自定义的编译过程,一般情况下,并不需要这么做,除非确实需要做一些xmake默认没有提供的编译操作。 + +你可以通过下面的方式覆盖它,来自定义编译操作: + +```lua +target("test") + + -- 设置自定义编译脚本 + on_build(function (target) + print("build it") + end) +``` + +注:2.1.5版本之后,所有target的自定义脚本都可以针对不同平台和架构,分别处理,例如: + +```lua +target("test") + on_build("iphoneos|arm*", function (target) + print("build for iphoneos and arm") + end) +``` + +其中如果第一个参数为字符串,那么就是指定这个脚本需要在哪个`平台|架构`下,才会被执行,并且支持模式匹配,例如`arm*`匹配所有arm架构。 + +当然也可以只设置平台,不设置架构,这样就是匹配指定平台下,执行脚本: + +```lua +target("test") + on_build("windows", function (target) + print("build for windows") + end) +``` + ++一旦对这个target目标设置了自己的build过程,那么xmake默认的构建过程将不再被执行。 +
+ +##### target:on_clean + +###### 自定义清理脚本 + +覆盖target目标的`xmake [c|clean}`的清理操作,实现自定义清理过程。 + +```lua +target("test") + + -- 设置自定义清理脚本 + on_clean(function (target) + + -- 仅删掉目标文件 + os.rm(target:targetfile()) + end) +``` + +一些target接口描述如下: + +| target接口 | 描述 | +| ----------------------------------- | ---------------------------------------------------------------- | +| target:name() | 获取目标名 | +| target:targetfile() | 获取目标文件路径 | +| target:get("kind") | 获取目标的构建类型 | +| target:get("defines") | 获取目标的宏定义 | +| target:get("xxx") | 其他通过 `set_/add_`接口设置的target信息,都可以通过此接口来获取 | +| target:add("links", "pthread") | 添加目标设置 | +| target:set("links", "pthread", "z") | 覆写目标设置 | +| target:deps() | 获取目标的所有依赖目标 | +| target:dep("depname") | 获取指定的依赖目标 | +| target:sourcebatches() | 获取目标的所有源文件列表 | + +##### target:on_package + +###### 自定义打包脚本 + +覆盖target目标的`xmake [p|package}`的打包操作,实现自定义打包过程,如果你想对指定target打包成自己想要的格式,可以通过这个接口自定义它。 + +这个接口还是挺实用的,例如,编译玩jni后,将生成的so,打包进apk包中。 + +```lua +-- 定义一个android app的测试demo +target("demo") + + -- 生成动态库:libdemo.so + set_kind("shared") + + -- 设置对象的输出目录,可选 + set_objectdir("$(buildir)/.objs") + + -- 每次编译完的libdemo.so的生成目录,设置为app/libs/armeabi + set_targetdir("libs/armeabi") + + -- 添加jni的代码文件 + add_files("jni/*.c") + + -- 设置自定义打包脚本,在使用xmake编译完libdemo.so后,执行xmake p进行打包 + -- 会自动使用ant将app编译成apk文件 + -- + on_package(function (target) + + -- 使用ant编译app成apk文件,输出信息重定向到日志文件 + os.run("ant debug") + end) +``` + +##### target:on_install + +###### 自定义安装脚本 + +覆盖target目标的`xmake [i|install}`的安装操作,实现自定义安装过程。 + +例如,将生成的apk包,进行安装。 + +```lua +target("test") + + -- 设置自定义安装脚本,自动安装apk文件 + on_install(function (target) + + -- 使用adb安装打包生成的apk文件 + os.run("adb install -r ./bin/Demo-debug.apk") + end) +``` + +##### target:on_uninstall + +###### 自定义卸载脚本 + +覆盖target目标的`xmake [u|uninstall}`的卸载操作,实现自定义卸载过程。 + +```lua +target("test") + on_uninstall(function (target) + ... + end) +``` + +##### target:on_run + +###### 自定义运行脚本 + +覆盖target目标的`xmake [r|run}`的运行操作,实现自定义运行过程。 + +例如,运行安装好的apk程序: + +```lua +target("test") + + -- 设置自定义运行脚本,自动运行安装好的app程序,并且自动获取设备输出信息 + on_run(function (target) + + os.run("adb shell am start -n com.demo/com.demo.DemoTest") + os.run("adb logcat") + end) +``` + +##### target:before_build + +###### 在构建之前执行一些自定义脚本 + +并不会覆盖默认的构建操作,只是在构建之前增加一些自定义的操作。 + +```lua +target("test") + before_build(function (target) + print("") + end) +``` + +##### target:before_clean + +###### 在清理之前执行一些自定义脚本 + +并不会覆盖默认的清理操作,只是在清理之前增加一些自定义的操作。 + +```lua +target("test") + before_clean(function (target) + print("") + end) +``` + +##### target:before_package + +###### 在打包之前执行一些自定义脚本 + +并不会覆盖默认的打包操作,只是在打包之前增加一些自定义的操作。 + +```lua +target("test") + before_package(function (target) + print("") + end) +``` + +##### target:before_install + +###### 在安装之前执行一些自定义脚本 + +并不会覆盖默认的安装操作,只是在安装之前增加一些自定义的操作。 + +```lua +target("test") + before_install(function (target) + print("") + end) +``` + +##### target:before_uninstall + +###### 在卸载之前执行一些自定义脚本 + +并不会覆盖默认的卸载操作,只是在卸载之前增加一些自定义的操作。 + +```lua +target("test") + before_uninstall(function (target) + print("") + end) +``` + +##### target:before_run + +###### 在运行之前执行一些自定义脚本 + +并不会覆盖默认的运行操作,只是在运行之前增加一些自定义的操作。 + +```lua +target("test") + before_run(function (target) + print("") + end) +``` + +##### target:after_build + +###### 在构建之后执行一些自定义脚本 + +并不会覆盖默认的构建操作,只是在构建之后增加一些自定义的操作。 + +例如,对于ios的越狱开发,构建完程序后,需要用`ldid`进行签名操作 + +```lua +target("test") + after_build(function (target) + os.run("ldid -S %s", target:targetfile()) + end) +``` + +##### target:after_clean + +###### 在清理之后执行一些自定义脚本 + +并不会覆盖默认的清理操作,只是在清理之后增加一些自定义的操作。 + +一般可用于清理编译某target自动生成的一些额外的临时文件,这些文件xmake默认的清理规则可能没有清理到,例如: + +```lua +target("test") + after_clean(function (target) + os.rm("$(buildir)/otherfiles") + end) +``` + +##### target:after_package + +###### 在打包之后执行一些自定义脚本 + +并不会覆盖默认的打包操作,只是在打包之后增加一些自定义的操作。 + +```lua +target("test") + after_package(function (target) + print("") + end) +``` + +##### target:after_install + +###### 在安装之后执行一些自定义脚本 + +并不会覆盖默认的安装操作,只是在安装之后增加一些自定义的操作。 + +```lua +target("test") + after_install(function (target) + print("") + end) +``` +##### target:after_uninstall + +###### 在卸载之后执行一些自定义脚本 + +并不会覆盖默认的卸载操作,只是在卸载之后增加一些自定义的操作。 + +```lua +target("test") + after_uninstall(function (target) + print("") + end) +``` + +##### target:after_run + +###### 在运行之后执行一些自定义脚本 + +并不会覆盖默认的运行操作,只是在运行之后增加一些自定义的操作。 + +```lua +target("test") + after_run(function (target) + print("") + end) +``` + +##### target:set_config_h + +###### 设置自动生成的配置头文件路径 + ++2.1.5版本之后,此接口已废弃,请使用[set_config_header](#targetset_config_header)。 +
+ +如果你想在xmake配置项目成功后,或者自动检测某个选项通过后,把检测的结果写入配置头文件,那么需要调用这个接口来启用自动生成`config.h`文件。 + +使用方式例如: + +```lua +target("test") + + -- 启用并设置需要自动生成的config.h文件路径 + set_config_h("$(buildir)/config.h") + + -- 设置自动检测生成的宏开关的名字前缀 + set_config_h_prefix("TB_CONFIG") +``` + +当这个target中通过下面的这些接口,对这个target添加了相关的选项依赖、包依赖、接口依赖后,如果某依赖被启用,那么对应的一些宏定义配置,会自动写入被设置的`config.h`文件中去。 + +* [add_options](#targetadd_options) +* [add_packages](#targetadd_packages) +* [add_cfuncs](#targetadd_cfuncs) +* [add_cxxfuncs](#targetadd_cxxfuncs) + +这些接口,其实底层都用到了[option](#option)选项中的一些检测设置,例如: + +```lua +option("wchar") + + -- 添加对wchar_t类型的检测 + add_ctypes("wchar_t") + + -- 如果检测通过,自动生成 TB_CONFIG_TYPE_HAVE_WCHAR的宏开关到config.h + add_defines_h("$(prefix)_TYPE_HAVE_WCHAR") + +target("test") + + -- 启用头文件自动生成 + set_config_h("$(buildir)/config.h") + set_config_h_prefix("TB_CONFIG") + + -- 添加对wchar选项的依赖关联,只有加上这个关联,wchar选项的检测结果才会写入指定的config.h中去 + add_options("wchar") +``` + +##### target:set_config_h_prefix + +###### 设置自动生成的头文件中宏定义命名前缀 + ++2.1.5版本之后,此接口已废弃,请使用[set_config_header](#targetset_config_header)。 +
+ +具体使用见:[set_config_h](#targetset_config_h) + +如果设置了: + +```lua +target("test") + set_config_h_prefix("TB_CONFIG") +``` + +那么,选项中`add_defines_h("$(prefix)_TYPE_HAVE_WCHAR")`的$(prefix)会自动被替换成新的前缀值。 + +##### target:set_config_header + +###### 设置自动生成的配置头文件路径和前缀 + +此接口是[set_config_h](#targetset_config_h)和[set_config_h_prefix](#targetset_config_h_prefix)的升级版本,2.1.5之后支持。 + +如果你想在xmake配置项目成功后,或者自动检测某个选项通过后,把检测的结果写入配置头文件,那么需要调用这个接口来启用自动生成`config.h`文件。 + +使用方式例如: + +```lua +target("test") + set_config_header("$(buildir)/config.h", {prefix = "TB_CONFIG"}) +``` + +上面的代码,启用并设置需要自动生成的config.h文件路径,并且设置自动检测生成的宏开关的名字前缀:`TB_CONFIG`, 当然这个前缀的设置是可选的。 + +```lua +target("test") + set_config_header("$(buildir)/config.h") +``` + +如果不设置前缀,将会自动根据target名生成一个唯一字串。 + +当这个target中通过下面的这些接口,对这个target添加了相关的选项依赖、包依赖、接口依赖后,如果某依赖被启用,那么对应的一些宏定义配置,会自动写入被设置的`config.h`文件中去。 + +* [add_options](#targetadd_options) +* [add_packages](#targetadd_packages) +* [add_cfuncs](#targetadd_cfuncs) +* [add_cxxfuncs](#targetadd_cxxfuncs) + +这些接口,其实底层都用到了[option](#option)选项中的一些检测设置,例如: + +```lua +option("wchar") + + -- 添加对wchar_t类型的检测 + add_ctypes("wchar_t") + + -- 如果检测通过,自动生成 TB_CONFIG_TYPE_HAVE_WCHAR的宏开关到config.h + add_defines_h("$(prefix)_TYPE_HAVE_WCHAR") + +target("test") + + -- 启用头文件自动生成 + set_config_header("$(buildir)/config.h", {prefix = "TB_CONFIG"}) + + -- 添加对wchar选项的依赖关联,只有加上这个关联,wchar选项的检测结果才会写入指定的config.h中去 + add_options("wchar") +``` + +##### target:set_pcheader + +###### 设置c预编译头文件 + +xmake支持通过预编译头文件去加速c程序编译,目前支持的编译器有:gcc, clang和msvc。 + +使用方式如下: + +```lua +target("test") + set_pcheader("header.h") +``` + +##### target:set_pcxxheader + +###### 设置c++预编译头文件 + +xmake支持通过预编译头文件去加速c++程序编译,目前支持的编译器有:gcc, clang和msvc。 + +使用方式如下: + +```lua +target("test") + set_pcxxheader("header.h") +``` + +##### target:add_deps + +###### 添加子工程目标依赖 + +添加当前目标的依赖目标,编译的时候,会去优先编译依赖的目标,然后再编译当前目标。。。 + +```lua +target("test1") + set_kind("static") + set_files("*.c") + +target("test2") + set_kind("static") + set_files("*.c") + +target("demo") + + -- 添加依赖目标:test1, test2 + add_deps("test1", "test2") + + -- 链接libtest1.a,libtest2.a + add_links("test1", "test2") +``` + +上面的例子,在编译目标demo的时候,需要先编译test1, test2目标,因为demo会去用到他们 + ++2.1.5版本后,target会自动继承依赖目标中的配置和属性,不再需要额外调用`add_links`, `add_includedirs`和`add_linkdirs`等接口去关联依赖目标了。 +
+ +2.1.5版本之后,上述代码可简化为: + +```lua +target("test1") + set_kind("static") + set_files("*.c") + +target("test2") + set_kind("static") + set_files("*.c") + +target("demo") + add_deps("test1", "test2") -- 会自动链接依赖目标 +``` + +并且继承关系是支持级联的,例如: + +```lua +target("library1") + set_kind("static") + add_files("*.c") + add_headers("inc1/*.h") + +target("library2") + set_kind("static") + add_deps("library1") + add_files("*.c") + add_headers("inc2/*.h") + +target("test") + set_kind("binary") + add_deps("library2") +``` + +如果我们还是想以前那样,并不想继承依赖target的任何配置,如何操作呢?这个在2.1.6版本中也对其进行了支持,通过: + +```lua +add_deps("dep1", "dep2", {inherit = false}) +``` + +通过显示设置inherit配置,来告诉xmake,这两个依赖的配置是否需要被继承,如果不设置,默认就是启用继承的。 + +##### target:add_links + +###### 添加链接库名 + +为当前目标添加链接库,一般这个要与[add_linkdirs](#targetadd_linkdirs)配对使用。 + +```lua +target("demo") + + -- 添加对libtest.a的链接,相当于 -ltest + add_links("test") + + -- 添加链接搜索目录 + add_linkdirs("$(buildir)/lib") +``` + +##### target:add_files + +###### 添加源代码文件 + +用于添加目标工程的源文件,甚至库文件,目前支持的一些文件类型: + +| 支持的源文件类型 | 描述 | +| ------------------ | ---------------------------------- | +| .c/.cpp/.cc/.cxx | c++文件 | +| .s/.S/.asm | 汇编文件 | +| .m/.mm | objc文件 | +| .swift | swift文件 | +| .go | golang文件 | +| .o/.obj | 对象文件 | +| .a/.lib | 静态库文件,会自动合并库到目标程序 | +| .rc | msvc的资源文件 | + +其中通配符`*`表示匹配当前目录下文件,而`**`则匹配多级目录下的文件。 + +例如: + +```lua +add_files("src/test_*.c") +add_files("src/xxx/**.cpp") +add_files("src/asm/*.S", "src/objc/**/hello.m") +``` + +`add_files`的使用其实是相当灵活方便的,其匹配模式借鉴了premake的风格,但是又对其进行了改善和增强。 + +使得不仅可以匹配文件,还有可以在添加文件同时,过滤排除指定模式的一批文件。 + +例如: + +```lua +-- 递归添加src下的所有c文件,但是不包括src/impl/下的所有c文件 +add_files("src/**.c|impl/*.c") + +-- 添加src下的所有cpp文件,但是不包括src/test.cpp、src/hello.cpp以及src下所有带xx_前缀的cpp文件 +add_files("src/*.cpp|test.cpp|hello.cpp|xx_*.cpp") +``` + +其中分隔符`|`之后的都是需要排除的文件,这些文件也同样支持匹配模式,并且可以同时添加多个过滤模式,只要中间用`|`分割就行了。。 + +添加文件的时候支持过滤一些文件的一个好处就是,可以为后续根据不同开关逻辑添加文件提供基础。 + ++为了使得描述上更加的精简,`|`之后的过滤描述都是基于起一个模式:`src/*.cpp` 中`*`之前的目录为基础的。 +所以上面的例子后面过滤的都是在src下的文件,这个是要注意的。 +
+ +2.1.6版本之后,对`add_files`进行了改进,支持基于files更细粒度的编译选项控制,例如: + +```lua +target("test") + add_defines("TEST1") + add_files("src/*.c") + add_files("test/*.c", "test2/test2.c", {defines = "TEST2", languages = "c99", includedirs = ".", cflags = "-O0"}) +``` + +可以在`add_files`的最后一个参数,传入一个配置table,去控制指定files的编译选项,里面的配置参数跟target的一致,并且这些文件还会继承target的通用配置`-DTEST1`。 + +##### target:add_headers + +###### 添加安装的头文件 + +安装指定的头文件到build目录,如果设置了[set_headerdir](#targetset_headerdir), 则输出到指定目录。 + +安装规则的语法跟[add_files](#targetadd_files)类似,例如: + +```lua + -- 安装tbox目录下所有的头文件(忽略impl目录下的文件),并且按()指定部分作为相对路径,进行安装 + add_headers("../(tbox/**.h)|**/impl/**.h") +``` + +##### target:add_linkdirs + +###### 添加链接库搜索目录 + +设置链接库的搜索目录,这个接口的使用方式如下: + +```lua +target("test") + add_linkdirs("$(buildir)/lib") +``` + +此接口相当于gcc的`-Lxxx`链接选项。 + +一般他是与[add_links](#targetadd_links)配合使用的,当然也可以直接通过[add_ldflags](#targetadd_ldflags)或者[add_shflags](#targetadd_shflags)接口来添加,也是可以的。 + ++如果不想在工程中写死,可以通过:`xmake f --linkdirs=xxx`或者`xmake f --ldflags="-L/xxx"`的方式来设置,当然这种手动设置的目录搜索优先级更高。 +
+ +##### target:add_rpathdirs + +###### 添加程序运行时动态库的加载搜索目录 + +通过[add_linkdirs](#targetadd_linkdirs)设置动态库的链接搜索目录后,程序被正常链接,但是在linux平台想要正常运行编译后的程序,会报加载动态库失败。 + +因为没找到动态库的加载目录,想要正常运行依赖动态库的程序,需要设置`LD_LIBRARY_PATH`环境变量,指定需要加载的动态库目录。 + +但是这种方式是全局的,影响太广,更好的方式是通过`-rpath=xxx`的链接器选项,在链接程序的时候设置好需要加载的动态库搜索路径,而xmake对其进行了封装,通过`add_rpathdirs`更好的处理跨平台问题。 + +具体使用如下: + +```lua +target("test") + set_kind("binary") + add_linkdirs("$(buildir)/lib") + add_rpathdirs("$(buildir)/lib") +``` + +只需要在链接的时候,在设置下rpath目录就好了,虽然也可以通过`add_ldflags("-Wl,-rpath=xxx")`达到相同的目的,但是这个接口更加通用。 + +内部会对不同平台进行处理,像在macOS下,是不需要`-rpath`设置的,也是可以正常加载运行程序,因此针对这个平台,xmake内部会直接忽略器设置,避免链接报错。 + +而在为dlang程序进行动态库链接时,xmake会自动处理成`-L-rpath=xxx`来传入dlang的链接器,这样就避免了直接使用`add_ldflags`需要自己判断和处理不同平台和编译器问题。 + +2.1.7版本对这个接口进行了改进,支持:`@loader_path`, `@executable_path` 和 `$ORIGIN`的内置变量,来指定程序的加载目录,它们的效果基本上是一样的,主要是为了同时兼容macho, elf。 + +例如: + +```lua +target("test") + set_kind("binary") + add_linkdirs("$(buildir)/lib") + add_rpathdirs("@loader_path/lib") +``` + +指定test程序加载当前执行目录下`lib/*.[so|dylib]`的动态库文件,这将有助于提升程序的可移植性,不用写死绝对路径和相对路径,导致程序和目录切换引起程序加载动态库失败。 + +##### target:add_includedirs + +###### 添加头文件搜索目录 + +设置头文件的搜索目录,这个接口的使用方式如下: + +```lua +target("test") + add_includedirs("$(buildir)/include") +``` + +当然也可以直接通过[add_cxflags](#targetadd_cxflags)或者[add_mxflags](#targetadd_mxflags)等接口来设置,也是可以的。 + ++如果不想在工程中写死,可以通过:`xmake f --includedirs=xxx`或者`xmake f --cxflags="-I/xxx"`的方式来设置,当然这种手动设置的目录搜索优先级更高。 +
+ +##### target:add_defines + +###### 添加宏定义 + +```lua +add_defines("DEBUG", "TEST=0", "TEST2=\"hello\"") +``` + +相当于设置了编译选项: + +``` +-DDEBUG -DTEST=0 -DTEST2=\"hello\" +``` + +##### target:add_undefines + +###### 取消宏定义 + +```lua +add_undefines("DEBUG") +``` + +相当于设置了编译选项:`-UDEBUG` + +在代码中相当于:`#undef DEBUG` + +##### target:add_defines_h + +###### 添加宏定义到头文件 + +添加宏定义到`config.h`配置文件,`config.h`的设置,可参考[set_config_h](#targetset_config_h)接口。 + +##### target:add_undefines_h + +###### 取消宏定义到头文件 + +在`config.h`配置文件中通过`undef`禁用宏定义,`config.h`的设置,可参考[set_config_h](#targetset_config_h)接口。 + +##### target:add_cflags + +###### 添加c编译选项 + +仅对c代码添加编译选项 + +```lua +add_cflags("-g", "-O2", "-DDEBUG") +``` + ++所有选项值都基于gcc的定义为标准,如果其他编译器不兼容(例如:vc),xmake会自动内部将其转换成对应编译器支持的选项值。 +用户无需操心其兼容性,如果其他编译器没有对应的匹配值,那么xmake会自动忽略器设置。 +
+ +##### target:add_cxflags + +###### 添加c/c++编译选项 + +同时对c/c++代码添加编译选项 + +##### target:add_cxxflags + +###### 添加c++编译选项 + +仅对c++代码添加编译选项 + +##### target:add_mflags + +###### 添加objcc编译选项 + +仅对objc代码添加编译选项 + +```lua +add_mflags("-g", "-O2", "-DDEBUG") +``` + +##### target:add_mxflags + +###### 添加objc/objc++编译选项 + +同时对objc/objc++代码添加编译选项 + +```lua +add_mxflags("-framework CoreFoundation") +``` + +##### target:add_mxxflags + +###### 添加objc++编译选项 + +仅对objc++代码添加编译选项 + +```lua +add_mxxflags("-framework CoreFoundation") +``` + +##### target:add_scflags + +###### 添加swift编译选项 + +对swift代码添加编译选项 + +```lua +add_scflags("xxx") +``` + +##### target:add_asflags + +###### 添加汇编编译选项 + +对汇编代码添加编译选项 + +```lua +add_asflags("xxx") +``` + +##### target:add_gcflags + +###### 添加go编译选项 + +对golang代码添加编译选项 + +```lua +add_gcflags("xxx") +``` + +##### target:add_dcflags + +###### 添加dlang编译选项 + +对dlang代码添加编译选项 + +```lua +add_dcflags("xxx") +``` + +##### target:add_rcflags + +###### 添加rust编译选项 + +对rust代码添加编译选项 + +```lua +add_rcflags("xxx") +``` + +##### target:add_ldflags + +###### 添加链接选项 + +添加静态链接库选项 + +```lua +add_ldflags("-L/xxx", "-lxxx") +``` + +##### target:add_arflags + +###### 添加静态库归档选项 + +影响对静态库的生成 + +```lua +add_arflags("xxx") +``` +##### target:add_shflags + +###### 添加动态库链接选项 + +影响对动态库的生成 + +```lua +add_shflags("xxx") +``` + +##### target:add_cfunc + +###### 添加单个c库函数检测 + +与[add_cfuncs](#targetadd_cfuncs)类似,只是仅对单个函数接口进行设置,并且仅对`target`域生效,`option`中不存在此接口。 + +此接口的目的主要是为了在`config.h`中更加高度定制化的生成宏开关,例如: + +```lua +target("demo") + + -- 设置和启用config.h + set_config_h("$(buildir)/config.h") + set_config_h_prefix("TEST") + + -- 仅通过参数一设置模块名前缀 + add_cfunc("libc", nil, nil, {"sys/select.h"}, "select") + + -- 通过参数三,设置同时检测链接库:libpthread.a + add_cfunc("pthread", nil, "pthread", "pthread.h", "pthread_create") + + -- 通过参数二设置接口别名 + add_cfunc(nil, "PTHREAD", nil, "pthread.h", "pthread_create") +``` + +生成的结果如下: + +```c +#ifndef TEST_H +#define TEST_H + +// 宏命名规则:$(prefix)前缀 _ 模块名(如果非nil)_ HAVE _ 接口名或者别名 (大写) +#define TEST_LIBC_HAVE_SELECT 1 +#define TEST_PTHREAD_HAVE_PTHREAD_CREATE 1 +#define TEST_HAVE_PTHREAD 1 + +#endif +``` + +如果要更加灵活的函数检测,可以通过[lib.detect.has_cfuncs](#detect-has_cfuncs)在自定义脚本中实现。 + +##### target:add_cxxfunc + +###### 添加单个c++库函数检测 + +与[add_cfunc](#targetadd_cfunc)类似,只是检测的函数接口是c++函数。 + +##### target:add_cfuncs + +###### 添加c库函数检测 + ++此接口是`target`和`option`共用的接口,但是接口行为稍有不同。 +
+ +| 接口域 | 描述 | 例子 | +| ------ | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| target | 头文件、链接库和函数接口同时指定 | `add_cfuncs("libc", nil, {"signal.h", "setjmp.h"}, "signal", "setjmp", "sigsetjmp{sigjmp_buf buf; sigsetjmp(buf, 0);}", "kill")` | +| option | 仅指定函数接口,头文件依赖[add_cincludes](#targetadd_cincludes)等独立接口 | `add_cincludes("setjmp.h")` `add_cfuncs("sigsetjmp")` | + +对于`option`,这个接口的使用很简单,跟[add_cincludes](#targetadd_cincludes)类似,例如: + +```lua +option("setjmp") + set_default(false) + add_cincludes("setjmp.h") + add_cfuncs("sigsetjmp", "setjmp") + add_defines("HAVE_SETJMP") + +target("test") + add_options("setjmp") +``` + +此选项检测是否存在`setjmp`的一些接口,如果检测通过那么`test`目标程序将会加上`HAVE_SETJMP`的宏定义。 + +
+需要注意的是,在`option`中使用此接口检测依赖函数,需要同时使用独立的[add_cincludes](#targetadd_cincludes)增加头文件搜索路径,指定[add_links](#targetadd_links)链接库(可选),否则检测不到指定函数。
+
+并且某些头文件接口是通过宏开关分别定义的,那么检测的时候最好通过[add_defines](#targetadd_defines)带上依赖的宏开关。
+
+如果当前设置的指令集编译器不支持,xmake会自动忽略掉,所以不需要用户手动去判断维护,只需要将你需要的指令集全部设置上就行了。 +
+ +##### target:add_frameworks + +###### 添加链接框架 + +目前主要用于`ios`和`macosx`平台的`objc`和`swift`程序,例如: + +```lua +target("test") + add_frameworks("Foundation", "CoreFoundation") +``` + +当然也可以使用[add_mxflags](#targetadd_mxflags)和[add_ldflags](#targetadd_ldflags)来设置,不过比较繁琐,不建议这样设置。 + +```lua +target("test") + add_mxflags("-framework Foundation", "-framework CoreFoundation") + add_ldflags("-framework Foundation", "-framework CoreFoundation") +``` + +如果不是这两个平台,这些设置将会被忽略。 + +##### target:add_frameworkdirs + +###### 添加链接框架搜索目录 + +对于一些第三方framework,那么仅仅通过[add_frameworks](#targetadd_frameworks)是没法找到的,还需要通过这个接口来添加搜索目录。 + +```lua +target("test") + add_frameworks("MyFramework") + add_frameworkdirs("/tmp/frameworkdir", "/tmp/frameworkdir2") +``` + +#### 选项定义 + +定义和设置选项开关,每个`option`对应一个选项,可用于自定义编译配置选项、开关设置。 + ++除了`target`以外的所有域接口,例如`option`,`task`等的接口,默认不能放置在外面的全局作用域中的(除非部分跟target共用的接口除外)。 +如果要设置值影响所有`option`,`task`等选项,可以通过匿名全局域来设置。 +
+ +例如: + +```lua +-- 进入option的匿名全局域,里面的设置会同时影响test和test2选项 +option() + add_defines("DEBUG") + +option("test") + -- ... + -- 尽量保持缩进,因为这个之后的所有设置,都是针对test选项的 + +option("test2") + -- ... +``` + ++`option`域是可以重复进入来实现分离设置的,如果要显示离开当前选项的作用域设置,可以手动调用[option_end](#option_end)接口。 +
+ + +| 接口 | 描述 | 支持版本 | +| ----------------------------------------------------- | -------------------------------------------- | -------- | +| [option](#option) | 定义选项 | >= 2.0.1 | +| [option_end](#option_end) | 结束定义选项 | >= 2.1.1 | +| [add_deps](#optionadd_deps) | 添加选项依赖 | >= 2.1.5 | +| [before_check](#optionbefore_check) | 选项检测之前执行此脚本 | >= 2.1.5 | +| [on_check](#optionon_check) | 自定义选项检测脚本 | >= 2.1.5 | +| [after_check](#optionafter_check) | 选项检测之后执行此脚本 | >= 2.1.5 | +| [set_default](#optionset_default) | 设置默认值 | >= 2.0.1 | +| [set_showmenu](#optionset_showmenu) | 设置是否启用菜单显示 | >= 1.0.1 | +| [set_category](#optionset_category) | 设置选项分类,仅用于菜单显示 | >= 1.0.1 | +| [set_description](#optionset_description) | 设置菜单显示描述 | >= 1.0.1 | +| [add_links](#optionadd_links) | 添加链接库检测 | >= 1.0.1 | +| [add_linkdirs](#optionadd_linkdirs) | 添加链接库检测需要的搜索目录 | >= 1.0.1 | +| [add_rpathdirs](#optionadd_rpathdirs) | 添加运行时候动态链接库搜索目录 | >= 2.1.3 | +| [add_cincludes](#optionadd_cincludes) | 添加c头文件检测 | >= 1.0.1 | +| [add_cxxincludes](#optionadd_cxxincludes) | 添加c++头文件检测 | >= 1.0.1 | +| [add_ctypes](#optionadd_ctypes) | 添加c类型检测 | >= 1.0.1 | +| [add_cxxtypes](#optionadd_cxxtypes) | 添加c++类型检测 | >= 1.0.1 | +| [add_csnippet](#optionadd_csnippet) | 添加c代码片段检测 | >= 2.1.5 | +| [add_cxxsnippet](#optionadd_cxxsnippet) | 添加c++代码片段检测 | >= 2.1.5 | +| [set_warnings](#targetset_warnings) | 设置警告级别 | >= 1.0.1 | +| [set_optimize](#targetset_optimize) | 设置优化级别 | >= 1.0.1 | +| [set_languages](#targetset_languages) | 设置代码语言标准 | >= 1.0.1 | +| [add_includedirs](#targetadd_includedirs) | 添加头文件搜索目录 | >= 1.0.1 | +| [add_defines](#targetadd_defines) | 添加宏定义 | >= 1.0.1 | +| [add_undefines](#targetadd_undefines) | 取消宏定义 | >= 1.0.1 | +| [add_defines_h](#targetadd_defines_h) | 添加宏定义到头文件 | >= 1.0.1 | +| [add_undefines_h](#targetadd_undefines_h) | 取消宏定义到头文件 | >= 1.0.1 | +| [add_cflags](#targetadd_cflags) | 添加c编译选项 | >= 1.0.1 | +| [add_cxflags](#targetadd_cxflags) | 添加c/c++编译选项 | >= 1.0.1 | +| [add_cxxflags](#targetadd_cxxflags) | 添加c++编译选项 | >= 1.0.1 | +| [add_mflags](#targetadd_mflags) | 添加objc编译选项 | >= 2.0.1 | +| [add_mxflags](#targetadd_mxflags) | 添加objc/objc++编译选项 | >= 2.0.1 | +| [add_mxxflags](#targetadd_mxxflags) | 添加objc++编译选项 | >= 2.0.1 | +| [add_scflags](#targetadd_scflags) | 添加swift编译选项 | >= 2.1.1 | +| [add_asflags](#targetadd_asflags) | 添加汇编编译选项 | >= 2.1.1 | +| [add_gcflags](#targetadd_gcflags) | 添加go编译选项 | >= 2.1.1 | +| [add_dcflags](#targetadd_dcflags) | 添加dlang编译选项 | >= 2.1.1 | +| [add_rcflags](#targetadd_rcflags) | 添加rust编译选项 | >= 2.1.1 | +| [add_ldflags](#targetadd_ldflags) | 添加链接选项 | >= 2.1.1 | +| [add_arflags](#targetadd_arflags) | 添加静态库归档选项 | >= 2.1.1 | +| [add_shflags](#targetadd_shflags) | 添加动态库链接选项 | >= 2.0.1 | +| [add_cfuncs](#targetadd_cfuncs) | 添加c库函数检测 | >= 1.0.1 | +| [add_cxxfuncs](#targetadd_cxxfuncs) | 添加c++库函数接口 | >= 1.0.1 | +| [add_languages](#targetadd_languages) | 添加语言标准 | >= 2.0.1 | +| [add_vectorexts](#targetadd_vectorexts) | 添加向量扩展指令 | >= 2.0.1 | +| [add_frameworks](#targetadd_frameworks) | 添加链接框架 | >= 2.1.1 | +| [add_frameworkdirs](#targetadd_frameworkdirs) | 添加链接框架 | >= 2.1.5 | + +| 废弃接口 | 描述 | 支持版本 | +| ----------------------------------------------------- | -------------------------------------------- | ---------------- | +| [add_bindings](#optionadd_bindings) | 添加正向关联选项,同步启用和禁用 | >= 2.0.1 < 2.1.5 | +| [add_rbindings](#optionadd_rbindings) | 添加逆向关联选项,同步启用和禁用 | >= 2.0.1 < 2.1.5 | +| [add_defines_if_ok](#optionadd_defines_if_ok) | 如果检测选项通过,则添加宏定义 | >= 1.0.1 < 2.1.5 | +| [add_defines_h_if_ok](#optionadd_defines_h_if_ok) | 如果检测选项通过,则添加宏定义到配置头文件 | >= 1.0.1 < 2.1.5 | +| [add_undefines_if_ok](#optionadd_undefines_if_ok) | 如果检测选项通过,则取消宏定义 | >= 1.0.1 < 2.1.5 | +| [add_undefines_h_if_ok](#optionadd_undefines_h_if_ok) | 如果检测选项通过,则在配置头文件中取消宏定义 | >= 1.0.1 < 2.1.5 | + +##### option + +###### 定义选项 + +定义和设置选项开关,可用于自定义编译配置选项、开关设置。 + +例如,定义一个是否启用test的选项: + +```lua +option("test") + set_default(false) + set_showmenu(true) + add_defines("-DTEST") +``` + +然后关联到指定的target中去: + +```lua +target("demo") + add_options("test") +``` + +这样,一个选项就算定义好了,如果这个选项被启用,那么编译这个target的时候,就会自动加上`-DTEST`的宏定义。 + +```lua +# 手动启用这个选项 +$ xmake f --test=y +$ xmake +``` + +##### option_end + +###### 结束定义选项 + +这是一个可选api,显示离开选项作用域,用法和[target_end](#target_end)类似。 + +##### option:add_deps + +###### 添加选项依赖 + +通过设置依赖,可以调整选项的检测顺序,一般用于[on_check](#optionon_check)等检测脚本的调用时机。 + +```lua +option("small") + set_default(true) + on_check(function (option) + -- ... + end) + +option("test") + add_deps("small") + set_default(true) + on_check(function (option) + if option:dep("small"):enabled() then + option:enable(false) + end + end) +``` + +当依赖的small选项检测完成后,通过判断small选项的状态,来控制test的选项状态。 + +##### option:before_check + +###### 选项检测之前执行此脚本 + +例如:在检测之前,通过[find_package](#detect-find_package)来查找包,将`links`, `includedirs`和`linkdirs`等信息添加到option中去, +然后开始选项检测,通过后就会自动链接到target上。 + +```lua +option("zlib") + before_check(function (option) + import("lib.detect.find_package") + option:add(find_package("zlib")) + end) +``` + +##### option:on_check + +###### 自定义选项检测脚本 + +此脚本会覆盖内置的选项检测逻辑。 + +```lua +option("test") + add_deps("small") + set_default(true) + on_check(function (option) + if option:dep("small"):enabled() then + option:enable(false) + end + end) +``` + +如果test依赖的选项通过,则禁用test选项。 + +##### option:after_check + +###### 选项检测之后执行此脚本 + +在选项检测完成后,执行此脚本做一些后期处理,也可以在此时重新禁用选项: + +```lua +option("test") + add_deps("small") + add_links("pthread") + after_check(function (option) + option:enable(false) + end) +``` + +##### option:set_default + +###### 设置选项默认值 + +在没有通过`xmake f --option=[y|n}`等命令修改选项值的时候,这个选项本身也是有个默认值的,可以通过这个接口来设置: + +```lua +option("test") + -- 默认禁用这个选项 + set_default(false) +``` + +选项的值不仅支持boolean类型,也可以是字符串类型,例如: + +```lua +option("test") + set_default("value") +``` + +| 值类型 | 描述 | 配置 | +| ------ | -------------------------------------- | -----------------------------------------------| +| boolean | 一般用作参数开关,值范围:`true/false` | `xmake f --optionname=[y/n/yes/no/true/false]` | +| string | 可以是任意字符串,一般用于模式判断 | `xmake f --optionname=value` | + +如果是`boolean`值的选项,可以通过[is_option](#is_option)来进行判断,选项是否被启用。 + +如果是`string`类型的选项,可以在内建变量中直接使用,例如: + +```lua +-- 定义一个路径配置选项,默认使用临时目录 +option("rootdir") + set_default("$(tmpdir)") + set_showmenu(true) + +target("test") + -- 添加指定选项目录中的源文件 + add_files("$(rootdir)/*.c") +``` + +其中,`$(rootdir)` 就是自定义的选项内建变量,通过手动配置,可以动态修改它的值: + +```bash +$ xmake f --rootdir=~/projectdir/src +$ xmake +``` + +给这个`rootdir`选项指定一个其他的源码目录路径,然后编译。 + +选项的检测行为: + +| default值 | 检测行为 | +| ---------- | --------------------------------------------------------------------------------------------- | +| 没有设置 | 优先手动配置修改,默认禁用,否则自动检测,可根据手动传入的值类型,自动切换boolean和string类型 | +| false | 开关选项,不自动检测,默认禁用,可手动配置修改 | +| true | 开关选项,不自动检测,默认启用,可手动配置修改 | +| string类型 | 无开关状态,不自动检测,可手动配置修改,一般用于配置变量传递 | + +##### option:set_showmenu + +###### 设置是否启用菜单显示 + +如果设置为`true`,那么在`xmake f --help`里面就会出现这个选项,也就能通过`xmake f --optionname=xxx`进行配置,否则只能在`xmake.lua`内部使用,无法手动配置修改。 + +```lua +option("test") + set_showmenu(true) +``` + +设置为启用菜单后,执行`xmake f --help`可以看到,帮助菜单里面多了一项: + +``` +Options: + ... + + --test=TEST +``` + +##### option:set_category + +###### 设置选项分类,仅用于菜单显示 + +这个是个可选配置,仅用于在帮助菜单中,进行分类显示选项,同一类别的选项,会在同一个分组里面显示,这样菜单看起来更加的美观。 + +例如: + +```lua +option("test1") + set_showmenu(true) + set_category("test") + +option("test2") + set_showmenu(true) + set_category("test") + +option("demo1") + set_showmenu(true) + set_category("demo") + +option("demo2") + set_showmenu(true) + set_category("demo") +``` + +这里四个选项分别归类于两个分组:`test`和`demo`,那么显示的布局类似这样: + +```bash +Options: + ... + + --test1=TEST1 + --test2=TEST2 + + --demo1=DEMO1 + --demo2=DEMO2 +``` + +这个接口,仅仅是为了调整显示布局,更加美观而已,没其他用途。 + +##### option:set_description + +###### 设置菜单显示描述 + +设置选项菜单显示时,右边的描述信息,用于帮助用户更加清楚的知道这个选项的用途,例如: + +```lua +option("test") + set_default(false) + set_showmenu(true) + set_description("Enable or disable test") +``` + +生成的菜单内容如下: + +``` +Options: + ... + + --test=TEST Enable or disable test (default: false) +``` + +这个接口也支持多行显示,输出更加详细的描述信息,例如: + +```lua +option("mode") + set_default("debug") + set_showmenu(true) + set_description("Set build mode" + " - debug" + " - release" + " - profile") +``` + +生成的菜单内容如下: + +``` +Options: + ... + + --mode=MODE Set build mode (default: debug) + - debug + - release + - profile +``` + +看到这个菜单,用户就能清楚地知道,定义的这个`mode`选项的具体用处,以及如何使用了: + +```bash +$ xmake f --mode=release +``` + +##### option:add_bindings + +###### 添加正向关联选项,同步启用和禁用 + ++2.1.5版本之后已废弃,请用[add_deps](#optionadd_deps), [on_check](#optionon_check), [after_check](#optionafter_check)等接口代替。 +
+ +绑定关联选项,例如我想在命令行中配置一个`smallest`的参数:`xmake f --smallest=y` + +这个时候,需要同时禁用多个其他的选项开关,来禁止编译多个模块,就是这个需求,相当于一个选项 与其他 多个选项之间 是有联动效应的。 + +而这个接口就是用来设置需要正向绑定的一些关联选项,例如: + +```lua +-- 定义选项开关: --smallest=y|n +option("smallest") + + -- 添加正向绑定,如果smallest被启用,下面的所有选项开关也会同步被启用 + add_bindings("nozip", "noxml", "nojson") +``` + +##### option:add_rbindings + +###### 添加逆向关联选项,同步启用和禁用 + ++2.1.5版本之后已废弃,请用[add_deps](#optionadd_deps), [on_check](#optionon_check), [after_check](#optionafter_check)等接口代替。 +
+ +逆向绑定关联选项,被关联选项的开关状态是相反的。 + +```lua +-- 定义选项开关: --smallest=y|n +option("smallest") + + -- 添加反向绑定,如果smallest被启用,下面的所有模块全部禁用 + add_rbindings("xml", "zip", "asio", "regex", "object", "thread", "network", "charset", "database") + add_rbindings("zlib", "mysql", "sqlite3", "openssl", "polarssl", "pcre2", "pcre", "base") +``` + ++需要注意的是,命令行配置是有顺序的,你可以先通过启用smallest禁用所有模块,然后添加其他选项,逐一启用。 +
+ +例如: + +```bash +-- 禁用所有模块,然后仅仅启用xml和zip模块 +$ xmake f --smallest=y --xml=y --zip=y +``` + +##### option:add_links + +###### 添加链接库检测 + +如果指定的链接库检测通过,此选项将被启用,并且对应关联的target会自动加上此链接,例如: + +```lua +option("pthread") + set_default(false) + add_links("pthread") + add_linkdirs("/usr/local/lib") + +target("test") + add_options("pthread") +``` + +如果检测通过,`test`目标编译的时候就会自动加上:`-L/usr/local/lib -lpthread` 编译选项 + + +##### option:add_linkdirs + +###### 添加链接库检测时候需要的搜索目录 + +这个是可选的,一般系统库不需要加这个,也能检测通过,如果确实没找到,可以自己追加搜索目录,提高检测通过率。具体使用见:[add_links](#optionadd_links) + +##### option:add_rpathdirs + +###### 添加程序运行时动态库的加载搜索目录 + +在选项通过检测后,会自动添加到对应的target上去,具体使用见:[target.add_rpathdirs](#targetadd_rpathdirs)。 + +##### option:add_cincludes + +###### 添加c头文件检测 + +如果c头文件检测通过,此选项将被启用,例如: + +```lua +option("pthread") + set_default(false) + add_cincludes("pthread.h") + add_defines("ENABLE_PTHREAD") + +target("test") + add_options("pthread") +``` + +此选项检测是否存在`pthread.h`的头文件,如果检测通过那么`test`目标程序将会加上`ENABLE_PTHREAD`的宏定义。 + +如果想要更加灵活的检测,可以通过[lib.detect.has_cincludes](#detect-has_cincludes)在[option.on_check](#optionon_check)中去实现。 + +##### option:add_cxxincludes + +###### 添加c++头文件检测 + +与[add_cincludes](#optionadd_cincludes)类似,只是检测的头文件类型是c++头文件。 + +##### option:add_ctypes + +###### 添加c类型检测 + +如果c类型检测通过,此选项将被启用,例如: + +```lua +option("wchar") + set_default(false) + add_cincludes("wchar_t") + add_defines("HAVE_WCHAR") + +target("test") + add_options("wchar") +``` + +此选项检测是否存在`wchar_t`的类型,如果检测通过那么`test`目标程序将会加上`HAVE_WCHAR`的宏定义。 + +如果想要更加灵活的检测,可以通过[lib.detect.has_ctypes](#detect-has_ctypes)在[option.on_check](#optionon_check)中去实现。 + +##### option:add_cxxtypes + +###### 添加c++类型检测 + +与[add_ctypes](#optionadd_ctypes)类似,只是检测的类型是c++类型。 + +##### option:add_csnippet + +###### 添加c代码片段检测 + +如果现有的[add_ctypes](#optionadd_ctypes), [add_cfuncs](#optionadd_cfuncs)等不能满足当前的检测需求, +可以用这个接口实现更加定制化检测一些编译器特性检测,具体见: [add_cxxsnippet](#optionadd_cxxsnippet)。 + +##### option:add_cxxsnippet + +###### 添加c++代码片段检测 + +可以用这个接口实现更加定制化检测一些编译器特性检测,尤其是c++的各种特性的检测支持,例如: + +```lua +option("constexpr") + add_cxxsnippet("constexpr int f(int x) { int sum=0; for (int i=0; i<=x; ++i) sum += i; return sum; } constexpr int x = f(5); static_assert(x == 15);") +``` + +上述代码,实现对c++的constexpr特性的检测,如果检测通过,则启用constexpr选项,当然这里只是个例子。 + +对于编译器特性的检测,有更加方便高效的检测模块,提供更强大的检测支持,具体见:[compiler.has_features](#compiler-has_features)和[detect.check_cxsnippets](#detect-check_cxsnippets) + +如果想要更加灵活的检测,可以通过[lib.detect.check_cxsnippets](#detect-check_cxsnippets)在[option.on_check](#optionon_check)中去实现。 + +##### option:add_defines_if_ok + +###### 如果检测选项通过,则添加宏定义 + ++2.1.5版本之后已废弃,请用[add_defines](#targetadd_defines)接口代替。 +
+ +检测选项通过后才会被设置,具体使用见[add_cincludes](#optionadd_cincludes)中的例子。 + +##### option:add_defines_h_if_ok + +###### 如果检测选项通过,则添加宏定义到配置头文件 + ++2.1.5版本之后已废弃,请用[add_defines_h](#targetadd_defines_h)接口代替。 +
+ +跟[add_defines_if_ok](#optionadd_defines_if_ok)类似,只是检测通过后,会在`config.h`头文件中自动加上被设置的宏定义。 + +例如: + +```lua +option("pthread") + set_default(false) + add_cincludes("pthread.h") + add_defines_h_if_ok("ENABLE_PTHREAD") + +target("test") + add_options("pthread") +``` + +通过后,会在`config.h`中加上: + +```c +#define ENABLE_PTHREAD 1 +``` + +具体`config.h`如何设置,见:[set_config_h](#targetset_config_h) + +##### option:add_undefines_if_ok + +###### 如果检测选项通过,则取消宏定义 + ++2.1.5版本之后已废弃,请用[add_undefines](#targetadd_undefines)接口代替。 +
+ +跟[add_defines_if_ok](#optionadd_defines_if_ok)类似,只是检测通过后,取消被设置的宏定义。 + +##### option:add_undefines_h_if_ok + +###### 如果检测选项通过,则在配置头文件中取消宏定义 + ++2.1.5版本之后已废弃,请用[add_undefines_h](#targetadd_undefines_h)接口代替。 +
+ +跟[add_defines_h_if_ok](#optionadd_defines_h_if_ok)类似,只是检测通过后,会在`config.h`中取消被设置的宏定义。 + +```c +#undef DEFINED_MACRO +``` + +具体`config.h`如何设置,见:[set_config_h](#targetset_config_h) + +#### 插件任务 + +xmake可以实现自定义任务或者插件,其两者的核心就是`task`任务,其两者实际上是一样的,xmake的插件都是用`task`实现的。 + +本质上都是任务,只是[set_category](#taskset_category)分类不同而已。 + +| 接口 | 描述 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [task](#task) | 定义插件或者任务 | >= 2.0.1 | +| [task_end](#task_end) | 结束定义插件或任务 | >= 2.1.1 | +| [set_menu](#taskset_menu) | 设置任务菜单 | >= 2.0.1 | +| [set_category](#taskset_category) | 设置任务类别 | >= 2.0.1 | +| [on_run](#taskon_run) | 设置任务运行脚本 | >= 2.0.1 | + +##### task + +###### 定义插件或者任务 + +`task`域用于描述一个自定义的任务实现,与[target](#target)和[option](#option)同级。 + +例如,这里定义一个最简单的任务: + +```lua +task("hello") + + -- 设置运行脚本 + on_run(function () + print("hello xmake!") + end) +``` + +这个任务只需要打印`hello xmake!`,那如何来运行呢? + +由于这里没有使用[set_menu](#taskset_menu)设置菜单,因此这个任务只能再`xmake.lua`的自定义脚本或者其他任务内部调用,例如: + +```lua +target("test") + + after_build(function (target) + + -- 导入task模块 + import("core.project.task") + + -- 运行hello任务 + task.run("hello") + end) +``` + +在构建完`test`目标后运行`hello`任务。 + +##### task_end + +###### 结束定义插件或任务 + +这是一个可选api,显示离开选项作用域,用法和[target_end](#target_end)类似。 + +##### task:set_menu + +###### 设置任务菜单 + +通过设置一个菜单,这个任务就可以开放给用户自己通过命令行手动调用,菜单的设置如下: + +```lua +task("echo") + + -- 设置运行脚本 + on_run(function () + + -- 导入参数选项模块 + import("core.base.option") + + -- 初始化颜色模式 + local modes = "" + for _, mode in ipairs({"bright", "dim", "blink", "reverse"}) do + if option.get(mode) then + modes = modes .. " " .. mode + end + end + + -- 获取参数内容并且显示信息 + cprint("${%s%s}%s", option.get("color"), modes, table.concat(option.get("contents") or {}, " ")) + end) + + -- 设置插件的命令行选项,这里没有任何参数选项,仅仅显示插件描述 + set_menu { + -- 设置菜单用法 + usage = "xmake echo [options]" + + -- 设置菜单描述 + , description = "Echo the given info!" + + -- 设置菜单选项,如果没有选项,可以设置为{} + , options = + { + -- 设置k模式作为key-only型bool参数 + {'b', "bright", "k", nil, "Enable bright." } + , {'d', "dim", "k", nil, "Enable dim." } + , {'-', "blink", "k", nil, "Enable blink." } + , {'r', "reverse", "k", nil, "Reverse color." } + + -- 菜单显示时,空白一行 + , {} + + -- 设置kv作为key-value型参数,并且设置默认值:black + , {'c', "color", "kv", "black", "Set the output color." + , " - red" + , " - blue" + , " - yellow" + , " - green" + , " - magenta" + , " - cyan" + , " - white" } + + -- 设置`vs`作为values多值型参数,还有`v`单值类型 + -- 一般放置在最后,用于获取可变参数列表 + , {} + , {nil, "contents", "vs", nil, "The info contents." } + } + } +``` + +定义完这个任务后,执行`xmake --help`,就会多出一个任务项来: + +``` +Tasks: + + ... + + echo Echo the given info! +``` + +如果通过[set_category](#taskset_category)设置分类为`plugin`,那么这个任务就是一个插件了: + +``` +Plugins: + + ... + + echo Echo the given info! +``` + +想要手动运行这个任务,可以执行: + +```bash +$ xmake echo hello xmake! +``` + +就行了,如果要看这个任务定义的菜单,只需要执行:`xmake echo [-h|--help]`,显示结果如下: + +```bash +Usage: $xmake echo [options] + +Echo the given info! + +Options: + -v, --verbose Print lots of verbose information. + --backtrace Print backtrace information for debugging. + --profile Print performance data for debugging. + --version Print the version number and exit. + -h, --help Print this help message and exit. + + -F FILE, --file=FILE Read a given xmake.lua file. + -P PROJECT, --project=PROJECT Change to the given project directory. + Search priority: + 1. The Given Command Argument + 2. The Envirnoment Variable: XMAKE_PROJECT_DIR + 3. The Current Directory + + -b, --bright Enable bright. + -d, --dim Enable dim. + --, --blink Enable blink. + -r, --reverse Reverse color. + + -c COLOR, --color=COLOR Set the output color. (default: black) + - red + - blue + - yellow + - green + - magenta + - cyan + - white + + contents ... The info contents. +``` + ++其中菜单最开头的部分选项,是xmake内置的常用选项,基本上每个任务都会用到,不需要自己额外定义,简化菜单定义。 +
+ +下面,我们来实际运行下这个任务,例如我要显示红色的`hello xmake!`,只需要: + +```bash +$ xmake echo -c red hello xmake! +``` + +也可以使用选项全名,并且加上高亮: + +```bash +$ xmake echo --color=red --bright hello xmake! +``` + +最后面的可变参数列表,在`run`脚本中通过`option.get("contents")`获取,返回的是一个`table`类型的数组。 + +##### task:set_category + +###### 设置任务类别 + +仅仅用于菜单的分组显示,当然插件默认会用`plugin`,内置任务默认会用:`action`,但也仅仅只是个约定。 + ++你可以使用任何自己定义的名字,相同名字会分组归类到一起显示,如果设置为`plugin`,就会显示到xmake的Plugins分组中去。 +
+ +例如: + +```lua +Plugins: + l, lua Run the lua script. + m, macro Run the given macro. + doxygen Generate the doxygen document. + project Generate the project file. + hello Hello xmake! + app2ipa Generate .ipa file from the given .app + echo Echo the given info! +``` + +如果没有调用这个接口设置分类,默认使用`Tasks`分组显示,代表普通任务。 + +##### task:on_run + +###### 设置任务运行脚本 + +可以有两种设置方式,最简单的就是设置内嵌函数: + +```lua +task("hello") + + on_run(function () + print("hello xmake!") + end) +``` + +这种对于小任务很方便,也很简洁,但是对于大型任务就不太适用了,例如插件等,需要复杂的脚本支持。 + +这个时候就需要独立的模块文件来设置运行脚本,例如: + +```lua +task("hello") + on_run("main") +``` + +这里的`main`设置为脚本运行主入口模块,文件名为`main.lua`,放在定义`task`的`xmake.lua`的同目录下,当然你可以起其他文件名。 + +目录结构如下: + +``` +projectdir + - xmake.lua + - main.lua +``` + +`main.lua`里面内容如下: + +```lua +function main(...) + print("hello xmake!") +end +``` + +就是一个简单的带`main`主函数的脚本文件,你可以通过[import](#import)导入各种扩展模块,实现复杂功能,例如: + +```lua +-- 导入参数选项模块 +import("core.base.option") + +-- 入口函数 +function main(...) + + -- 获取参数内容 + print("color: %s", option.get("color")) +end +``` + +你也可以在当前目录下,创建多个自定义的模块文件,通过[import](#import)导入后使用,例如: + +``` +projectdir + - xmake.lua + - main.lua + - module.lua +``` + +`module.lua`的内容如下: + +```lua +-- 定义一个导出接口 +function hello() + print("hello xmake!") +end +``` + ++私有接口,通过`_hello`带下滑线前缀命名,这样导入的模块就不会包含此接口,只在模块自身内部使用。 +
+ +然后在`main.lua`进行调用: + + +```lua +import("module") + +function main(...) + module.hello() +end +``` + +更多模块介绍见:[内置模块](#内置模块)和[扩展模块](扩展模块) + +其中,`main(...)`中参数,是通过`task.run`指定的,例如: + +```lua +task.run("hello", {color="red"}, arg1, arg2, arg3) +``` + +里面的`arg1, arg2`这些就是传入`hello`任务`main(...)`入口的参数列表,而`{color="red"}`用来指定任务菜单中的参数选项。 + +更加详细的`task.run`描述,见:[task.run](#task-run) + +#### 平台扩展 + +xmake除了内置的一些构建平台,还可以自己扩展自定义构建平台,可以将自己实现的平台放置在以下目录即可, xmake会自动检测并且加载他们: + +| 平台目录 | 描述 | +| --------------------------- | ------------------------------------ | +| projectdir/.xmake/platforms | 当前工程的平台目录, 只对当前工程有效 | +| globaldir/.xmake/platforms | 全局配置的平台目录,当前主机全局有效 | +| installdir/xmake/platforms | xmake安装后内置的平台目录 | + +用户可根据不同需求,将自定义的平台放置在对应的目录中。 + ++平台的扩展定义,尽量不要放到工程`xmake.lua`中去,新建一个单独的平台目录放置相关描述实现。 +
+ +平台描述的目录结构: + +``` +platforms + + - myplat1 + - xmake.lua + + - myplat2 + - xmake.lua +``` + +其中`xmake.lua`为每个平台的主描述文件,相当于入口描述。 + +| 接口 | 描述 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [platform](#platform) | 定义平台 | >= 2.0.1 | +| [platform_end](#platform_end) | 结束定义平台 | >= 2.1.1 | +| [set_os](#platformset_os) | 设置平台系统 | >= 2.0.1 | +| [set_menu](#platformset_menu) | 设置平台菜单 | >= 2.0.1 | +| [set_hosts](#platformset_hosts) | 设置平台支持的主机环境 | >= 2.0.1 | +| [set_archs](#platformset_archs) | 设置平台支持的架构环境 | >= 2.0.1 | +| [set_tooldirs](#platformset_tooldirs) | 设置平台工具的搜索目录 | >= 2.0.1 | +| [on_load](#platformon_load) | 设置加载平台环境配置脚本 | >= 2.0.1 | +| [on_check](#platformon_check) | 设置平台工具的检测脚本 | >= 2.0.1 | +| [on_install](#platformon_install) | 设置平台相关的工程目标安装脚本 | >= 2.0.5 | +| [on_uninstall](#platformon_uninstall) | 设置平台相关的工程目标卸载脚本 | >= 2.0.5 | + +##### platform + +###### 定义平台 + +自定义一个平台域,例如: + +```lua +platform("iphoneos") + + -- 设置操作系统 + set_os("ios") + + -- 设置主机环境 + set_hosts("macosx") + + -- 设置支持的架构 + set_archs("armv7", "armv7s", "arm64", "i386", "x86_64") + + -- 设置gcc, clang等平台相关工具的搜索目录 + set_tooldirs("/usr/bin", "/usr/local/bin", "/opt/bin", "/opt/local/bin") + + -- 设置gcc,clang等工具的检测脚本文件 + on_check("check") + + -- 设置平台初始化加载脚本文件,如果实现不复杂的话,可以使用内嵌函数 + on_load("load") + + -- 设置平台的帮助菜单 + set_menu { + config = + { + {} + , {nil, "xcode_dir", "kv", "auto", "the xcode application directory" } + , {nil, "xcode_sdkver", "kv", "auto", "the sdk version for xcode" } + , {nil, "target_minver", "kv", "auto", "the target minimal version" } + , {} + , {nil, "mobileprovision","kv", "auto", "The Provisioning Profile File" } + , {nil, "codesign", "kv", "auto", "The Code Signing Indentity" } + , {nil, "entitlements", "kv", "auto", "The Code Signing Entitlements" } + } + + , global = + { + {} + , {nil, "xcode_dir", "kv", "auto", "the xcode application directory" } + , {} + , {nil, "mobileprovision","kv", "auto", "The Provisioning Profile File" } + , {nil, "codesign", "kv", "auto", "The Code Signing Indentity" } + , {nil, "entitlements", "kv", "auto", "The Code Signing Entitlements" } + } + } + +``` + ++是在`platforms`目录相关平台的`xmake.lua`中编写,而不是在工程目录的`xmake.lua`中。 +
+ +##### platform_end + +###### 结束定义平台 + +这是一个可选api,显示离开选项作用域,用法和[target_end](#target_end)类似。 + +##### set_os + +###### 设置平台系统 + +设置目标平台的操作系统,例如:`ios`, `android`, `linux`, `windows` 等 + +```lua +platform("iphoneos") + set_os("ios") +``` + +这个一般用于在自定义脚本和插件开发中,[core.platform.platform](#core-platform-platform)模块中进行访问,获取当前平台的操作系统。 + +##### set_menu + +###### 设置平台菜单 + +先给个设置的例子: + +```lua +platform("iphoneos") + ... + + -- 设置平台的帮助菜单 + set_menu { + config = + { + {} + , {nil, "xcode_dir", "kv", "auto", "the xcode application directory" } + , {nil, "xcode_sdkver", "kv", "auto", "the sdk version for xcode" } + , {nil, "target_minver", "kv", "auto", "the target minimal version" } + , {} + , {nil, "mobileprovision","kv", "auto", "The Provisioning Profile File" } + , {nil, "codesign", "kv", "auto", "The Code Signing Indentity" } + , {nil, "entitlements", "kv", "auto", "The Code Signing Entitlements" } + } + + , global = + { + {} + , {nil, "xcode_dir", "kv", "auto", "the xcode application directory" } + , {} + , {nil, "mobileprovision","kv", "auto", "The Provisioning Profile File" } + , {nil, "codesign", "kv", "auto", "The Code Signing Indentity" } + , {nil, "entitlements", "kv", "auto", "The Code Signing Entitlements" } + } + } +``` + +其中`config`组用来设置:`xmake f --help`中的本地工程菜单,`global`用来设置:`xmake g --help`全局平台配置中的菜单。 + +具体设置格式可参考:[task:set_menu](#taskset_menu)。 + +##### set_hosts + +###### 设置平台支持的主机环境 + +用来设置当前目标平台支持主机构建环境,例如`iphoneos`平台可以在`macosx`主机系统上构建,那么可以设置为: + +```lua +platform("iphoneos") + set_hosts("macosx") +``` + +而`android`平台可以同时在`linux`, "macosx", `windows`主机环境中构建,那么可以设置为: + +```lua +platform("android") + set_hosts("linux", "macosx", "windows") +``` + +##### set_archs + +###### 设置平台支持的架构环境 + +用来设置当前目标平台支持的编译架构环境,例如`iphoneos`平台可以构建`armv7`, `armv7s`, `arm64`, `i386`, `x86_64`等架构,那么可以设置为: + +```lua +platform("iphoneos") + set_archs("armv7", "armv7s", "arm64", "i386", "x86_64") +``` + +配置好架构后,执行:`xmake f -h`,就会在对应arch参数描述,自动显示设置的架构列表: + +``` + -a ARCH, --arch=ARCH Compile for the given architecture. (default: auto) + - android: armv5te armv6 armv7-a armv8-a arm64-v8a + - iphoneos: armv7 armv7s arm64 i386 x86_64 + - linux: i386 x86_64 + - macosx: i386 x86_64 + - mingw: i386 x86_64 + - watchos: armv7k i386 + - windows: x86 x64 amd64 x86_amd64 +``` + +##### set_tooldirs + +###### 设置平台工具的搜索目录 + +xmake会自动检测当前平台支持的一些构建工具是否存在,例如编译器、链接器等,如果要提高检测通过率,可以在平台配置的时候,设置一些工具环境搜索目录,例如: + +```lua +platform("linux") + + -- 在linux下检测这些目录环境 + set_tooldirs("/usr/bin", "/usr/local/bin", "/opt/bin", "/opt/local/bin") +``` + +##### on_load + +###### 设置加载平台环境配置脚本 + +一般用于在平台刚加载时,设置一些基本配置:生成目标文件命名格式、平台相关编译选项等 + +```lua +platform("windows") + + -- on load + on_load(function () + + -- init the file formats + _g.formats = {} + _g.formats.static = {"", ".lib"} + _g.formats.object = {"", ".obj"} + _g.formats.shared = {"", ".dll"} + _g.formats.binary = {"", ".exe"} + _g.formats.symbol = {"", ".pdb"} + + -- init flags for dlang + local dc_archs = { x86 = "-m32", x64 = "-m64", amd64 = "-m64", x86_amd64 = "-m64" } + _g.dcflags = { dc_archs[arch] or "" } + _g["dc-shflags"] = { dc_archs[arch] or "" } + _g["dc-ldflags"] = { dc_archs[arch] or "" } + + -- ok + return _g + end) +``` + +如果加载逻辑比较复杂,可以独立成单独`init.lua`文件,然后设置为: + +```lua +platform("xxxx") + on_load("init") +``` + +通过这种方式,会自动加载平台脚本目录下对应的`init.lua`文件,调用`function main() end`函数入口,完成复杂加载逻辑。 + +##### on_check + +###### 设置平台工具的检测脚本 + +由于每个平台检测的工具非常多,脚本比较复杂,一般直接独立成`check.lua`文件来实现检测逻辑,例如: + +```lua +platform("xxx") + on_check("check") +``` + +具体的检测代码入口如下: + +```lua +-- check it +function main(kind) + + -- init the check list of config + _g.config = + { + __check_arch + , checker.check_ccache + , _check_toolchains + } + + -- init the check list of global + _g.global = + { + checker.check_ccache + , _check_ndk_sdkver + } + + -- check it + checker.check(kind, _g) +end +``` + +具体实现这里就不介绍了,可以参考xmake源码目录下的`platforms`平台配置代码: [check.lua](https://github.com/tboox/xmake/blob/master/xmake/platforms/macosx/check.lua) + +##### on_install + +###### 设置目标工程在指定平台的安装脚本 + +具体实现逻辑见xmake源码:[install.lua](https://github.com/tboox/xmake/blob/master/xmake/platforms/macosx/install.lua) + +##### on_uninstall + +###### 设置目标工程在指定平台的卸载脚本 + +具体实现逻辑见xmake源码:[uninstall.lua](https://github.com/tboox/xmake/blob/master/xmake/platforms/macosx/uninstall.lua) + +#### 语言扩展 + +有待后续完善。。 + +#### 工程模板 + +##### template +##### set_description +##### set_projectdir +##### add_macros +##### add_macrofiles + +#### 内置变量 + +xmake提供了 `$(varname)` 的语法,来支持内置变量的获取,例如: + +```lua +add_cxflags("-I$(buildir)") +``` + +它将会在在实际编译的时候,将内置的 `buildir` 变量转换为实际的构建输出目录:`-I./build` + +一般内置变量可用于在传参时快速获取和拼接变量字符串,例如: + +```lua +target("test") + + -- 添加工程源码目录下的源文件 + add_files("$(projectdir)/src/*.c") + + -- 添加构建目录下的头文件搜索路径 + add_includedirs("$(buildir)/inc") +``` + +也可以在自定义脚本的模块接口中使用,例如: + +```lua +target("test") + on_run(function (target) + -- 复制当前脚本目录下的头文件到输出目录 + os.cp("$(scriptdir)/xxx.h", "$(buildir)/inc") + end) +``` + +所有的内置变量,也可以通过[val](#val)接口,来获取他们的值。 + +这种使用内置变量的方式,使得描述编写更加的简洁易读,下面是一些xmake内置的变量,可以直接获取: + +| 接口 | 描述 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [$(os)](#var-os) | 获取当前编译平台的操作系统 | >= 2.0.1 | +| [$(host)](#var-host) | 获取本机操作系统 | >= 2.0.1 | +| [$(tmpdir)](#var-tmpdir) | 获取临时目录 | >= 2.0.1 | +| [$(curdir)](#var-curdir) | 获取当前目录 | >= 2.0.1 | +| [$(buildir)](#var-buildir) | 获取构建输出目录 | >= 2.0.1 | +| [$(scriptdir)](#var-scriptdir) | 获取工程描述脚本目录 | >= 2.1.1 | +| [$(globaldir)](#var-globaldir) | 获取全局配置目录 | >= 2.0.1 | +| [$(configdir)](#var-configdir) | 获取本地工程配置目录 | >= 2.0.1 | +| [$(programdir)](#var-programdir) | xmake安装脚本目录 | >= 2.1.5 | +| [$(projectdir)](#var-projectdir) | 获取工程根目录 | >= 2.0.1 | +| [$(shell)](#var-shell) | 执行外部shell命令 | >= 2.0.1 | +| [$(env)](#var-env) | 获取外部环境变量 | >= 2.1.5 | +| [$(reg)](#var-reg) | 获取windows注册表配置项的值 | >= 2.1.5 | + +当然这种变量模式,也是可以扩展的,默认通过`xmake f --var=val`命令,配置的参数都是可以直接获取,例如: + +```lua +target("test") + add_defines("-DTEST=$(var)") +``` + ++所有`xmake f --xxx=...`配置的参数值,都是可以通过内置变量获取到,例如:`xmake f --arch=x86`对应`$(arch)`,其他的还有`$(plat)`, `$(mode)`等等。 +具体有哪些参数,可以通过:`xmake f -h`才查看。 +
+ +既然支持直接从配置选项中获取,那么当然也就能很方便的扩展自定义的选项,来获取自定义的变量了,具体如何自定义选项见:[option](#option) + +##### var.$(os) + +###### 获取当前编译平台的操作系统 + +如果当前编译的是iphoneos,那么这个值就是:`ios`,以此类推。 + +##### var.$(host) + +###### 获取本机操作系统 + +指的是当前本机环境的主机系统,如果你是在macOS上编译,那么系统就是:`macosx` + +##### var.$(tmpdir) + +###### 获取临时目录 + +一般用于临时存放一些非永久性文件。 + +##### var.$(curdir) + +###### 获取当前目录 + +一般默认是执行`xmake`命令时的工程根目录,当然如果通过[os.cd](#os-cd)改变了目录的话,这个值也会一起改变。 + +##### var.$(buildir) + +###### 获取当前的构建输出目录 + +默认一般为当前工程根目录下的:`./build`目录,也可以通过执行:`xmake f -o /tmp/build`命令来修改默认的输出目录。 + +##### var.$(scriptdir) + +###### 获取当前工程描述脚本的目录 + +也就是对应`xmake.lua`所在的目录路径。 + +##### var.$(globaldir) + +###### 全局配置目录 + +xmake的`xmake g|global`全局配置命令,数据存储的目录路径,在里面可以放置一些自己的插件、平台脚本。 + +默认为:`~/.config` + +##### var.$(configdir) + +###### 当前工程配置目录 + +当前工程的配置存储目录,也就是`xmake f|config`配置命令的存储目录,默认为:`projectdir/.config` + +##### var.$(programdir) + +###### xmake安装脚本目录 + +也就是`XMAKE_PROGRAM_DIR`环境变量所在目录,我们也可以通过设置这个环境量,来修改xmake的加载脚本,实现版本切换。 + +##### var.$(projectdir) + +###### 工程根目录 + +也就是`xmake -P xxx`命令中指定的目录路径,默认不指定就是`xmake`命令执行时的当前目录,一般用于定位工程文件。 + +##### var.$(shell) + +###### 执行外部shell命令 + +除了内置的变量处理,xmake还支持原生shell的运行,来处理一些xmake内置不支持的功能 + +例如,现在有个需求,我想用在编译linux程序时,调用`pkg-config`获取到实际的第三方链接库名,可以这么做: + +```lua +target("test") + set_kind("binary") + if is_plat("linux") then + add_ldflags("$(shell pkg-config --libs sqlite3)") + end +``` + +当然,xmake有自己的自动化第三库检测机制,一般情况下不需要这么麻烦,而且lua自身的脚本化已经很不错了。。 + +但是这个例子可以说明,xmake是完全可以通过原生shell,来与一些第三方的工具进行配合使用。。 + +##### var.$(env) + +###### 获取外部环境变量 + +例如,可以通过获取环境变量中的路径: + +```lua +target("test") + add_includedirs("$(env PROGRAMFILES)/OpenSSL/inc") +``` + +##### var.$(reg) + +###### 获取windows注册表配置项的值 + +通过 `regpath; name` 的方式获取注册表中某个项的值: + +```lua +print("$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\XXXX;Name)") +``` + +#### 内置模块 + +在自定义脚本、插件脚本、任务脚本、平台扩展、模板扩展等脚本代码中使用,也就是在类似下面的代码块中,可以使用这些模块接口: + +```lua +on_run(function (target) + print("hello xmake!") +end) +``` + ++为了保证外层的描述域尽可能简洁、安全,一般不建议在这个域使用接口和模块操作api,因此大部分模块接口只能再脚本域使用,来实现复杂功能。 +当然少部分只读的内置接口还是可以在描述域使用的,具体见下表: +
+ +| 接口 | 描述 | 可使用域 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------------------------- | -------- | +| [val](#val) | 获取内置变量的值 | 脚本域 | >= 2.1.5 | +| [import](#import) | 导入扩展摸块 | 脚本域 | >= 2.0.1 | +| [inherit](#inherit) | 导入并继承基类模块 | 脚本域 | >= 2.0.1 | +| [ifelse](#ifelse) | 类似三元条件判断 | 描述域、脚本域 | >= 2.0.1 | +| [try-catch-finally](#try-catch-finally) | 异常捕获 | 脚本域 | >= 2.0.1 | +| [pairs](#pairs) | 用于遍历字典 | 描述域、脚本域 | >= 2.0.1 | +| [ipairs](#ipairs) | 用于遍历数组 | 描述域、脚本域 | >= 2.0.1 | +| [print](#print) | 换行打印终端日志 | 描述域、脚本域 | >= 2.0.1 | +| [printf](#printf) | 无换行打印终端日志 | 脚本域 | >= 2.0.1 | +| [cprint](#cprint) | 换行彩色打印终端日志 | 脚本域 | >= 2.0.1 | +| [cprintf](#cprintf) | 无换行彩色打印终端日志 | 脚本域 | >= 2.0.1 | +| [format](#format) | 格式化字符串 | 描述域、脚本域 | >= 2.0.1 | +| [vformat](#vformat) | 格式化字符串,支持内置变量转义 | 脚本域 | >= 2.0.1 | +| [raise](#raise) | 抛出异常中断程序 | 脚本域 | >= 2.0.1 | +| [os](#os) | 系统操作模块 | 部分只读操作描述域、脚本域 | >= 2.0.1 | +| [io](#io) | 文件操作模块 | 脚本域 | >= 2.0.1 | +| [path](#path) | 路径操作模块 | 描述域、脚本域 | >= 2.0.1 | +| [table](#table) | 数组和字典操作模块 | 描述域、脚本域 | >= 2.0.1 | +| [string](#string) | 字符串操作模块 | 描述域、脚本域 | >= 2.0.1 | +| [process](#process) | 进程操作模块 | 脚本域 | >= 2.0.1 | +| [coroutine](#coroutine) | 协程操作模块 | 脚本域 | >= 2.0.1 | + +在描述域使用接口调用的实例如下,一般仅用于条件控制: + +```lua +-- 扫描当前xmake.lua目录下的所有子目录,以每个目录的名字定义一个task任务 +for _, taskname in ipairs(os.dirs("*"), path.basename) do + task(taskname) + on_run(function () + end) +end +``` + +上面所说的脚本域、描述域主要是指: + +```lua +-- 描述域 +target("test") + + -- 描述域 + set_kind("static") + add_files("src/*.c") + + on_run(function (target) + -- 脚本域 + end) + +-- 描述域 +``` + +##### val + +###### 获取内置变量的值 + +[内置变量](#内置变量)可以通过此接口直接获取,而不需要再加`$()`的包裹,使用更加简单,例如: + +```lua +print(val("host")) +print(val("env PATH")) +local s = val("shell echo hello") +``` + +而用[vformat](#vformat)就比较繁琐了: + +```lua +local s = vformat("$(shell echo hello)") +``` + +不过`vformat`支持字符串参数格式化,更加强大, 所以应用场景不同。 + +##### import + +###### 导入扩展摸块 + +import的主要用于导入xmake的扩展类库以及一些自定义的类库模块,一般用于: + +* 自定义脚本([on_build](#targeton_build), [on_run](#targeton_run) ..) +* 插件开发 +* 模板开发 +* 平台扩展 +* 自定义任务task + +导入机制如下: + +1. 优先从当前脚本目录下导入 +2. 再从扩展类库中导入 + +导入的语法规则: + +基于`.`的类库路径规则,例如: + +导入core核心扩展模块 + +```lua +import("core.base.option") +import("core.project") +import("core.base.task") -- 2.1.5 以前是 core.project.task +import("core") + +function main() + + -- 获取参数选项 + print(option.get("version")) + + -- 运行任务和插件 + task.run("hello") + project.task.run("hello") + core.base.task.run("hello") +end +``` + +导入当前目录下的自定义模块: + +目录结构: + +``` +plugin + - xmake.lua + - main.lua + - modules + - hello1.lua + - hello2.lua +``` + +在main.lua中导入modules + +```lua +import("modules.hello1") +import("modules.hello2") +``` + +导入后就可以直接使用里面的所有公有接口,私有接口用`_`前缀标示,表明不会被导出,不会被外部调用到。。 + +除了当前目录,我们还可以导入其他指定目录里面的类库,例如: + +```lua +import("hello3", {rootdir = "/home/xxx/modules"}) +``` + +为了防止命名冲突,导入后还可以指定的别名: + +```lua +import("core.platform.platform", {alias = "p"}) + +function main() + + -- 这样我们就可以使用p来调用platform模块的plats接口,获取所有xmake支持的平台列表了 + table.dump(p.plats()) +end +``` + +import不仅可以导入类库,还支持导入的同时作为继承导入,实现模块间的继承关系 + +```lua +import("xxx.xxx", {inherit = true}) +``` + +这样导入的不是这个模块的引用,而是导入的这个模块的所有公有接口本身,这样就会跟当前模块的接口进行合并,实现模块间的继承。 + +2.1.5版本新增两个新属性:`import("xxx.xxx", {try = true, anonymous = true})` + +try为true,则导入的模块不存在的话,仅仅返回nil,并不会抛异常后中断xmake. +anonymous为true,则导入的模块不会引入当前作用域,仅仅在import接口返回导入的对象引用。 + +##### inherit + +###### 导入并继承基类模块 + +这个等价于[import](#import)接口的`inherit`模式,也就是: + +```lua +import("xxx.xxx", {inherit = true}) +``` + +用`inherit`接口的话,会更简洁些: + +```lu +inherit("xxx.xxx") +``` + +使用实例,可以参看xmake的tools目录下的脚本:[clang.lua](#https://github.com/tboox/xmake/blob/master/xmake/tools/clang.lua) + +这个就是clang工具模块继承了gcc的部分实现。 + +##### ifelse + +###### 类似三元条件判断 + +由于lua没有内置的三元运算符,通过封装`ifelse`接口,实现更加简洁的条件选择: + +```lua +local ok = ifelse(a == 0, "ok", "no") +``` + +##### try-catch-finally + +###### 异常捕获 + +lua原生并没有提供try-catch的语法来捕获异常处理,但是提供了`pcall/xpcall`等接口,可在保护模式下执行lua函数。 + +因此,可以通过封装这两个接口,来实现try-catch块的捕获机制。 + +我们可以先来看下,封装后的try-catch使用方式: + +```lua +try +{ + -- try 代码块 + function () + error("error message") + end, + + -- catch 代码块 + catch + { + -- 发生异常后,被执行 + function (errors) + print(errors) + end + } +} +``` + +上面的代码中,在try块内部认为引发了一个异常,并且抛出错误消息,在catch中进行了捕获,并且将错误消息进行输出显示。 + +而finally的处理,这个的作用是对于`try{}`代码块,不管是否执行成功,都会执行到finally块中 + +也就说,其实上面的实现,完整的支持语法是:`try-catch-finally`模式,其中catch和finally都是可选的,根据自己的实际需求提供 + +例如: + +```lua +try +{ + -- try 代码块 + function () + error("error message") + end, + + -- catch 代码块 + catch + { + -- 发生异常后,被执行 + function (errors) + print(errors) + end + }, + + -- finally 代码块 + finally + { + -- 最后都会执行到这里 + function (ok, errors) + -- 如果try{}中存在异常,ok为true,errors为错误信息,否则为false,errors为try中的返回值 + end + } +} + +``` + +或者只有finally块: + +```lua +try +{ + -- try 代码块 + function () + return "info" + end, + + -- finally 代码块 + finally + { + -- 由于此try代码没发生异常,因此ok为true,errors为返回值: "info" + function (ok, errors) + end + } +} +``` + +处理可以在finally中获取try里面的正常返回值,其实在仅有try的情况下,也是可以获取返回值的: + +```lua +-- 如果没发生异常,result 为返回值:"xxxx",否则为nil +local result = try +{ + function () + return "xxxx" + end +} +``` + +在xmake的自定义脚本、插件开发中,也是完全基于此异常捕获机制 + +这样使得扩展脚本的开发非常的精简可读,省去了繁琐的`if err ~= nil then`返回值判断,在发生错误时,xmake会直接抛出异常进行中断,然后高亮提示详细的错误信息。 + +例如: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + + -- 在编译完ios程序后,对目标程序进行ldid签名 + after_build(function (target)) + os.run("ldid -S %s", target:targetfile()) + end +``` + +只需要一行`os.run`就行了,也不需要返回值判断是否运行成功,因为运行失败后,xmake会自动抛异常,中断程序并且提示错误 + +如果你想在运行失败后,不直接中断xmake,继续往下运行,可以自己加个try快就行了: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + + after_build(function (target)) + try + { + function () + os.run("ldid -S %s", target:targetfile()) + end + } + end +``` + +如果还想捕获出错信息,可以再加个catch: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + + after_build(function (target)) + try + { + function () + os.run("ldid -S %s", target:targetfile()) + end, + catch + { + function (errors) + print(errors) + end + } + } + end +``` + +不过一般情况下,在xmake中写自定义脚本,是不需要手动加try-catch的,直接调用各种api,出错后让xmake默认的处理程序接管,直接中断就行了。。 + +##### pairs + +###### 用于遍历字典 + +这个是lua原生的内置api,在xmake中,在原有的行为上对其进行了一些扩展,来简化一些日常的lua遍历代码。 + +先看下默认的原生写法: + +```lua +local t = {a = "a", b = "b", c = "c", d = "d", e = "e", f = "f"} + +for key, val in pairs(t) do + print("%s: %s", key, val) +end +``` + +这对于通常的遍历操作就足够了,但是如果我们相对其中每个遍历出来的元素,获取其大写,我们可以这么写: + +```lua +for key, val in pairs(t, function (v) return v:upper() end) do + print("%s: %s", key, val) +end +``` + +甚至传入一些参数到第二个`function`中,例如: + +```lua +for key, val in pairs(t, function (v, a, b) return v:upper() .. a .. b end, "a", "b") do + print("%s: %s", key, val) +end +``` + +##### ipairs + +###### 用于遍历数组 + +这个是lua原生的内置api,在xmake中,在原有的行为上对其进行了一些扩展,来简化一些日常的lua遍历代码。 + +先看下默认的原生写法: + +```lua +for idx, val in ipairs({"a", "b", "c", "d", "e", "f"}) do + print("%d %s", idx, val) +end +``` + +扩展写法类似[pairs](#pairs)接口,例如: + +```lua +for idx, val in ipairs({"a", "b", "c", "d", "e", "f"}, function (v) return v:upper() end) do + print("%d %s", idx, val) +end + +for idx, val in ipairs({"a", "b", "c", "d", "e", "f"}, function (v, a, b) return v:upper() .. a .. b end, "a", "b") do + print("%d %s", idx, val) +end +``` + +这样可以简化`for`块代码的逻辑,例如我要遍历指定目录,获取其中的文件名,但不包括路径,就可以通过这种扩展方式,简化写法: + +```lua +for _, filename in ipairs(os.dirs("*"), path.filename) do + -- ... +end +``` + +##### print + +###### 换行打印终端日志 + +此接口也是lua的原生接口,xmake在原有行为不变的基础上也进行了扩展,同时支持:格式化输出、多变量输出。 + +先看下原生支持的方式: + +```lua +print("hello xmake!") +print("hello", "xmake!", 123) +``` + +并且同时还支持扩展的格式化写法: + +```lua +print("hello %s!", "xmake") +print("hello xmake! %d", 123) +``` + +xmake会同时支持这两种写法,内部会去自动智能检测,选择输出行为。 + +##### printf + +###### 无换行打印终端日志 + +类似[print](#print)接口,唯一的区别就是不换行。 + +##### cprint + +###### 换行彩色打印终端日志 + +行为类似[print](#print),区别就是此接口还支持彩色终端输出,并且支持`emoji`字符输出。 + +例如: + +```lua + cprint('${bright}hello xmake') + cprint('${red}hello xmake') + cprint('${bright green}hello ${clear}xmake') + cprint('${blue onyellow underline}hello xmake${clear}') + cprint('${red}hello ${magenta}xmake') + cprint('${cyan}hello ${dim yellow}xmake') +``` + +显示结果如下: + + + +跟颜色相关的描述,都放置在 `${ }` 里面,可以同时设置多个不同的属性,例如: + +``` + ${bright red underline onyellow} +``` + +表示:高亮红色,背景黄色,并且带下滑线 + +所有这些描述,都会影响后面一整行字符,如果只想显示部分颜色的文字,可以在结束位置,插入`${clear}`清楚前面颜色描述 + +例如: + +``` + ${red}hello ${clear}xmake +``` + +这样的话,仅仅hello是显示红色,其他还是正常默认黑色显示。 + +其他颜色属于,我这里就不一一介绍,直接贴上xmake代码里面的属性列表吧: + +```lua + colors.keys = + { + -- 属性 + reset = 0 -- 重置属性 + , clear = 0 -- 清楚属性 + , default = 0 -- 默认属性 + , bright = 1 -- 高亮 + , dim = 2 -- 暗色 + , underline = 4 -- 下划线 + , blink = 5 -- 闪烁 + , reverse = 7 -- 反转颜色 + , hidden = 8 -- 隐藏文字 + + -- 前景色 + , black = 30 + , red = 31 + , green = 32 + , yellow = 33 + , blue = 34 + , magenta = 35 + , cyan = 36 + , white = 37 + + -- 背景色 + , onblack = 40 + , onred = 41 + , ongreen = 42 + , onyellow = 43 + , onblue = 44 + , onmagenta = 45 + , oncyan = 46 + , onwhite = 47 +``` + +除了可以色彩高亮显示外,如果你的终端是在macosx下,lion以上的系统,xmake还可以支持emoji表情的显示哦,对于不支持系统,会 +忽略显示,例如: + +```lua + cprint("hello xmake${beer}") + cprint("hello${ok_hand} xmake") +``` + +上面两行代码,我打印了一个homebrew里面经典的啤酒符号,下面那行打印了一个ok的手势符号,是不是很炫哈。。 + + + +所有的emoji表情,以及xmake里面对应的key,都可以通过[emoji符号](http://www.emoji-cheat-sheet.com/)里面找到。。 + +2.1.7版本支持24位真彩色输出,如果终端支持的话: + +```lua +import("core.base.colors") +if colors.truecolor() then + cprint("${255;0;0}hello") + cprint("${on;255;0;0}hello${clear} xmake") + cprint("${bright 255;0;0 underline}hello") + cprint("${bright on;255;0;0 0;255;0}hello${clear} xmake") +end +``` + +xmake对于truecolor的检测支持,是通过`$COLORTERM`环境变量来实现的,如果你的终端支持truecolor,可以手动设置此环境变量,来告诉xmake启用truecolor支持。 + +可以通过下面的命令来启用和测试: + +```bash +$ export COLORTERM=truecolor +$ xmake --version +``` + +##### cprintf + +###### 无换行彩色打印终端日志 + +此接口类似[cprint](#cprint),区别就是不换行输出。 + +##### format + +###### 格式化字符串 + +如果只是想格式化字符串,不进行输出,可以使用这个接口,此接口跟[string.format](#string-format)接口等价,只是个接口名简化版。 + +```lua +local s = format("hello %s", xmake) +``` + +##### vformat + +###### 格式化字符串,支持内置变量转义 + +此接口跟[format](#format)接口类似,只是增加对内置变量的获取和转义支持。 + +```lua +local s = vformat("hello %s $(mode) $(arch) $(env PATH)", xmake) +``` + +##### raise + +###### 抛出异常中断程序 + +如果想在自定义脚本、插件任务中中断xmake运行,可以使用这个接口跑出异常,如果上层没有显示调用[try-catch](#try-catch-finally)捕获的话,xmake就会中断执行,并且显示出错信息。 + +```lua +if (errors) raise(errors) +``` + +如果在try块中抛出异常,就会在catch和finally中进行errors信息捕获,具体见:[try-catch](#try-catch-finally) + +##### os + +系统操作模块,属于内置模块,无需使用[import](#import)导入,可直接脚本域调用其接口。 + +此模块也是lua的原生模块,xmake在其基础上进行了扩展,提供更多实用的接口。 + +| 接口 | 描述 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [os.cp](#os-cp) | 复制文件或目录 | >= 2.0.1 | +| [os.mv](#os-mv) | 移动重命名文件或目录 | >= 2.0.1 | +| [os.rm](#os-rm) | 删除文件或目录树 | >= 2.0.1 | +| [os.cd](#os-cd) | 进入指定目录 | >= 2.0.1 | +| [os.rmdir](#os-rmdir) | 删除目录树 | >= 2.0.1 | +| [os.mkdir](#os-mkdir) | 创建指定目录 | >= 2.0.1 | +| [os.isdir](#os-isdir) | 判断目录是否存在 | >= 2.0.1 | +| [os.isfile](#os-isfile) | 判断文件是否存在 | >= 2.0.1 | +| [os.exists](#os-exists) | 判断文件或目录是否存在 | >= 2.0.1 | +| [os.dirs](#os-dirs) | 遍历获取指定目录下的所有目录 | >= 2.0.1 | +| [os.files](#os-files) | 遍历获取指定目录下的所有文件 | >= 2.0.1 | +| [os.filedirs](#os-filedirs) | 遍历获取指定目录下的所有文件或目录 | >= 2.0.1 | +| [os.run](#os-run) | 安静运行程序 | >= 2.0.1 | +| [os.runv](#os-runv) | 安静运行程序,带参数列表 | >= 2.1.5 | +| [os.exec](#os-exec) | 回显运行程序 | >= 2.0.1 | +| [os.execv](#os-execv) | 回显运行程序,带参数列表 | >= 2.1.5 | +| [os.iorun](#os-iorun) | 运行并获取程序输出内容 | >= 2.0.1 | +| [os.iorunv](#os-iorunv) | 运行并获取程序输出内容,带参数列表 | >= 2.1.5 | +| [os.getenv](#os-getenv) | 获取环境变量 | >= 2.0.1 | +| [os.setenv](#os-setenv) | 设置环境变量 | >= 2.0.1 | +| [os.tmpdir](#os-tmpdir) | 获取临时目录路径 | >= 2.0.1 | +| [os.tmpfile](#os-tmpfile) | 获取临时文件路径 | >= 2.0.1 | +| [os.curdir](#os-curdir) | 获取当前目录路径 | >= 2.0.1 | +| [os.scriptdir](#os-scriptdir) | 获取脚本目录路径 | >= 2.0.1 | +| [os.programdir](#os-programdir) | 获取xmake安装主程序脚本目录 | >= 2.1.5 | +| [os.projectdir](#os-projectdir) | 获取工程主目录 | >= 2.1.5 | +| [os.arch](#os-arch) | 获取当前系统架构 | >= 2.0.1 | +| [os.host](#os-host) | 获取当前主机系统 | >= 2.0.1 | + +###### os.cp + +- 复制文件或目录 + +行为和shell中的`cp`命令类似,支持路径通配符匹配(使用的是lua模式匹配),支持多文件复制,以及内置变量支持。 + +例如: + +```lua +os.cp("$(scriptdir)/*.h", "$(projectdir)/src/test/**.h", "$(buildir)/inc") +``` + +上面的代码将:当前`xmake.lua`目录下的所有头文件、工程源码test目录下的头文件全部复制到`$(buildir)`输出目录中。 + +其中`$(scriptdir)`, `$(projectdir)` 这些变量是xmake的内置变量,具体详情见:[内置变量](#内置变量)的相关文档。 + +而`*.h`和`**.h`中的匹配模式,跟[add_files](#targetadd_files)中的类似,前者是单级目录匹配,后者是递归多级目录匹配。 + +此接口同时支持目录的`递归复制`,例如: + +```lua +-- 递归复制当前目录到临时目录 +os.cp("$(curdir)/test/", "$(tmpdir)/test") +``` + ++尽量使用`os.cp`接口,而不是`os.run("cp ..")`,这样更能保证平台一致性,实现跨平台构建描述。 +
+ +###### os.mv + +- 移动重命名文件或目录 + +跟[os.cp](#os-cp)的使用类似,同样支持多文件移动操作和模式匹配,例如: + +```lua +-- 移动多个文件到临时目录 +os.mv("$(buildir)/test1", "$(buildir)/test2", "$(tmpdir)") + +-- 文件移动不支持批量操作,也就是文件重命名 +os.mv("$(buildir)/libtest.a", "$(buildir)/libdemo.a") +``` + +###### os.rm + +- 删除文件或目录树 + +支持递归删除目录,批量删除操作,以及模式匹配和内置变量,例如: + +```lua +os.rm("$(buildir)/inc/**.h", "$(buildir)/lib/") +``` + +###### os.cd + +- 进入指定目录 + +这个操作用于目录切换,同样也支持内置变量,但是不支持模式匹配和多目录处理,例如: + +```lua +-- 进入临时目录 +os.cd("$(tmpdir)") +``` + +如果要离开进入之前的目录,有多种方式: + +```lua +-- 进入上级目录 +os.cd("..") + +-- 进入先前的目录,相当于:cd - +os.cd("-") + +-- 进入目录前保存之前的目录,用于之后跨级直接切回 +local oldir = os.cd("./src") +... +os.cd(oldir) +``` + +###### os.rmdir + +- 仅删除目录 + +如果不是目录就无法删除。 + +###### os.mkdir + +- 创建目录 + +支持批量创建和内置变量,例如: + +```lua +os.mkdir("$(tmpdir)/test", "$(buildir)/inc") +``` + +###### os.isdir + +- 判断是否为目录 + +如果目录不存在,则返回false + +```lua +if os.isdir("src") then + -- ... +end +``` + +###### os.isfile + +- 判断是否为文件 + +如果文件不存在,则返回false + +```lua +if os.isfile("$(buildir)/libxxx.a") then + -- ... +end +``` + +###### os.exists + +- 判断文件或目录是否存在 + +如果文件或目录不存在,则返回false + +```lua +-- 判断目录存在 +if os.exists("$(buildir)") then + -- ... +end + +-- 判断文件存在 +if os.exists("$(buildir)/libxxx.a") then + -- ... +end +``` + +###### os.dirs + +- 遍历获取指定目录下的所有目录 + +支持[add_files](#targetadd_files)中的模式匹配,支持递归和非递归模式遍历,返回的结果是一个table数组,如果获取不到,返回空数组,例如: + +```lua +-- 递归遍历获取所有子目录 +for _, dir in ipairs(os.dirs("$(buildir)/inc/**")) do + print(dir) +end +``` + +###### os.files + +- 遍历获取指定目录下的所有文件 + +支持[add_files](#targetadd_files)中的模式匹配,支持递归和非递归模式遍历,返回的结果是一个table数组,如果获取不到,返回空数组,例如: + +```lua +-- 非递归遍历获取所有子文件 +for _, filepath in ipairs(os.files("$(buildir)/inc/*.h")) do + print(filepath) +end +``` + +###### os.filedirs + +- 遍历获取指定目录下的所有文件和目录 + +支持[add_files](#targetadd_files)中的模式匹配,支持递归和非递归模式遍历,返回的结果是一个table数组,如果获取不到,返回空数组,例如: + +```lua +-- 递归遍历获取所有子文件和目录 +for _, filedir in ipairs(os.filedirs("$(buildir)/**")) do + print(filedir) +end +``` + +###### os.run + +- 安静运行原生shell命令 + +用于执行第三方的shell命令,但不会回显输出,仅仅在出错后,高亮输出错误信息。 + +此接口支持参数格式化、内置变量,例如: + +```lua +-- 格式化参数传入 +os.run("echo hello %s!", "xmake") + +-- 列举构建目录文件 +os.run("ls -l $(buildir)") +``` + +
+使用此接口执行shell命令,容易使构建跨平台性降低,对于`os.run("cp ..")`这种尽量使用`os.cp`代替。
+如果必须使用此接口运行shell程序,请自行使用[config.plat](#config-plat)接口判断平台支持。
+
+2.1.5版本之前为`core.project.global`。 +
+ +###### global.get + +- 获取指定配置值 + +类似[config.get](#config-get),唯一的区别就是这个是从全局配置中获取。 + +###### global.load + +- 加载配置 + +类似[global.get](#global-get),唯一的区别就是这个是从全局配置中加载。 + +###### global.directory + +- 获取全局配置信息目录 + +默认为`~/.config`目录。 + +###### global.dump + +- 打印输出所有全局配置信息 + +输出结果如下: + +```lua +{ + clean = true +, ccache = "ccache" +, xcode_dir = "/Applications/Xcode.app" +} +``` + +##### core.base.task + +用于任务操作,一般用于在自定义脚本中、插件任务中,调用运行其他task任务。 + +| 接口 | 描述 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [task.run](#task-run) | 运行指定任务 | >= 2.0.1 | + ++2.1.5版本之前为`core.project.task`。 +
+ +###### task.run + +- 运行指定任务 + +用于在自定义脚本、插件任务中运行[task](#task)定义的任务或插件,例如: + +```lua +task("hello") + on_run(function () + print("hello xmake!") + end) + +target("demo") + on_clean(function(target) + + -- 导入task模块 + import("core.base.task") + + -- 运行这个hello task + task.run("hello") + end) +``` + +我们还可以在运行任务时,增加参数传递,例如: + +```lua +task("hello") + on_run(function (arg1, arg2) + print("hello xmake: %s %s!", arg1, arg2) + end) + +target("demo") + on_clean(function(target) + + -- 导入task + import("core.base.task") + + -- {} 这个是给第一种选项传参使用,这里置空,这里在最后面传入了两个参数:arg1, arg2 + task.run("hello", {}, "arg1", "arg2") + end) +``` + +对于`task.run`的第二个参数,用于传递命令行菜单中的选项,而不是直接传入`function (arg, ...)`函数入口中,例如: + +```lua +-- 导入task +import("core.base.task") + +-- 插件入口 +function main(...) + + -- 运行内置的xmake配置任务,相当于:xmake f|config --plat=iphoneos --arch=armv7 + task.run("config", {plat="iphoneos", arch="armv7"}) +emd +``` + +##### core.tool.linker + +链接器相关操作,常用于插件开发。 + +| 接口 | 描述 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [linker.link](#linker-link) | 执行链接 | >= 2.0.1 | +| [linker.linkcmd](#linker-linkcmd) | 获取链接命令行 | >= 2.0.1 | +| [linker.linkargv](#linker-linkargv) | 获取链接命令行列表 | >= 2.1.5 | +| [linker.linkflags](#linker-linkflags) | 获取链接选项 | >= 2.0.1 | +| [linker.has_flags](#linker-has_flags) | 判断指定链接选项是否支持 | >= 2.1.5 | + +###### linker.link + +- 执行链接 + +针对target,链接指定对象文件列表,生成对应的目标文件,例如: + +```lua +linker.link({"a.o", "b.o", "c.o"}, target:targetfile(), {target = target}) +``` + +其中[target](#target),为工程目标,这里传入,主要用于获取target特定的链接选项,具体如果获取工程目标对象,见:[core.project.project](#core-project-project) + +当然也可以不指定target,例如: + +```lua +linker.link("binary", "cc", {"a.o", "b.o", "c.o"}, "/tmp/targetfile") +``` + +###### linker.linkcmd + +- 获取链接命令行字符串 + +直接获取[linker.link](#linker-link)中执行的命令行字符串,相当于: + +```lua +local cmdstr = linker.linkcmd("static", "cxx", {"a.o", "b.o", "c.o"}, target:targetfile(), {target = target}) +``` + +注:后面`{target = target}`扩展参数部分是可选的,如果传递了target对象,那么生成的链接命令,会加上这个target配置对应的链接选项。 + +并且还可以自己传递各种配置,例如: + +```lua +local cmdstr = linker.linkcmd("static", "cxx", {"a.o", "b.o", "c.o"}, target:targetfile(), {config = {linkdirs = "/usr/lib"}}) +``` + +###### linker.linkargv + +- 获取链接命令行参数列表 + +跟[linker.linkcmd](#linker-linkcmd)稍微有点区别的是,此接口返回的是参数列表,table表示,更加方便操作: + +```lua +local program, argv = linker.linkargv("static", "cxx", {"a.o", "b.o", "c.o"}, target:targetfile(), {target = target}) +``` + +其中返回的第一个值是主程序名,后面是参数列表,而`os.args(table.join(program, argv))`等价于`linker.linkcmd`。 + +我们也可以通过传入返回值给[os.runv](#os-runv)来直接运行它:`os.runv(linker.linkargv(..))` + +###### linker.linkflags + +- 获取链接选项 + +获取[linker.linkcmd](#linker-linkcmd)中的链接选项字符串部分,不带shellname和对象文件列表,并且是按数组返回,例如: + +```lua +local flags = linker.linkflags("shared", "cc", {target = target}) +for _, flag in ipairs(flags) do + print(flag) +end +``` + +返回的是flags的列表数组。 + +###### linker.has_flags + +- 判断指定链接选项是否支持 + +虽然通过[lib.detect.has_flags](detect-has_flags)也能判断,但是那个接口更加底层,需要指定链接器名称 +而此接口只需要指定target的目标类型,源文件类型,它会自动切换选择当前支持的链接器。 + +```lua +if linker.has_flags(target:targetkind(), target:sourcekinds(), "-L/usr/lib -lpthread") then + -- ok +end +``` + +##### core.tool.compiler + +编译器相关操作,常用于插件开发。 + +| 接口 | 描述 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [compiler.compile](#compiler-compile) | 执行编译 | >= 2.0.1 | +| [compiler.compcmd](#compiler-compcmd) | 获取编译命令行 | >= 2.0.1 | +| [compiler.compargv](#compiler-compargv) | 获取编译命令行列表 | >= 2.1.5 | +| [compiler.compflags](#compiler-compflags) | 获取编译选项 | >= 2.0.1 | +| [compiler.has_flags](#compiler-has_flags) | 判断指定编译选项是否支持 | >= 2.1.5 | +| [compiler.features](#compiler-features) | 获取所有编译器特性 | >= 2.1.5 | +| [compiler.has_features](#compiler-has_features) | 判断指定编译特性是否支持 | >= 2.1.5 | + +###### compiler.compile + +- 执行编译 + +针对target,链接指定对象文件列表,生成对应的目标文件,例如: + +```lua +compiler.compile("xxx.c", "xxx.o", "xxx.h.d", {target = target}) +``` + +其中[target](#target),为工程目标,这里传入主要用于获取taeget的特定编译选项,具体如果获取工程目标对象,见:[core.project.project](#core-project-project) + +而`xxx.h.d`文件用于存储为此源文件的头文件依赖文件列表,最后这两个参数都是可选的,编译的时候可以不传他们: + +```lua +compiler.compile("xxx.c", "xxx.o") +``` + +来单纯编译一个源文件。 + +###### compiler.compcmd + +- 获取编译命令行 + +直接获取[compiler.compile](#compiler-compile)中执行的命令行字符串,相当于: + +```lua +local cmdstr = compiler.compcmd("xxx.c", "xxx.o", {target = target}) +``` + +注:后面`{target = target}`扩展参数部分是可选的,如果传递了target对象,那么生成的编译命令,会加上这个target配置对应的链接选项。 + +并且还可以自己传递各种配置,例如: + +```lua +local cmdstr = compiler.compcmd("xxx.c", "xxx.o", {config = {includedirs = "/usr/include", defines = "DEBUG"}}) +``` + +通过target,我们可以导出指定目标的所有源文件编译命令: + +```lua +import("core.project.project") + +for _, target in pairs(project.targets()) do + for sourcekind, sourcebatch in pairs(target:sourcebatches()) do + for index, objectfile in ipairs(sourcebatch.objectfiles) do + local cmdstr = compiler.compcmd(sourcebatch.sourcefiles[index], objectfile, {target = target}) + end + end +end +``` + +###### compiler.compargv + +- 获取编译命令行列表 + +跟[compiler.compargv](#compiler-compargv)稍微有点区别的是,此接口返回的是参数列表,table表示,更加方便操作: + +```lua +local program, argv = compiler.compargv("xxx.c", "xxx.o") +``` + +###### compiler.compflags + +- 获取编译选项 + +获取[compiler.compcmd](#compiler-compcmd)中的编译选项字符串部分,不带shellname和文件列表,例如: + +```lua +local flags = compiler.compflags(sourcefile, {target = target}) +for _, flag in ipairs(flags) do + print(flag) +end +``` + +返回的是flags的列表数组。 + +###### compiler.has_flags + +- 判断指定编译选项是否支持 + +虽然通过[lib.detect.has_flags](detect-has_flags)也能判断,但是那个接口更加底层,需要指定编译器名称。 +而此接口只需要指定语言类型,它会自动切换选择当前支持的编译器。 + +```lua +-- 判断c语言编译器是否支持选项: -g +if compiler.has_flags("c", "-g") then + -- ok +end + +-- 判断c++语言编译器是否支持选项: -g +if compiler.has_flags("cxx", "-g") then + -- ok +end +``` + +###### compiler.features + +- 获取所有编译器特性 + +虽然通过[lib.detect.features](detect-features)也能获取,但是那个接口更加底层,需要指定编译器名称。 +而此接口只需要指定语言类型,它会自动切换选择当前支持的编译器,然后获取当前的编译器特性列表。 + +```lua +-- 获取当前c语言编译器的所有特性 +local features = compiler.features("c") + +-- 获取当前c++语言编译器的所有特性,启用c++11标准,否则获取不到新标准的特性 +local features = compiler.features("cxx", {config = {cxxflags = "-std=c++11"}}) + +-- 获取当前c++语言编译器的所有特性,传递工程target的所有配置信息 +local features = compiler.features("cxx", {target = target, config = {defines = "..", includedirs = ".."}}) +``` + +所有c编译器特性列表: + +| 特性名 | +| --------------------- | +| c_static_assert | +| c_restrict | +| c_variadic_macros | +| c_function_prototypes | + +所有c++编译器特性列表: + +| 特性名 | +| ------------------------------------ | +| cxx_variable_templates | +| cxx_relaxed_constexpr | +| cxx_aggregate_default_initializers | +| cxx_contextual_conversions | +| cxx_attribute_deprecated | +| cxx_decltype_auto | +| cxx_digit_separators | +| cxx_generic_lambdas | +| cxx_lambda_init_captures | +| cxx_binary_literals | +| cxx_return_type_deduction | +| cxx_decltype_incomplete_return_types | +| cxx_reference_qualified_functions | +| cxx_alignof | +| cxx_attributes | +| cxx_inheriting_constructors | +| cxx_thread_local | +| cxx_alias_templates | +| cxx_delegating_constructors | +| cxx_extended_friend_declarations | +| cxx_final | +| cxx_nonstatic_member_init | +| cxx_override | +| cxx_user_literals | +| cxx_constexpr | +| cxx_defaulted_move_initializers | +| cxx_enum_forward_declarations | +| cxx_noexcept | +| cxx_nullptr | +| cxx_range_for | +| cxx_unrestricted_unions | +| cxx_explicit_conversions | +| cxx_lambdas | +| cxx_local_type_template_args | +| cxx_raw_string_literals | +| cxx_auto_type | +| cxx_defaulted_functions | +| cxx_deleted_functions | +| cxx_generalized_initializers | +| cxx_inline_namespaces | +| cxx_sizeof_member | +| cxx_strong_enums | +| cxx_trailing_return_types | +| cxx_unicode_literals | +| cxx_uniform_initialization | +| cxx_variadic_templates | +| cxx_decltype | +| cxx_default_function_template_args | +| cxx_long_long_type | +| cxx_right_angle_brackets | +| cxx_rvalue_references | +| cxx_static_assert | +| cxx_extern_templates | +| cxx_func_identifier | +| cxx_variadic_macros | +| cxx_template_template_parameters | + +###### compiler.has_features + +- 判断指定的编译器特性是否支持 + +虽然通过[lib.detect.has_features](detect-has-features)也能获取,但是那个接口更加底层,需要指定编译器名称。 +而此接口只需要指定需要检测的特姓名称列表,就能自动切换选择当前支持的编译器,然后判断指定特性在当前的编译器中是否支持。 + +```lua +if compiler.has_features("c_static_assert") then + -- ok +end + +if compiler.has_features({"c_static_assert", "cxx_constexpr"}, {languages = "cxx11"}) then + -- ok +end + +if compiler.has_features("cxx_constexpr", {target = target, defines = "..", includedirs = ".."}) then + -- ok +end +``` + +具体特性名有哪些,可以参考:[compiler.features](#compiler-features)。 + +##### core.project.config + +用于获取工程编译时候的配置信息,也就是`xmake f|config --xxx=val` 传入的参数选项值。 + +| 接口 | 描述 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [config.get](#config-get) | 获取指定配置值 | >= 2.0.1 | +| [config.load](#config-load) | 加载配置 | >= 2.0.1 | +| [config.arch](#config-arch) | 获取当前工程的架构配置 | >= 2.0.1 | +| [config.plat](#config-plat) | 获取当前工程的平台配置 | >= 2.0.1 | +| [config.mode](#config-mode) | 获取当前工程的编译模式配置 | >= 2.0.1 | +| [config.buildir](#config-buildir) | 获取当前工程的输出目录配置 | >= 2.0.1 | +| [config.directory](#config-directory) | 获取当前工程的配置信息目录 | >= 2.0.1 | +| [config.dump](#config-dump) | 打印输出当前工程的所有配置信息 | >= 2.0.1 | + +###### config.get + +- 获取指定配置值 + +用于获取`xmake f|config --xxx=val`的配置值,例如: + +```lua +target("test") + on_run(function (target) + + -- 导入配置模块 + import("core.project.config") + + -- 获取配置值 + print(config.get("xxx")) + end) +``` + +###### config.load + +- 加载配置 + +一般用于插件开发中,插件任务中不像工程的自定义脚本,环境需要自己初始化加载,默认工程配置是没有被加载的,如果要用[config.get](#config-get)接口获取工程配置,那么需要先: + +```lua + +-- 导入配置模块 +import("core.project.config") + +function main(...) + + -- 先加载工程配置 + config.load() + + -- 获取配置值 + print(config.get("xxx")) +end +``` + +###### config.arch + +- 获取当前工程的架构配置 + +也就是获取`xmake f|config --arch=armv7`的平台配置,相当于`config.get("arch")`。 + +###### config.plat + +- 获取当前工程的平台配置 + +也就是获取`xmake f|config --plat=iphoneos`的平台配置,相当于`config.get("plat")`。 + +###### config.mode + +- 获取当前工程的编译模式配置 + +也就是获取`xmake f|config --mode=debug`的平台配置,相当于`config.get("mode")`。 + +###### config.buildir + +- 获取当前工程的输出目录配置 + +也就是获取`xmake f|config -o /tmp/output`的平台配置,相当于`config.get("buildir")`。 + +###### config.directory + +- 获取当前工程的配置信息目录 + +获取工程配置的存储目录,默认为:`projectdir/.config` + +###### config.dump + +- 打印输出当前工程的所有配置信息 + +输出结果例如: + +```lua +{ + sh = "xcrun -sdk macosx clang++" +, xcode_dir = "/Applications/Xcode.app" +, ar = "xcrun -sdk macosx ar" +, small = true +, object = false +, arch = "x86_64" +, xcode_sdkver = "10.12" +, ex = "xcrun -sdk macosx ar" +, cc = "xcrun -sdk macosx clang" +, rc = "rustc" +, plat = "macosx" +, micro = false +, host = "macosx" +, as = "xcrun -sdk macosx clang" +, dc = "dmd" +, gc = "go" +, openssl = false +, ccache = "ccache" +, cxx = "xcrun -sdk macosx clang" +, sc = "xcrun -sdk macosx swiftc" +, mm = "xcrun -sdk macosx clang" +, buildir = "build" +, mxx = "xcrun -sdk macosx clang++" +, ld = "xcrun -sdk macosx clang++" +, mode = "release" +, kind = "static" +} +``` + +##### core.project.global + ++此模块自2.1.5版本后迁移至[core.base.global](#core-base-global)。 +
+ +##### core.project.task + ++此模块自2.1.5版本后迁移至[core.base.task](#core-base-task)。 +
+ +##### core.project.project + +用于获取当前工程的一些描述信息,也就是在`xmake.lua`工程描述文件中定义的配置信息,例如:[target](#target)、[option](#option)等。 + +| 接口 | 描述 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------------------- | +| [project.load](#project-load) | 加载工程配置 | >= 2.0.1 (2.1.5废弃) | +| [project.directory](#project-directory) | 获取工程目录 | >= 2.0.1 | +| [project.target](#project-target) | 获取指定工程目标对象 | >= 2.0.1 | +| [project.targets](#project-targets) | 获取工程目标对象列表 | >= 2.0.1 | +| [project.option](#project-option) | 获取指定的选项对象 | >= 2.1.5 | +| [project.options](#project-options) | 获取工程所有的选项对象 | >= 2.1.5 | +| [project.name](#project-name) | 获取当前工程名 | >= 2.0.1 | +| [project.version](#project-version) | 获取当前工程版本号 | >= 2.0.1 | + +###### project.load + +- 加载工程描述配置 + +仅在插件中使用,因为这个时候还没有加载工程配置信息,在工程目标的自定义脚本中,不需要执行此操作,就可以直接访问工程配置。 + +```lua +-- 导入工程模块 +import("core.project.project") + +-- 插件入口 +function main(...) + + -- 加载工程描述配置 + project.load() + + -- 访问工程描述,例如获取指定工程目标 + local target = project.target("test") +end +``` + ++2.1.5版本后,不在需要,工程加载会自动在合适时机延迟加载。 +
+ +###### project.directory + +- 获取工程目录 + +获取当前工程目录,也就是`xmake -P xxx`中指定的目录,否则为默认当前`xmake`命令执行目录。 + ++2.1.5版本后,建议使用[os.projectdir](#os-projectdir)来获取。 +
+ +###### project.target + +- 获取指定工程目标对象 + +获取和访问指定工程目标配置,例如: + +```lua +local target = project.target("test") +if target then + + -- 获取目标文件名 + print(target:targetfile()) + + -- 获取目标类型,也就是:binary, static, shared + print(target:targetkind()) + + -- 获取目标名 + print(target:name()) + + -- 获取目标源文件 + local sourcefiles = target:sourcefiles() + + -- 获取目标安装头文件列表 + local srcheaders, dstheaders = target:headerfiles() + + -- 获取目标依赖 + print(target:get("deps")) +end +``` + +###### project.targets + +- 获取工程目标对象列表 + +返回当前工程的所有编译目标,例如: + +```lua +for targetname, target in pairs(project.targets()) + print(target:targetfile()) +end +``` + +###### project.option + +- 获取指定选项对象 + +获取和访问工程中指定的选项对象,例如: + +```lua +local option = project.option("test") +if option:enabled() then + option:enable(false) +end +``` + +###### project.options + +- 获取工程所有选项对象 + +返回当前工程的所有编译目标,例如: + +```lua +for optionname, option in pairs(project.options()) + print(option:enabled()) +end +``` + +###### project.name + +- 获取当前工程名 + +也就是获取[set_project](#set_project)的工程名配置。 + +```lua +print(project.name()) +``` + +###### project.version + +- 获取当前工程版本号 + +也就是获取[set_version](#set_version)的工程版本配置。 + +```lua +print(project.version()) +``` + +##### core.language.language + +用于获取编译语言相关信息,一般用于代码文件的操作。 + +| 接口 | 描述 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [language.extensions](#language-extensions) | 获取所有语言的代码后缀名列表 | >= 2.1.1 | +| [language.targetkinds](#language-targetkinds) | 获取所有语言的目标类型列表 | >= 2.1.1 | +| [language.sourcekinds](#language-sourcekinds) | 获取所有语言的源文件类型列表 | >= 2.1.1 | +| [language.sourceflags](#language-sourceflags) | 加载所有语言的源文件编译选项名列表 | >= 2.1.1 | +| [language.load](#language-load) | 加载指定语言 | >= 2.1.1 | +| [language.load_sk](#language-load_sk) | 从源文件类型加载指定语言 | >= 2.1.1 | +| [language.load_ex](#language-load_ex) | 从源文件后缀名加载指定语言 | >= 2.1.1 | +| [language.sourcekind_of](#language-sourcekind_of) | 获取指定源文件的源文件类型 | >= 2.1.1 | + +###### language.extensions + +- 获取所有语言的代码后缀名列表 + +获取结果如下: + +```lua +{ + [".c"] = cc +, [".cc"] = cxx +, [".cpp"] = cxx +, [".m"] = mm +, [".mm"] = mxx +, [".swift"] = sc +, [".go"] = gc +} +``` + +###### language.targetkinds + +- 获取所有语言的目标类型列表 + +获取结果如下: + +```lua +{ + binary = {"ld", "gc-ld", "dc-ld"} +, static = {"ar", "gc-ar", "dc-ar"} +, shared = {"sh", "dc-sh"} +} +``` + +###### language.sourcekinds + +- 获取所有语言的源文件类型列表 + +获取结果如下: + +```lua +{ + cc = ".c" +, cxx = {".cc", ".cpp", ".cxx"} +, mm = ".m" +, mxx = ".mm" +, sc = ".swift" +, gc = ".go" +, rc = ".rs" +, dc = ".d" +, as = {".s", ".S", ".asm"} +} +``` + +###### language.sourceflags + +- 加载所有语言的源文件编译选项名列表 + +获取结果如下: + +```lua +{ + cc = {"cflags", "cxflags"} +, cxx = {"cxxflags", "cxflags"} +, ... +} +``` + +###### language.load + +- 加载指定语言 + +从语言名称加载具体语言对象,例如: + +```lua +local lang = language.load("c++") +if lang then + print(lang:name()) +end +``` + +###### language.load_sk + +- 从源文件类型加载指定语言 + +从源文件类型:`cc, cxx, mm, mxx, sc, gc, as ..`加载具体语言对象,例如: + +```lua +local lang = language.load_sk("cxx") +if lang then + print(lang:name()) +end +``` + +###### language.load_ex + +- 从源文件后缀名加载指定语言 + +从源文件后缀名:`.cc, .c, .cpp, .mm, .swift, .go ..`加载具体语言对象,例如: + +```lua +local lang = language.load_sk(".cpp") +if lang then + print(lang:name()) +end +``` + +###### language.sourcekind_of + +- 获取指定源文件的源文件类型 + +也就是从给定的一个源文件路径,获取它是属于那种源文件类型,例如: + +```lua +print(language.sourcekind_of("/xxxx/test.cpp")) +``` + +显示结果为:`cxx`,也就是`c++`类型,具体对应列表见:[language.sourcekinds](#language-sourcekinds) + +##### core.platform.platform + +平台信息相关操作 + +| 接口 | 描述 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [platform.get](#platform-get) | 获取指定平台相关配置信息 | >= 2.0.1 | + +###### platform.get + +- 获取指定平台相关配置信息 + +获取平台配置`xmake.lua`中设置的信息,一般只有在写插件的时候会用到,例如: + +```lua +-- 获取当前平台的所有支持架构 +print(platform.get("archs")) + +-- 获取指定iphoneos平台的目标文件格式信息 +local formats = platform.get("formats", "iphoneos") +table.dump(formats) +``` + +具体有哪些可读的平台配置信息,可参考:[platform](#platform) + +##### core.platform.environment + +环境相关操作,用于进入和离开指定环境变量对应的终端环境,一般用于`path`环境的进入和离开,尤其是一些需要特定环境的构建工具,例如:msvc的工具链。 + +| 接口 | 描述 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [environment.enter](#environment-enter) | 进入指定环境 | >= 2.0.1 | +| [environment.leave](#environment-leave) | 离开指定环境 | >= 2.0.1 | + +目前支持的环境有: + +| 接口 | 描述 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| toolchains | 工具链执行环境 | >= 2.0.1 | + +###### environment.enter + +- 进入指定环境 + +进入指定环境,例如msvc有自己的环境变量环境用于运行构建工具,例如:`cl.exe`, `link.exe`这些,这个时候想要在xmake里面运行他们,需要: + +```lua +-- 进入工具链环境 +environment.enter("toolchains") + +-- 这个时候运行cl.exe才能正常运行,这个时候的path等环境变量都会进入msvc的环境模式 +os.run("cl.exe ..") + +-- 离开工具链环境 +environment.leave("toolchains") +``` + +因此为了通用性,默认xmake编译事都会设置这个环境,在linux下基本上内部环境不需要特殊切换,目前仅对windows下msvc进行了处理。 + +###### environment.leave + +- 离开指定环境 + +具体使用见:[environment.enter](#environment-enter) + +##### lib.detect + +此模块提供了非常强大的探测功能,用于探测程序、编译器、语言特性、依赖包等。 + ++此模块的接口分散在多个模块目录中,尽量通过导入单个接口来使用,这样效率更高,例如:`import("lib.detect.find_package")`,而不是通过`import("lib.detect")`导入所有来调用。 +
+ +| 接口 | 描述 | 支持版本 | +| --------------------------------------------------- | -------------------------------------------- | -------------------- | +| [detect.find_file](#detect-find_file) | 查找文件 | >= 2.1.5 | +| [detect.find_path](#detect-find_path) | 查找文件路径 | >= 2.1.5 | +| [detect.find_library](#detect-find_library) | 查找库文件 | >= 2.1.5 | +| [detect.find_program](#detect-find_program) | 查找可执行程序 | >= 2.1.5 | +| [detect.find_programver](#detect-find_programver) | 查找可执行程序版本号 | >= 2.1.5 | +| [detect.find_package](#detect-find_package) | 查找包文件,包含库文件和搜索路径 | >= 2.1.5 | +| [detect.find_tool](#detect-find_tool) | 查找工具 | >= 2.1.5 | +| [detect.find_toolname](#detect-find_toolname) | 查找工具名 | >= 2.1.5 | +| [detect.features](#detect-features) | 获取指定工具的所有特性 | >= 2.1.5 | +| [detect.has_features](#detect-has_features) | 判断指定特性是否支持 | >= 2.1.5 | +| [detect.has_flags](#detect-has_flags) | 判断指定参数选项是否支持 | >= 2.1.5 | +| [detect.has_cfuncs](#detect-has_cfuncs) | 判断指定c函数是否存在 | >= 2.1.5 | +| [detect.has_cxxfuncs](#detect-has_cxxfuncs) | 判断指定c++函数是否存在 | >= 2.1.5 | +| [detect.has_cincludes](#detect-has_cincludes) | 判断指定c头文件是否存在 | >= 2.1.5 | +| [detect.has_cxxincludess](#detect-has_cxxincludes) | 判断指定c++头文件是否存在 | >= 2.1.5 | +| [detect.has_ctypes](#detect-has_ctypes) | 判断指定c类型是否存在 | >= 2.1.5 | +| [detect.has_cxxtypes](#detect-has_cxxtypes) | 判断指定c++类型是否存在 | >= 2.1.5 | +| [detect.check_cxsnippets](#detect-check_cxsnippets) | 检测c/c++代码片段是否能够编译通过 | >= 2.1.5 | + +###### detect.find_file + +- 查找文件 + +这个接口提供了比[os.files](#os-files)更加强大的工程, 可以同时指定多个搜索目录,并且还能对每个目录指定附加的子目录,来模式匹配查找,相当于是[os.files](#os-files)的增强版。 + +例如: + +```lua +import("lib.detect.find_file") + +local file = find_file("ccache", { "/usr/bin", "/usr/local/bin"}) +``` + +如果找到,返回的结果是:`/usr/bin/ccache` + +它同时也支持模式匹配路径,进行递归查找,类似`os.files`: + +```lua +local file = find_file("test.h", { "/usr/include", "/usr/local/include/**"}) +``` + +不仅如此,里面的路径也支持内建变量,来从环境变量和注册表中获取路径进行查找: + +```lua +local file = find_file("xxx.h", { "$(env PATH)", "$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\XXXX;Name)"}) +``` + +如果路径规则比较复杂多变,还可以通过自定义脚本来动态生成路径传入: + +```lua +local file = find_file("xxx.h", { "$(env PATH)", function () return val("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\XXXX;Name"):match("\"(.-)\"") end}) +``` + +大部分场合下,上面的使用已经满足各种需求了,如果还需要一些扩展功能,可以通过传入第三个参数,自定义一些可选配置,例如: + +```lua +local file = find_file("test.h", { "/usr", "/usr/local"}, {suffixes = {"/include", "/lib"}}) +``` + +通过指定suffixes子目录列表,可以扩展路径列表(第二个参数),使得实际的搜索目录扩展为: + +``` +/usr/include +/usr/lib +/usr/local/include +/usr/local/lib +``` + +并且不用改变路径列表,就能动态切换子目录来搜索文件。 + ++我们也可以通过`xmake lua`插件来快速调用和测试此接口:`xmake lua lib.detect.find_file test.h /usr/local` +
+ +###### detect.find_path + +- 查找路径 + +这个接口的用法跟[lib.detect.find_file](#detect-find_file)类似,唯一的区别是返回的结果不同。 +此接口查找到传入的文件路径后,返回的是对应的搜索路径,而不是文件路径本身,一般用于查找文件对应的父目录位置。 + +```lua +import("lib.detect.find_path") + +local p = find_path("include/test.h", { "/usr", "/usr/local"}) +``` + +上述代码如果查找成功,则返回:`/usr/local`,如果`test.h`在`/usr/local/include/test.h`的话。 + +还有一个区别就是,这个接口传入不只是文件路径,还可以传入目录路径来查找: + +```lua +local p = find_path("lib/xxx", { "$(env PATH)", "$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\XXXX;Name)"}) +``` + +同样,此接口也支持模式匹配和后缀子目录: + +```lua +local p = find_path("include/*.h", { "/usr", "/usr/local/**"}, {suffixes = "/subdir"}) +``` + +###### detect.find_library + +- 查找库文件 + +此接口用于指定的搜索目录中查找库文件(静态库,动态库),例如: + +```lua +import("lib.detect.find_library") + +local library = find_library("crypto", {"/usr/lib", "/usr/local/lib"}) +``` + +在macosx上运行,返回的结果如下: + +```lua +{ + filename = libcrypto.dylib +, linkdir = /usr/lib +, link = crypto +, kind = shared +} +``` + +如果不指定是否需要静态库还是动态库,那么此接口会自动选择一个存在的库(有可能是静态库、也有可能是动态库)进行返回。 + +如果需要强制指定需要查找的库类型,可以指定kind参数为(`static/shared`): + +```lua +local library = find_library("crypto", {"/usr/lib", "/usr/local/lib"}, {kind = "static"}) +``` + +此接口也支持suffixes后缀子目录搜索和模式匹配操作: + +```lua +local library = find_library("cryp*", {"/usr", "/usr/local"}, {suffixes = "/lib"}) +``` + +###### detect.find_program + +- 查找可执行程序 + +这个接口比[lib.detect.find_tool](#detect-find_tool)较为原始底层,通过指定的参数目录来查找可执行程序。 + +```lua +import("lib.detect.find_program") + +local program = find_program("ccache") +``` + +上述代码犹如没有传递搜索目录,所以它会尝试直接执行指定程序,如果运行ok,那么直接返回:`ccache`,表示查找成功。 + +指定搜索目录,修改尝试运行的检测命令参数(默认是:`ccache --version`): + +```lua +local program = find_program("ccache", {"/usr/bin", "/usr/local/bin"}, "--help") +``` + +上述代码会尝试运行:`/usr/bin/ccache --help`,如果运行成功,则返回:`/usr/bin/ccache`。 + +如果`--help`也没法满足需求,有些程序没有`--version/--help`参数,那么可以自定义运行脚本,来运行检测: + +```lua +local program = find_program("ccache", {"/usr/bin", "/usr/local/bin"}, function (program) os.run("%s -h", program) end) +``` + +同样,搜索路径列表支持内建变量和自定义脚本: + +```lua +local program = find_program("ccache", {"$(env PATH)", "$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug;Debugger)"}) +local program = find_program("ccache", {"$(env PATH)", function () return "/usr/local/bin" end}) +``` + ++为了加速频发查找的效率,此接口是默认自带cache的,所以就算频繁查找相同的程序,也不会花太多时间。 +如果要禁用cache,可以在工程目录执行`xmake f -c`清除本地cache。 +
+ +我们也可以通过`xmake lua lib.detect.find_program ccache` 来快速测试。 + +###### detect.find_programver + +- 查找可执行程序版本号 + + +```lua +import("lib.detect.find_programver") + +local programver = find_programver("ccache") +``` + +返回结果为:3.2.2 + +默认它会通过`ccache --version`尝试获取版本,如果不存在此参数,可以自己指定其他参数: + +```lua +local version = find_programver("ccache", "-v") +``` + +甚至自定义版本获取脚本: + +```lua +local version = find_programver("ccache", function () return os.iorun("ccache --version") end) +``` + +对于版本号的提取规则,如果内置的匹配模式不满足要求,也可以自定义: + +```lua +local version = find_programver("ccache", "--version", "(%d+%.?%d*%.?%d*.-)%s") +local version = find_programver("ccache", "--version", function (output) return output:match("(%d+%.?%d*%.?%d*.-)%s") end) +``` + ++为了加速频发查找的效率,此接口是默认自带cache的,如果要禁用cache,可以在工程目录执行`xmake f -c`清除本地cache。 +
+ +我们也可以通过`xmake lua lib.detect.find_programver ccache` 来快速测试。 + +###### detect.find_package + +- 查找包文件 + +此接口也是用于查找库文件,但是比[lib.detect.find_library](#detect-find_library)更加上层,也更为强大和简单易用,因为它是以包为力度进行查找。 + +那怎样算是一个完整的包,它包含: + +1. 多个静态库或者动态库文件 +2. 库的搜索目录 +3. 头文件的搜索目录 +4. 可选的编译链接选项,例如:`defines`等 +5. 可选的版本号 + +例如我们查找一个openssl包: + +```lua +import("lib.detect.find_package") + +local package = find_package("openssl") +``` + +返回的结果如下: + +```lua +{links = {"ssl", "crypto", "z"}, linkdirs = {"/usr/local/lib"}, includedirs = {"/usr/local/include"}} +``` + +如果查找成功,则返回一个包含所有包信息的table,如果失败返回nil + +这里的返回结果可以直接作为`target:add`, `option:add`的参数传入,用于动态增加`target/option`的配置: + +```lua +option("zlib") + set_showmenu(true) + before_check(function (option) + import("lib.detect.find_package") + option:add(find_package("zlib")) + end) +``` + +```lua +target("test") + on_load(function (target) + import("lib.detect.find_package") + target:add(find_package("zlib")) + end) +``` + +如果系统上装有`homebrew`, `pkg-config`等第三方工具,那么此接口会尝试使用它们去改进查找结果。 + +我们也可以通过指定版本号,来选择查找指定版本的包(如果这个包获取不到版本信息或者没有匹配版本的包,则返回nil): + +```lua +local package = find_package("openssl", {version = "1.0.1"}) +``` + +默认情况下查找的包是根据如下规则匹配平台,架构和模式的: + +1. 如果参数传入指定了`{plat = "iphoneos", arch = "arm64", mode = "release"}`,则优先匹配,例如:`find_package("openssl", {plat = "iphoneos"})`。 +2. 如果是在当前工程环境,存在配置文件,则优先尝试从`config.get("plat")`, `config.get("arch")`和`config.get("mode")`获取平台架构进行匹配。 +3. 最后从`os.host()`和`os.arch()`中进行匹配,也就是当前主机的平台架构环境。 + +如果系统的库目录以及`pkg-config`都不能满足需求,找不到包,那么可以自己手动设置搜索路径: + +```lua +local package = find_package("openssl", {linkdirs = {"/usr/lib", "/usr/local/lib"}, includedirs = "/usr/local/include"}) +``` + +也可以同时指定需要搜索的链接名,头文件名: + +```lua +local package = find_package("openssl", {links = {"ssl", "crypto"}, includes = "ssl.h"}}) +``` + +甚至可以指定xmake的`packagedir/*.pkg`包目录,用于查找对应的`openssl.pkg`包,一般用于查找内置在工程目录中的本地包。 + +例如,tbox工程内置了`pkg/openssl.pkg`本地包载项目中,我们可以通过下面的脚本,传入`{packagedirs = ""}`参数优先查找本地包,如果找不到再去找系统包。 + +```lua +target("test") + on_load(function (target) + import("lib.detect.find_package") + target:add(find_package("openssl", {packagedirs = path.join(os.projectdir(), "pkg")})) + end) +``` + +总结下,现在的查找顺序: + +1. 如果指定`{packagedirs = ""}`参数,优先从这个参数指定的路径中查找本地包`*.pkg` +2. 如果在`xmake/modules`下面存在`detect.packages.find_xxx`脚本,那么尝试调用此脚本来改进查找结果 +3. 如果系统存在`pkg-config`,并且查找的是系统环境的库,则尝试使用`pkg-config`提供的路径和链接信息进行查找 +4. 如果系统存在`homebrew`,并且查找的是系统环境的库,则尝试使用`brew --prefix xxx`提供的信息进行查找 +5. 从参数中指定的pathes路径和一些已知的系统路径`/usr/lib`, `/usr/include`中进行查找 + +这里需要着重说下第二点,通过在`detect.packages.find_xxx`脚本来改进查找结果,很多时候自动的包探测是没法完全探测到包路径的, +尤其是针对windows平台,没有默认的库目录,也没有包管理app,很多库装的时候,都是自己所处放置在系统目录,或者添加注册表项。 + +因此查找起来没有同意的规则,这个时候,就可以自定义一个查找脚本,去改进`find_package`的查找机制,对指定包进行更精准的查找。 + +在xmake自带的`xmake/modules/detect/packages`目录下,已经有许多的内置包脚本,来对常用的包进行更好的查找支持。 +当然这不可能满足所有用户的需求,如果用户需要的包还是找不到,那么可以自己定义一个查找脚本,例如: + +查找一个名为`openssl`的包,可以编写一个`find_openssl.lua`的脚本放置在工程目录: + +``` +projectdir + - xmake + - modules + - detect/package/find_openssl.lua +``` + +然后在工程的`xmake.lua`文件的开头指定下这个modules的目录: + +```lua +add_moduledirs("$(projectdir)/xmake/modules") +``` + +这样xmake就能找到自定义的扩展模块了。 + +接下来我们看下`find_openssl.lua`的实现: + +```lua +-- imports +import("lib.detect.find_path") +import("lib.detect.find_library") + +-- find openssl +-- +-- @param opt the package options. e.g. see the options of find_package() +-- +-- @return see the return value of find_package() +-- +function main(opt) + + -- for windows platform + -- + -- http://www.slproweb.com/products/Win32OpenSSL.html + -- + if opt.plat == "windows" then + + -- init bits + local bits = ifelse(opt.arch == "x64", "64", "32") + + -- init search pathes + local pathes = {"$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL %(" .. bits .. "-bit%)_is1;Inno Setup: App Path)", + "$(env PROGRAMFILES)/OpenSSL", + "$(env PROGRAMFILES)/OpenSSL-Win" .. bits, + "C:/OpenSSL", + "C:/OpenSSL-Win" .. bits} + + -- find library + local result = {links = {}, linkdirs = {}, includedirs = {}} + for _, name in ipairs({"libssl", "libcrypto"}) do + local linkinfo = find_library(name, pathes, {suffixes = "lib"}) + if linkinfo then + table.insert(result.links, linkinfo.link) + table.insert(result.linkdirs, linkinfo.linkdir) + end + end + + -- not found? + if #result.links ~= 2 then + return + end + + -- find include + table.insert(result.includedirs, find_path("openssl/ssl.h", pathes, {suffixes = "include"})) + + -- ok + return result + end +end +``` + +里面对windows平台进行注册表读取,去查找指定的库文件,其底层其实也是调用的[find_library](#detect-find_library)等接口。 + ++为了加速频发查找的效率,此接口是默认自带cache的,如果要禁用cache,可以在工程目录执行`xmake f -c`清除本地cache。 +也可以通过指定force参数,来禁用cache,强制重新查找:`find_package("openssl", {force = true})` +
+ +我们也可以通过`xmake lua lib.detect.find_package openssl` 来快速测试。 + +###### detect.find_tool + +- 查找工具 + +此接口也是用于查找可执行程序,不过比[lib.detect.find_program](#detect-find_program)更加的高级,功能也更加强大,它对可执行程序进行了封装,提供了工具这个概念: + +* toolname: 工具名,可执行程序的简称,用于标示某个工具,例如:`gcc`, `clang`等 +* program: 可执行程序命令,例如:`xcrun -sdk macosx clang` + +其对应关系如下: + +| toolname | program | +| --------- | ----------------------------------- | +| clang | `xcrun -sdk macosx clang` | +| gcc | `/usr/toolchains/bin/arm-linux-gcc` | +| link | `link.exe -lib` | + +[lib.detect.find_program](#detect-find_program)只能通过传入的原始program命令或路径,去判断该程序是否存在。 +而`find_tool`则可以通过更加一致的toolname去查找工具,并且返回对应的program完整命令路径,例如: + +```lua +import("lib.detect.find_tool") + +local tool = find_tool("clang") +``` + +返回的结果为:`{name = "clang", program = "clang"}`,这个时候还看不出区别,我们可以手动指定可执行的命令: + +```lua +local tool = find_tool("clang", {program = "xcrun -sdk macosx clang"}) +``` + +返回的结果为:`{name = "clang", program = "xcrun -sdk macosx clang"}` + +而在macosx下,gcc就是clang,如果我们执行`gcc --version`可以看到就是clang的一个马甲,我们可以通过`find_tool`接口进行智能识别: + +```lua +local tool = find_tool("gcc") +``` + +返回的结果为:`{name = "clang", program = "gcc"}` + +通过这个结果就可以看的区别来了,工具名实际会被标示为clang,但是可执行的命令用的是gcc。 + +我们也可以指定`{version = true}`参数去获取工具的版本,并且指定一个自定义的搜索路径,也支持内建变量和自定义脚本哦: + +```lua +local tool = find_tool("clang", {version = true, {pathes = {"/usr/bin", "/usr/local/bin", "$(env PATH)", function () return "/usr/xxx/bin" end}}) +``` + +返回的结果为:`{name = "clang", program = "/usr/bin/clang", version = "4.0"}` + +这个接口是对`find_program`的上层封装,因此也支持自定义脚本检测: + +```lua +local tool = find_tool("clang", {check = "--help"}) +local tool = find_tool("clang", {check = function (tool) os.run("%s -h", tool) end}) +``` + +最后总结下,`find_tool`的查找流程: + +1. 优先通过`{program = "xxx"}`的参数来尝试运行和检测。 +2. 如果在`xmake/modules/detect/tools`下存在`detect.tools.find_xxx`脚本,则调用此脚本进行更加精准的检测。 +3. 尝试从`/usr/bin`,`/usr/local/bin`等系统目录进行检测。 + +我们也可以在工程`xmake.lua`中`add_moduledirs`指定的模块目录中,添加自定义查找脚本,来改进检测机制: + +``` +projectdir + - xmake/modules + - detect/tools/find_xxx.lua +``` + +例如我们自定义一个`find_7z.lua`的查找脚本: + +```lua +import("lib.detect.find_program") +import("lib.detect.find_programver") + +function main(opt) + + -- init options + opt = opt or {} + + -- find program + local program = find_program(opt.program or "7z", opt.pathes, opt.check or "--help") + + -- find program version + local version = nil + if program and opt and opt.version then + version = find_programver(program, "--help", "(%d+%.?%d*)%s") + end + + -- ok? + return program, version +end +``` + +将它放置到工程的模块目录下后,执行:`xmake l lib.detect.find_tool 7z`就可以查找到了。 + ++为了加速频发查找的效率,此接口是默认自带cache的,如果要禁用cache,可以在工程目录执行`xmake f -c`清除本地cache。 +
+ +我们也可以通过`xmake lua lib.detect.find_tool clang` 来快速测试。 + +###### detect.find_toolname + +- 查找工具名 + +通过program命令匹配对应的工具名,例如: + +| program | toolname | +| ------------------------- | ---------- | +| `xcrun -sdk macosx clang` | clang | +| `/usr/bin/arm-linux-gcc` | gcc | +| `link.exe -lib` | link | +| `gcc-5` | gcc | +| `arm-android-clang++` | clangxx | +| `pkg-config` | pkg_config | + +toolname相比program,更能唯一标示某个工具,也方便查找和加载对应的脚本`find_xxx.lua`。 + +###### detect.features + +- 获取指定工具的所有特性 + +此接口跟[compiler.features](#compiler-features)类似,区别就是此接口更加的原始,传入的参数是实际的工具名toolname。 + +并且此接口不仅能够获取编译器的特性,任何工具的特性都可以获取,因此更加通用。 + +```lua +import("lib.detect.features") + +local features = features("clang") +local features = features("clang", {flags = "-O0", program = "xcrun -sdk macosx clang"}) +local features = features("clang", {flags = {"-g", "-O0", "-std=c++11"}}) +``` + +通过传入flags,可以改变特性的获取结果,例如一些c++11的特性,默认情况下获取不到,通过启用`-std=c++11`后,就可以获取到了。 + +所有编译器的特性列表,可以见:[compiler.features](#compiler-features)。 + +###### detect.has_features + +- 判断指定特性是否支持 + +此接口跟[compiler.has_features](#compiler-has_features)类似,但是更加原始,传入的参数是实际的工具名toolname。 + +并且此接口不仅能够判断编译器的特性,任何工具的特性都可以判断,因此更加通用。 + +```lua +import("lib.detect.has_features") + +local features = has_features("clang", "cxx_constexpr") +local features = has_features("clang", {"cxx_constexpr", "c_static_assert"}, {flags = {"-g", "-O0"}, program = "xcrun -sdk macosx clang"}) +local features = has_features("clang", {"cxx_constexpr", "c_static_assert"}, {flags = "-g"}) +``` + +如果指定的特性列表存在,则返回实际支持的特性子列表,如果都不支持,则返回nil,我们也可以通过指定flags去改变特性的获取规则。 + +所有编译器的特性列表,可以见:[compiler.features](#compiler-features)。 + +###### detect.has_flags + +- 判断指定参数选项是否支持 + +此接口跟[compiler.has_flags](#compiler-has_flags)类似,但是更加原始,传入的参数是实际的工具名toolname。 + +```lua +import("lib.detect.has_flags") + +local ok = has_flags("clang", "-g") +local ok = has_flags("clang", {"-g", "-O0"}, {program = "xcrun -sdk macosx clang"}) +local ok = has_flags("clang", "-g -O0", {toolkind = "cxx"}) +``` + +如果检测通过,则返回true。 + +此接口的检测做了一些优化,除了cache机制外,大部分场合下,会去拉取工具的选项列表(`--help`)直接判断,如果选项列表里获取不到的话,才会通过尝试运行的方式来检测。 + +###### detect.has_cfuncs + +- 判断指定c函数是否存在 + +此接口是[lib.detect.check_cxsnippets](#detect-check_cxsnippets)的简化版本,仅用于检测函数。 + +```lua +import("lib.detect.has_cfuncs") + +local ok = has_cfuncs("setjmp") +local ok = has_cfuncs({"sigsetjmp((void*)0, 0)", "setjmp"}, {includes = "setjmp.h"}) +``` + +对于函数的描述规则如下: + +| 函数描述 | 说明 | +| ----------------------------------------------- | ------------- | +| `sigsetjmp` | 纯函数名 | +| `sigsetjmp((void*)0, 0)` | 函数调用 | +| `sigsetjmp{int a = 0; sigsetjmp((void*)a, a);}` | 函数名 + {}块 | + +在最后的可选参数中,除了可以指定`includes`外,还可以指定其他的一些参数用于控制编译检测的选项条件: + +```lua +{ verbose = false, target = [target|option], includes = .., config = {linkdirs = .., links = .., defines = ..}} +``` + +其中verbose用于回显检测信息,target用于在检测前追加target中的配置信息, 而config用于自定义配置跟target相关的编译选项。 + +###### detect.has_cxxfuncs + +- 判断指定c++函数是否存在 + +此接口跟[lib.detect.has_cfuncs](#detect-has_cfuncs)类似,请直接参考它的使用说明,唯一区别是这个接口用于检测c++函数。 + +###### detect.has_cincludes + +- 判断指定c头文件是否存在 + +此接口是[lib.detect.check_cxsnippets](#detect-check_cxsnippets)的简化版本,仅用于检测头文件。 + +```lua +import("lib.detect.has_cincludes") + +local ok = has_cincludes("stdio.h") +local ok = has_cincludes({"stdio.h", "stdlib.h"}, {target = target}) +local ok = has_cincludes({"stdio.h", "stdlib.h"}, {config = {defines = "_GNU_SOURCE=1", languages = "cxx11"}}) +``` + +###### detect.has_cxxincludes + +- 判断指定c++头文件是否存在 + +此接口跟[lib.detect.has_cincludess](#detect-has_cincludes)类似,请直接参考它的使用说明,唯一区别是这个接口用于检测c++头文件。 + +###### detect.has_ctypes + +- 判断指定c类型是否存在 + +此接口是[lib.detect.check_cxsnippets](#detect-check_cxsnippets)的简化版本,仅用于检测函数。 + +```lua +import("lib.detect.has_ctypes") + +local ok = has_ctypes("wchar_t") +local ok = has_ctypes({"char", "wchar_t"}, {includes = "stdio.h"}) +local ok = has_ctypes("wchar_t", {includes = {"stdio.h", "stdlib.h"}, config = {"defines = "_GNU_SOURCE=1", languages = "cxx11"}}) +``` + +###### detect.has_cxxtypes + +- 判断指定c++类型是否存在 + +此接口跟[lib.detect.has_ctypess](#detect-has_ctypes)类似,请直接参考它的使用说明,唯一区别是这个接口用于检测c++类型。 + +###### detect.check_cxsnippets + +- 检测c/c++代码片段是否能够编译通过 + +通用的c/c++代码片段检测接口,通过传入多个代码片段列表,它会自动生成一个编译文件,然后常识对它进行编译,如果编译通过返回true。 + +对于一些复杂的编译器特性,连[compiler.has_features](#compiler-has_features)都无法检测到的时候,可以通过此接口通过尝试编译来检测它。 + +```lua +import("lib.detect.check_cxsnippets") + +local ok = check_cxsnippets("void test() {}") +local ok = check_cxsnippets({"void test(){}", "#define TEST 1"}, {types = "wchar_t", includes = "stdio.h"}) +``` + +此接口是[detect.has_cfuncs](#detect-has_cfuncs), [detect.has_cincludes](#detect-has_cincludes)和[detect.has_ctypes](detect-has_ctypes)等接口的通用版本,也更加底层。 + +因此我们可以用它来检测:types, functions, includes 还有 links,或者是组合起来一起检测。 + +第一个参数为代码片段列表,一般用于一些自定义特性的检测,如果为空,则可以仅仅检测可选参数中条件,例如: + +```lua +local ok = check_cxsnippets({}, {types = {"wchar_t", "char*"}, includes = "stdio.h", funcs = {"sigsetjmp", "sigsetjmp((void*)0, 0)"}}) +``` + +上面那个调用,会去同时检测types, includes和funcs是否都满足,如果通过返回true。 + +还有其他一些可选参数: + +```lua +{ verbose = false, target = [target|option], sourcekind = "[cc|cxx]"} +``` + +其中verbose用于回显检测信息,target用于在检测前追加target中的配置信息, sourcekind 用于指定编译器等工具类型,例如传入`cxx`强制作为c++代码来检测。 + +##### net.http + +此模块提供http的各种操作支持,目前提供的接口如下: + +| 接口 | 描述 | 支持版本 | +| --------------------------------------------------- | -------------------------------------------- | -------------------- | +| [http.download](#http-download) | 下载http文件 | >= 2.1.5 | + +###### http.download + +- 下载http文件 + +这个接口比较简单,就是单纯的下载文件。 + +```lua +import("net.http") + +http.download("http://xmake.io", "/tmp/index.html") +``` + +##### privilege.sudo + +此接口用于通过`sudo`来运行命令,并且提供了平台一致性处理,对于一些需要root权限运行的脚本,可以使用此接口。 + ++为了保证安全性,除非必须使用的场合,其他情况下尽量不要使用此接口。 +
+ +| 接口 | 描述 | 支持版本 | +| --------------------------------------------------- | -------------------------------------------- | -------------------- | +| [sudo.has](#sudo-has) | 判断sudo是否支持 | >= 2.1.5 | +| [sudo.run](#sudo-run) | 安静运行程序 | >= 2.1.5 | +| [sudo.runv](#sudo-runv) | 安静运行程序,带参数列表 | >= 2.1.5 | +| [sudo.exec](#sudo-exec) | 回显运行程序 | >= 2.1.5 | +| [sudo.execv](#sudo-execv) | 回显运行程序,带参数列表 | >= 2.1.5 | +| [sudo.iorun](#sudo-iorun) | 运行并获取程序输出内容 | >= 2.1.5 | +| [sudo.iorunv](#sudo-iorunv) | 运行并获取程序输出内容,带参数列表 | >= 2.1.5 | + +###### sudo.has + +- 判断sudo是否支持 + +目前仅在`macosx/linux`下支持sudo,windows上的管理员权限运行暂时还不支持,因此建议使用前可以通过此接口判断支持情况后,针对性处理。 + +```lua +import("privilege.sudo") + +if sudo.has() then + sudo.run("rm /system/file") +end +``` + +###### sudo.run + +- 安静运行原生shell命令 + +具体用法可参考:[os.run](#os-run)。 + +```lua +import("privilege.sudo") + +sudo.run("rm /system/file") +``` + +###### sudo.runv + +- 安静运行原生shell命令,带参数列表 + +具体用法可参考:[os.runv](#os-runv)。 + +###### sudo.exec + +- 回显运行原生shell命令 + +具体用法可参考:[os.exec](#os-exec)。 + +###### sudo.execv + +- 回显运行原生shell命令,带参数列表 + +具体用法可参考:[os.execv](#os-execv)。 + +###### sudo.iorun + +- 安静运行原生shell命令并获取输出内容 + +具体用法可参考:[os.iorun](#os-iorun)。 + +###### sudo.iorunv + +- 安静运行原生shell命令并获取输出内容,带参数列表 + +具体用法可参考:[os.iorunv](#os-iorunv)。 + +##### devel.git + +此接口提供了git各种命令的访问接口,相对于直接调用git命令,此模块提供了更加上层易用的封装接口,并且提供对git的自动检测和跨平台处理。 + ++目前windows上,需要手动安装git包后,才能检测到,后续版本会提供自动集成git功能,用户将不用关心如何安装git,就可以直接使用。 +
+ +| 接口 | 描述 | 支持版本 | +| --------------------------------------------------- | -------------------------------------------- | -------------------- | +| [git.clone](#git-clone) | clone代码库 | >= 2.1.5 | +| [git.pull](#git-pull) | 拉取代码库最新提交 | >= 2.1.5 | +| [git.clean](#git-clean) | 清理代码库文件 | >= 2.1.5 | +| [git.checkout](#git-checkout) | 签出指定分支版本 | >= 2.1.5 | +| [git.refs](#git-refs) | 获取所有引用列表 | >= 2.1.5 | +| [git.tags](#git-tags) | 获取所有标记列表 | >= 2.1.5 | +| [git.branches](#git-branches) | 获取所有分支列表 | >= 2.1.5 | + +###### git.clone + +- clone代码库 + +此接口对应`git clone`命令 + +```lua +import("devel.git") + +git.clone("git@github.com:tboox/xmake.git") +git.clone("git@github.com:tboox/xmake.git", {depth = 1, branch = "master", outputdir = "/tmp/xmake"}) +``` + +###### git.pull + +- 拉取代码库最新提交 + +此接口对应`git pull`命令 + +```lua +import("devel.git") + +git.pull() +git.pull({remote = "origin", tags = true, branch = "master", repodir = "/tmp/xmake"}) +``` + +###### git.clean + +- 清理代码库文件 + +此接口对应`git clean`命令 + +```lua +import("devel.git") + +git.clean() +git.clean({repodir = "/tmp/xmake", force = true}) +``` + +###### git.checkout + +- 签出指定分支版本 + +此接口对应`git checkout`命令 + +```lua +import("devel.git") + +git.checkout("master", {repodir = "/tmp/xmake"}) +git.checkout("v1.0.1", {repodir = "/tmp/xmake"}) +``` + +###### git.refs + +- 获取所有引用列表 + +此接口对应`git ls-remote --refs`命令 + +```lua +import("devel.git") + +local refs = git.refs(url) +``` + +###### git.tags + +- 获取所有标记列表 + +此接口对应`git ls-remote --tags`命令 + +```lua +import("devel.git") + +local tags = git.tags(url) +``` + +###### git.branches + +- 获取所有分支列表 + +此接口对应`git ls-remote --heads`命令 + +```lua +import("devel.git") + +local branches = git.branches(url) +``` + +##### utils.archive + +此模块用于压缩和解压缩文件。 + +| 接口 | 描述 | 支持版本 | +| --------------------------------------------------- | -------------------------------------------- | -------------------- | +| [archive.extract](#archive-extract) | 解压文件 | >= 2.1.5 | + +###### archive.extract + +- 解压文件 + +支持大部分常用压缩文件的解压,它会自动检测系统提供了哪些解压工具,然后适配到最合适的解压器对指定压缩文件进行解压操作。 + +```lua +import("utils.archive") + +archive.extract("/tmp/a.zip", "/tmp/outputdir") +archive.extract("/tmp/a.7z", "/tmp/outputdir") +archive.extract("/tmp/a.gzip", "/tmp/outputdir") +archive.extract("/tmp/a.tar.bz2", "/tmp/outputdir") +``` diff --git a/zh/plugins.md b/zh/plugins.md new file mode 100644 index 00000000..36b1a948 --- /dev/null +++ b/zh/plugins.md @@ -0,0 +1,525 @@ +--- +nav: zh +search: zh +--- + +## 插件开发 + +#### 简介 + +XMake完全支持插件模式,我们可以很方便的扩展实现自己的插件,并且xmake也提供了一些内建的使用插件。 + +我们可以执行下 `xmake -h` 看下当前支持的插件: + +``` +Plugins: + l, lua Run the lua script. + m, macro Run the given macro. + doxygen Generate the doxygen document. + hello Hello xmake! + project Create the project file. +``` + +* lua: 运行lua脚本的插件 +* macro: 这个很实用,宏脚本插件,可以手动录制多条xmake命令并且回放,也可以通过脚本实现一些复杂的宏脚本,这个我们后续会更加详细的介绍 +* doxygen:一键生成doxygen文档的插件 +* hello: 插件demo,仅仅显示一句话:'hello xmake!' +* project: 生成工程文件的插件,目前仅支持(makefile),后续还会支持(vs,xcode等工程)的生成 + +#### 快速开始 + +接下来我们介绍下本文的重点,一个简单的hello xmake插件的开发,代码如下: + +```lua +-- 定义一个名叫hello的插件任务 +task("hello") + + -- 设置类型为插件 + set_category("plugin") + + -- 插件运行的入口 + on_run(function () + + -- 显示hello xmake! + print("hello xmake!") + + end) + + -- 设置插件的命令行选项,这里没有任何参数选项,仅仅显示插件描述 + set_menu { + -- usage + usage = "xmake hello [options]" + + -- description + , description = "Hello xmake!" + + -- options + , options = {} + } +``` + +这个插件的文件结构如下: + +``` +hello + - xmake.lua + +``` + +现在一个最简单的插件写完了,那怎么让它被xmake检测到呢,有三种方式: + +1. 把 hello 这个文件夹放置在 xmake的插件安装目录 `xmake/plugins`,这个里面都是些内建的插件 +2. 把 hello 文件夹放置在 `~/.xmake/plugins` 用户全局目录,这样对当前xmake 全局生效 +3. 把 hello 文件夹放置在任意地方,通过在工程描述文件xmake.lua中调用`add_plugindirs("./hello")` 添加当前的工程的插件搜索目录,这样只对当前工程生效 + +#### 运行插件 + +接下来,我们尝试运行下这个插件: + +```console +xmake hello +``` + +显示结果: + +``` +hello xmake! +``` + +最后我们还可以在target自定义的脚本中运行这个插件: + +```lua +target("demo") + + -- 构建之后运行插件 + after_build(function (target) + + -- 导入task模块 + import("core.project.task") + + -- 运行插件任务 + task.run("hello") + end) +``` + +## 内置插件 + +#### 宏记录和回放 + +##### 简介 + +我们可以通过这个插件,快速记录和回放我们平常频繁使用到的一些xmake操作,来简化我们日常的开发工作。 + +它提供了一些功能: + +* 手动记录和回放多条执行过的xmake命令 +* 支持快速的匿名宏创建和回放 +* 支持命名宏的长久记录和重用 +* 支持宏脚本的批量导入和导出 +* 支持宏脚本的删除、显示等管理功能 +* 支持自定义高级宏脚本,以及参数配置 + +##### 记录操作 + +```console +# 开始记录宏 +$ xmake macro --begin + +# 执行一些xmake命令 +$ xmake f -p android --ndk=/xxx/ndk -a armv7-a +$ xmake p +$ xmake f -p mingw --sdk=/mingwsdk +$ xmake p +$ xmake f -p linux --sdk=/toolsdk --toolchains=/xxxx/bin +$ xmake p +$ xmake f -p iphoneos -a armv7 +$ xmake p +$ xmake f -p iphoneos -a arm64 +$ xmake p +$ xmake f -p iphoneos -a armv7s +$ xmake p +$ xmake f -p iphoneos -a i386 +$ xmake p +$ xmake f -p iphoneos -a x86_64 +$ xmake p + +# 结束宏记录,这里不设置宏名字,所以记录的是一个匿名宏 +xmake macro --end +``` + +##### 回放 + +```console +# 回放一个匿名宏 +$ xmake macro . +``` + +##### 命名宏 + +匿名宏的好处就是快速记录,快速回放,如果需要长久保存,就需要给宏取个名字。 + +```console +$ xmake macro --begin +$ ... +$ xmake macro --end macroname +$ xmake macro macroname +``` + +##### 导入导出宏 + +导入指定的宏脚本或者宏目录: + +```console +$ xmake macro --import=/xxx/macro.lua macroname +$ xmake macro --import=/xxx/macrodir +``` + +导出指定的宏到脚本或者目录: + +```console +$ xmake macro --export=/xxx/macro.lua macroname +$ xmake macro --export=/xxx/macrodir +``` + +##### 列举显示宏 + +列举所有`xmake`内置的宏脚本: + +```console +$ xmake macro --list +``` + +显示指定的宏脚本内容: + +```console +$ xmake macro --show macroname +``` + +##### 自定义宏脚本 + +我们也可以自己编写个宏脚本 `macro.lua` 然后导入到xmake中去。 + +```lua +function main() + os.exec("xmake f -p android --ndk=/xxx/ndk -a armv7-a") + os.exec("xmake p") + os.exec("xmake f -p mingw --sdk=/mingwsdk") + os.exec("xmake p") + os.exec("xmake f -p linux --sdk=/toolsdk --toolchains=/xxxx/bin") + os.exec("xmake p") + os.exec("xmake f -p iphoneos -a armv7") + os.exec("xmake p") + os.exec("xmake f -p iphoneos -a arm64") + os.exec("xmake p") + os.exec("xmake f -p iphoneos -a armv7s") + os.exec("xmake p") + os.exec("xmake f -p iphoneos -a i386") + os.exec("xmake p") + os.exec("xmake f -p iphoneos -a x86_64") + os.exec("xmake p") +end +``` + +导入到xmake,并且定义宏名字: + +```console +$ xmake macro --import=/xxx/macro.lua [macroname] +``` + +回放这个宏脚本: + +```console +$ xmake macro [.|macroname] +``` + +##### 内置的宏脚本 + +XMake 提供了一些内置的宏脚本,来简化我们的日常开发工作。 + +例如,我们可以使用 `package` 宏来对`iphoneos`平台的所有架构,一次性批量构建和打包: + +```console +$ xmake macro package -p iphoneos +``` + +##### 高级的宏脚本编写 + +以上面提到的`package`宏为例,我们看下其具体代码,里面通过`import`导入一些扩展模块,实现了复杂的脚本操作。 + + +```lua +-- imports +import("core.base.option") +import("core.project.config") +import("core.project.project") +import("core.platform.platform") + +-- the options +local options = +{ + {'p', "plat", "kv", os.host(), "Set the platform." } +, {'f', "config", "kv", nil, "Pass the config arguments to \"xmake config\" .." } +, {'o', "outputdir", "kv", nil, "Set the output directory of the package." } +} + +-- package all +-- +-- .e.g +-- xmake m package +-- xmake m package -f "-m debug" +-- xmake m package -p linux +-- xmake m package -p iphoneos -f "-m debug --xxx ..." -o /tmp/xxx +-- xmake m package -f \"--mode=debug\" +-- +function main(argv) + + -- parse arguments + local args = option.parse(argv, options, "Package all architectures for the given the platform." + , "" + , "Usage: xmake macro package [options]") + + -- package all archs + local plat = args.plat + for _, arch in ipairs(platform.archs(plat)) do + + -- config it + os.exec("xmake f -p %s -a %s %s -c %s", plat, arch, args.config or "", ifelse(option.get("verbose"), "-v", "")) + + -- package it + if args.outputdir then + os.exec("xmake p -o %s %s", args.outputdir, ifelse(option.get("verbose"), "-v", "")) + else + os.exec("xmake p %s", ifelse(option.get("verbose"), "-v", "")) + end + end + + -- package universal for iphoneos, watchos ... + if plat == "iphoneos" or plat == "watchos" then + + -- load configure + config.load() + + -- load project + project.load() + + -- enter the project directory + os.cd(project.directory()) + + -- the outputdir directory + local outputdir = args.outputdir or config.get("buildir") + + -- package all targets + for _, target in pairs(project.targets()) do + + -- get all modes + local modedirs = os.match(format("%s/%s.pkg/lib/*", outputdir, target:name()), true) + for _, modedir in ipairs(modedirs) do + + -- get mode + local mode = path.basename(modedir) + + -- make lipo arguments + local lipoargs = nil + for _, arch in ipairs(platform.archs(plat)) do + local archfile = format("%s/%s.pkg/lib/%s/%s/%s/%s", outputdir, target:name(), mode, plat, arch, path.filename(target:targetfile())) + if os.isfile(archfile) then + lipoargs = format("%s -arch %s %s", lipoargs or "", arch, archfile) + end + end + if lipoargs then + + -- make full lipo arguments + lipoargs = format("-create %s -output %s/%s.pkg/lib/%s/%s/universal/%s", lipoargs, outputdir, target:name(), mode, plat, path.filename(target:targetfile())) + + -- make universal directory + os.mkdir(format("%s/%s.pkg/lib/%s/%s/universal", outputdir, target:name(), mode, plat)) + + -- package all archs + os.execv("xmake", {"l", "lipo", lipoargs}) + end + end + end + end +end +``` + ++ 如果你想要获取更多宏参数选项信息,请运行: `xmake macro --help` +
+ +#### 运行自定义lua脚本 + +这个跟宏脚本类似,只是省去了导入导出操作,直接指定lua脚本来加载运行,这对于想要快速测试一些接口模块,验证自己的某些思路,都是一个不错的方式。 + +##### 运行指定的脚本文件 + +我们先写个简单的lua脚本: + +```lua +function main() + print("hello xmake!") +end +``` + +然后直接运行它就行了: + +```console +$ xmake lua /tmp/test.lua +``` + ++ 当然,你也可以像宏脚本那样,使用`import`接口导入扩展模块,实现复杂的功能。 +
+ +##### 运行内置的脚本命令 + +你可以运行 `xmake lua -l` 来列举所有内置的脚本名,例如: + +```console +$ xmake lua -l +scripts: + cat + cp + echo + versioninfo + ... +``` + +并且运行它们: + +```console +$ xmake lua cat ~/file.txt +$ xmake lua echo "hello xmake" +$ xmake lua cp /tmp/file /tmp/file2 +$ xmake lua versioninfo +``` + +##### 运行交互命令 (REPL) + +有时候在交互模式下,运行命令更加的方便测试和验证一些模块和api,也更加的灵活,不需要再去额外写一个脚本文件来加载。 + +我们先看下,如何进入交互模式: + +```console +# 不带任何参数执行,就可以进入 +$ xmake lua +> + +# 进行表达式计算 +> 1 + 2 +3 + +# 赋值和打印变量值 +> a = 1 +> a +1 + +# 多行输入和执行 +> for _, v in pairs({1, 2, 3}) do +>> print(v) +>> end +1 +2 +3 +``` + +我们也能够通过 `import` 来导入扩展模块: + +```console +> task = import("core.project.task") +> task.run("hello") +hello xmake! +``` + +如果要中途取消多行输入,只需要输入字符:`q` 就行了 + +```console +> for _, v in ipairs({1, 2}) do +>> print(v) +>> q <-- 取消多行输入,清空先前的输入数据 +> 1 + 2 +3 +``` + +#### 生成IDE工程文件 + +##### 简介 + +XMake跟`cmake`, `premake`等其他一些构建工具的区别在于: + ++`xmake`默认是直接构建运行的,生成第三方的IDE的工程文件仅仅作为`插件`来提供。 +
+ +这样做的一个好处是:插件更加容易扩展,维护也更加独立和方便。 + +##### 生成Makefile + +```console +$ xmake project -k makefile +``` + +##### 生成compiler_commands + +导出每个源文件的编译信息,生成基于clang的编译数据库文件,json格式,可用于跟ide,编辑器,静态分析工具进行交互。 + +```console +$ xmake project -k compile_commands +``` + +输出的内容格式如下: + +``` +[ + { "directory": "/home/user/llvm/build", + "command": "/usr/bin/clang++ -Irelative -DSOMEDEF=\"With spaces, quotes and \\-es.\" -c -o file.o file.cc", + "file": "file.cc" }, + ... +] + +``` + +对于`compile_commands`的详细说明见:[JSONCompilationDatabase](#https://clang.llvm.org/docs/JSONCompilationDatabase.html) + +##### 生成VisualStudio工程 + +```console +$ xmake project -k [vs2008|vs2013|vs2015|..] +``` + +v2.1.2以上版本,增强了vs201x版本工程的生成,支持多模式+多架构生成,生成的时候只需要指定: + +```console +$ xmake project -k vs2017 -m "debug,release" +``` + +生成后的工程文件,同时支持`debug|x86`, `debug|x64`, `release|x86`, `release|x64`四种配置模式。 + +如果不想每次生成的时候,指定模式,可以把模式配置加到`xmake.lua`的中,例如: + +```lua +-- 配置当前的工程,支持哪些编译模式 +set_modes("debug", "release") +``` + +具体`set_modes`的使用,可以参考对应的接口手册文档。 + +#### 生成doxygen文档 + +请先确保本机已安装`doxygen`工具,然后在工程目录下运行: + +```console +$ xmake doxygen +``` + +## 更多插件 + +请到插件仓库进行下载安装: [xmake-plugins](https://github.com/tboox/xmake-plugins). + +#### 从app生成ipa包 + +这仅仅是一个小插件,ios开发的同学,可能会用的到。 + +```console +$ xmake app2ipa --icon=/xxx.png /xxx/ios.app -o /xxx.ios.ipa +``` -- cgit v1.2.3