From 2809e37a97e58c4920b4631052f9903af7a5a74e Mon Sep 17 00:00:00 2001
From: ruki
+
+ If you want to known more options, please run: `xmake create --help` +
+ +#### Build Project + +```bash +$ xmake +``` + +#### Run Program + +```bash +$ xmake run hello +``` + +#### Debug Program + +```bash +$ xmake run -d hello +``` + +It will start the debugger (.e.g lldb, gdb, windbg, vsjitdebugger, ollydbg ..) to load our program. + +```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]$ +``` + ++ You can also use short command option, for exmaple: `xmake r` or `xmake run` +
+ +## Project Examples + +#### Executable Program + +```lua +target("test") + set_kind("binary") + add_files("src/*c") +``` + +#### Static Library Program + +```lua +target("library") + set_kind("static") + add_files("src/library/*.c") + +target("test") + set_kind("binary") + add_files("src/*c") + add_deps("library") +``` + +We use `add_deps` to link a static library to test target. + +#### Share Library Program + +```lua +target("library") + set_kind("shared") + add_files("src/library/*.c") + +target("test") + set_kind("binary") + add_files("src/*c") + add_deps("library") +``` + +We use `add_deps` to link a share library to test target. + +#### Qt Program + +Create an empty project: + +```console +$ xmake create -l c++ -t console_qt test +$ xmake create -l c++ -t static_qt test +$ xmake create -l c++ -t shared_qt test +$ xmake create -l c++ -t quickapp_qt test +``` + +xmake will detect Qt SDK automatically and we can also set the SDK directory manually. + +```console +$ xmake f --qt=~/Qt/Qt5.9.1 +``` + +If you want to use the MinGW Qt environment on windows, you can set the MinGW platform configuration and specify the SDK path for the MinGW compilation environment, for example: + +```console +$ xmake f -p mingw --sdk=C:\Qt\Qt5.10.1\Tools\mingw530_32 +``` + +If you want to known more information, you can see [#160](https://github.com/xmake-io/xmake/issues/160). + +##### Static Library + +```lua +target("qt_static_library") + add_rules("qt.static") + add_files("src/*.cpp") + add_frameworks("QtNetwork", "QtGui") +``` + +##### Shared Library + +```lua +target("qt_shared_library") + add_rules("qt.shared") + add_files("src/*.cpp") + add_frameworks("QtNetwork", "QtGui") +``` + +##### Console Program + +```lua +target("qt_console") + add_rules("qt.console") + add_files("src/*.cpp") +``` + +##### Quick Application + +```lua +target("qt_quickapp") + add_rules("qt.application") + add_files("src/*.cpp") + add_files("src/qml.qrc") + add_frameworks("QtQuick") +``` + +##### Widgets Application + +```lua +target("qt_widgetapp") + add_rules("qt.application") + add_files("src/*.cpp") + add_files("src/mainwindow.ui") + add_files("src/mainwindow.h") -- add files with Q_OBJECT meta (only for qt.moc) + add_frameworks("QtWidgets") +``` + +##### Android Application + +After the 2.2.6 version, you can directly switch to the android platform to compile the Quick/Widgets application, generate the apk package, and install it to the device via the `xmake install` command. + +```console +$ xmake create -t quickapp_qt -l c ++ appdemo +$ cd appdemo +$ xmake f -p android --ndk=~/Downloads/android-ndk-r19c/ --android_sdk=~/Library/Android/sdk/ -c +$ xmake +[0%]: compiling.qt.qrc src/qml.qrc +[ 50%]: ccache compiling.release src/main.cpp +[100%]: linking.release libappdemo.so +[100%]: generating.qt.app appdemo.apk +``` + +Then install to the device: + +```console +$ xmake install +installing appdemo ... +installing build/android/armv7-a/release/appdemo.apk .. +success +install ok!👌 +``` + +#### Cuda Program + +Create an empty project: + +```console +$ xmake create -P test -l cuda +$ cd test +$ xmake +``` + +```lua +-- define target +target("cuda_console") + set_kind("binary") + add_files("src/*.cu") + -- generate SASS code for SM architecture of current host + add_cugencodes("native") + -- generate PTX code for the virtual architecture to guarantee compatibility + add_cugencodes("compute_30") +``` + ++Starting with v2.2.7, the default build will enable device-link, @see https://devblogs.nvidia.com/separate-compilation-linking-cuda-device-code/ +If you want to disable device-link, you can set it with `add_values("cuda.devlink", false)`. +
+ +xmake will detect Cuda SDK automatically and we can also set the SDK directory manually. + +```console +$ xmake f --cuda=/usr/local/cuda-9.1/ +$ xmake +``` + +If you want to known more information, you can see [#158](https://github.com/xmake-io/xmake/issues/158). + +#### WDK Driver Program + +xmake will detect WDK automatically and we can also set the WDK directory manually. + +```console +$ xmake f --wdk="G:\Program Files\Windows Kits\10" -c +$ xmake +``` + +If you want to known more information, you can see [#159](https://github.com/xmake-io/xmake/issues/159). + +##### UMDF Driver Program + +```lua +target("echo") + add_rules("wdk.driver", "wdk.env.umdf") + add_files("driver/*.c") + add_files("driver/*.inx") + add_includedirs("exe") + +target("app") + add_rules("wdk.binary", "wdk.env.umdf") + add_files("exe/*.cpp") +``` + +##### KMDF Driver Program + +```lua +target("nonpnp") + add_rules("wdk.driver", "wdk.env.kmdf") + add_values("wdk.tracewpp.flags", "-func:TraceEvents(LEVEL,FLAGS,MSG,...)", "-func:Hexdump((LEVEL,FLAGS,MSG,...))") + add_files("driver/*.c", {rule = "wdk.tracewpp"}) + add_files("driver/*.rc") + +target("app") + add_rules("wdk.binary", "wdk.env.kmdf") + add_files("exe/*.c") + add_files("exe/*.inf") +``` + +##### WDM Driver Program + +```lua +target("kcs") + add_rules("wdk.driver", "wdk.env.wdm") + add_values("wdk.man.flags", "-prefix Kcs") + add_values("wdk.man.resource", "kcsCounters.rc") + add_values("wdk.man.header", "kcsCounters.h") + add_values("wdk.man.counter_header", "kcsCounters_counters.h") + add_files("*.c", "*.rc", "*.man") +``` + +```lua +target("msdsm") + add_rules("wdk.driver", "wdk.env.wdm") + add_values("wdk.tracewpp.flags", "-func:TracePrint((LEVEL,FLAGS,MSG,...))") + add_files("*.c", {rule = "wdk.tracewpp"}) + add_files("*.rc", "*.inf") + add_files("*.mof|msdsm.mof") + add_files("msdsm.mof", {values = {wdk_mof_header = "msdsmwmi.h"}}) +``` + +##### Package Driver + +We can run the following command to generate a .cab driver package. + +```console +$ xmake [p|package] +$ xmake [p|package] -o outputdir +``` + +The output files like: + +``` + - drivers + - sampledsm + - debug/x86/sampledsm.cab + - release/x64/sampledsm.cab + - debug/x86/sampledsm.cab + - release/x64/sampledsm.cab +``` + +##### Driver Signing + +The driver signing is disabled when we compile driver in default case, +but we can add `set_values("wdk.sign.mode")` to enable test/release sign. + +###### TestSign + +We can use test certificate of xmake to do testsign, but please run `$xmake l utils.wdk.testcert` install as admin to install a test certificate first (only once)! + +```lua +target("msdsm") + add_rules("wdk.driver", "wdk.env.wdm") + set_values("wdk.sign.mode", "test") +``` + +Or we set a valid certificate thumbprint to do it in local machine. + +```lua +target("msdsm") + add_rules("wdk.driver", "wdk.env.wdm") + set_values("wdk.sign.mode", "test") + set_values("wdk.sign.thumbprint", "032122545DCAA6167B1ADBE5F7FDF07AE2234AAA") +``` + +We can also do testsign via setting store/company info. + +```lua +target("msdsm") + add_rules("wdk.driver", "wdk.env.wdm") + set_values("wdk.sign.mode", "test") + set_values("wdk.sign.store", "PrivateCertStore") + set_values("wdk.sign.company", "tboox.org(test)") +``` + +###### ReleaseSign + +We can set a certificate file for release signing. + +```lua +target("msdsm") + add_rules("wdk.driver", "wdk.env.wdm") + set_values("wdk.sign.mode", "release") + set_values("wdk.sign.company", "xxxx") + set_values("wdk.sign.certfile", path.join(os.projectdir(), "xxxx.cer")) +``` + +##### Support Low-version System + +We can set `wdk.env.winver` to generate a driver package that is compatible with a low version system. + +```lua +set_values("wdk.env.winver", "win10") +set_values("wdk.env.winver", "win10_rs3") +set_values("wdk.env.winver", "win81") +set_values("wdk.env.winver", "win8") +set_values("wdk.env.winver", "win7") +set_values("wdk.env.winver", "win7_sp1") +set_values("wdk.env.winver", "win7_sp2") +set_values("wdk.env.winver", "win7_sp3") +``` + +We can also set windows version for WDK driver program: + +```console +$ xmake f --wdk_winver=[win10_rs3|win8|win7|win7_sp1] +$ xmake +``` + +#### WinSDK Application Program + +```lua +target("usbview") + add_rules("win.sdk.application") + + add_files("*.c", "*.rc") + add_files("xmlhelper.cpp", {rule = "win.sdk.dotnet"}) +``` + +If you want to known more information, you can see [#173](https://github.com/xmake-io/xmake/issues/173). + +## Configuration + +Set compilation configuration before building project with command `xmake f|config`. + +And if you want to known more options, please run: `xmake f --help`。 + +
+ You can use short or long command option, for exmaple:
+ `xmake f` or `xmake config`.
+ `xmake f -p linux` or `xmake config --plat=linux`.
+
+ XMake will detect the current host platform automatically and build project. +
+ +##### 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 +``` + +If you want to set the other android toolchains, you can use [--bin](#-bin) option. + +For example: + +```bash +$ xmake f -p android --ndk=~/files/android-ndk-r10e/ -a arm64-v8a --bin=~/files/android-ndk-r10e/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin +``` + +The [--bin](#-bin) option is used to set `bin` directory of toolchains. + ++Please attempt to set `--arch=` option if it had failed to check compiler. +
+ +##### 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 +``` + +##### Cross Compilation + +For linux platform: + +```bash +$ xmake f -p linux --sdk=/usr/local/arm-linux-gcc/ [--bin=/sdk/bin] [--cross=arm-linux-] +$ xmake +``` + +Fro other cross platform: + +```bash +$ xmake f -p cross --sdk=/usr/local/arm-xxx-gcc/ [--bin=/sdk/bin] [--cross=arm-linux-] +$ xmake +``` + +For custem cross platform (`is_plat("myplat")`): + +```bash +$ xmake f -p myplat --sdk=/usr/local/arm-xxx-gcc/ [--bin=/sdk/bin] [--cross=arm-linux-] +$ xmake +``` + +| Configuration Option | Description | +| ---------------------------- | -------------------------------------------- | +| [--sdk](#-sdk) | Set the sdk root directory of toolchains | +| [--bin](#-bin) | Set the `bin` directory of toolchains | +| [--cross](#-cross) | Set the prefix of compilation tools | +| [--as](#-as) | Set `asm` assembler | +| [--cc](#-cc) | Set `c` compiler | +| [--cxx](#-cxx) | Set `c++` compiler | +| [--mm](#-mm) | Set `objc` compiler | +| [--mxx](#-mxx) | Set `objc++` compiler | +| [--sc](#-sc) | Set `swift` compiler | +| [--gc](#-gc) | Set `golang` compiler | +| [--dc](#-dc) | Set `dlang` compiler | +| [--rc](#-rc) | Set `rust` compiler | +| [--cu](#-cu) | Set `cuda` compiler | +| [--ld](#-ld) | Set `c/c++/objc/asm` linker | +| [--sh](#-sh) | Set `c/c++/objc/asm` shared library linker | +| [--ar](#-ar) | Set `c/c++/objc/asm` static library archiver | +| [--sc-ld](#-sc-ld) | Set `swift` linker | +| [--sc-sh](#-sc-sh) | Set `swift` shared library linker | +| [--gc-ld](#-gc-ld) | Set `golang` linker | +| [--gc-ar](#-gc-ar) | Set `golang` static library archiver | +| [--dc-ld](#-dc-ld) | Set `dlang` linker | +| [--dc-sh](#-dc-sh) | Set `dlang` shared library linker | +| [--dc-ar](#-dc-ar) | Set `dlang` static library archiver | +| [--rc-ld](#-rc-ld) | Set `rust` linker | +| [--rc-sh](#-rc-sh) | Set `rust` shared library linker | +| [--rc-ar](#-rc-ar) | Set `rust` static library archiver | +| [--cu-cxx](#-cu-cxx) | Set `cuda` host compiler | +| [--cu-ld](#-cu-ld) | Set `cuda` linker | +| [--asflags](#-asflags) | Set `asm` assembler option | +| [--cflags](#-cflags) | Set `c` compiler option | +| [--cxflags](#-cxflags) | Set `c/c++` compiler option | +| [--cxxflags](#-cxxflags) | Set `c++` compiler option | +| [--mflags](#-mflags) | Set `objc` compiler option | +| [--mxflags](#-mxflags) | Set `objc/c++` compiler option | +| [--mxxflags](#-mxxflags) | Set `objc++` compiler option | +| [--scflags](#-scflags) | Set `swift` compiler option | +| [--gcflags](#-gcflags) | Set `golang` compiler option | +| [--dcflags](#-dcflags) | Set `dlang` compiler option | +| [--rcflags](#-rcflags) | Set `rust` compiler option | +| [--cuflags](#-cuflags) | Set `cuda` compiler option | +| [--ldflags](#-ldflags) | Set linker option | +| [--shflags](#-shflags) | Set shared library linker option | +| [--arflags](#-arflags) | Set static library archiver option | + ++if you want to known more options, please run: `xmake f --help`。 +
+ +###### --sdk + +- Set the sdk root directory of toolchains + +xmake provides a convenient and flexible cross-compiling support. +In most cases, we need not to configure complex toolchains prefix, for example: `arm-linux-` + +As long as this toolchains meet the following directory structure: + +``` +/home/toolchains_sdkdir + - bin + - arm-linux-gcc + - arm-linux-ld + - ... + - lib + - libxxx.a + - include + - xxx.h +``` + +Then,we can only configure the sdk directory and build it. + +```bash +$ xmake f -p linux --sdk=/home/toolchains_sdkdir +$ xmake +``` + +xmake will detect the prefix: arm-linux- and add the include and library search directory automatically. + +``` +-I/home/toolchains_sdkdir/include -L/home/toolchains_sdkdir/lib +``` + +###### --bin + +- Set the `bin` directory of toolchains + +We need set it manually if the toolchains /bin directory is in other places, for example: + +```bash +$ xmake f -p linux --sdk=/home/toolchains_sdkdir --bin=/usr/opt/bin +$ xmake +``` + ++Before v2.2.1 version, this parameter name is `--toolchains`, exists more ambiguous, so we changed to `--bin=` to set the bin directory. +
+ +###### --cross + +- Set the prefix of compilation tools + +For example, under the same toolchains directory at the same time, there are two different compilers: + +``` +/opt/bin + - armv7-linux-gcc + - aarch64-linux-gcc +``` + +If we want to use the `armv7-linux-gcc` compiler, we can run the following command: + +```bash +$ xmake f -p linux --sdk=/usr/toolsdk --bin=/opt/bin --cross=armv7-linux- +``` + +###### --as + +- Set `asm` assembler + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --as=armv7-linux-as +``` + +If the 'AS' environment variable exists, it will use the values specified in the current environment variables. + ++We can set a unknown compiler as like-gcc/clang compiler, .e.g `xmake f --as=gcc@/home/xxx/asmips.exe` +
+ +###### --cc + +- Set c compiler + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --cc=armv7-linux-clang +``` + +If the 'CC' environment variable exists, it will use the values specified in the current environment variables. + ++We can set a unknown compiler as like-gcc/clang compiler, .e.g `xmake f --cc=gcc@/home/xxx/ccmips.exe` +
+ +###### --cxx + +- Set `c++` compiler + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --cxx=armv7-linux-clang++ +``` + +If the 'CXX' environment variable exists, it will use the values specified in the current environment variables. + ++We can set a unknown compiler as like-gcc/clang compiler, .e.g `xmake f --cxx=g++@/home/xxx/c++mips.exe` +
+ +###### --ld + +- Set `c/c++/objc/asm` linker + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --ld=armv7-linux-clang++ +``` + +If the 'LD' environment variable exists, it will use the values specified in the current environment variables. + ++We can set a unknown compiler as like-gcc/clang linker, .e.g `xmake f --ld=g++@/home/xxx/c++mips.exe` +
+ +###### --sh + +- Set `c/c++/objc/asm` shared library linker + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --sh=armv7-linux-clang++ +``` + +If the 'SH' environment variable exists, it will use the values specified in the current environment variables. + ++We can set a unknown compiler as like-gcc/clang linker, .e.g `xmake f --sh=g++@/home/xxx/c++mips.exe` +
+ +###### --ar + +- Set `c/c++/objc/asm` static library archiver + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --ar=armv7-linux-ar +``` + +If the 'AR' environment variable exists, it will use the values specified in the current environment variables. + ++We can set a unknown compiler as like-ar archiver, .e.g `xmake f --ar=ar@/home/xxx/armips.exe` +
+ +#### Global Configuration + +You can save to the global configuration for simplfying operation. + +For example: + +```bash +$ xmake g --ndk=~/files/android-ndk-r10e/ +``` + +Now, we config and build project for android again. + +```bash +$ xmake f -p android +$ xmake +``` + +
+ You can use short or long command option, for exmaple: `xmake g` or `xmake global`.
+
+
+For more information and progress on package dependency management see the related issues: [Remote package management](https://github.com/xmake-io/xmake/issues/69)
+
+##### Currently Supported Features
+
+* Semantic version support, for example: ">= 1.1.0 < 1.2", "~1.6", "1.2.x", "1.*"
+* Provide multi-warehouse management support such as official package warehouse, self-built private warehouse, project built-in warehouse, etc.
+* Cross-platform package compilation integration support (packages of different platforms and different architectures can be installed at the same time, fast switching use)
+* Debug dependency package support, source code debugging
+
+##### Dependency Package Processing Mechanism
+
+Here we briefly introduce the processing mechanism of the entire dependency package:
+
+
++This interface has been deprecated after v2.2.2, please use [has_config](#has_config) instead. +
+ +You can use this api to check the custom option configuration command:`xmake f --xxxx=y` + +For example, we want to enable the custom option: `xmake f --demo=y` and check it from `xmake.lua`. + +```lua +if is_option("demo") then + add_subdirs("src/demo") +end +``` + +##### is_config + +###### Is the given config values? + +This interface is introduced from version 2.2.2 to determine whether the specified configuration is a given value. + +For example: + +```console +$ xmake f --test=hello1 +``` + +```lua +option("test") + set_showmenu("true") + set_description("The test config option") +option_end() + +if is_config("test", "hello1", "hello2") then + add_defines("HELLO") +end +``` + +Not only that, we can also set pattern matching rules to determine values, such as: + +```lua +if is_config("test", "hello.*") then + add_defines("HELLO") +end +``` + ++This interface is not only able to determine the custom options defined through the [option](#option), +but also to determine the built-in global and local configuration. +
+ +##### has_config + +###### Is the given configs enabled? + +This interface is introduced from version 2.2.2 to detect whether a custom or built-in option/configuration exists or is enabled. + +For example, the following configuration will be true: + +```console +# enable the given config or option (if be boolean type) +$ xmake f --test1=y +$ xmake f --test1=yes +$ xmake f --test1=true + +# set the config value +$ xmake f --test2=value +``` + +```lua +if has_config("test1", "test2") then + add_defines("TEST") +end +``` + +And the following configuration will be false: + +```console +# disable config/option(if be boolean type) +$ xmake f --test1=n +$ xmake f --test1=no +$ xmake f --test1=false +``` + ++This interface can determine not only the built-in global and local configs, +but also the custom options defined through the [option](#option). +
+ +##### has_package + +###### Is the given dependent package enabled? + +This interface is introduced from version 2.2.3 to detect whether a dependent package exists or is enabled. + +It is usually used to [add_requires](#add_requires). + +```lua +add_requires("tbox", {optional = true}) + +target("test") + set_kind("binary") + add_files("src/*.c") + add_packages("tbox") + + if has_package("tbox") then + add_defines("HAVE_TBOX") + end +``` + +If the remote dependencies are added via the optional add-on package added by `add_requires`, or the current platform does not support the actual installation, then `has_package` will return false. +Indicates that it does not exist, and then does some special processing for other flags definitions and even source file compilation controls. + ++The difference between this interface and [has_config](#has_config) is that [has_config](#has_config) is used for [option](#option) whereas this is used for [add_requires](#add_requires). +
+ +#### Global Interfaces + +The global interface affects the whole project description scope and all sub-project files. + +| Interfaces | Description | Version | +| ------------------------------------- | ------------------------------------- | -------- | +| [includes](#includes) | Add sub-project files and directories | >= 2.1.5 | +| [set_modes](#set_modes) | Set project compilation modes | >= 2.1.2 | +| [set_project](#set_project) | Set project name | >= 2.0.1 | +| [set_version](#set_version) | Set project version | >= 2.0.1 | +| [set_xmakever](#set_xmakever) | Set minimal xmake version | >= 2.1.1 | +| [add_subdirs](#add_subdirs) | Add sub-project directories | >= 1.0.1 | +| [add_subfiles](#add_subfiles) | Add sub-project files | >= 1.0.1 | +| [add_moduledirs](#add_moduledirs) | Add module directories | >= 2.1.5 | +| [add_plugindirs](#add_plugindirs) | Add plugin directories | >= 2.0.1 | +| [add_packagedirs](#add_packagedirs) | Add package directories | >= 2.0.1 | +| [get_config](#get_config) | Get the configuration value | >= 2.2.2 | +| [set_config](#set_config) | Set the default configuration value | >= 2.2.2 | +| [add_requires](#add_requires) | Add required package dependencies | >= 2.2.2 | +| [add_repositories](#add_repositories) | Add 3rd package repositories | >= 2.2.2 | + +##### includes + +###### Add sub-project files and directories + +It is used to replace [add_subdirs](#add_subdirs) and [add_subfiles](#add_subfiles). + +In addition, in 2.2.5 and later, this interface provides some built-in helper functions, which can be used directly after the include, specifically which built-in functions can be seen at: https://github.com/xmake-io/xmake/tree/master/xmake/includes + +For a more complete description of this, see: [https://github.com/xmake-io/xmake/issues/342](https://github.com/xmake-io/xmake/issues/342 ) + +##### set_modes + +###### Set project compilation modes + +This is an optional api, just to make it easy for plugins to get mode configuration information. + +```lua +set_modes("debug", "release") +``` + +If you set this configuration, you need not set them manually when generating vs201x project. + +```bash +$ xmake project -k vs2017 +``` + +Otherwise, you need to run: + +```bash +$ xmake project -k vs2017 -m "debug,release" +``` + ++If you do not set this configuration, [is_mode](#is_mode) can also be used normally. +
+ +##### set_project + +###### Set project name + +Set the whole project name, we can set it at the beginning of `xmake.lua`. + +```lua +-- set project name +set_project("tbox") + +-- set project version +set_version("1.5.1") +``` + +##### set_version + +###### Set project version + +Set the whole project version, we can set it at the beginning of `xmake.lua`. + +```lua +set_version("1.5.1") +``` + +It will add project version info to this file automatically if we call [set_config_header](#targetset_config_header) to set `config.h`.` + +For example: + +```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 +``` + +We can set build version in v2.1.7 version: + +```lua +set_version("1.5.1", {build = "%Y%m%d%H%M"}) +``` + +##### set_xmakever + +###### Set minimal xmake version + +If the current xmake version less than the required version, it will prompt an error. + +```lua +-- the current xmake version must be larger than 2.1.0 +set_xmakever("2.1.0") +``` + +##### add_subdirs + +###### Add sub-project directories + ++For xmake 2.x and above, try to use the [includes](#includes) interface, which is a generic version of add_subdirs and add_subfiles, and supports some built-in extensions. +
+ +This interface will add sub-project directories to the current `xmake.lua`, it will load the `xmake.lua` file of the sub-directories. + +For example, assume we have the following project directory tree: + +``` +./tbox +├── src +│ ├── demo +│ │ └── xmake.lua +│ └── tbox +│ └── xmake.lua +└── xmake.lua +```` + +We can add sub-project `tbox` and `demo` directories to the root `xmake.lua`. + +```lua +add_subdirs("src/tbox") +if is_option("demo") then + add_subdirs("src/demo") +end +``` + +By default, xmake will compile all targets. If you only want to compile a specific target, you can do: + +```bash +# only build `tbox` target +$ xmake build tbox +``` + +##### add_subfiles + +###### Add sub-project files + ++For xmake 2.x and above, try to use the [includes](#includes) interface, which is a generic version of add_subdirs and add_subfiles, and supports some built-in extensions. +
+ +`add_subfiles` is similar to [add_subdirs](#add_subdirs). + +The only difference is that this interface specifies the path to the 'xmake.lua' file directly, rather than a directory. + +for example: + +```lua +add_subfiles("src/tbox/xmake.lua") +``` + +##### add_moduledirs + +###### Add module directories + +The builtin modules are placed in the 'xmake/modules' directory, but for user-defined modules for a specific project, you can configure additional module directories in the 'xmake.lua` file. + +```lua +add_moduledirs("$(projectdir)/modules") +``` +xmake will load the given module in the given directory when calling `import`. + +##### add_plugindirs + +###### Add plugin directories + +The builtin plugins are placed in the 'xmake/plugins' directory, but for user-defined plugins for a specific project, you can configure additional plugin directories in the 'xmake.lua` file. + +```lua +add_plugindirs("$(projectdir)/plugins") +``` +xmake will load all plugins in the given directory. + +##### add_packagedirs + +###### Add package directories + +By setting up a dependency package directory, you can easily integrate some third-party dependent libraries. +Taking the tbox project as an example, its package directory is as follows: + + +``` +tbox.pkg +- base.pkg +- zlib.pkg +- polarssl.pkg +- openssl.pkg +- mysql.pkg +- pcre.pkg +- ... +``` + +If you want the current project to load these packages, first specify the package directory path, for example: + +```lua +add_packagedirs("pkg") +``` + +Then, please add these packages to the given target by [add_packages](#add_packages): + +```lua +target("tbox") + add_packages("zlib", "polarssl", "pcre", "mysql") +``` + +xmake will check these packages automatically and link with them if they exist, and we can disable them manually. + +```bash +$ xmake f --openssl=n +``` + +##### get_config + +###### Get the configuration value + +This interface is introduced from version 2.2.2 to get the configuration value from the given name. + +```lua +if get_config("myconfig") == "xxx" then + add_defines("HELLO") +end +``` + +##### set_config + +###### Set the default configuration value + +This interface is introduced from version 2.2.2 to set the default configuration value in xmake.lua. + +Many previous configurations, including the build toolchain, build directory, etc. +We can only be configured by `$xmake f --name=value`. If we want to write a default value in xmake.lua, we can use the following method: + +```lua +set_config("name", "value") +set_config("buildir", "other/buildir") +set_config("cc", "gcc") +set_config("ld", "g++") +``` + +However, we can still modify the default configuration in xmake.lua by `$xmake f --name=value`. + +##### add_requires + +###### Add package dependencies + +Xmake's dependency package management fully supports semantic version selection, for example: "~1.6.1". For a detailed description of semantic versioning, see: [https://semver.org/](https://semver.org/) + +Some examples: + +```lua +add_requires("tbox 1.6.*", "pcre 1.3.x", "libpng ^1.18") +add_requires("libpng ~1.16", "zlib 1.1.2 || >=1.2.11 <1.3.0") +``` + +The semantic version parser currently used by xmake is the [sv](https://github.com/uael/sv) library contributed by [uael](https://github.com/uael), which also has a description of the version. For detailed instructions, please refer to the following: [Version Description](https://github.com/uael/sv#versions) + +Of course, if we have no special requirements for the version of the dependency package, we can omit the version: + +```lua +add_requires("tbox", "libpng", "zlib") +``` + +This will use the latest known version of the package, or the source code compiled from the master branch. If the current package has a git repo address we can also specify a specific branch version: + +```lua +add_requires("tbox master") +add_requires("tbox dev") +``` + +If the specified dependency package is not supported by the current platform, or if the compilation and installation fails, then xmake will exit with an error, which is reasonable for some projects that must rely on certain packages to work. +However, if some packages are optional dependencies, they can be set to optional packages even if they are not compiled properly. + +```lua +add_requires("tbox", {optional = true}) +``` + +With the default settings, xmake will first check to see if the system library exists (if no version is required). If the user does not want to use the system library and the library is provided by a third-party package manager, then you can set: + +```lua +add_requires("tbox", {system = false}) +``` + +If we want to debug the dependencies at the same time, we can set them to use the debug version of the package (provided that this package supports debug compilation): + +```lua +add_requires("tbox", {debug = true}) +``` + +If the current package does not support debug compilation, you can submit the modified compilation rules in the repository to support the debug, for example: + +```lua +package("openssl") + on_install("linux", "macosx", function (package) + os.vrun("./config %s --prefix=\"%s\"", package:debug() and "--debug" or "", package:installdir()) + os.vrun("make -j4") + os.vrun("make install") + end) +``` + +Some packages have various compile options at compile time, and we can pass them in. Of course, the package itself supports: + +```lua +add_requires("tbox", {config = {small=true}}) +``` + +Pass `--small=true` to the tbox package so that compiling the installed tbox package is enabled. +After v2.2.3, you can control whether you need to add a dependency package in your own definition configuration option parameter by [option](#option) and [has_config](#has_config): + +```lua +option("luajit") + set_default(false) + set_showmenu(true) + set_category("option") + set_description("Enable the luajit runtime engine.") +option_end() + +if has_config("luajit") then + add_requires("luajit") +else + add_requires("lua") +end +``` + +We can switch dependencies by `$xmake f --luajit=y`. + +And we also added the group parameter to group the dependencies, all the dependencies under the same group, only one can be enabled, the order of the dependencies is the same as the order in which they were added by `add_requires`: + +```lua +add_requires("openssl", {group = "ssl", optional = true}) +add_requires("mbedtls", {group = "ssl", optional = true}) + +target("test") + add_packages("openssl", "mbedtls") +``` + +After version 2.2.5, xmake supports third-party package managers, such as: conan, brew, vcpkg, etc. + +Add a homebrew dependency package: + +```lua +add_requires("brew::zlib", {alias = "zlib"}}) +add_requires("brew::pcre2/libpcre2-8", {alias = "pcre2"}}) + +target("test") + set_kind("binary") + add_files("src/*.c") + add_packages("pcre2", "zlib") +``` + +Add a dependency package for vcpkg: + +```lua +add_requires("vcpkg::zlib", "vcpkg::pcre2") + +target("test") + set_kind("binary") + add_files("src/*.c") + add_packages("vcpkg::zlib", "vcpkg::pcre2") +``` + +Add a conan dependency package: + +```lua +add_requires("CONAN::zlib/1.2.11@conan/stable", {alias = "zlib", debug = true}) +add_requires("CONAN::OpenSSL/1.0.2n@conan/stable", {alias = "openssl", + configs = {options = "OpenSSL:shared=True"}}) + +target("test") + set_kind("binary") + add_files("src/*.c") + add_packages("openssl", "zlib") +``` + +After executing xmake to compile: + +```console +ruki:test_package ruki$ xmake +checking for the architecture ... x86_64 +checking for the Xcode directory ... /Applications/Xcode.app +checking for the SDK version of Xcode ... 10.14 +note: try installing these packages (pass -y to skip confirm)? + -> CONAN::zlib/1.2.11@conan/stable (debug) + -> CONAN::OpenSSL/1.0.2n@conan/stable +please input: y (y/n) + + => installing CONAN::zlib/1.2.11@conan/stable .. ok + => installing CONAN::OpenSSL/1.0.2n@conan/stable .. ok + +[ 0%]: ccache compiling.release src/main.c +[100%]: linking.release test +``` + +We can see https://github.com/xmake-io/xmake/issues/339 to know more details. + +Add a clib dependency package: + +Clib is a source-based dependency package manager. The dependent package is downloaded directly to the corresponding library source code, integrated into the project to compile, rather than binary library dependencies. + +It is also very convenient to integrate in xmake. The only thing to note is that you need to add the source code of the corresponding library to xmake.lua, for example: + +```lua +add_requires("clib::clibs/bytes@0.0.4", {alias = "bytes"}) + +target("xmake-test") + set_kind("binary") + add_files("clib/bytes/*.c") + add_files("src/*.c") + add_packages("bytes") +``` + +##### add_repositories + +###### Add 3rd package repositories + +If the required package is not in the official repository [xmake-repo](https://github.com/xmake-io/xmake-repo), we can submit the contribution code to the repository for support. +But if some packages are only for personal or private projects, we can create a private repository repo. The repository organization structure can be found at: [xmake-repo](https://github.com/xmake-io/xmake-repo) + +For example, now we have a private repository repo:`git@github.com:myrepo/xmake-repo.git` + +We can add through this interface: + +```lua +add_repositories("my-repo git@github.com:myrepo/xmake-repo.git") +``` + +If we just want to add one or two private packages, this time to build a git repo is too big, we can directly put the package repository into the project, for example: + +``` +projectdir + - myrepo + - packages + - t/tbox/xmake.lua + - z/zlib/xmake.lua + - src + - main.c + - xmake.lua +``` + +The above myrepo directory is your own private package repository, built into your own project, and then add this repository location in xmake.lua: + +```lua +add_repositories("my-repo myrepo") +``` + +This can be referred to [benchbox](https://github.com/tboox/benchbox) project, which has a built-in private repository. + +#### Project Target + +We can use `target("test")` to define a project target named "test", each target generates an executable program, a static library, or a dynamic library. + ++All interfaces of target can be set in the global scope, which affects all sub-targets. +
+ +For example: + +```lua +-- affects both test and test2 targets +add_defines("DEBUG") + +target("test") + add_files("*.c") + +target("test2") + add_files("*.c") +``` + ++`target()' interface can be repeatedly invoked in different places to set the same target. +
+ +| Interfaces | Description | Support version | +| --------------------------------------------- | ------------------------------------------------------ | --------------------------- | +| [target](#target) | Define a project target | >= 1.0.1 | +| [target_end](#target_end) | End target definition | >= 2.1.1 | +| [set_kind](#targetset_kind) | Set target kind | >= 1.0.1 | +| [set_strip](#targetset_strip) | Strip target symbols | >= 1.0.1 | +| [set_enabled](#targetset_enabled) | Enable or disable target | >= 2.2.2 | +| [set_default](#targetset_default) | Mark as default target | >= 2.1.3 | +| [set_options](#targetset_options) | Set configuartion options | >= 1.0.1 | +| [set_symbols](#targetset_symbols) | Set symbol info | >= 1.0.1 | +| [set_basename](#targetset_basename) | Set the base name of target file | >= 2.1.2 | +| [set_filename](#targetset_filename) | Set the full name of target file | >= 2.1.2 | +| [set_warnings](#targetset_warnings) | Set compilation warning level | >= 1.0.1 | +| [set_optimize](#targetset_optimize) | Set compilation optimization level | >= 1.0.1 | +| [set_languages](#targetset_languages) | Set source code language standards | >= 1.0.1 | +| [set_headerdir](#targetset_headerdir) | Set output directories for header files | >= 1.0.1 < 2.2.5 deprecated | +| [set_targetdir](#targetset_targetdir) | Set output directories for target file | >= 1.0.1 | +| [set_objectdir](#targetset_objectdir) | Set output directories for object files | >= 1.0.1 | +| [set_dependir](#targetset_dependir) | Set output directories for dependent files | >= 2.2.2 | +| [add_imports](#targetadd_imports) | Add imported modules for the custom script | >= 2.1.7 | +| [add_rules](#targetadd_rules) | Add custom compilation rule to target | >= 2.1.9 | +| [on_load](#targeton_load) | Run custom load target configuartion script | >= 2.1.5 | +| [on_link](#targeton_link) | Run custom link target script | >= 2.2.7 | +| [on_build](#targeton_build) | Run custom build target script | >= 2.0.1 | +| [on_build_file](#targeton_build_file) | Run custom build single file script | >= 2.2.3 | +| [on_build_files](#targeton_build_files) | Run custom build files script | >= 2.2.3 | +| [on_clean](#targeton_clean) | Run custom clean files script | >= 2.0.1 | +| [on_package](#targeton_package) | Run custom package target script | >= 2.0.1 | +| [on_install](#targeton_install) | Run custom install target file script | >= 2.0.1 | +| [on_uninstall](#targeton_uninstall) | Run custom uninstall target file script | >= 2.0.1 | +| [on_run](#targeton_run) | Run custom run target script | >= 2.0.1 | +| [before_link](#targetbefore_link) | Run custom script before linking target | >= 2.2.7 | +| [before_build](#targetbefore_build) | Run custom script before building target | >= 2.0.1 | +| [before_build_file](#targetbefore_build_file) | Run custom script before building single file | >= 2.2.3 | +| [before_build_files](#targetbefore_build_files) | Run custom script before building files | >= 2.2.3 | +| [before_clean](#targetbefore_clean) | Run custom script before cleaning target | >= 2.0.1 | +| [before_package](#targetbefore_package) | Run custom script before packaging target | >= 2.0.1 | +| [before_install](#targetbefore_install) | Run custom script before installing target | >= 2.0.1 | +| [before_uninstall](#targetbefore_uninstall) | Run custom script before uninstalling target | >= 2.0.1 | +| [before_run](#targetbefore_run) | Run custom script before running target | >= 2.0.1 | +| [after_link](#targetafter_link) | Run custom script after linking target | >= 2.2.7 | +| [after_build](#targetafter_build) | Run custom script after building target | >= 2.0.1 | +| [after_build_file](#targetafter_build_file) | Run custom script after building single file | >= 2.2.3 | +| [after_build_files](#targetafter_build_files) | Run custom script after building files | >= 2.2.3 | +| [after_clean](#targetafter_clean) | Run custom script after cleaning target | >= 2.0.1 | +| [after_package](#targetafter_package) | Run custom script after packaging target | >= 2.0.1 | +| [after_install](#targetafter_install) | Run custom script after installing target | >= 2.0.1 | +| [after_uninstall](#targetafter_uninstall) | Run custom script after uninstalling target | >= 2.0.1 | +| [after_run](#targetafter_run) | Run custom script after running target | >= 2.0.1 | +| [set_config_h](#targetset_config_h) | Set auto-generated config header file | >= 1.0.1 < 2.1.5 deprecated | +| [set_config_h_prefix](#targetset_config_h) | Set macro prefix in auto-generated config header | >= 1.0.1 < 2.1.5 deprecated | +| [set_config_header](#targetset_config_header) | Set auto-generated config header file | >= 2.1.5 < 2.2.5 deprecated | +| [set_pcheader](#targetset_pcheader) | Set pre-compiled c header file | >= 2.1.5 | +| [set_pcxxheader](#targetset_pcxxheader) | Set pre-compiled c++ header file | >= 2.1.5 | +| [add_deps](#targetadd_deps) | Add target dependencies | >= 1.0.1 | +| [add_links](#targetadd_links) | Add link libraries | >= 1.0.1 | +| [add_syslinks](#targetadd_syslinks) | Add system link libraries | >= 2.2.3 | +| [add_files](#targetadd_files) | Add source files | >= 1.0.1 | +| [del_files](#targetdel_files) | Remove source files | >= 2.1.9 | +| [add_headers](#targetadd_headers) | Add installed header files | >= 1.0.1 < 2.2.5 deprecated | +| [add_linkdirs](#targetadd_linkdirs) | Add link search directories | >= 1.0.1 | +| [add_rpathdirs](#targetadd_rpathdirs) | Add load search directories for dynamic library | >= 2.1.3 | +| [add_includedirs](#targetadd_includedirs) | Add include search directories | >= 1.0.1 | +| [add_defines](#targetadd_defines) | Add macro definition | >= 1.0.1 | +| [add_undefines](#targetadd_undefines) | Add macro undefinition | >= 1.0.1 | +| [add_defines_h](#targetadd_defines_h) | Add macro definition to auto-generated config header | >= 1.0.1 < 2.1.5 deprecated | +| [add_undefines_h](#targetadd_undefines_h) | Add macro undefinition to auto-generated config header | >= 1.0.1 < 2.1.5 deprecated | +| [add_cflags](#targetadd_cflags) | Add c compilation flags | >= 1.0.1 | +| [add_cxflags](#targetadd_cxflags) | Add c/c++ compilation flags | >= 1.0.1 | +| [add_cxxflags](#targetadd_cxxflags) | Add c++ compilation flags | >= 1.0.1 | +| [add_mflags](#targetadd_mflags) | Add objc compilation flags | >= 1.0.1 | +| [add_mxflags](#targetadd_mxflags) | Add objc/objc++ compilation flags | >= 1.0.1 | +| [add_mxxflags](#targetadd_mxxflags) | Add objc++ compilation flags | >= 1.0.1 | +| [add_scflags](#targetadd_scflags) | Add swift compilation flags | >= 2.0.1 | +| [add_asflags](#targetadd_asflags) | Add asm compilation flags | >= 2.0.1 | +| [add_gcflags](#targetadd_gcflags) | Add go compilation flags | >= 2.1.1 | +| [add_dcflags](#targetadd_dcflags) | Add dlang compilation flags | >= 2.1.1 | +| [add_rcflags](#targetadd_rcflags) | Add rust compilation flags | >= 2.1.1 | +| [add_cuflags](#targetadd_cuflags) | Add cuda compilation flags | >= 2.1.1 | +| [add_culdflags](#targetadd_culdflags) | Add cuda device-link flags | >= 2.2.7 | +| [add_ldflags](#targetadd_ldflags) | Add static library link flags | >= 1.0.1 | +| [add_arflags](#targetadd_arflags) | Add archive library flags | >= 1.0.1 | +| [add_shflags](#targetadd_shflags) | Add dynamic library link flags | >= 1.0.1 | +| [add_cfunc](#targetadd_cfunc) | Add single c function for checking | >= 2.0.1 | +| [add_cxxfunc](#targetadd_cxxfunc) | Add single c++ function for checking | >= 2.0.1 | +| [add_cfuncs](#targetadd_cfuncs) | Add c functions for checking | >= 2.0.1 | +| [add_cxxfuncs](#targetadd_cxxfuncs) | Add c++ functions for checking | >= 2.0.1 | +| [add_packages](#targetadd_packages) | Add package dependencies | >= 2.0.1 | +| [add_options](#targetadd_options) | Add options dependencies | >= 2.0.1 | +| [add_languages](#targetadd_languages) | Add language standards | >= 1.0.1 | +| [add_vectorexts](#targetadd_vectorexts) | Add vector extensions | >= 1.0.1 | +| [add_frameworks](#targetadd_frameworks) | Add frameworks | >= 2.1.1 | +| [add_frameworkdirs](#targetadd_frameworkdirs) | Add framework search directories | >= 2.1.5 | +| [set_tools](#targetset_tools) | Set toolchains | >= 2.2.1 | +| [add_tools](#targetadd_tools) | Add toolchains | >= 2.2.1 | +| [set_values](#targetset_values) | Set custom configuartion values | >= 2.2.1 | +| [add_values](#targetadd_values) | Add custom configuartion values | >= 2.2.1 | +| [set_rundir](#targetset_rundir) | Set run directory | >= 2.2.7 | +| [add_runenvs](#targetadd_runenvs) | Add run environments | >= 2.2.7 | +| [set_installdir](#targetset_installdir) | Set the installation directory | >= 2.2.5 | +| [add_installfiles](#targetadd_installfiles) | add installation files | >= 2.2.5 | +| [add_headerfiles](#targetadd_headerfiles) | Add header files | >= 2.2.5 | +| [set_configdir](#targetset_configdir) | Set the output directory of configuartion files | >= 2.2.5 | +| [set_configvar](#targetset_configvar) | Set template configuartion variable | >= 2.2.5 | +| [add_configfiles](#targetadd_configfiles) | Add template configuartion files | >= 2.2.5 | + +##### target + +###### Define a project target + +Defines a console target named `test` in project and the default target filename is `test`. + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") +``` + +And we can call `target("demo")` repeatly to enter the target scope for modifying it's configuartion. + +```lua +-- defines target: demo and enter it's scope to set configuartion +target("demo") + set_kind("binary") + add_files("src/demo.c") + +-- defines and set `other` target +target("other") + ... + +-- re-enter demo target scope and add file `test.c` to `demo` +target("demo") + add_files("src/test.c") +``` + ++All configuartion in root scope affects all targets, but does not affect the configuartion of `option()`. +
+ +For example: + +```lua +add_defines("DEBUG") + +target("demo") -- add -DDEBUG + set_kind("binary") + add_files("src/demo.c") + +target("test") -- add -DDEBUG + set_kind("binary") + add_files("src/test.c") +``` + +##### target_end + +###### End target definition + +This is an optional api. If not called, then all settings after +`target("xxx")` are made for that target, unless you enter other +`target`, `option` or `task` scope. If you want to leave the current +`target` and enter the root scope setting, then you can use this api. For example: + +```lua +target("test") + set_kind("static") + add_files("src/*.c") +target_end() + +-- Here we are in the root scope +-- ... +``` + +If you don't call this api: + +```lua +target("test") + set_kind("static") + add_files("src/*.c") + +-- Here we are in the target scope above, the subsequent settings are still +set for test +-- ... + +-- Enter another target scope +target("test2") + ... +``` + +##### target:set_kind + +###### Set target kind + +Set the target type. Currently supported types are: + +| Value | Description | +| ------ | -----------| +| binary | Binary Program | +| static | Static library program | +| shared | dynamic library program | + +```lua +target("demo") + set_kind("binary") +``` + +##### target:set_strip + +###### Strip target symbols + +Set the current target strip mode, currently supports the mode: + +| Value | Description | +| ------ | ----------------------------------------- | +| debug | When you link, strip off debugging symbols | +| all | When you link, strip all symbols, including debugging symbols | + +This api is generally used in release mode and can generate smaller binary programs. + +```lua +target("xxxx") + set_strip("all") +``` + ++This api does not have to be used after the target. If no target is specified, it will be set to global mode. . +
+ +##### target:set_enabled + +###### Enable or disable target + +If `set_enabled(false)` is set, the corresponding target will be directly disabled, including target loading and information acquisition, while [set_default](#targetset_default) is just set to not compile by default, but the target can still get related information. , the default will also be loaded. + +##### target:set_default + +###### Mark as default target + +This interface is used to set whether the given project target is the default build. If this interface is not called for setting, then this target is built by default, for example: + +```lua +target("test1") + set_default(false) + +target("test2") + set_default(true) + +target("test3") + ... +``` + +The three goals of the above code, when executing the `xmake`, `xmake install`, `xmake package`, `xmake run` and other commands, if you do not specify the target name, then: + +| Target Name | Behavior | +| ------ | -------------------------------- | +| test1 | will not be built, installed, packaged, and run by default | +| test2 | Default build, install, package, and run | +| test3 | Default build, install, package, and run | + +Through the above example, you can see that the default target can be set more than one, and it will run in turn when running. + ++ Note that the `xmake uninstall` and `xmake clean` commands are not affected by this interface setting, as most users prefer to clean and unload all of them. +
+ +If you don't want to use the default target, you can manually specify which targets you need to build the installation: + +```bash +$ xmake build targetname +$ xmake install targetname +``` + +If you want to force the build to install all targets, you can pass in the `[-a|--all]` parameter: + +```bash +$ xmake build [-a|--all] +$ xmake install [-a|--all] +``` + +##### target:set_options + +###### Set configuartion options + +Add option dependencies. If you have customized some options through the [option](#option) interface, you can add associations only if you specify this option under the target target field. + +```lua +-- Define a hello option +option("hello") + set_default(false) + set_showmenu(true) + add_defines("HELLO_ENABLE") + +target("test") + -- If the hello option is enabled, this time the -DHELLO_ENABLE macro will be applied to the test target. + set_options("hello") +``` + ++Some settings defined in [option](#option) will affect this `target` target only after calling `set_options` for the association to take effect, such as macro definitions, link libraries, compile options, etc. +
+ +##### target:set_symbols + +###### Set symbol info + +Set the symbol mode of the target. If no target is currently defined, it will be set to the global state, affecting all subsequent targets. + +At present, we mainly support several levels: + +| Value | Description | +| ------ | ---------------------- | +| debug | Add debug symbols | +| hidden | set symbol not visible | + +These two values can also be set at the same time, for example: + +```lua +-- Add debug symbols, set symbols are not visible +set_symbols("debug", "hidden") +``` + +If this api is not called, the debug symbol is disabled by default. . + +##### target:set_basename + +###### Set the base name of target file + +By default, the generated target file name is based on the value configured in `target("name")`, for example: + +```lua +-- The target file name is: libxxx.a +target("xxx") + set_kind("static") + +-- The target file name is: libxxx2.so +target("xxx2") + set_kind("shared") +``` + +The default naming method basically meets the needs of most situations, but if you want to customize the target file name sometimes + +For example, to distinguish the target name by compile mode and architecture, this time you can use this interface to set: + +```lua +target("xxx") + set_kind("static") + set_basename("xxx_$(mode)_$(arch)") +``` + +if this time, the build configuration is: `xmake f -m debug -a armv7`, then the generated file name is: `libxxx_debug_armv7.a` + +If you want to further customize the directory name of the target file, refer to: [set_targetdir](#targetset_targetdir). + +Or implement more advanced logic by writing custom scripts, see: [after_build](#targetafter_build) and [os.mv](#os-mv). + +##### target:set_filename + +###### Set the full name of target file + +The difference between it and [set_basename](#targetset_basename) is that [set_basename](#targetset_basename) sets the name without a suffix and a prefix, for example: `libtest.a`, if the basename is changed to test2, it becomes `libtest2.a `. + +The modification of filename is to modify the entire target file name, including the prefix and suffix. For example, you can directly change `libtest.a` to `test.dll`, which is not available for [set_basename](#targetset_basename). + +##### target:set_warnings + +###### Set compilation warning level + +Set the warning level of the compilation of the current target, generally supporting several levels: + +| Value | Description | gcc/clang | msvc | +| ----- | ---------------------- | ---------- | ----------------------------- | +| none | disable all warnings | -w | -W0 | +| less | Enable fewer warnings | -W1 | -W1 | +| more | Enable more warnings | -W3 | -W3 | +| all | Enable all warnings | -Wall | -W3 (-Wall too more warnings) | +| everything | Enable all supported warnings | -Wall -Wextra -Weffc++ / -Weverything | -Wall | +| error | Use all warnings as compilation errors | -Werror | -WX | + +The parameters of this api can be added in combination, for example: + +```lua +-- Enable all warnings and handle them as compilation errors +set_warnings("all", "error") +``` + +If there is no target currently, calling this api will set it to global mode. . + +##### target:set_optimize + +###### Set competition optimization level + +Set the compile optimization level of the target. If no target is currently set, it will be set to the global state, affecting all subsequent targets. + +At present, we mainly support several levels: + +| Value | Description | gcc/clang | msvc | +| ---------- | ---------------------- | ---------- | ------------ | +| none | disable optimization | -O0 | -Od | +| fast | quick optimization | -O1 | default | +| faster | faster optimization | -O2 | -Ox | +| fastest | Optimization of the fastest running speed | -O3 | -Ox -fp:fast | +| smallest | Minimize code optimization | -Os | -O1 | +| aggressive | over-optimization | -Ofast | -Ox -fp:fast | + +E.g: + +```lua +-- Optimization of the fastest running speed +set_optimize("fastest") +``` + +##### target:set_languages + +###### Set source code language standards + +Set the language standard for target code compilation. If no target exists, it will be set to global mode. . . + +The supported language standards currently have the following main ones: + +| Value | Description | +| ---------- | ---------------------- | +| ansi | c language standard: ansi | +| c89 | c language standard: c89 | +| gnu89 | c language standard: gnu89 | +| c99 | c language standard: c99 | +| gnu99 | c language standard: gnu99 | +| cxx98 | c++ language standard: `c++98` | +| gnuxx98 | c++ language standard: `gnu++98` | +| cxx11 | c++ language standard: `c++11` | +| gnuxx11 | c++ language standard: `gnu++11` | +| cxx14 | c++ language standard: `c++14` | +| gnuxx14 | c++ language standard: `gnu++14` | +| cxx1z | c++ language standard: `c++1z` | +| gnuxx1z | c++ language standard: `gnu++1z` | +| cxx17 | c++ language standard: `c++17` | +| gnuxx17 | c++ language standard: `gnu++17` | + +The c standard and the c++ standard can be set at the same time, for example: + +```lua +-- Set c code standard: c99, c++ code standard: c++11 +set_languages("c99", "cxx11") +``` + +
+Instead of setting the specified standard, the compiler will compile according to this standard. After all, each compiler supports different strengths, but xmake will try to adapt the support standard of the current compiler tool to the greatest extent possible. . .
+
+E.g:
+
+Windows vs compiler does not support compiling c code according to c99 standard, can only support c89, but xmake in order to support it as much as possible, so after setting c99 standard, xmake will force to compile according to c++ code mode c code, to some extent solved the c code problem of compiling c99 under windows. .
+Users do not need to make any additional modifications. .
+
+Note that this interface has been deprecated after version 2.2.5, please use [add_headerfiles](#targetadd_headerfiles) instead. +
+ +Set the output directory of the header file, and output it to the build directory by default. + +```lua +target("test") + set_headerdir("$(buildir)/include") +``` + +For the header files that need to be installed, refer to the [add_headers](#targetadd_headers) interface. + +##### target:set_targetdir + +###### Set output directories for target files + +Set the output directory of the target program file. Under normal circumstances, you do not need to set it. The default output will be in the build directory. + +The build directory can be manually modified during project configuration: + +```bash +Xmake f -o /tmp/build +``` + +After modifying to `/tmp/build`, the target file is output to `/tmp/build` by default. + +And if you use this interface to set, you don't need to change the command every time, for example: + +```lua +target("test") + set_targetdir("/tmp/build") +``` + ++If the display sets `set_targetdir`, then the directory specified by `set_targetdir` is preferred as the output directory of the target file. +
+ +##### target:set_objectdir + +###### Set output directories for object files + +Set the output directory of the object file (`*.o/obj`) of the target target, for example: + +```lua +target("test") + set_objectdir("$(buildir)/.objs") +``` + +##### target:set_dependir + +###### Set output directories for dependent files + +Set the output directory of the compile dependency file (`.deps`) of the target target, for example: + +```lua +target("test") + set_dependir("$(buildir)/.deps") +``` + +##### target:add_imports + +###### Add imports modules for the custom script + +Usually, we can import extension modules via `import("core.base.task")` inside a custom script such as [on_build](#targeton_build). +However, in the case of a large number of custom scripts, each custom script is repeatedly imported again, which is very cumbersome. Then you can implement pre-import through this interface, for example: + +```lua +target("test") + on_load(function (target) + import("core.base.task") + import("core.project.project") + + task.run("xxxx") + end) + on_build(function (target) + import("core.base.task") + import("core.project.project") + + task.run("xxxx") + end) + on_install(function (target) + import("core.base.task") + import("core.project.project") + + task.run("xxxx") + end) +``` + +This interface can be simplified to: + +```lua +target("test") + add_imports("core.base.task", "core.project.project") + on_load(function (target) + task.run("xxxx") + end) + on_build(function (target) + task.run("xxxx") + end) + on_install(function (target) + task.run("xxxx") + end) +``` + +##### target:add_rules + +###### Add custom compilation rule to target + +We can extend the build support for other files by pre-setting the file suffixes supported by the rules: + +```lua +-- Define a build rule for a markdown file +rule("markdown") + set_extensions(".md", ".markdown") + on_build(function (target, sourcefile) + os.cp(sourcefile, path.join(target:targetdir(), path.basename(sourcefile) .. ".html")) + end) + +target("test") + set_kind("binary") + + -- Make the test target support the construction rules of the markdown file + add_rules("markdown") + + -- Adding a markdown file to build + add_files("src/*.md") + add_files("src/*.markdown") +``` + +We can also specify the application of local files to the rules, see: [add_files](#targetadd_files). + +##### target:on_load + +###### Run custom load target configuartion script + +This script will be executed when the target is initialized and loaded, and some dynamic target configurations can be made to achieve more flexible target description definitions, for example: + +```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) +``` + +You can dynamically add various target attributes in `on_load` via `target:set`, `target:add`. + +##### target:on_link + +###### Run custom link target script + +This is a new interface after v2.2.7, which is used to customize the link process of the target. + +```lua +target("test") + on_link(function (target) + print("link it") + end) +``` + +##### target:on_build + +###### Run custom build target script + +Override the target build behavior of the target target, implement a custom compilation process, in general, do not need to do this, unless you really need to do some compiler operations that xmake does not provide by default. + +You can override it by following the steps below to customize the compilation: + +```lua +target("test") + + -- Set up custom build scripts + on_build(function (target) + print("build it") + end) +``` + +Note: After version 2.1.5, all target custom scripts can be processed separately for different platforms and architectures, for example: + +```lua +target("test") + on_build("iphoneos|arm*", function (target) + print("build for iphoneos and arm") + end) +``` + +If the first parameter is a string, then it is specified in which platform_architecture the script needs to be executed, and mode matching is supported, for example, `arm*` matches all arm architectures. + +Of course, you can also set the platform only, do not set the architecture, this is to match the specified platform, execute the script: + +```lua +target("test") + on_build("windows", function (target) + print("build for windows") + end) +``` + ++Once the build process is set for this target target, the default build process for xmake will no longer be executed. +
+ +##### target:on_build_file + +###### Run custom build single file script + +Through this interface, you can use hook to specify the built-in build process of the target, replacing each source file compilation process: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + on_build_file(function (target, sourcefile, opt) + opt.origin(target, sourcefile, opt) + end) +``` + +The `opt.origin` in the above code has a built-in build script. If you want to call the built-in build script to compile the source file after hooking, just continue to call `opt.origin`. + +If you don't want to rewrite the built-in build script, just add some of your own processing before and after compiling. Its utility: [target.before_build_file](#targetbefore_build_file) and [target.after_build_file](#targetafter_build_file) will be more convenient and you don't need to call it. Opt.origin`. + +##### target:on_build_files + +###### Run custom build files script + +Through this interface, you can use hook to specify the built-in build process of the target, and replace a batch of the same type of source file compilation process: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + on_build_files(function (target, sourcebatch, opt) + opt.origin(target, sourcebatch, opt) + end) +``` + +After setting this interface, the corresponding file in the source file list will not appear in the custom [target.on_build_file](#targeton_build_file), because this is an inclusion relationship. + +Where sourcebatch describes the same source files of the same type: + +* `sourcebatch.sourcekind`: Get the type of this batch of source files, for example: cc, as, .. +* `sourcebatch.sourcefiles()`: get the list of source files +* `sourcebatch.objectfiles()`: get the list of object files +* `sourcebatch.dependfiles()`: Get the list of corresponding dependent files, compile dependency information in the stored source file, for example: xxx.d + +The `opt.origin` in the above code has a built-in build script. If you want to call the built-in build script to compile the source file after hooking, just continue to call `opt.origin`. + +##### target:on_clean + +###### Run custom clean files script + +Override the cleanup operation of the target target's `xmake [c|clean}` to implement a custom cleanup process. + +```lua +target("test") + + -- Set up a custom cleanup script + on_clean(function (target) + + -- Delete only target files + os.rm(target:targetfile()) + end) +``` + +Some target interfaces are described as follows: + +| target interface | description | +| ----------------------------------- | -------------------------------------------- | +| target:name() | Get the target name | +| target:targetfile() | Get the target file path | +| target:get("kind") | Get the build type of the target | +| target:get("defines") | Get the macro definition of the target | +| target:get("xxx") | Other target information set by the `set_/add_` interface can be obtained through this interface | +| target:add("links", "pthread") | Add target settings | +| target:set("links", "pthread", "z") | Override target settings | +| target:deps() | Get all dependent targets of the target | +| target:dep("depname") | Get the specified dependency target | +| target:sourcebatches() | Get a list of all source files for the target | + +##### target:on_package + +###### Run custom package target script + +Override the target object's `xmake [p|package}` package operation to implement the custom packaging process. If you want to package the specified target into the format you want, you can customize it through this interface. + +This interface is quite practical. For example, after compiling jni, the generated so is packaged into the apk package. + +```lua +-- Define a test demo for an android app +target("demo") + + -- Generate dynamic libraries: libdemo.so + set_kind("shared") + + -- Set the output directory of the object, optional + set_objectdir("$(buildir)/.objs") + + -- Every time you compile the build directory of libdemo.so, set it to app/libs/armeabi + set_targetdir("libs/armeabi") + + -- Add jni code files + add_files("jni/*.c") + + -- Set up a custom package script. After compiling libdemo.so with xmake, execute xmake p to package + -- will automatically compile the app into an apk file using ant + -- + on_package(function (target) + + -- Use ant to compile the app into an apk file, and redirect the output to a log file. + os.run("ant debug") + end) +``` + +##### target:on_install + +###### Run custom install target file script + +Override the installation of `xmake [i|install}` of the target target to implement a custom installation process. + +For example, the generated apk package will be installed. + +```lua +target("test") + + -- Set up a custom installation script to automatically install apk files + on_install(function (target) + + -- Use adb to install packaged apk files + os.run("adb install -r ./bin/Demo-debug.apk") + end) +``` + +##### target:on_uninstall + +###### Run custom uninstall target file script + +Override the uninstallation of `xmake [u|uninstall}` of the target target to implement a custom uninstall process. + +```lua +target("test") + on_uninstall(function (target) + ... + end) +``` + +##### target:on_run + +###### Run custom run target script + +Override the running operation of the target target's `xmake [r|run}` to implement a custom running process. + +For example, run the installed apk program: + +```lua +target("test") + + -- Set custom run scripts, automatically run the installed app, and automatically get device output information + on_run(function (target) + + os.run("adb shell am start -n com.demo/com.demo.DemoTest") + os.run("adb logcat") + end) +``` + +##### target:before_link + +###### Run custom script before linking target + +This is a new interface after v2.2.7 to add custom script before linking target. + +```lua +target("test") + before_link(function (target) + print("") + end) +``` + +##### target:before_build + +###### Run custom script before building target + +It does not override the default build operation, just add some custom actions before building. + +```lua +target("test") + before_build(function (target) + print("") + end) +``` + +##### target:before_build_file + +###### Run custom script before building single file + +Through this interface, you can use hook to specify the built-in build process of the target, and execute some custom scripts before each source file compilation process: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + before_build_file(function (target, sourcefile, opt) + end) +``` + +##### target:before_build_files + +###### Run custom script before building files + +Through this interface, you can use hook to specify the built-in build process of the target, and execute some custom scripts before a batch of source files of the same type: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + before_build_files(function (target, sourcebatch, opt) + end) +``` + + +##### target:before_clean + +###### Run custom script before cleaning target + +It does not override the default cleanup operation, just add some custom actions before cleaning. + +```lua +target("test") + before_clean(function (target) + print("") + end) +``` + +##### target:before_package + +###### Run custom script before packaging target + +It does not override the default packaging operation, just add some custom operations before packaging. + +```lua +target("test") + before_package(function (target) + print("") + end) +``` + +##### target:before_install + +###### Run custom script before installing target + +It does not override the default installation operation, just add some custom actions before installation. + +```lua +target("test") + before_install(function (target) + print("") + end) +``` + +##### target:before_uninstall + +###### Run custom script before uninstalling target + +It does not override the default uninstall operation, just add some custom actions before uninstalling. + +```lua +target("test") + before_uninstall(function (target) + print("") + end) +``` + +##### target:before_run + +###### Run custom script before running target + +It does not override the default run operation, just add some custom actions before running. + +```lua +target("test") + before_run(function (target) + print("") + end) +``` + +##### target:after_link + +###### Run custom script after linking target + +This is a new interface after v2.2.7 to add custom script after linking target. + +```lua +target("test") + after_link(function (target) + print("") + end) +``` + +##### target:after_build + +###### Run custom script after building target + +It does not override the default build operation, just add some custom actions after the build. + +For example, for jailbreak development of ios, after the program is built, you need to use `ldid` for signature operation. + +```lua +target("test") + after_build(function (target) + os.run("ldid -S %s", target:targetfile()) + end) +``` + +##### target:after_build_file + +###### Run custom script after building single file + +Through this interface, you can use hook to specify the built-in build process of the target, and execute some custom scripts after each source file compilation process: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + after_build_file(function (target, sourcefile, opt) + end) +``` + +##### target:after_build_files + +###### Run custom script after building files + +Through this interface, you can use hook to specify the built-in build process of the target, and execute some custom scripts after a batch of source files of the same type: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + after_build_files(function (target, sourcebatch, opt) + end) +``` + +##### target:after_clean + +###### Run custom script after cleaning target + +It does not override the default cleanup operation, just add some custom actions after cleanup. + +Generally used to clean up some extra temporary files automatically generated by a target. The default cleanup rules of these files may not be cleaned up. +To, for example: + +```lua +target("test") + after_clean(function (target) + os.rm("$(buildir)/otherfiles") + end) +``` + +##### target:after_package + +###### Run custom script after packaging target + +It does not override the default packaging operation, just add some custom operations after packaging. + +```lua +target("test") + after_package(function (target) + print("") + end) +``` + +##### target:after_install + +###### Run custom script after installing target + +It does not override the default installation operation, just add some custom actions after installation. + +```lua +target("test") + after_install(function (target) + print("") + end) +``` +##### target:after_uninstall + +###### Run custom script after uninstalling target + +It does not override the default uninstall operation, just add some custom actions after uninstalling. + +```lua +target("test") + after_uninstall(function (target) + print("") + end) +``` + +##### target:after_run + +###### Run custom script after running target + +It does not override the default run operation, just add some custom actions after the run. + +```lua +target("test") + after_run(function (target) + print("") + end) +``` + + +##### target:set_config_h + +###### Set auto-generated config header file + ++After the 2.2.5 version, this interface has been deprecated, please use [add_configfiles](#targetadd_configfiles). +After the 2.1.5 version, this interface has been deprecated, please use [set_config_header](#targetset_config_header). +
+ +If you want to write the result of the test to the configuration header after the xmake configuration project succeeds, or automatically detect an option, you need to call this interface to enable automatic generation of the `config.h` file. + +How to use, for example: + +```lua +target("test") + + -- Enable and set the path to the config.h file that needs to be automatically generated + set_config_h("$(buildir)/config.h") + + -- Set the name prefix of the macro switch generated by automatic detection + set_config_h_prefix("TB_CONFIG") +``` + +When the target passes the following interfaces, the related option dependencies, package dependencies, and interface dependencies are added to the target. If a dependency is enabled, the corresponding macro definition configuration will be automatically written to the set `config. Go to the .h` file. + +* [add_options](#targetadd_options) +* [add_packages](#targetadd_packages) +* [add_cfuncs](#targetadd_cfuncs) +* [add_cxxfuncs](#targetadd_cxxfuncs) + +These interfaces, in fact, use some of the detection settings in the [option](#option) option, for example: + +```lua +option("wchar") + + -- Add detection of wchar_t type + add_ctypes("wchar_t") + + -- If the test passes, automatically generate a macro switch of TB_CONFIG_TYPE_HAVE_WCHAR to config.h + add_defines_h("$(prefix)_TYPE_HAVE_WCHAR") + +target("test") + + -- Enable automatic generation of header files + set_config_h("$(buildir)/config.h") + set_config_h_prefix("TB_CONFIG") + + -- Add dependency on the wchar option. Only with this association, the detection result of the wchar option will be written to the specified config.h. + add_options("wchar") +``` + +##### target:set_config_h_prefix + +###### Set macro prefix in auto-generated config header + ++After the 2.2.5 version, this interface has been deprecated, please use [add_configfiles](#targetadd_configfiles). +After the 2.1.5 version, this interface has been deprecated, please use [set_config_header](#targetset_config_header). +
+ +For details, see: [set_config_h](#targetset_config_h) + +If set: + +```lua +target("test") + set_config_h_prefix("TB_CONFIG") +``` + +Then, the $(prefix) of `add_defines_h("$(prefix)_TYPE_HAVE_WCHAR")` in the option is automatically replaced with the new prefix value. + + +##### target:set_config_header + +###### Set macro prefix in auto-generated config header and prefix + ++After the 2.2.5 version, this interface has been deprecated, please use [add_configfiles](#targetadd_configfiles). +After the 2.1.5 version, this interface has been deprecated, please use [set_config_header](#targetset_config_header). +
+ +This interface is an upgraded version of [set_config_h](#targetset_config_h) and [set_config_h_prefix](#targetset_config_h_prefix), supported after 2.1.5. + +If you want to write the result of the test to the configuration header after the xmake configuration project succeeds, or automatically detect an option, you need to call this interface to enable automatic generation of the `config.h` file. + +How to use, for example: + +```lua +target("test") + set_config_header("$(buildir)/config.h", {prefix = "TB_CONFIG"}) +``` + +The above code, enable and set the path to the config.h file that needs to be automatically generated, and set the name prefix of the macro switch generated by the automatic detection: `TB_CONFIG`, of course, the setting of this prefix is optional. + +```lua +target("test") + set_config_header("$(buildir)/config.h") +``` + +If you do not set a prefix, it will automatically generate a unique string based on the target name. + +After version 2.1.8, the version number is set separately for each local configuration file, which takes precedence over the global [set_version](#set_version), for example: + +```lua + set_config_header("$(buildir)/config.h", {prefix = "TB_CONFIG", version = "2.1.8", build = "%Y%m%d%H%M"}) +``` + +###### Generate configuration with built-in detection rules + +When the target passes the following interfaces, the related option dependencies, package dependencies, and interface dependencies are added to the target. If a dependency is enabled, the corresponding macro definition configuration will be automatically written to the set `config. Go to the .h` file. + +* [add_options](#targetadd_options) +* [add_packages](#targetadd_packages) +* [add_cfunc](#targetadd_cfunc) +* [add_cfuncs](#targetadd_cfuncs) +* [add_cxxfuncs](#targetadd_cxxfuncs) + +###### Customize detection and generate configuration header files + +These interfaces, in fact, use some of the detection settings in the [option](#option) option, for example: + +```lua +option("wchar") + + -- Add detection of wchar_t type + add_ctypes("wchar_t") + + -- If the test passes, automatically generate a macro switch of TB_CONFIG_TYPE_HAVE_WCHAR to config.h + add_defines_h("$(prefix)_TYPE_HAVE_WCHAR") + +target("test") + + -- Enable automatic generation of header files + set_config_header("$(buildir)/config.h", {prefix = "TB_CONFIG"}) + + -- Add dependency on the wchar option. Only with this association, the detection result of the wchar option will be written to the specified config.h. + add_options("wchar") +``` + +Even we can define a function in `xmake.lua`, package the option, provide more customized detection and process of generating config.h. + +For example: there is a requirement here, we want to batch check some header files, if there is a macro switch such as `HAVE_LIMITS_H` in config.h, we can write + +```lua +function add_checking_to_config(...) + + -- Batch definition of option detection rules, only include include files + local options = {} + for _, header in ipairs({...}) do + local define = header:upper():gsub("[%./]", "_") + option(define) + add_cincludes(header) + add_defines_h("HAVE_" .. define) -- Generate a macro switch like HAVE_LIMITS_H to config.h + option_end() + table.insert(options, define) + end + + -- Define a built-in __config empty target, only for association settings automatedconfig.h, and corresponding options detection rules + -- Because set_config_header is globally set, it will affect all targets, and each target will detect the generation of a macro switch. + target("__config") + set_kind("phony") + set_cOnfig_header("includes/automatedconfig.h") + add_options(options) + target_end() +end + +-- Add some header file detection +add_checking_to_config("arpa/inet.h", "limits.h", "fcntl.h", "xxxx.h") +``` + +##### target:set_pcheader + +###### Set pre-compiled c header file + +Xmake supports accelerating c program compilation by precompiling header files. Currently supported compilers are: gcc, clang, and msvc. + +The usage is as follows: + +```lua +target("test") + set_pcheader("header.h") +``` + +##### target:set_pcxxheader + +###### Set pre-compiled c++ header file + +Xmake supports precompiled header files to speed up C++ program compilation. Currently supported compilers are: gcc, clang, and msvc. + +The usage is as follows: + +```lua +target("test") + set_pcxxheader("header.h") +``` + +##### target:add_deps + +###### Add target dependencies + + + +Add the dependency target of the current target. When compiling, it will first compile the target of the dependency and then compile the current target. . . + +```lua +target("test1") + set_kind("static") + set_files("*.c") + +target("test2") + set_kind("static") + set_files("*.c") + +target("demo") + add_deps("test1", "test2") +``` + +In the above example, when compiling the target demo, you need to compile the test1 and test2 targets first, because the demo will use them. + ++The target will automatically inherit the configuration and properties in the dependent target. You don't need to call the interfaces `add_links`, `add_linkdirs` and `add_rpathdirs` to associate the dependent targets. +
+ +And the inheritance relationship is to support cascading, for example: + +```lua +target("library1") + set_kind("static") + add_files("*.c") + add_includedirs("inc") -- The default private header file directory will not be inherited + add_includedirs("inc1", {public = true}) -- The header file related directory here will also be inherited + +target("library2") + set_kind("static") + add_deps("library1") + add_files("*.c") + +target("test") + set_kind("binary") + add_deps("library2") +``` + +If we don't want to inherit any configuration that depends on the target, what should we do? + +```lua +add_deps("dep1", "dep2", {inherit = false}) +``` + +By explicitly setting the inherit configuration, tell xmake whether the two dependent configurations need to be inherited. If not set, the default is to enable inheritance. + +After version 2.2.5, you can set public to true by `add_includedirs("inc1", {public = true})`, and expose the settings of includers to other dependent child targets. + +At present, for the target compilation link flags related interface settings, support for inheritance properties, you can artificially control whether you need to export to other targets to rely on inheritance, the currently supported properties are: + +| Attribute | Description | +| ---- | ---- | +| private | The default setting, as the private configuration of the current target, will not be inherited by other targets that depend on | +Public | public configuration, current target, dependent child targets will be set | +Interface | interface settings, only inherited by the dependent child target, the current target does not participate | + +For a detailed description of this, you can look at it: https://github.com/xmake-io/xmake/issues/368 + +##### target:add_links + +###### Add link libraries + +Add a link library for the current target, which is usually paired with [add_linkdirs](#targetadd_linkdirs). + +```lua +target("demo") + + -- Add a link to libtest.a, equivalent to -ltest + add_links("test") + + -- Add link search directory + add_linkdirs("$(buildir)/lib") +``` + +##### target:add_syslinks + +###### Add system link libraries + +This interface is similar to [add_links](#targetadd_links). The only difference is that the link library added through this interface is in the order of all `add_links`. + +Therefore, it is mainly used to add system library dependencies, because the link order of the system libraries is very backward, for example: + +```lua +add_syslinks("pthread", "m", "dl") +target("demo") + add_links("a", "b") + add_linkdirs("$(buildir)/lib") +``` + +The above configuration, even if `add_syslinks` is set in advance, the final link order is still: `-la -lb -lpthread -lm -ldl` + +##### target:add_files + +###### Add source files + +Source files used to add target projects, even library files, some file types currently supported: + +| Supported source file types | Description | +| ------------------ | ---------------------------------- | +| .c/.cpp/.cc/.cxx | c++ file | +| .s/.S/.asm | Assembly files | +| .m/.mm | objc file | +| .swift | swift file | +| .go | golang file | +| .o/.obj | Object File | +| .a/.lib | Static library files, will automatically merge the library to the target program | +| .rc | msvc resource file | + +The wildcard `*` indicates that the file in the current directory is matched, and `**` matches the file in the multi-level directory. + +E.g: + +```lua +add_files("src/test_*.c") +add_files("src/xxx/**.cpp") +add_files("src/asm/*.S", "src/objc/**/hello.m") +``` + +The use of `add_files` is actually quite flexible and convenient. Its matching mode draws on the style of premake, but it has been improved and enhanced. + +This makes it possible to not only match files, but also to filter out a batch of files in the specified mode while adding files. + +E.g: + +```lua +-- Recursively add all c files under src, but not all c files under src/impl/ +add_files("src/**.c|impl/*.c") + +-- Add all cpp files under src, but not including src/test.cpp, src/hello.cpp, and all cpp files with xx_ prefix under src +add_files("src/*.cpp|test.cpp|hello.cpp|xx_*.cpp") +``` + +The separators after the ``` are all files that need to be excluded. These files also support the matching mode, and you can add multiple filtering modes at the same time, as long as the middle is separated by `|`. . + +One of the benefits of supporting the filtering of some files when adding files is that they provide the basis for subsequent file additions based on different switching logic. + ++In order to make the description more streamlined, the filter descriptions after `|` are based on a schema: the directory before `*` in `src/*.cpp`. +So the above example is filtered after the file under src, this is to pay attention to. +
+ +After version 2.1.6, `add_files` has been improved to support more fine-grained compilation option controls based on files, such as: + +```lua +target("test") + add_defines("TEST1") + add_files("src/*.c") + add_files("test/*.c", "test2/test2.c", {defines = "TEST2", languages = "c99", includedirs = ".", cflags = "-O0"}) +``` + +You can pass a configuration table in the last parameter of `add_files` to control the compilation options of the specified files. The configuration parameters are consistent with the target, and these files will also inherit the target's common configuration `-DTEST1`. + +After version 2.1.9, support for adding unknown code files, by setting rule custom rules, to achieve custom build of these files, for example: + +```lua +target("test") + -- ... + add_files("src/test/*.md", {rule = "markdown"}) +``` + +For instructions on using custom build rules, see: [Building Rules](#Building Rules). + +And after the 2.1.9 version, you can use the force parameter to force the automatic detection of cxflags, cflags and other compile options, directly into the compiler, even if the compiler may not support, it will also be set: + +```lua +add_files("src/*.c", {force = {cxflags = "-DTEST", mflags = "-framework xxx"}}) +``` + +##### target:del_files + +###### Remove source files + +Through this interface, you can delete the specified file from the list of files added by the [add_files](targetadd_files) interface, for example: + +```lua +target("test") + add_files("src/*.c") + del_files("src/test.c") +``` + +In the above example, you can add all files except `test.c` from the `src` directory. Of course, this can also be done by `add_files("src/*.c|test.c").To achieve the same purpose, but this way is more flexible. + +For example, we can conditionally determine which files to delete, and this interface also supports the matching mode of [add_files](targetadd_files), filtering mode, and bulk removal. + +```lua +target("test") + add_files("src/**.c") + del_files("src/test*.c") + del_files("src/subdir/*.c|xxx.c") + if is_plat("iphoneos") then + add_files("xxx.m") + end +``` + +Through the above example, we can see that `add_files` and `del_files` are added and deleted sequentially according to the calling sequence, and deleted by `del_files("src/subdir/*.c|xxx.c")` Batch file, +And exclude `src/subdir/xxx.c` (that is, don't delete this file). + +##### target:add_headers + +###### Add installed header files + ++Note that this interface has been deprecated after version 2.2.5, please use [add_headerfiles](#targetadd_headerfiles) instead. +
+ +Install the specified header file into the build directory. If [set_headerdir](#targetset_headerdir) is set, it will be output to the specified directory. + +The syntax of the installation rules is similar to [add_files](#targetadd_files), for example: + +```lua + -- Install all the header files in the tbox directory (ignore the files in the impl directory), and press () to specify the part as a relative path to install + add_headers("../(tbox/**.h)|**/impl/**.h") +``` + +##### target:add_linkdirs + +###### Add link search directories + +Set the search directory of the link library. This interface is used as follows: + +```lua +target("test") + add_linkdirs("$(buildir)/lib") +``` + +This interface is equivalent to gcc's `-Lxxx` link option. + +Generally, it is used together with [add_links](#targetadd_links). Of course, it can also be added directly through the [add_ldflags](#targetadd_ldflags) or [add_shflags](#targetadd_shflags) interface. It is also possible. + ++If you don't want to write to death in the project, you can set it by: `xmake f --linkdirs=xxx` or `xmake f --ldflags="-L/xxx"`, of course, this manually set directory search priority. higher. +
+ +##### target:add_rpathdirs + +###### Add load search directories for dynamic libraries + +After [add_linkdirs](#targetadd_linkdirs) sets the link search directory of the dynamic library, the program is normally linked, but in the Linux platform, if you want to run the compiled program normally, it will report that the dynamic library fails to be loaded. + +Because the dynamic library's load directory is not found, if you want to run the program that depends on the dynamic library, you need to set the `LD_LIBRARY_PATH` environment variable to specify the dynamic library directory to be loaded. + +However, this method is global, and the impact is too wide. The better way is to set the dynamic library search path to be loaded when the linker is set by the linker option of `-rpath=xxx`, and xmake does it. Encapsulation, better handling cross-platform issues with `add_rpathdirs`. + +The specific use is as follows: + +```lua +target("test") + set_kind("binary") + add_linkdirs("$(buildir)/lib") + add_rpathdirs("$(buildir)/lib") +``` + +Just need to set the rpath directory when linking, although the same purpose can be achieved by `add_ldflags("-Wl,-rpath=xxx")`, but this interface is more general. + +Internally, different platforms will be processed. For example, under macOS, the `-rpath` setting is not required, and the running program can be loaded normally. Therefore, for this platform, xmake internally ignores the setting directly to avoid link error. + +When doing dynamic library linking for dlang programs, xmake will automatically process it into `-L-rpath=xxx` to pass in the linker of dlang, thus avoiding the need to directly use `add_ldflags` to determine and handle different platforms and compile. Problem. + +The 2.1.7 version has improved this interface, supporting: `@loader_path`, `@executable_path` and `$ORIGIN` built-in variables to specify the program's load directory. Their effects are basically the same, mainly for Also compatible with macho, elf. + +E.g: + +```lua +target("test") + set_kind("binary") + add_linkdirs("$(buildir)/lib") + add_rpathdirs("@loader_path/lib") +``` + +Specify the test program to load the dynamic library file of `lib/*.[so|dylib]` in the current execution directory, which will help to improve the portability of the program without writing dead absolute paths and relative paths, resulting in program and directory switching. Causes the program to load the dynamic library failed. + ++It should be noted that under macos, if the add_rpathdirs setting is in effect, you need to do some preprocessing on dylib and add the `@rpath/xxx` path setting: +`$install_name_tool -add_rpath @rpath/libxxx.dylib xxx/libxxx.dylib` +We can also check if there is a path with @rpath via `otool -L libxxx.dylib` +
+ +##### target:add_includedirs + +###### Add include search directories + +Set the search directory for the header file. This interface is used as follows: + +```lua +target("test") + add_includedirs("$(buildir)/include") +``` + +Of course, it can also be set directly through interfaces such as [add_cxflags](#targetadd_cxflags) or [add_mxflags](#targetadd_mxflags), which is also possible. + +After 2.2.5, includedirs can be exported to dependent child targets via the extra `{public|interface = true}` property setting, for example: + +```lua +target("test") + set_kind("static") + add_includedirs("src/include") -- only for the current target + add_includedirs("$(buildir)/include", {public = true}), the current target and child targets will be set + +target("demo") + set_kind("binary") + add_deps("test") +``` + +For more on this block, see: [add_deps](#targetadd_deps) + ++If you don't want to write to death in the project, you can set it by: `xmake f --includedirs=xxx` or `xmake f --cxflags="-I/xxx"`, of course, this manually set directory search priority. higher. +
+ +##### target:add_defines + +###### Add macro definition + +```lua +add_defines("DEBUG", "TEST=0", "TEST2=\"hello\"") +``` + +Equivalent to setting the compile option: + +``` +-DDEBUG -DTEST=0 -DTEST2=\"hello\" +``` + +##### target:add_undefines + +###### Add macro undefinition + +```lua +add_undefines("DEBUG") +``` + +Equivalent to setting the compile option: `-UDEBUG` + +In the code is equivalent to: `#undef DEBUG` + +##### target:add_defines_h + +###### Add macro definition to auto-generated config header + ++After the 2.2.5 version, this interface has been deprecated, please use [add_configfiles](#targetadd_configfiles). +
+ +Add macro definitions to the `config.h` configuration file, `config.h` settings, refer to the [set_config_h](#targetset_config_h) interface. + +##### target:add_undefines_h + +###### Add macro undefinition to auto-generated config header + ++After the 2.2.5 version, this interface has been deprecated, please use [add_configfiles](#targetadd_configfiles). +
+ +Disable the macro definition by `undef` in the `config.h` configuration file. For the setting of `config.h`, refer to the [set_config_h](#targetset_config_h) interface. + +##### target:add_cflags + +###### Add c compilation flags + +Add compilation options only for c code + +```lua +add_cflags("-g", "-O2", "-DDEBUG") +``` + ++All option values are based on the definition of gcc as standard. If other compilers are not compatible (for example: vc), xmake will automatically convert it internally to the corresponding option values supported by the compiler. +Users don't have to worry about compatibility. If other compilers don't have matching values, xmake will automatically ignore the settings. +
+ +After version 2.1.9, the force parameter can be used to force the automatic detection of flags to be disabled and passed directly to the compiler. Even if the compiler may not support it, it will be set: + +```lua +add_cflags("-g", "-O2", {force = true}) +``` + +##### target:add_cxflags + +###### Add c/c++ compilation flags + +Add compilation options to c/c++ code at the same time + +##### target:add_cxxflags + +###### Add c++ compilation flags + +Add compilation options only to c++ code + +##### target:add_mflags + +###### Add objc compilation flags + +Add compilation options only to objc code + +```lua +add_mflags("-g", "-O2", "-DDEBUG") +``` + +After version 2.1.9, the force parameter can be used to force the automatic detection of flags to be disabled and passed directly to the compiler. Even if the compiler may not support it, it will be set: + +```lua +add_mflags("-g", "-O2", {force = true}) +``` + +##### target:add_mxflags + +###### Add objc/objc++ compilation flags + +Also add compile options to objc/objc++ code + +```lua +add_mxflAgs("-framework CoreFoundation") +``` + +##### target:add_mxxflags + +###### Add objc++ compilation flags + +Add compilation options only to objc++ code + +```lua +add_mxxflags("-framework CoreFoundation") +``` + +##### target:add_scflags + +###### Add swift compilation flags + +Add compilation options to swift code + +```lua +add_scflags("xxx") +``` + +##### target:add_asflags + +###### Add asm compilation flags + +Add compilation options to assembly code + +```lua +add_asflags("xxx") +``` + +##### target:add_gcflags + +###### Add go compilation flags + +Add compile options to golang code + +```lua +add_gcflags("xxx") +``` + +##### target:add_dcflags + +###### Add dlang compilation flags + +Add compilation options to dlang code + +```lua +add_dcflags("xxx") +``` + +##### target:add_rcflags + +###### Add MASTER compilation flags + +Add compilation options to the rust code + +```lua +add_rcflags("xxx") +``` + +##### target:add_cuflags + +###### Add cuda compilation flags + +Add compilation options to cuda code + +```lua +add_cuflags("-gencode arch=compute_30,code=sm_30") +``` + +##### target:add_culdflags + +###### Add cuda device link flags + +After v2.2.7, cuda default build will use device-link. If you want to set some link flags in this stage, you can set it through this interface. +The final program link will use ldflags, will not call nvcc, and directly link through c/c++ linker such as gcc/clang. + +For a description of device-link, please refer to: https://devblogs.nvidia.com/separate-compilation-linking-cuda-device-code/ + +```lua +add_culdflags("-gencode arch=compute_30,code=sm_30") +``` + +##### target:add_ldflags + +###### Add static library link flags + +Add static link option + +```lua +add_ldflags("-L/xxx", "-lxxx") +``` + +##### target:add_arflags + +###### Add archive library flags + +Affect the generation of static libraries + +```lua +add_arflags("xxx") +``` +##### target:add_shflags + +###### Add dynamic library link flags + +Affect the generation of dynamic libraries + +```lua +add_shflags("xxx") +``` + +##### target:add_cfunc + +###### Add single c function for checking + +Similar to [add_cfuncs](#targetadd_cfuncs), only a single function interface is set and only valid for the `target` domain. This interface does not exist in `option`. + +The purpose of this interface is primarily to create a more highly customized macro switch in `config.h`, for example: + +```lua +target("demo") + + -- Set and enable config.h + set_config_header("$(buildir)/config.h", {prefix = "TEST"}) + + -- Set module name prefix only by parameter one + add_cfunc("libc", nil, nil, {"sys/select.h"}, "select") + + -- Set the simultaneous detection of the link library via parameter three: libpthread.a + add_cfunc("pthread", nil, "pthread", "pthread.h", "pthread_create") + + -- Set interface alias by parameter two + add_cfunc(nil, "PTHREAD", nil, "pthread.h", "pthread_create") +``` + +The resulting results are as follows: + +```c +#ifndef TEST_H +#define TEST_H + +// Macro naming convention: $(prefix) prefix _ module name (if non-nil) _ HAVE _ interface name or alias (uppercase) +#define TEST_LIBC_HAVE_SELECT 1 +#define TEST_PTHREAD_HAVE_PTHREAD_CREATE 1 +#define TEST_HAVE_PTHREAD 1 + +#endif +``` + +For more flexible function detection, you can do this in a custom script with [lib.detect.has_cfuncs](#detect-has_cfuncs). + +##### target:add_cxxfunc + +###### Add single c++ function for checking + +Similar to [add_cfunc](#targetadd_cfunc), only the function interface detected is a c++ function. + +##### target:add_cfuncs + +###### Add c functions for checking + ++This interface is the interface shared by `target` and `option`, but the interface behavior is slightly different. +
+ +| Interface Field | Description | Examples | +| ------ | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| target | header files, link libraries, and function interfaces are also specified | `add_cfuncs("libc", nil, {"signal.h", "setjmp.h"}, "signal", "setjmp", "sigsetjmp{sigjmp_buf buf ; sigsetjmp(buf, 0);}", "kill")` | +Option | only specifies the function interface, the header file depends on [add_cincludes](#targetadd_cincludes) and other independent interfaces | `add_cincludes("setjmp.h")` `add_cfuncs("sigsetjmp")` | + +For `option`, this interface is very simple to use, similar to [add_cincludes](#targetadd_cincludes), for example: + +```lua +option("setjmp") + set_default(false) + add_cincludes("setjmp.h") + add_cfuncs("sigsetjmp", "setjmp") + add_defines("HAVE_SETJMP") + +target("test") + add_options("setjmp") +``` + +This option detects if there are some interfaces of `setjmp`. If the test passes, then the `test` target program will add the macro definition of `HAVE_SETJMP`. + +
+Note that using this interface to detect dependencies in `option` requires adding a separate [add_cincludes](#targetadd_cincludes) header file search path and specifying [add_links](#targetadd_links) link library (optional). Otherwise the specified function is not detected.
+
+And some header file interfaces are defined by macro switches, so it is best to pass the dependent macro switch with [add_defines](#targetadd_defines) when detecting.
+
+If the currently set instruction set compiler does not support it, xmake will automatically ignore it, so you don't need the user to manually determine the maintenance. Just set all the instruction sets you need. +
+ +##### target:add_frameworks + +###### Add frameworks + +Currently used for the `objc` and `swift` programs of the `ios` and `macosx` platforms, for example: + +```lua +target("test") + add_frameworks("Foundation", "CoreFoundation") +``` + +Of course, you can also use [add_mxflags](#targetadd_mxflags) and [add_ldflags](#targetadd_ldflags) to set them up, but it is cumbersome and is not recommended. + +```lua +target("test") + add_mxflags("-framework Foundation", "-framework CoreFoundation") + add_ldflags("-framework Foundation", "-framework CoreFoundation") +``` + +If it is not for both platforms, these settings will be ignored. + +##### target:add_frameworkdirs + +###### Add framework search directories + +For some third-party frameworks, it is impossible to find them only through [add_frameworks](#targetadd_frameworks). You also need to add a search directory through this interface. + +```lua +target("test") + add_frameworks("MyFramework") + add_frameworkdirs("/tmp/frameworkdir", "/tmp/frameworkdir2") +``` + +##### target:set_tools + +###### Set toolchains + +For the source files added by `add_files("*.c")`, the default is to call the system's best matching compiler to compile, or manually modify it by `xmake f --cc=clang` command, but these are Globally affects all target targets. + +If there are some special requirements, you need to specify a different compiler, linker or specific version of the compiler for a specific target target under the current project. At this time, the interface can be used for purposes. For example: + +```lua +target("test1") + add_files("*.c") + +target("test2") + add_files("*.c") + set_tools("cc", "$(projectdir)/tools/bin/clang-5.0") +``` + +The above description only makes special settings for the compiler of the test2 target, compiling test2 with a specific clang-5.0 compiler, and test1 still uses the default settings. + +For setting multiple compiler types at the same time, you can write: + +```lua +set_tools { + cc = path.join(os.projectdir(), "tools/bin/clang-5.0"), + mm = path.join(os.projectdir(), "tools/bin/clang-5.0"), +} +``` + ++Each setting will override the previous setting under the current target target. Different targets will not be overwritten and independent of each other. If set in the root domain, all child targets will be affected. +
+ +Or you can use [add_tools](#targetadd_tools) to set: + +```lua +add_tools("cc", "$(projectdir)/tools/bin/clang-5.0") +add_tools("mm", "$(projectdir)/tools/bin/clang-5.0") +``` + +The previous parameter is key, which is used to specify the tool type. Currently supported (compiler, linker, archiver): + +| Tool Type | Description | +| ------------ | ------------------------------------ | +| cc | c compiler | +| cxx | c++ compiler | +| mm | objc compiler | +| mxx | objc++ compiler | +| gc | go compiler | +| as | assembler | +| sc | swift compiler | +| rc | rust compiler | +| dc | dlang compiler | +| ld | Common executable program linker such as c/c++/asm/objc | +| sh | c/c++/asm/objc and other universal dynamic library linker | +| ar | general static library archiver such as c/c++/asm/objc | +| dc-ld | dlang executable linker, rc-ld/gc-ld, etc. | +Dc-sh | dlang dynamic library linker, rc-sh/gc-sh, etc. | + +For some compiler file names that are irregular, causing xmake to fail to recognize the known compiler name, we can also add a tool name prompt, for example: + +```lua +add_tools("cc", "gcc@$(projectdir)/tools/bin/Mipscc.exe") +``` + +The above description sets mipscc.exe as the c compiler, and prompts xmake to compile as a pass-through processing method for gcc. + +##### target:add_tools + +###### Add toolchains + +Similar to [set_tools](#targetset_tools), the difference is that this interface can be called multiple times to add multiple tools, and [set_tools](#targetset_tools) will overwrite the previous settings each time. + +##### target:set_values + +###### Set custom configuration values + +Set some extended configuration values for the target. These configurations do not have a built-in api like `set_ldflags`. You can extend the configuration by passing in a configuration name with the first argument. +Generally used to pass configuration parameters to scripts in custom rules, for example: + +```lua +rule("markdown") + on_build_file(function (target, sourcefile) + -- compile .markdown with flags + local flags = target:values("markdown.flags") + if flags then + -- .. + end + end) + +target("test") + add_files("src/*.md", {rule = "markdown"}) + set_values("markdown.flags", "xxx", "xxx") +``` + +In the above code example, it can be seen that when the target applies the markdown rule, some flag values are set by set_values and provided to the markdown rule for processing. +In the rule script, you can get the extended flag value set in the target by `target:values("markdown.flags")`. + ++The specific extension configuration name will be different according to different rules. Currently, you can refer to the description of related rules: [built-in rules](#built-in rules) +
+ +##### target:add_values + +###### Add custom configuration values + +Usage is similar to [target:set_values](#targetset_tools), the difference is that this interface is an additional setting, and will not override the settings each time. + +##### target:set_rundir + +###### Setting the running directory + +This interface is used to set the current running directory of the default running target program. If not set, by default, the target is loaded and run in the directory where the executable file is located. + +If the user wants to modify the load directory, one is to customize the run logic by `on_run()`, and to do the switch inside, but just to cut the directory, this is too cumbersome. + +Therefore, you can quickly switch settings to the default directory environment through this interface. + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + set_rundir("$(projectdir)/xxx") +``` + +##### target:add_runenvs + +###### Adding runtime variables + +This interface is used to add environment variables that set the default run target program. + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + add_runenvs("PATH", "/tmp/bin", "xxx/bin") + add_runenvs("NAME", "value") +``` + +##### target:set_installdir + +###### Set the installation directory + +By default, `xmake install` will be installed to the system `/usr/local` directory. We can specify other installation directories except `xmake install -o /usr/local`. +You can also set a different installation directory for the target in xmake.lua instead of the default directory. + +##### target:set_configdir + +###### Set the output directory of configuration files + +Version 2.2.5 adds a new interface, mainly used for the output directory of the template configuration file set by the [add_configfiles](#targetadd_configfiles) interface. + +##### target:set_configvar + +###### Set template configuration variable + +2.2.5 version of the new interface, used to add some template configuration variables that need to be pre-compiled before compilation, generally used for [add_configfiles](#targetadd_configfiles) interface. + +##### target:add_configfiles + +###### Add template configuration files + +2.2.5 version of the new interface, used to add some configuration files that need to be pre-processed before compiling, used to replace the old interface such as [set_config_header](#targetset_config_header). + +Because this interface is more versatile, it is not only used to handle the automatic generation and preprocessing of config.h, but also to handle various file types, while `set_config_header` is only used to process header files and does not support template variable substitution. + +Let's start with a simple example: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + set_configdir("$(buildir)/config") + add_configfiles("src/config.h.in") +``` + +The above settings will automatically configure the `config.h.in` header file template before compiling. After preprocessing, it will generate the output to the specified `build/config/config.h`. + +If `set_configdir` is not set, the default output is in the `build` directory. + +The `.in` suffix will be automatically recognized and processed. If you want to store the output as a different file name, you can pass: + +```lua +add_configfiles("src/config.h", {filename = "myconfig.h"}) +``` + +The way to rename the output, again, this interface is similar to [add_installfiles](#targetadd_configfiles), which also supports prefixdir and subdirectory extraction settings: + +```lua +add_configfiles("src/*.h.in", {prefixdir = "subdir"}) +add_configfiles("src/(tbox/config.h)") +``` + +One of the most important features of this interface is that it can be preprocessed and replaced with some of the template variables in the preprocessing, for example: + +Config.h.in + +``` +#define VAR1 "${VAR1}" +#define VAR2 "${VAR2}" +#define HELLO "${HELLO}" +``` + +```lua +set_configvar("VAR1", "1") + +target("test") + set_kind("binary") + add_files("main.c") + + set_configvar("VAR2", 2) + add_configfiles("config.h.in", {variables = {hello = "xmake"}}) + add_configfiles("*.man", {copyonly = true}) +``` + +The template variable is set via the [set_configvar](#targetset_configvar) interface, and the substitution is handled by the variable set in `{variables = {xxx = ""}}`. + +The preprocessed file `config.h` is: + +``` +#define VAR1 "1" +#define VAR2 "2" +#define HELLO "xmake" +``` + +The `{copyonly = true}` setting will force `*.man` to be treated as a normal file, copying files only during the preprocessing stage, and not replacing variables. + +The default template variable matching mode is `${var}`, of course we can also set other matching modes, for example, to `@var@` matching rules: + +```lua +target("test") + add_configfiles("config.h.in", {pattern = "@(.-)@"}) +``` + +We also have some built-in variables that can be replaced with default variables even if they are not set through this interface: + +``` +${VERSION} -> 1.6.3 +${VERSION_MAJOR} -> 1 +${VERSION_MINOR} -> 6 +${VERSION_ALTER} -> 3 +${VERSION_BUILD} -> set_version("1.6.3", {build = "%Y%m%d%H%M"}) -> 201902031421 +${PLAT} and ${plat} -> MACOS and macosx +${ARCH} and ${arch} -> ARM and arm +${MODE} and ${mode} -> DEBUG/RELEASE and debug/release +${DEBUG} and ${debug} -> 1 or 0 +${OS} and ${os} -> IOS or ios +``` + +E.g: + +Config.h.in + +```c +#define CONFIG_VERSION "${VERSION}" +#define CONFIG_VERSION_MAJOR ${VERSION_MAJOR} +#define CONFIG_VERSION_MINOR ${VERSION_MINOR} +#define CONFIG_VERSION_ALTER ${VERSION_ALTER} +#define CONFIG_VERSION_BUILD ${VERSION_BUILD} +``` + +Config.h + +```c +#define CONFIG_VERSION "1.6.3" +#define CONFIG_VERSION_MAJOR 1 +#define CONFIG_VERSION_MINOR 6 +#define CONFIG_VERSION_ALTER 3 +#define CONFIG_VERSION_BUILD 201902031401 +``` + +We can also perform some variable state control processing on the `#define` definition: + +Config.h.in + +```c +${define FOO_ENABLE} +``` + +```lua +set_configvar("FOO_ENABLE", 1) -- or pass true +set_configvar("FOO_STRING", "foo") +``` + +After setting the above variable, `${define xxx}` will be replaced with: + +```c +#define FOO_ENABLE 1 +#define FOO_STRING "foo" +``` + +Or (when set to 0 disable) + +```c +/* #undef FOO_ENABLE */ +/* #undef FOO_STRING */ +``` + +This method is very useful for some automatic detection generation config.h, such as with the option to do automatic detection: + +```lua +option("foo") + set_default(true) + set_description("Enable Foo") + set_configvar("FOO_ENABLE", 1) -- or pass true to enable the FOO_ENABLE variable + set_configvar("FOO_STRING", "foo") + +target("test") + add_configfiles("config.h.in") + + -- If the foo option is enabled -> Tianjian FOO_ENABLE and FOO_STRING definitions + add_options("foo") +``` + +Config.h.in + +```c +${define FOO_ENABLE} +${define FOO_STRING} +``` + +Config.h + +```c +#define FOO_ENABLE 1 +#define FOO_STRING "foo" +``` + +Regarding the option option detection, and the automatic generation of config.h, there are some helper functions, you can look at it: https://github.com/xmake-io/xmake/issues/342 + +In addition to `#define`, if you want to other non-##defIne xxx` also performs state switching processing. You can use the `${default xxx 0}` mode to set default values, for example: + +``` +HAVE_SSE2 equ ${default VAR_HAVE_SSE2 0} +``` + +After `set_configvar("HAVE_SSE2", 1)` is enabled, it becomes `HAVE_SSE2 equ 1`. If no variable is set, the default value is used: `HAVE_SSE2 equ 0` + +For a detailed description of this, see: https://github.com/xmake-io/xmake/issues/320 + + +#### Configuration Option + +Define and set option switches. Each `option` corresponds to an option that can be used to customize the build configuration options and switch settings. + ++All domain interfaces except `target`, such as `option`, `task`, etc., cannot be placed in the outer global scope by default (unless some interfaces are shared with the target). +If you want to set the value to affect all options such as `option`, `task`, you can set it by anonymous global domain. +
+ +E.g: + +```lua +-- Enter the anonymous global domain of the option, the settings inside will affect the test and test2 options. +option() + add_defines("DEBUG") + +option("test") + -- ... + -- Try to keep indented, because all settings after this are for the test option. + +option("test2") + -- ... +``` + ++The `option` field can be repeatedly entered to implement separate settings. If you want to display the scope settings away from the current option, you can manually call the [option_end](#option_end) interface. +
+ + +| Interface | Description | Supported Versions | +| ----------------------------------------------------- | -------------------------------------------- | -------- | +| [option](#option) | Define Options | >= 2.0.1 | +| [option_end](#option_end) | End Definition Options | >= 2.1.1 | +| [add_deps](#optionadd_deps) | Add Options Dependencies | >= 2.1.5 | +| [before_check](#optionbefore_check) | Execute this script before option detection | >= 2.1.5 | +| [on_check](#optionon_check) | Custom Option Detection Script | >= 2.1.5 | +| [after_check](#optionafter_check) | Execute this script after option detection | >= 2.1.5 | +| [set_values](#optionset_values) | Setting the list of option values | >= 2.1.9 | +| [set_default](#optionset_default) | Set Defaults | >= 2.0.1 | +| [set_showmenu](#optionset_showmenu) | Set whether to enable menu display | >= 1.0.1 | +| [set_category](#optionset_category) | Set option categories, only for menu display | >= 1.0.1 | +| [set_description](#optionset_description) | Settings Menu Display Description | >= 1.0.1 | +| [add_links](#optionadd_links) | Add Linked Library Detection | >= 1.0.1 | +| [add_linkdirs](#optionadd_linkdirs) | Add a search directory for link library detection | >= 1.0.1 | +| [add_rpathdirs](#optionadd_rpathdirs) | Add runtime dynamic link library search directory | >= 2.1.3 | +| [add_cincludes](#optionadd_cincludes) | Add c header file detection | >= 1.0.1 | +| [add_cxxincludes](#optionadd_cxxincludes) | Add c++ header file detection | >= 1.0.1 | +| [add_ctypes](#optionadd_ctypes) | Add c type detection | >= 1.0.1 | +| [add_cxxtypes](#optionadd_cxxtypes) | Add c++ type detection | >= 1.0.1 | +| [add_csnippet](#optionadd_csnippet) | Add c-code snippets detection | >= 2.1.5 | +| [add_cxxsnippet](#optionadd_cxxsnippet) | Add c++ code snippet detection | >= 2.1.5 | +| [set_warnings](#targetset_warnings) | Setting the warning level | >= 1.0.1 | +| [set_optimize](#targetset_optimize) | Setting the optimization level | >= 1.0.1 | +| [set_languages](#targetset_languages) | Setting the Code Language Standard | >= 1.0.1 | +| [add_includedirs](#targetadd_includedirs) | Add Header Search Directory | >= 1.0.1 | +| [add_defines](#targetadd_defines) | Add Macro Definition | >= 1.0.1 | +| [add_undefines](#targetadd_undefines) | Cancel Macro Definition | >= 1.0.1 | +| [add_defines_h](#targetadd_defines_h) | Add macro definitions to header files | >= 1.0.1 | +| [add_undefines_h](#targetadd_undefines_h) | Cancel macro definition to header file | >= 1.0.1 | +| [add_cflags](#targetadd_cflags) | Add c Compile Options | >= 1.0.1 | +| [add_cxflags](#targetadd_cxflags) | Add c/c++ Compile Options | >= 1.0.1 | +| [add_cxxflags](#targetadd_cxxflags) | Add c++ Compile Options | >= 1.0.1 | +| [add_mflags](#targetadd_mflags) | Add objc compile options | >= 2.0.1 | +| [add_mxflags](#targetadd_mxflags) | Add objc/objc++ Compile Options | >= 2.0.1 | +| [add_mxxflags](#targetadd_mxxflags) | Add objc++ Compile Options | >= 2.0.1 | +| [add_scflags](#targetadd_scflags) | Add swift compile options | >= 2.1.1 | +| [add_asflags](#targetadd_asflags) | Add assembly compile options | >= 2.1.1 | +| [add_gcflags](#targetadd_gcflags) | Add go compile options | >= 2.1.1 | +| [add_dcflags](#targetadd_dcflags) | Add dlang compile options | >= 2.1.1 | +| [add_rcflags](#targetadd_rcflags) | Add rust compile option | >= 2.1.1 | +| [add_cuflags](#targetadd_cuflags) | Add cuda compile options | >= 2.2.1 | +| [add_culdflags](#targetadd_culdflags) | Add cuda device-link options | >= 2.2.7 | +| [add_ldflags](#targetadd_ldflags) | Add Link Options | >= 2.1.1 | +| [add_arflags](#targetadd_arflags) | Add Static Library Archive Options | >= 2.1.1 | +| [add_shflags](#targetadd_shflags) | Add Dynamic Library Link Options | >= 2.0.1 | +| [add_cfuncs](#targetadd_cfuncs) | Add c library function detection | >= 1.0.1 | +| [add_cxxfuncs](#targetadd_cxxfuncs) | Add C++ Library Function Interface | >= 1.0.1 | +| [add_languages](#targetadd_languages) | Add Language Standards | >= 2.0.1 | +| [add_vectorexts](#targetadd_vectorexts) | Add Vector Extension Instructions | >= 2.0.1 | +| [add_frameworks](#targetadd_frameworks) | Add Linked Framework | >= 2.1.1 | +| [add_frameworkdirs](#targetadd_frameworkdirs) | Add Linked Framework | >= 2.1.5 | + +| Obsolete Interface | Description | Supported Version | +| ----------------------------------------------------- | -------------------------------------------- | ---------------- | +| [add_bindings](#optionadd_bindings) | Add Forward Association Options, Sync Enable and Disable | >= 2.0.1 < 2.1.5 | +| [add_rbindings](#optionadd_rbindings) | Add reverse association option, sync enable and disable | >= 2.0.1 < 2.1.5 | +| [add_defines_if_ok](#optionadd_defines_if_ok) | Add macro definitions if the detection option passes | >= 1.0.1 < 2.1.5 | +| [add_defines_h_if_ok](#optionadd_defines_h_if_ok) | Add macro definitions to the configuration header if the detection option passes | >= 1.0.1 < 2.1.5 | +| [add_undefines_if_ok](#optionadd_undefines_if_ok) | Cancel macro definition if detection option passes | >= 1.0.1 < 2.1.5 | +| [add_undefines_h_if_ok](#optionadd_undefines_h_if_ok) | If the detection option passes, cancel the macro definition in the configuration header file | >= 1.0.1 < 2.1.5 | + +##### option + +###### Defining options + +Define and set option switches for custom compilation configuration options, switch settings. + +For example, define an option to enable test: + +```lua +option("test") + set_default(false) + set_showmenu(true) + add_defines("TEST") +``` + +Then associate it with the specified target: + +```lua +target("demo") + add_options("test") +``` + +Thus, if an option is defined, if this option is enabled, the macro definition of `-DTEST` will be automatically added when compiling the target. + +```lua +# Manually enable this option +$ xmake f --test=y +$ xmake +``` + +##### option_end + +###### End definition option + +This is an optional api that shows the departure option scope, similar to [target_end](#target_end). + +##### option:add_deps + +###### Adding options depends + +By setting the dependency, you can adjust the detection order of the options, which is generally used when the detection script is called by [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) +``` + +After the detection of the dependent small option is completed, the state of the option of the test is controlled by judging the state of the small option. + +##### option:before_check + +Execute this script before option detection + +For example: before testing, find the package by [find_package](#detect-find_package), and add information such as `links`, `includedirs` and `linkdirs` to the option. +Then start the option detection, and then automatically link to the target after passing. + +```lua +option("zlib") + before_check(function (option) + import("lib.detect.find_package") + option:add(find_package("zlib")) + end) +``` + +##### option:on_check + +###### Custom Option Detection Script + +This script overrides the built-in option detection logic. + +```lua +option("test") + add_deps("small") + set_default(true) + on_check(function (option) + if option:dep("small"):enabled() then + option:enable(false) + end + end) +``` + +If the option that test depends on passes, disable the test option. + +##### option:after_check + +Execute this script after option detection + +After the option detection is complete, execute this script for some post-processing, or you can re-disable the option at this time: + +```lua +option("test") + add_deps("small") + add_links("pthread") + after_check(function (option) + option:enable(false) + end) +``` + +##### option:set_values + +###### Setting the list of option values + +For the graphical menu configuration of `xmake f --menu` only, a list of option values is provided for quick selection by the user, for example: + +```lua +option("test") + set_default("b") + set_showmenu(true) + set_values("a", "b", "c") +``` + +The effect chart is as follows: + +
+
+##### option:set_default
+
+###### Setting options defaults
+
+When the option value is not modified by the command `xmake f --option=[y|n}`, the option itself has a default value, which can be set through this interface:
+
+```lua
+option("test")
+ -- This option is disabled by default
+ set_default(false)
+```
+
+The value of the option supports not only the boolean type but also the string type, for example:
+
+```lua
+option("test")
+ set_default("value")
+```
+
+| Value Type | Description | Configuration |
+| ------ | -------------------------------------- | ----------------------------------------------- |
+| boolean | Typically used as a parameter switch, value range: `true/false` | `xmake f --optionname=[y/n/yes/no/true/false]` |
+| string | can be any string, generally used for pattern judgment | `xmake f --optionname=value` |
+
+If it is an option of the `boolean` value, it can be judged by [is_option](#is_option), and the option is enabled.
+
+If it is an option of type `string`, it can be used directly in built-in variables, for example:
+
+```lua
+-- Define a path configuration option, using the temporary directory by default
+option("rootdir")
+ set_default("$(tmpdir)")Set_showmenu(true)
+
+target("test")
+ -- Add source files in the specified options directory
+ add_files("$(rootdir)/*.c")
+```
+
+Among them, `$(rootdir)` is a custom option built-in variable, which can be dynamically modified by manual configuration:
+
+```bash
+$ xmake f --rootdir=~/projectdir/src
+$ xmake
+```
+
+Specify a different source directory path for this `rootdir` option and compile it.
+
+Detection behavior of the option:
+
+| default value | detection behavior |
+| ---------- | --------------------------------------------------------------------------------------------- |
+| No setting | Priority manual configuration modification, disabled by default, otherwise automatic detection, can automatically switch boolean and string type according to the type of value manually passed in |
+| false | switch option, not automatic detection, disabled by default, can be manually configured to modify |
+| true | switch option, not automatic detection, enabled by default, can be manually configured to modify |
+| string type | no switch state, no automatic detection, can be manually configured and modified, generally used for configuration variable transfer |
+
+##### option:set_showmenu
+
+###### Set whether to enable menu display
+
+If set to `true`, then this option will appear in `xmake f --help`, which can also be configured via `xmake f --optionname=xxx`, otherwise it can only be used inside `xmake.lua` , the modification cannot be configured manually.
+
+```lua
+option("test")
+ set_showmenu(true)
+```
+
+After setting the menu to enable, execute `xmake f --help` to see that there is one more item in the help menu:
+
+```
+Options:
+ ...
+
+ --test=TEST
+```
+
+##### option:set_category
+
+###### Setting option categories, only for menu display
+
+This is an optional configuration, only used in the help menu, the classification display options, the same category of options, will be displayed in the same group, so the menu looks more beautiful.
+
+E.g:
+
+```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")
+```
+
+The four options here are grouped into two groups: `test` and `demo`, and the layout shown is similar to this:
+
+```bash
+Options:
+ ...
+
+ --test1=TEST1
+ --test2=TEST2
+
+ --demo1=DEMO1
+ --demo2=DEMO2
+```
+
+This interface is just to adjust the display layout, more beautiful, no other use.
+
+In version 2.1.9, the hierarchical path name `set_category("root/submenu/submenu2")` can be set via category to configure the graphical menu interface of `xmake f --menu`, for example:
+
+```lua
+-- 'boolean' option
+option("test1")
+ set_default(true)
+ set_showmenu(true)
+ set_category("root menu/test1")
+
+-- 'choice' option with values: "a", "b", "c"
+option("test2")
+ set_default("a")
+ set_values("a", "b", "c")
+ set_showmenu(true)
+ set_category("root menu/test2")
+
+-- 'string' option
+option("test3")
+ set_default("xx")
+ set_showmenu(true)
+ set_category("root menu/test3/test3")
+
+-- 'number' option
+option("test4")
+ set_default(6)
+ set_showmenu(true)
+ set_category("root menu/test4")
+```
+
+The menu interface path structure finally displayed in the above configuration:
+
+- root menu
+ - test1
+ - test2
+ - test3
+ - test3
+ - test4
+
+The effect chart is as follows:
+
+
+
+##### option:set_description
+
+###### Setting menu display description
+
+When the option menu is displayed, the description on the right is used to help the user know more clearly about the purpose of this option, for example:
+
+```lua
+option("test")
+ set_default(false)
+ set_showmenu(true)
+ set_description("Enable or disable test")
+```
+
+The generated menu contents are as follows:
+
+```
+Options:
+ ...
+
+ --test=TEST Enable or disable test (default: false)
+```
+
+This interface also supports multi-line display and outputs more detailed description information, such as:
+
+```lua
+option("mode")
+ set_default("debug")
+ set_showmenu(true)
+ set_description("Set build mode",
+ " - debug",
+ " - release",
+ "-profile")
+```
+
+The generated menu contents are as follows:
+
+```
+Options:
+ ...
+
+ --mode=MODE Set build mode (default: debug)
+ - debug
+ - release
+ - profile
+```
+
+When you see this menu, the user can clearly know the specific use of the defined `mode` option and how to use it:
+
+```bash
+$ xmake f --mode=release
+```
+
+##### option:add_bindings
+
+###### Add forward association option, sync enable and disable
+
++After the 2.1.5 version has been deprecated, please use [add_deps](#optionadd_deps), [on_check](#optionon_check), [after_check](#optionafter_check) and other interfaces instead. +
+ +Bind association options, for example I want to configure a `smallest` parameter on the command line: `xmake f --smallest=y` + +At this time, it is necessary to disable multiple other option switches at the same time to prohibit compiling multiple modules. This is the requirement, which is equivalent to the linkage between one option and other options. + +This interface is used to set some association options that need to be forward bound, for example: + +```lua +-- Define option switches: --smallest=y|n +option("smallest") + + -- Add forward binding. If smallest is enabled, all of the following option switches will also be enabled synchronously. + add_bindings("nozip", "noxml", "nojson") +``` + +##### option:add_rbindings + +###### Add reverse association option, sync enable and disable + ++After the 2.1.5 version has been deprecated, please use [add_deps](#optionadd_deps), [on_check](#optionon_check), [after_check](#optionafter_check) and other interfaces instead. +
+ +Reverse binding association options, the switch state of the associated option is reversed. + +```lua +-- Define option switches: --smallest=y|n +option("smallest") + + -- Add reverse binding, if smallest is enabled, all modules below are disabled + add_rbindings("xml", "zip", "asio", "regex", "object", "thread", "network", "charset", "database") + add_rbindings("zlib", "mysql", "sqlite3", "openssl", "polarssl", "pcre2", "pcre", "base") +``` + ++It should be noted that the command line configuration is sequential. You can disable all modules by enabling smallest and then add other options to enable them one by one. +
+ +E.g: + +```bash +-- disable all modules and then only enable xml and zip modules +$ xmake f --smallest=y --xml=y --zip=y +``` + +##### option:add_links + +###### Add Link Library Detection + +If the specified link library is passed, this option will be enabled and the associated target will automatically be added to this link, for example: + +```lua +option("pthread") + set_default(false) + add_links("pthread") + add_linkdirs("/usr/local/lib") + +target("test") + add_options("pthread") +``` + +If the test passes, the `test` target will be automatically added when it is compiled: `-L/usr/local/lib -lpthread` compile option + + +##### option:add_linkdirs + +###### Adding the search directory needed for link library detection + +This is optional. Generally, the system library does not need to add this, and it can also pass the test. If it is not found, you can add the search directory yourself to improve the detection pass rate. For details, see: [add_links](#optionadd_links) + +##### optiOn:add_rpathdirs + +###### Adding a load search directory for a dynamic library at runtime + +After the option passes the detection, it will be automatically added to the corresponding target. For details, see: [target.add_rpathdirs](#targetadd_rpathdirs). + +##### option:add_cincludes + +###### Add c header file detection + +This option will be enabled if the c header file is passed, for example: + +```lua +option("pthread") + set_default(false) + add_cincludes("pthread.h") + add_defines("ENABLE_PTHREAD") + +target("test") + add_options("pthread") +``` + +This option checks if there is a `pthread.h` header file. If the test passes, then the `test` target program will add the macro definition of `ENABLE_PTHREAD`. + +If you want more flexible detection, you can do this in [option.on_check](#optionon_check) via [lib.detect.has_cincludes](#detect-has_cincludes). + +##### option:add_cxxincludes + +###### Add c++ header file detection + +Similar to [add_cincludes](#optionadd_cincludes), except that the detected header file type is a c++ header file. + +##### option:add_ctypes + +###### Add c type detection + +This option will be enabled if the c type is passed, for example: + +```lua +option("wchar") + set_default(false) + add_cincludes("wchar_t") + add_defines("HAVE_WCHAR") + +target("test") + add_options("wchar") +``` + +This option checks if there is a type of `wchar_t`. If the test passes, then the `test` target program will add the macro definition of `HAVE_WCHAR`. + +If you want more flexible detection, you can do this in [option.on_check](#optionon_check) via [lib.detect.has_ctypes](#detect-has_ctypes). + +##### option:add_cxxtypes + +###### Adding c++ type detection + +Similar to [add_ctypes](#optionadd_ctypes), except that the type detected is a c++ type. + +##### option:add_csnippet + +###### Add c code fragment detection + +If the existing [add_ctypes](#optionadd_ctypes), [add_cfuncs](#optionadd_cfuncs), etc. cannot meet the current detection requirements, +You can use this interface to implement more custom detection of some compiler feature detection, see: [add_cxxsnippet](#optionadd_cxxsnippet). + +##### option:add_cxxsnippet + +###### Adding c++ code snippet detection + +This interface can be used to implement more custom detection of some compiler feature detection, especially the detection support of various features of C++, such as: + +```lua +option("constexpr") + add_cxxsnippet("constexpr", "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);") +``` + +The first parameter sets the name of the code snippet as a label, and is displayed when the output information is detected. + +The above code implements the detection of the constexpr feature of C++. If the test passes, the constexpr option is enabled. Of course, this is just an example. + +For the detection of compiler features, there is a more convenient and efficient detection module, providing more powerful detection support, see: [compiler.has_features](#compiler-has_features) and [detect.check_cxsnippets](#detect-check_cxsnippets) + +If you want more flexible detection, you can do this in [option.on_check](#optionon_check) via [lib.detect.check_cxsnippets](#detect-check_cxsnippets). + +##### option:add_defines_if_ok + +###### Add macro definition if the detection option is passed + ++After the 2.1.5 version has been deprecated, please use the [add_defines](#targetadd_defines) interface instead. +
+ +The detection options will not be set until they are passed. See the example in [add_cincludes](#optionadd_cincludes) for details. + +##### option:add_defines_h_if_ok + +###### If the detection option is passed, add the macro definition to the configuration header file. + ++After the 2.1.5 version has been deprecated, please use the [add_defines_h](#targetadd_defines_h) interface instead. +
+ +Similar to [add_defines_if_ok](#optionadd_defines_if_ok), the macro definitions are automatically added to the `config.h` header file after the test is passed. + +E.g: + +```lua +option("pthread") + set_default(false) + add_cincludes("pthread.h") + add_defines_h_if_ok("ENABLE_PTHREAD") + +target("test") + add_options("pthread") +``` + +After passing, it will be added to `config.h`: + +```c +#define ENABLE_PTHREAD 1 +``` + +How to set the specific `config.h`, see: [set_config_h](#targetset_config_h) + +##### option:add_undefines_if_ok + +###### If the detection option is passed, cancel the macro definition + ++After the 2.1.5 version has been deprecated, please use the [add_undefines](#targetadd_undefines) interface instead. +
+ +Similar to [add_defines_if_ok](#optionadd_defines_if_ok), except that the macro definition is canceled after the pass is detected. + +##### option:add_undefines_h_if_ok + +###### If the detection option is passed, the macro definition is canceled in the configuration header file. + ++Deprecated after version 2.1.5, please use [add_undefines_h](#targetadd_undefines_h) interface instead. +
+ +Similar to [add_defines_h_if_ok](#optionadd_defines_h_if_ok), the macro definition will be canceled in `config.h` after the test is passed. + +```c +#undef DEFINED_MACRO +``` + +How to set the specific `config.h`, see: [set_config_h](#targetset_config_h) + +#### Plugin and Task + +Xmake can implement custom tasks or plugins. The core of both is the `task` task. The two are actually the same. The xmake plugins are implemented with `task`. + +In essence, they are tasks, except that the [set_category](#taskset_category) classification is different. + +| Interface | Description | Supported Versions | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [task](#task) | Define plugins or tasks | >= 2.0.1 | +| [task_end](#task_end) | End defining plugins or tasks | >= 2.1.1 | +| [set_menu](#taskset_menu) | Set Task Menu | >= 2.0.1 | +| [set_category](#taskset_category) | Set Task Category | >= 2.0.1 | +| [on_run](#taskon_run) | Set Task Run Script | >= 2.0.1 | + +##### task + +###### Defining plugins or tasks + +The `task` field is used to describe a custom task implementation, in the same level as [target](#target) and [option](#option). + +For example, here is a simple task defined: + +```lua +task("hello") + + -- Set the run script + on_run(function () + print("hello xmake!") + end) +``` + +This task only needs to print `hello xmake!`, how do you run it? + +Since the [set_menu](#taskset_menu) setting menu is not used here, this task can only be called inside the custom script of `xmake.lua` or other tasks, for example: + +```lua +target("test") + + after_build(function (target) + + -- Import task module + import("core.project.task") + + -- Run the hello task + task.run("hello") + end) +``` + +Run the `hello` task after building the `test` target. + +##### task_end + +###### End defining plugins or tasks + +This is an optional api that shows the departure option scope, similar to [target_end](#target_end). + +##### task:set_menu + +###### Setting the task menu + +By setting a menu, this task can be opened to the user to manually call through the command line. The menu settings are as follows: + +```lua +task("echo") + + -- Set the run script + on_run(function () + + -- Import parameter option module + import("core.base.option") + + -- Initialize color mode + local modes = "" + for _, mode in ipairs({"bright", "dim", "blink", "reverse"}) do + if option.get(mode) then + modes = modes .. " " .. mode + end + end + + -- Get parameter content and display information + cprint("${%s%s}%s", option.get("color"), modes, table.concat(option.get("contents") or {}, " ")) + end) + + -- Set the command line options for the plugin. There are no parameter options here, just the plugin description. + set_menu { + -- Settings menu usage + usage = "xmake echo [options]" + + -- Setup menu description + , description = "Echo the given info!" + + -- Set menu options, if there are no options, you can set it to {} + , options = + { + -- Set k mode as key-only bool parameter + {'b', "bright", "k", nil, "Enable bright." } + , {'d', "dim", "k", nil, "Enable dim." } + , {'-', "blink", "k", nil, "Enable blink." } + , {'r', "reverse", "k", nil, "Reverse color." } + + -- When the menu is displayed, a blank line + , {} + + -- Set kv as the key-value parameter and set the default value: black + , {'c', "color", "kv", "black", "Set the output color." + , " - red" + , " - blue" + , " - yellow" + , " - green" + , " - magenta" + , " - cyan" + , " - white" } + + -- Set `vs` as a value multivalued parameter and a `v` single value type + -- generally placed last, used to get a list of variable parameters + , {} + , {nil, "contents", "vs", nil, "The info contents." } + } + } +``` + +After defining this task, execute `xmake --help` and you will have one more task item: + +``` +Tasks: + + ... + + Echo Echo the given info! +``` + +If the classification is `plugin` by [set_category](#taskset_category), then this task is a plugin: + +``` +Plugins: + + ... + + Echo Echo the given info! +``` + +To run this task manually, you can execute: + +```bash +$ xmake echo hello xmake! +``` + +Just fine, if you want to see the menu defined by this task, you only need to execute: `xmake echo [-h|--help]`, the result is as follows: + +```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. +``` + ++The most part of the menu is the common options built into xmake. Basically, each task will be used. You don't need to define it yourself to simplify the menu definition. +
+ +Below, let's actually run this task, for example, I want to display the red `hello xmake!`, only need to: + +```bash +$ xmake echo -c red hello xmake! +``` + +You can also use the full name of the option and highlight it: + +```bash +$ xmake echo --color=red --bright hello xmake! +``` + +The last variable argument list is retrieved by `option.get("contents")` in the `run` script, which returns an array of type `table`. + +##### task:set_category + +###### Setting task categories + +It is only used for grouping of menus. Of course, the plugin will use `plugin` by default. The built-in task will use `action` by default, but it is just a convention. + ++You can use any name you define yourself. The same name will be grouped and displayed together. If it is set to `plugin`, it will be displayed in the Plugins group of xmake. +
+ +E.g: + +```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 theGiven .app + echo Echo the given info! +``` + +If you do not call this interface to set the classification, the default is to use the `Tasks` group display, which represents the normal task. + +##### task:on_run + +###### Setting up a task to run a script + +There are two ways to set it up. The easiest way is to set the inline function: + +```lua +task("hello") + + on_run(function () + print("hello xmake!") + end) +``` + +This is convenient and small for small tasks, but it is not suitable for large tasks, such as plugins, which require complex scripting support. + +This time you need a separate module file to set up the run script, for example: + +```lua +task("hello") + on_run("main") +``` + +Here the `main` is set to run the main entry module for the script. The file name is `main.lua`, placed in the same directory as `xmake.lua` that defines `task`. Of course, you can use other file names. + +The directory structure is as follows: + +``` +projectdir + - xmake.lua + - main.lua +``` + +The contents of `main.lua` are as follows: + +```lua +function main(...) + print("hello xmake!") +end +``` + +It's a simple script file with the main function of `main`. You can import various extension modules via [import](#import) to implement complex functions, such as: + +```lua +-- Import parameter option module +import("core.base.option") + +-- Entrance function +function main(...) + + -- Get the parameter content + print("color: %s", option.get("color")) +end +``` + +You can also create multiple custom module files in the current directory and use them after importing via [import](#import), for example: + +``` +Projectdir + - xmake.lua + - main.lua + - module.lua +``` + +The contents of `module.lua` are as follows: + +```lua +-- Define an export interface +function hello() + print("hello xmake!") +end +``` + ++The private interface is named by the `_hello` with a descending line prefix, so that the imported module will not contain this interface and will only be used inside the module itself. +
+ +Then make a call in `main.lua`: + + +```lua +import("module") + +function main(...) + module.hello() +end +``` + +For more modules, see: [Built-in Module](#Built-in Module) and [Extension Module](Extension Module) + +Among them, the parameter in `main(...)` is specified by `task.run`, for example: + +```lua +task.run("hello", {color="red"}, arg1, arg2, arg3) +``` + +Inside the `arg1, arg2` these are the arguments to the `hello` task `main(...)` entry, and `{color="red"}` to specify the parameter options in the task menu. + +For a more detailed description of `task.run`, see: [task.run](#task-run) + +#### Custom Rule + +After the 2.2.1 release, xmake not only natively supports the construction of multi-language files, but also allows users to implement complex unknown file builds by custom building rules. + +We can extend the build support for other files by pre-setting the file suffixes supported by the rules: + +```lua +-- Define a build rule for a markdown file +rule("markdown") + set_extensions(".md", ".markdown") + on_build_file(function (target, sourcefile) + os.cp(sourcefile, path.join(target:targetdir(), path.basename(sourcefile) .. ".html")) + end) + +target("test") + set_kind("binary") + + -- Make the test target support the construction rules of the markdown file + add_rules("markdown") + + -- Adding a markdown file to build + add_files("src/*.md") + add_files("src/*.markdown") +``` + +We can also specify some other scattered files to be processed as markdown rules: + +```lua +target("test") + -- ... + add_files("src/test/*.md.in", {rule = "markdown"}) +``` + +A target can be superimposed to apply multiple rules to more customize its own build behavior, and even support different build environments. + ++Rules specified by `add_files("*.md", {rule = "markdown"})`, with a higher priority than the rule set by `add_rules("markdown")`. +
+ +| Interface | Description | Supported Versions | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [rule](#rule) | Defining Rules | >= 2.1.9 | +| [add_imports](#ruleadd_imports) | Pre-importing extension modules for all custom scripts | >= 2.1.9 | +| [set_extensions](#ruleset_extensions) | Set the file extension type supported by the rule | >= 2.1.9 | +| [on_load](#ruleon_load) | Custom Load Script | >= 2.2.1 | +| [on_link](#ruleon_link) | Custom Link Script | >= 2.2.7 | +| [on_build](#ruleon_build) | Custom Compilation Script | >= 2.1.9 | +| [on_clean](#ruleon_clean) | Custom Cleanup Script | >= 2.1.9 | +| [on_package](#ruleon_package) | Custom Package Script | >= 2.1.9 | +| [on_install](#ruleon_install) | Custom Installation Script | >= 2.1.9 | +| [on_uninstall](#ruleon_uninstall) | Custom Uninstall Script | >= 2.1.9 | +| [on_build_file](#ruleon_build_file) | Custom the build script to implement single file build | >= 2.2.1 | +| [on_build_files](#ruleon_build_files) | Custom Compilation Scripts for Multi-File Construction | >= 2.2.1 | +| [before_load](#rulebefore_load) | Custom pre-load script | >= 2.2.1 | +| [before_link](#rulebefore_link) | Custom pre-link script | >= 2.2.7 | +| [before_build](#rulebefore_build) | Custom pre-compilation script | >= 2.2.1 | +| [before_clean](#rulebefore_clean) | Custom the script before cleanup | >= 2.2.1 | +| [before_package](#rulebefore_package) | Custom the script before packaging | >= 2.2.1 | +| [before_install](#rulebefore_install) | Custom Pre-Installation Scripts | >= 2.2.1 | +| [before_uninstall](#rulebefore_uninstall) | Custom the script before uninstalling | >= 2.2.1 | +| [before_build_file](#rulebefore_build_file) | Custom pre-compilation scripts to implement single file builds | >= 2.2.1 | +| [before_build_files](#rulebefore_build_files) | Custom pre-compilation scripts for multi-file build | >= 2.2.1 | +| [after_load](#ruleafter_load) | Custom script after loading | >= 2.2.1 | +| [after_link](#ruleafter_link) | Custom script after linking | >= 2.2.7 | +| [after_build](#ruleafter_build) | Custom script after compilation | >= 2.2.1 | +| [after_clean](#ruleafter_clean) | Custom script after cleaning | >= 2.2.1 | +| [after_package](#ruleafter_package) | Custom script after packaging | >= 2.2.1 | +| [after_install](#ruleafter_install) | Custom script after installing | >= 2.2.1 | +| [after_uninstall](#ruleafter_uninstall) | Custom script after uninstalling | >= 2.2.1 | +| [after_build_file](#ruleafter_build_file) | Custom script after compiling the single file | >= 2.2.1 | +| [after_build_files](#ruleafter_build_files) | Custom script after compiling the multiple file | >= 2.2.1 | +| [rule_end](#rule_end) | End Definition Rule | >= 2.1.9 | + +##### Built-in rules + +sinceAfter the 2.2.1 release, xmake provides some built-in rules to simplify the daily xmake.lua description and support for some common build environments. + +| Rules | Description | Supported Versions | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [mode.debug](#mode-debug) | Debug Mode Compilation Rules | >= 2.2.1 | +| [mode.release](#mode-release) | Release Mode Compilation Rules | >= 2.2.1 | +| [mode.check](#mode-check) | Detection Mode Compilation Rules | >= 2.2.1 | +| [mode.profile](#mode-profile) | Performance Analysis Mode Compilation Rules | >= 2.2.1 | +| [mode.coverage](#mode-coverage) | Coverage Analysis Compilation Mode Rules | >= 2.2.1 | +| [qt.static](#qt-static) | Qt Static Library Compilation Rules | >= 2.2.1 | +| [qt.shared](#qt-shared) | Qt Dynamic Library Compilation Rules | >= 2.2.1 | +| [qt.console](#qt-console) | Qt Console Compilation Rules | >= 2.2.1 | +| [qt.application](#qt-application) | Qt Application Compilation Rules | >= 2.2.1 | +| [wdk.umdf.driver](#wdk-umdf-driver) | WDK Environment umdf Driver Compilation Rules | >= 2.2.1 | +[wdk.umdf.binary](#wdk-umdf-binary) | WDK Environment umdf Driver Application Compilation Rules | >= 2.2.1 | +| [wdk.kmdf.driver](#wdk-kmdf-driver) | WDK Environment kmdf Driver Compilation Rules | >= 2.2.1 | +[wdk.kmdf.binary](#wdk-kmdf-binary) | WDK Environment kmdf Driver Application Compilation Rules | >= 2.2.1 | +| [wdk.wdm.driver](#wdk-wdm-driver) | WDK Environment wdm Driver Compilation Rules | >= 2.2.1 | +[wdk.wdm.binary](#wdk-wdm-binary) | WDK Environment wdm Driver Application Compilation Rules | >= 2.2.1 | + +###### mode.debug + +Add the configuration rules for the debug compilation mode for the current project xmake.lua, for example: + +```lua +add_rules("mode.debug") +``` + +Equivalent to: + +```lua +-- the debug mode +if is_mode("debug") then + + -- enable the debug symbols + set_symbols("debug") + + -- disable optimization + set_optimize("none") +end +``` + +We can switch to this compilation mode by ``xmake f -m debug`. + +###### mode.release + +Add the configuration rules for the release compilation mode for the current project xmake.lua, for example: + +```lua +add_rules("mode.release") +``` + +Equivalent to: + +```lua +-- the release mode +if is_mode("release") then + + -- set the symbols visibility: hidden + set_symbols("hidden") + + -- enable2017 optimization + set_optimize("fastest") + + -- strip all symbols + set_strip("all") +end +``` + +We can switch to this compilation mode by ``xmake f -m release`. + +###### mode.check + +Add the check compilation mode configuration rules for the current project xmake.lua, generally used for memory detection, for example: + +```lua +add_rules("mode.check") +``` + +Equivalent to: + +```lua +-- the check mode +if is_mode("check") then + + -- enable the debug symbols + set_symbols("debug") + + -- disable optimization + set_optimize("none") + + -- attempt to enable some checkers for pc + add_cxflags("-fsanitize=address", "-ftrapv") + add_mxflags("-fsanitize=address", "-ftrapv") + add_ldflags("-fsanitize=address") +end +``` + +We can switch to this compilation mode by ``xmake f -m check`. + +###### mode.profile + +Add configuration rules for the profile compilation mode for the current project xmake.lua, which is generally used for performance analysis, for example: + +```lua +add_rules("mode.profile") +``` + +Equivalent to: + +```lua +-- the profile mode +if is_mode("profile") then + + -- enable the debug symbols + set_symbols("debug") + + -- enable gprof + add_cxflags("-pg") + add_ldflags("-pg") +end +``` + +We can switch to this compilation mode by ``xmake f -m profile`. + +###### mode.coverage + +Add the configuration rules for the coverage compilation mode for the current project xmake.lua, which is generally used for coverage analysis, for example: + +```lua +add_rules("mode.coverage") +``` + +Equivalent to: + +```lua +-- the coverage mode +if is_mode("coverage") then + add_cxflags("--coverage") + add_mxflags("--coverage") + add_ldflags("--coverage") +end +``` + +We can switch to this compilation mode by ``xmake f -m coverage`. + +###### qt.static + +A static library program used to compile and generate Qt environments: + +```lua +target("qt_static_library") + add_rules("qt.static") + add_files("src/*.cpp") + add_frameworks("QtNetwork", "QtGui") +``` + +###### qt.shared + +Dynamic library program for compiling and generating Qt environment: + +```lua +target("qt_shared_library") + add_rules("qt.shared") + add_files("src/*.cpp") + add_frameworks("QtNetwork", "QtGui") +``` + +###### qt.console + +A console program for compiling and generating a Qt environment: + +```lua +target("qt_console") + add_rules("qt.console") + add_files("src/*.cpp") +``` + +###### qt.application + +Used to compile ui applications that generate Qt environments. + +Quick(qml) application: + +```lua +target("qt_quickapp") + add_rules("qt.application") + add_files("src/*.cpp") + add_files("src/qml.qrc") + add_frameworks("QtQuick") +``` + +Qt Widgets (ui/moc) application: + +```lua +-- add target +target("qt_widgetapp") + add_rules("qt.application") + add_files("src/*.cpp") + add_files("src/mainwindow.ui") + add_files("src/mainwindow.h") -- Add a meta header file with Q_OBJECT + add_frameworks("QtWidgets") +``` + +For more descriptions of Qt, see: [#160](https://github.com/xmake-io/xmake/issues/160) + + + +###### wdk.env.kmdf + +Application of the compilation environment setting of kmdf under WDK, need to cooperate with: `wdk.[driver|binary|static|shared]` and other rules to use. + +###### wdk.env.umdf + +Application of the umdf compiler environment settings under WDK, you need to cooperate with: `wdk.[driver|binary|static|shared]` and other rules to use. + +###### wdk.env.wdm + +Application wdm compiler environment settings under WDK, need to cooperate with: `wdk.[driver|binary|static|shared]` and other rules to use. + +###### wdk.driver + +Compile and generate drivers based on the WDK environment under Windows. Currently, only the WDK10 environment is supported. + +Note: need to cooperate: `wdk.env.[umdf|kmdf|wdm]`Environmental rules are used. + +```lua +-- add target +target("echo") + + -- add rules + add_rules("wdk.driver", "wdk.env.kmdf") + + -- add files + add_files("driver/*.c") + add_files("driver/*.inx") + + -- add includedirs + add_includedirs("exe") +``` + +###### wdk.binary + +Compile and generate executable programs based on WDK environment under Windows. Currently, only WDK10 environment is supported. + +Note: It is necessary to cooperate with: environment rules such as `wdk.env.[umdf|kmdf|wdm]`. + +```lua +-- add target +target("app") + + -- add rules + add_rules("wdk.binary", "wdk.env.umdf") + + -- add files + add_files("exe/*.cpp") +``` + +###### wdk.static + +Compile and generate static library programs based on WDK environment under Windows. Currently, only WDK10 environment is supported. + +Note: It is necessary to cooperate with: environment rules such as `wdk.env.[umdf|kmdf|wdm]`. + +```lua +target("nonpnp") + + -- add rules + add_rules("wdk.static", "wdk.env.kmdf") + + -- add flags for rule: wdk.tracewpp + add_values("wdk.tracewpp.flags", "-func:TraceEvents(LEVEL,FLAGS,MSG,...)", "-func:Hexdump((LEVEL,FLAGS,MSG,...))") + + -- add files + add_files("driver/*.c", {rule = "wdk.tracewpp"}) +``` + +###### wdk.shared + +Compile and generate dynamic library programs based on WDK environment under Windows. Currently, only WDK10 environment is supported. + +Note: It is necessary to cooperate with: environment rules such as `wdk.env.[umdf|kmdf|wdm]`. + +```lua +target("nonpnp") + + -- add rules + add_rules("wdk.shared", "wdk.env.wdm") + + -- add flags for rule: wdk.tracewpp + add_values("wdk.tracewpp.flags", "-func:TraceEvents(LEVEL,FLAGS,MSG,...)", "-func:Hexdump((LEVEL,FLAGS,MSG,...))") + + -- add files + add_files("driver/*.c", {rule = "wdk.tracewpp"}) +``` + +###### wdk.tracewpp + +Used to enable tracewpp to preprocess source files: + +```lua +target("nonpnp") + + -- add rules + add_rules("wdk.driver", "wdk.env.kmdf") + + -- add flags for rule: wdk.tracewpp + add_values("wdk.tracewpp.flags", "-func:TraceEvents(LEVEL,FLAGS,MSG,...)", "-func:Hexdump((LEVEL,FLAGS,MSG,...))") + + -- add files + add_files("driver/*.c", {rule = "wdk.tracewpp"}) + add_files("driver/*.rc") +``` + +For more information on WDK rules, see: [#159](https://github.com/xmake-io/xmake/issues/159) + +###### win.sdk.application + +Compile and generate the winsdk application. + +```lua +-- add rules +add_rules("mode.debug", "mode.release") + +-- define target +target("usbview") + + -- windows application + add_rules("win.sdk.application") + + -- add files + add_files("*.c", "*.rc") + add_files("xmlhelper.cpp", {rule = "win.sdk.dotnet"}) +``` + +###### wdk.sdk.dotnet + +Used to specify certain c++ source files to be compiled as c++.net. + +```lua +add_files("xmlhelper.cpp", {rule = "win.sdk.dotnet"}) +``` + +For more information on WDK rules, see: [#159](https://github.com/xmake-io/xmake/issues/159) + +##### rule + +###### Defining rules + +```lua +rule("markdown") + set_extensions(".md", ".markdown") + on_build_file(function (target, sourcefile, opt) + os.cp(sourcefile, path.join(target:targetdir(), path.basename(sourcefile) .. ".html")) + end) +``` + +##### rule:add_imports + +###### Pre-importing extension modules for all custom scripts + +For usage and description, please see: [target:add_imports](#targetadd_imports), the usage is the same. + +##### rule:set_extensions + +###### Setting the file extension type supported by the rule + +Apply rules to files with these suffixes by setting the supported extension file types, for example: + +```lua +-- Define a build rule for a markdown file +rule("markdown") + set_extensions(".md", ".markdown") + on_build_file(function (target, sourcefile, opt) + os.cp(sourcefile, path.join(target:targetdir(), path.basename(sourcefile) .. ".html")) + end) + +target("test") + set_kind("binary") + + -- Make the test target support the construction rules of the markdown file + add_rules("markdown") + + -- Adding a markdown file to build + add_files("src/*.md") + add_files("src/*.markdown") +``` + +##### rule:on_load + +###### Custom load script + +The load script used to implement the custom rules will be executed when the target is loaded. You can customize some target configurations in it, for example: + +```lua +rule("test") + on_load(function (target) + target:add("defines", "-DTEST") + end) +``` + +##### rule:on_link + +###### Custom link script + +The link script used to implement the custom rules overrides the default link behavior of the applied target, for example: + +```lua +rule("test") + on_link(function (target) + end) +``` + +##### rule:on_build + +###### Custom compilation script + +The build script used to implement the custom rules overrides the default build behavior of the target being applied, for example: + +```lua +rule("markdown") + on_build(function (target) + end) +``` + +##### rule:on_clean + +###### Custom cleanup script + +The cleanup script used to implement the custom rules will override the default cleanup behavior of the applied target, for example: + +```lua +rule("markdown") + on_clean(function (target) + -- remove sourcefile.html + end) +``` + +##### rule:on_package + +###### Custom packaging script + +A packaging script for implementing custom rules that overrides the default packaging behavior of the target being applied, for example: + +```lua +rule("markdown") + on_package(function (target) + -- package sourcefile.html + end) +``` + +##### rule:on_install + +###### Custom installation script + +An installation script for implementing custom rules that overrides the default installation behavior of the target being applied, for example: + +```lua +rule("markdown") + on_install(function (target) + end) +``` + +##### rule:on_uninstall + +###### Custom Uninstall Script + +An uninstall script for implementing custom rules that overrides the default uninstall behavior of the target being applied, for example: + +```lua +rule("markdown") + on_uninstall(function (target) + end) +``` + +##### rule:on_build_file + +###### Customizing the build script to process one source file at a time + +```lua +rule("markdown") + on_build_file(function (target, sourcefile, opt) + print("%%%d: %s", opt.progress, sourcefile) + end) +``` + +The third parameter opt is an optional parameter, which is used to obtain some information state during the compilation process. For example, opt.progress is the compilation progress of the current period. + +##### rule:on_build_files + +###### Customizing the build script to process multiple source files at once + +Most of the custom build rules, each time processing a single file, output a target file, for example: a.c => a.o + +However, in some cases, we need to enter multiple source files together to build an object file, for example: a.c b.c d.c => x.o + +For this situation, we can achieve this by customizing this script: + +```lua +rule("markdown") + on_build_files(function (target, sourcebatch, opt) + -- build some source files + for _, sourcefile in ipairs(sourcebatch.sourcefiles) do + -- ... + end + end) +``` + +##### rule:before_load + +###### Custom pre-load script + +Used to implement the execution script before the custom target is loaded, for example: + +```lua +rule("test") + before_load(function (target) + target:add("defines", "-DTEST") + end) +``` + +##### rule:before_link + +###### Custom pre-link script + +Execution scripts used to implement custom target links, for example: + +```lua +rule("test") + before_link(function (target) + end) +``` + +##### rule:before_build + +###### Custom pre-compilation script + +Used to implement the execution script before the custom target is built, for example: + +```lua +rule("markdown") + before_build(function (target) + end) +``` + +##### rule:before_clean + +###### Custom pre-cleanup script + +Used to implement the execution script before the custom target cleanup, for example: + +```lua +rule("markdown") + before_clean(function (target) + end) +``` + +##### rule:before_package + +###### Custom the pre-package script + +Used to implement the execution script before the custom target is packaged, for example: + +```lua +rule("markdown") + before_package(function (target) + end) +``` + +##### rule:before_install + +###### Custom pre-installation script + +Used to implement the execution script before the custom target installation, for example: + +```lua +rule("markdown") + before_install(function (target) + end) +``` + +##### rule:before_uninstall + +###### Custom pre-uninstall script + +Used to implement the execution script before the custom target is uninstalled, for example: + +```lua +rule("markdown") + before_uninstall(function (target) + end) +``` + +##### rule:before_build_file + +###### Custom pre-compilation script to process one source file at a time + +Similar to [rule:on_build_file](#ruleon_build_file), but the timing of this interface is called before compiling a source file. +Generally used to preprocess some source files before compiling. + +##### rule:before_build_files + +###### Custom pre-compilation script to process multiple source files at once + +Similar to [rule:on_build_files](#ruleon_build_files), but the timing of this interface is called before compiling some source files. +Generally used to preprocess some source files before compiling. + +##### rule:after_load + +###### Custom post-loading script + +The execution script used to implement the custom target loading is similar to [rule:after_load](#ruleafter_load). + +##### rule:after_link + +###### Custom post-linking script + +The execution script used to implement the custom target link is similar to [rule:after_link](#ruleafter_link). + +##### rule:after_build + +###### Custom post-compilation script + +The execution script used to implement the custom target build is similar to [rule:before_build](#rulebefore_build). + +##### rule:after_clean + +###### Custom post-cleaning script + +The execution script used to implement the custom target cleanup is similar to [rule:before_clean](#rulebefore_clean). + +##### rule:after_package + +###### Custom post-packaging script + +The execution script used to implement the custom target package is similar to [rule:before_package](#rulebefore_package). + +##### rule:after_install + +###### Custom post-installation script + +The execution script used to implement the custom target installation is similar to [rule:before_install](#rulebefore_install). + +##### rule:after_uninstall + +###### Custom post-uninstallation Script + +The execution script used to implement the custom target uninstallation is similar to [rule:before_uninstall](#rulebefore_uninstall). + +##### rule:after_build_file + +###### Custom post-compilation scripts to process one source file at a time + +Similar to [rule:on_build_file](#ruleon_build_file), but the timing of this interface is called after compiling a source file. +Generally used to post-process some compiled object files. + +##### rule:after_build_files + +###### Custom post-compilation scripts to process multiple source files at once + +Similar to [rule:on_build_files](#ruleon_build_files), but the timing of this interface is called after compiling some source files. +Generally used to post-process some compiled object files. + +##### rule_end + +###### End definition rules + +This is optional. If you want to manually end the rule definition, you can call it: + +```lua +rule("test") + -- .. +rule_end() +``` + +#### Remote package dependencies + +The repository depends on the package definition description, the `package()` related interface definition, etc. There will be time to elaborate, so stay tuned. . + +Please refer to the existing package description in the official repository: [xmake-repo](https://github.com/xmake-io/xmake-repo) + +Here is a more representative example for reference: + +```lua +package("libxml2") + + set_homepage("http://xmlsoft.org/") + set_description("The XML C parser and toolkit of Gnome.") + + set_urls("https://github.com/GNOME/libxml2/archive/$(version).zip", {excludes = {"*/result/*", "*/test/*"}}) + + add_versions("v2.9.8", "c87793e45e66a7aa19200f861873f75195065de786a21c1b469bdb7bfc1230fb") + add_versions("v2.9.7", "31dd4c0e10fa625b47e27fd6a5295d246c883f214da947b9a4a9e13733905ed9") + + if is_plat("macosx", "linux") then + add_deps("autoconf", "automake", "libtool", "pkg-config") + end + + on_load(function (package) + package:addvar("includedirs", "include/libxml2") + package:addvar("links", "xml2") + end) + + if is_plat("windows") and winos.version():gt("winxp") then + on_install("windows", function (package) + os.cd("win32") + os.vrun("cscript configure.js iso8859x=yes iconv=no compiler=msvc cruntime=/MT debug=%s prefix=\"%s\"", package:debug() and "yes" or "no", Package:installdir()) + os.vrun("nmake /f Makefile.msvc") + os.vrun("nmake /f Makefile.msvc install") + end) + end + + on_install("macosx", "linux", function (package) + import("package.tools.autoconf").install(package, {"--disable-dependency-tracking", "--without-python", "--without-lzma"}) + end) +``` + +#### Builtin Variables + +Xmake provides the syntax of `$(varname)` to support the acquisition of built-in variables, for example: + +```lua +add_cxflags("-I$(buildir)") +``` + +It will convert the built-in `buildir` variable to the actual build output directory when compiling: `-I./build` + +General built-in variables can be used to quickly get and splicing variable strings when passing arguments, for example: + +```lua +target("test") + + -- Add source files in the project source directory + add_files("$(projectdir)/src/*.c") + + -- Add a header file search path under the build directory + add_includedirs("$(buildir)/inc") +``` + +It can also be used in the module interface of a custom script, for example: + +```lua +target("test") + on_run(function (target) + -- Copy the header file in the current script directory to the output directory + os.cp("$(scriptdir)/xxx.h", "$(buildir)/inc") + end) +``` + +All built-in variables can also be retrieved via the [val](#val) interface. + +This way of using built-in variables makes the description writing more concise and easy to read. Here are some of the variables built into xmake that can be obtained directly: + +| Interface | Description | Supported Versions | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [$(os)](#var-os) | Get the operating system of the current build platform | >= 2.0.1 | +| [$(host)](#var-host) | Get native operating system | >= 2.0.1 | +| [$(tmpdir)](#var-tmpdir) | Get Temporary Directory | >= 2.0.1 | +| [$(curdir)](#var-curdir) | Get current directory | >= 2.0.1 | +| [$(buildir)](#var-buildir) | Get the build output directory | >= 2.0.1 | +| [$(scriptdir)](#var-scriptdir) | Get Project Description Script Directory | >= 2.1.1 | +| [$(globaldir)](#var-globaldir) | Get Global Configuration Directory | >= 2.0.1 | +| [$(configdir)](#var-configdir) | Get Local Project Configuration Directory | >= 2.0.1 | +| [$(programdir)](#var-programdir) | xmake installation script directory | >= 2.1.5 | +| [$(projectdir)](#var-projectdir) | Get the project root directory | >= 2.0.1 | +| [$(shell)](#var-sheLl) | Execute external shell command | >= 2.0.1 | +| [$(env)](#var-env) | Get external environment variables | >= 2.1.5 | +| [$(reg)](#var-reg) | Get the value of the windows registry configuration item | >= 2.1.5 | + +Of course, this variable mode can also be extended. By default, the `xmake f --var=val` command can be used to directly obtain the parameters. For example: + +```lua +target("test") + add_defines("-DTEST=$(var)") +``` + ++All the parameter values of the `xmake f --xxx=...` configuration can be obtained through built-in variables, for example: `xmake f --arch=x86` corresponds to `$(arch)`, others have ` $(plat)`, `$(mode)` and so on. +What are the specific parameters, you can check it out by `xmake f -h`. +
+ +Since the support is directly obtained from the configuration options, it is of course convenient to extend the custom options to get the custom variables. For details on how to customize the options, see: [option](#option) + +##### var.$(os) + +###### Get the operating system of the current build platform + +If iphoneos is currently compiled, then this value is: `ios`, and so on. + +##### var.$(host) + +###### Get the native operating system + +Refers to the host system of the current native environment, if you compile on macOS, then the system is: `macosx` + +##### var.$(tmpdir) + +###### Getting a temporary directory + +Generally used to temporarily store some non-permanent files. + +##### var.$(curdir) + +###### Get the current directory + +The default is the project root directory when the `xmake` command is executed. Of course, if the directory is changed by [os.cd](#os-cd), this value will also change. + +##### var.$(buildir) + +###### Get the current build output directory + +The default is usually the `./build` directory in the current project root directory. You can also modify the default output directory by executing the `xmake f -o /tmp/build` command. + +##### var.$(scriptdir) + +###### Get the directory of the current project description script + +That is, the directory path corresponding to `xmake.lua`. + +##### var.$(globaldir) + +###### Global Configuration Directory + +Xmake's `xmake g|global` global configuration command, directory path for data storage, where you can place some of your own plugins and platform scripts. + +The default is: `~/.config` + +##### var.$(configdir) + +###### Current project configuration directory + +The current project configuration storage directory, which is the storage directory of the `xmake f|config` configuration command, defaults to: `projectdir/.config` + +##### var.$(programdir) + +###### xmake installation script directory + +That is, the directory where the `XMAKE_PROGRAM_DIR` environment variable is located. We can also modify the xmake load script by setting this environment amount to implement version switching. + +##### var.$(projectdir) + +###### Project root directory + +That is, the directory path specified in the `xmake -P xxx` command, the default is not specified is the current directory when the `xmake` command is executed, which is generally used to locate the project file. + +##### var.$(shell) + +###### Executing external shell commands + +In addition to the built-in variable handling, xmake also supports the native shell to handle some of the features that xmake does not support. + +For example, there is a need now, I want to use the `pkg-config` to get the actual third-party link library name when compiling the Linux program, you can do this: + +```lua +target("test") + set_kind("binary") + if is_plat("linux") then + add_ldflags("$(shell pkg-config --libs sqlite3)") + end +``` + +Of course, xmake has its own automated third library detection mechanism, which generally does not need such trouble, and lua's own scripting is very good. . + +But this example shows that xmake can be used with some third-party tools through the native shell. . + +##### var.$(env) + +###### Get external environment variables + +For example, you can get the path in the environment variable: + +```lua +target("test") + add_includedirs("$(env PROGRAMFILES)/OpenSSL/inc") +``` + +##### var.$(reg) + +###### Get the value of the windows registry configuration item + +Get the value of an item in the registry by `regpath; name`: + +```lua +print("$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\XXXX;Name)") +``` + +#### Builtin Modules + +Used in script code such as custom scripts, plug-in scripts, task scripts, platform extensions, template extensions, etc., that is, in code blocks like the following, you can use these module interfaces: + +```lua +on_run(function (target) + print("hello xmake!") +end) +``` + ++In order to ensure that the description field of the outer layer is as simple and secure as possible, it is generally not recommended to use the interface and module operation api in this domain. Therefore, most module interfaces can only be used in the script domain to implement complex functions. +Of course, a small number of read-only built-in interfaces can still be used in the description field, as shown in the following table: +
+ +| Interface | Description | Available Domains | Supported Versions | +| ----------------------------------------------- | -------------------------------------------- | -------------------------- | -------- | +| [val](#val) | Get the value of the built-in variable | Script Field | >= 2.1.5 | +| [import](#import) | Importing Extension Blocks | Script Fields | >= 2.0.1 | +| [inherit](#inherit) | Import and inherit base class modules | Script Domain | >= 2.0.1 | +| [ifelse](#ifelse) | Similar ternary conditional judgment | Description field, script field | >= 2.0.1 | +| [try-catch-finally](#try-catch-finally) | Exception Capture | Script Field | >= 2.0.1 | +| [pairs](#pairs) | Used to Traverse the Dictionary | Description Field, Script Field | >= 2.0.1 | +| [ipairs](#ipairs) | Used to traverse arrays | Description fields, script fields | >= 2.0.1 | +[print](#print) | Wrap Print Terminal Log | Description Field, Script Field | >= 2.0.1 | +| [printf](#printf) | No Line Printing Terminal Log | Script Field | >= 2.0.1 | +[cprint](#cprint) | Wrap Color Print Terminal Log | Script Field | >= 2.0.1 | +| [cprintf](#cprintf) | No Line Color Print Terminal Log | Script Field | >= 2.0.1 | +| [format](#format) | Format String | Description Field, Script Field | >= 2.0.1 | +| [vformat](#vformat) | Format string, support for built-in variable escaping | Script Domain | >= 2.0.1 | +| [raise](#raise) | Throwing an abort program | Script Field | >= 2.0.1 | +| [os](#os) | System Operation Module | Partial Read-Only Operation Description Field, Script Field | >= 2.0.1 | +| [io](#io) | File Manipulation Module | Script Field | >= 2.0.1 | +| [path](#path) | Path Manipulation Module | Description Field, Script Field |= 2.0.1 | +| [table](#table) | Array and Dictionary Operations Module | Description Field, Script Field | >= 2.0.1 | +| [string](#string) | String Manipulation Module | Description Field, Script Field | >= 2.0.1 | +| [process](#process) | Process Operation Module | Script Field | >= 2.0.1 | +| [coroutine](#coroutine) | Coroutine Operation Module | Script Field | >= 2.0.1 | +| [find_packages](#find_packages) | Find Dependency Packages | Script Fields | >= 2.2.5 | + +An example of using an interface call in a description field is as follows, generally only for conditional control: + +```lua +-- Scan all subdirectories under the current xmake.lua directory, defining a task task with the name of each directory +for _, taskname in ipairs(os.dirs("*"), path.basename) do + task(taskname) + on_run(function () + end) +end +``` + +The script field and description field mentioned above mainly refer to: + +```lua +-- Description field +target("test") + + -- Description field + set_kind("static") + add_files("src/*.c") + + on_run(function (target) + -- Script domain + end) + +-- Description field +``` + +##### val + +###### Get the value of the built-in variable + +[Built-in variables](#built-in variables) can be obtained directly through this interface, without the need to add a `$()` package, which is much simpler to use, for example: + +```lua +print(val("host")) +print(val("env PATH")) +local s = val("shell echo hello") +``` + +Using [vformat](#vformat) is cumbersome: + +```lua +local s = vformat("$(shell echo hello)") +``` + +However, `vformat` supports string parameter formatting, which is more powerful, so the application scenario is different. + +##### import + +###### Importing extension blocks + +Import is mainly used to import xmake's extension class library and some custom class library modules, generally used to: + +* Custom script ([on_build](#targeton_build), [on_run](#targeton_run) ..) +* Plugin development +* Template development +* Platform extension +* Custom task task + +The import mechanism is as follows: + +1. Import from the current script directory first +2. Import from the extended class library + +Imported grammar rules: + +Class library path rules based on `.`, for example: + +Import core core extension module + +```lua +import("core.base.option") +import("core.project") +import("core.base.task") -- 2.1.5 Previously core.project.task +import("core") + +function main() + + -- Get parameter options + print(option.get("version")) + + -- Run tasks and plugins + task.run("hello") + project.task.run("hello") + core.base.task.run("hello") +end +``` + +Import the custom module in the current directory: + +Directory Structure: + +``` +Plugin + - xmake.lua + - main.lua + - modules + - hello1.lua + - hello2.lua +``` + +Import modules in main.lua + +```lua +import("modules.hello1") +import("modules.hello2") +``` + +After importing, you can directly use all the public interfaces inside. The private interface is marked with the `_` prefix, indicating that it will not be exported and will not be called externally. . + +In addition to the current directory, we can also import libraries in other specified directories, for example: + +```lua +import("hello3", {rootdir = "/home/xxx/modules"}) +``` + +To prevent naming conflicts, you can also specify an alias after import: + +```lua +import("core.platform.platform", {alias = "p"}) + +function main() + + -- So we can use p to call the plats interface of the platform module to get a list of all the platforms supported by xmake. + table.dump(p.plats()) +end +``` + +Import can not only import the class library, but also import and import as inheritance, realize the inheritance relationship between modules. + +```lua +import("xxx.xxx", {inherit = true}) +``` + +This is not a reference to the module, but all the public interfaces of the module imported, so that it will be merged with the interface of the current module to achieve inheritance between modules. + +Version 2.1.5 adds two new properties: `import("xxx.xxx", {try = true, anonymous = true}). + +If the try is true, the imported module does not exist, only return nil, and will not interrupt xmake after throwing an exception. +If anonymous is true, the imported module will not introduce the current scope, only the imported object reference will be returned in the import interface. + +##### inherit + +###### Import and inherit base class modules + +This is equivalent to the `inherit` mode of the [import](#import) interface, which is: + +```lua +import("xxx.xxx", {inherit = true}) +``` + +With the `inherit` interface, it will be more concise: + +```lu +Inherit("xxx.xxx") +``` + +For an example, see the script in the xmake tools directory: [clang.lua](#https://github.com/xmake-io/xmake/blob/master/xmake/tools/clang.lua) + +This is part of the clang tool module that inherits gcc. + +##### ifelse + +###### Similar to the ternary condition judgment + +Since lua does not have a built-in ternary operator, a more concise conditional choice is achieved by encapsulating the `ifelse` interface: + +```lua +local ok = ifelse(a == 0, "ok", "no") +``` + +##### try-catch-finally + +###### Exception capture + +Lua native does not provide try-catch syntax to catch exception handling, but provides interfaces such as `pcall/xpcall` to execute lua functions in protected mode. + +Therefore, the capture mechanism of the try-catch block can be implemented by encapsulating these two interfaces. + +We can look at the packaged try-catch usage first: + +```lua +try +{ + -- try code block + function () + error("error message") + end, + + -- catch code block + catch + { + -- After an exception occurs, it is executed + function (errors) + print(errors) + end + } +} +``` + +In the above code, an exception is thrown inside the try block, and an error message is thrown, caught in the catch, and the error message is output. + +And finally processing, this role is for the `try{}` code block, regardless of whether the execution is successful, will be executed into the finally block + +In other words, in fact, the above implementation, the complete support syntax is: `try-catch-finally` mode, where catch and finally are optional, according to their actual needs. + +E.g: + +```lua +try +{ + -- try code block + function () + error("error message") + end, + + -- catch code block + catch + { + -- After an exception occurs, it is executed + function (errors) + print(errors) + end + }, + + -- finally block + finally + { + -- Finally will be executed here + function (ok, errors) + -- If there is an exception in try{}, ok is true, errors is the error message, otherwise it is false, and error is the return value in try + end + } +} + +``` + +Or only the finally block: + +```lua +try +{ + -- try code block + function () + return "info" + end, + + -- finally block + finally + { + -- Since there is no exception in this try code, ok is true and errors is the return value: "info" + function (ok, errors) + end + } +} +``` + +Processing can get the normal return value in try in finally, in fact, in the case of only try, you can also get the return value: + +```lua +-- If no exception occurs, result is the return value: "xxxx", otherwise nil +local result = try +{ + function () + return "xxxx" + end +} +``` + +In xmake's custom scripting and plugin development, it is also based entirely on this exception catching mechanism. + +This makes the development of the extended script very succinct and readable, eliminating the cumbersome `if err ~= nil then` return value judgment. When an error occurs, xmake will directly throw an exception to interrupt, and then highlight the detailed error. information. + +E.g: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + + -- After the ios program is compiled, the target program is ldid signed + after_build(function (target)) + os.run("ldid -S %s", target:targetfile()) + end +``` + +Only one line `os.run` is needed, and there is no need to return a value to determine whether it runs successfully. After the operation fails, xmake will automatically throw an exception, interrupt the program and prompt the error. + +If you want to run xmake without running interrupts directly after running, you can do it yourself.Add a try and you will be fine: + +```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 +``` + +If you want to capture the error message, you can add a 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 +``` + +However, in general, write custom scripts in xmake, do not need to manually add try-catch, directly call a variety of api, after the error, let xmake default handler to take over, directly interrupted. . + +##### pairs + +###### Used to traverse the dictionary + +This is lua's native built-in api. In xmake, it has been extended in its original behavior to simplify some of the daily lua traversal code. + +First look at the default native notation: + +```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 +``` + +This is sufficient for normal traversal operations, but if we get the uppercase for each of the elements it traverses, we can write: + +```lua +for key, val in pairs(t, function (v) return v:upper() end) do + print("%s: %s", key, val) +end +``` + +Even pass in some parameters to the second `function`, for example: + +```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 + +###### for traversing arrays + +This is lua's native built-in api. In xmake, it has been extended in its original behavior to simplify some of the daily lua traversal code. + +First look at the default native notation: + +```lua +for idx, val in ipairs({"a", "b", "c", "d", "e", "f"}) do + print("%d %s", idx, val) +end +``` + +The extension is written like the [pairs](#pairs) interface, for example: + +```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 +``` + +This simplifies the logic of the `for` block code. For example, if I want to traverse the specified directory and get the file name, but not including the path, I can simplify the writing by this extension: + +```lua +for _, filename in ipairs(os.dirs("*"), path.filename) do + -- ... +end +``` + +##### print + +###### Wrapping print terminal log + +This interface is also the native interface of lua. xmake is also extended based on the original behavior, and supports: formatted output, multivariable output. + +First look at the way native support: + +```lua +print("hello xmake!") +print("hello", "xmake!", 123) +``` + +And also supports extended formatting: + +```lua +print("hello %s!", "xmake") +print("hello xmake! %d", 123) +``` + +Xmake will support both types of writing at the same time, and the internal will automatically detect and select the output behavior. + +##### printf + +###### No line printing terminal log + +Like the [print](#print) interface, the only difference is that it doesn't wrap. + +##### cprint + +###### Wrap color print terminal log + +The behavior is similar to [print](#print), the difference is that this interface also supports color terminal output, and supports `emoji` character output. + +E.g: + +```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') +``` + +The results are as follows: + + + +The color-related descriptions are placed in `${ }`, and you can set several different properties at the same time, for example: + +``` + ${bright red underline onyellow} +``` + +Indicates: highlighted red, background yellow, and with a down line + +All of these descriptions will affect the entire entire line of characters. If you only want to display partial color text, you can insert `${clear}` at the end position to clear the previous color description. + +E.g: + +``` + ${red}hello ${clear}xmake +``` + +In this case, only hello is displayed in red, and the others are still normal black display. + +Other colors belong to, I will not introduce them here, directly paste the list of attributes in the xmake code: + +```lua + colors.keys = + { + -- Attributes + reset = 0 -- reset attribute + , clear = 0 -- clear attribute + , default = 0 -- default property + , bright = 1 -- highlight + , dim = 2 -- dark + , underline = 4 -- underline + , blink = 5 -- flashing + , reverse = 7 -- reverse color + , hidden = 8 -- hidden text + + -- Foreground + , black = 30 + , red = 31 + , green = 32 + , yellow = 33 + , blue = 34 + , magenta = 35 + , cyan = 36 + , white = 37 + + -- Background color + , onblack = 40 + , onred = 41 + , ongreen = 42 + , onyellow = 43 + , onblue = 44 + , onmagenta = 45 + , oncyan = 46 + , onwhite = 47 +``` + +In addition to color highlighting, if your terminal is under macosx, lion above the system, xmake can also support the display of emoji expressions, for systems that do not support +Ignore the display, for example: + +```lua + cprint("hello xmake${beer}") + cprint("hello${ok_hand} xmake") +``` + +The above two lines of code, I printed a classic beer symbol in the homebrew, the following line printed an ok gesture symbol, is not very dazzling. . + + + +All emoji emoticons, as well as the corresponding keys in xmake, can be found in [emoji](http://www.emoji-cheat-sheet.com/). . + +Version 2.1.7 supports 24-bit true color output, if the terminal supports it: + +```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's detection support for truecolor is implemented by the `$COLORTERM` environment variable. If your terminal supports truecolor, you can manually set this environment variable to tell xmake to enable truecolor support. + +It can be enabled and tested with the following command: + +```bash +$ export COLORTERM=truecolor +$ xmake --version +``` + +The 2.1.7 version can disable color output with `COLORTERM=nocolor`. + +##### cprintf + +###### No line feed color print terminal log + +This interface is similar to [cprint](#cprint), the difference is that it does not wrap the output. + +##### format + +###### Formatting a string + +If you just want to format the string and don't output it, you can use this interface. This interface is equivalent to the [string.format](#string-format) interface, just a simplified version of the interface name. + +```lua +local s = format("hello %s", xmake) +``` + +##### vformat + +###### Formatting strings, support for built-in variable escaping + +This interface is followed by [format](The #format) interface is similar, but adds support for the acquisition and escaping of built-in variables. + +```lua +local s = vformat("hello %s $(mode) $(arch) $(env PATH)", xmake) +``` + +##### raise + +###### Throwing an abort program + +If you want to interrupt xmake running in custom scripts and plug-in tasks, you can use this interface to throw an exception. If the upper layer does not show the call to [try-catch](#try-catch-finally), xmake will be executed. An error message is displayed. + +```lua +if (errors) raise(errors) +``` + +If an exception is thrown in the try block, the error information is captured in catch and finally. See: [try-catch](#try-catch-finally) + +##### find_packages + +###### Finding dependencies + +This interface is a wrapper around the [lib.detect.find_package](#detect-find_package) interface and provides lookup support for multiple dependencies, for example: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + on_load(function (target) + target:add(find_packages("openssl", "zlib")) + end) +``` + +##### os + +The system operation module belongs to the built-in module. It can be called directly by the script field without using [import](#import) import. + +This module is also a native module of lua, and xmake has been extended to provide more practical interfaces. + ++Only some readonly interfaces (for example: `os.getenv`, `os.arch`) in the os module can be used in the description field. Other interfaces can only be used in the script domain, for example: `os.cp`, `os .rm`etc. +
+ +| Interface | Description | Supported Versions | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [os.cp](#os-cp) | Copy files or directories | >= 2.0.1 | +| [os.mv](#os-mv) | Move Renamed File or Directory | >= 2.0.1 | +| [os.rm](#os-rm) | Delete files or directory tree | >= 2.0.1 | +| [os.trycp](#os-trycp) | Try copying files or directories | >= 2.1.6 | +| [os.trymv](#os-trymv) | Try moving the renamed file or directory | >= 2.1.6 | +| [os.tryrm](#os-tryrm) | Try deleting a file or directory tree | >= 2.1.6 | +| [os.cd](#os-cd) | Go to the specified directory | >= 2.0.1 | +| [os.rmdir](#os-rmdir) | Delete Directory Tree | >= 2.0.1 | +| [os.mkdir](#os-mkdir) | Create the specified directory | >= 2.0.1 | +| [os.isdir](#os-isdir) | Determine if the directory exists | >= 2.0.1 | +| [os.isfile](#os-isfile) | Determine if the file exists | >= 2.0.1 | +| [os.exists](#os-exists) | Determine if a file or directory exists | >= 2.0.1 | +| [os.dirs](#os-dirs) | Traversing to get all directories under the specified directory | >= 2.0.1 | +| [os.files](#os-files) | Traversing to get all the files in the specified directory | >= 2.0.1 | +| [os.filedirs](#os-filedirs) | Traversing to get all files or directories under the specified directory | >= 2.0.1 | +| [os.run](#os-run) | Quiet running program | >= 2.0.1 | +| [os.runv](#os-runv) | Quiet running program with parameter list | >= 2.1.5 | +| [os.exec](#os-exec) | Evoke Run Program | >= 2.0.1 | +| [os.execv](#os-execv) | Echo running program with parameter list | >= 2.1.5 | +| [os.iorun](#os-iorun) | Run and get the program output | >= 2.0.1 | +| [os.iorunv](#os-iorunv) | Run and get the program output with parameter list | >= 2.1.5 | +| [os.getenv](#os-getenv) | Get Environment Variables | >= 2.0.1 | +| [os.setenv](#os-setenv) | Setting environment variables | >= 2.0.1 | +| [os.tmpdir](#os-tmpdir) | Get Temp directory path | >= 2.0.1 | +| [os.tmpfile](#os-tmpfile) | Get Temporary File Path | >= 2.0.1 | +| [os.curdir](#os-curdir) | Get current directory path | >= 2.0.1 | +| [os.filesize](#os-filesize) | Get File Size | >= 2.1.9 | +| [os.scriptdir](#os-scriptdir) | Get script directory path | >= 2.0.1 | +| [os.programdir](#os-programdir) | Get xmake install main program script directory | >= 2.1.5 | +| [os.projectdir](#os-projectdir) | Get Project Home | |= 2.1.5 | +| [os.arch](#os-arch) | Get Current System Architecture | >= 2.0.1 | +| [os.host](#os-host) | Get Current Host System | >= 2.0.1 | + +###### os.cp + +- Copy files or directories + +The behavior is similar to the `cp` command in the shell, supporting path wildcard matching (using lua pattern matching), support for multi-file copying, and built-in variable support. + +E.g: + +```lua +os.cp("$(scriptdir)/*.h", "$(projectdir)/src/test/**.h", "$(buildir)/inc") +``` + +The above code will: all the header files in the current `xmake.lua` directory, the header files in the project source test directory are all copied to the `$(buildir)` output directory. + +Among them `$(scriptdir)`, `$(projectdir)` These variables are built-in variables of xmake. For details, see the related documentation of [built-in variables](#built-in variables). + +The matching patterns in `*.h` and `**.h` are similar to those in [add_files](#targetadd_files), the former is a single-level directory matching, and the latter is a recursive multi-level directory matching. + +This interface also supports `recursive replication' of directories, for example: + +```lua +-- Recursively copy the current directory to a temporary directory +os.cp("$(curdir)/test/", "$(tmpdir)/test") +``` + ++Try to use the `os.cp` interface instead of `os.run("cp ..")`, which will ensure platform consistency and cross-platform build description. +
+ +###### os.mv + +- Move to rename a file or directory + +Similar to the use of [os.cp](#os-cp), it also supports multi-file move operations and pattern matching, for example: + +```lua +-- Move multiple files to a temporary directory +os.mv("$(buildir)/test1","$(buildir)/test2", "$(tmpdir)") + +-- File movement does not support bulk operations, which is file renaming +os.mv("$(buildir)/libtest.a", "$(buildir)/libdemo.a") +``` + +###### os.rm + +- Delete files or directory trees + +Support for recursive deletion of directories, bulk delete operations, and pattern matching and built-in variables, such as: + +```lua +os.rm("$(buildir)/inc/**.h", "$(buildir)/lib/") +``` + +###### os.trycp + +- Try copying files or directories + +Similar to [os.cp](#os-cp), the only difference is that this interface operation will not throw an exception interrupt xmake, but the return value indicates whether the execution is successful. + +```lua +if os.trycp("file", "dest/file") then +end +``` + +###### os.trymv + +- Try moving a file or directory + +Similar to [os.mv](#os-mv), the only difference is that this interface operation will not throw an exception interrupt xmake, but the return value indicates whether the execution is successful. + +```lua +if os.trymv("file", "dest/file") then +end +``` + +###### os.tryrm + +- Try deleting files or directories + +Similar to [os.rm](#os-rm), the only difference is that this interface operation will not throw an exception interrupt xmake, but the return value indicates whether the execution is successful. + +```lua +if os.tryrm("file") then +end +``` + +###### os.cd + +- Enter the specified directory + +This operation is used for directory switching and also supports built-in variables, but does not support pattern matching and multi-directory processing, for example: + +```lua +-- Enter the temporary directory +os.cd("$(tmpdir)") +``` + +If you want to leave the previous directory, there are several ways: + +```lua +-- Enter the parent directory +os.cd("..") + +-- Enter the previous directory, equivalent to: cd - +os.cd("-") + +-- Save the previous directory before entering the directory, then use it to cut back directly after the level +local oldir = os.cd("./src") +... +os.cd(oldir) +``` + +###### os.rmdir + +- delete only the directory + +If it is not a directory, it cannot be deleted. + +###### os.mkdir + +- Create a directory + +Support for batch creation and built-in variables, such as: + +```lua +os.mkdir("$(tmpdir)/test", "$(buildir)/inc") +``` + +###### os.isdir + +- Determine if it is a directory + +Return false if the directory does not exist + +```lua +if os.isdir("src") then + -- ... +end +``` + +###### os.isfile + +- Determine if it is a file + +Return false if the file does not exist + +```lua +if os.isfile("$(buildir)/libxxx.a") then + -- ... +end +``` + +###### os.exists + +- Determine if a file or directory exists + +Return false if the file or directory does not exist + +```lua +-- Judging the existence of the directory +if os.exists("$(buildir)") then + -- ... +end + +-- Judging the existence of the file +if os.exists("$(buildir)/libxxx.a") then + -- ... +end +``` + +###### os.dirs + +- Traverse to get all the directories under the specified directory + +Supports pattern matching in [add_files](#targetadd_files), supports recursive and non-recursive mode traversal, and returns a table array. If not, returns an empty array, for example: + +```lua +-- Recursive traversal to get all subdirectories +for _, dir in ipairs(os.dirs("$(buildir)/inc/**")) do + print(dir) +end +``` + +###### os.files + +- Traverse to get all the files in the specified directory + +Supports pattern matching in [add_files](#targetadd_files), supports recursive and non-recursive mode traversal, and returns a table array. If not, returns an empty array, for example: + +```lua +-- Non-recursive traversal to get all child files +for _, filepath in ipairs(os.files("$(buildir)/inc/*.h")) do + print(filepath) +end +``` + +###### os.filedirs + +- Traverse to get all files and directories under the specified directory + +Supports pattern matching in [add_files](#targetadd_files), supports recursive and non-recursive mode traversal, and returns a table array. If not, returns an empty array, for example: + +```lua +-- Recursive traversal to get all child files and directories +for _, filedir in ipairs(os.filedirs("$(buildir)/**")) do + print(filedir) +end +``` + +###### os.run + +- Quietly running native shell commands + +Used to execute third-party shell commands, but will not echo the output, only after the error, highlight the error message. + +This interface supports parameter formatting and built-in variables such as: + +```lua +-- Formatted parameters passed in +os.run("echo hello %s!", "xmake") + +-- List build directory files +os.run("ls -l $(buildir)") +``` + +
+Using this interface to execute shell commands can easily reduce the cross-platform build. For `os.run("cp ..")`, try to use `os.cp` instead.
+If you must use this interface to run the shell program, please use the [config.plat](#config-plat) interface to determine the platform support.
+
+Prior to version 2.1.5, it was `core.project.global`. +
+ +###### global.get + +- Get the specified configuration value + +Similar to [config.get](#config-get), the only difference is that this is obtained from the global configuration. + +###### global.load + +- Load configuration + +Similar to [global.get](#global-get), the only difference is that this is loaded from the global configuration. + +###### global.directory + +- Get the global configuration information directory + +The default is the `~/.config` directory. + +###### global.dump + +- Print out all global configuration information + +The output is as follows: + +```lua +{ + clean = true +, ccache = "ccache" +, xcode_dir = "/Applications/Xcode.app" +} +``` + +##### core.base.task + +Used for task operations, generally used to call other task tasks in custom scripts and plug-in tasks. + +| Interface | Description | Supported Versions | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [task.run](#task-run) | Run the specified task | >= 2.0.1 | + ++Prior to version 2.1.5, it was `core.project.task`. +
+ +###### task.run + +- Run the specified task + +Used to run tasks or plugins defined by [task](#task) in custom scripts, plugin tasks, for example: + +```lua +task("hello") + on_run(function () + print("hello xmake!") + end) + +target("demo") + on_clean(function(target) + + -- Import task module + import("core.base.task") + + -- Run this hello task + task.run("hello") + end) +``` + +We can also increase parameter passing when running a task, for example: + +```lua +task("hello") + on_run(function (arg1, arg2) + print("hello xmake: %s %s!", arg1, arg2) + end) + +target("demo") + on_clean(function(target) + + -- Import task + import("core.base.task") + + -- {} This is used for the first option, which is set to null, where two arguments are passed in the last: arg1, arg2 + task.run("hello", {}, "arg1", "arg2") + end) +``` + +The second argument to `task.run` is used to pass options from the command line menu instead of passing directly into the `function (arg, ...)` function entry, for example: + +```lua +-- Import task +import("core.base.task") + +-- Plugin entry +function main(...) + + -- Run the built-in xmake configuration task, equivalent to: xmake f|config --plat=iphoneos --arch=armv7 + task.run("config", {plat="iphoneos", arch="armv7"}) +emd +``` + +##### core.tool.linker + +Linker related operations, often used for plugin development. + +| Interface | Description | Supported Versions | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [linker.link](#linker-link) | Execute Link | >= 2.0.1 | +| [linker.linkcmd](#linker-linkcmd) | Get Link Command Line | >= 2.0.1 | +| [linker.linkargv](#linker-linkargv) | Get Link Command Line List | >= 2.1.5 | +| [linker.linkflags](#linker-linkflags) | Get LinksOptions | >= 2.0.1 | +| [linker.has_flags](#linker-has_flags) | Determine if the specified link option is supported | >= 2.1.5 | + +###### linker.link + +- Execute link + +For the target, link the specified object file list to generate the corresponding target file, for example: + +```lua +linker.link("binary", "cc", {"a.o", "b.o", "c.o"}, target:targetfile(), {target = target}) +``` + +Where [target](#target) is the project target, here is passed in, mainly used to get the target-specific link options. For the project target object, see: [core.project.project](#core-project-project ) + +Of course, you can also not specify the target, for example: + +```lua +linker.link("binary", "cc", {"a.o", "b.o", "c.o"}, "/tmp/targetfile") +``` + +The first parameter specifies the link type and currently supports: binary, static, shared +The second parameter tells the linker that it should be linked as the source file object, and what compiler source files are compiled with, for example: + +| Second Parameter Value | Description | +| ------------ | ------------ | +| cc | c compiler | +| cxx | c++ compiler | +| mm | objc compiler | +| mxx | objc++ compiler | +| gc | go compiler | +| as | assembler | +| sc | swift compiler | +| rc | rust compiler | +| dc | dlang compiler | + +Specifying different compiler types, the linker will adapt the most appropriate linker to handle the link, and if several languages support mixed compilation, you can pass in multiple compiler types at the same time, specifying that the linker chooses to support these hybrid compilations. The linker of the language performs link processing: + +```lua +linker.link("binary", {"cc", "mxx", "sc"}, {"a.o", "b.o", "c.o"}, "/tmp/targetfile") +``` + +The above code tells the linker that the three object files a, b, c may be c, objc++, compiled by swift code. The linker will select the most suitable linker from the current system and toolchain to handle the link process. . + +###### linker.linkcmd + +- Get link command line string + +Get the command line string executed in [linker.link](#linker-link) directly, which is equivalent to: + +```lua +local cmdstr = linker.linkcmd("static", "cxx", {"a.o", "b.o", "c.o"}, target:targetfile(), {target = target}) +``` + +Note: The extension part of ``target = target}` is optional. If the target object is passed, the generated link command will add the link option corresponding to this target configuration. + +And you can also pass various configurations yourself, for example: + +```lua +local cmdstr = linker.linkcmd("static", "cxx", {"a.o", "b.o", "c.o"}, target:targetfile(), {config = {linkdirs = "/usr/lib"}}) +``` + +###### linker.linkargv + +- Get a list of link command line arguments + +A little different from [linker.linkcmd](#linker-linkcmd) is that this interface returns a list of parameters, table representation, more convenient to operate: + +```lua +local program, argv = linker.linkargv("static", "cxx", {"a.o", "b.o", "c.o"}, target:targetfile(), {target = target}) +``` + +The first value returned is the main program name, followed by the parameter list, and `os.args(table.join(program, argv))` is equivalent to `linker.linkcmd`. + +We can also run it directly by passing the return value to [os.runv](#os-runv): `os.runv(linker.linkargv(..))` + +###### linker.linkflags + +- Get link options + +Get the link option string part of [linker.linkcmd](#linker-linkcmd) without shellname and object file list, and return by array, for example: + +```lua +local flags = linker.linkflags("shared", "cc", {target = target}) +for _, flag in ipairs(flags) do + print(flag) +end +``` + +The returned array of flags is an array. + +###### linker.has_flags + +- Determine if the specified link option is supported + +Although it can be judged by [lib.detect.has_flags](detect-has_flags), but the interface is more low-level, you need to specify the linker name. +This interface only needs to specify the target type of the target, the source file type, which will automatically switch to select the currently supported linker. + +```lua +if linker.has_flags(target:targetkind(), target:sourcekinds(), "-L/usr/lib -lpthread") then + -- ok +end +``` + +##### core.tool.compiler + +Compiler related operations, often used for plugin development. + +| Interface | Description | Supported Versions | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [compiler.compile](#compiler-compile) | Execute Compilation | >= 2.0.1 | +| [compiler.compcmd](#compiler-compcmd) | Get Compiler Command Line | >= 2.0.1 | +| [compiler.compargv](#compiler-compargv) | Get Compiled Command Line List | >= 2.1.5 | +| [compiler.compflags](#compiler-compflags) | Get Compilation Options | >= 2.0.1 | +| [compiler.has_flags](#compiler-has_flags) | Determine if the specified compilation option is supported | >= 2.1.5 | +| [compiler.features](#compiler-features) | Get all compiler features | >= 2.1.5 | +| [compiler.has_features](#compiler-has_features) | Determine if the specified compilation feature is supported | >= 2.1.5 | + +###### compiler.compile + +- Perform compilation + +For the target, link the specified object file list to generate the corresponding target file, for example: + +```lua +compiler.compile("xxx.c", "xxx.o", "xxx.h.d", {target = target}) +``` + +Where [target](#target) is the project target, here is the specific compile option that is mainly used to get the taeget. If you get the project target object, see: [core.project.project](#core-project-project) + +The `xxx.h.d` file is used to store the header file dependency file list for this source file. Finally, these two parameters are optional. You can not pass them when compiling: + +```lua +compiler.compile("xxx.c", "xxx.o") +``` + +To simply compile a source file. + +###### compiler.compcmd + +- Get the compile command line + +Get the command line string executed directly in [compiler.compile](#compiler-compile), which is equivalent to: + +```lua +local cmdstr = compiler.compcmd("xxx.c", "xxx.o", {target = target}) +``` + +Note: The extension part of ``target = target}` is optional. If the target object is passed, the generated compile command will add the link option corresponding to this target configuration. + +And you can also pass various configurations yourself, for example: + +```lua +local cmdstr = compiler.compcmd("xxx.c", "xxx.o", {config = {includedirs = "/usr/include", defines = "DEBUG"}}) +``` + +With target, we can export all source file compilation commands for the specified 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 + +- Get compiled command line list + +A little different from [compiler.compargv](#compiler-compargv) is that this interface returns a list of parameters, table representation, more convenient to operate: + +```lua +local program, argv = compiler.compargv("xxx.c", "xxx.o") +``` + +###### compiler.compflags + +- Get compilation options + +Get the compile option string part of [compiler.compcmd](#compiler-compcmd) without shList of ellnames and files, for example: + +```lua +local flags = compiler.compflags(sourcefile, {target = target}) +for _, flag in ipairs(flags) do + print(flag) +end +``` + +The returned array of flags is an array. + +###### compiler.has_flags + +- Determine if the specified compilation option is supported + +Although it can be judged by [lib.detect.has_flags](detect-has_flags), but the interface is more low-level, you need to specify the compiler name. +This interface only needs to specify the language type, it will automatically switch to select the currently supported compiler. + +```lua +-- Determine if the c language compiler supports the option: -g +if compiler.has_flags("c", "-g") then + -- ok +end + +-- Determine if the C++ language compiler supports the option: -g +if compiler.has_flags("cxx", "-g") then + -- ok +end +``` + +###### compiler.features + +- Get all compiler features + +Although it can be obtained by [lib.detect.features](detect-features), but the interface is more low-level, you need to specify the compiler name. +This interface only needs to specify the language type, it will automatically switch to select the currently supported compiler, and then get the current list of compiler features. + +```lua +-- Get all the features of the current c compiler +local features = compiler.features("c") + +-- Get all the features of the current C++ language compiler, enable the C++11 standard, otherwise you will not get the new standard features. +local features = compiler.features("cxx", {cofnig = {cxxflags = "-std=c++11"}}) + +-- Get all the features of the current C++ language compiler, pass all configuration information of the project target +local features = compiler.features("cxx", {target = target, config = {defines = "..", includedirs = ".."}}) +``` + +A list of all c compiler features: + +| Feature Name | +| --------------------- | +| c_static_assert | +| c_restrict | +| c_variadic_macros | +| c_function_prototypes | + +A list of all C++ compiler features: + +| Feature Name | +| ------------------------------------ | +| 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 + +- Determine if the specified compiler feature is supported + +Although it can be obtained by [lib.detect.has_features](detect-has-features), but the interface is more low-level, you need to specify the compiler name. +And this interface only needs to specify the special name list that needs to be detected, it can automatically switch to select the currently supported compiler, and then determine whether the specified feature is supported in the current compiler. + +```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 +``` + +For specific feature names, refer to [compiler.features](#compiler-features). + +##### core.project.config + +Used to get the configuration information when the project is compiled, that is, the value of the parameter option passed in `xmake f|config --xxx=val`. + +| Interface | Description | Supported Versions | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [config.get](#config-get) | Get the specified configuration value | >= 2.0.1 | +| [config.load](#config-load) | Load Configuration | >= 2.0.1 | +| [config.arch](#config-arch) | Get the schema configuration of the current project | >= 2.0.1 | +| [config.plat](#config-plat) | Get the platform configuration of the current project | >= 2.0.1 | +| [config.mode](#config-mode) | Get the compilation mode configuration of the current project | >= 2.0.1 | +| [config.buildir](#config-buildir) | Get the output directory configuration of the current project | >= 2.0.1 | +| [config.directory](#config-dIrectory) | Get the configuration information directory of the current project | >= 2.0.1 | +| [config.dump](#config-dump) | Print out all configuration information for the current project | >= 2.0.1 | + +###### config.get + +- Get the specified configuration value + +Used to get the configuration value of `xmake f|config --xxx=val`, for example: + +```lua +target("test") + on_run(function (target) + + -- Import configuration module + import("core.project.config") + + -- Get configuration values + print(config.get("xxx")) + end) +``` + +###### config.load + +- Load configuration + +Generally used in plug-in development, the plug-in task is not like the custom script of the project, the environment needs to be initialized and loaded by itself, the default project configuration is not loaded, if you want to use [config.get](#config-get) interface to get the project Configuration, then you need to: + +```lua + +-- Import configuration module +import("core.project.config") + +function main(...) + + -- Load project configuration first + config.load() + + -- Get configuration values + print(config.get("xxx")) +end +``` + +###### config.arch + +- Get the schema configuration of the current project + +That is to get the platform configuration of `xmake f|config --arch=armv7`, which is equivalent to `config.get("arch")`. + +###### config.plat + +- Get the platform configuration of the current project + +That is to get the platform configuration of `xmake f|config --plat=iphoneos`, which is equivalent to `config.get("plat")`. + +###### config.mode + +- Get the compilation mode configuration of the current project + +That is to get the platform configuration of `xmake f|config --mode=debug`, which is equivalent to `config.get("mode")`. + +###### config.buildir + +- Get the output directory configuration of the current project + +That is to get the platform configuration of `xmake f|config -o /tmp/output`, which is equivalent to `config.get("buildir")`. + +###### config.directory + +- Get the configuration information directory of the current project + +Get the storage directory of the project configuration, the default is: `projectdir/.config` + +###### config.dump + +- Print out all configuration information of the current project + +The output is for example: + +```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 + ++This module was migrated to [core.base.global](#core-base-global) since version 2.1.5. +
+ +##### core.project.task + ++This module has been migrated to [core.base.task](#core-base-task) since version 2.1.5. +
+ +##### core.project.project + +Used to get some description information of the current project, that is, the configuration information defined in the `xmake.lua` project description file, for example: [target](#target), [option](#option), etc. + +| Interface | Description | Supported Versions | +| ----------------------------------------------- | -------------------------------------------- | -------------------- | +| [project.load](#project-load) | Load Project Configuration | >= 2.0.1 (2.1.5 Obsolete) | +| [project.directory](#project-directory) | Get Project Directory | >= 2.0.1 | +| [project.target](#project-target) | Get the specified project target object | >= 2.0.1 | +| [project.targets](#project-targets) | Get the list of project target objects | >= 2.0.1 | +| [project.option](#project-option) | Get the specified option object | >= 2.1.5 | +| [project.options](#project-options) | Get all option objects for the project | >= 2.1.5 | +| [project.name](#project-name) | Get current project name | >= 2.0.1 | +| [project.version](#project-version) | Get current project version number | >= 2.0.1 | + +###### project.load + +- Load project description configuration + +It is only used in the plugin, because the project configuration information has not been loaded at this time. In the custom script of the project target, you do not need to perform this operation, you can directly access the project configuration. + +```lua +-- Import engineering modules +import("core.project.project") + +-- Plugin entry +function main(...) + + -- Load project description configuration + project.load() + + -- access project descriptions, such as getting specified project goals + local target = project.target("test") +end +``` + ++After version 2.1.5, if not needed, the project load will automatically load at the appropriate time. +
+ +###### project.directory + +- Get the project directory + +Get the current project directory, which is the directory specified in `xmake -P xxx`, otherwise it is the default current `xmake` command execution directory. + ++After version 2.1.5, it is recommended to use [os.projectdir](#os-projectdir) to get it. +
+ +###### project.target + +- Get the specified project target object + +Get and access the specified project target configuration, for example: + +```lua +local target = project.target("test") +if target then + + -- Get the target file name + print(target:targetfile()) + + -- Get the target type, which is: binary, static, shared + print(target:targetkind()) + + -- Get the target name + print(target:name()) + + -- Get the target source file + local sourcefiles = target:sourcefiles() + + -- Get a list of target installation header files + local srcheaders, dstheaders = target:headerfiles() + + -- Get target dependencies + print(target:get("deps")) +end +``` + +###### project.targets + +- Get a list of project target objects + +Returns all compilation targets for the current project, for example: + +```lua +for targetname, target in pairs(project.targets()) + print(target:targetfile()) +end +``` + +###### project.option + +- Get the specified option object + +Get and access the option objects specified in the project, for example: + +```lua +local option = project.option("test") +if option:enabled() then + option:enable(false) +end +``` + +###### project.options + +- Get all project option objects + +Returns all compilation targets for the current project, for example: + +```lua +for optionname, option in pairs(project.options()) + print(option:enabled()) +end +``` + +###### project.name + +- Get the current project name + +That is, get the project name configuration of [set_project](#set_project). + +```lua +print(project.name()) +``` + +###### project.version + +- Get the current project version number + +That is, get [set_version](#set_version) project version configuration. + +```lua +print(project.version()) +``` + +##### core.language.language + +Used to obtain information about the compiled language, generally used for the operation of code files. + +| Interface | Description | Supported Versions | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [language.extensions](#language-extensions) | Get a list of code suffixes for all languages | >= 2.1.1 | +| [language.targetkinds](#language-targetkinds) | Get a list of target types for all languages | >= 2.1.1 | +| [language.sourcekinds](#language-sourcekinds) | Get a list of source file types for all languages | >= 2.1.1 | +| [language.sourceflags](#language-sourceflags) | Load source file compilation option name list for all languages | >= 2.1.1 | +| [language.load](#language-load) | Load the specified language | >= 2.1.1 | +| [language.load_sk](#language-load_sk) | Load the specified language from the source file type | >= 2.1.1 | +| [language.load_ex](#language-load_ex) | Load the specified language from the source file suffix | >= 2.1.1 | +| [language.sourcekind_of](#language-sourcekind_of) | Get the source file type of the specified source file | >= 2.1.1 | + +###### language.extensions + +- Get a list of code suffixes for all languages + +The results are as follows: + +```lua +{ + [".c"] = cc +, [".cc"] = cxx +, [".cpp"] = cxx +, [".m"] = mm +, [".mm"] = mxx +, [".swift"] = sc +, [".go"] = gc +} +``` + +###### language.targetkinds + +- Get a list of target types in all languages + +The results are as follows: + +```lua +{ + binary = {"ld", "gc-ld", "dc-ld"} +, static = {"ar", "gc-ar", "dc-ar"} +, shared = {"sh", "dc-sh"} +} +``` + +###### language.sourcekinds + +- Get a list of source file types in all languages + +The results are as follows: + +```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 + +- Load a list of source file compilation option names for all languages + +The results are as follows: + +```lua +{ + cc = {"cflags", "cxflags"} +, cxx = {"cxxflags", "cxflags"} +, ... +} +``` + +###### language.load + +- Load the specified language + +Load a specific language object from the language name, for example: + +```lua +local lang = language.load("c++") +if lang then + print(lang:name()) +end +``` + +###### language.load_sk + +- Load the specified language from the source file type + +Load specific language objects from the source file type: `cc, cxx, mm, mxx, sc, gc, as ..`, for example: + +```lua +local lang = language.load_sk("cxx") +if lang then + print(lang:name()) +end +``` + +###### language.load_ex + +- Load the specified language from the source file suffix name + +Load specific language objects from the source file extension: `.cc, .c, .cpp, .mm, .swift, .go ..`, for example: + +```lua +local lang = language.load_sk(".cpp") +if lang then + print(lang:name()) +end +``` + +###### language.sourcekind_of + +- Get the source file type of the specified source file + +That is, from a given source file path, get the type of source file it belongs to, for example: + +```lua +print(language.sourcekind_of("/xxxx/test.cpp")) +``` + +The result is: `cxx`, which is the `c++` type. For the corresponding list, see: [language.sourcekinds](#language-sourcekinds) + +##### core.platform.platform + +Platform information related operations + +| Interface | Description | Supported Versions | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [platform.get](#platform-get) | Get configuration information about the specified platform | >= 2.0.1 | + +###### platform.get + +- Get configuration information about the specified platform + +Get the information set in the platform configuration `xmake.lua`, which is generally only used when writing plugins, for example: + +```lua +-- Get all support architectures for the current platform +print(platform.get("archs")) + +-- Get the target file format information of the specified iphoneos platform +local formats = platform.get("formats", "iphoneos") +table.dump(formats) +``` + +For specific readable platform configuration information, please refer to: [platform](#platform) + +##### core.platform.environment + +Environment-related operations, used to enter and leave the terminal environment corresponding to the specified environment variables, generally used for the entry and departure of the `path` environment, especially some build tools that require a specific environment, such as: msvc toolchain. + +| Interface | Description | Supported Versions | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [environment.enter](#environment-enter) | Enter the specified environment | >= 2.0.1 | +| [environment.leave](#environment-leave) | Leave the specified environment | >= 2.0.1 | + +The currently supported environments are: + +| Interface | Description | Supported Versions | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| toolchains | Toolchain Execution Environment | >= 2.0.1 | + +###### environment.enter + +- Enter the specified environment + +Enter the specified environment, for example, msvc has its own environment variable environment for running build tools, such as: `cl.exe`, `link.exe`, these time you want to run them in xmake, you need: + +```lua +-- Enter the toolchain environment +environment.enter("toolchains") + +-- At this time, run cl.exe to run normally. At this time, environment variables such as path will enter the environment mode of msvc. +os.run("cl.exe ..") + +-- leaving the toolchain environment +environment.leave("toolchains") +``` + +Therefore, for the sake of versatility, the default xmake compiler will set this environment. Under Linux, basically the internal environment does not need special switching. At present, only msvc under Windows is processed. + +###### environment.leave + +- leaving the designated environment + +For specific use, see: [environment.enter](#environment-enter) + +##### lib.detect + +This module provides very powerful probing capabilities for probing programs, compilers, language features, dependencies, and more. + ++The interface of this module is spread across multiple module directories, try to import it by importing a single interface, which is more efficient, for example: `import("lib.detect.find_package")` instead of `import("lib.detect ") `Import all to call. +
+ +| Interface | Description | Supported Versions | +| --------------------------------------------------- | -------------------------------------------- | -------------------- | +| [detect.find_file](#detect-find_file) | Find Files | >= 2.1.5 | +| [detect.find_path](#detect-find_path) | Find File Path | >= 2.1.5 | +| [detect.find_library](#detect-find_library) | Find Library Files | >= 2.1.5 | +| [detect.find_program](#detect-find_program) | Find executables | >= 2.1.5 | +| [detect.find_programver](#detect-find_programver) | Find executable version number | >= 2.1.5 | +| [detect.find_package](#detect-find_package) | Find package files, including library files and search paths | >= 2.1.5 | +| [detect.find_tool](#detect-find_tool) | Find Tool | >= 2.1.5 | +| [detect.find_toolname](#detect-find_toolname) | Find Tool Name | >= 2.1.5 | +| [detect.find_cudadevices](#detect-find_cudadevices) | Find CUDA devices of the host | >= 2.2.7 | +| [detect.features](#detect-features) | Get all the features of the specified tool | >= 2.1.5 | +| [detect.has_features](#detect-has_features) | Determine if the specified feature is supported | >= 2.1.5 | +| [detect.has_flags](#detect-has_flags) | Determine if the specified parameter options are supported | >= 2.1.5 | +| [detect.has_cfuncs](#detect-has_cfuncs) | Determine if the specified c function exists | >= 2.1.5 | +| [detect.has_cxxfuncs](#detect-has_cxxfuncs) | Determine if the specified c++ function exists | >= 2.1.5 | +| [detect.has_cincludes](#detect-has_cincludes) | Determine if the specified c header file exists | >= 2.1.5 | +| [detect.has_cxxincludess](#detect-has_cxxincludes) | Determine if the specified c++ header file exists | >= 2.1.5 | +| [detect.has_ctypes](#detect-has_ctypes) | Determine if the specified c type exists | >= 2.1.5 | +| [detect.has_cxxtypes](#detect-has_cxxtypes) | Determine if the specified c++ type exists | >= 2.1.5 | +| [detect.check_cxsnippets](#detect-check_cxsnippets) | Check if c/c++ code snippets can be compiled by | >= 2.1.5 | + +###### detect.find_file + +- Find files + +This interface provides a more powerful project than [os.files](#os-files), which can specify multiple search directories at the same time, and can also specify additional subdirectories for each directory to match the pattern lookup, which is equivalent to [ An enhanced version of os.files](#os-files). + +E.g: + +```lua +import("lib.detect.find_file") + +local file = find_file("ccache", { "/usr/bin", "/usr/local/bin"}) +``` + +If found, the result returned is: `/usr/bin/ccache` + +It also supports pattern matching paths for recursive lookups, similar to `os.files`: + +```lua +local file = find_file("test.h", { "/usr/include", "/usr/local/include/**"}) +``` + +Not only that, but the path inside also supports built-in variables to get the path from the environment variables and the registry to find: + +```lua +local file = find_file("xxx.h", { "$(env PATH)", "$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\XXXX;Name)"}) +``` + +If the path rules are more complex, you can also dynamically generate path entries through a custom script: + +```lua +local file = find_file("xxx.h", { "$(env PATH)", function () return val("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\XXXX;Name"):match ("\"(.-)\"") end}) +``` + +In most cases, the above use has met various needs. If you need some extended functions, you can customize some optional configurations by passing in the third parameter, for example: + +```lua +local file = find_file("test.h", { "/usr", "/usr/local"}, {suffixes = {"/include", "/lib"}}) +``` + +By specifying a list of suffixes subdirectories, you can extend the list of paths (the second parameter) so that the actual search directory is expanded to: + +``` +/usr/include +/usr/lib +/usr/local/include +/usr/local/lib +``` + +And without changing the path list, you can dynamically switch subdirectories to search for files. + ++We can also quickly call and test this interface with the `xmake lua` plugin: `xmake lua lib.detect.find_file test.h /usr/local` +
+ +###### detect.find_path + +- Find the path + +The usage of this interface is similar to [lib.detect.find_file](#detect-find_file), the only difference is that the returned results are different. +After the interface finds the incoming file path, it returns the corresponding search path, not the file path itself. It is generally used to find the parent directory location corresponding to the file. + +```lua +import("lib.detect.find_path") + +local p = find_path("include/test.h", { "/usr", "/usr/local"}) +``` + +If the above code is successful, it returns: `/usr/local`, if `test.h` is in `/usr/local/include/test.h`. + +Another difference is that this interface is passed in not only the file path, but also the directory path to find: + +```lua +local p = find_path("lib/xxx", { "$(env PATH)", "$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\XXXX;Name)"}) +``` + +Again, this interface also supports pattern matching and suffix subdirectories: + +```lua +local p = find_path("include/*.h", { "/usr", "/usr/local/**"}, {suffixes = "/subdir"}) +``` + +###### detect.find_library + +- Find library files + +This interface is used to find library files (static libraries, dynamic libraries) in the specified search directory, for example: + +```lua +import("lib.detect.find_library") + +local library = find_library("crypto", {"/usr/lib", "/usr/local/lib"}) +``` + +Running on macosx, the results returned are as follows: + +```lua +{ + filename = libcrypto.dylib +, linkdir = /usr/lib +, link = crypto +, kind = shared +} +``` + +If you do not specify whether you need a static library or a dynamic library, then this interface will automatically select an existing library (either a static library or a dynamic library) to return. + +If you need to force the library type you need to find, you can specify the kind parameter as (`static/shared`): + +```lua +local library = find_library("crypto", {"/usr/lib", "/usr/local/lib"}, {kind = "static"}) +``` + +This interface also supports suffixes suffix subdirectory search and pattern matching operations: + +```lua +local library = find_library("cryp*", {"/usr", "/usr/local"}, {suffixes = "/lib"}) +``` + +###### detect.find_program + +- Find executable programs + +This interface is more primitive than [lib.detect.find_tool](#detect-find_tool), looking for executables through the specified parameter directory. + +```lua +import("lib.detect.find_program") + +local program = find_program("ccache") +``` + +The above code is like not passing the search directory, so it will try to execute the specified program directly. If it runs ok, it will return directly: `ccache`, indicating that the search is successful. + +Specify the search directory and modify the test command parameters that are attempted to run (default: `ccache --version`): + +```lua +localProgram = find_program("ccache", {pathes = {"/usr/bin", "/usr/local/bin"}, check = "--help"}) +``` + +The above code will try to run: `/usr/bin/ccache --help`, if it runs successfully, it returns: `/usr/bin/ccache`. + +If `--help` can't satisfy the requirement, some programs don't have the `--version/--help` parameter, then you can customize the run script to run the test: + +```lua +local program = find_program("ccache", {pathes = {"/usr/bin", "/usr/local/bin"}, check = function (program) os.run("%s -h", program) end }) +``` + +Similarly, the search path list supports built-in variables and custom scripts: + +```lua +local program = find_program("ccache", {pathes = {"$(env PATH)", "$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug;Debugger)"}}) +local program = find_program("ccache", {pathes = {"$(env PATH)", function () return "/usr/local/bin" end}}) +``` + ++In order to speed up the efficiency of frequent lookups, this interface comes with a default cache, so even if you frequently find the same program, it will not take too much time. +If you want to disable the cache, you can clear the local cache by executing `xmake f -c` in the project directory. +
+ +We can also test quickly with `xmake lua lib.detect.find_program ccache`. + +###### detect.find_programver + +- Find the executable version number + + +```lua +import("lib.detect.find_programver") + +local programver = find_programver("ccache") +``` + +The return result is: 3.2.2 + +By default it will try to get the version via `ccache --version`. If this parameter doesn't exist, you can specify other parameters yourself: + +```lua +local version = find_programver("ccache", {command = "-v"}) +``` + +Even the custom version gets the script: + +```lua +local version = find_programver("ccache", {command = function () return os.iorun("ccache --version") end}) +``` + +For the extraction rule of the version number, if the built-in matching mode does not meet the requirements, you can also customize: + +```lua +local version = find_programver("ccache", {command = "--version", parse = "(%d+%.?%d*%.?%d*.-)%s"}) +local version = find_programver("ccache", {command = "--version", parse = function (output) return output:match("(%d+%.?%d*%.?%d*.-)%s ") end}) +``` + ++In order to speed up the efficiency of frequent lookups, this interface is self-contained by default. If you want to disable the cache, you can execute `xmake f -c` in the project directory to clear the local cache. +
+ +We can also test quickly with `xmake lua lib.detect.find_programver ccache`. + +###### detect.find_package + +- Find package files + +This interface is also used to find library files, but it is higher than [lib.detect.find_library](#detect-find_library), and it is more powerful and easy to use, because it is based on the strength of the package. + +So what is a complete package, it contains: + +1. Multiple static libraries or dynamic library files +2. Library search directory +3. Search directory for header files +4. Optional compile link options, such as `defines` +5. Optional version number + +For example, we look for an openssl package: + +```lua +import("lib.detect.find_package") + +local package = find_package("openssl") +``` + +The returned results are as follows: + +```lua +{links = {"ssl", "crypto", "z"}, linkdirs = {"/usr/local/lib"}, includedirs = {"/usr/local/include"}} +``` + +If the search is successful, return a table containing all the package information, if it fails, return nil + +The return result here can be directly passed as the parameter of `target:add`, `option:add`, which is used to dynamically increase the configuration of `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) +``` + +If third-party tools such as `homebrew`, `pkg-config` are installed on the system, then this interface will try to use them to improve the search results. + +We can also choose to find the package of the specified version by specifying the version number (if the package does not get the version information or there is no matching version of the package, then return nil): + +```lua +local package = find_package("openssl", {version = "1.0.1"}) +``` + +The packages that are looked up by default are matched to the platform, architecture, and mode according to the following rules: + +1. If the parameter passed in specifies `{plat = "iphoneos", arch = "arm64", mode = "release"}`, then match first, for example: `find_package("openssl", {plat = "iphoneos"} )`. +2. If there is a configuration file in the current project environment, try to get it from `config.get("plat")`, `config.get("arch")` and `config.get("mode")` The platform architecture is matched. +3. Finally, the matching is done from `os.host()` and `os.arch()`, which is the platform architecture environment of the current host. + +If the system's library directory and `pkg-config` are not enough to meet the requirements and the package cannot be found, you can manually set the search path yourself: + +```lua +local package = find_package("openssl", {linkdirs = {"/usr/lib", "/usr/local/lib"}, includedirs = "/usr/local/include"}) +``` + +You can also specify the link name you want to search at the same time, the header file name: + +```lua +local package = find_package("openssl", {links = {"ssl", "crypto"}, includes = "ssl.h"}}) +``` + +You can even specify xmake's `packagedir/*.pkg` package directory to find the corresponding `openssl.pkg` package, which is typically used to find local packages built into the project directory. + +For example, the tbox project has a built-in `pkg/openssl.pkg` local package project. We can use the following script to pass in the `{packagedirs = ""}` parameter to find the local package first. If it can't find it, go to the system. package. + +```lua +target("test") + on_load(function (target) + import("lib.detect.find_package") + target:add(find_package("openssl", {packagedirs = path.join(os.projectdir(), "pkg")})) + end) +``` + +To summarize, the current search order: + +1. If you specify the `{packagedirs = ""}` parameter, look for the local package `*.pkg` from the path specified by this parameter. +2. If there is a `detect.packages.find_xxx` script under `xmake/modules`, try calling this script to improve the lookup results. +3. If vcpkg exists in the system, obtain the package from the vcpkg package management system. +4. If the system has `pkg-config` and you are looking for a library for the system environment, try to find it using the path and link information provided by `pkg-config` +5. If the system has `homebrew` and you are looking for a library for the system environment, try to find it using the information provided by `brew --prefix xxx` +6. Find from the pathes path specified in the parameter and some known system paths `/usr/lib`, `/usr/include` + +Here we need to focus on the second point, through the `detect.packages.find_xxx` script to improve the search results, many times automatic packet detection is unable to fully detect the package path, +Especially for the windows platform, there is no default library directory, and there is no package management app. When many libraries are installed, they are placed in the system directory, or add a registry key. + +Therefore, there is no uniform rule for finding it. At this time, you can customize a search script to improve the lookup mechanism of `find_package` and perform more accurate search for the specified package. + +In the xmake/modules/detect/packages` directory that comes with xmake, there are already many built-in package scripts for better lookup support for commonly used packages. +Of course, this is not enough for all users. If the package that the user needs is still not found, then you can define a search script yourself, for example: + +To find a package named `openssl`, you can write a script for `find_openssl.lua` placed in the project directory: + +``` +Projectdir + - xmake + - modules + - detect/package/find_openssl.lua +``` + +Then specify the directory for this module at the beginning of the project's `xmake.lua` file: + +```lua +add_moduledirs("$(projectdir)/xmake/modules") +``` + +This way xmake will be able to find custom extensions. + +Next we look at the implementation of `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 +``` + +Inside the windows platform to read the registry, to find the +specified library file, the bottom layer is actually called +[find_library](#detect-find_library) and other interfaces. + +In order to speed up the efficiency of frequent +lookups, this interface is self-contained by default. If you want to +disable the cache, you can execute `xmake f -c` in the project +directory to clear the local cache. You can also disable the cache by +specifying the force parameter, forcing a re-find: +`find_package("openssl", {force = true})`
+ +We can also test quickly with `xmake lua lib.detect.find_package +openssl`. + +After the 2.2.5 version, the built-in interface [find_packages] +(#find_packages) has been added, which can find multiple packages at +the same time, and can be used directly without importing by import. + +And after this release, support for explicitly looking for packages +from a specified third-party package manager, for example: + +```lua find_package("brew::pcre2/libpcre2-8") ``` + +Since the package name of each third-party package manager is not +completely consistent, for example, pcre2 has three library versions +in homebrew, we can specify the library corresponding to libpcre2-8 +version by the above method. + +In addition, for vcpkg, conan can also specify the library inside by +adding the `vcpkg::`, `conan::` package namespace. + +###### detect.find_tool + +- Find tool + +This interface is also used to find executable programs, but more +advanced than [lib.detect.find_program](#detect-find_program), the +function is also more powerful, it encapsulates the executable +program, providing the concept of tools: + +* toolname: tool name, short for executable program, used to mark a +* tool, for example: `gcc`, `clang`, etc. program: executable program +* command, for example: `xcrun -sdk macosx clang` + +The corresponding relationship is as follows: + +| 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) can only determine +whether the program exists by passing in the original program command +or path. And `find_tool` can find the tool through a more consistent +toolname, and return the corresponding program complete command path, +for example: + +```lua +import("lib.detect.find_tool") + +local tool = find_tool("clang") +``` + +The result returned is: `{name = "clang", program = "clang"}`, at this +time there is no difference, we can manually specify the executable +command: + +```lua +local tool = find_tool("clang", {program = "xcrun -sdk macosx clang"}) +``` + +The result returned is: `{name = "clang", program = "xcrun -sdk macosx +clang"}` + +In macosx, gcc is clang. If we execute `gcc --version`, we can see +that it is a vest of clang. We can intelligently identify it through +the `find_tool` interface: + +```lua +local tool = find_tool("gcc") +``` + +The result returned is: `{name = "clang", program = "gcc"}` + +The difference can be seen by this result. The tool name will actually +be marked as clang, but the executable command uses gcc. + +We can also specify the `{version = true}` parameter to get the +version of the tool, and specify a custom search path. It also +supports built-in variables and custom scripts: + +```lua +local tool = find_tool("clang", {version = true, {pathes = {"/usr/bin", "/usr/local/bin", "$(env PATH)", function () return "/usr/xxx/bin" end}}) +``` + +The result returned is: `{name = "clang", program = "/usr/bin/clang", +version = "4.0"}` + +This interface is a high-level wrapper around `find_program`, so it +also supports custom script detection: + +```lua +local tool = find_tool("clang", {check = "--help"}) +local tool = find_tool("clang", {check = function (tool) os.run("%s -h", tool) end}) +``` + +Finally, the search process of `find_tool`: + +1. First try to run and detect with the argument of `{program = +"xxx"}`. 2. If there is a `detect.tools.find_xxx` script in +`xmake/modules/detect/tools`, call this script for more accurate +detection. 3. Try to detect from the system directory such as +`/usr/bin`, `/usr/local/bin`. + +We can also add a custom lookup script to the module directory +specified by `add_moduledirs` in the project `xmake.lua` to improve +the detection mechanism: + +``` +projectdir +- xmake/modules +- detect/tools/find_xxx.lua +``` + +For example, we customize a lookup script for `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 +``` + +After placing it in the project's module directory, execute: `xmake l +lib.detect.find_tool 7z` to find it. + +In order to speed up the efficiency of frequent +lookups, this interface is self-contained by default. If you want to +disable the cache, you can execute `xmake f -c` in the project +directory to clear the local cache.
+ +We can also test quickly with `xmake lua lib.detect.find_tool clang`. + +###### detect.find_toolname + +- Find tool name + +Match the corresponding tool name with the program command, for +example: + +| 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 | + +Compared with program, toolname can uniquely mark a tool, and it is also convenient to find and load the corresponding script `find_xxx.lua`. + +###### detect.find_cudadevices + +- Find CUDA devices of the host + +Enumerate CUDA devices through the CUDA Runtime API and query theirs properties. + +```lua +import("lib.detect.find_cudadevices") + +local devices = find_cudadevices({ skip_compute_mode_prohibited = true }) +local devices = find_cudadevices({ min_sm_arch = 35, order_by_flops = true }) +``` + +The result returned is: `{ { ['$id'] = 0, name = "GeForce GTX 960M", major = 5, minor = 0, ... }, ... }` + +The included properties will vary depending on the current CUDA version. +Please refer to [CUDA Toolkit Documentation](https://docs.nvidia.com/cuda/cuda-runtime-api/structcudaDeviceProp.html#structcudaDeviceProp) and its historical version for more information. + +###### detect.features + +- Get all the features of the specified tool + +This interface is similar to [compiler.features](#compiler-features). The difference is that this interface is more primitive. The passed argument is the actual tool name toolname. + +And this interface not only can get the characteristics of the compiler, the characteristics of any tool can be obtained, so it is more versatile. + +```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"}}) +``` + +By passing in flags, you can change the result of the feature, for example, some features of C++11, which are not available by default. After enabling `-std=c++11`, you can get it. + +A list of all compiler features can be found at [compiler.features](#compiler-features). + +###### detect.has_features + +- Determine if the specified feature is supported + +This interface is similar to [compiler.has_features](#compiler-has_features), but more primitive, the passed argument is the actual tool name toolname. + +And this interface can not only judge the characteristics of the compiler, but the characteristics of any tool can be judged, so it is more versatile. + +```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"}) +``` + +If the specified feature list exists, the actual supported feature sublist is returned. If none is supported, nil is returned. We can also change the feature acquisition rule by specifying flags. + +A list of all compiler features can be found at [compiler.features](#compiler-features). + +###### detect.has_flags + +- Determine if the specified parameter option is supported + +This interface is similar to [compiler.has_flags](#compiler-has_flags), but more primitive, the passed argument is the actual tool name 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"}) +``` + +Returns true if the test passed. + +The detection of this interface has been optimized. Except for the cache mechanism, in most cases, the tool's option list (`--help`) will be directly judged. If the option list is not available, it will be tried. The way to run to detect. + +###### detect.has_cfuncs + +- Determine if the specified c function exists + +This interface is a simplified version of [lib.detect.check_cxsnippets](#detect-check_cxsnippets) and is only used to detect functions. + +```lua +import("lib.detect.has_cfuncs") + +local ok = has_cfuncs("setjmp") +local ok = has_cfuncs({"sigsetjmp((void*)0, 0)", "setjmp"}, {includes = "setjmp.h"}) +``` + +The rules for describing functions are as follows: + +| Function Description | Description | +| ----------------------------------------------- | ------------- | +| `sigsetjmp` | pure function name | +| `sigsetjmp((void*)0, 0)` | Function Call | +| `sigsetjmp{int a = 0; sigsetjmp((void*)a, a);}` | function name + {} block | + +In the last optional parameter, in addition to specifying `includes`, you can also specify other parameters to control the option conditions for compile detection: + +```lua +{ verbose = false, target = [target|option], includes = .., config = {linkdirs = .., links = .., defines = ..}} +``` + +The verbose is used to echo the detection information, the target is used to append the configuration information in the target before the detection, and the config is used to customize the compilation options related to the target. + +###### detect.has_cxxfuncs + +- Determine if the specified c++ function exists + +This interface is similar to [lib.detect.has_cfuncs](#detect-has_cfuncs), please refer to its instructions for use. The only difference is that this interface is used to detect c++ functions. + +###### detect.has_cincludes + +- Determine if the specified c header file exists + +This interface is a simplified version of [lib.detect.check_cxsnippets](#detect-check_cxsnippets) and is only used to detect header files. + +```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 + +- Determine if the specified c++ header file exists + +This interface is similar to [lib.detect.has_cincludess](#detect-has_cincludes), please refer to its instructions for use. The only difference is that this interface is used to detect c++ header files. + +###### detect.has_ctypes + +- Determine if the specified c type exists + +This interface is a simplified version of [lib.detect.check_cxsnippets](#detect-check_cxsnippets) and is only used to detect functions. + +```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 + +- Determine if the specified c++ type exists + +This interface is similar to [lib.detect.has_ctypess](#detect-has_ctypes). Please refer to its instructions for use. The only difference is that this interface is used to detect c++ types. + +###### detect.check_cxsnippets + +- Check if the c/c++ code snippet can be compiled + +The generic c/c++ code snippet detection interface, by passing in a list of multiple code snippets, it will automatically generate a compiled file, and then common sense to compile it, if the compilation pass returns true. + +For some complex compiler features, even if [compiler.has_features](#compiler-has_features) can't detect it, you can detect it by trying to compile through this interface. + +```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"}) +``` + +This interface is a generic version of interfaces such as [detect.has_cfuncs](#detect-has_cfuncs), [detect.has_cincludes](#detect-has_cincludes), and [detect.has_ctypes](detect-has_ctypes), and is also lower level. + +So we can use it to detect: types, functions, includes and links, or combine them together to detect. + +The first parameter is a list of code fragments, which are generally used for the detection of some custom features. If it is empty, it can only detect the conditions in the optional parameters, for example: + +```lua +local ok = check_cxsnippets({}, {types = {"wchar_t", "char*"}, includes = "stdio.h", funcs = {"sigsetjmp", "sigsetjmp((void*)0, 0)"} }) +``` + +The above call will check if the types, includes and funcs are both satisfied, and return true if passed. + +There are other optional parameters: + +```lua +{ verbose = false, target = [target|option], sourcekind = "[cc|cxx]"} +``` + +The verbose is used to echo the detection information. The target is used to append the configuration information in the target before the detection. The sourcekind is used to specify the tool type such as the compiler. For example, the incoming `cxx` is forced to be detected as c++ code. + +##### net.http + +This module provides various operational support for http. The currently available interfaces are as follows: + +| Interface | Description | Supported version| +| --------------------------------------------------- | -------------------------------------------- | -------------------- | +[http.download](#http-download) | Download http file | >= 2.1.5 | + +###### http.download + +- Download http file + +This interface is relatively simple, is simply download files. + +```lua +import("net.http") + +http.download("https://xmake.io", "/tmp/index.html") +``` + +##### privilege.sudo + +This interface is used to run commands via `sudo` and provides platform consistency handling, which can be used for scripts that require root privileges to run. + ++In order to ensure security, unless you must use it, try not to use this interface in other cases. +
+ +| Interface | Description | Supported Versions | +| --------------------------------------------------- | -------------------------------------------- | -------------------- | +| [sudo.has](#sudo-has) | Determine if sudo supports | >= 2.1.5 | +| [sudo.run](#sudo-run) | Quiet running program | >= 2.1.5 | +| [sudo.runv](#sudo-runv) | Quiet running program with parameter list | >= 2.1.5 | +| [sudo.exec](#sudo-exec) | Evoke Run Program | >= 2.1.5 | +| [sudo.execv](#sudo-execv) | Echo running program with parameter list | >= 2.1.5 | +| [sudo.iorun](#sudo-iorun) | Run and get the program output | >= 2.1.5 | +| [sudo.iorunv](#sudo-iorunv) | Run and get the program output with parameter list | >= 2.1.5 | + +###### sudo.has + +- Determine if sudo supports + +At present, sudo is supported only under `macosx/linux`. The administrator privilege running on Windows is not supported yet. Therefore, it is recommended to use the interface to judge the support situation before use. + +```lua +import("privilege.sudo") + +if sudo.has() then + sudo.run("rm /system/file") +end +``` + +###### sudo.run + +- Quietly running native shell commands + +For specific usage, please refer to: [os.run](#os-run). + +```lua +import("privilege.sudo") + +sudo.run("rm /system/file") +``` + +###### sudo.runv + +- Quietly running native shell commands with parameter list + +For specific usage, please refer to: [os.runv](#os-runv). + +###### sudo.exec + +- Echo running native shell commands + +For specific usage, please refer to: [os.exec](#os-exec). + +###### sudo.execv + +- Echo running native shell commands with parameter list + +For specific usage, please refer to: [os.execv](#os-execv). + +###### sudo.iorun + +- Quietly running native shell commands and getting output + +For specific usage, please refer to: [os.iorun](#os-iorun). + +###### sudo.iorunv + +- Run the native shell command quietly and get the output with a list of parameters + +For specific usage, please refer to: [os.iorunv](#os-iorunv). + +##### devel.git + +This interface provides access to various commands of git. Compared to the direct call to git command, this module provides a more easy-to-use package interface and provides automatic detection and cross-platform processing for git. + ++Currently on Windows, you need to manually install the git package before you can detect it. The subsequent version will provide automatic integration of git function. Users will not need to care about how to install git, they can be used directly. +
+ +| Interface | Description | Supported Versions | +| --------------------------------------------------- | -------------------------------------------- | -------------------- | +| [git.clone](#git-clone) | clone codebase | >= 2.1.5 | +| [git.pull](#git-pull) | Pull the codebase latest commit | >= 2.1.5 | +| [git.clean](#git-clean) | Clean up the codebase file | >= 2.1.5 | +| [git.checkout](#git-checkout) | Check out the specified branch version | >= 2.1.5 | +| [git.refs](#git-refs) | Get a list of all references | >= 2.1.5 | +| [git.tags](#git-tags) | Get all tag lists | >= 2.1.5 | +| [git.branches](#git-branches) | Get a list of all branches | >= 2.1.5 | + +###### git.clone + +- clone codebase + +This interface corresponds to the `git clone` command. + +```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 + +- Pull the code base for the latest submission + +This interface corresponds to the `git pull` command. + +```lua +import("devel.git") + +git.pull() +git.pull({remote = "origin", tags = true, branch = "master", repodir = "/tmp/xmake"}) +``` + +###### git.clean + +- Clean up the code base file + +This interface corresponds to the `git clean` command. + +```lua +import("devel.git") + +git.clean() +git.clean({repodir = "/tmp/xmake", force = true}) +``` + +###### git.checkout + +- Check out the specified branch version + +This interface corresponds to the `git checkout` command + +```lua +import("devel.git") + +git.checkout("master", {repodir = "/tmp/xmake"}) +git.checkout("v1.0.1", {repodir = "/tmp/xmake"}) +``` + +###### git.refs + +- Get a list of all references + +This interface corresponds to the `git ls-remote --refs` command + +```lua +import("devel.git") + +local refs = git.refs(url) +``` + +###### git.tags + +- Get a list of all tags + +This interface corresponds to the `git ls-remote --tags` command + +```lua +import("devel.git") + +local tags = git.tags(url) +``` + +###### git.branches + +- Get a list of all branches + +This interface corresponds to the `git ls-remote --heads` command + +```lua +import("devel.git") + +local branches = git.branches(url) +``` + +##### utils.archive + +This module is used to compress and decompress files. + +| Interface | Description | Supported Versions | +| --------------------------------------------------- | -------------------------------------------- | -------------------- | +| [archive.extract](#archive-extract)| Extract files | >= 2.1.5 | + +###### archive.extract + +- unzip files + +Supports the decompression of most commonly used compressed files. It automatically detects which decompression tools are provided by the system, and then adapts them to the most suitable decompressor to decompress the specified compressed files. + +```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/old/plugins.md b/old/plugins.md new file mode 100644 index 00000000..01825823 --- /dev/null +++ b/old/plugins.md @@ -0,0 +1,499 @@ +--- +search: en +--- + +## Plugin Development + +#### Introduction + +XMake supports the plugin module and we can develop ourself plugin module conveniently. + +We can run command `xmake -h` to look over some builtin plugins of xmake + +``` +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: Run a given lua script. +* macro: Record and playback some xmake commands repeatly. +* doxygen:Generate doxygen document automatically. +* hello: The demo plugin and only print: 'hello xmake!' +* project:Generate project file for IDE, only generate makefile now and will generate vs, xcode project in the future + +#### Quick Start + +Now we write a simple plugin demo for printing 'hello xmake!' + +```lua +-- define a plugin task +task("hello") + + -- set the category for showing it in plugin category menu (optional) + set_category("plugin") + + -- the main entry of the plugin + on_run(function () + + -- print 'hello xmake!' + print("hello xmake!") + end) + + -- set the menu options, but we put empty options now. + set_menu { + -- usage + usage = "xmake hello [options]" + + -- description + , description = "Hello xmake!" + + -- options + , options = {} + } +``` + +The file tree of this plugin: + +``` +hello + - xmake.lua + +``` + +Now one of the most simple plugin finished, how was it to be xmake detected it, there are three ways: + +1. Put this plugin directory into xmake/plugins the source codes as the builtin plugin. +2. Put this plugin directory into ~/.xmake/plugins as the global user plugin. +3. Put this plugin directory to anywhere and call `add_plugindirs("./hello")` in xmake.lua as the local project plugin. + +#### Run Plugin + +Next we run this plugin + +```bash +xmake hello +``` + +The results is + +``` +hello xmake! +``` + +Finally, we can also run this plugin in the custom scripts of `xmake.lua` + +```lua + +target("demo") + + -- run this plugin after building target + after_build(function (target) + + -- import task module + import("core.project.task") + + -- run the plugin task + task.run("hello") + end) +``` + +## Builtin Plugins + +#### Macros Recording and Playback + +##### Introduction + +We can record and playback our xmake commands and save as macro quickly using this plugin. + +And we can run this macro to simplify our jobs repeatly. + +##### Record Commands + +```bash +# begin to record commands +$ xmake macro --begin + +# run some xmake commands +$ 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 + +# stop to record and save as anonymous macro +xmake macro --end +``` + +##### Playback Macro + +```bash +# playback the previous anonymous macro +$ xmake macro . +``` + +##### Named Macro + +```bash +$ xmake macro --begin +$ ... +$ xmake macro --end macroname +$ xmake macro macroname +``` + +##### Import and Export Macro + +Import the given macro file or directory. + +```bash +$ xmake macro --import=/xxx/macro.lua macroname +$ xmake macro --import=/xxx/macrodir +``` + +Export the given macro to file or directory. + +```bash +$ xmake macro --export=/xxx/macro.lua macroname +$ xmake macro --export=/xxx/macrodir +``` + +##### List and Show Macro + +List all builtin macros. + +```bash +$ xmake macro --list +``` + +Show the given macro script content. + +```bash +$ xmake macro --show macroname +``` + +##### Custom Macro Script + +Create and write a `macro.lua` script first. + +```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 +``` + +Import this macro script to xmake. + +```bash +$ xmake macro --import=/xxx/macro.lua [macroname] +``` + +Playback this macro script. + +```bash +$ xmake macro [.|macroname] +``` + +##### Builtin Macros + +XMake supports some builtins macros to simplify our jobs. + +For example, we use `package` macro to package all architectures of the iphoneos platform just for once. + +```bash +$ xmake macro package -p iphoneos +``` + +##### Advance Macro Script + +Let's see the `package` macro script: + +```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 +``` + ++ If you want to known more options, please run: `xmake macro --help` +
+ +#### Run the Custom Lua Script + +##### Run the given script + +Write a simple lua script: + +```lua +function main() + print("hello xmake!") +end +``` + +Run this lua script. + +```bash +$ xmake lua /tmp/test.lua +``` + ++ You can also use `import` api to write a more advance lua script. +
+ +##### Run the builtin script + +You can run `xmake lua -l` to list all builtin script name, for example: + +```bash +$ xmake lua -l +scripts: + cat + cp + echo + versioninfo + ... +``` + +And run them: + +```bash +$ xmake lua cat ~/file.txt +$ xmake lua echo "hello xmake" +$ xmake lua cp /tmp/file /tmp/file2 +$ xmake lua versioninfo +``` + +##### Run interactive commands (REPL) + +Enter interactive mode: + +```bash +$ xmake lua +> 1 + 2 +3 + +> a = 1 +> a +1 + +> for _, v in pairs({1, 2, 3}) do +>> print(v) +>> end +1 +2 +3 +``` + +And we can `import` modules: + +```bash +> task = import("core.project.task") +> task.run("hello") +hello xmake! +``` + +If you want to cancel multiline input, please input character `q`, for example: + +```bash +> for _, v in ipairs({1, 2}) do +>> print(v) +>> q <-- cancel multiline and clear previous input +> 1 + 2 +3 +``` + +#### Generate IDE Project Files + +##### Generate Makefile + +```bash +$ xmake project -k makefile +``` + +##### Generate compiler_commands + +We can export the compilation commands info of all source files and it is JSON compilation database format. + +```console +$ xmake project -k compile_commands +``` + +The the content of the output file: + +``` +[ + { "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" }, + ... +] + +``` + +Please see [JSONCompilationDatabase](#https://clang.llvm.org/docs/JSONCompilationDatabase.html) if need known more info about `compile_commands`. + +##### Generate VisualStudio Project + +```bash +$ xmake project -k [vs2008|vs2013|vs2015|..] +``` + +v2.1.2 or later, it supports multi-mode and multi-architecture generation for vs201x project. + +For example: + +```bash +$ xmake project -k vs2017 -m "debug,release" +``` + +It will generate four project configurations: `debug|x86`, `debug|x64`, `release|x86`, `release|x64`. + +Or you can set modes to `xmake.lua`: + +```lua +set_modes("debug", "release") +``` + +Then, we run the following command: + +```bash +$ xmake project -k vs2017 +``` + +The effect is same. + +#### Generate Doxygen Document + +Please ensure that the doxygen tool has been installed first. + +```bash +$ xmake doxygen +``` + +## More Plugins + +Please download and install from the plugins repository [xmake-plugins](https://github.com/xmake-io/xmake-plugins). + +#### Convert .app to .ipa + +```bash +$ xmake app2ipa --icon=/xxx.png /xxx/ios.app -o /xxx.ios.ipa +``` diff --git a/old/zh/README.md b/old/zh/README.md new file mode 100644 index 00000000..73c63a7a --- /dev/null +++ b/old/zh/README.md @@ -0,0 +1,1892 @@ +--- +nav: zh +search: zh +--- + ++
+ + +## 简介 + +XMake是一个基于Lua的轻量级跨平台自动构建工具,支持在各种主流平台上构建项目 + +xmake的目标是开发者更加关注于项目本身开发,简化项目的描述和构建,并且提供平台无关性,使得一次编写,随处构建 + +它跟cmake、automake、premake有点类似,但是机制不同,它默认不会去生成IDE相关的工程文件,采用直接编译,并且更加的方便易用 +采用lua的工程描述语法更简洁直观,支持在大部分常用平台上进行构建,以及交叉编译 + +并且xmake提供了创建、配置、编译、打包、安装、卸载、运行等一些actions,使得开发和构建更加的方便和流程化。 + +不仅如此,它还提供了许多更加高级的特性,例如插件扩展、脚本宏记录、批量打包、自动文档生成等等。。 + +## 安装 + +#### Master版本 + +##### 使用curl + +```bash +bash <(curl -fsSL https://raw.githubusercontent.com/tboox/xmake/master/scripts/get.sh) +``` + +##### 使用wget + +```bash +bash <(wget https://raw.githubusercontent.com/tboox/xmake/master/scripts/get.sh -O -) +``` + +##### 使用powershell + +```bash +Invoke-Expression (Invoke-Webrequest 'https://raw.githubusercontent.com/tboox/xmake/master/scripts/get.ps1' -UseBasicParsing).Content +``` + +#### Windows + +##### 使用安装包 + +1. 从 [Releases](https://github.com/xmake-io/xmake/releases) 上下载windows安装包 +2. 运行安装程序 xmake-[version].exe + +##### 使用scoop + +```bash +scoop install xmake +``` + +#### MacOS + +```bash +$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" +$ brew install xmake +``` + +或者: + +1. 从 [Releases](https://github.com/xmake-io/xmake/releases) 上下载pkg安装包 +2. 双击运行 + +或者安装master版本: + +```bash +# 使用homebrew安装master版本 +$ brew install xmake --HEAD + +# 或者直接调用shell下载安装 +$ bash <(curl -fsSL https://raw.githubusercontent.com/tboox/xmake/master/scripts/get.sh) +``` + +#### Linux + +在archlinux上安装: + +```bash +$ yaourt xmake +``` + +在ubuntu上安装: + +```bash +$ sudo add-apt-repository ppa:tboox/xmake +$ sudo apt-get update +$ sudo apt-get install xmake +``` + +或者手动添加包源: + +``` +deb http://ppa.launchpad.net/tboox/xmake/ubuntu yakkety main +deb-src http://ppa.launchpad.net/tboox/xmake/ubuntu yakkety main +``` + +然后执行: + +```bash +$ sudo apt-get update +$ sudo apt-get install xmake +``` + +或者下载deb包来安装: + +1. 从 [Releases](https://github.com/xmake-io/xmake/releases) 上下载deb安装包 +2. 运行: `dpkg -i xmake-xxxx.deb` + +在`redhat/centos`上安装: + +1. 从 [Releases](https://github.com/xmake-io/xmake/releases) 上下载rpm安装包 +2. 运行: `yum install xmake-xxx.rpm --nogpgcheck` + +#### 编译安装 + +通过脚本编译安装: + +```bash +$ git clone https://github.com/xmake-io/xmake.git +$ cd ./xmake +$ ./scripts/get.sh __local__ +``` + +仅仅安装和更新xmake的lua脚本: + +```bash +$ ./scripts/get.sh __local__ __install_only__ +``` + +卸载: + +```bash +$ ./scripts/get.sh __uninstall__ +``` + +通过make进行编译安装: + +```bash +$ make build; sudo make install +``` + +安装到其他指定目录: + +```bash +$ sudo make install prefix=/usr/local +``` + +卸载: + +```bash +$ sudo make uninstall +``` + +#### 更新升级 + +从v2.2.3版本开始,新增了`xmake update`命令,来快速进行自我更新和升级,默认是升级到最新版本,当然也可以指定升级或者回退到某个版本: + +```bash +$ xmake update 2.2.4 +``` + +我们也可以指定更新到master/dev分支版本: + +```bash +$ xmake update master +$ xmake update dev +``` + +最后,我们如果要卸载xmake,也是支持的:`xmake update --uninstall` + +## 快速开始 + +[](https://asciinema.org/a/133693) + +#### 创建工程 + +创建一个名叫`hello`的`c`控制台工程: + +```bash +$ xmake create -l c -P ./hello +``` + +执行完后,将会生成一个简单工程结构: + +``` +hello +├── src +│ └── main.c +└── xmake.lua +``` + +其中`xmake.lua`是工程描述文件,内容非常简单,告诉xmake添加`src`目录下的所有`.c`源文件: + +```lua +target("hello") + set_kind("binary") + add_files("src/*.c") +``` + +目前支持的语言如下: + +* c/c++ +* objc/c++ +* cuda +* asm +* swift +* dlang +* golang +* rust + ++ 如果你想了解更多参数选项,请运行: `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` +
+ +## 工程实例 + +#### 可执行程序 + +```lua +target("test") + set_kind("binary") + add_files("src/*c") +``` + +#### 静态库程序 + +```lua +target("library") + set_kind("static") + add_files("src/library/*.c") + +target("test") + set_kind("binary") + add_files("src/*c") + add_deps("library") +``` + +通过`add_deps`将一个静态库自动链接到test可执行程序。 + +#### 动态库程序 + +```lua +target("library") + set_kind("shared") + add_files("src/library/*.c") + +target("test") + set_kind("binary") + add_files("src/*c") + add_deps("library") +``` + +通过`add_deps`将一个动态库自动链接到test可执行程序。 + +#### Qt程序 + +创建一个空工程: + +```console +$ xmake create -l c++ -t console_qt test +$ xmake create -l c++ -t static_qt test +$ xmake create -l c++ -t shared_qt test +$ xmake create -l c++ -t quickapp_qt test +``` + +默认会自动探测Qt环境,当然也可以指定Qt SDK环境目录: + +```console +$ xmake f --qt=~/Qt/Qt5.9.1 +``` + +如果想要使用windows下mingw的Qt环境,可以切到mingw的平台配置,并且指定下mingw编译环境的sdk路径即可,例如: + +```console +$ xmake f -p mingw --sdk=C:\Qt\Qt5.10.1\Tools\mingw530_32 +``` + +上述指定的mingw sdk用的是Qt下Tools目录自带的环境,当然如果有其他第三方mingw编译环境,也可以手动指定, 具体可以参考:[mingw编译配置](#mingw)。 + +更多详情可以参考:[#160](https://github.com/xmake-io/xmake/issues/160) + +##### 静态库程序 + +```lua +target("qt_static_library") + add_rules("qt.static") + add_files("src/*.cpp") + add_frameworks("QtNetwork", "QtGui") +``` + +##### 动态库程序 + +```lua +target("qt_shared_library") + add_rules("qt.shared") + add_files("src/*.cpp") + add_frameworks("QtNetwork", "QtGui") +``` + +##### 控制台程序 + +```lua +target("qt_console") + add_rules("qt.console") + add_files("src/*.cpp") +``` + +##### Quick应用程序 + +```lua +target("qt_quickapp") + add_rules("qt.application") + add_files("src/*.cpp") + add_files("src/qml.qrc") + add_frameworks("QtQuick") +``` + +##### Widgets应用程序 + +```lua +target("qt_widgetapp") + add_rules("qt.application") + add_files("src/*.cpp") + add_files("src/mainwindow.ui") + add_files("src/mainwindow.h") -- 添加带有 Q_OBJECT 的meta头文件 + add_frameworks("QtWidgets") +``` + +##### Android应用程序 + +2.2.6之后版本,可以直接切到android平台编译Quick/Widgets应用程序,生成apk包,并且可通过`xmake install`命令安装到设备。 + +```console +$ xmake create -t quickapp_qt -l c++ appdemo +$ cd appdemo +$ xmake f -p android --ndk=~/Downloads/android-ndk-r19c/ --android_sdk=~/Library/Android/sdk/ -c +$ xmake +[ 0%]: compiling.qt.qrc src/qml.qrc +[ 50%]: ccache compiling.release src/main.cpp +[100%]: linking.release libappdemo.so +[100%]: generating.qt.app appdemo.apk +``` + +然后安装到设备: + +```console +$ xmake install +installing appdemo ... +installing build/android/armv7-a/release/appdemo.apk .. +Success +install ok!👌 +``` + +#### Cuda程序 + +创建一个空工程: + +```console +$ xmake create -P test -l cuda +$ cd test +$ xmake +``` + +```lua +-- define target +target("cuda_console") + set_kind("binary") + add_files("src/*.cu") + -- generate SASS code for SM architecture of current host + add_cugencodes("native") + -- generate PTX code for the virtual architecture to guarantee compatibility + add_cugencodes("compute_30") +``` + ++从v2.2.7版本开始,默认构建会启用device-link,@see https://devblogs.nvidia.com/separate-compilation-linking-cuda-device-code/ +如果要显示禁用device-link,可以通过`add_values("cuda.devlink", false)` 来设置。 +
+ +默认会自动探测cuda环境,当然也可以指定Cuda SDK环境目录: + +```console +$ xmake f --cuda=/usr/local/cuda-9.1/ +$ xmake +``` + +更多详情可以参考:[#158](https://github.com/xmake-io/xmake/issues/158) + +#### WDK驱动程序 + +默认会自动探测wdk所在环境,当然也可以指定wdk sdk环境目录: + +```console +$ xmake f --wdk="G:\Program Files\Windows Kits\10" -c +$ xmake +``` + +更多详情可以参考:[#159](https://github.com/xmake-io/xmake/issues/159) + +##### umdf驱动程序 + +```lua +target("echo") + add_rules("wdk.driver", "wdk.env.umdf") + add_files("driver/*.c") + add_files("driver/*.inx") + add_includedirs("exe") + +target("app") + add_rules("wdk.binary", "wdk.env.umdf") + add_files("exe/*.cpp") +``` + +##### kmdf驱动程序 + +```lua +target("nonpnp") + add_rules("wdk.driver", "wdk.env.kmdf") + add_values("wdk.tracewpp.flags", "-func:TraceEvents(LEVEL,FLAGS,MSG,...)", "-func:Hexdump((LEVEL,FLAGS,MSG,...))") + add_files("driver/*.c", {rule = "wdk.tracewpp"}) + add_files("driver/*.rc") + +target("app") + add_rules("wdk.binary", "wdk.env.kmdf") + add_files("exe/*.c") + add_files("exe/*.inf") +``` + +##### wdm驱动程序 + +```lua +target("kcs") + add_rules("wdk.driver", "wdk.env.wdm") + add_values("wdk.man.flags", "-prefix Kcs") + add_values("wdk.man.resource", "kcsCounters.rc") + add_values("wdk.man.header", "kcsCounters.h") + add_values("wdk.man.counter_header", "kcsCounters_counters.h") + add_files("*.c", "*.rc", "*.man") +``` + +```lua +target("msdsm") + add_rules("wdk.driver", "wdk.env.wdm") + add_values("wdk.tracewpp.flags", "-func:TracePrint((LEVEL,FLAGS,MSG,...))") + add_files("*.c", {rule = "wdk.tracewpp"}) + add_files("*.rc", "*.inf") + add_files("*.mof|msdsm.mof") + add_files("msdsm.mof", {values = {wdk_mof_header = "msdsmwmi.h"}}) +``` + +##### 生成驱动包 + +可以通过以下命令生成.cab驱动包: + +```console +$ xmake [p|package] +$ xmake [p|package] -o outputdir +``` + +输出的目录结构如下: + +``` + - drivers + - sampledsm + - debug/x86/sampledsm.cab + - release/x64/sampledsm.cab + - debug/x86/sampledsm.cab + - release/x64/sampledsm.cab +``` + +##### 驱动签名 + +默认编译禁用签名,可以通过`set_values("wdk.sign.mode", ...)`设置签名模式来启用签名。 + +###### 测试签名 + +测试签名一般本机调试时候用,可以使用xmake自带的test证书来进行签名,例如: + +```lua +target("msdsm") + add_rules("wdk.driver", "wdk.env.wdm") + set_values("wdk.sign.mode", "test") +``` + +不过这种情况下,需要用户手动在管理员模式下,执行一遍:`$xmake l utils.wdk.testcert install`,来生成和注册test证书到本机环境。 +这个只需要执行一次就行了,后续就可以正常编译和签名了。 + +当然也可以使用本机已有的有效证书去签名。 + +从sha1来选择合适的证书进行签名: + +```lua +target("msdsm") + add_rules("wdk.driver", "wdk.env.wdm") + set_values("wdk.sign.mode", "test") + set_values("wdk.sign.thumbprint", "032122545DCAA6167B1ADBE5F7FDF07AE2234AAA") +``` + +从store/company来选择合适的证书进行签名: + +```lua +target("msdsm") + add_rules("wdk.driver", "wdk.env.wdm") + set_values("wdk.sign.mode", "test") + set_values("wdk.sign.store", "PrivateCertStore") + set_values("wdk.sign.company", "tboox.org(test)") +``` + +###### 正式签名 + +通过指定对应的正式签名证书文件进行签名: + +```lua +target("msdsm") + add_rules("wdk.driver", "wdk.env.wdm") + set_values("wdk.sign.mode", "release") + set_values("wdk.sign.company", "xxxx") + set_values("wdk.sign.certfile", path.join(os.projectdir(), "xxxx.cer")) +``` + +##### 生成低版本驱动 + +如果想在wdk10环境编译生成win7, win8等低版本系统支持的驱动,可以通过设置`wdk.env.winver`来切换系统版本: + +```lua +set_values("wdk.env.winver", "win10") +set_values("wdk.env.winver", "win10_rs3") +set_values("wdk.env.winver", "win81") +set_values("wdk.env.winver", "win8") +set_values("wdk.env.winver", "win7") +set_values("wdk.env.winver", "win7_sp1") +set_values("wdk.env.winver", "win7_sp2") +set_values("wdk.env.winver", "win7_sp3") +``` + +我们也可以手动指定编译的目标程序支持的windows版本: + +```console +$ xmake f --wdk_winver=[win10_rs3|win8|win7|win7_sp1] +$ xmake +``` + +#### WinSDK程序 + +```lua +target("usbview") + add_rules("win.sdk.application") + + add_files("*.c", "*.rc") + add_files("xmlhelper.cpp", {rule = "win.sdk.dotnet"}) +``` + +更多详情可以参考:[#173](https://github.com/xmake-io/xmake/issues/173) + +## 编译配置 + +通过`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中具体某个工具链,而不是使用默认检测的配置,可以通过[--bin](#-bin)来设置,例如: + +```bash +$ xmake f -p android --ndk=~/files/android-ndk-r10e/ -a arm64-v8a --bin=~/files/android-ndk-r10e/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin +``` + +[--bin](#-bin)主要用于设置选择编译工具的具体bin目录,这个的使用跟[交叉编译](#交叉编译)中的[--bin](#-bin)的行为是一致的。 + ++如果手动设置了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/ [--bin=/sdk/bin] [--cross=arm-linux-] +$ xmake +``` + +其他平台的交叉编译: + +```bash +$ xmake f -p cross --sdk=/usr/local/arm-xxx-gcc/ [--bin=/sdk/bin] [--cross=arm-linux-] +$ xmake +``` + +如果不关心实际的平台名,只想交叉编译,可以直接用上面的命令,如果需要通过`is_plat("myplat")`判断自己的平台逻辑,则: + +```bash +$ xmake f -p myplat --sdk=/usr/local/arm-xxx-gcc/ [--bin=/sdk/bin] [--cross=arm-linux-] +$ xmake +``` + +其中: + +| 参数名 | 描述 | +| ---------------------------- | -------------------------------- | +| [--sdk](#-sdk) | 设置交叉工具链的sdk根目录 | +| [--bin](#-bin) | 设置工具链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`编译器 | +| [--cu](#-cu) | 设置`cuda`编译器 | +| [--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`静态库归档器 | +| [--cu-cxx](#-cu-cxx) | 设置`cuda` host编译器 | +| [--cu-ld](#-cu-ld) | 设置`cuda`链接器 | +| [--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`编译选项 | +| [--cuflags](#-cuflags) | 设置`cuda`编译选项 | +| [--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自动处理的,不需要手动配置他们。。 + +###### --bin + +- 设置工具链bin目录 + +对于不规则工具链目录结构,靠单纯地[--sdk](#-sdk)选项设置,没法完全检测通过的情况下,可以通过这个选项继续附加设置工具链的bin目录位置。 + +例如:一些特殊的交叉工具链的,编译器bin目录,并不在 `/home/toolchains_sdkdir/bin` 这个位置,而是独立到了 `/usr/opt/bin` + +```bash +$ xmake f -p linux --sdk=/home/toolchains_sdkdir --bin=/usr/opt/bin +$ xmake +``` + ++v2.2.1版本之前,这个参数名是`--toolchains`,比较有歧义,因此新版本中,统一改成`--bin=`来设置bin目录。 +
+ +###### --cross + +- 设置交叉工具链工具前缀 + +像`aarch64-linux-android-`这种,通常如果你配置了[--sdk](#-sdk)或者[--bin](#-bin)的情况下,xmake会去自动检测的,不需要自己手动设置。 + +但是对于一些极特殊的工具链,一个目录下同时有多个cross前缀的工具bin混在一起的情况,你需要手动设置这个配置,来区分到底需要选用哪个bin。 + +例如,toolchains的bin目录下同时存在两个不同的编译器: + +``` +/opt/bin + - armv7-linux-gcc + - aarch64-linux-gcc +``` + +我们现在想要选用armv7的版本,则配置如下: + +```bash +$ xmake f -p linux --sdk=/usr/toolsdk --bin=/opt/bin --cross=armv7-linux- +``` + +###### --as + +- 设置`asm`汇编器 + +如果还要继续细分选择编译器,则继续追加相关编译器选项,例如: + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --as=armv7-linux-as +``` + +如果存在`AS`环境变量的话,会优先使用当前环境变量中指定的值。 + ++如果指定的编译器名不是那些xmake内置可识别的名字(带有gcc, clang等字样),那么编译器工具检测就会失败。 +这个时候我们可以通过:`xmake f --as=gcc@/home/xxx/asmips.exe` 设置ccmips.exe编译器作为类gcc的使用方式来编译。 +也就是说,在指定编译器为`asmips.exe`的同时,告诉xmake,它跟gcc用法和参数选项基本相同。 +
+ +###### --cc + +- 设置c编译器 + +如果还要继续细分选择编译器,则继续追加相关编译器选项,例如: + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --cc=armv7-linux-clang +``` + +如果存在`CC`环境变量的话,会优先使用当前环境变量中指定的值。 + ++如果指定的编译器名不是那些xmake内置可识别的名字(带有gcc, clang等字样),那么编译器工具检测就会失败。 +这个时候我们可以通过:`xmake f --cc=gcc@/home/xxx/ccmips.exe` 设置ccmips.exe编译器作为类gcc的使用方式来编译。 +也就是说,在指定编译器为`ccmips.exe`的同时,告诉xmake,它跟gcc用法和参数选项基本相同。 +
+ +###### --cxx + +- 设置`c++`编译器 + +如果还要继续细分选择编译器,则继续追加相关编译器选项,例如: + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --cxx=armv7-linux-clang++ +``` + +如果存在`CXX`环境变量的话,会优先使用当前环境变量中指定的值。 + ++如果指定的编译器名不是那些xmake内置可识别的名字(带有gcc, clang等字样),那么编译器工具检测就会失败。 +这个时候我们可以通过:`xmake f --cxx=clang++@/home/xxx/c++mips.exe` 设置c++mips.exe编译器作为类clang++的使用方式来编译。 +也就是说,在指定编译器为`c++mips.exe`的同时,告诉xmake,它跟clang++用法和参数选项基本相同。 +
+ +###### --ld + +- 设置`c/c++/objc/asm`链接器 + +如果还要继续细分选择链接器,则继续追加相关编译器选项,例如: + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --ld=armv7-linux-clang++ +``` + +如果存在`LD`环境变量的话,会优先使用当前环境变量中指定的值。 + ++如果指定的编译器名不是那些xmake内置可识别的名字(带有gcc, clang等字样),那么链接器工具检测就会失败。 +这个时候我们可以通过:`xmake f --ld=g++@/home/xxx/c++mips.exe` 设置c++mips.exe链接器作为类g++的使用方式来编译。 +也就是说,在指定链接器为`c++mips.exe`的同时,告诉xmake,它跟g++用法和参数选项基本相同。 +
+ +###### --sh + +- 设置`c/c++/objc/asm`共享库链接器 + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --sh=armv7-linux-clang++ +``` + +如果存在`SH`环境变量的话,会优先使用当前环境变量中指定的值。 + ++如果指定的编译器名不是那些xmake内置可识别的名字(带有gcc, clang等字样),那么链接器工具检测就会失败。 +这个时候我们可以通过:`xmake f --sh=g++@/home/xxx/c++mips.exe` 设置c++mips.exe链接器作为类g++的使用方式来编译。 +也就是说,在指定链接器为`c++mips.exe`的同时,告诉xmake,它跟g++用法和参数选项基本相同。 +
+ +###### --ar + +- 设置`c/c++/objc/asm`静态库归档器 + +```bash +$ xmake f -p linux --sdk=/user/toolsdk --ar=armv7-linux-ar +``` + +如果存在`AR`环境变量的话,会优先使用当前环境变量中指定的值。 + ++如果指定的编译器名不是那些xmake内置可识别的名字(带有ar等字样),那么链接器工具检测就会失败。 +这个时候我们可以通过:`xmake f --ar=ar@/home/xxx/armips.exe` 设置armips.exe链接器作为类ar的使用方式来编译。 +也就是说,在指定链接器为`armips.exe`的同时,告诉xmake,它跟ar用法和参数选项基本相同。 +
+ +#### 全局配置 + +我们也可以将一些常用配置保存到全局配置中,来简化频繁地输入: + +例如: + +```bash +$ xmake g --ndk=~/files/android-ndk-r10e/ +``` + +现在,我们重新配置和编译`android`程序: + +```bash +$ xmake f -p android +$ xmake +``` + +以后,就不需要每次重复配置`--ndk=`参数了。 + +
+ 每个命令都有其简写,例如: `xmake g` 或者 `xmake global`.
+
+
+关于包依赖管理的更多相关信息和进展见相关issues:[Remote package management](https://github.com/xmake-io/xmake/issues/69)
+
+##### 目前支持的特性
+
+* 语义版本支持,例如:">= 1.1.0 < 1.2", "~1.6", "1.2.x", "1.*"
+* 提供官方包仓库、自建私有仓库、项目内置仓库等多仓库管理支持
+* 跨平台包编译集成支持(不同平台、不同架构的包可同时安装,快速切换使用)
+* debug依赖包支持,实现源码调试
+
+##### 依赖包处理机制
+
+这里我们简单介绍下整个依赖包的处理机制:
+
+
+
+1. 优先检测当前系统目录、第三方包管理下有没有存在指定的包,如果有匹配的包,那么就不需要下载安装了 (当然也可以设置不使用系统包)
+2. 检索匹配对应版本的包,然后下载、编译、安装(注:安装在特定xmake目录,不会干扰系统库环境)
+3. 编译项目,最后自动链接启用的依赖包
+
+##### 快速上手
+
+新建一个依赖tbox库的空工程:
+
+```console
+$ xmake create -t console_tbox test
+$ cd test
+```
+
+执行编译即可,如果当前没有安装tbox库,则会自动下载安装后使用:
+
+```console
+$ xmake
+```
+
+切换到iphoneos平台进行编译,将会重新安装iphoneos版本的tbox库进行链接使用:
+
+```console
+$ xmake f -p iphoneos
+$ xmake
+```
+
+切换到android平台arm64-v8a架构编译:
+
+```console
+$ xmake f -p android [--ndk=~/android-ndk-r16b]
+$ xmake
+```
+
+##### 语义版本设置
+
+xmake的依赖包管理是完全支持语义版本选择的,例如:"~1.6.1",对于语义版本的具体描述见:[https://semver.org/](https://semver.org/)
+
+一些语义版本写法:
+
+```lua
+add_requires("tbox 1.6.*", "pcre 1.3.x", "libpng ^1.18")
+add_requires("libpng ~1.16", "zlib 1.1.2 || >=1.2.11 <1.3.0")
+```
+
+目前xmake使用的语义版本解析器是[uael](https://github.com/uael)贡献的[sv](https://github.com/uael/sv)库,里面也有对版本描述写法的详细说明,可以参考下:[版本描述说明](https://github.com/uael/sv#versions)
+
+当然,如果我们对当前的依赖包的版本没有特殊要求,那么可以直接这么写:
+
+```lua
+add_requires("tbox", "libpng", "zlib")
+```
+
+这会使用已知的最新版本包,或者是master分支的源码编译的包,如果当前包有git repo地址,我们也能指定特定分支版本:
+
+```lua
+add_requires("tbox master")
+add_requires("tbox dev")
+```
+
+##### 额外的包信息设置
+
+###### 可选包设置
+
+如果指定的依赖包当前平台不支持,或者编译安装失败了,那么xmake会编译报错,这对于有些必须要依赖某些包才能工作的项目,这是合理的。
+但是如果有些包是可选的依赖,即使没有也可以正常编译使用的话,可以设置为可选包:
+
+```lua
+add_requires("tbox", {optional = true})
+```
+
+###### 禁用系统库
+
+默认的设置,xmake会去优先检测系统库是否存在(如果没设置版本要求),如果用户完全不想使用系统库以及第三方包管理提供的库,那么可以设置:
+
+```lua
+add_requires("tbox", {system = false})
+```
+
+###### 使用调试版本的包
+
+如果我们想同时源码调试依赖包,那么可以设置为使用debug版本的包(当然前提是这个包支持debug编译):
+
+```lua
+add_requires("tbox", {debug = true})
+```
+
+如果当前包还不支持debug编译,可在仓库中提交修改编译规则,对debug进行支持,例如:
+
+```lua
+package("openssl")
+ on_install("linux", "macosx", function (package)
+ os.vrun("./config %s --prefix=\"%s\"", package:debug() and "--debug" or "", package:installdir())
+ os.vrun("make -j4")
+ os.vrun("make install")
+ end)
+```
+
+###### 传递额外的编译信息到包
+
+某些包在编译时候有各种编译选项,我们也可以传递进来,当然包本身得支持:
+
+```lua
+add_requires("tbox", {configs = {small=true}})
+```
+
+传递`--small=true`给tbox包,使得编译安装的tbox包是启用此选项的。
+
+##### 第三方依赖包安装
+
+2.2.5版本之后,xmake支持对对第三方包管理器里面的依赖库安装支持,例如:conan,brew, vcpkg等
+
+添加homebrew的依赖包:
+
+```lua
+add_requires("brew::zlib", {alias = "zlib"}})
+add_requires("brew::pcre2/libpcre2-8", {alias = "pcre2"}})
+
+target("test")
+ set_kind("binary")
+ add_files("src/*.c")
+ add_packages("pcre2", "zlib")
+```
+
+添加vcpkg的依赖包:
+
+```lua
+add_requires("vcpkg::zlib", "vcpkg::pcre2")
+
+target("test")
+ set_kind("binary")
+ add_files("src/*.c")
+ add_packages("vcpkg::zlib", "vcpkg::pcre2")
+```
+
+添加conan的依赖包:
+
+```lua
+add_requires("CONAN::zlib/1.2.11@conan/stable", {alias = "zlib", debug = true})
+add_requires("CONAN::OpenSSL/1.0.2n@conan/stable", {alias = "openssl",
+ configs = {options = "OpenSSL:shared=True"}})
+
+target("test")
+ set_kind("binary")
+ add_files("src/*.c")
+ add_packages("openssl", "zlib")
+```
+
+执行xmake进行编译后:
+
+```console
+ruki:test_package ruki$ xmake
+checking for the architecture ... x86_64
+checking for the Xcode directory ... /Applications/Xcode.app
+checking for the SDK version of Xcode ... 10.14
+note: try installing these packages (pass -y to skip confirm)?
+ -> CONAN::zlib/1.2.11@conan/stable (debug)
+ -> CONAN::OpenSSL/1.0.2n@conan/stable
+please input: y (y/n)
+
+ => installing CONAN::zlib/1.2.11@conan/stable .. ok
+ => installing CONAN::OpenSSL/1.0.2n@conan/stable .. ok
+
+[ 0%]: ccache compiling.release src/main.c
+[100%]: linking.release test
+```
+
+##### 使用自建私有包仓库
+
+如果需要的包不在官方仓库[xmake-repo](https://github.com/xmake-io/xmake-repo)中,我们可以提交贡献代码到仓库进行支持。
+但如果有些包仅用于个人或者私有项目,我们可以建立一个私有仓库repo,仓库组织结构可参考:[xmake-repo](https://github.com/xmake-io/xmake-repo)
+
+比如,现在我们有一个一个私有仓库repo:`git@github.com:myrepo/xmake-repo.git`
+
+我们可以通过下面的命令进行仓库添加:
+
+```console
+$ xmake repo --add myrepo git@github.com:myrepo/xmake-repo.git
+```
+
+或者我们直接写在xmake.lua中:
+
+```lua
+add_repositories("my-repo git@github.com:myrepo/xmake-repo.git")
+```
+
+如果我们只是想添加一两个私有包,这个时候特定去建立一个git repo太小题大做了,我们可以直接把包仓库放置项目里面,例如:
+
+```
+projectdir
+ - myrepo
+ - packages
+ - t/tbox/xmake.lua
+ - z/zlib/xmake.lua
+ - src
+ - main.c
+ - xmake.lua
+```
+
+上面myrepo目录就是自己的私有包仓库,内置在自己的项目里面,然后在xmake.lua里面添加一下这个仓库位置:
+
+```lua
+add_repositories("my-repo myrepo")
+```
+
+这个可以参考[benchbox](https://github.com/tboox/benchbox)项目,里面就内置了一个私有仓库。
+
+我们甚至可以连仓库也不用建,直接定义包描述到项目xmake.lua中,这对依赖一两个包的情况还是很有用的,例如:
+
+```lua
+package("libjpeg")
+
+ set_urls("http://www.ijg.org/files/jpegsrc.$(version).tar.gz")
+
+ add_versions("v9c", "650250979303a649e21f87b5ccd02672af1ea6954b911342ea491f351ceb7122")
+
+ on_install("windows", function (package)
+ os.mv("jconfig.vc", "jconfig.h")
+ os.vrun("nmake -f makefile.vc")
+ os.cp("*.h", package:installdir("include"))
+ os.cp("libjpeg.lib", package:installdir("lib"))
+ end)
+
+ on_install("macosx", "linux", function (package)
+ import("package.tools.autoconf").install(package)
+ end)
+
+package_end()
+
+add_requires("libjpeg")
+
+target("test")
+ set_kind("binary")
+ add_files("src/*.c")
+ add_packages("libjpeg")
+```
+
+##### 包管理命令使用
+
+包管理命令`$ xmake require` 可用于手动显示的下载编译安装、卸载、检索、查看包信息。
+
+###### 安装指定包
+
+```console
+$ xmake require tbox
+```
+
+安装指定版本包:
+
+```console
+$ xmake require tbox "~1.6"
+```
+
+强制重新下载安装,并且显示详细安装信息:
+
+```console
+$ xmake require -f -v tbox "1.5.x"
+```
+
+传递额外的设置信息:
+
+```console
+$ xmake require --extra="debug=true,config={small=true}" tbox
+```
+
+安装debug包,并且传递`small=true`的编译配置信息到包中去。
+
+###### 卸载指定包
+
+```console
+$ xmake require --uninstall tbox
+```
+
+这会完全卸载删除包文件。
+
+###### 查看包详细信息
+
+```console
+$ xmake require --info tbox
+```
+
+###### 在当前仓库中搜索包
+
+```console
+$ xmake require --search tbox
+```
+
+这个是支持模糊搜素以及lua模式匹配搜索的:
+
+```console
+$ xmake require --search pcr
+```
+
+会同时搜索到pcre, pcre2等包。
+
+###### 列举当前已安装的包
+
+```console
+$ xmake require --list
+```
+
+##### 仓库管理命令使用
+
+上文已经简单讲过,添加私有仓库可以用(支持本地路径添加):
+
+```console
+$ xmake repo --add myrepo git@github.com:myrepo/xmake-repo.git
+```
+
+v2.2.3开始,支持添加指定分支的repo,例如:
+
+```console
+$ xmake repo --add myrepo git@github.com:myrepo/xmake-repo.git dev
+```
+
+我们也可以移除已安装的某个仓库:
+
+```console
+$ xmake repo --remove myrepo
+```
+
+或者查看所有已添加的仓库:
+
+```console
+$ xmake repo --list
+```
+
+如果远程仓库有更新,可以手动执行仓库更新,来获取更多、最新的包:
+
+```console
+$ xmake repo -u
+```
+
+##### 提交包到官方仓库
+
+目前这个特性刚完成不久,目前官方仓库的包还不是很多,有些包也许还不支持部分平台,不过这并不是太大问题,后期迭代几个版本后,我会不断扩充完善包仓库。
+
+如果你需要的包,当前的官方仓库还没有收录,可以提交issues或者自己可以在本地调通后,贡献提交到官方仓库:[xmake-repo](https://github.com/xmake-io/xmake-repo)
+
+详细的贡献说明,见:[CONTRIBUTING.md](https://github.com/xmake-io/xmake-repo/blob/master/CONTRIBUTING.md)
+
+## 问答
+
+#### 怎样获取更多参数选项信息?
+
+获取主菜单的帮助信息,里面有所有action和plugin的列表描述。
+
+```bash
+$ xmake [-h|--help]
+```
+
+获取配置菜单的帮助信息,里面有所有配置选项的描述信息,以及支持平台、架构列表。
+
+```bash
+$ xmake f [-h|--help]
+```
+
+获取action和plugin命令菜单的帮助信息,里面有所有内置命令和插件任务的参数使用信息。
+
+```bash
+$ xmake [action|plugin] [-h|--help]
+```
+
+例如,获取`run`命令的参数信息:
+
+```bash
+$ xmake run --help
+```
+
+#### 怎样实现静默构建,不输出任何信息?
+
+```bash
+$ xmake [-q|--quiet]
+```
+
+#### 如果xmake运行失败了怎么办?
+
+可以先尝试清除下配置,重新构建下:
+
+```bash
+$ xmake f -c
+$ xmake
+```
+
+如果还是失败了,请加上 `-v` 或者 `--verbose` 选项重新执行xmake后,获取更加详细的输出信息
+
+例如:
+
+```hash
+$ xmake [-v|--verbose]
+```
+
+并且可以加上 `--backtrace` 选项获取出错时的xmake的调试栈信息, 然后你可以提交这些信息到[issues](https://github.com/xmake-io/xmake/issues).
+
+```bash
+$ xmake -v --backtrace
+```
+
+#### 怎样看实时编译警告信息?
+
+为了避免刷屏,在构建时候,默认是不实时输出警告信息的,如果想要看的话可以加上`-w`选项启用编译警告输出就行了。
+
+```bash
+$ xmake [-w|--warning]
+```
+
+#### 怎样基于源码自动生成xmake.lua
+
+如果你想临时写一两个测试代码、或者手上有一些移植过来的零散源码想要快速编译运行,可以不用专门xmake.lua,直接运行:
+
+```bash
+$ xmake
+```
+
+xmake会自动扫描分析当前的源码目录,识别程序结构和类型,生成一个xmake.lua,并且会尝试直接构建它。
+
+如果编译成功,可以直接运行:
+
+```bash
+$ xmake run
+```
+
+当然,如果仅仅只是想要生成xmake.lua,默认不去构建,可以执行:
+
+```bash
+$ xmake f -y
+```
+
+更多相关介绍,请参考文章:[xmake新增智能代码扫描编译模式,无需手写任何make文件](https://tboox.org/cn/2017/01/07/build-without-makefile/)
+
+## 支持项目
+
+xmake项目属于个人开源项目,它的发展需要您的帮助,如果您愿意支持xmake项目的开发,欢迎为其捐赠,支持它的发展。 🙏 [[支持此项目](https://opencollective.com/xmake#backer)]
+
++此接口在2.2.2版本之后已经弃用,请使用[has_config](#has_config)来代替。 +
+ +用于检测自定义的编译配置选型:`xmake f --xxxx=y` + +如果某个自动检测选项、手动设置选项被启用,那么可以通过`is_option`接口来判断,例如: + +```lua +-- 如果手动启用了xmake f --demo=y 选项 +if is_option("demo") then + + -- 编译demo目录下的代码 + add_subdirs("src/demo") +end +``` + +##### is_config + +###### 判断指定配置是否为给定的值 + +此接口从2.2.2版本开始引入,用于判断指定配置是否为给定的值,可用于描述域。 + +例如: + +```console +$ xmake f --test=hello1 +``` + +```lua +-- 自定义一个配置选项到命令行菜单 +option("test") + set_showmenu("true") + set_description("The test config option") +option_end() + +-- 如果自定义的test配置值是hello1或者hello2 +if is_config("test", "hello1", "hello2") then + add_defines("HELLO") +end +``` + +不仅如此,我们还可以设置模式匹配规则去判断值,例如: + +```lua +-- 如果自定义的test配置值带有hello前缀 +if is_config("test", "hello.*") then + add_defines("HELLO") +end +``` + ++此接口不仅能够判断通过[option](#option)定义的自定义配置选项,同时还能判断内置的全局配置、本地配置。 +
+ +##### has_config + +###### 判断配置是否启用或者存在 + +此接口从2.2.2版本开始引入,用于检测自定义或者内置的编译配置是否存在或启用,可用于描述域。 + +例如以下配置情况,都会返回true: + +```console +# 启用某个配置选项(如果是boolean类型配置) +$ xmake f --test1=y +$ xmake f --test1=yes +$ xmake f --test1=true + +# 设置某个配置选项的值 +$ xmake f --test2=value +``` + +```lua +-- 如果test1或者test2被设置或者启用 +if has_config("test1", "test2") then + add_defines("TEST") +end +``` + +而下面的情况则会禁用配置,返回false: + +```console +# 禁用配置(如果是boolean类型配置) +$ xmake f --test1=n +$ xmake f --test1=no +$ xmake f --test1=false +``` + ++此接口不仅能够判断内置的全局配置、本地配置,同时还可以判断通过[option](#option)定义的自定义配置选项。 +
+ + +##### has_package + +###### 判断依赖包是否启用或者存在 + +此接口从2.2.3版本开始引入,用于检测远程依赖包是否存在或启用,可用于描述域。 + +一般配合[add_requires](#add_requires)一起使用,例如: + +```lua +add_requires("tbox", {optional = true}) + +target("test") + set_kind("binary") + add_files("src/*.c") + add_packages("tbox") + + if has_package("tbox") then + add_defines("HAVE_TBOX") + end +``` + +如果通过`add_requires`添加的可选依赖包,远程下载安装失败,或者当前平台不支持导致实际上没有被正常安装上,那么`has_package`就会返回false, +表示不存在,然后对其他flags定义甚至源文件编译控制做一些特殊处理。 + ++此接口跟[has_config](#has_config)的区别在于,[has_config](#has_config)用于[option](#option),而它用于[add_requires](#add_requires)。 +
+ +#### 全局接口 + +全局接口影响整个工程描述,被调用后,后面被包含进来的所有子`xmake.lua`都会受影响。 + +| 接口 | 描述 | 支持版本 | +| ------------------------------------- | ----------------------------- | -------- | +| [includes](#includes) | 添加子工程文件和目录 | >= 2.1.5 | +| [set_modes](#set_modes) | 设置支持的编译模式 | >= 2.1.2 | +| [set_project](#set_project) | 设置工程名 | >= 2.0.1 | +| [set_version](#set_version) | 设置工程版本 | >= 2.0.1 | +| [set_xmakever](#set_xmakever) | 设置最小xmake版本 | >= 2.1.1 | +| [add_subdirs](#add_subdirs) | 添加子工程目录 | >= 1.0.1 | +| [add_subfiles](#add_subfiles) | 添加子工程文件 | >= 1.0.1 | +| [add_moduledirs](#add_moduledirs) | 添加模块目录 | >= 2.1.5 | +| [add_plugindirs](#add_plugindirs) | 添加插件目录 | >= 2.0.1 | +| [add_packagedirs](#add_packagedirs) | 添加包目录 | >= 2.0.1 | +| [get_config](#get_config) | 获取给的配置值 | >= 2.2.2 | +| [set_config](#set_config) | 设置默认的配置值 | >= 2.2.2 | +| [add_requires](#add_requires) | 添加需要的依赖包 | >= 2.2.2 | +| [add_repositories](#add_repositories) | 添加依赖包仓库 | >= 2.2.2 | + +##### includes + +###### 添加子工程文件和目录 + +同时支持子工程文件和目录的添加,用于替代[add_subdirs](#add_subdirs)和[add_subfiles](#add_subfiles)接口。 + +另外,此接口在2.2.5之后的版本,提供了一些内置的辅助函数,可以直接includes后使用,具体有哪些内置函数可以看下:https://github.com/xmake-io/xmake/tree/master/xmake/includes + +关于这块的更加完整的说明,可以看下:[https://github.com/xmake-io/xmake/issues/342](https://github.com/xmake-io/xmake/issues/342) + +##### set_modes + +###### 设置支持的编译模式 + +这个是可选接口,一般情况下不需要设置,目前仅用于对工程增加更加细致的描述信息,方便vs工程的多模式生成,以及其他xmake插件中获取模式信息。 + +例如: + +```lua +set_modes("debug", "release") +``` + +如果设置了这个,xmake就知道当前工程支持哪些编译模式,这样生成vs工程文件的时候,只需要: + +```bash +$ xmake project -k vs2017 +``` + +不再需要额外手动指定需要的编译模式了,此外其他一些想要获取工程信息的插件,也许也会需要这些设置信息。 + ++当然,对于[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 2.x以上版本,请尽量使用[includes](#includes)这个接口,这个是add_subdirs和add_subfiles的通用版本,并且支持一些内建扩展模块。 +
+ +每个子工程对应一个`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 + +###### 添加子工程文件 + ++xmake 2.x以上版本,请尽量使用[includes](#includes)这个接口,这个是add_subdirs和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工程为例,其依赖包如下: + +``` +- base.pkg +- zlib.pkg +- polarssl.pkg +- openssl.pkg +- mysql.pkg +- pcre.pkg +- ... +``` + +如果要让当前工程识别加载这些包,首先要指定包目录路径,例如: + +```lua +add_packagedirs("packages") +``` + +指定好后,就可以在target作用域中,通过[add_packages](#add_packages)接口,来添加集成包依赖了,例如: + +```lua +target("tbox") + add_packages("zlib", "polarssl", "pcre", "mysql") +``` + +##### get_config + +###### 获取给定的配置值 + +此接口从2.2.2版本开始引入,用于快速获取给定的配置值,可用于描述域。 + +```lua +if get_config("myconfig") == "xxx" then + add_defines("HELLO") +end +``` + +##### set_config + +###### 设置给定的默认配置值 + +此接口从2.2.2版本开始引入,用于快速在xmake.lua中设置一个默认配置值,仅用于描述域。 + +之前很多配置,包括编译工具链,构建目录等只能通过`$ xmake f --name=value`的方式来配置,如果我们想写死在xmake.lua提供一个默认值,就可以通过下面的方式来配置: + +```lua +set_config("name", "value") +set_config("buildir", "other/buildir") +set_config("cc", "gcc") +set_config("ld", "g++") +``` + +不过,我们还是可以通过`$ xmake f --name=value`的方式,去修改xmake.lua中的默认配置。 + +##### add_requires + +###### 添加需要的依赖包 + +xmake的依赖包管理是完全支持语义版本选择的,例如:"~1.6.1",对于语义版本的具体描述见:[https://semver.org/](https://semver.org/) + +一些语义版本写法: + +```lua +add_requires("tbox 1.6.*", "pcre 1.3.x", "libpng ^1.18") +add_requires("libpng ~1.16", "zlib 1.1.2 || >=1.2.11 <1.3.0") +``` + +目前xmake使用的语义版本解析器是[uael](https://github.com/uael)贡献的[sv](https://github.com/uael/sv)库,里面也有对版本描述写法的详细说明,可以参考下:[版本描述说明](https://github.com/uael/sv#versions) + +当然,如果我们对当前的依赖包的版本没有特殊要求,那么可以直接这么写: + +```lua +add_requires("tbox", "libpng", "zlib") +``` + +这会使用已知的最新版本包,或者是master分支的源码编译的包,如果当前包有git repo地址,我们也能指定特定分支版本: + +```lua +add_requires("tbox master") +add_requires("tbox dev") +``` + +如果指定的依赖包当前平台不支持,或者编译安装失败了,那么xmake会编译报错,这对于有些必须要依赖某些包才能工作的项目,这是合理的。 +但是如果有些包是可选的依赖,即使没有也可以正常编译使用的话,可以设置为可选包: + +```lua +add_requires("tbox", {optional = true}) +``` + +默认的设置,xmake会去优先检测系统库是否存在(如果没设置版本要求),如果用户完全不想使用系统库以及第三方包管理提供的库,那么可以设置: + +```lua +add_requires("tbox", {system = false}) +``` + +如果我们想同时源码调试依赖包,那么可以设置为使用debug版本的包(当然前提是这个包支持debug编译): + +```lua +add_requires("tbox", {debug = true}) +``` + +如果当前包还不支持debug编译,可在仓库中提交修改编译规则,对debug进行支持,例如: + +```lua +package("openssl") + on_install("linux", "macosx", function (package) + os.vrun("./config %s --prefix=\"%s\"", package:debug() and "--debug" or "", package:installdir()) + os.vrun("make -j4") + os.vrun("make install") + end) +``` + +某些包在编译时候有各种编译选项,我们也可以传递进来,当然包本身得支持: + +```lua +add_requires("tbox", {config = {small=true}}) +``` + +传递`--small=true`给tbox包,使得编译安装的tbox包是启用此选项的。 + +v2.2.3之后,可以通过[option](#option)和[has_config](#has_config)配合,在自己定义配置选项参数中控制是否需要添加某个依赖包: + +```lua +option("luajit") + set_default(false) + set_showmenu(true) + set_category("option") + set_description("Enable the luajit runtime engine.") +option_end() + +if has_config("luajit") then + add_requires("luajit") +else + add_requires("lua") +end +``` + +我们可以通过`$xmake f --luajit=y`去切换依赖包。 + +并且我们也新增了group参数,来分组依赖包,同一个组下的所有依赖包,只能有一个生效启用,启用顺序依赖`add_requires`添加的顺序: + +```lua +add_requires("openssl", {group = "ssl", optional = true}) +add_requires("mbedtls", {group = "ssl", optional = true}) + +target("test") + add_packages("openssl", "mbedtls") +``` + +例如上面,所以同时依赖两个ssl包,实际上只会启用生效实际安装成功的那一个ssl包,并不会同时链接两个依赖包。 + +2.2.5版本之后,xmake支持对对第三方包管理器里面的依赖库安装支持,例如:conan,brew, vcpkg等 + +添加homebrew的依赖包: + +```lua +add_requires("brew::zlib", {alias = "zlib"}}) +add_requires("brew::pcre2/libpcre2-8", {alias = "pcre2"}}) + +target("test") + set_kind("binary") + add_files("src/*.c") + add_packages("pcre2", "zlib") +``` + +添加vcpkg的依赖包: + +```lua +add_requires("vcpkg::zlib", "vcpkg::pcre2") + +target("test") + set_kind("binary") + add_files("src/*.c") + add_packages("vcpkg::zlib", "vcpkg::pcre2") +``` + +添加conan的依赖包: + +```lua +add_requires("CONAN::zlib/1.2.11@conan/stable", {alias = "zlib", debug = true}) +add_requires("CONAN::OpenSSL/1.0.2n@conan/stable", {alias = "openssl", + configs = {options = "OpenSSL:shared=True"}}) + +target("test") + set_kind("binary") + add_files("src/*.c") + add_packages("openssl", "zlib") +``` + +执行xmake进行编译后: + +```console +ruki:test_package ruki$ xmake +checking for the architecture ... x86_64 +checking for the Xcode directory ... /Applications/Xcode.app +checking for the SDK version of Xcode ... 10.14 +note: try installing these packages (pass -y to skip confirm)? + -> CONAN::zlib/1.2.11@conan/stable (debug) + -> CONAN::OpenSSL/1.0.2n@conan/stable +please input: y (y/n) + + => installing CONAN::zlib/1.2.11@conan/stable .. ok + => installing CONAN::OpenSSL/1.0.2n@conan/stable .. ok + +[ 0%]: ccache compiling.release src/main.c +[100%]: linking.release test +``` + +关于这块的更多详情见:https://github.com/xmake-io/xmake/issues/339 + +添加clib的依赖包: + +clib是一款基于源码的依赖包管理器,拉取的依赖包是直接下载对应的库源码,集成到项目中编译,而不是二进制库依赖。 + +其在xmake中集成也很方便,唯一需要注意的是,还需要自己添加上对应库的源码到xmake.lua,例如: + +```lua +add_requires("clib::clibs/bytes@0.0.4", {alias = "bytes"}) + +target("xmake-test") + set_kind("binary") + add_files("clib/bytes/*.c") + add_files("src/*.c") + add_packages("bytes") +``` + +##### add_repositories + +###### 添加依赖包仓库 + +如果需要的包不在官方仓库[xmake-repo](https://github.com/xmake-io/xmake-repo)中,我们可以提交贡献代码到仓库进行支持。 +但如果有些包仅用于个人或者私有项目,我们可以建立一个私有仓库repo,仓库组织结构可参考:[xmake-repo](https://github.com/xmake-io/xmake-repo) + +比如,现在我们有一个一个私有仓库repo:`git@github.com:myrepo/xmake-repo.git` + +我们可以通过此接口来添加: + +```lua +add_repositories("my-repo git@github.com:myrepo/xmake-repo.git") +``` + +如果我们只是想添加一两个私有包,这个时候特定去建立一个git repo太小题大做了,我们可以直接把包仓库放置项目里面,例如: + +``` +projectdir + - myrepo + - packages + - t/tbox/xmake.lua + - z/zlib/xmake.lua + - src + - main.c + - xmake.lua +``` + +上面myrepo目录就是自己的私有包仓库,内置在自己的项目里面,然后在xmake.lua里面添加一下这个仓库位置: + +```lua +add_repositories("my-repo myrepo") +``` + +这个可以参考[benchbox](https://github.com/tboox/benchbox)项目,里面就内置了一个私有仓库。 + +#### 工程目标 + +定义和设置子工程模块,每个`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_enabled](#targetset_enabled) | 设置是否启用或禁用目标 | >= 2.2.2 | +| [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_filename](#targetset_filename) | 设置目标文件全名 | >= 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 < 2.2.5 已废弃 | +| [set_targetdir](#targetset_targetdir) | 设置生成目标文件目录 | >= 1.0.1 | +| [set_objectdir](#targetset_objectdir) | 设置对象文件生成目录 | >= 1.0.1 | +| [set_dependir](#targetset_dependir) | 设置依赖文件生成目录 | >= 2.2.2 | +| [add_imports](#targetadd_imports) | 为所有自定义脚本预先导入扩展模块 | >= 2.1.7 | +| [add_rules](#targetadd_rules) | 添加规则到目标 | >= 2.1.9 | +| [on_load](#targeton_load) | 自定义目标加载脚本 | >= 2.1.5 | +| [on_link](#targeton_link) | 自定义链接脚本 | >= 2.2.7 | +| [on_build](#targeton_build) | 自定义编译脚本 | >= 2.0.1 | +| [on_build_file](#targeton_build_file) | 自定义编译脚本, 实现单文件构建 | >= 2.2.3 | +| [on_build_files](#targeton_build_files) | 自定义编译脚本, 实现多文件构建 | >= 2.2.3 | +| [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_link](#targetbefore_link) | 在链接之前执行一些自定义脚本 | >= 2.2.7 | +| [before_build](#targetbefore_build) | 在构建之前执行一些自定义脚本 | >= 2.0.1 | +| [before_build_file](#targetbefore_build_file) | 自定义编译前的脚本, 实现单文件构建 | >= 2.2.3 | +| [before_build_files](#targetbefore_build_files) | 自定义编译前的脚本, 实现多文件构建 | >= 2.2.3 | +| [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_link](#targetafter_link) | 在链接之后执行一些自定义脚本 | >= 2.2.7 | +| [after_build](#targetafter_build) | 在构建之后执行一些自定义脚本 | >= 2.0.1 | +| [after_build_file](#targetafter_build_file) | 自定义编译后的脚本, 实现单文件构建 | >= 2.2.3 | +| [after_build_files](#targetafter_build_files) | 自定义编译后的脚本, 实现多文件构建 | >= 2.2.3 | +| [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 < 2.2.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_syslinks](#targetadd_syslinks) | 添加系统链接库名 | >= 2.2.3 | +| [add_files](#targetadd_files) | 添加源代码文件 | >= 1.0.1 | +| [del_files](#targetdel_files) | 从前面的源文件列表中删除指定文件 | >= 2.1.9 | +| [add_headers](#targetadd_headers) | 添加安装的头文件 | >= 1.0.1 < 2.2.5 已废弃 | +| [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_dcflags](#targetadd_dcflags) | 添加dlang编译选项 | >= 2.1.1 | +| [add_rcflags](#targetadd_rcflags) | 添加rust编译选项 | >= 2.1.1 | +| [add_cuflags](#targetadd_cuflags) | 添加cuda编译选项 | >= 2.2.1 | +| [add_culdflags](#targetadd_culdflags) | 添加cuda设备链接选项 | >= 2.2.7 | +| [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 | +| [set_tools](#targetset_tools) | 设置编译链接工具链 | >= 2.2.1 | +| [add_tools](#targetadd_tools) | 添加编译链接工具链 | >= 2.2.1 | +| [set_values](#targetset_values) | 设置一些扩展配置值 | >= 2.2.1 | +| [add_values](#targetadd_values) | 添加一些扩展配置值 | >= 2.2.1 | +| [set_rundir](#targetset_rundir) | 设置运行目录 | >= 2.2.7 | +| [add_runenvs](#targetadd_runenvs) | 添加运行环境变量 | >= 2.2.7 | +| [set_installdir](#targetset_installdir) | 设置安装目录 | >= 2.2.5 | +| [add_installfiles](#targetadd_installfiles) | 添加安装文件 | >= 2.2.5 | +| [add_headerfiles](#targetadd_headerfiles) | 添加安装头文件 | >= 2.2.5 | +| [set_configdir](#targetset_configdir) | 设置模板配置文件输出目录 | >= 2.2.5 | +| [set_configvar](#targetset_configvar) | 设置模板配置变量 | >= 2.2.5 | +| [add_configfiles](#targetadd_configfiles) | 添加模板配置文件 | >= 2.2.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_enabled + +###### 设置是否启用或禁用目标 + +如果设置`set_enabled(false)`,则会直接禁用对应的target,包括target的加载和信息获取,而[set_default](#targetset_default)仅仅只是设置默认不去编译,但是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_filename + +###### 设置目标文件全名 + +它跟[set_basename](#targetset_basename)的区别在于,[set_basename](#targetset_basename)设置名字不带后缀跟前缀,例如:`libtest.a`,basename如果改成test2后就变成了`libtest2.a`。 + +而filename的修改,是修改整个目标文件名,包括前后缀,例如可以直接把`libtest.a`改成`test.dll`,这个对于[set_basename](#targetset_basename)是做不到的。 + +##### target:set_warnings + +###### 设置警告级别 + +设置当前目标的编译的警告级别,一般支持一下几个级别: + +| 值 | 描述 | gcc/clang | msvc | +| ----- | ---------------------- | ---------- | ----------------------------- | +| none | 禁用所有警告 | -w | -W0 | +| less | 启用较少的警告 | -W1 | -W1 | +| more | 启用较多的警告 | -W3 | -W3 | +| all | 启用所有警告 | -Wall | -W3 (-Wall too more warnings) | +| everything | 启用全部支持的警告 | -Wall -Wextra -Weffc++ / -Weverything | -Wall | +| error | 将所有警告作为编译错误 | -Werror | -WX | + +这个api的参数是可以混合添加的,例如: + +```lua +-- 启用所有警告,并且作为编译错误处理 +set_warnings("all", "error") +``` + +如果当前没有目标,调用这个api将会设置到全局模式。。 + +##### target:set_optimize + +###### 设置优化级别 + +设置目标的编译优化等级,如果当前没有设置目标,那么将会设置到全局状态中,影响所有后续的目标。 + +目前主要支持一下几个级别: + +| 值 | 描述 | gcc/clang | msvc | +| ---------- | ---------------------- | ---------- | ------------ | +| none | 禁用优化 | -O0 | -Od | +| fast | 快速优化 | -O1 | default | +| faster | 更快的优化 | -O2 | -Ox | +| fastest | 最快运行速度的优化 | -O3 | -Ox -fp:fast | +| smallest | 最小化代码优化 | -Os | -O1 | +| aggressive | 过度优化 | -Ofast | -Ox -fp:fast | + + +例如: + +```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代码问题。。
+用户不需要去额外做任何修改。。
+
+注,2.2.5版本之后,此接口已废弃,请使用[add_headerfiles](#targetadd_headerfiles)代替。 +
+ +设置头文件的输出目录,默认输出到build目录中。 + +```lua +target("test") + set_headerdir("$(buildir)/include") +``` + +对于需要安装哪些头文件,可参考[add_headers](#targetadd_headers)接口。 + +##### target:set_targetdir + +###### 设置生成目标文件目录 + +设置目标程序文件的输出目录,一般情况下,不需要设置,默认会输出在build目录下 + +而build的目录可以在工程配置的时候,手动修改: + +```bash +xmake f -o /tmp/build +``` + +修改成`/tmp/build`后,目标文件默认输出到`/tmp/build`下面。 + +而如果用这个接口去设置,就不需要每次敲命令修改了,例如: + +```lua +target("test") + set_targetdir("/tmp/build") +``` + ++如果显示设置了`set_targetdir`, 那么优先选择`set_targetdir`指定的目录为目标文件的输出目录。 +
+ +##### target:set_objectdir + +###### 设置对象文件生成目录 + +设置目标target的对象文件(`*.o/obj`)的输出目录,例如: + +```lua +target("test") + set_objectdir("$(buildir)/.objs") +``` + +##### target:set_dependir + +###### 设置依赖文件生成目录 + +设置目标target的编译依赖文件(`.deps`)的输出目录,例如: + +```lua +target("test") + set_dependir("$(buildir)/.deps") +``` + +##### target:add_imports + +###### 为自定义脚本预先导入扩展模块 + +通常,我们在[on_build](#targeton_build)等自定义脚本内部,可以通过`import("core.base.task")`的方式导入扩展模块, +但是对于自定义脚本比较多的情况下,每个自定义脚本都重复导入一遍,非常的繁琐,那么可以通过这个接口,实现预先导入,例如: + +```lua +target("test") + on_load(function (target) + import("core.base.task") + import("core.project.project") + + task.run("xxxx") + end) + on_build(function (target) + import("core.base.task") + import("core.project.project") + + task.run("xxxx") + end) + on_install(function (target) + import("core.base.task") + import("core.project.project") + + task.run("xxxx") + end) +``` + +通过此接口可以简化为: + +```lua +target("test") + add_imports("core.base.task", "core.project.project") + on_load(function (target) + task.run("xxxx") + end) + on_build(function (target) + task.run("xxxx") + end) + on_install(function (target) + task.run("xxxx") + end) +``` + +##### target:add_rules + +###### 添加规则到目标 + +我们可以通过预先设置规则支持的文件后缀,来扩展其他文件的构建支持: + +```lua +-- 定义一个markdown文件的构建规则 +rule("markdown") + set_extensions(".md", ".markdown") + on_build(function (target, sourcefile) + os.cp(sourcefile, path.join(target:targetdir(), path.basename(sourcefile) .. ".html")) + end) + +target("test") + set_kind("binary") + + -- 使test目标支持markdown文件的构建规则 + add_rules("markdown") + + -- 添加markdown文件的构建 + add_files("src/*.md") + add_files("src/*.markdown") +``` + +我们也可以指定应用局部文件到规则,具体使用见:[add_files](#targetadd_files)。 + +##### 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_link + +###### 自定义链接脚本 + +这个是在v2.2.7之后新加的接口,用于定制化处理target的链接过程。 + +```lua +target("test") + on_link(function (target) + print("link it") + end) +``` + +##### 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_build_file + +###### 自定义编译脚本, 实现单文件构建 + +通过此接口,可以用来hook指定target内置的构建过程,替换每个源文件编译过程: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + on_build_file(function (target, sourcefile, opt) + opt.origin(target, sourcefile, opt) + end) +``` + +上面代码中的`opt.origin`存有内置的构建脚本,如果hook后还是想调用内置的构建脚本去编译源文件,那么直接继续调用`opt.origin`就行了。 + +如果不想重写内置的编译脚本,仅仅只是在编译前后添加一些自己的处理,其实用:[target.before_build_file](#targetbefore_build_file)和[target.after_build_file](#targetafter_build_file)会更加方便,不需要调用`opt.origin`。 + +##### target:on_build_files + +###### 自定义编译脚本, 实现多文件构建 + +通过此接口,可以用来hook指定target内置的构建过程,替换一批同类型源文件编译过程: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + on_build_files(function (target, sourcebatch, opt) + opt.origin(target, sourcebatch, opt) + end) +``` + +设置此接口后,对应源文件列表中文件,就不会出现在自定义的[target.on_build_file](#targeton_build_file)了,因为这个是包含关系。 + +其中sourcebatch描述了这批同类型源文件: + +* `sourcebatch.sourcekind`: 获取这批源文件的类型,比如:cc, as, .. +* `sourcebatch.sourcefiles()`: 获取源文件列表 +* `sourcebatch.objectfiles()`: 获取对象文件列表 +* `sourcebatch.dependfiles()`: 获取对应依赖文件列表,存有源文件中编译依赖信息,例如:xxx.d + +上面代码中的`opt.origin`存有内置的构建脚本,如果hook后还是想调用内置的构建脚本去编译源文件,那么直接继续调用`opt.origin`就行了。 + +##### 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_link + +###### 在链接之前执行一些自定义脚本 + +这个是在v2.2.7之后新加的接口,用于在链接之前增加一些自定义的操作。 + +```lua +target("test") + before_link(function (target) + print("") + end) +``` + +##### target:before_build + +###### 在构建之前执行一些自定义脚本 + +并不会覆盖默认的构建操作,只是在构建之前增加一些自定义的操作。 + +```lua +target("test") + before_build(function (target) + print("") + end) +``` + +##### target:before_build_file + +###### 自定义编译前的脚本, 实现单文件构建 + +通过此接口,可以用来hook指定target内置的构建过程,在每个源文件编译过程之前执行一些自定义脚本: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + before_build_file(function (target, sourcefile, opt) + end) +``` + +##### target:before_build_files + +###### 自定义编译前的脚本, 实现多文件构建 + +通过此接口,可以用来hook指定target内置的构建过程,在一批同类型源文件编译过程之前执行一些自定义脚本: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + before_build_files(function (target, sourcebatch, opt) + 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_link + +###### 在链接之后执行一些自定义脚本 + +这个是在v2.2.7之后新加的接口,用于在链接之后增加一些自定义的操作。 + +```lua +target("test") + after_link(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_build_file + +###### 自定义编译前的脚本, 实现单文件构建 + +通过此接口,可以用来hook指定target内置的构建过程,在每个源文件编译过程之后执行一些自定义脚本: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + after_build_file(function (target, sourcefile, opt) + end) +``` + +##### target:after_build_files + +###### 自定义编译前的脚本, 实现多文件构建 + +通过此接口,可以用来hook指定target内置的构建过程,在一批同类型源文件编译过程之后执行一些自定义脚本: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + after_build_files(function (target, sourcebatch, opt) + 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.2.5版本之后,此接口已废弃,请使用[add_configfiles](#targetadd_configfiles)。 +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.2.5版本之后,此接口已废弃,请使用[add_configfiles](#targetadd_configfiles)。 +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 + +###### 设置自动生成的配置头文件路径和前缀 + ++2.2.5版本之后,此接口已废弃,请使用[add_configfiles](#targetadd_configfiles)。 +
+ +此接口是[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名生成一个唯一字串。 + +2.1.8 之后版本,支持针对每个局部配置文件,单独设置版本号,优先于全局的[set_version](#set_version),例如: + +```lua + set_config_header("$(buildir)/config.h", {prefix = "TB_CONFIG", version = "2.1.8", build = "%Y%m%d%H%M"}) +``` + +###### 通过内置的检测规则生成配置 + +当这个target中通过下面的这些接口,对这个target添加了相关的选项依赖、包依赖、接口依赖后,如果某依赖被启用,那么对应的一些宏定义配置,会自动写入被设置的`config.h`文件中去。 + +* [add_options](#targetadd_options) +* [add_packages](#targetadd_packages) +* [add_cfunc](#targetadd_cfunc) +* [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") +``` + +甚至我们可以在`xmake.lua`中自己定义个function,针对option进行封装,提供更加定制化的检测和生成config.h的过程 + +例如:这里有个需求,我们想批量检测一些头文件,如果存在则在config.h里面输出`HAVE_LIMITS_H`这样的宏开关,我们可以这么写 + +```lua +function add_checking_to_config(...) + + -- 批量定义option检测规则,仅检测include文件 + local options = {} + for _, header in ipairs({...}) do + local define = header:upper():gsub("[%./]", "_") + option(define) + add_cincludes(header) + add_defines_h("HAVE_" .. define) -- 生成 HAVE_LIMITS_H 这样的宏开关到config.h + option_end() + table.insert(options, define) + end + + -- 定义个内置__config空目标,仅用于关联设置automatedconfig.h,以及对应的options检测规则 + -- 因为set_config_header在全局设置,会影响所有target,对每个target都会检测生成一次宏开关 + target("__config") + set_kind("phony") + set_config_header("includes/automatedconfig.h") + add_options(options) + target_end() +end + +-- 添加一些头文件检测 +add_checking_to_config("arpa/inet.h", "limits.h", "fcntl.h", "xxxx.h") +``` + +##### 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") + add_deps("test1", "test2") +``` + +上面的例子,在编译目标demo的时候,需要先编译test1, test2目标,因为demo会去用到他们 + ++target会自动继承依赖目标中的配置和属性,不需要额外调用`add_links`, `add_linkdirs`和`add_rpathdirs`等接口去关联依赖目标了。 +
+ +并且继承关系是支持级联的,例如: + +```lua +target("library1") + set_kind("static") + add_files("*.c") + add_includedirs("inc") -- 默认私有头文件目录不会被继承 + add_includedirs("inc1", {public = true}) -- 此处的头文件相关目录也会被继承 + +target("library2") + set_kind("static") + add_deps("library1") + add_files("*.c") + +target("test") + set_kind("binary") + add_deps("library2") +``` + +如果我们不想继承依赖target的任何配置,如何操作呢? + +```lua +add_deps("dep1", "dep2", {inherit = false}) +``` + +通过显式设置inherit配置,来告诉xmake,这两个依赖的配置是否需要被继承,如果不设置,默认就是启用继承的。 + +2.2.5版本之后,可通过 `add_includedirs("inc1", {public = true})`, 设置public为true, 将includedirs的设置公开给其他依赖的子target继承。 + +目前对于target的编译链接flags相关接口设置,都是支持继承属性的,可以人为控制是否需要导出给其他target来依赖继承,目前支持的属性有: + +| 属性 | 描述 | +| ---- | ---- | +| private | 默认设置,作为当前target的私有配置,不会被依赖的其他target所继承 | +| public | 公有配置,当前target,依赖的子target都会被设置 | +| interface | 接口设置,仅被依赖的子target所继承设置,当前target不参与 | + +对于这块的详细说明,可以看下:https://github.com/xmake-io/xmake/issues/368 + +##### target:add_links + +###### 添加链接库名 + +为当前目标添加链接库,一般这个要与[add_linkdirs](#targetadd_linkdirs)配对使用。 + +```lua +target("demo") + + -- 添加对libtest.a的链接,相当于 -ltest + add_links("test") + + -- 添加链接搜索目录 + add_linkdirs("$(buildir)/lib") +``` + +##### target:add_syslinks + +###### 添加系统链接库名 + +这个接口使用上跟[add_links](#targetadd_links)类似,唯一的区别就是,通过这个接口添加的链接库顺序在所有`add_links`之后。 + +因此主要用于添加系统库依赖,因为系统库的链接顺序是非常靠后的,例如: + +```lua +add_syslinks("pthread", "m", "dl") +target("demo") + add_links("a", "b") + add_linkdirs("$(buildir)/lib") +``` + +上面的配置,即使`add_syslinks`被优先提前设置了,但最后的链接顺序依然是:`-la -lb -lpthread -lm -ldl` + +##### 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`。 + +2.1.9版本之后,支持添加未知的代码文件,通过设置rule自定义规则,实现这些文件的自定义构建,例如: + +```lua +target("test") + -- ... + add_files("src/test/*.md", {rule = "markdown"}) +``` + +关于自定义构建规则的使用说明,详细见:[构建规则](#构建规则)。 + +并且在2.1.9版本之后,可以通过force参数来强制禁用cxflags,cflags等编译选项的自动检测,直接传入编译器,哪怕编译器有可能不支持,也会设置: + +```lua +add_files("src/*.c", {force = {cxflags = "-DTEST", mflags = "-framework xxx"}}) +``` + +##### target:del_files + +###### 从前面的源代码文件列表中删除指定文件 + +通过此接口,可以从前面[add_files](targetadd_files)接口添加的文件列表中,删除指定的文件,例如: + +```lua +target("test") + add_files("src/*.c") + del_files("src/test.c") +``` + +上面的例子,可以从`src`目录下添加除`test.c`以外的所有文件,当然这个也可以通过`add_files("src/*.c|test.c")`来达到相同的目的,但是这种方式更加灵活。 + +例如,我们可以条件判断来控制删除哪些文件,并且此接口也支持[add_files](targetadd_files)的匹配模式,过滤模式,进行批量移除。 + +```lua +target("test") + add_files("src/**.c") + del_files("src/test*.c") + del_files("src/subdir/*.c|xxx.c") + if is_plat("iphoneos") then + add_files("xxx.m") + end +``` + +通过上面的例子,我们可以看出`add_files`和`del_files`是根据调用顺序,进行顺序添加和删除的,并且通过`del_files("src/subdir/*.c|xxx.c")`删除一批文件, +并且排除`src/subdir/xxx.c`(就是说,不删除这个文件)。 + +##### target:add_headers + +###### 添加安装的头文件 + ++注,2.2.5版本之后,此接口已废弃,请使用[add_headerfiles](#targetadd_headerfiles)代替。 +
+ +安装指定的头文件到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]`的动态库文件,这将有助于提升程序的可移植性,不用写死绝对路径和相对路径,导致程序和目录切换引起程序加载动态库失败。 + ++需要注意的是,在macos下,要想add_rpathdirs设置生效,需要对dylib做一些预处理,添加`@rpath/xxx`路径设置: +`$install_name_tool -add_rpath @rpath/libxxx.dylib xxx/libxxx.dylib` +我们也可以通过`otool -L libxxx.dylib`查看是否存在带@rpath的路径 +
+ +##### target:add_includedirs + +###### 添加头文件搜索目录 + +设置头文件的搜索目录,这个接口的使用方式如下: + +```lua +target("test") + add_includedirs("$(buildir)/include") +``` + +当然也可以直接通过[add_cxflags](#targetadd_cxflags)或者[add_mxflags](#targetadd_mxflags)等接口来设置,也是可以的。 + +2.2.5之后,可通过额外的`{public|interface = true}`属性设置,将includedirs导出给依赖的子target,例如: + +```lua +target("test") + set_kind("static") + add_includedirs("src/include") -- 仅对当前target生效 + add_includedirs("$(buildir)/include", {public = true}),当前target和子target都会被设置 + +target("demo") + set_kind("binary") + add_deps("test") +``` + +更多关于这块的说明,见:[add_deps](#targetadd_deps) + ++如果不想在工程中写死,可以通过:`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 + +###### 添加宏定义到头文件 + ++2.2.5版本之后,此接口已废弃,请使用[add_configfiles](#targetadd_configfiles)。 +
+ +添加宏定义到`config.h`配置文件,`config.h`的设置,可参考[set_config_h](#targetset_config_h)接口。 + +##### target:add_undefines_h + +###### 取消宏定义到头文件 + ++2.2.5版本之后,此接口已废弃,请使用[add_configfiles](#targetadd_configfiles)。 +
+ +在`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会自动忽略器设置。 +
+ + +在2.1.9版本之后,可以通过force参数来强制禁用flags的自动检测,直接传入编译器,哪怕编译器有可能不支持,也会设置: + +```lua +add_cflags("-g", "-O2", {force = true}) +``` + +##### target:add_cxflags + +###### 添加c/c++编译选项 + +同时对c/c++代码添加编译选项 + +##### target:add_cxxflags + +###### 添加c++编译选项 + +仅对c++代码添加编译选项 + +##### target:add_mflags + +###### 添加objc编译选项 + +仅对objc代码添加编译选项 + +```lua +add_mflags("-g", "-O2", "-DDEBUG") +``` + +在2.1.9版本之后,可以通过force参数来强制禁用flags的自动检测,直接传入编译器,哪怕编译器有可能不支持,也会设置: + +```lua +add_mflags("-g", "-O2", {force = true}) +``` + +##### 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_cuflags + +###### 添加cuda编译选项 + +对cuda代码添加编译选项 + +```lua +add_cuflags("-gencode arch=compute_30,code=sm_30") +``` + +##### target:add_culdflags + +###### 添加cuda设备链接选项 + +v2.2.7之后,cuda默认构建会使用device-link,这个阶段如果要设置一些链接flags,则可以通过这个接口来设置。 +而最终的程序链接,会使用ldflags,不会调用nvcc,直接通过gcc/clang等c/c++链接器来链接。 + +关于device-link的说明,可以参考:https://devblogs.nvidia.com/separate-compilation-linking-cuda-device-code/ + +```lua +add_culdflags("-gencode arch=compute_30,code=sm_30") +``` + +##### 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_header("$(buildir)/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") +``` + +##### target:set_tools + +###### 设置编译链接工具链 + +对于`add_files("*.c")`添加的源码文件,默认都是会调用系统最匹配的编译工具去编译,或者通过`xmake f --cc=clang`命令手动去修改,不过这些都是全局影响所有target目标的。 + +如果有些特殊需求,需要对当前工程下某个特定的target目标单独指定不同的编译器、链接器或者特定版本的编译器,这个时候此接口就可以排上用途了,例如: + +```lua +target("test1") + add_files("*.c") + +target("test2") + add_files("*.c") + set_tools("cc", "$(projectdir)/tools/bin/clang-5.0") +``` + +上述描述仅对test2目标的编译器进行特殊设置,使用特定的clang-5.0编译器来编译test2,而test1还是使用默认设置。 + +对于同时设置多个编译器类型,可以这么写: + +```lua +set_tools { + cc = path.join(os.projectdir(), "tools/bin/clang-5.0"), + mm = path.join(os.projectdir(), "tools/bin/clang-5.0"), +} +``` + ++每次设置都会覆盖当前target目标下之前的那次设置,不同target之间不会被覆盖,互相独立,如果在根域设置,会影响所有子target。 +
+ +或者可以使用[add_tools](#targetadd_tools)来设置: + +```lua +add_tools("cc", "$(projectdir)/tools/bin/clang-5.0") +add_tools("mm", "$(projectdir)/tools/bin/clang-5.0") +``` + +前一个参数是key,用于指定工具类型,目前支持的有(编译器、链接器、归档器): + +| 工具类型 | 描述 | +| ------------ | ------------------------------------ | +| cc | c编译器 | +| cxx | c++编译器 | +| mm | objc编译器 | +| mxx | objc++编译器 | +| gc | go编译器 | +| as | 汇编器 | +| sc | swift编译器 | +| rc | rust编译器 | +| dc | dlang编译器 | +| ld | c/c++/asm/objc等通用可执行程序链接器 | +| sh | c/c++/asm/objc等通用动态库链接器 | +| ar | c/c++/asm/objc等通用静态库归档器 | +| dc-ld | dlang可执行链接器, rc-ld/gc-ld等类似 | +| dc-sh | dlang动态库链接器, rc-sh/gc-sh等类似 | + +对于一些编译器文件名不规则,导致xmake无法正常识别处理为已知的编译器名的情况下,我们也可以加一个工具名提示,例如: + +```lua +add_tools("cc", "gcc@$(projectdir)/tools/bin/mipscc.exe") +``` + +上述描述设置mipscc.exe作为c编译器,并且提示xmake作为gcc的传参处理方式进行编译。 + +##### target:add_tools + +###### 添加编译链接工具链 + +类似[set_tools](#targetset_tools),区别就是此接口可以多次调用,去添加多个工具,而[set_tools](#targetset_tools)每次设置都会覆盖之前的设置。 + +##### target:set_values + +###### 设置一些扩展配置值 + +给target设置一些扩展的配置值,这些配置没有像`set_ldflags`这种内置的api可用,通过第一个参数传入一个配置名,来扩展配置。 +一般用于传入配置参数给自定义rule中的脚本使用,例如: + +```lua +rule("markdown") + on_build_file(function (target, sourcefile, opt) + -- compile .markdown with flags + local flags = target:values("markdown.flags") + if flags then + -- .. + end + end) + +target("test") + add_files("src/*.md", {rule = "markdown"}) + set_values("markdown.flags", "xxx", "xxx") +``` + +上述代码例子中,可以看出,在target应用markdown规则的时候,通过set_values去设置一些flags值,提供给markdown规则去处理。 +在规则脚本中可以通过`target:values("markdown.flags")`获取到target中设置的扩展flags值。 + ++具体扩展配置名,根据不同的rule,会有所不同,目前有哪些,可以参考相关规则的描述:[内建规则](#内建规则) +
+ +##### target:add_values + +###### 添加一些扩展配置值 + +用法跟[target:set_values](#targetset_tools)类似,区别就是这个接口是追加设置,而不会每次覆盖设置。 + +##### target:set_rundir + +###### 设置运行目录 + +此接口用于设置默认运行target程序的当前运行目录,如果不设置,默认情况下,target是在可执行文件所在目录加载运行。 + +如果用户想要修改加载目录,一种是通过`on_run()`的方式自定义运行逻辑,里面去做切换,但仅仅为了切个目录就这么做,太过繁琐。 + +因此可以通过这个接口快速的对默认执行的目录环境做设置切换。 + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + set_rundir("$(projectdir)/xxx") +``` + +##### target:add_runenvs + +###### 添加运行环境变量 + +此接口用于添加设置默认运行target程序的环境变量。 + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + add_runenvs("PATH", "/tmp/bin", "xxx/bin") + add_runenvs("NAME", "value") +``` + +##### target:set_installdir + +###### 设置安装目录 + +2.2.5版本新增接口,用于针对每个target设置不同的默认安装目录,一般用于`xmake install/uninstall`命令。 + +默认情况下执行`xmake install`会安装到系统`/usr/local`目录,我们除了可以通过`xmake install -o /usr/local`指定其他安装目录外, +还可以在xmake.lua中针对target设置不同的安装目录来替代默认目录。 + +除了上述两种方式,我们也可以通过`INSTALLDIR`和`DESTDIR`环境变量设置默认的安装目录。 + +##### target:add_installfiles + +###### 添加安装文件 + +2.2.5版本新增接口,用于针对每个target设置对应需要安装的文件,一般用于`xmake install/uninstall`命令。 + +比如我们可以指定安装各种类型的文件到安装目录: + +```lua +target("test") + add_installfiles("src/*.h") + add_installfiles("doc/*.md") +``` + +默认在linux等系统上,我们会安装到`/usr/local/*.h, /usr/local/*.md`,不过我们也可以指定安装到特定子目录: + +```lua +target("test") + add_installfiles("src/*.h", {prefixdir = "include"}) + add_installfiles("doc/*.md", {prefixdir = "share/doc"}) +``` + +上面的设置,我们会安装到`/usr/local/include/*.h, /usr/local/share/doc/*.md` + +我们也可以通过`()`去提取源文件中的子目录来安装,例如: + +```lua +target("test") + add_installfiles("src/(tbox/*.h)", {prefixdir = "include"}) + add_installfiles("doc/(tbox/*.md)", {prefixdir = "share/doc"}) +``` + +我们把`src/tbox/*.h`中的文件,提取`tbox/*.h`子目录结构后,在进行安装:`/usr/local/include/tbox/*.h, /usr/local/share/doc/tbox/*.md` + +当然,用户也可以通过[set_installdir](#targetset_installdir)接口,来配合使用。 + +关于此接口的详细说明,见:https://github.com/xmake-io/xmake/issues/318 + +##### target:add_headerfiles + +###### 添加安装头文件 + +2.2.5版本新增接口,用于针对每个target设置对应需要安装的头文件,一般用于`xmake install/uninstall`命令。 + +此接口使用方式跟[add_installfiles](#targetadd_installfiles)接口几乎完全一样,都可以用来天剑安装文件,不过此接口仅用于安装头文件。 +因此,使用上比`add_installfiles`简化了不少,默认不设置prefixdir,也会自动将头文件安装到对应的`include`子目录中。 + +并且此接口对于`xmake project -k vs201x`等插件生成的IDE文件,也会添加对应的头文件进去。 + ++需要注意的是,之前的[add_headers](#targetadd_headers)接口已经被废弃,新版本请用此接口替代,这个老接口在编译过程中也会自动复制头文件到build目录,这个逻辑设计的并不是很好。 +
+ +##### target:set_configdir + +###### 设置模板配置文件的输出目录 + +2.2.5版本新增接口,主要用于[add_configfiles](#targetadd_configfiles)接口设置的模板配置文件的输出目录。 + +##### target:set_configvar + +###### 设置模板配置变量 + +2.2.5版本新增接口,用于在编译前,添加一些需要预处理的模板配置变量,一般用于[add_configfiles](#targetadd_configfiles)接口。 + +##### target:add_configfiles + +###### 添加模板配置文件 + +2.2.5版本新增接口,用于在编译前,添加一些需要预处理的配置文件,用于替代[set_config_header](#targetset_config_header)等老接口。 + +因为此接口更加的通用,不仅用于处理config.h的自动生成和预处理,还可以处理各种文件类型,而`set_config_header`仅用于处理头文件,并且不支持模板变量替换。 + +先来一个简单的例子: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + set_configdir("$(buildir)/config") + add_configfiles("src/config.h.in") +``` + +上面的设置,会在编译前,自动的将`config.h.in`这个头文件配置模板,经过预处理后,生成输出到指定的`build/config/config.h`。 + +如果`set_configdir`不设置,那么默认输出到`build`目录下。 + +其中`.in`后缀会被自动识别处理掉,如果想要输出存储为其他文件名,可以通过: + +```lua +add_configfiles("src/config.h", {filename = "myconfig.h"}) +``` + +的方式,来重命名输出,同样,这个接口跟[add_installfiles](#targetadd_configfiles)类似,也是支持prefixdir和子目录提取设置: + +```lua +add_configfiles("src/*.h.in", {prefixdir = "subdir"}) +add_configfiles("src/(tbox/config.h)") +``` + +这个接口的一个最重要的特性就是,可以在预处理的时候,对里面的一些模板变量进行预处理替换,例如: + +config.h.in + +``` +#define VAR1 "${VAR1}" +#define VAR2 "${VAR2}" +#define HELLO "${HELLO}" +``` + +```lua +set_configvar("VAR1", "1") + +target("test") + set_kind("binary") + add_files("main.c") + + set_configvar("VAR2", 2) + add_configfiles("config.h.in", {variables = {hello = "xmake"}}) + add_configfiles("*.man", {copyonly = true}) +``` + +通过[set_configvar](#targetset_configvar)接口设置模板变量,裹着通过`{variables = {xxx = ""}}`中设置的变量进行替换处理。 + +预处理后的文件`config.h`内容为: + +``` +#define VAR1 "1" +#define VAR2 "2" +#define HELLO "xmake" +``` + +而`{copyonly = true}`设置,会强制将`*.man`作为普通文件处理,仅在预处理阶段copy文件,不进行变量替换。 + +默认的模板变量匹配模式为`${var}`,当然我们也可以设置其他的匹配模式,例如,改为`@var@`匹配规则: + +```lua +target("test") + add_configfiles("config.h.in", {pattern = "@(.-)@"}) +``` + +我们也有提供了一些内置的变量,即使不通过此接口设置,也是可以进行默认变量替换的: + +``` +${VERSION} -> 1.6.3 +${VERSION_MAJOR} -> 1 +${VERSION_MINOR} -> 6 +${VERSION_ALTER} -> 3 +${VERSION_BUILD} -> set_version("1.6.3", {build = "%Y%m%d%H%M"}) -> 201902031421 +${PLAT} and ${plat} -> MACOS and macosx +${ARCH} and ${arch} -> ARM and arm +${MODE} and ${mode} -> DEBUG/RELEASE and debug/release +${DEBUG} and ${debug} -> 1 or 0 +${OS} and ${os} -> IOS or ios +``` + +例如: + +config.h.in + +```c +#define CONFIG_VERSION "${VERSION}" +#define CONFIG_VERSION_MAJOR ${VERSION_MAJOR} +#define CONFIG_VERSION_MINOR ${VERSION_MINOR} +#define CONFIG_VERSION_ALTER ${VERSION_ALTER} +#define CONFIG_VERSION_BUILD ${VERSION_BUILD} +``` + +config.h + +```c +#define CONFIG_VERSION "1.6.3" +#define CONFIG_VERSION_MAJOR 1 +#define CONFIG_VERSION_MINOR 6 +#define CONFIG_VERSION_ALTER 3 +#define CONFIG_VERSION_BUILD 201902031401 +``` + +我们还可以对`#define`定义进行一些变量状态控制处理: + +config.h.in + +```c +${define FOO_ENABLE} +``` + +```lua +set_configvar("FOO_ENABLE", 1) -- or pass true +set_configvar("FOO_STRING", "foo") +``` + +通过上面的变量设置后,`${define xxx}`就会替换成: + +```c +#define FOO_ENABLE 1 +#define FOO_STRING "foo" +``` + +或者(设置为0禁用的时候) + +```c +/* #undef FOO_ENABLE */ +/* #undef FOO_STRING */ +``` + +这种方式,对于一些自动检测生成config.h非常有用,比如配合option来做自动检测: + +```lua +option("foo") + set_default(true) + set_description("Enable Foo") + set_configvar("FOO_ENABLE", 1) -- 或者传递true,启用FOO_ENABLE变量 + set_configvar("FOO_STRING", "foo") + +target("test") + add_configfiles("config.h.in") + + -- 如果启用foo选项 -> 天剑 FOO_ENABLE 和 FOO_STRING 定义 + add_options("foo") +``` + +config.h.in + +```c +${define FOO_ENABLE} +${define FOO_STRING} +``` + +config.h + +```c +#define FOO_ENABLE 1 +#define FOO_STRING "foo" +``` + +关于option选项检测,以及config.h的自动生成,有一些辅助函数,可以看下:https://github.com/xmake-io/xmake/issues/342 + +除了`#define`,如果想要对其他非`#define xxx`也做状态切换处理,可以使用 `${default xxx 0}` 模式,设置默认值,例如: + +``` +HAVE_SSE2 equ ${default VAR_HAVE_SSE2 0} +``` + +通过`set_configvar("HAVE_SSE2", 1)`启用变量后,变为`HAVE_SSE2 equ 1`,如果没有设置变量,则使用默认值:`HAVE_SSE2 equ 0` + +关于这个的详细说明,见:https://github.com/xmake-io/xmake/issues/320 + + +#### 选项定义 + +定义和设置选项开关,每个`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_values](#optionset_values) | 设置选项值列表 | >= 2.1.9 | +| [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_cuflags](#targetadd_cuflags) | 添加cuda编译选项 | >= 2.2.1 | +| [add_culdflags](#targetadd_culdflags) | 添加cuda设备链接选项 | >= 2.2.7 | +| [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("TEST") +``` + +然后关联到指定的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_values + +###### 设置选项值列表 + +仅用于`xmake f --menu`的图形菜单配置时,提供选项值列表供用户快速选择使用,例如: + +```lua +option("test") + set_default("b") + set_showmenu(true) + set_values("a", "b", "c") +``` + +效果图如下: + +
+
+##### 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
+```
+
+这个接口,仅仅是为了调整显示布局,更加美观而已,没其他用途。
+
+在2.1.9版本中,可以通过category设置分级路径名`set_category("root/submenu/submenu2")`,来配置`xmake f --menu`的图形菜单界面,例如:
+
+```lua
+-- 'boolean' option
+option("test1")
+ set_default(true)
+ set_showmenu(true)
+ set_category("root menu/test1")
+
+-- 'choice' option with values: "a", "b", "c"
+option("test2")
+ set_default("a")
+ set_values("a", "b", "c")
+ set_showmenu(true)
+ set_category("root menu/test2")
+
+-- 'string' option
+option("test3")
+ set_default("xx")
+ set_showmenu(true)
+ set_category("root menu/test3/test3")
+
+-- 'number' option
+option("test4")
+ set_default(6)
+ set_showmenu(true)
+ set_category("root menu/test4")
+```
+
+上述配置最后显示的菜单界面路径结构:
+
+- root menu
+ - test1
+ - test2
+ - test3
+ - test3
+ - test4
+
+效果图如下:
+
+
+
+##### 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", "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) + +#### 构建规则 + +在2.2.1版本之后,xmake不仅原生内置支持多种语言文件的构建,而且还可以通过自定义构建规则,让用户自己来实现复杂的未知文件构建。 + +我们可以通过预先设置规则支持的文件后缀,来扩展其他文件的构建支持: + +```lua +-- 定义一个markdown文件的构建规则 +rule("markdown") + set_extensions(".md", ".markdown") + on_build_file(function (target, sourcefile, opt) + os.cp(sourcefile, path.join(target:targetdir(), path.basename(sourcefile) .. ".html")) + end) + +target("test") + set_kind("binary") + + -- 使test目标支持markdown文件的构建规则 + add_rules("markdown") + + -- 添加markdown文件的构建 + add_files("src/*.md") + add_files("src/*.markdown") +``` + +我们也可以指定某些零散的其他文件作为markdown规则来处理: + +```lua +target("test") + -- ... + add_files("src/test/*.md.in", {rule = "markdown"}) +``` + +一个target可以叠加应用多个rules去更加定制化实现自己的构建行为,甚至支持不同的构建环境。 + ++通过`add_files("*.md", {rule = "markdown"})`方式指定的规则,优先级高于`add_rules("markdown")`设置的规则。 +
+ +| 接口 | 描述 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [rule](#rule) | 定义规则 | >= 2.1.9 | +| [add_imports](#ruleadd_imports) | 为所有自定义脚本预先导入扩展模块 | >= 2.1.9 | +| [set_extensions](#ruleset_extensions) | 设置规则支持的文件扩展类型 | >= 2.1.9 | +| [on_load](#ruleon_load) | 自定义加载脚本 | >= 2.2.1 | +| [on_link](#ruleon_link) | 自定义链接脚本 | >= 2.2.7 | +| [on_build](#ruleon_build) | 自定义编译脚本 | >= 2.1.9 | +| [on_clean](#ruleon_clean) | 自定义清理脚本 | >= 2.1.9 | +| [on_package](#ruleon_package) | 自定义打包脚本 | >= 2.1.9 | +| [on_install](#ruleon_install) | 自定义安装脚本 | >= 2.1.9 | +| [on_uninstall](#ruleon_uninstall) | 自定义卸载脚本 | >= 2.1.9 | +| [on_build_file](#ruleon_build_file) | 自定义编译脚本, 实现单文件构建 | >= 2.2.1 | +| [on_build_files](#ruleon_build_files) | 自定义编译脚本, 实现多文件构建 | >= 2.2.1 | +| [before_load](#rulebefore_load) | 自定义加载前的脚本 | >= 2.2.1 | +| [before_link](#rulebefore_link) | 自定义链接前的脚本 | >= 2.2.7 | +| [before_build](#rulebefore_build) | 自定义编译前的脚本 | >= 2.2.1 | +| [before_clean](#rulebefore_clean) | 自定义清理前的脚本 | >= 2.2.1 | +| [before_package](#rulebefore_package) | 自定义打包前的脚本 | >= 2.2.1 | +| [before_install](#rulebefore_install) | 自定义安装前的脚本 | >= 2.2.1 | +| [before_uninstall](#rulebefore_uninstall) | 自定义卸载前的脚本 | >= 2.2.1 | +| [before_build_file](#rulebefore_build_file) | 自定义编译前的脚本, 实现单文件构建 | >= 2.2.1 | +| [before_build_files](#rulebefore_build_files) | 自定义编译前的脚本, 实现多文件构建 | >= 2.2.1 | +| [after_load](#ruleafter_load) | 自定义加载后的脚本 | >= 2.2.1 | +| [after_link](#ruleafter_link) | 自定义链接后的脚本 | >= 2.2.7 | +| [after_build](#ruleafter_build) | 自定义编译后的脚本 | >= 2.2.1 | +| [after_clean](#ruleafter_clean) | 自定义清理后的脚本 | >= 2.2.1 | +| [after_package](#ruleafter_package) | 自定义打包后的脚本 | >= 2.2.1 | +| [after_install](#ruleafter_install) | 自定义安装后的脚本 | >= 2.2.1 | +| [after_uninstall](#ruleafter_uninstall) | 自定义卸载后的脚本 | >= 2.2.1 | +| [after_build_file](#ruleafter_build_file) | 自定义编译后的脚本, 实现单文件构建 | >= 2.2.1 | +| [after_build_files](#ruleafter_build_files) | 自定义编译后的脚本, 实现多文件构建 | >= 2.2.1 | +| [rule_end](#rule_end) | 结束定义规则 | >= 2.1.9 | + +##### 内建规则 + +自从2.2.1版本后,xmake提供了一些内置规则去简化日常xmake.lua描述,以及一些常用构建环境的支持。 + +| 规则 | 描述 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [mode.debug](#mode-debug) | 调试模式编译规则 | >= 2.2.1 | +| [mode.release](#mode-release) | 发布模式编译规则 | >= 2.2.1 | +| [mode.check](#mode-check) | 检测模式编译规则 | >= 2.2.1 | +| [mode.profile](#mode-profile) | 性能分析模式编译规则 | >= 2.2.1 | +| [mode.coverage](#mode-coverage) | 覆盖分析编译模式规则 | >= 2.2.1 | +| [qt.static](#qt-static) | Qt静态库编译规则 | >= 2.2.1 | +| [qt.shared](#qt-shared) | Qt动态库编译规则 | >= 2.2.1 | +| [qt.console](#qt-console) | Qt控制台编译规则 | >= 2.2.1 | +| [qt.application](#qt-application) | Qt应用程序编译规则 | >= 2.2.1 | +| [wdk.umdf.driver](#wdk-umdf-driver) | WDK环境umdf驱动编译规则 | >= 2.2.1 | +| [wdk.umdf.binary](#wdk-umdf-binary) | WDK环境umdf驱动应用编译规则 | >= 2.2.1 | +| [wdk.kmdf.driver](#wdk-kmdf-driver) | WDK环境kmdf驱动编译规则 | >= 2.2.1 | +| [wdk.kmdf.binary](#wdk-kmdf-binary) | WDK环境kmdf驱动应用编译规则 | >= 2.2.1 | +| [wdk.wdm.driver](#wdk-wdm-driver) | WDK环境wdm驱动编译规则 | >= 2.2.1 | +| [wdk.wdm.binary](#wdk-wdm-binary) | WDK环境wdm驱动应用编译规则 | >= 2.2.1 | + +###### mode.debug + +为当前工程xmake.lua添加debug编译模式的配置规则,例如: + +```lua +add_rules("mode.debug") +``` + +相当于: + +```lua +-- the debug mode +if is_mode("debug") then + + -- enable the debug symbols + set_symbols("debug") + + -- disable optimization + set_optimize("none") +end +``` + +我们可以通过:`xmake f -m debug`来切换到此编译模式。 + +###### mode.release + +为当前工程xmake.lua添加release编译模式的配置规则,例如: + +```lua +add_rules("mode.release") +``` + +相当于: + +```lua +-- the release mode +if is_mode("release") then + + -- set the symbols visibility: hidden + set_symbols("hidden") + + -- enable fastest optimization + set_optimize("fastest") + + -- strip all symbols + set_strip("all") +end +``` + +我们可以通过:`xmake f -m release`来切换到此编译模式。 + +###### mode.check + +为当前工程xmake.lua添加check编译模式的配置规则,一般用于内存检测,例如: + +```lua +add_rules("mode.check") +``` + +相当于: + +```lua +-- the check mode +if is_mode("check") then + + -- enable the debug symbols + set_symbols("debug") + + -- disable optimization + set_optimize("none") + + -- attempt to enable some checkers for pc + add_cxflags("-fsanitize=address", "-ftrapv") + add_mxflags("-fsanitize=address", "-ftrapv") + add_ldflags("-fsanitize=address") +end +``` + +我们可以通过:`xmake f -m check`来切换到此编译模式。 + +###### mode.profile + +为当前工程xmake.lua添加profile编译模式的配置规则,一般用于性能分析,例如: + +```lua +add_rules("mode.profile") +``` + +相当于: + +```lua +-- the profile mode +if is_mode("profile") then + + -- enable the debug symbols + set_symbols("debug") + + -- enable gprof + add_cxflags("-pg") + add_ldflags("-pg") +end +``` + +我们可以通过:`xmake f -m profile`来切换到此编译模式。 + +###### mode.coverage + +为当前工程xmake.lua添加coverage编译模式的配置规则,一般用于覆盖分析,例如: + +```lua +add_rules("mode.coverage") +``` + +相当于: + +```lua +-- the coverage mode +if is_mode("coverage") then + add_cxflags("--coverage") + add_mxflags("--coverage") + add_ldflags("--coverage") +end +``` + +我们可以通过:`xmake f -m coverage`来切换到此编译模式。 + +###### qt.static + +用于编译生成Qt环境的静态库程序: + +```lua +target("qt_static_library") + add_rules("qt.static") + add_files("src/*.cpp") + add_frameworks("QtNetwork", "QtGui") +``` + +###### qt.shared + +用于编译生成Qt环境的动态库程序: + +```lua +target("qt_shared_library") + add_rules("qt.shared") + add_files("src/*.cpp") + add_frameworks("QtNetwork", "QtGui") +``` + +###### qt.console + +用于编译生成Qt环境的控制台程序: + +```lua +target("qt_console") + add_rules("qt.console") + add_files("src/*.cpp") +``` + +###### qt.application + +用于编译生成Qt环境的ui应用程序。 + +Quick(qml)应用程序: + +```lua +target("qt_quickapp") + add_rules("qt.application") + add_files("src/*.cpp") + add_files("src/qml.qrc") + add_frameworks("QtQuick") +``` + +Qt Widgets(ui/moc)应用程序: + +```lua +-- add target +target("qt_widgetapp") + add_rules("qt.application") + add_files("src/*.cpp") + add_files("src/mainwindow.ui") + add_files("src/mainwindow.h") -- 添加带有 Q_OBJECT 的meta头文件 + add_frameworks("QtWidgets") +``` + +更多Qt相关描述见:[#160](https://github.com/xmake-io/xmake/issues/160) + +###### wdk.env.kmdf + +应用WDK下kmdf的编译环境设置,需要配合:`wdk.[driver|binary|static|shared]`等规则来使用。 + +###### wdk.env.umdf + +应用WDK下umdf的编译环境设置,需要配合:`wdk.[driver|binary|static|shared]`等规则来使用。 + +###### wdk.env.wdm + +应用WDK下wdm的编译环境设置,需要配合:`wdk.[driver|binary|static|shared]`等规则来使用。 + +###### wdk.driver + +编译生成windows下基于WDK环境的驱动程序,目前仅支持WDK10环境。 + +注:需要配合:`wdk.env.[umdf|kmdf|wdm]`等环境规则使用。 + +```lua +-- add target +target("echo") + + -- add rules + add_rules("wdk.driver", "wdk.env.kmdf") + + -- add files + add_files("driver/*.c") + add_files("driver/*.inx") + + -- add includedirs + add_includedirs("exe") +``` + +###### wdk.binary + +编译生成windows下基于WDK环境的可执行程序,目前仅支持WDK10环境。 + +注:需要配合:`wdk.env.[umdf|kmdf|wdm]`等环境规则使用。 + +```lua +-- add target +target("app") + + -- add rules + add_rules("wdk.binary", "wdk.env.umdf") + + -- add files + add_files("exe/*.cpp") +``` + +###### wdk.static + +编译生成windows下基于WDK环境的静态库程序,目前仅支持WDK10环境。 + +注:需要配合:`wdk.env.[umdf|kmdf|wdm]`等环境规则使用。 + +```lua +target("nonpnp") + + -- add rules + add_rules("wdk.static", "wdk.env.kmdf") + + -- add flags for rule: wdk.tracewpp + add_values("wdk.tracewpp.flags", "-func:TraceEvents(LEVEL,FLAGS,MSG,...)", "-func:Hexdump((LEVEL,FLAGS,MSG,...))") + + -- add files + add_files("driver/*.c", {rule = "wdk.tracewpp"}) +``` + +###### wdk.shared + +编译生成windows下基于WDK环境的动态库程序,目前仅支持WDK10环境。 + +注:需要配合:`wdk.env.[umdf|kmdf|wdm]`等环境规则使用。 + +```lua +target("nonpnp") + + -- add rules + add_rules("wdk.shared", "wdk.env.wdm") + + -- add flags for rule: wdk.tracewpp + add_values("wdk.tracewpp.flags", "-func:TraceEvents(LEVEL,FLAGS,MSG,...)", "-func:Hexdump((LEVEL,FLAGS,MSG,...))") + + -- add files + add_files("driver/*.c", {rule = "wdk.tracewpp"}) +``` + +###### wdk.tracewpp + +用于启用tracewpp预处理源文件: + +```lua +target("nonpnp") + + -- add rules + add_rules("wdk.driver", "wdk.env.kmdf") + + -- add flags for rule: wdk.tracewpp + add_values("wdk.tracewpp.flags", "-func:TraceEvents(LEVEL,FLAGS,MSG,...)", "-func:Hexdump((LEVEL,FLAGS,MSG,...))") + + -- add files + add_files("driver/*.c", {rule = "wdk.tracewpp"}) + add_files("driver/*.rc") +``` + +更多WDK规则描述见:[#159](https://github.com/xmake-io/xmake/issues/159) + +###### win.sdk.application + +编译生成winsdk应用程序。 + +```lua +-- add rules +add_rules("mode.debug", "mode.release") + +-- define target +target("usbview") + + -- windows application + add_rules("win.sdk.application") + + -- add files + add_files("*.c", "*.rc") + add_files("xmlhelper.cpp", {rule = "win.sdk.dotnet"}) +``` + +###### wdk.sdk.dotnet + +用于指定某些c++源文件作为c++.net来编译。 + +```lua +add_files("xmlhelper.cpp", {rule = "win.sdk.dotnet"}) +``` + +##### rule + +###### 定义规则 + +```lua +rule("markdown") + set_extensions(".md", ".markdown") + on_build_file(function (target, sourcefile, opt) + os.cp(sourcefile, path.join(target:targetdir(), path.basename(sourcefile) .. ".html")) + end) +``` + +##### rule:add_imports + +###### 为所有自定义脚本预先导入扩展模块 + +使用方式和说明请见:[target:add_imports](#targetadd_imports),用法相同。 + +##### rule:set_extensions + +###### 设置规则支持的文件扩展类型 + +通过设置支持的扩展文件类型,将规则应用于带这些后缀的文件上,例如: + +```lua +-- 定义一个markdown文件的构建规则 +rule("markdown") + set_extensions(".md", ".markdown") + on_build_file(function (target, sourcefile, opt) + os.cp(sourcefile, path.join(target:targetdir(), path.basename(sourcefile) .. ".html")) + end) + +target("test") + set_kind("binary") + + -- 使test目标支持markdown文件的构建规则 + add_rules("markdown") + + -- 添加markdown文件的构建 + add_files("src/*.md") + add_files("src/*.markdown") +``` + +##### rule:on_load + +###### 自定义加载脚本 + +用于实现自定规则的加载脚本,当加载target的时候,会被执行,可在里面自定义设置一些target配置,例如: + +```lua +rule("test") + on_load(function (target) + target:add("defines", "-DTEST") + end) +``` + +##### rule:on_link + +###### 自定义链接脚本 + +用于实现自定规则的链接脚本,会覆盖被应用的target的默认链接行为,例如: + +```lua +rule("test") + on_link(function (target) + end) +``` + +##### rule:on_build + +###### 自定义编译脚本 + +用于实现自定规则的构建脚本,会覆盖被应用的target的默认构建行为,例如: + +```lua +rule("markdown") + on_build(function (target) + end) +``` + +##### rule:on_clean + +###### 自定义清理脚本 + +用于实现自定规则的清理脚本会,覆盖被应用的target的默认清理行为,例如: + +```lua +rule("markdown") + on_clean(function (target) + -- remove sourcefile.html + end) +``` + +##### rule:on_package + +###### 自定义打包脚本 + +用于实现自定规则的打包脚本,覆盖被应用的target的默认打包行为, 例如: + +```lua +rule("markdown") + on_package(function (target) + -- package sourcefile.html + end) +``` + +##### rule:on_install + +###### 自定义安装脚本 + +用于实现自定规则的安装脚本,覆盖被应用的target的默认安装行为, 例如: + +```lua +rule("markdown") + on_install(function (target) + end) +``` + +##### rule:on_uninstall + +###### 自定义卸载脚本 + +用于实现自定规则的卸载脚本,覆盖被应用的target的默认卸载行为, 例如: + +```lua +rule("markdown") + on_uninstall(function (target) + end) +``` + +##### rule:on_build_file + +###### 自定义编译脚本,一次处理一个源文件 + +```lua +rule("markdown") + on_build_file(function (target, sourcefile, opt) + print("%%%d: %s", opt.progress, sourcefile) + end) +``` + +其中第三个参数opt是可选参数,用于获取一些编译过程中的信息状态,例如:opt.progress 为当期的编译进度。 + +##### rule:on_build_files + +###### 自定义编译脚本,一次处理多个源文件 + +大部分的自定义构建规则,每次都是处理单独一个文件,输出一个目标文件,例如:a.c => a.o + +但是,有些情况下,我们需要同时输入多个源文件一起构建生成一个目标文件,例如:a.c b.c d.c => x.o + +对于这种情况,我们可以通过自定义这个脚本来实现: + +```lua +rule("markdown") + on_build_files(function (target, sourcebatch, opt) + -- build some source files + for _, sourcefile in ipairs(sourcebatch.sourcefiles) do + -- ... + end + end) +``` + +##### rule:before_load + +###### 自定义加载前脚本 + +用于实现自定义target加载前的执行脚本,例如: + +```lua +rule("test") + before_load(function (target) + target:add("defines", "-DTEST") + end) +``` + +##### rule:before_link + +###### 自定义链接前脚本 + +用于实现自定义target链接前的执行脚本,例如: + +```lua +rule("test") + before_link(function (target) + end) +``` + +##### rule:before_build + +###### 自定义编译前脚本 + +用于实现自定义target构建前的执行脚本,例如: + +```lua +rule("markdown") + before_build(function (target) + end) +``` + +##### rule:before_clean + +###### 自定义清理前脚本 + +用于实现自定义target清理前的执行脚本,例如: + +```lua +rule("markdown") + before_clean(function (target) + end) +``` + +##### rule:before_package + +###### 自定义打包前脚本 + +用于实现自定义target打包前的执行脚本, 例如: + +```lua +rule("markdown") + before_package(function (target) + end) +``` + +##### rule:before_install + +###### 自定义安装前脚本 + +用于实现自定义target安装前的执行脚本,例如: + +```lua +rule("markdown") + before_install(function (target) + end) +``` + +##### rule:before_uninstall + +###### 自定义卸载前脚本 + +用于实现自定义target卸载前的执行脚本,例如: + +```lua +rule("markdown") + before_uninstall(function (target) + end) +``` + +##### rule:before_build_file + +###### 自定义编译前脚本,一次处理一个源文件 + +跟[rule:on_build_file](#ruleon_build_file)用法类似,不过这个接口被调用的时机是在编译某个源文件之前, +一般用于对某些源文件进行编译前的预处理。 + +##### rule:before_build_files + +###### 自定义编译前脚本,一次处理多个源文件 + +跟[rule:on_build_files](#ruleon_build_files)用法类似,不过这个接口被调用的时机是在编译某些源文件之前, +一般用于对某些源文件进行编译前的预处理。 + +##### rule:after_load + +###### 自定义加载后脚本 + +用于实现自定义target加载后的执行脚本,用法跟[rule:before_load](#rulebefore_load)类似。 + +##### rule:after_link + +###### 自定义链接后脚本 + +用于实现自定义target链接后的执行脚本,用法跟[rule:before_link](#rulebefore_link)类似。 + +##### rule:after_build + +###### 自定义编译后脚本 + +用于实现自定义target构建后的执行脚本,用法跟[rule:before_build](#rulebefore_build)类似。 + +##### rule:after_clean + +###### 自定义清理后脚本 + +用于实现自定义target清理后的执行脚本,用法跟[rule:before_clean](#rulebefore_clean)类似。 + +##### rule:after_package + +###### 自定义打包后脚本 + +用于实现自定义target打包后的执行脚本, 用法跟[rule:before_package](#rulebefore_package)类似。 + +##### rule:after_install + +###### 自定义安装后脚本 + +用于实现自定义target安装后的执行脚本,用法跟[rule:before_install](#rulebefore_install)类似。 + +##### rule:after_uninstall + +###### 自定义卸载后脚本 + +用于实现自定义target卸载后的执行脚本,用法跟[rule:before_uninstall](#rulebefore_uninstall)类似。 + +##### rule:after_build_file + +###### 自定义编译后脚本,一次处理一个源文件 + +跟[rule:on_build_file](#ruleon_build_file)用法类似,不过这个接口被调用的时机是在编译某个源文件之后, +一般用于对某些编译后对象文件进行后期处理。 + +##### rule:after_build_files + +###### 自定义编译后脚本,一次处理多个源文件 + +跟[rule:on_build_files](#ruleon_build_files)用法类似,不过这个接口被调用的时机是在编译某些源文件之后, +一般用于对某些编译后对象文件进行后期处理。 + +##### rule_end + +###### 结束定义规则 + +这个是可选的,如果想要手动结束rule的定义,可以调用它: + +```lua +rule("test") + -- .. +rule_end() +``` + +#### 库包依赖 + +仓库依赖包定义描述,`package()`相关接口定义,等有时间会详细说明,敬请期待。。 + +可先参考官方仓库中现有包描述:[xmake-repo](https://github.com/xmake-io/xmake-repo) + +这里给个比较具有代表性的实例供参考: + +```lua +package("libxml2") + + set_homepage("http://xmlsoft.org/") + set_description("The XML C parser and toolkit of Gnome.") + + set_urls("https://github.com/GNOME/libxml2/archive/$(version).zip", {excludes = {"*/result/*", "*/test/*"}}) + + add_versions("v2.9.8", "c87793e45e66a7aa19200f861873f75195065de786a21c1b469bdb7bfc1230fb") + add_versions("v2.9.7", "31dd4c0e10fa625b47e27fd6a5295d246c883f214da947b9a4a9e13733905ed9") + + if is_plat("macosx", "linux") then + add_deps("autoconf", "automake", "libtool", "pkg-config") + end + + on_load(function (package) + package:addvar("includedirs", "include/libxml2") + package:addvar("links", "xml2") + end) + + if is_plat("windows") and winos.version():gt("winxp") then + on_install("windows", function (package) + os.cd("win32") + os.vrun("cscript configure.js iso8859x=yes iconv=no compiler=msvc cruntime=/MT debug=%s prefix=\"%s\"", package:debug() and "yes" or "no", package:installdir()) + os.vrun("nmake /f Makefile.msvc") + os.vrun("nmake /f Makefile.msvc install") + end) + end + + on_install("macosx", "linux", function (package) + import("package.tools.autoconf").install(package, {"--disable-dependency-tracking", "--without-python", "--without-lzma"}) + end) +``` + +#### 内置变量 + +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 | +| [find_packages](#find_packages) | 查找依赖包 | 脚本域 | >= 2.2.5 | + +在描述域使用接口调用的实例如下,一般仅用于条件控制: + +```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/xmake-io/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 +``` + +2.1.7版本可通过`COLORTERM=nocolor`来禁用色彩输出。 + +##### 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) + +##### find_packages + +###### 查找依赖包 + +此接口是对[lib.detect.find_package](#detect-find_package)接口的封装,提供多个依赖包的查找支持,例如: + +```lua +target("test") + set_kind("binary") + add_files("src/*.c") + on_load(function (target) + target:add(find_packages("openssl", "zlib")) + end) +``` + +##### os + +系统操作模块,属于内置模块,无需使用[import](#import)导入,可直接脚本域调用其接口。 + +此模块也是lua的原生模块,xmake在其基础上进行了扩展,提供更多实用的接口。 + ++os模块里面只有部分readonly接口(例如:`os.getenv`, `os.arch`)是可以在描述域中使用,其他接口只能在脚本域中使用,例如:`os.cp`, `os.rm`等 +
+ +| 接口 | 描述 | 支持版本 | +| ----------------------------------------------- | -------------------------------------------- | -------- | +| [os.cp](#os-cp) | 复制文件或目录 | >= 2.0.1 | +| [os.mv](#os-mv) | 移动重命名文件或目录 | >= 2.0.1 | +| [os.rm](#os-rm) | 删除文件或目录树 | >= 2.0.1 | +| [os.trycp](#os-trycp) | 尝试复制文件或目录 | >= 2.1.6 | +| [os.trymv](#os-trymv) | 尝试移动重命名文件或目录 | >= 2.1.6 | +| [os.tryrm](#os-tryrm) | 尝试删除文件或目录树 | >= 2.1.6 | +| [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.filesize](#os-filesize) | 获取文件大小 | >= 2.1.9 | +| [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.trycp + +- 尝试复制文件或目录 + +跟[os.cp](#os-cp)类似,唯一的区别就是,此接口操作失败不会抛出异常中断xmake,而是通过返回值标示是否执行成功。 + +```lua +if os.trycp("file", "dest/file") then +end +``` + +###### os.trymv + +- 尝试移动文件或目录 + +跟[os.mv](#os-mv)类似,唯一的区别就是,此接口操作失败不会抛出异常中断xmake,而是通过返回值标示是否执行成功。 + +```lua +if os.trymv("file", "dest/file") then +end +``` + +###### os.tryrm + +- 尝试删除文件或目录 + +跟[os.rm](#os-rm)类似,唯一的区别就是,此接口操作失败不会抛出异常中断xmake,而是通过返回值标示是否执行成功。 + +```lua +if os.tryrm("file") then +end +``` + +###### 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("binary", "cc", {"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") +``` + +第一个参数指定链接类型,目前支持:binary, static, shared +第二个参数告诉链接器,应该作为那种源文件对象进行链接,这些对象源文件使用什么编译器编译的,例如: + +| 第二个参数值 | 描述 | +| ------------ | ------------ | +| cc | c编译器 | +| cxx | c++编译器 | +| mm | objc编译器 | +| mxx | objc++编译器 | +| gc | go编译器 | +| as | 汇编器 | +| sc | swift编译器 | +| rc | rust编译器 | +| dc | dlang编译器 | + +指定不同的编译器类型,链接器会适配最合适的链接器来处理链接,并且如果几种支持混合编译的语言,那么可以同时传入多个编译器类型,指定链接器选择支持这些混合编译语言的链接器进行链接处理: + +```lua +linker.link("binary", {"cc", "mxx", "sc"}, {"a.o", "b.o", "c.o"}, "/tmp/targetfile") +``` + +上述代码告诉链接器,a, b, c三个对象文件有可能分别是c, objc++, swift代码编译出来的,链接器会从当前系统和工具链中选择最合适的链接器去处理这个链接过程。 + +###### 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:name()) + + -- 获取目标目录, 2.1.9版本之后才有 + print(target:targetdir()) + + -- 获取目标文件名 + 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.find_cudadevices](#detect-find_cudadevices) | 查找本机的 CUDA 设备 | >= 2.2.7 | +| [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", {pathes = {"/usr/bin", "/usr/local/bin"}, check = "--help"}) +``` + +上述代码会尝试运行:`/usr/bin/ccache --help`,如果运行成功,则返回:`/usr/bin/ccache`。 + +如果`--help`也没法满足需求,有些程序没有`--version/--help`参数,那么可以自定义运行脚本,来运行检测: + +```lua +local program = find_program("ccache", {pathes = {"/usr/bin", "/usr/local/bin"}, check = function (program) os.run("%s -h", program) end}) +``` + +同样,搜索路径列表支持内建变量和自定义脚本: + +```lua +local program = find_program("ccache", {pathes = {"$(env PATH)", "$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug;Debugger)"}}) +local program = find_program("ccache", {pathes = {"$(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", {command = "-v"}) +``` + +甚至自定义版本获取脚本: + +```lua +local version = find_programver("ccache", {command = function () return os.iorun("ccache --version") end}) +``` + +对于版本号的提取规则,如果内置的匹配模式不满足要求,也可以自定义: + +```lua +local version = find_programver("ccache", {command = "--version", parse = "(%d+%.?%d*%.?%d*.-)%s"}) +local version = find_programver("ccache", {command = "--version", parse = 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. 如果系统存在vcpkg,优先从vcpkg的包管理系统中去获取包 +4. 如果系统存在`pkg-config`,并且查找的是系统环境的库,则尝试使用`pkg-config`提供的路径和链接信息进行查找 +5. 如果系统存在`homebrew`,并且查找的是系统环境的库,则尝试使用`brew --prefix xxx`提供的信息进行查找 +6. 从参数中指定的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` 来快速测试。 + +2.2.5版本之后,新增了内置接口[find_packages](#find_packages),可以同时查找多个包,并且不需要通过import导入即可直接使用。 + +并且此版本之后,支持显式的从指定第三方包管理器中查找包,例如: + +```lua +find_package("brew::pcre2/libpcre2-8") +``` + +由于每个第三方包管理器的包名不完全一致,比如pcre2在homebrew中有三个库版本,我们可以通过上面的方式,指定查找对应libpcre2-8版本的库。 + +另外,对于vcpkg, conan也可以通过加上`vcpkg::`, `conan::`包命名空间来指定查找里面的库。 + +###### 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.find_cudadevices + +- 查找本机的 CUDA 设备 + +通过 CUDA Runtime API 枚举本机的 CUDA 设备,并查询其属性。 + +```lua +import("lib.detect.find_cudadevices") + +local devices = find_cudadevices({ skip_compute_mode_prohibited = true }) +local devices = find_cudadevices({ min_sm_arch = 35, order_by_flops = true }) +``` + +返回的结果为:`{ { ['$id'] = 0, name = "GeForce GTX 960M", major = 5, minor = 0, ... }, ... }` + +包含的属性依据当前 CUDA 版本会有所不同,可以参考 [CUDA 官方文档](https://docs.nvidia.com/cuda/cuda-runtime-api/structcudaDeviceProp.html#structcudaDeviceProp)及其历史版本。 + +###### 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("https://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/old/zh/plugins.md b/old/zh/plugins.md new file mode 100644 index 00000000..964b1204 --- /dev/null +++ b/old/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/xmake-io/xmake-plugins). + +#### 从app生成ipa包 + +这仅仅是一个小插件,ios开发的同学,可能会用的到。 + +```console +$ xmake app2ipa --icon=/xxx.png /xxx/ios.app -o /xxx.ios.ipa +``` -- cgit v1.2.3